From d3628a744bc5fd311351ee38067e7ade70ee339d Mon Sep 17 00:00:00 2001 From: Alex Lee Date: Mon, 10 Apr 2017 11:08:06 -0700 Subject: [PATCH 001/110] Fixes for compatibility with TF 1.0 and python 3. --- video_prediction/lstm_ops.py | 8 +------- video_prediction/prediction_train.py | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/video_prediction/lstm_ops.py b/video_prediction/lstm_ops.py index d8afe56be..1f8c8d97a 100644 --- a/video_prediction/lstm_ops.py +++ b/video_prediction/lstm_ops.py @@ -38,17 +38,11 @@ def init_state(inputs, if inputs is not None: # Handle both the dynamic shape as well as the inferred shape. inferred_batch_size = inputs.get_shape().with_rank_at_least(1)[0] - batch_size = tf.shape(inputs)[0] dtype = inputs.dtype else: inferred_batch_size = 0 - batch_size = 0 - initial_state = state_initializer( - tf.stack([batch_size] + state_shape), - dtype=dtype) - initial_state.set_shape([inferred_batch_size] + state_shape) - + [inferred_batch_size] + state_shape, dtype=dtype) return initial_state diff --git a/video_prediction/prediction_train.py b/video_prediction/prediction_train.py index 598226035..46f881426 100644 --- a/video_prediction/prediction_train.py +++ b/video_prediction/prediction_train.py @@ -103,21 +103,24 @@ class Model(object): actions=None, states=None, sequence_length=None, - reuse_scope=None): + reuse_scope=None, + prefix=None): if sequence_length is None: sequence_length = FLAGS.sequence_length - self.prefix = prefix = tf.placeholder(tf.string, []) + if prefix is None: + prefix = tf.placeholder(tf.string, []) + self.prefix = prefix self.iter_num = tf.placeholder(tf.float32, []) summaries = [] # Split into timesteps. - actions = tf.split(axis=1, num_or_size_splits=actions.get_shape()[1], value=actions) + actions = tf.split(axis=1, num_or_size_splits=int(actions.get_shape()[1]), value=actions) actions = [tf.squeeze(act) for act in actions] - states = tf.split(axis=1, num_or_size_splits=states.get_shape()[1], value=states) + states = tf.split(axis=1, num_or_size_splits=int(states.get_shape()[1]), value=states) states = [tf.squeeze(st) for st in states] - images = tf.split(axis=1, num_or_size_splits=images.get_shape()[1], value=images) + images = tf.split(axis=1, num_or_size_splits=int(images.get_shape()[1]), value=images) images = [tf.squeeze(img) for img in images] if reuse_scope is None: @@ -183,17 +186,18 @@ class Model(object): def main(unused_argv): - print 'Constructing models and inputs.' + print('Constructing models and inputs.') with tf.variable_scope('model', reuse=None) as training_scope: images, actions, states = build_tfrecord_input(training=True) - model = Model(images, actions, states, FLAGS.sequence_length) + model = Model(images, actions, states, FLAGS.sequence_length, + prefix='train') with tf.variable_scope('val_model', reuse=None): val_images, val_actions, val_states = build_tfrecord_input(training=False) val_model = Model(val_images, val_actions, val_states, - FLAGS.sequence_length, training_scope) + FLAGS.sequence_length, training_scope, prefix='val') - print 'Constructing saver.' + print('Constructing saver.') # Make saver. saver = tf.train.Saver( tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES), max_to_keep=0) @@ -214,8 +218,7 @@ def main(unused_argv): # Run training. for itr in range(FLAGS.num_iterations): # Generate new batch of data. - feed_dict = {model.prefix: 'train', - model.iter_num: np.float32(itr), + feed_dict = {model.iter_num: np.float32(itr), model.lr: FLAGS.learning_rate} cost, _, summary_str = sess.run([model.loss, model.train_op, model.summ_op], feed_dict) @@ -226,7 +229,6 @@ def main(unused_argv): if (itr) % VAL_INTERVAL == 2: # Run through validation set. feed_dict = {val_model.lr: 0.0, - val_model.prefix: 'val', val_model.iter_num: np.float32(itr)} _, val_summary_str = sess.run([val_model.train_op, val_model.summ_op], feed_dict) -- GitLab From 983b7d08b6e98c60c4016ac9d4b647ea7935928d Mon Sep 17 00:00:00 2001 From: Evan Kepner Date: Thu, 11 May 2017 15:19:48 -0400 Subject: [PATCH 002/110] correct authorship --- tutorials/rnn/ptb/ptb_word_lm.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/tutorials/rnn/ptb/ptb_word_lm.py b/tutorials/rnn/ptb/ptb_word_lm.py index 7430f2e43..fccbd4125 100644 --- a/tutorials/rnn/ptb/ptb_word_lm.py +++ b/tutorials/rnn/ptb/ptb_word_lm.py @@ -162,11 +162,21 @@ class PTBModel(object): "softmax_w", [size, vocab_size], dtype=data_type()) softmax_b = tf.get_variable("softmax_b", [vocab_size], dtype=data_type()) logits = tf.matmul(output, softmax_w) + softmax_b - loss = tf.contrib.legacy_seq2seq.sequence_loss_by_example( - [logits], - [tf.reshape(input_.targets, [-1])], - [tf.ones([batch_size * num_steps], dtype=data_type())]) - self._cost = cost = tf.reduce_sum(loss) / batch_size + + # Reshape logits to be 3-D tensor for sequence loss + logits = tf.reshape(logits, [batch_size, num_steps, vocab_size]) + + # use the contrib sequence loss and average over the batches + loss = tf.contrib.seq2seq.sequence_loss( + logits, + input_.targets, + tf.ones([batch_size, num_steps], dtype=data_type()), + average_across_timesteps=False, + average_across_batch=True + ) + + # update the cost variables + self._cost = cost = tf.reduce_sum(loss) self._final_state = state if not is_training: -- GitLab From d229273e43cde137cde9766674423387d10c5283 Mon Sep 17 00:00:00 2001 From: Hiroki Teranishi Date: Wed, 17 May 2017 20:27:54 +0900 Subject: [PATCH 003/110] improved ArcStandardTransitionSystem.PerformRightArc() --- syntaxnet/syntaxnet/arc_standard_transitions.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/syntaxnet/syntaxnet/arc_standard_transitions.cc b/syntaxnet/syntaxnet/arc_standard_transitions.cc index 8feebe1a9..24b94dbfc 100644 --- a/syntaxnet/syntaxnet/arc_standard_transitions.cc +++ b/syntaxnet/syntaxnet/arc_standard_transitions.cc @@ -269,9 +269,7 @@ class ArcStandardTransitionSystem : public ParserTransitionSystem { void PerformRightArc(ParserState *state, int label) const { DCHECK(IsAllowedRightArc(*state)); int s0 = state->Pop(); - int s1 = state->Pop(); - state->AddArc(s0, s1, label); - state->Push(s1); + state->AddArc(s0, state->Top(), label); } // We are in a deterministic state when we either reached the end of the input -- GitLab From 320be60bd22a4518b1300c5fa6d572b49f20bafc Mon Sep 17 00:00:00 2001 From: Richard Davies Date: Wed, 17 May 2017 17:06:45 +0100 Subject: [PATCH 004/110] Force new instance creation in MultiRNNCell --- neural_gpu/neural_gpu.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/neural_gpu/neural_gpu.py b/neural_gpu/neural_gpu.py index 4d1877393..e8ba66e9d 100644 --- a/neural_gpu/neural_gpu.py +++ b/neural_gpu/neural_gpu.py @@ -478,8 +478,10 @@ class NeuralGPU(object): # This is just for running a baseline RNN seq2seq model. if do_rnn: self.after_enc_step.append(step) # Not meaningful here, but needed. - lstm_cell = tf.contrib.rnn.BasicLSTMCell(height * nmaps) - cell = tf.contrib.rnn.MultiRNNCell([lstm_cell] * nconvs) + def lstm_cell(): + return tf.contrib.rnn.BasicLSTMCell(height * nmaps) + cell = tf.contrib.rnn.MultiRNNCell( + [lstm_cell() for _ in range(nconvs)]) with tf.variable_scope("encoder"): encoder_outputs, encoder_state = tf.nn.dynamic_rnn( cell, tf.reshape(step, [batch_size, length, height * nmaps]), -- GitLab From a3711c9f35f4f77cb8110dc8a03ee026573006fa Mon Sep 17 00:00:00 2001 From: Andrei Costinescu Date: Thu, 18 May 2017 15:31:24 +0200 Subject: [PATCH 005/110] Update inception_preprocessing.py Corrected documentation word "cropt" -> "crop" --- slim/preprocessing/inception_preprocessing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slim/preprocessing/inception_preprocessing.py b/slim/preprocessing/inception_preprocessing.py index ca3eba0ba..b907aab1f 100644 --- a/slim/preprocessing/inception_preprocessing.py +++ b/slim/preprocessing/inception_preprocessing.py @@ -241,7 +241,7 @@ def preprocess_for_eval(image, height, width, If height and width are specified it would output an image with that size by applying resize_bilinear. - If central_fraction is specified it would cropt the central fraction of the + If central_fraction is specified it would crop the central fraction of the input image. Args: -- GitLab From 88ebe49adc4bee3769fbbd52f0025ca77b694748 Mon Sep 17 00:00:00 2001 From: hizagalilo Date: Thu, 18 May 2017 14:23:03 -0400 Subject: [PATCH 006/110] Update slim_walkthrough.ipynb --- slim/slim_walkthrough.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slim/slim_walkthrough.ipynb b/slim/slim_walkthrough.ipynb index 125590e93..da868ef19 100644 --- a/slim/slim_walkthrough.ipynb +++ b/slim/slim_walkthrough.ipynb @@ -157,7 +157,7 @@ "\n", " # Print name and shape of each tensor.\n", " print \"Layers\"\n", - " for k, v in end_points.iteritems():\n", + " for k, v in end_points.items():\n", " print 'name = {}, shape = {}'.format(v.name, v.get_shape())\n", "\n", " # Print name and shape of parameter nodes (values not yet initialized)\n", @@ -391,7 +391,7 @@ " final_op=names_to_value_nodes.values())\n", "\n", " names_to_values = dict(zip(names_to_value_nodes.keys(), metric_values))\n", - " for key, value in names_to_values.iteritems():\n", + " for key, value in names_to_values.items():\n", " print('%s: %f' % (key, value))" ] }, -- GitLab From 443c074527f164955720dcde5c1830faf519f89f Mon Sep 17 00:00:00 2001 From: Neal Wu Date: Thu, 18 May 2017 16:48:51 -0700 Subject: [PATCH 007/110] Convert control_flow_ops.with_dependencies to tf.control_dependencies --- inception/inception/slim/ops_test.py | 11 +++++----- slim/deployment/model_deploy.py | 4 ++-- slim/preprocessing/vgg_preprocessing.py | 29 ++++++++++--------------- slim/train_image_classifier.py | 5 ++--- 4 files changed, 22 insertions(+), 27 deletions(-) diff --git a/inception/inception/slim/ops_test.py b/inception/inception/slim/ops_test.py index 0978e0ef3..ab205debf 100644 --- a/inception/inception/slim/ops_test.py +++ b/inception/inception/slim/ops_test.py @@ -21,8 +21,6 @@ from __future__ import print_function import numpy as np import tensorflow as tf -from tensorflow.python.ops import control_flow_ops - from inception.slim import ops from inception.slim import scopes from inception.slim import variables @@ -602,7 +600,8 @@ class BatchNormTest(tf.test.TestCase): update_ops = tf.get_collection(ops.UPDATE_OPS_COLLECTION) with tf.control_dependencies(update_ops): barrier = tf.no_op(name='gradient_barrier') - output = control_flow_ops.with_dependencies([barrier], output) + with tf.control_dependencies([barrier]): + output = output # Initialize all variables sess.run(tf.global_variables_initializer()) moving_mean = variables.get_variables('BatchNorm/moving_mean')[0] @@ -632,7 +631,8 @@ class BatchNormTest(tf.test.TestCase): update_ops = tf.get_collection(ops.UPDATE_OPS_COLLECTION) with tf.control_dependencies(update_ops): barrier = tf.no_op(name='gradient_barrier') - output = control_flow_ops.with_dependencies([barrier], output) + with tf.control_dependencies([barrier]): + output = output # Initialize all variables sess.run(tf.global_variables_initializer()) moving_mean = variables.get_variables('BatchNorm/moving_mean')[0] @@ -666,7 +666,8 @@ class BatchNormTest(tf.test.TestCase): update_ops = tf.get_collection(ops.UPDATE_OPS_COLLECTION) with tf.control_dependencies(update_ops): barrier = tf.no_op(name='gradient_barrier') - output = control_flow_ops.with_dependencies([barrier], output) + with tf.control_dependencies([barrier]): + output = output # Initialize all variables sess.run(tf.global_variables_initializer()) moving_mean = variables.get_variables('BatchNorm/moving_mean')[0] diff --git a/slim/deployment/model_deploy.py b/slim/deployment/model_deploy.py index 8855f2aee..24dd5c34a 100644 --- a/slim/deployment/model_deploy.py +++ b/slim/deployment/model_deploy.py @@ -378,8 +378,8 @@ def deploy(config, update_ops.append(grad_updates) update_op = tf.group(*update_ops) - train_op = control_flow_ops.with_dependencies([update_op], total_loss, - name='train_op') + with tf.control_dependencies([update_op]): + train_op = total_loss else: clones_losses = [] regularization_losses = tf.get_collection( diff --git a/slim/preprocessing/vgg_preprocessing.py b/slim/preprocessing/vgg_preprocessing.py index 1900cae22..c2c92f0a7 100644 --- a/slim/preprocessing/vgg_preprocessing.py +++ b/slim/preprocessing/vgg_preprocessing.py @@ -34,8 +34,6 @@ from __future__ import print_function import tensorflow as tf -from tensorflow.python.ops import control_flow_ops - slim = tf.contrib.slim _R_MEAN = 123.68 @@ -71,9 +69,8 @@ def _crop(image, offset_height, offset_width, crop_height, crop_width): rank_assertion = tf.Assert( tf.equal(tf.rank(image), 3), ['Rank of image must be equal to 3.']) - cropped_shape = control_flow_ops.with_dependencies( - [rank_assertion], - tf.stack([crop_height, crop_width, original_shape[2]])) + with tf.control_dependencies([rank_assertion]): + cropped_shape = tf.stack([crop_height, crop_width, original_shape[2]]) size_assertion = tf.Assert( tf.logical_and( @@ -85,9 +82,8 @@ def _crop(image, offset_height, offset_width, crop_height, crop_width): # Use tf.slice instead of crop_to_bounding box as it accepts tensors to # define the crop size. - image = control_flow_ops.with_dependencies( - [size_assertion], - tf.slice(image, offsets, cropped_shape)) + with tf.control_dependencies([size_assertion]): + image = tf.slice(image, offsets, cropped_shape) return tf.reshape(image, cropped_shape) @@ -126,9 +122,8 @@ def _random_crop(image_list, crop_height, crop_width): image_list[i].name, 3, image_rank]) rank_assertions.append(rank_assert) - image_shape = control_flow_ops.with_dependencies( - [rank_assertions[0]], - tf.shape(image_list[0])) + with tf.control_dependencies([rank_assertions[0]]): + image_shape = tf.shape(image_list[0]) image_height = image_shape[0] image_width = image_shape[1] crop_size_assert = tf.Assert( @@ -142,8 +137,8 @@ def _random_crop(image_list, crop_height, crop_width): for i in range(1, len(image_list)): image = image_list[i] asserts.append(rank_assertions[i]) - shape = control_flow_ops.with_dependencies([rank_assertions[i]], - tf.shape(image)) + with tf.control_dependencies([rank_assertions[i]]): + shape = tf.shape(image) height = shape[0] width = shape[1] @@ -162,10 +157,10 @@ def _random_crop(image_list, crop_height, crop_width): # Use tf.random_uniform and not numpy.random.rand as doing the former would # generate random numbers at graph eval time, unlike the latter which # generates random numbers at graph definition time. - max_offset_height = control_flow_ops.with_dependencies( - asserts, tf.reshape(image_height - crop_height + 1, [])) - max_offset_width = control_flow_ops.with_dependencies( - asserts, tf.reshape(image_width - crop_width + 1, [])) + with tf.control_dependencies(asserts): + max_offset_height = tf.reshape(image_height - crop_height + 1, []) + with tf.control_dependencies(asserts): + max_offset_width = tf.reshape(image_width - crop_width + 1, []) offset_height = tf.random_uniform( [], maxval=max_offset_height, dtype=tf.int32) offset_width = tf.random_uniform( diff --git a/slim/train_image_classifier.py b/slim/train_image_classifier.py index 146f70026..5aa674f41 100755 --- a/slim/train_image_classifier.py +++ b/slim/train_image_classifier.py @@ -20,7 +20,6 @@ from __future__ import print_function import tensorflow as tf -from tensorflow.python.ops import control_flow_ops from datasets import dataset_factory from deployment import model_deploy from nets import nets_factory @@ -540,8 +539,8 @@ def main(_): update_ops.append(grad_updates) update_op = tf.group(*update_ops) - train_tensor = control_flow_ops.with_dependencies([update_op], total_loss, - name='train_op') + with tf.control_dependencies([update_op]): + train_tensor = total_loss # Add the summaries from the first clone. These contain the summaries # created by model_fn and either optimize_clones() or _gather_clone_loss(). -- GitLab From 8e54ffc86b988dad41ae13422010e2b6af151939 Mon Sep 17 00:00:00 2001 From: Neal Wu Date: Fri, 19 May 2017 16:32:06 -0700 Subject: [PATCH 008/110] Add tf.identity --- inception/inception/slim/ops_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/inception/inception/slim/ops_test.py b/inception/inception/slim/ops_test.py index ab205debf..cf5afbba9 100644 --- a/inception/inception/slim/ops_test.py +++ b/inception/inception/slim/ops_test.py @@ -601,7 +601,7 @@ class BatchNormTest(tf.test.TestCase): with tf.control_dependencies(update_ops): barrier = tf.no_op(name='gradient_barrier') with tf.control_dependencies([barrier]): - output = output + output = tf.identity(output) # Initialize all variables sess.run(tf.global_variables_initializer()) moving_mean = variables.get_variables('BatchNorm/moving_mean')[0] @@ -632,7 +632,7 @@ class BatchNormTest(tf.test.TestCase): with tf.control_dependencies(update_ops): barrier = tf.no_op(name='gradient_barrier') with tf.control_dependencies([barrier]): - output = output + output = tf.identity(output) # Initialize all variables sess.run(tf.global_variables_initializer()) moving_mean = variables.get_variables('BatchNorm/moving_mean')[0] @@ -667,7 +667,7 @@ class BatchNormTest(tf.test.TestCase): with tf.control_dependencies(update_ops): barrier = tf.no_op(name='gradient_barrier') with tf.control_dependencies([barrier]): - output = output + output = tf.identity(output) # Initialize all variables sess.run(tf.global_variables_initializer()) moving_mean = variables.get_variables('BatchNorm/moving_mean')[0] -- GitLab From 50a03e7f887730f350fc904a4a57999e2214c137 Mon Sep 17 00:00:00 2001 From: Adam Myers Date: Fri, 19 May 2017 18:12:41 -0700 Subject: [PATCH 009/110] Update protobuf version dependency Updates protobuf version as previous (3.0.0b2) was no longer compatible and resulted in failing bazel tests during setup process. --- syntaxnet/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syntaxnet/README.md b/syntaxnet/README.md index e76d627a5..779ba2d8d 100644 --- a/syntaxnet/README.md +++ b/syntaxnet/README.md @@ -77,7 +77,7 @@ source. You'll need to install: * `brew install swig` on OSX * protocol buffers, with a version supported by TensorFlow: * check your protobuf version with `pip freeze | grep protobuf` - * upgrade to a supported version with `pip install -U protobuf==3.0.0b2` + * upgrade to a supported version with `pip install -U protobuf==3.3.0` * mock, the testing package: * `pip install mock` * asciitree, to draw parse trees on the console for the demo: -- GitLab From 1e986ea62ab781df0a6ed0dc01b399470cbf8d51 Mon Sep 17 00:00:00 2001 From: Andrew Hundt Date: Sun, 21 May 2017 19:39:59 -0400 Subject: [PATCH 010/110] typo fix --- differential_privacy/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/differential_privacy/README.md b/differential_privacy/README.md index 9cda93aa1..4bd6c22c9 100644 --- a/differential_privacy/README.md +++ b/differential_privacy/README.md @@ -3,7 +3,7 @@ Open Sourced By: Xin Pan (xpan@google.com, github: panyx0718) -###Introduction for dp_sgd/README.md +### Introduction for [dp_sgd/README.md](dp_sgd/README.md) Machine learning techniques based on neural networks are achieving remarkable results in a wide variety of domains. Often, the training of models requires @@ -18,7 +18,7 @@ manageable cost in software complexity, training efficiency, and model quality. paper: https://arxiv.org/abs/1607.00133 -###Introduction for multiple_teachers/README.md +### Introduction for [multiple_teachers/README.md](multiple_teachers/README.md) This repository contains code to create a setup for learning privacy-preserving student models by transferring knowledge from an ensemble of teachers trained -- GitLab From 120b1fb677d4e18dccc99a388fd84ee2626759ab Mon Sep 17 00:00:00 2001 From: Damien Vincent Date: Mon, 22 May 2017 13:08:44 +0200 Subject: [PATCH 011/110] Image compression: initial version of the entropy coder. --- compression/entropy_coder/README.md | 102 +++++ compression/entropy_coder/__init__.py | 0 .../entropy_coder/all_models/__init__.py | 0 .../entropy_coder/all_models/all_models.py | 19 + .../all_models/all_models_test.py | 68 ++++ .../configs/gru_prime3/model_config.json | 4 + .../configs/synthetic/input_config.json | 4 + .../configs/synthetic/model_config.json | 4 + .../configs/synthetic/train_config.json | 6 + compression/entropy_coder/core/code_loader.py | 73 ++++ .../entropy_coder/core/config_helper.py | 52 +++ .../core/entropy_coder_single.py | 116 ++++++ .../entropy_coder/core/entropy_coder_train.py | 184 +++++++++ .../dataset/gen_synthetic_dataset.py | 88 +++++ .../dataset/gen_synthetic_single.py | 72 ++++ .../entropy_coder/dataset/synthetic_model.py | 74 ++++ compression/entropy_coder/lib/__init__.py | 0 compression/entropy_coder/lib/block_base.py | 258 +++++++++++++ compression/entropy_coder/lib/block_util.py | 100 +++++ compression/entropy_coder/lib/blocks.py | 24 ++ .../entropy_coder/lib/blocks_binarizer.py | 35 ++ .../lib/blocks_entropy_coding.py | 49 +++ .../lib/blocks_entropy_coding_test.py | 56 +++ compression/entropy_coder/lib/blocks_lstm.py | 263 +++++++++++++ .../entropy_coder/lib/blocks_lstm_test.py | 113 ++++++ .../entropy_coder/lib/blocks_masked_conv2d.py | 225 +++++++++++ .../lib/blocks_masked_conv2d_lstm.py | 79 ++++ .../lib/blocks_masked_conv2d_test.py | 206 ++++++++++ .../entropy_coder/lib/blocks_operator.py | 87 +++++ .../entropy_coder/lib/blocks_operator_test.py | 64 +++ compression/entropy_coder/lib/blocks_std.py | 364 ++++++++++++++++++ .../entropy_coder/lib/blocks_std_test.py | 340 ++++++++++++++++ compression/entropy_coder/model/__init__.py | 0 .../model/entropy_coder_model.py | 55 +++ .../entropy_coder/model/model_factory.py | 53 +++ .../entropy_coder/progressive/__init__.py | 0 .../entropy_coder/progressive/progressive.py | 242 ++++++++++++ 37 files changed, 3479 insertions(+) create mode 100644 compression/entropy_coder/README.md create mode 100644 compression/entropy_coder/__init__.py create mode 100644 compression/entropy_coder/all_models/__init__.py create mode 100644 compression/entropy_coder/all_models/all_models.py create mode 100644 compression/entropy_coder/all_models/all_models_test.py create mode 100644 compression/entropy_coder/configs/gru_prime3/model_config.json create mode 100644 compression/entropy_coder/configs/synthetic/input_config.json create mode 100644 compression/entropy_coder/configs/synthetic/model_config.json create mode 100644 compression/entropy_coder/configs/synthetic/train_config.json create mode 100644 compression/entropy_coder/core/code_loader.py create mode 100644 compression/entropy_coder/core/config_helper.py create mode 100644 compression/entropy_coder/core/entropy_coder_single.py create mode 100644 compression/entropy_coder/core/entropy_coder_train.py create mode 100644 compression/entropy_coder/dataset/gen_synthetic_dataset.py create mode 100644 compression/entropy_coder/dataset/gen_synthetic_single.py create mode 100644 compression/entropy_coder/dataset/synthetic_model.py create mode 100644 compression/entropy_coder/lib/__init__.py create mode 100644 compression/entropy_coder/lib/block_base.py create mode 100644 compression/entropy_coder/lib/block_util.py create mode 100644 compression/entropy_coder/lib/blocks.py create mode 100644 compression/entropy_coder/lib/blocks_binarizer.py create mode 100644 compression/entropy_coder/lib/blocks_entropy_coding.py create mode 100644 compression/entropy_coder/lib/blocks_entropy_coding_test.py create mode 100644 compression/entropy_coder/lib/blocks_lstm.py create mode 100644 compression/entropy_coder/lib/blocks_lstm_test.py create mode 100644 compression/entropy_coder/lib/blocks_masked_conv2d.py create mode 100644 compression/entropy_coder/lib/blocks_masked_conv2d_lstm.py create mode 100644 compression/entropy_coder/lib/blocks_masked_conv2d_test.py create mode 100644 compression/entropy_coder/lib/blocks_operator.py create mode 100644 compression/entropy_coder/lib/blocks_operator_test.py create mode 100644 compression/entropy_coder/lib/blocks_std.py create mode 100644 compression/entropy_coder/lib/blocks_std_test.py create mode 100644 compression/entropy_coder/model/__init__.py create mode 100644 compression/entropy_coder/model/entropy_coder_model.py create mode 100644 compression/entropy_coder/model/model_factory.py create mode 100644 compression/entropy_coder/progressive/__init__.py create mode 100644 compression/entropy_coder/progressive/progressive.py diff --git a/compression/entropy_coder/README.md b/compression/entropy_coder/README.md new file mode 100644 index 000000000..806743304 --- /dev/null +++ b/compression/entropy_coder/README.md @@ -0,0 +1,102 @@ +# Neural net based entropy coding + +This is a [TensorFlow](http://www.tensorflow.org/) model for additional +lossless compression of bitstreams generated by neural net based image +encoders as described in +[https://arxiv.org/abs/1703.10114](https://arxiv.org/abs/1703.10114). + +To be more specific, the entropy coder aims at compressing further binary +codes which have a 3D tensor structure with: + +* the first two dimensions of the tensors corresponding to the height and +the width of the binary codes, +* the last dimension being the depth of the codes. The last dimension can be +sliced into N groups of K, where each additional group is used by the image +decoder to add more details to the reconstructed image. + + +## Prerequisites +The only software requirements for running the encoder and decoder is having +Tensorflow installed. + +You will also need to add the top level source directory of the entropy coder +to your `PYTHONPATH`, for example: + +`export PYTHONPATH=${PYTHONPATH}:/tmp/compression/entropy_coder` + + +## Training the entropy coder + +### Synthetic dataset +If you do not have a training dataset, there is a simple code generative model +that you can use to generate a dataset and play with the entropy coder. +The generative model is located under dataset/gen\_synthetic\_dataset.py. Note +that this simple generative model is not going to give good results on real +images as it is not supposed to be close to the statistics of the binary +representation of encoded images. Consider it as a toy dataset, no more, no +less. + +To generate a synthetic dataset with 20000 samples: + +`python ./dataset/gen_synthetic_dataset.py --dataset_dir=/tmp/dataset/ +--count=20000` + +Note that the generator has not been optimized at all, generating the synthetic +dataset is currently pretty slow. + +### Training + +If you just want to play with the entropy coder trainer, here is the command +line that can be used to train the entropy coder on the synthetic dataset: + +`mkdir -p /tmp/entropy_coder_train` + +`python ./core/entropy_coder_train.py --task=0 +--train_dir=/tmp/entropy_coder_train/ +--model=progressive +--model_config=./configs/synthetic/model_config.json +--train_config=./configs/synthetic/train_config.json +--input_config=./configs/synthetic/input_config.json +` + +Training is configured using 3 files formatted using JSON: + +* One file is used to configure the underlying entropy coder model. + Currently, only the *progressive* model is supported. + This model takes 2 mandatory parameters and an optional one: + * `layer_depth`: the number of bits per layer (a.k.a. iteration). + Background: the image decoder takes each layer to add more detail + to the image. + * `layer_count`: the maximum number of layers that should be supported + by the model. This should be equal or greater than the maximum number + of layers in the input binary codes. + * `coded_layer_count`: This can be used to consider only partial codes, + keeping only the first `coded_layer_count` layers and ignoring the + remaining layers. If left empty, the binary codes are left unchanged. +* One file to configure the training, including the learning rate, ... + The meaning of the parameters are pretty straightforward. Note that this + file is only used during training and is not needed during inference. +* One file to specify the input dataset to use during training. + The dataset is formatted using tf.RecordIO. + + +## Inference: file size after entropy coding. + +### Using a synthetic sample + +Here is the command line to generate a single synthetic sample formatted +in the same way as what is provided by the image encoder: + +`python ./dataset/gen_synthetic_single.py +--sample_filename=/tmp/dataset/sample_0000.npz` + +To actually compute the additional compression ratio using the entropy coder +trained in the previous step: + +`python ./core/entropy_coder_single.py +--model=progressive +--model_config=./configs/synthetic/model_config.json +--input_codes=/tmp/dataset/sample_0000.npz +--checkpoint=/tmp/entropy_coder_train/model.ckpt-209078` + +where the checkpoint number should be adjusted accordingly. diff --git a/compression/entropy_coder/__init__.py b/compression/entropy_coder/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/compression/entropy_coder/all_models/__init__.py b/compression/entropy_coder/all_models/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/compression/entropy_coder/all_models/all_models.py b/compression/entropy_coder/all_models/all_models.py new file mode 100644 index 000000000..e376dac73 --- /dev/null +++ b/compression/entropy_coder/all_models/all_models.py @@ -0,0 +1,19 @@ +# 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 and register all the entropy coder models.""" + +# pylint: disable=unused-import +from entropy_coder.progressive import progressive diff --git a/compression/entropy_coder/all_models/all_models_test.py b/compression/entropy_coder/all_models/all_models_test.py new file mode 100644 index 000000000..b8aff504a --- /dev/null +++ b/compression/entropy_coder/all_models/all_models_test.py @@ -0,0 +1,68 @@ +# 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. +# ============================================================================== + +"""Basic test of all registered models.""" + +import tensorflow as tf + +# pylint: disable=unused-import +import all_models +# pylint: enable=unused-import +from entropy_coder.model import model_factory + + +class AllModelsTest(tf.test.TestCase): + + def testBuildModelForTraining(self): + factory = model_factory.GetModelRegistry() + model_names = factory.GetAvailableModels() + + for m in model_names: + tf.reset_default_graph() + + global_step = tf.Variable(tf.zeros([], dtype=tf.int64), + trainable=False, + name='global_step') + + optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.1) + + batch_size = 3 + height = 40 + width = 20 + depth = 5 + binary_codes = tf.placeholder(dtype=tf.float32, + shape=[batch_size, height, width, depth]) + + # Create a model with the default configuration. + print('Creating model: {}'.format(m)) + model = factory.CreateModel(m) + model.Initialize(global_step, + optimizer, + model.GetConfigStringForUnitTest()) + self.assertTrue(model.loss is None, 'model: {}'.format(m)) + self.assertTrue(model.train_op is None, 'model: {}'.format(m)) + self.assertTrue(model.average_code_length is None, 'model: {}'.format(m)) + + # Build the Tensorflow graph corresponding to the model. + model.BuildGraph(binary_codes) + self.assertTrue(model.loss is not None, 'model: {}'.format(m)) + self.assertTrue(model.average_code_length is not None, + 'model: {}'.format(m)) + if model.train_op is None: + print('Model {} is not trainable'.format(m)) + + +if __name__ == '__main__': + tf.test.main() diff --git a/compression/entropy_coder/configs/gru_prime3/model_config.json b/compression/entropy_coder/configs/gru_prime3/model_config.json new file mode 100644 index 000000000..cf63a4c45 --- /dev/null +++ b/compression/entropy_coder/configs/gru_prime3/model_config.json @@ -0,0 +1,4 @@ +{ + "layer_count": 16, + "layer_depth": 32 +} diff --git a/compression/entropy_coder/configs/synthetic/input_config.json b/compression/entropy_coder/configs/synthetic/input_config.json new file mode 100644 index 000000000..18455e651 --- /dev/null +++ b/compression/entropy_coder/configs/synthetic/input_config.json @@ -0,0 +1,4 @@ +{ + "data": "/tmp/dataset/synthetic_dataset", + "unique_code_size": true +} diff --git a/compression/entropy_coder/configs/synthetic/model_config.json b/compression/entropy_coder/configs/synthetic/model_config.json new file mode 100644 index 000000000..c6f1f3e11 --- /dev/null +++ b/compression/entropy_coder/configs/synthetic/model_config.json @@ -0,0 +1,4 @@ +{ + "layer_depth": 2, + "layer_count": 8 +} diff --git a/compression/entropy_coder/configs/synthetic/train_config.json b/compression/entropy_coder/configs/synthetic/train_config.json new file mode 100644 index 000000000..79e4909fd --- /dev/null +++ b/compression/entropy_coder/configs/synthetic/train_config.json @@ -0,0 +1,6 @@ +{ + "batch_size": 4, + "learning_rate": 0.1, + "decay_rate": 0.9, + "samples_per_decay": 20000 +} diff --git a/compression/entropy_coder/core/code_loader.py b/compression/entropy_coder/core/code_loader.py new file mode 100644 index 000000000..47d947ce2 --- /dev/null +++ b/compression/entropy_coder/core/code_loader.py @@ -0,0 +1,73 @@ +# 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. +# ============================================================================== + +"""Load binary codes stored as tf.Example in a TFRecord table.""" + +import tensorflow as tf + + +def ReadFirstCode(dataset): + """Read the first example from a binary code RecordIO table.""" + for record in tf.python_io.tf_record_iterator(dataset): + tf_example = tf.train.Example() + tf_example.ParseFromString(record) + break + return tf_example + + +def LoadBinaryCode(input_config, batch_size): + """Load a batch of binary codes from a tf.Example dataset. + + Args: + input_config: An InputConfig proto containing the input configuration. + batch_size: Output batch size of examples. + + Returns: + A batched tensor of binary codes. + """ + data = input_config.data + + # TODO(damienv): Possibly use multiple files (instead of just one). + file_list = [data] + filename_queue = tf.train.string_input_producer(file_list, + capacity=4) + reader = tf.TFRecordReader() + _, values = reader.read(filename_queue) + + serialized_example = tf.reshape(values, shape=[1]) + serialized_features = { + 'code_shape': tf.FixedLenFeature([3], + dtype=tf.int64), + 'code': tf.VarLenFeature(tf.float32), + } + example = tf.parse_example(serialized_example, serialized_features) + + # 3D shape: height x width x binary_code_depth + z = example['code_shape'] + code_shape = tf.reshape(tf.cast(z, tf.int32), [3]) + # Un-flatten the binary codes. + code = tf.reshape(tf.sparse_tensor_to_dense(example['code']), code_shape) + + queue_size = 10 + queue = tf.PaddingFIFOQueue( + queue_size + 3 * batch_size, + dtypes=[code.dtype], + shapes=[[None, None, None]]) + enqueue_op = queue.enqueue([code]) + dequeue_code = queue.dequeue_many(batch_size) + queue_runner = tf.train.queue_runner.QueueRunner(queue, [enqueue_op]) + tf.add_to_collection(tf.GraphKeys.QUEUE_RUNNERS, queue_runner) + + return dequeue_code diff --git a/compression/entropy_coder/core/config_helper.py b/compression/entropy_coder/core/config_helper.py new file mode 100644 index 000000000..a7d949e32 --- /dev/null +++ b/compression/entropy_coder/core/config_helper.py @@ -0,0 +1,52 @@ +# 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. +# ============================================================================== + +"""Helper functions used in both train and inference.""" + +import json +import os.path + +import tensorflow as tf + + +def GetConfigString(config_file): + config_string = '' + if config_file is not None: + config_string = open(config_file).read() + return config_string + + +class InputConfig(object): + + def __init__(self, config_string): + config = json.loads(config_string) + self.data = config["data"] + self.unique_code_size = config["unique_code_size"] + + +class TrainConfig(object): + + def __init__(self, config_string): + config = json.loads(config_string) + self.batch_size = config["batch_size"] + self.learning_rate = config["learning_rate"] + self.decay_rate = config["decay_rate"] + self.samples_per_decay = config["samples_per_decay"] + + +def SaveConfig(directory, filename, config_string): + path = os.path.join(directory, filename) + with tf.gfile.Open(path, mode='w') as f: + f.write(config_string) diff --git a/compression/entropy_coder/core/entropy_coder_single.py b/compression/entropy_coder/core/entropy_coder_single.py new file mode 100644 index 000000000..40a1317c9 --- /dev/null +++ b/compression/entropy_coder/core/entropy_coder_single.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. +# ============================================================================== + +"""Compute the additional compression ratio after entropy coding.""" + +import io +import os + +import numpy as np +import tensorflow as tf + +import config_helper + +# pylint: disable=unused-import +from entropy_coder.all_models import all_models +# pylint: enable=unused-import +from entropy_coder.model import model_factory + + +# Checkpoint used to restore the model parameters. +tf.app.flags.DEFINE_string('checkpoint', None, + """Model checkpoint.""") + +# Model selection and configuration. +tf.app.flags.DEFINE_string('model', None, """Underlying encoder model.""") +tf.app.flags.DEFINE_string('model_config', None, + """Model config protobuf given as text file.""") + +# File holding the binary codes. +tf.flags.DEFINE_string('input_codes', None, 'Location of binary code file.') + +FLAGS = tf.flags.FLAGS + + +def main(_): + if (FLAGS.input_codes is None or FLAGS.model is None): + print ('\nUsage: python entropy_coder_single.py --model=progressive ' + '--model_config=model_config.json' + '--iteration=15\n\n') + return + + #if FLAGS.iteration < -1 or FLAGS.iteration > 15: + # print ('\n--iteration must be between 0 and 15 inclusive, or -1 to infer ' + # 'from file.\n') + # return + #iteration = FLAGS.iteration + + if not tf.gfile.Exists(FLAGS.input_codes): + print '\nInput codes not found.\n' + return + + with tf.gfile.FastGFile(FLAGS.input_codes, 'rb') as code_file: + contents = code_file.read() + loaded_codes = np.load(io.BytesIO(contents)) + assert ['codes', 'shape'] not in loaded_codes.files + loaded_shape = loaded_codes['shape'] + loaded_array = loaded_codes['codes'] + + # Unpack and recover code shapes. + unpacked_codes = np.reshape(np.unpackbits(loaded_array) + [:np.prod(loaded_shape)], + loaded_shape) + + numpy_int_codes = unpacked_codes.transpose([1, 2, 3, 0, 4]) + numpy_int_codes = numpy_int_codes.reshape([numpy_int_codes.shape[0], + numpy_int_codes.shape[1], + numpy_int_codes.shape[2], + -1]) + numpy_codes = numpy_int_codes.astype(np.float32) * 2.0 - 1.0 + + with tf.Graph().as_default() as graph: + # TF tensor to hold the binary codes to losslessly compress. + batch_size = 1 + codes = tf.placeholder(tf.float32, shape=numpy_codes.shape) + + # Create the entropy coder model. + global_step = None + optimizer = None + model = model_factory.GetModelRegistry().CreateModel(FLAGS.model) + model_config_string = config_helper.GetConfigString(FLAGS.model_config) + model.Initialize(global_step, optimizer, model_config_string) + model.BuildGraph(codes) + + saver = tf.train.Saver(sharded=True, keep_checkpoint_every_n_hours=12.0) + + with tf.Session(graph=graph) as sess: + # Initialize local variables. + sess.run(tf.local_variables_initializer()) + + # Restore model variables. + saver.restore(sess, FLAGS.checkpoint) + + tf_tensors = { + 'code_length': model.average_code_length + } + feed_dict = {codes: numpy_codes} + np_tensors = sess.run(tf_tensors, feed_dict=feed_dict) + + print('Additional compression ratio: {}'.format( + np_tensors['code_length'])) + + +if __name__ == '__main__': + tf.app.run() diff --git a/compression/entropy_coder/core/entropy_coder_train.py b/compression/entropy_coder/core/entropy_coder_train.py new file mode 100644 index 000000000..fd7266153 --- /dev/null +++ b/compression/entropy_coder/core/entropy_coder_train.py @@ -0,0 +1,184 @@ +# 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. +# ============================================================================== + +"""Train an entropy coder model.""" + +import time + +import tensorflow as tf + +import code_loader +import config_helper + +# pylint: disable=unused-import +from entropy_coder.all_models import all_models +# pylint: enable=unused-import +from entropy_coder.model import model_factory + + +FLAGS = tf.app.flags.FLAGS + +# Hardware resources configuration. +tf.app.flags.DEFINE_string('master', '', + """Name of the TensorFlow master to use.""") +tf.app.flags.DEFINE_string('train_dir', None, + """Directory where to write event logs.""") +tf.app.flags.DEFINE_integer('task', None, + """Task id of the replica running the training.""") +tf.app.flags.DEFINE_integer('ps_tasks', 0, """Number of tasks in the ps job. + If 0 no ps job is used.""") + +# Model selection and configuration. +tf.app.flags.DEFINE_string('model', None, """Underlying encoder model.""") +tf.app.flags.DEFINE_string('model_config', None, + """Model config protobuf given as text file.""") + +# Training data and parameters configuration. +tf.app.flags.DEFINE_string('input_config', None, + """Path to the training input config file.""") +tf.app.flags.DEFINE_string('train_config', None, + """Path to the training experiment config file.""") + + +def train(): + if FLAGS.train_dir is None: + raise ValueError('Parameter train_dir must be provided') + if FLAGS.task is None: + raise ValueError('Parameter task must be provided') + if FLAGS.model is None: + raise ValueError('Parameter model must be provided') + + input_config_string = config_helper.GetConfigString(FLAGS.input_config) + input_config = config_helper.InputConfig(input_config_string) + + # Training parameters. + train_config_string = config_helper.GetConfigString(FLAGS.train_config) + train_config = config_helper.TrainConfig(train_config_string) + + batch_size = train_config.batch_size + initial_learning_rate = train_config.learning_rate + decay_rate = train_config.decay_rate + samples_per_decay = train_config.samples_per_decay + + # Parameters for learning-rate decay. + # The formula is decay_rate ** floor(steps / decay_steps). + decay_steps = samples_per_decay / batch_size + decay_steps = max(decay_steps, 1) + + first_code = code_loader.ReadFirstCode(input_config.data) + first_code_height = ( + first_code.features.feature['code_shape'].int64_list.value[0]) + first_code_width = ( + first_code.features.feature['code_shape'].int64_list.value[1]) + max_bit_depth = ( + first_code.features.feature['code_shape'].int64_list.value[2]) + print('Maximum code depth: {}'.format(max_bit_depth)) + + with tf.Graph().as_default(): + ps_ops = ["Variable", "VariableV2", "AutoReloadVariable", "VarHandleOp"] + with tf.device(tf.train.replica_device_setter(FLAGS.ps_tasks, + ps_ops=ps_ops)): + codes = code_loader.LoadBinaryCode( + input_config=input_config, + batch_size=batch_size) + if input_config.unique_code_size: + print('Input code size: {} x {}'.format(first_code_height, + first_code_width)) + codes.set_shape( + [batch_size, first_code_height, first_code_width, max_bit_depth]) + else: + codes.set_shape([batch_size, None, None, max_bit_depth]) + codes_effective_shape = tf.shape(codes) + + global_step = tf.contrib.framework.create_global_step() + + # Apply learning-rate decay. + learning_rate = tf.train.exponential_decay( + learning_rate=initial_learning_rate, + global_step=global_step, + decay_steps=decay_steps, + decay_rate=decay_rate, + staircase=True) + tf.contrib.deprecated.scalar_summary('Learning Rate', learning_rate) + optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate, + epsilon=1.0) + + # Create the entropy coder model. + model = model_factory.GetModelRegistry().CreateModel(FLAGS.model) + model_config_string = config_helper.GetConfigString(FLAGS.model_config) + model.Initialize(global_step, optimizer, model_config_string) + model.BuildGraph(codes) + + summary_op = tf.summary.merge_all() + + # Verify that the model can actually be trained. + if model.train_op is None: + raise ValueError('Input model {} is not trainable'.format(FLAGS.model)) + + # We disable the summary thread run by Supervisor class by passing + # summary_op=None. We still pass save_summaries_secs because it is used by + # the global step counter thread. + is_chief = (FLAGS.task == 0) + sv = tf.train.Supervisor(logdir=FLAGS.train_dir, + is_chief=is_chief, + global_step=global_step, + # saver=model.saver, + summary_op=None, + save_summaries_secs=120, + save_model_secs=600, + recovery_wait_secs=30) + + sess = sv.PrepareSession(FLAGS.master) + sv.StartQueueRunners(sess) + + step = sess.run(global_step) + print('Trainer initial step: {}.'.format(step)) + + # Once everything has been setup properly, save the configs. + if is_chief: + config_helper.SaveConfig(FLAGS.train_dir, 'input_config.json', + input_config_string) + config_helper.SaveConfig(FLAGS.train_dir, 'model_config.json', + model_config_string) + config_helper.SaveConfig(FLAGS.train_dir, 'train_config.json', + train_config_string) + + # Train the model. + next_summary_time = time.time() + while not sv.ShouldStop(): + feed_dict = None + + # Once in a while, update the summaries on the chief worker. + if is_chief and next_summary_time < time.time(): + summary_str = sess.run(summary_op, feed_dict=feed_dict) + sv.SummaryComputed(sess, summary_str) + next_summary_time = time.time() + sv.save_summaries_secs + else: + tf_tensors = { + 'train': model.train_op, + 'code_length': model.average_code_length + } + np_tensors = sess.run(tf_tensors, feed_dict=feed_dict) + print np_tensors['code_length'] + + sv.Stop() + + +def main(argv=None): # pylint: disable=unused-argument + train() + + +if __name__ == '__main__': + tf.app.run() diff --git a/compression/entropy_coder/dataset/gen_synthetic_dataset.py b/compression/entropy_coder/dataset/gen_synthetic_dataset.py new file mode 100644 index 000000000..aa511b530 --- /dev/null +++ b/compression/entropy_coder/dataset/gen_synthetic_dataset.py @@ -0,0 +1,88 @@ +# 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. +# ============================================================================== + +"""Generate a synthetic dataset.""" + +import os + +import numpy as np +import tensorflow as tf + +import synthetic_model + + +FLAGS = tf.app.flags.FLAGS + +tf.app.flags.DEFINE_string( + 'dataset_dir', None, + """Directory where to write the dataset and the configs.""") +tf.app.flags.DEFINE_integer( + 'count', 1000, + """Number of samples to generate.""") + + +def int64_feature(values): + """Returns a TF-Feature of int64s. + + Args: + values: A scalar or list of values. + + Returns: + A TF-Feature. + """ + if not isinstance(values, (tuple, list)): + values = [values] + return tf.train.Feature(int64_list=tf.train.Int64List(value=values)) + + +def float_feature(values): + """Returns a TF-Feature of floats. + + Args: + values: A scalar of list of values. + + Returns: + A TF-Feature. + """ + if not isinstance(values, (tuple, list)): + values = [values] + return tf.train.Feature(float_list=tf.train.FloatList(value=values)) + + +def AddToTFRecord(code, tfrecord_writer): + example = tf.train.Example(features=tf.train.Features(feature={ + 'code_shape': int64_feature(code.shape), + 'code': float_feature(code.flatten().tolist()), + })) + tfrecord_writer.write(example.SerializeToString()) + + +def GenerateDataset(filename, count, code_shape): + with tf.python_io.TFRecordWriter(filename) as tfrecord_writer: + for _ in xrange(count): + code = synthetic_model.GenerateSingleCode(code_shape) + # Convert {0,1} codes to {-1,+1} codes. + code = 2.0 * code - 1.0 + AddToTFRecord(code, tfrecord_writer) + + +def main(argv=None): # pylint: disable=unused-argument + GenerateDataset(os.path.join(FLAGS.dataset_dir + '/synthetic_dataset'), + FLAGS.count, + [35, 48, 8]) + + +if __name__ == '__main__': + tf.app.run() diff --git a/compression/entropy_coder/dataset/gen_synthetic_single.py b/compression/entropy_coder/dataset/gen_synthetic_single.py new file mode 100644 index 000000000..b8c3821c3 --- /dev/null +++ b/compression/entropy_coder/dataset/gen_synthetic_single.py @@ -0,0 +1,72 @@ +# Copyright 2016 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. +# ============================================================================== + +"""Generate a single synthetic sample.""" + +import io +import os + +import numpy as np +import tensorflow as tf + +import synthetic_model + + +FLAGS = tf.app.flags.FLAGS + +tf.app.flags.DEFINE_string( + 'sample_filename', None, + """Output file to store the generated binary code.""") + + +def GenerateSample(filename, code_shape, layer_depth): + # {0, +1} binary codes. + # No conversion since the output file is expected to store + # codes using {0, +1} codes (and not {-1, +1}). + code = synthetic_model.GenerateSingleCode(code_shape) + code = np.round(code) + + # Reformat the code so as to be compatible with what is generated + # by the image encoder. + # The image encoder generates a tensor of size: + # iteration_count x batch_size x height x width x iteration_depth. + # Here: batch_size = 1 + if code_shape[-1] % layer_depth != 0: + raise ValueError('Number of layers is not an integer') + height = code_shape[0] + width = code_shape[1] + code = code.reshape([1, height, width, -1, layer_depth]) + code = np.transpose(code, [3, 0, 1, 2, 4]) + + int_codes = code.astype(np.int8) + exported_codes = np.packbits(int_codes.reshape(-1)) + + output = io.BytesIO() + np.savez_compressed(output, shape=int_codes.shape, codes=exported_codes) + with tf.gfile.FastGFile(filename, 'wb') as code_file: + code_file.write(output.getvalue()) + + +def main(argv=None): # pylint: disable=unused-argument + # Note: the height and the width is different from the training dataset. + # The main purpose is to show that the entropy coder model is fully + # convolutional and can be used on any image size. + layer_depth = 2 + GenerateSample(FLAGS.sample_filename, [31, 36, 8], layer_depth) + + +if __name__ == '__main__': + tf.app.run() + diff --git a/compression/entropy_coder/dataset/synthetic_model.py b/compression/entropy_coder/dataset/synthetic_model.py new file mode 100644 index 000000000..481120838 --- /dev/null +++ b/compression/entropy_coder/dataset/synthetic_model.py @@ -0,0 +1,74 @@ +# Copyright 2016 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. +# ============================================================================== + +"""Binary code sample generator.""" + +import numpy as np + + +_CRC_LINE = [ + [0, 1, 0], + [1, 1, 0], + [1, 0, 0] +] + +_CRC_DEPTH = [1, 1, 0, 1] + + +def ComputeLineCrc(code, width, y, x, d): + crc = 0 + for dy in xrange(len(_CRC_LINE)): + i = y - 1 - dy + if i < 0: + continue + for dx in xrange(len(_CRC_LINE[dy])): + j = x - 2 + dx + if j < 0 or j >= width: + continue + crc += 1 if (code[i, j, d] != _CRC_LINE[dy][dx]) else 0 + return crc + + +def ComputeDepthCrc(code, y, x, d): + crc = 0 + for delta in xrange(len(_CRC_DEPTH)): + k = d - 1 - delta + if k < 0: + continue + crc += 1 if (code[y, x, k] != _CRC_DEPTH[delta]) else 0 + return crc + + +def GenerateSingleCode(code_shape): + code = np.zeros(code_shape, dtype=np.int) + + keep_value_proba = 0.8 + + height = code_shape[0] + width = code_shape[1] + depth = code_shape[2] + + for d in xrange(depth): + for y in xrange(height): + for x in xrange(width): + v1 = ComputeLineCrc(code, width, y, x, d) + v2 = ComputeDepthCrc(code, y, x, d) + v = 1 if (v1 + v2 >= 6) else 0 + if np.random.rand() < keep_value_proba: + code[y, x, d] = v + else: + code[y, x, d] = 1 - v + + return code diff --git a/compression/entropy_coder/lib/__init__.py b/compression/entropy_coder/lib/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/compression/entropy_coder/lib/block_base.py b/compression/entropy_coder/lib/block_base.py new file mode 100644 index 000000000..615dff828 --- /dev/null +++ b/compression/entropy_coder/lib/block_base.py @@ -0,0 +1,258 @@ +# 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 class for Tensorflow building blocks.""" + +import collections +import contextlib +import itertools + +import tensorflow as tf + +_block_stacks = collections.defaultdict(lambda: []) + + +class BlockBase(object): + """Base class for transform wrappers of Tensorflow. + + To implement a Tensorflow transform block, inherit this class. + + 1. To create a variable, use NewVar() method. Do not overload this method! + For example, use as follows. + a_variable = self.NewVar(initial_value) + + 2. All Tensorflow-related code must be done inside 'with self._BlockScope().' + Otherwise, name scoping and block hierarchy will not work. An exception + is _Apply() method, which is already called inside the context manager + by __call__() method. + + 3. Override and implement _Apply() method. This method is called by + __call__() method. + + The users would use blocks like the following. + nn1 = NN(128, bias=Bias(0), act=tf.nn.relu) + y = nn1(x) + + Some things to consider. + + - Use lazy-initialization if possible. That is, initialize at first Apply() + rather than at __init__(). + + Note: if needed, the variables can be created on a specific parameter + server by creating blocks in a scope like: + with g.device(device): + linear = Linear(...) + """ + + def __init__(self, name): + self._variables = [] + self._subblocks = [] + self._called = False + + # Intentionally distinguishing empty string and None. + # If name is an empty string, then do not use name scope. + self.name = name if name is not None else self.__class__.__name__ + self._graph = tf.get_default_graph() + + if self.name: + # Capture the scope string at the init time. + with self._graph.name_scope(self.name) as scope: + self._scope_str = scope + else: + self._scope_str = '' + + # Maintain hierarchy structure of blocks. + self._stack = _block_stacks[self._graph] + if self.__class__ is BlockBase: + # This code is only executed to create the root, which starts in the + # initialized state. + assert not self._stack + self._parent = None + self._called = True # The root is initialized. + return + + # Create a fake root if a root is not already present. + if not self._stack: + self._stack.append(BlockBase('NoOpRoot')) + + self._parent = self._stack[-1] + self._parent._subblocks.append(self) # pylint: disable=protected-access + + def __repr__(self): + return '"{}" ({})'.format(self._scope_str, self.__class__.__name__) + + @contextlib.contextmanager + def _OptionalNameScope(self, scope_str): + if scope_str: + with self._graph.name_scope(scope_str): + yield + else: + yield + + @contextlib.contextmanager + def _BlockScope(self): + """Context manager that handles graph, namescope, and nested blocks.""" + self._stack.append(self) + + try: + with self._graph.as_default(): + with self._OptionalNameScope(self._scope_str): + yield self + finally: # Pop from the stack no matter exception is raised or not. + # The following line is executed when leaving 'with self._BlockScope()' + self._stack.pop() + + def __call__(self, *args, **kwargs): + assert self._stack is _block_stacks[self._graph] + + with self._BlockScope(): + ret = self._Apply(*args, **kwargs) + + self._called = True + return ret + + def _Apply(self, *args, **kwargs): + """Implementation of __call__().""" + raise NotImplementedError() + + # Redirect all variable creation to this single function, so that we can + # switch to better variable creation scheme. + def NewVar(self, value, **kwargs): + """Creates a new variable. + + This function creates a variable, then returns a local copy created by + Identity operation. To get the Variable class object, use LookupRef() + method. + + Note that each time Variable class object is used as an input to an + operation, Tensorflow will create a new Send/Recv pair. This hurts + performance. + + If not for assign operations, use the local copy returned by this method. + + Args: + value: Initialization value of the variable. The shape and the data type + of the variable is determined by this initial value. + **kwargs: Extra named arguments passed to Variable.__init__(). + + Returns: + A local copy of the new variable. + """ + v = tf.Variable(value, **kwargs) + + self._variables.append(v) + return v + + @property + def initialized(self): + """Returns bool if the block is initialized. + + By default, BlockBase assumes that a block is initialized when __call__() + is executed for the first time. If this is an incorrect assumption for some + subclasses, override this property in those subclasses. + + Returns: + True if initialized, False otherwise. + """ + return self._called + + def AssertInitialized(self): + """Asserts initialized property.""" + if not self.initialized: + raise RuntimeError('{} has not been initialized.'.format(self)) + + def VariableList(self): + """Returns the list of all tensorflow variables used inside this block.""" + variables = list(itertools.chain( + itertools.chain.from_iterable( + t.VariableList() for t in self._subblocks), + self._VariableList())) + return variables + + def _VariableList(self): + """Returns the list of all tensorflow variables owned by this block.""" + self.AssertInitialized() + return self._variables + + def CreateWeightLoss(self): + """Returns L2 loss list of (almost) all variables used inside this block. + + When this method needs to be overridden, there are two choices. + + 1. Override CreateWeightLoss() to change the weight loss of all variables + that belong to this block, both directly and indirectly. + 2. Override _CreateWeightLoss() to change the weight loss of all + variables that directly belong to this block but not to the sub-blocks. + + Returns: + A Tensor object or None. + """ + losses = list(itertools.chain( + itertools.chain.from_iterable( + t.CreateWeightLoss() for t in self._subblocks), + self._CreateWeightLoss())) + return losses + + def _CreateWeightLoss(self): + """Returns weight loss list of variables that belong to this block.""" + self.AssertInitialized() + with self._BlockScope(): + return [tf.nn.l2_loss(v) for v in self._variables] + + def CreateUpdateOps(self): + """Creates update operations for this block and its sub-blocks.""" + ops = list(itertools.chain( + itertools.chain.from_iterable( + t.CreateUpdateOps() for t in self._subblocks), + self._CreateUpdateOps())) + return ops + + def _CreateUpdateOps(self): + """Creates update operations for this block.""" + self.AssertInitialized() + return [] + + def MarkAsNonTrainable(self): + """Mark all the variables of this block as non-trainable. + + All the variables owned directly or indirectly (through subblocks) are + marked as non trainable. + + This function along with CheckpointInitOp can be used to load a pretrained + model that consists in only one part of the whole graph. + """ + assert self._called + + all_variables = self.VariableList() + collection = tf.get_collection_ref(tf.GraphKeys.TRAINABLE_VARIABLES) + for v in all_variables: + if v in collection: + collection.remove(v) + + +def CreateWeightLoss(): + """Returns all weight losses from the blocks in the graph.""" + stack = _block_stacks[tf.get_default_graph()] + if not stack: + return [] + return stack[0].CreateWeightLoss() + + +def CreateBlockUpdates(): + """Combines all updates from the blocks in the graph.""" + stack = _block_stacks[tf.get_default_graph()] + if not stack: + return [] + return stack[0].CreateUpdateOps() diff --git a/compression/entropy_coder/lib/block_util.py b/compression/entropy_coder/lib/block_util.py new file mode 100644 index 000000000..957f8d603 --- /dev/null +++ b/compression/entropy_coder/lib/block_util.py @@ -0,0 +1,100 @@ +# 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. +# ============================================================================== + +"""Utility functions for blocks.""" + +from __future__ import division +from __future__ import unicode_literals + +import math + +import numpy as np +import tensorflow as tf + + +class RsqrtInitializer(object): + """Gaussian initializer with standard deviation 1/sqrt(n). + + Note that tf.truncated_normal is used internally. Therefore any random sample + outside two-sigma will be discarded and re-sampled. + """ + + def __init__(self, dims=(0,), **kwargs): + """Creates an initializer. + + Args: + dims: Dimension(s) index to compute standard deviation: + 1.0 / sqrt(product(shape[dims])) + **kwargs: Extra keyword arguments to pass to tf.truncated_normal. + """ + if isinstance(dims, (int, long)): + self._dims = [dims] + else: + self._dims = dims + self._kwargs = kwargs + + def __call__(self, shape, dtype): + stddev = 1.0 / np.sqrt(np.prod([shape[x] for x in self._dims])) + return tf.truncated_normal( + shape=shape, dtype=dtype, stddev=stddev, **self._kwargs) + + +class RectifierInitializer(object): + """Gaussian initializer with standard deviation sqrt(2/fan_in). + + Note that tf.random_normal is used internally to ensure the expected weight + distribution. This is intended to be used with ReLU activations, specially + in ResNets. + + For details please refer to: + Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet + Classification + """ + + def __init__(self, dims=(0,), scale=2.0, **kwargs): + """Creates an initializer. + + Args: + dims: Dimension(s) index to compute standard deviation: + sqrt(scale / product(shape[dims])) + scale: A constant scaling for the initialization used as + sqrt(scale / product(shape[dims])). + **kwargs: Extra keyword arguments to pass to tf.truncated_normal. + """ + if isinstance(dims, (int, long)): + self._dims = [dims] + else: + self._dims = dims + self._kwargs = kwargs + self._scale = scale + + def __call__(self, shape, dtype): + stddev = np.sqrt(self._scale / np.prod([shape[x] for x in self._dims])) + return tf.random_normal( + shape=shape, dtype=dtype, stddev=stddev, **self._kwargs) + + +class GaussianInitializer(object): + """Gaussian initializer with a given standard deviation. + + Note that tf.truncated_normal is used internally. Therefore any random sample + outside two-sigma will be discarded and re-sampled. + """ + + def __init__(self, stddev=1.0): + self._stddev = stddev + + def __call__(self, shape, dtype): + return tf.truncated_normal(shape=shape, dtype=dtype, stddev=self._stddev) diff --git a/compression/entropy_coder/lib/blocks.py b/compression/entropy_coder/lib/blocks.py new file mode 100644 index 000000000..002384eb0 --- /dev/null +++ b/compression/entropy_coder/lib/blocks.py @@ -0,0 +1,24 @@ +# 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. +# ============================================================================== + +from block_base import * +from block_util import * +from blocks_binarizer import * +from blocks_entropy_coding import * +from blocks_lstm import * +from blocks_masked_conv2d import * +from blocks_masked_conv2d_lstm import * +from blocks_operator import * +from blocks_std import * diff --git a/compression/entropy_coder/lib/blocks_binarizer.py b/compression/entropy_coder/lib/blocks_binarizer.py new file mode 100644 index 000000000..820673161 --- /dev/null +++ b/compression/entropy_coder/lib/blocks_binarizer.py @@ -0,0 +1,35 @@ +# 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. +# ============================================================================== + +"""Activation and weight binarizer implementations.""" + +import math + +import numpy as np +import tensorflow as tf + + +def ConvertSignCodeToZeroOneCode(x): + """Conversion from codes {-1, +1} to codes {0, 1}.""" + return 0.5 * (x + 1.0) + + +def ConvertZeroOneCodeToSignCode(x): + """Convert from codes {0, 1} to codes {-1, +1}.""" + return 2.0 * x - 1.0 + + +def CheckZeroOneCode(x): + return tf.reduce_all(tf.equal(x * (x - 1.0), 0)) diff --git a/compression/entropy_coder/lib/blocks_entropy_coding.py b/compression/entropy_coder/lib/blocks_entropy_coding.py new file mode 100644 index 000000000..6ee5d9792 --- /dev/null +++ b/compression/entropy_coder/lib/blocks_entropy_coding.py @@ -0,0 +1,49 @@ +# 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. +# ============================================================================== + +"""Set of blocks related to entropy coding.""" + +import math + +import tensorflow as tf + +import block_base + +# pylint does not recognize block_base.BlockBase.__call__(). +# pylint: disable=not-callable + + +class CodeLength(block_base.BlockBase): + """Theoretical bound for a code length given a probability distribution. + """ + + def __init__(self, name=None): + super(CodeLength, self).__init__(name) + + def _Apply(self, c, p): + """Theoretical bound of the coded length given a probability distribution. + + Args: + c: The binary codes. Belong to {0, 1}. + p: The probability of: P(code==+1) + + Returns: + The average code length. + Note: the average code length can be greater than 1 bit (e.g. when + encoding the least likely symbol). + """ + entropy = ((1.0 - c) * tf.log(1.0 - p) + c * tf.log(p)) / (-math.log(2)) + entropy = tf.reduce_mean(entropy) + return entropy diff --git a/compression/entropy_coder/lib/blocks_entropy_coding_test.py b/compression/entropy_coder/lib/blocks_entropy_coding_test.py new file mode 100644 index 000000000..5209865f5 --- /dev/null +++ b/compression/entropy_coder/lib/blocks_entropy_coding_test.py @@ -0,0 +1,56 @@ +# 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 basic tensorflow blocks_entropy_coding.""" + +from __future__ import division +from __future__ import unicode_literals + +import math + +import numpy as np +import tensorflow as tf + +import blocks_entropy_coding + + +class BlocksEntropyCodingTest(tf.test.TestCase): + + def testCodeLength(self): + shape = [2, 4] + proba_feed = [[0.65, 0.25, 0.70, 0.10], + [0.28, 0.20, 0.44, 0.54]] + symbol_feed = [[1.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0]] + mean_code_length = - ( + (math.log(0.65) + math.log(0.75) + math.log(0.70) + math.log(0.90) + + math.log(0.72) + math.log(0.80) + math.log(0.56) + math.log(0.54)) / + math.log(2.0)) / (shape[0] * shape[1]) + + symbol = tf.placeholder(dtype=tf.float32, shape=shape) + proba = tf.placeholder(dtype=tf.float32, shape=shape) + code_length_calculator = blocks_entropy_coding.CodeLength() + code_length = code_length_calculator(symbol, proba) + + with self.test_session(): + tf.global_variables_initializer().run() + code_length_eval = code_length.eval( + feed_dict={symbol: symbol_feed, proba: proba_feed}) + + self.assertAllClose(mean_code_length, code_length_eval) + + +if __name__ == '__main__': + tf.test.main() diff --git a/compression/entropy_coder/lib/blocks_lstm.py b/compression/entropy_coder/lib/blocks_lstm.py new file mode 100644 index 000000000..6e474e3e3 --- /dev/null +++ b/compression/entropy_coder/lib/blocks_lstm.py @@ -0,0 +1,263 @@ +# 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. +# ============================================================================== + +"""Blocks of LSTM and its variants.""" + +import numpy as np +import tensorflow as tf + +import block_base +import block_util +import blocks_std + +# pylint does not recognize block_base.BlockBase.__call__(). +# pylint: disable=not-callable + + +def LSTMBiasInit(shape, dtype): + """Returns ones for forget-gate, and zeros for the others.""" + shape = np.array(shape) + + # Check internal consistencies. + assert shape.shape == (1,), shape + assert shape[0] % 4 == 0, shape + + n = shape[0] // 4 + ones = tf.fill([n], tf.constant(1, dtype=dtype)) + zeros = tf.fill([3 * n], tf.constant(0, dtype=dtype)) + return tf.concat([ones, zeros], 0) + + +class LSTMBase(block_base.BlockBase): + """Base class for LSTM implementations. + + These LSTM implementations use the pattern found in [1]. No peephole + connection, i.e., cell content is not used in recurrence computation. + Hidden units are also output units. + + [1] Zaremba, Sutskever, Vinyals. Recurrent Neural Network Regularization, + 2015. arxiv:1409.2329. + """ + + def __init__(self, output_shape, name): + """Initializes LSTMBase class object. + + Args: + output_shape: List representing the LSTM output shape. This argument + does not include batch dimension. For example, if the LSTM output has + shape [batch, depth], then pass [depth]. + name: Name of this block. + """ + super(LSTMBase, self).__init__(name) + + with self._BlockScope(): + self._output_shape = [None] + list(output_shape) + self._hidden = None + self._cell = None + + @property + def hidden(self): + """Returns the hidden units of this LSTM.""" + return self._hidden + + @hidden.setter + def hidden(self, value): + """Assigns to the hidden units of this LSTM. + + Args: + value: The new value for the hidden units. If None, the hidden units are + considered to be filled with zeros. + """ + if value is not None: + value.get_shape().assert_is_compatible_with(self._output_shape) + self._hidden = value + + @property + def cell(self): + """Returns the cell units of this LSTM.""" + return self._cell + + @cell.setter + def cell(self, value): + """Assigns to the cell units of this LSTM. + + Args: + value: The new value for the cell units. If None, the cell units are + considered to be filled with zeros. + """ + if value is not None: + value.get_shape().assert_is_compatible_with(self._output_shape) + self._cell = value + + # Consider moving bias terms to the base, and require this method to be + # linear. + def _TransformInputs(self, _): + """Transforms the input units to (4 * depth) units. + + The forget-gate, input-gate, output-gate, and cell update is computed as + f, i, j, o = T(h) + R(x) + where h is hidden units, x is input units, and T, R are transforms of + h, x, respectively. + + This method implements R. Note that T is strictly linear, so if LSTM is + going to use bias, this method must include the bias to the transformation. + + Subclasses must implement this method. See _Apply() for more details. + """ + raise NotImplementedError() + + def _TransformHidden(self, _): + """Transforms the hidden units to (4 * depth) units. + + The forget-gate, input-gate, output-gate, and cell update is computed as + f, i, j, o = T(h) + R(x) + where h is hidden units, x is input units, and T, R are transforms of + h, x, respectively. + + This method implements T in the equation. The method must implement a + strictly linear transformation. For example, it may use MatMul or Conv2D, + but must not add bias. This is because when hidden units are zeros, then + the LSTM implementation will skip calling this method, instead of passing + zeros to this function. + + Subclasses must implement this method. See _Apply() for more details. + """ + raise NotImplementedError() + + def _Apply(self, *args): + xtransform = self._TransformInputs(*args) + depth_axis = len(self._output_shape) - 1 + + if self.hidden is not None: + htransform = self._TransformHidden(self.hidden) + f, i, j, o = tf.split( + value=htransform + xtransform, num_or_size_splits=4, axis=depth_axis) + else: + f, i, j, o = tf.split( + value=xtransform, num_or_size_splits=4, axis=depth_axis) + + if self.cell is not None: + self.cell = tf.sigmoid(f) * self.cell + tf.sigmoid(i) * tf.tanh(j) + else: + self.cell = tf.sigmoid(i) * tf.tanh(j) + + self.hidden = tf.sigmoid(o) * tf.tanh(self.cell) + return self.hidden + + +class LSTM(LSTMBase): + """Efficient LSTM implementation used in [1]. + + [1] Zaremba, Sutskever, Vinyals. Recurrent Neural Network Regularization, + 2015. arxiv:1409.2329. + """ + + def __init__(self, + depth, + bias=LSTMBiasInit, + initializer=block_util.RsqrtInitializer(), + name=None): + super(LSTM, self).__init__([depth], name) + + with self._BlockScope(): + self._depth = depth + self._nn = blocks_std.NN( + 4 * depth, bias=bias, act=None, initializer=initializer) + self._hidden_linear = blocks_std.Linear( + 4 * depth, initializer=initializer) + + def _TransformInputs(self, *args): + return self._nn(*args) + + def _TransformHidden(self, h): + return self._hidden_linear(h) + + +class Conv2DLSTM(LSTMBase): + """Convolutional LSTM implementation with optimizations inspired by [1]. + + Note that when using the batch normalization feature, the bias initializer + will not be used, since BN effectively cancels its effect out. + + [1] Zaremba, Sutskever, Vinyals. Recurrent Neural Network Regularization, + 2015. arxiv:1409.2329. + """ + + def __init__(self, + depth, + filter_size, + hidden_filter_size, + strides, + padding, + bias=LSTMBiasInit, + initializer=block_util.RsqrtInitializer(dims=(0, 1, 2)), + use_moving_average=False, + name=None): + super(Conv2DLSTM, self).__init__([None, None, depth], name) + self._iter = 0 + + with self._BlockScope(): + self._input_conv = blocks_std.Conv2D( + 4 * depth, + filter_size, + strides, + padding, + bias=None, + act=None, + initializer=initializer, + name='input_conv2d') + + self._hidden_conv = blocks_std.Conv2D( + 4 * depth, + hidden_filter_size, + [1, 1], + 'SAME', + bias=None, + act=None, + initializer=initializer, + name='hidden_conv2d') + + if bias is not None: + self._bias = blocks_std.BiasAdd(bias, name='biases') + else: + self._bias = blocks_std.PassThrough() + + def _TransformInputs(self, x): + return self._bias(self._input_conv(x)) + + def _TransformHidden(self, h): + return self._hidden_conv(h) + + def _Apply(self, *args): + xtransform = self._TransformInputs(*args) + depth_axis = len(self._output_shape) - 1 + + if self.hidden is not None: + htransform = self._TransformHidden(self.hidden) + f, i, j, o = tf.split( + value=htransform + xtransform, num_or_size_splits=4, axis=depth_axis) + else: + f, i, j, o = tf.split( + value=xtransform, num_or_size_splits=4, axis=depth_axis) + + if self.cell is not None: + self.cell = tf.sigmoid(f) * self.cell + tf.sigmoid(i) * tf.tanh(j) + else: + self.cell = tf.sigmoid(i) * tf.tanh(j) + + self.hidden = tf.sigmoid(o) * tf.tanh(self.cell) + + self._iter += 1 + return self.hidden diff --git a/compression/entropy_coder/lib/blocks_lstm_test.py b/compression/entropy_coder/lib/blocks_lstm_test.py new file mode 100644 index 000000000..03c32dc13 --- /dev/null +++ b/compression/entropy_coder/lib/blocks_lstm_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 LSTM tensorflow blocks.""" +from __future__ import division + +import numpy as np +import tensorflow as tf + +import block_base +import blocks_std +import blocks_lstm + + +class BlocksLSTMTest(tf.test.TestCase): + + def CheckUnary(self, y, op_type): + self.assertEqual(op_type, y.op.type) + self.assertEqual(1, len(y.op.inputs)) + return y.op.inputs[0] + + def CheckBinary(self, y, op_type): + self.assertEqual(op_type, y.op.type) + self.assertEqual(2, len(y.op.inputs)) + return y.op.inputs + + def testLSTM(self): + lstm = blocks_lstm.LSTM(10) + lstm.hidden = tf.zeros(shape=[10, 10], dtype=tf.float32) + lstm.cell = tf.zeros(shape=[10, 10], dtype=tf.float32) + x = tf.placeholder(dtype=tf.float32, shape=[10, 11]) + y = lstm(x) + + o, tanhc = self.CheckBinary(y, 'Mul') + self.assertEqual(self.CheckUnary(o, 'Sigmoid').name, 'LSTM/split:3') + + self.assertIs(lstm.cell, self.CheckUnary(tanhc, 'Tanh')) + fc, ij = self.CheckBinary(lstm.cell, 'Add') + + f, _ = self.CheckBinary(fc, 'Mul') + self.assertEqual(self.CheckUnary(f, 'Sigmoid').name, 'LSTM/split:0') + + i, j = self.CheckBinary(ij, 'Mul') + self.assertEqual(self.CheckUnary(i, 'Sigmoid').name, 'LSTM/split:1') + j = self.CheckUnary(j, 'Tanh') + self.assertEqual(j.name, 'LSTM/split:2') + + def testLSTMBiasInit(self): + lstm = blocks_lstm.LSTM(9) + x = tf.placeholder(dtype=tf.float32, shape=[15, 7]) + lstm(x) + b = lstm._nn._bias + + with self.test_session(): + tf.global_variables_initializer().run() + bias_var = b._bias.eval() + + comp = ([1.0] * 9) + ([0.0] * 27) + self.assertAllEqual(bias_var, comp) + + def testConv2DLSTM(self): + lstm = blocks_lstm.Conv2DLSTM(depth=10, + filter_size=[1, 1], + hidden_filter_size=[1, 1], + strides=[1, 1], + padding='SAME') + lstm.hidden = tf.zeros(shape=[10, 11, 11, 10], dtype=tf.float32) + lstm.cell = tf.zeros(shape=[10, 11, 11, 10], dtype=tf.float32) + x = tf.placeholder(dtype=tf.float32, shape=[10, 11, 11, 1]) + y = lstm(x) + + o, tanhc = self.CheckBinary(y, 'Mul') + self.assertEqual(self.CheckUnary(o, 'Sigmoid').name, 'Conv2DLSTM/split:3') + + self.assertIs(lstm.cell, self.CheckUnary(tanhc, 'Tanh')) + fc, ij = self.CheckBinary(lstm.cell, 'Add') + + f, _ = self.CheckBinary(fc, 'Mul') + self.assertEqual(self.CheckUnary(f, 'Sigmoid').name, 'Conv2DLSTM/split:0') + + i, j = self.CheckBinary(ij, 'Mul') + self.assertEqual(self.CheckUnary(i, 'Sigmoid').name, 'Conv2DLSTM/split:1') + j = self.CheckUnary(j, 'Tanh') + self.assertEqual(j.name, 'Conv2DLSTM/split:2') + + def testConv2DLSTMBiasInit(self): + lstm = blocks_lstm.Conv2DLSTM(9, 1, 1, [1, 1], 'SAME') + x = tf.placeholder(dtype=tf.float32, shape=[1, 7, 7, 7]) + lstm(x) + b = lstm._bias + + with self.test_session(): + tf.global_variables_initializer().run() + bias_var = b._bias.eval() + + comp = ([1.0] * 9) + ([0.0] * 27) + self.assertAllEqual(bias_var, comp) + + +if __name__ == '__main__': + tf.test.main() diff --git a/compression/entropy_coder/lib/blocks_masked_conv2d.py b/compression/entropy_coder/lib/blocks_masked_conv2d.py new file mode 100644 index 000000000..395af3349 --- /dev/null +++ b/compression/entropy_coder/lib/blocks_masked_conv2d.py @@ -0,0 +1,225 @@ +# 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. +# ============================================================================== + +"""Define some typical masked 2D convolutions.""" + +import numpy as np +import tensorflow as tf + +import block_util +import blocks_std + +# pylint does not recognize block_base.BlockBase.__call__(). +# pylint: disable=not-callable + + +class RasterScanConv2D(blocks_std.Conv2DBase): + """Conv2D with no dependency on future pixels (in raster scan order). + + For example, assuming a 5 x 5 kernel, the kernel is applied a spatial mask: + T T T T T + T T T T T + T T x F F + F F F F F + F F F F F + where 'T' are pixels which are available when computing the convolution + for pixel 'x'. All the pixels marked with 'F' are not available. + 'x' itself is not available if strict_order is True, otherwise, it is + available. + """ + + def __init__(self, depth, filter_size, strides, padding, + strict_order=True, + bias=None, act=None, initializer=None, name=None): + super(RasterScanConv2D, self).__init__( + depth, filter_size, strides, padding, bias, act, name=name) + + if (filter_size[0] % 2) != 1 or (filter_size[1] % 2) != 1: + raise ValueError('Kernel size should be odd.') + + with self._BlockScope(): + if initializer is None: + initializer = block_util.RsqrtInitializer(dims=(0, 1, 2)) + self._initializer = initializer + self._strict_order = strict_order + + def _CreateKernel(self, shape, dtype): + init = self._initializer(shape, dtype) + kernel = self.NewVar(init) + + mask = np.ones(shape[:2], dtype=dtype.as_numpy_dtype) + center = shape[:2] // 2 + mask[center[0] + 1:, :] = 0 + if not self._strict_order: + mask[center[0], center[1] + 1:] = 0 + else: + mask[center[0], center[1]:] = 0 + mask = mask.reshape(mask.shape + (1, 1)) + + return tf.convert_to_tensor(mask, dtype) * kernel + + +class DepthOrderConv2D(blocks_std.Conv2DBase): + """Conv2D with no dependency on higher depth dimensions. + + More precisely, the output depth #n has only dependencies on input depths #k + for k < n (if strict_order is True) or for k <= n (if strict_order is False). + """ + + def __init__(self, depth, filter_size, strides, padding, + strict_order=True, + bias=None, act=None, initializer=None, name=None): + super(DepthOrderConv2D, self).__init__( + depth, filter_size, strides, padding, bias, act, name=name) + + with self._BlockScope(): + if initializer is None: + initializer = block_util.RsqrtInitializer(dims=(0, 1, 2)) + self._initializer = initializer + self._strict_order = strict_order + + def _CreateKernel(self, shape, dtype): + init = self._initializer(shape, dtype) + kernel = self.NewVar(init) + + mask = np.ones(shape[2:], dtype=dtype.as_numpy_dtype) + depth_output = shape[3] + for d in xrange(depth_output): + if self._strict_order: + mask[d:, d] = 0 + else: + mask[d + 1:, d] = 0 + mask = mask.reshape((1, 1) + mask.shape) + + return tf.convert_to_tensor(mask, dtype) * kernel + + +class GroupRasterScanConv2D(blocks_std.Conv2DBase): + """Conv2D with no dependency on future pixels (in raster scan order). + + This version only introduces dependencies on previous pixels in raster scan + order. It can also introduce some dependencies on previous depth positions + of the current pixel (current pixel = center pixel of the kernel) in the + following way: + the depth dimension of the input is split into Ki groups of size + |input_group_size|, the output dimension is split into Ko groups of size + |output_group_size| (usually Ki == Ko). Each output group ko of the current + pixel position can only depend on previous input groups ki + (i.e. ki < ko if strict_order is True or ki <= ko if strict_order is False). + + Notes: + - Block RasterScanConv2D is a special case of GroupRasterScanConv2D + where Ki == Ko == 1 (i.e. input_group_size == input_depth and + output_group_size == output_depth). + - For 1x1 convolution, block DepthOrderConv2D is a special case of + GroupRasterScanConv2D where input_group_size == 1 and + output_group_size == 1. + """ + + def __init__(self, depth, filter_size, strides, padding, + strict_order=True, + input_group_size=1, + output_group_size=1, + bias=None, act=None, initializer=None, name=None): + super(GroupRasterScanConv2D, self).__init__( + depth, filter_size, strides, padding, bias, act, name=name) + + if (filter_size[0] % 2) != 1 or (filter_size[1] % 2) != 1: + raise ValueError('Kernel size should be odd.') + + with self._BlockScope(): + if initializer is None: + initializer = block_util.RsqrtInitializer(dims=(0, 1, 2)) + self._initializer = initializer + self._input_group_size = input_group_size + self._output_group_size = output_group_size + self._strict_order = strict_order + + if depth % self._output_group_size != 0: + raise ValueError( + 'Invalid depth group size: {} for depth {}'.format( + self._output_group_size, depth)) + self._output_group_count = depth // self._output_group_size + + def _CreateKernel(self, shape, dtype): + init = self._initializer(shape, dtype) + kernel = self.NewVar(init) + + depth_input = shape[2] + if depth_input % self._input_group_size != 0: + raise ValueError( + 'Invalid depth group size: {} for depth {}'.format( + self._input_group_size, depth_input)) + input_group_count = depth_input // self._input_group_size + output_group_count = self._output_group_count + + # Set the mask to 0 for future pixels in raster scan order. + center = shape[:2] // 2 + mask = np.ones([shape[0], shape[1], + input_group_count, self._input_group_size, + output_group_count, self._output_group_size], + dtype=dtype.as_numpy_dtype) + mask[center[0] + 1:, :, :, :, :, :] = 0 + mask[center[0], center[1] + 1:, :, :, :, :] = 0 + + # Adjust the mask for the current position (the center position). + depth_output = shape[3] + for d in xrange(output_group_count): + mask[center[0], center[1], d + 1:, :, d:d + 1, :] = 0 + if self._strict_order: + mask[center[0], center[1], d, :, d:d + 1, :] = 0 + + mask = mask.reshape([shape[0], shape[1], depth_input, depth_output]) + return tf.convert_to_tensor(mask, dtype) * kernel + + +class InFillingConv2D(blocks_std.Conv2DBase): + """Conv2D with kernel having no dependency on the current pixel. + + For example, assuming a 5 x 5 kernel, the kernel is applied a spatial mask: + T T T T T + T T T T T + T T x T T + T T T T T + T T T T T + where 'T' marks a pixel which is available when computing the convolution + for pixel 'x'. 'x' itself is not available. + """ + + def __init__(self, depth, filter_size, strides, padding, + bias=None, act=None, initializer=None, name=None): + super(InFillingConv2D, self).__init__( + depth, filter_size, strides, padding, bias, act, name=name) + + if (filter_size[0] % 2) != 1 or (filter_size[1] % 2) != 1: + raise ValueError('Kernel size should be odd.') + if filter_size[0] == 1 and filter_size[1] == 1: + raise ValueError('Kernel size should be larger than 1x1.') + + with self._BlockScope(): + if initializer is None: + initializer = block_util.RsqrtInitializer(dims=(0, 1, 2)) + self._initializer = initializer + + def _CreateKernel(self, shape, dtype): + init = self._initializer(shape, dtype) + kernel = self.NewVar(init) + + mask = np.ones(shape[:2], dtype=dtype.as_numpy_dtype) + center = shape[:2] // 2 + mask[center[0], center[1]] = 0 + mask = mask.reshape(mask.shape + (1, 1)) + + return tf.convert_to_tensor(mask, dtype) * kernel diff --git a/compression/entropy_coder/lib/blocks_masked_conv2d_lstm.py b/compression/entropy_coder/lib/blocks_masked_conv2d_lstm.py new file mode 100644 index 000000000..2d6dfeffc --- /dev/null +++ b/compression/entropy_coder/lib/blocks_masked_conv2d_lstm.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. +# ============================================================================== + +"""Masked conv2d LSTM.""" + +import block_base +import block_util +import blocks_masked_conv2d +import blocks_lstm +import blocks_std + +# pylint: disable=not-callable + + +class RasterScanConv2DLSTM(blocks_lstm.LSTMBase): + """Convolutional LSTM implementation with optimizations inspired by [1]. + + Note that when using the batch normalization feature, the bias initializer + will not be used, since BN effectively cancels its effect out. + + [1] Zaremba, Sutskever, Vinyals. Recurrent Neural Network Regularization, + 2015. arxiv:1409.2329. + """ + + def __init__(self, + depth, + filter_size, + hidden_filter_size, + strides, + padding, + bias=blocks_lstm.LSTMBiasInit, + initializer=block_util.RsqrtInitializer(dims=(0, 1, 2)), + name=None): + super(RasterScanConv2DLSTM, self).__init__([None, None, depth], name) + + with self._BlockScope(): + self._input_conv = blocks_masked_conv2d.RasterScanConv2D( + 4 * depth, + filter_size, + strides, + padding, + strict_order=False, + bias=None, + act=None, + initializer=initializer, + name='input_conv2d') + + self._hidden_conv = blocks_std.Conv2D( + 4 * depth, + hidden_filter_size, + [1, 1], + 'SAME', + bias=None, + act=None, + initializer=initializer, + name='hidden_conv2d') + + if bias is not None: + self._bias = blocks_std.BiasAdd(bias, name='biases') + else: + self._bias = blocks_std.PassThrough() + + def _TransformInputs(self, x): + return self._bias(self._input_conv(x)) + + def _TransformHidden(self, h): + return self._hidden_conv(h) diff --git a/compression/entropy_coder/lib/blocks_masked_conv2d_test.py b/compression/entropy_coder/lib/blocks_masked_conv2d_test.py new file mode 100644 index 000000000..adb546778 --- /dev/null +++ b/compression/entropy_coder/lib/blocks_masked_conv2d_test.py @@ -0,0 +1,206 @@ +# 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 of the 2D masked convolution blocks.""" + +from __future__ import division +from __future__ import unicode_literals + +import numpy as np +import tensorflow as tf + +import blocks_masked_conv2d + + +class MaskedConv2DTest(tf.test.TestCase): + + def testRasterScanKernel(self): + kernel_size = 5 + input_depth = 1 + output_depth = 1 + kernel_shape = [kernel_size, kernel_size, input_depth, output_depth] + + # pylint: disable=bad-whitespace + kernel_feed = [[ 1.0, 2.0, 3.0, 4.0, 5.0], + [ 6.0, 7.0, 8.0, 9.0, 10.0], + [11.0, 12.0, 13.0, 14.0, 15.0], + [16.0, 17.0, 18.0, 19.0, 20.0], + [21.0, 22.0, 23.0, 24.0, 25.0]] + kernel_feed = np.reshape(kernel_feed, kernel_shape) + kernel_expected = [[ 1.0, 2.0, 3.0, 4.0, 5.0], + [ 6.0, 7.0, 8.0, 9.0, 10.0], + [11.0, 12.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]] + kernel_expected = np.reshape(kernel_expected, kernel_shape) + # pylint: enable=bad-whitespace + + init_kernel = lambda s, t: tf.constant(kernel_feed, dtype=t, shape=s) + masked_conv2d = blocks_masked_conv2d.RasterScanConv2D( + output_depth, [kernel_size] * 2, [1] * 2, 'SAME', + initializer=init_kernel) + x = tf.placeholder(dtype=tf.float32, shape=[10] * 3 + [input_depth]) + _ = masked_conv2d(x) + + with self.test_session(): + tf.global_variables_initializer().run() + kernel_value = masked_conv2d._kernel.eval() + + self.assertAllEqual(kernel_expected, kernel_value) + + def testDepthOrderKernel(self): + kernel_size = 1 + input_depth = 7 + output_depth = input_depth + kernel_shape = [kernel_size, kernel_size, input_depth, output_depth] + + kernel_feed = np.ones(kernel_shape) + x_shape = [5] * 3 + [input_depth] + x_feed = np.ones(x_shape) + y_expected = np.zeros(x_shape[0:3] + [output_depth]) + y_expected[:, :, :] = np.arange(output_depth) + + init_kernel = lambda s, t: tf.constant(kernel_feed, dtype=t, shape=s) + masked_conv2d = blocks_masked_conv2d.DepthOrderConv2D( + output_depth, [kernel_size] * 2, [1] * 2, 'SAME', + strict_order=True, + initializer=init_kernel) + x = tf.placeholder(dtype=tf.float32, shape=x_shape) + y = masked_conv2d(x) + + with self.test_session(): + tf.global_variables_initializer().run() + y_value = y.eval(feed_dict={x: x_feed}) + + self.assertAllEqual(y_expected, y_value) + + def testGroupRasterScanKernel(self): + kernel_size = 3 + input_depth = 4 + input_group_size = 2 + output_depth = 2 + output_group_size = 1 + kernel_shape = [kernel_size, kernel_size, input_depth, output_depth] + kernel_feed = np.ones(shape=kernel_shape) + + height = 5 + width = 5 + x_shape = [1, height, width, input_depth] + x_feed = np.ones(shape=x_shape) + + # pylint: disable=bad-whitespace + y_expected = [ + [[ 0, 2], [ 4, 6], [ 4, 6], [ 4, 6], [ 4, 6]], + [[ 8, 10], [16, 18], [16, 18], [16, 18], [12, 14]], + [[ 8, 10], [16, 18], [16, 18], [16, 18], [12, 14]], + [[ 8, 10], [16, 18], [16, 18], [16, 18], [12, 14]], + [[ 8, 10], [16, 18], [16, 18], [16, 18], [12, 14]], + ] + y_expected = np.reshape(y_expected, [1, height, width, output_depth]) + # pylint: enable=bad-whitespace + + init_kernel = lambda s, t: tf.constant(kernel_feed, dtype=t, shape=s) + masked_conv2d = blocks_masked_conv2d.GroupRasterScanConv2D( + output_depth, [kernel_size] * 2, [1] * 2, 'SAME', + strict_order=True, + input_group_size=input_group_size, + output_group_size=output_group_size, + initializer=init_kernel) + x = tf.placeholder(dtype=tf.float32, shape=x_shape) + y = masked_conv2d(x) + + with self.test_session(): + tf.global_variables_initializer().run() + y_value = y.eval(feed_dict={x: x_feed}) + + self.assertAllEqual(y_expected, y_value) + + def testInFillingKernel(self): + kernel_size = 5 + input_depth = 1 + output_depth = 1 + kernel_shape = [kernel_size, kernel_size, input_depth, output_depth] + + # pylint: disable=bad-whitespace + kernel_feed = [[ 1.0, 2.0, 3.0, 4.0, 5.0], + [ 6.0, 7.0, 8.0, 9.0, 10.0], + [11.0, 12.0, 13.0, 14.0, 15.0], + [16.0, 17.0, 18.0, 19.0, 20.0], + [21.0, 22.0, 23.0, 24.0, 25.0]] + kernel_feed = np.reshape(kernel_feed, kernel_shape) + kernel_expected = [[ 1.0, 2.0, 3.0, 4.0, 5.0], + [ 6.0, 7.0, 8.0, 9.0, 10.0], + [11.0, 12.0, 0.0, 14.0, 15.0], + [16.0, 17.0, 18.0, 19.0, 20.0], + [21.0, 22.0, 23.0, 24.0, 25.0]] + kernel_expected = np.reshape(kernel_expected, kernel_shape) + # pylint: enable=bad-whitespace + + init_kernel = lambda s, t: tf.constant(kernel_feed, dtype=t, shape=s) + masked_conv2d = blocks_masked_conv2d.InFillingConv2D( + output_depth, [kernel_size] * 2, [1] * 2, 'SAME', + initializer=init_kernel) + x = tf.placeholder(dtype=tf.float32, shape=[10] * 3 + [input_depth]) + _ = masked_conv2d(x) + + with self.test_session(): + tf.global_variables_initializer().run() + kernel_value = masked_conv2d._kernel.eval() + + self.assertAllEqual(kernel_expected, kernel_value) + + def testConv2DMaskedNumerics(self): + kernel_size = 5 + input_shape = [1, 10, 10, 1] + filter_shape = [kernel_size, kernel_size, 1, 1] + strides = [1, 1, 1, 1] + output_shape = [1, 10, 10, 1] + + conv = blocks_masked_conv2d.RasterScanConv2D( + depth=filter_shape[-1], + filter_size=filter_shape[0:2], + strides=strides[1:3], + padding='SAME', + initializer=tf.constant_initializer(value=1.0)) + x = tf.placeholder(dtype=tf.float32, shape=input_shape) + y = conv(x) + + x_feed = - np.ones(input_shape, dtype=float) + y_expected = np.ones(output_shape, dtype=float) + for i in xrange(input_shape[1]): + for j in xrange(input_shape[2]): + x_feed[0, i, j, 0] = 10 * (j + 1) + i + v = 0 + ki_start = max(i - kernel_size // 2, 0) + kj_start = max(j - kernel_size // 2, 0) + kj_end = min(j + kernel_size // 2, input_shape[2] - 1) + for ki in range(ki_start, i + 1): + for kj in range(kj_start, kj_end + 1): + if ki > i: + continue + if ki == i and kj >= j: + continue + v += 10 * (kj + 1) + ki + y_expected[0, i, j, 0] = v + + with self.test_session(): + tf.global_variables_initializer().run() + y_value = y.eval(feed_dict={x: x_feed}) + + self.assertAllEqual(y_expected, y_value) + + +if __name__ == '__main__': + tf.test.main() diff --git a/compression/entropy_coder/lib/blocks_operator.py b/compression/entropy_coder/lib/blocks_operator.py new file mode 100644 index 000000000..e35e37b27 --- /dev/null +++ b/compression/entropy_coder/lib/blocks_operator.py @@ -0,0 +1,87 @@ +# 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 blocks which work as operators on other blocks.""" + +import tensorflow as tf + +import block_base + +# pylint: disable=not-callable + + +class CompositionOperator(block_base.BlockBase): + """Composition of several blocks.""" + + def __init__(self, block_list, name=None): + """Initialization of the composition operator. + + Args: + block_list: List of blocks.BlockBase that are chained to create + a new blocks.BlockBase. + name: Name of this block. + """ + super(CompositionOperator, self).__init__(name) + self._blocks = block_list + + def _Apply(self, x): + """Apply successively all the blocks on the given input tensor.""" + h = x + for layer in self._blocks: + h = layer(h) + return h + + +class LineOperator(block_base.BlockBase): + """Repeat the same block over all the lines of an input tensor.""" + + def __init__(self, block, name=None): + super(LineOperator, self).__init__(name) + self._block = block + + def _Apply(self, x): + height = x.get_shape()[1].value + if height is None: + raise ValueError('Unknown tensor height') + all_line_x = tf.split(value=x, num_or_size_splits=height, axis=1) + + y = [] + for line_x in all_line_x: + y.append(self._block(line_x)) + y = tf.concat(values=y, axis=1) + + return y + + +class TowerOperator(block_base.BlockBase): + """Parallel execution with concatenation of several blocks.""" + + def __init__(self, block_list, dim=3, name=None): + """Initialization of the parallel exec + concat (Tower). + + Args: + block_list: List of blocks.BlockBase that are chained to create + a new blocks.BlockBase. + dim: the dimension on which to concat. + name: Name of this block. + """ + super(TowerOperator, self).__init__(name) + self._blocks = block_list + self._concat_dim = dim + + def _Apply(self, x): + """Apply successively all the blocks on the given input tensor.""" + outputs = [layer(x) for layer in self._blocks] + return tf.concat(outputs, self._concat_dim) diff --git a/compression/entropy_coder/lib/blocks_operator_test.py b/compression/entropy_coder/lib/blocks_operator_test.py new file mode 100644 index 000000000..8b6d80da1 --- /dev/null +++ b/compression/entropy_coder/lib/blocks_operator_test.py @@ -0,0 +1,64 @@ +# 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 of the block operators.""" + +import numpy as np +import tensorflow as tf + +import block_base +import blocks_operator + + +class AddOneBlock(block_base.BlockBase): + + def __init__(self, name=None): + super(AddOneBlock, self).__init__(name) + + def _Apply(self, x): + return x + 1.0 + + +class SquareBlock(block_base.BlockBase): + + def __init__(self, name=None): + super(SquareBlock, self).__init__(name) + + def _Apply(self, x): + return x * x + + +class BlocksOperatorTest(tf.test.TestCase): + + def testComposition(self): + x_value = np.array([[1.0, 2.0, 3.0], + [-1.0, -2.0, -3.0]]) + y_expected_value = np.array([[4.0, 9.0, 16.0], + [0.0, 1.0, 4.0]]) + + x = tf.placeholder(dtype=tf.float32, shape=[2, 3]) + complex_block = blocks_operator.CompositionOperator( + [AddOneBlock(), + SquareBlock()]) + y = complex_block(x) + + with self.test_session(): + y_value = y.eval(feed_dict={x: x_value}) + + self.assertAllClose(y_expected_value, y_value) + + +if __name__ == '__main__': + tf.test.main() diff --git a/compression/entropy_coder/lib/blocks_std.py b/compression/entropy_coder/lib/blocks_std.py new file mode 100644 index 000000000..ff39df679 --- /dev/null +++ b/compression/entropy_coder/lib/blocks_std.py @@ -0,0 +1,364 @@ +# 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. +# ============================================================================== + +"""Basic blocks for building tensorflow models.""" + +import numpy as np +import tensorflow as tf + +import block_base +import block_util + +# pylint does not recognize block_base.BlockBase.__call__(). +# pylint: disable=not-callable + + +def HandleConvPaddingModes(x, padding, kernel_shape, strides): + """Returns an updated tensor and padding type for REFLECT and SYMMETRIC. + + Args: + x: A 4D tensor with shape [batch_size, height, width, depth]. + padding: Padding mode (SAME, VALID, REFLECT, or SYMMETRIC). + kernel_shape: Shape of convolution kernel that will be applied. + strides: Convolution stride that will be used. + + Returns: + x and padding after adjustments for REFLECT and SYMMETRIC. + """ + # For 1x1 convolution, all padding modes are the same. + if np.all(kernel_shape[:2] == 1): + return x, 'VALID' + + if padding == 'REFLECT' or padding == 'SYMMETRIC': + # We manually compute the number of paddings as if 'SAME'. + # From Tensorflow kernel, the formulas are as follows. + # output_shape = ceil(input_shape / strides) + # paddings = (output_shape - 1) * strides + filter_size - input_shape + # Let x, y, s be a shorthand notations for input_shape, output_shape, and + # strides, respectively. Let (x - 1) = sn + r where 0 <= r < s. Note that + # y - 1 = ceil(x / s) - 1 = floor((x - 1) / s) = n + # provided that x > 0. Therefore + # paddings = n * s + filter_size - (sn + r + 1) + # = filter_size - r - 1. + input_shape = x.get_shape() # shape at graph construction time + img_shape = tf.shape(x)[1:3] # image shape (no batch) at run time + remainder = tf.mod(img_shape - 1, strides[1:3]) + pad_sizes = kernel_shape[:2] - remainder - 1 + + pad_rows = pad_sizes[0] + pad_cols = pad_sizes[1] + pad = tf.stack([[0, 0], tf.stack([pad_rows // 2, (pad_rows + 1) // 2]), + tf.stack([pad_cols // 2, (pad_cols + 1) // 2]), [0, 0]]) + + # Manually pad the input and switch the padding mode to 'VALID'. + x = tf.pad(x, pad, mode=padding) + x.set_shape([input_shape[0], x.get_shape()[1], + x.get_shape()[2], input_shape[3]]) + padding = 'VALID' + + return x, padding + + +class PassThrough(block_base.BlockBase): + """A dummy transform block that does nothing.""" + + def __init__(self): + # Pass an empty string to disable name scoping. + super(PassThrough, self).__init__(name='') + + def _Apply(self, inp): + return inp + + @property + def initialized(self): + """Always returns True.""" + return True + + +class Bias(object): + """An initialization helper class for BiasAdd block below.""" + + def __init__(self, value=0): + self.value = value + + +class BiasAdd(block_base.BlockBase): + """A tf.nn.bias_add wrapper. + + This wrapper may act as a PassThrough block depending on the initializer + provided, to make easier optional bias applications in NN blocks, etc. + See __init__() for the details. + """ + + def __init__(self, initializer=Bias(0), name=None): + """Initializes Bias block. + + |initializer| parameter have two special cases. + + 1. If initializer is None, then this block works as a PassThrough. + 2. If initializer is a Bias class object, then tf.constant_initializer is + used with the stored value. + + Args: + initializer: An initializer for the bias variable. + name: Name of this block. + """ + super(BiasAdd, self).__init__(name) + + with self._BlockScope(): + if isinstance(initializer, Bias): + self._initializer = tf.constant_initializer(value=initializer.value) + else: + self._initializer = initializer + + self._bias = None + + def _Apply(self, x): + if not self._bias: + init = self._initializer([int(x.get_shape()[-1])], x.dtype) + self._bias = self.NewVar(init) + + return tf.nn.bias_add(x, self._bias) + + def CreateWeightLoss(self): + return [] + + +class LinearBase(block_base.BlockBase): + """A matmul wrapper. + + Returns input * W, where matrix W can be customized through derivation. + """ + + def __init__(self, depth, name=None): + super(LinearBase, self).__init__(name) + + with self._BlockScope(): + self._depth = depth + self._matrix = None + + def _CreateKernel(self, shape, dtype): + raise NotImplementedError('This method must be sub-classed.') + + def _Apply(self, x): + if not self._matrix: + shape = [int(x.get_shape()[-1]), self._depth] + self._matrix = self._CreateKernel(shape, x.dtype) + + return tf.matmul(x, self._matrix) + + +class Linear(LinearBase): + """A matmul wrapper. + + Returns input * W, where matrix W is learned. + """ + + def __init__(self, + depth, + initializer=block_util.RsqrtInitializer(), + name=None): + super(Linear, self).__init__(depth, name) + + with self._BlockScope(): + self._initializer = initializer + + def _CreateKernel(self, shape, dtype): + init = self._initializer(shape, dtype) + return self.NewVar(init) + + +class NN(block_base.BlockBase): + """A neural network layer wrapper. + + Returns act(input * W + b), where matrix W, bias b are learned, and act is an + optional activation function (i.e., nonlinearity). + + This transform block can handle multiple inputs. If x_1, x_2, ..., x_m are + the inputs, then returns act(x_1 * W_1 + ... + x_m * W_m + b). + + Attributes: + nunits: The dimension of the output. + """ + + def __init__(self, + depth, + bias=Bias(0), + act=None, # e.g., tf.nn.relu + initializer=block_util.RsqrtInitializer(), + linear_block_factory=(lambda d, i: Linear(d, initializer=i)), + name=None): + """Initializes NN block. + + Args: + depth: The depth of the output. + bias: An initializer for the bias, or a Bias class object. If None, there + will be no bias term for this NN block. See BiasAdd block. + act: Optional activation function. If None, no activation is applied. + initializer: The initialization method for the matrix weights. + linear_block_factory: A function used to create a linear block. + name: The name of this block. + """ + super(NN, self).__init__(name) + + with self._BlockScope(): + self._linear_block_factory = linear_block_factory + self._depth = depth + self._initializer = initializer + self._matrices = None + + self._bias = BiasAdd(bias) if bias else PassThrough() + self._act = act if act else PassThrough() + + # TODO(sjhwang): Stop using **kwargs, if we ever switch to python3. + def _Apply(self, *args): + if not self._matrices: + self._matrices = [ + self._linear_block_factory(self._depth, self._initializer) + for _ in args] + + if len(self._matrices) != len(args): + raise ValueError('{} expected {} inputs, but observed {} inputs'.format( + self.name, len(self._matrices), len(args))) + + if len(args) > 1: + y = tf.add_n([m(x) for m, x in zip(self._matrices, args)]) + else: + y = self._matrices[0](args[0]) + + return self._act(self._bias(y)) + + +class Conv2DBase(block_base.BlockBase): + """A tf.nn.conv2d operator.""" + + def __init__(self, depth, filter_size, strides, padding, + bias=None, act=None, atrous_rate=None, conv=tf.nn.conv2d, + name=None): + """Initializes a Conv2DBase block. + + Arguments: + depth: The output depth of the block (i.e. #filters); if negative, the + output depth will be set to be the same as the input depth. + filter_size: The size of the 2D filter. If it's specified as an integer, + it's going to create a square filter. Otherwise, this is a tuple + specifying the height x width of the filter. + strides: A tuple specifying the y and x stride. + padding: One of the valid padding modes allowed by tf.nn.conv2d, or + 'REFLECT'/'SYMMETRIC' for mirror padding. + bias: An initializer for the bias, or a Bias class object. If None, there + will be no bias in this block. See BiasAdd block. + act: Optional activation function applied to the output. + atrous_rate: optional input rate for ATrous convolution. If not None, this + will be used and the strides will be ignored. + conv: The convolution function to use (e.g. tf.nn.conv2d). + name: The name for this conv2d op. + """ + super(Conv2DBase, self).__init__(name) + + with self._BlockScope(): + self._act = act if act else PassThrough() + self._bias = BiasAdd(bias) if bias else PassThrough() + + self._kernel_shape = np.zeros((4,), dtype=np.int32) + self._kernel_shape[:2] = filter_size + self._kernel_shape[3] = depth + + self._strides = np.ones((4,), dtype=np.int32) + self._strides[1:3] = strides + self._strides = list(self._strides) + + self._padding = padding + + self._kernel = None + self._conv = conv + + self._atrous_rate = atrous_rate + + def _CreateKernel(self, shape, dtype): + raise NotImplementedError('This method must be sub-classed') + + def _Apply(self, x): + """Apply the self._conv op. + + Arguments: + x: input tensor. It needs to be a 4D tensor of the form + [batch, height, width, channels]. + Returns: + The output of the convolution of x with the current convolutional + kernel. + Raises: + ValueError: if number of channels is not defined at graph construction. + """ + input_shape = x.get_shape().with_rank(4) + input_shape[3:].assert_is_fully_defined() # channels must be defined + if self._kernel is None: + assert self._kernel_shape[2] == 0, self._kernel_shape + self._kernel_shape[2] = input_shape[3].value + if self._kernel_shape[3] < 0: + # Make output depth be the same as input depth. + self._kernel_shape[3] = self._kernel_shape[2] + self._kernel = self._CreateKernel(self._kernel_shape, x.dtype) + + x, padding = HandleConvPaddingModes( + x, self._padding, self._kernel_shape, self._strides) + if self._atrous_rate is None: + x = self._conv(x, self._kernel, strides=self._strides, padding=padding) + else: + x = self._conv(x, self._kernel, rate=self._atrous_rate, padding=padding) + + if self._padding != 'VALID': + # Manually update shape. Known shape information can be lost by tf.pad(). + height = (1 + (input_shape[1].value - 1) // self._strides[1] + if input_shape[1].value else None) + width = (1 + (input_shape[2].value - 1) // self._strides[2] + if input_shape[2].value else None) + shape = x.get_shape() + x.set_shape([shape[0], height, width, shape[3]]) + + return self._act(self._bias(x)) + + +class Conv2D(Conv2DBase): + """A tf.nn.conv2d operator.""" + + def __init__(self, depth, filter_size, strides, padding, + bias=None, act=None, initializer=None, name=None): + """Initializes a Conv2D block. + + Arguments: + depth: The output depth of the block (i.e., #filters) + filter_size: The size of the 2D filter. If it's specified as an integer, + it's going to create a square filter. Otherwise, this is a tuple + specifying the height x width of the filter. + strides: A tuple specifying the y and x stride. + padding: One of the valid padding modes allowed by tf.nn.conv2d, or + 'REFLECT'/'SYMMETRIC' for mirror padding. + bias: An initializer for the bias, or a Bias class object. If None, there + will be no bias in this block. See BiasAdd block. + act: Optional activation function applied to the output. + initializer: Optional initializer for weights. + name: The name for this conv2d op. + """ + super(Conv2D, self).__init__(depth, filter_size, strides, padding, bias, + act, conv=tf.nn.conv2d, name=name) + + with self._BlockScope(): + if initializer is None: + initializer = block_util.RsqrtInitializer(dims=(0, 1, 2)) + self._initializer = initializer + + def _CreateKernel(self, shape, dtype): + return self.NewVar(self._initializer(shape, dtype)) diff --git a/compression/entropy_coder/lib/blocks_std_test.py b/compression/entropy_coder/lib/blocks_std_test.py new file mode 100644 index 000000000..1ec12e75f --- /dev/null +++ b/compression/entropy_coder/lib/blocks_std_test.py @@ -0,0 +1,340 @@ +# 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 basic tensorflow blocks_std.""" + +from __future__ import division +from __future__ import unicode_literals + +import math +import os + +import numpy as np +import tensorflow as tf + +import blocks_std + + +def _NumpyConv2D(x, f, strides, padding, rate=1): + assert strides[0] == 1 and strides[3] == 1, strides + + if rate > 1: + f_shape = f.shape + expand_f = np.zeros([f_shape[0], ((f_shape[1] - 1) * rate + 1), + f_shape[2], f_shape[3]]) + expand_f[:, [y * rate for y in range(f_shape[1])], :, :] = f + f = np.zeros([((f_shape[0] - 1) * rate + 1), expand_f.shape[1], + f_shape[2], f_shape[3]]) + f[[y * rate for y in range(f_shape[0])], :, :, :] = expand_f + + if padding != 'VALID': + assert x.shape[1] > 0 and x.shape[2] > 0, x.shape + # Compute the number of padded rows and cols. + # See Conv2D block comments for a math explanation. + remainder = ((x.shape[1] - 1) % strides[1], (x.shape[2] - 1) % strides[2]) + pad_rows = f.shape[0] - remainder[0] - 1 + pad_cols = f.shape[1] - remainder[1] - 1 + pad = ((0, 0), + (pad_rows // 2, (pad_rows + 1) // 2), + (pad_cols // 2, (pad_cols + 1) // 2), + (0, 0)) + + # Pad the input using numpy.pad(). + mode = None + if padding == 'SAME': + mode = str('constant') + if padding == 'REFLECT': + mode = str('reflect') + if padding == 'SYMMETRIC': + mode = str('symmetric') + x = np.pad(x, pad, mode=mode) + + # Since x is now properly padded, proceed as if padding mode is VALID. + x_window = np.empty( + (x.shape[0], + int(math.ceil((x.shape[1] - f.shape[0] + 1) / strides[1])), + int(math.ceil((x.shape[2] - f.shape[1] + 1) / strides[2])), + np.prod(f.shape[:3]))) + + # The output at pixel location (i, j) is the result of linear transformation + # applied to the window whose top-left corner is at + # (i * row_stride, j * col_stride). + for i in xrange(x_window.shape[1]): + k = i * strides[1] + for j in xrange(x_window.shape[2]): + l = j * strides[2] + x_window[:, i, j, :] = x[:, + k:(k + f.shape[0]), + l:(l + f.shape[1]), + :].reshape((x_window.shape[0], -1)) + + y = np.tensordot(x_window, f.reshape((-1, f.shape[3])), axes=1) + return y + + +class BlocksStdTest(tf.test.TestCase): + + def CheckUnary(self, y, op_type): + self.assertEqual(op_type, y.op.type) + self.assertEqual(1, len(y.op.inputs)) + return y.op.inputs[0] + + def CheckBinary(self, y, op_type): + self.assertEqual(op_type, y.op.type) + self.assertEqual(2, len(y.op.inputs)) + return y.op.inputs + + def testPassThrough(self): + p = blocks_std.PassThrough() + x = tf.placeholder(dtype=tf.float32, shape=[1]) + self.assertIs(p(x), x) + + def CheckBiasAdd(self, y, b): + x, u = self.CheckBinary(y, 'BiasAdd') + self.assertIs(u, b._bias.value()) + self.assertEqual(x.dtype, u.dtype.base_dtype) + return x + + def testBiasAdd(self): + b = blocks_std.BiasAdd() + x = tf.placeholder(dtype=tf.float32, shape=[4, 8]) + y = b(x) + self.assertEqual(b._bias.get_shape(), x.get_shape()[-1:]) + self.assertIs(x, self.CheckBiasAdd(y, b)) + + def testBiasRankTest(self): + b = blocks_std.BiasAdd() + x = tf.placeholder(dtype=tf.float32, shape=[10]) + with self.assertRaises(ValueError): + b(x) + + def CheckLinear(self, y, m): + x, w = self.CheckBinary(y, 'MatMul') + self.assertIs(w, m._matrix.value()) + self.assertEqual(x.dtype, w.dtype.base_dtype) + return x + + def testLinear(self): + m = blocks_std.Linear(10) + x = tf.placeholder(dtype=tf.float32, shape=[8, 9]) + y = m(x) + self.assertEqual(m._matrix.get_shape(), [9, 10]) + self.assertIs(x, self.CheckLinear(y, m)) + + def testLinearShared(self): + # Create a linear map which is applied twice on different inputs + # (i.e. the weights of the map are shared). + # TODO(sjhwang): Make this test deterministic. + linear_map = blocks_std.Linear(6) + x1 = tf.random_normal(shape=[1, 5]) + x2 = tf.random_normal(shape=[1, 5]) + xs = x1 + x2 + + # Apply the transform with the same weights. + y1 = linear_map(x1) + y2 = linear_map(x2) + ys = linear_map(xs) + + with self.test_session() as sess: + # Initialize all the variables of the graph. + tf.global_variables_initializer().run() + + y1_res, y2_res, ys_res = sess.run([y1, y2, ys]) + self.assertAllClose(y1_res + y2_res, ys_res) + + def CheckNN(self, y, nn, act=None): + if act: + pre_act = self.CheckUnary(y, act) + else: + pre_act = y + + if not isinstance(nn._bias, blocks_std.PassThrough): + pre_bias = self.CheckBiasAdd(pre_act, nn._bias) + else: + pre_bias = pre_act + + if len(nn._matrices) > 1: + self.assertEqual('AddN', pre_bias.op.type) + pre_bias = pre_bias.op.inputs + else: + pre_bias = [pre_bias] + + self.assertEqual(len(pre_bias), len(nn._matrices)) + return [self.CheckLinear(u, m) for u, m in zip(pre_bias, nn._matrices)] + + def testNNWithoutActWithoutBias(self): + nn = blocks_std.NN(10, act=None, bias=None) + x = tf.placeholder(dtype=tf.float32, shape=[5, 7]) + y = nn(x) + self.assertIs(x, self.CheckNN(y, nn)[0]) + + def testNNWithoutBiasWithAct(self): + nn = blocks_std.NN(10, act=tf.nn.relu, bias=None) + x = tf.placeholder(dtype=tf.float32, shape=[5, 7]) + y = nn(x) + self.assertIs(x, self.CheckNN(y, nn, 'Relu')[0]) + + def testNNWithBiasWithoutAct(self): + nn = blocks_std.NN(10, bias=blocks_std.Bias(0), act=None) + x = tf.placeholder(dtype=tf.float32, shape=[5, 7]) + y = nn(x) + self.assertIs(x, self.CheckNN(y, nn)[0]) + + def testNNWithBiasWithAct(self): + nn = blocks_std.NN(10, bias=blocks_std.Bias(0), act=tf.square) + x = tf.placeholder(dtype=tf.float32, shape=[5, 7]) + y = nn(x) + self.assertIs(x, self.CheckNN(y, nn, 'Square')[0]) + + def testNNMultipleInputs(self): + nn = blocks_std.NN(10, bias=blocks_std.Bias(0), act=tf.tanh) + x = [tf.placeholder(dtype=tf.float32, shape=[5, 7]), + tf.placeholder(dtype=tf.float32, shape=[5, 3]), + tf.placeholder(dtype=tf.float32, shape=[5, 5])] + y = nn(*x) + xs = self.CheckNN(y, nn, 'Tanh') + self.assertEqual(len(x), len(xs)) + for u, v in zip(x, xs): + self.assertIs(u, v) + + def testConv2DSAME(self): + np.random.seed(142536) + + x_shape = [4, 16, 11, 5] + f_shape = [4, 3, 5, 6] + strides = [1, 2, 2, 1] + padding = 'SAME' + + conv = blocks_std.Conv2D(depth=f_shape[-1], + filter_size=f_shape[0:2], + strides=strides[1:3], + padding=padding, + act=None, + bias=None) + x_value = np.random.normal(size=x_shape) + x = tf.convert_to_tensor(x_value, dtype=tf.float32) + y = conv(x) + + with self.test_session(): + tf.global_variables_initializer().run() + f_value = conv._kernel.eval() + y_value = y.eval() + + y_expected = _NumpyConv2D(x_value, f_value, + strides=strides, padding=padding) + self.assertAllClose(y_expected, y_value) + + def testConv2DValid(self): + np.random.seed(253647) + + x_shape = [4, 11, 12, 5] + f_shape = [5, 2, 5, 5] + strides = [1, 2, 2, 1] + padding = 'VALID' + + conv = blocks_std.Conv2D(depth=f_shape[-1], + filter_size=f_shape[0:2], + strides=strides[1:3], + padding=padding, + act=None, + bias=None) + x_value = np.random.normal(size=x_shape) + x = tf.convert_to_tensor(x_value, dtype=tf.float32) + y = conv(x) + + with self.test_session(): + tf.global_variables_initializer().run() + f_value = conv._kernel.eval() + y_value = y.eval() + + y_expected = _NumpyConv2D(x_value, f_value, + strides=strides, padding=padding) + self.assertAllClose(y_expected, y_value) + + def testConv2DSymmetric(self): + np.random.seed(364758) + + x_shape = [4, 10, 12, 6] + f_shape = [3, 4, 6, 5] + strides = [1, 1, 1, 1] + padding = 'SYMMETRIC' + + conv = blocks_std.Conv2D(depth=f_shape[-1], + filter_size=f_shape[0:2], + strides=strides[1:3], + padding=padding, + act=None, + bias=None) + x_value = np.random.normal(size=x_shape) + x = tf.convert_to_tensor(x_value, dtype=tf.float32) + y = conv(x) + + with self.test_session(): + tf.global_variables_initializer().run() + f_value = conv._kernel.eval() + y_value = y.eval() + + y_expected = _NumpyConv2D(x_value, f_value, + strides=strides, padding=padding) + self.assertAllClose(y_expected, y_value) + + def testConv2DReflect(self): + np.random.seed(768798) + + x_shape = [4, 10, 12, 6] + f_shape = [3, 4, 6, 5] + strides = [1, 2, 2, 1] + padding = 'REFLECT' + + conv = blocks_std.Conv2D(depth=f_shape[-1], + filter_size=f_shape[0:2], + strides=strides[1:3], + padding=padding, + act=None, + bias=None) + x_value = np.random.normal(size=x_shape) + x = tf.convert_to_tensor(x_value, dtype=tf.float32) + y = conv(x) + + with self.test_session(): + tf.global_variables_initializer().run() + f_value = conv._kernel.eval() + y_value = y.eval() + + y_expected = _NumpyConv2D(x_value, f_value, + strides=strides, padding=padding) + self.assertAllClose(y_expected, y_value) + + def testConv2DBias(self): + input_shape = [19, 14, 14, 64] + filter_shape = [3, 7, 64, 128] + strides = [1, 2, 2, 1] + output_shape = [19, 6, 4, 128] + + conv = blocks_std.Conv2D(depth=filter_shape[-1], + filter_size=filter_shape[0:2], + strides=strides[1:3], + padding='VALID', + act=None, + bias=blocks_std.Bias(1)) + x = tf.placeholder(dtype=tf.float32, shape=input_shape) + + y = conv(x) + self.CheckBiasAdd(y, conv._bias) + self.assertEqual(output_shape, y.get_shape().as_list()) + + +if __name__ == '__main__': + tf.test.main() diff --git a/compression/entropy_coder/model/__init__.py b/compression/entropy_coder/model/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/compression/entropy_coder/model/entropy_coder_model.py b/compression/entropy_coder/model/entropy_coder_model.py new file mode 100644 index 000000000..6d40e77cc --- /dev/null +++ b/compression/entropy_coder/model/entropy_coder_model.py @@ -0,0 +1,55 @@ +# 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. +# ============================================================================== + +"""Entropy coder model.""" + + +class EntropyCoderModel(object): + """Entropy coder model.""" + + def __init__(self): + # Loss used for training the model. + self.loss = None + + # Tensorflow op to run to train the model. + self.train_op = None + + # Tensor corresponding to the average code length of the input bit field + # tensor. The average code length is a number of output bits per input bit. + # To get an effective compression, this number should be between 0.0 + # and 1.0 (1.0 corresponds to no compression). + self.average_code_length = None + + def Initialize(self, global_step, optimizer, config_string): + raise NotImplementedError() + + def BuildGraph(self, input_codes): + """Build the Tensorflow graph corresponding to the entropy coder model. + + Args: + input_codes: Tensor of size: batch_size x height x width x bit_depth + corresponding to the codes to compress. + The input codes are {-1, +1} codes. + """ + # TODO(damienv): + # - consider switching to {0, 1} codes. + # - consider passing an extra tensor which gives for each (b, y, x) + # what is the actual depth (which would allow to use more or less bits + # for each (y, x) location. + raise NotImplementedError() + + def GetConfigStringForUnitTest(self): + """Returns a default model configuration to be used for unit tests.""" + return None diff --git a/compression/entropy_coder/model/model_factory.py b/compression/entropy_coder/model/model_factory.py new file mode 100644 index 000000000..e6f9902f3 --- /dev/null +++ b/compression/entropy_coder/model/model_factory.py @@ -0,0 +1,53 @@ +# 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. +# ============================================================================== + +"""Entropy coder model registrar.""" + + +class ModelFactory(object): + """Factory of encoder/decoder models.""" + + def __init__(self): + self._model_dictionary = dict() + + def RegisterModel(self, + entropy_coder_model_name, + entropy_coder_model_factory): + self._model_dictionary[entropy_coder_model_name] = ( + entropy_coder_model_factory) + + def CreateModel(self, model_name): + current_model_factory = self._model_dictionary[model_name] + return current_model_factory() + + def GetAvailableModels(self): + return self._model_dictionary.keys() + + +_model_registry = ModelFactory() + + +def GetModelRegistry(): + return _model_registry + + +class RegisterEntropyCoderModel(object): + + def __init__(self, model_name): + self._model_name = model_name + + def __call__(self, f): + _model_registry.RegisterModel(self._model_name, f) + return f diff --git a/compression/entropy_coder/progressive/__init__.py b/compression/entropy_coder/progressive/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/compression/entropy_coder/progressive/progressive.py b/compression/entropy_coder/progressive/progressive.py new file mode 100644 index 000000000..8560ab540 --- /dev/null +++ b/compression/entropy_coder/progressive/progressive.py @@ -0,0 +1,242 @@ +# 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. +# ============================================================================== + +"""Code probability model used for entropy coding.""" + +import json + +import tensorflow as tf + +from entropy_coder.lib import blocks +from entropy_coder.model import entropy_coder_model +from entropy_coder.model import model_factory + +# pylint: disable=not-callable + + +class BrnnPredictor(blocks.BlockBase): + """BRNN prediction applied on one layer.""" + + def __init__(self, code_depth, name=None): + super(BrnnPredictor, self).__init__(name) + + with self._BlockScope(): + hidden_depth = 2 * code_depth + + # What is coming from the previous layer/iteration + # is going through a regular Conv2D layer as opposed to the binary codes + # of the current layer/iteration which are going through a masked + # convolution. + self._adaptation0 = blocks.RasterScanConv2D( + hidden_depth, [7, 7], [1, 1], 'SAME', + strict_order=True, + bias=blocks.Bias(0), act=tf.tanh) + self._adaptation1 = blocks.Conv2D( + hidden_depth, [3, 3], [1, 1], 'SAME', + bias=blocks.Bias(0), act=tf.tanh) + self._predictor = blocks.CompositionOperator([ + blocks.LineOperator( + blocks.RasterScanConv2DLSTM( + depth=hidden_depth, + filter_size=[1, 3], + hidden_filter_size=[1, 3], + strides=[1, 1], + padding='SAME')), + blocks.Conv2D(hidden_depth, [1, 1], [1, 1], 'SAME', + bias=blocks.Bias(0), act=tf.tanh), + blocks.Conv2D(code_depth, [1, 1], [1, 1], 'SAME', + bias=blocks.Bias(0), act=tf.tanh) + ]) + + def _Apply(self, x, s): + # Code estimation using both: + # - the state from the previous iteration/layer, + # - the binary codes that are before in raster scan order. + h = tf.concat(values=[self._adaptation0(x), self._adaptation1(s)], axis=3) + + estimated_codes = self._predictor(h) + + return estimated_codes + + +class LayerPrediction(blocks.BlockBase): + """Binary code prediction for one layer.""" + + def __init__(self, layer_count, code_depth, name=None): + super(LayerPrediction, self).__init__(name) + + self._layer_count = layer_count + + # No previous layer. + self._layer_state = None + self._current_layer = 0 + + with self._BlockScope(): + # Layers used to do the conditional code prediction. + self._brnn_predictors = [] + for _ in xrange(layer_count): + self._brnn_predictors.append(BrnnPredictor(code_depth)) + + # Layers used to generate the input of the LSTM operating on the + # iteration/depth domain. + hidden_depth = 2 * code_depth + self._state_blocks = [] + for _ in xrange(layer_count): + self._state_blocks.append(blocks.CompositionOperator([ + blocks.Conv2D( + hidden_depth, [3, 3], [1, 1], 'SAME', + bias=blocks.Bias(0), act=tf.tanh), + blocks.Conv2D( + code_depth, [3, 3], [1, 1], 'SAME', + bias=blocks.Bias(0), act=tf.tanh) + ])) + + # Memory of the RNN is equivalent to the size of 2 layers of binary + # codes. + hidden_depth = 2 * code_depth + self._layer_rnn = blocks.CompositionOperator([ + blocks.Conv2DLSTM( + depth=hidden_depth, + filter_size=[1, 1], + hidden_filter_size=[1, 1], + strides=[1, 1], + padding='SAME'), + blocks.Conv2D(hidden_depth, [1, 1], [1, 1], 'SAME', + bias=blocks.Bias(0), act=tf.tanh), + blocks.Conv2D(code_depth, [1, 1], [1, 1], 'SAME', + bias=blocks.Bias(0), act=tf.tanh) + ]) + + def _Apply(self, x): + assert self._current_layer < self._layer_count + + # Layer state is set to 0 when there is no previous iteration. + if self._layer_state is None: + self._layer_state = tf.zeros_like(x, dtype=tf.float32) + + # Code estimation using both: + # - the state from the previous iteration/layer, + # - the binary codes that are before in raster scan order. + estimated_codes = self._brnn_predictors[self._current_layer]( + x, self._layer_state) + + # Compute the updated layer state. + h = self._state_blocks[self._current_layer](x) + self._layer_state = self._layer_rnn(h) + self._current_layer += 1 + + return estimated_codes + + +class ProgressiveModel(entropy_coder_model.EntropyCoderModel): + """Progressive BRNN entropy coder model.""" + + def __init__(self): + super(ProgressiveModel, self).__init__() + + def Initialize(self, global_step, optimizer, config_string): + if config_string is None: + raise ValueError('The progressive model requires a configuration.') + config = json.loads(config_string) + if 'coded_layer_count' not in config: + config['coded_layer_count'] = 0 + + self._config = config + self._optimizer = optimizer + self._global_step = global_step + + def BuildGraph(self, input_codes): + """Build the graph corresponding to the progressive BRNN model.""" + layer_depth = self._config['layer_depth'] + layer_count = self._config['layer_count'] + + code_shape = input_codes.get_shape() + code_depth = code_shape[-1].value + if self._config['coded_layer_count'] > 0: + prefix_depth = self._config['coded_layer_count'] * layer_depth + if code_depth < prefix_depth: + raise ValueError('Invalid prefix depth: {} VS {}'.format( + prefix_depth, code_depth)) + input_codes = input_codes[:, :, :, :prefix_depth] + + code_shape = input_codes.get_shape() + code_depth = code_shape[-1].value + if code_depth % layer_depth != 0: + raise ValueError( + 'Code depth must be a multiple of the layer depth: {} vs {}'.format( + code_depth, layer_depth)) + code_layer_count = code_depth // layer_depth + if code_layer_count > layer_count: + raise ValueError('Input codes have too many layers: {}, max={}'.format( + code_layer_count, layer_count)) + + # Block used to estimate binary codes. + layer_prediction = LayerPrediction(layer_count, layer_depth) + + # Block used to compute code lengths. + code_length_block = blocks.CodeLength() + + # Loop over all the layers. + code_length = [] + code_layers = tf.split( + value=input_codes, num_or_size_splits=code_layer_count, axis=3) + for k in xrange(code_layer_count): + x = code_layers[k] + predicted_x = layer_prediction(x) + # Saturate the prediction to avoid infinite code length. + epsilon = 0.001 + predicted_x = tf.clip_by_value( + predicted_x, -1 + epsilon, +1 - epsilon) + code_length.append(code_length_block( + blocks.ConvertSignCodeToZeroOneCode(x), + blocks.ConvertSignCodeToZeroOneCode(predicted_x))) + tf.contrib.deprecated.scalar_summary('code_length_layer_{:02d}'.format(k), + code_length[-1]) + code_length = tf.stack(code_length) + self.loss = tf.reduce_mean(code_length) + tf.contrib.deprecated.scalar_summary('loss', self.loss) + + # Loop over all the remaining layers just to make sure they are + # instantiated. Otherwise, loading model params could fail. + dummy_x = tf.zeros_like(code_layers[0]) + for _ in xrange(layer_count - code_layer_count): + dummy_predicted_x = layer_prediction(dummy_x) + + # Average bitrate over total_line_count. + self.average_code_length = tf.reduce_mean(code_length) + + if self._optimizer: + optim_op = self._optimizer.minimize(self.loss, + global_step=self._global_step) + block_updates = blocks.CreateBlockUpdates() + if block_updates: + with tf.get_default_graph().control_dependencies([optim_op]): + self.train_op = tf.group(*block_updates) + else: + self.train_op = optim_op + else: + self.train_op = None + + def GetConfigStringForUnitTest(self): + s = '{\n' + s += '"layer_depth": 1,\n' + s += '"layer_count": 8\n' + s += '}\n' + return s + + +@model_factory.RegisterEntropyCoderModel('progressive') +def CreateProgressiveModel(): + return ProgressiveModel() -- GitLab From c9397c90fe6949e6009a2ac9d5c44bfc8167ee12 Mon Sep 17 00:00:00 2001 From: Aaron Schumacher Date: Mon, 22 May 2017 09:57:16 -0400 Subject: [PATCH 012/110] typo: "observed to common words" -> "two" (#1495) Thanks! --- swivel/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swivel/README.md b/swivel/README.md index d12ff1578..ed77c747a 100644 --- a/swivel/README.md +++ b/swivel/README.md @@ -24,7 +24,7 @@ Note that the resulting co-occurrence matrix is very sparse (i.e., contains many zeros) since most words won't have been observed in the context of other words. In the case of very rare words, it seems reasonable to assume that you just haven't sampled enough data to spot their co-occurrence yet. On the other hand, -if we've failed to observed to common words co-occuring, it seems likely that +if we've failed to observed two common words co-occuring, it seems likely that they are *anti-correlated*. Swivel attempts to capture this intuition by using both the observed and the -- GitLab From 18e5ce8d9ea5abd5eb549a923d74826ad9461016 Mon Sep 17 00:00:00 2001 From: Damien Vincent Date: Mon, 22 May 2017 17:42:30 +0200 Subject: [PATCH 013/110] Remove names in TODO. --- compression/entropy_coder/core/code_loader.py | 2 +- compression/entropy_coder/lib/blocks_std.py | 1 - compression/entropy_coder/lib/blocks_std_test.py | 1 - compression/entropy_coder/model/entropy_coder_model.py | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/compression/entropy_coder/core/code_loader.py b/compression/entropy_coder/core/code_loader.py index 47d947ce2..603ab724a 100644 --- a/compression/entropy_coder/core/code_loader.py +++ b/compression/entropy_coder/core/code_loader.py @@ -39,7 +39,7 @@ def LoadBinaryCode(input_config, batch_size): """ data = input_config.data - # TODO(damienv): Possibly use multiple files (instead of just one). + # TODO: Possibly use multiple files (instead of just one). file_list = [data] filename_queue = tf.train.string_input_producer(file_list, capacity=4) diff --git a/compression/entropy_coder/lib/blocks_std.py b/compression/entropy_coder/lib/blocks_std.py index ff39df679..2c6174853 100644 --- a/compression/entropy_coder/lib/blocks_std.py +++ b/compression/entropy_coder/lib/blocks_std.py @@ -222,7 +222,6 @@ class NN(block_base.BlockBase): self._bias = BiasAdd(bias) if bias else PassThrough() self._act = act if act else PassThrough() - # TODO(sjhwang): Stop using **kwargs, if we ever switch to python3. def _Apply(self, *args): if not self._matrices: self._matrices = [ diff --git a/compression/entropy_coder/lib/blocks_std_test.py b/compression/entropy_coder/lib/blocks_std_test.py index 1ec12e75f..7e8d42cf1 100644 --- a/compression/entropy_coder/lib/blocks_std_test.py +++ b/compression/entropy_coder/lib/blocks_std_test.py @@ -136,7 +136,6 @@ class BlocksStdTest(tf.test.TestCase): def testLinearShared(self): # Create a linear map which is applied twice on different inputs # (i.e. the weights of the map are shared). - # TODO(sjhwang): Make this test deterministic. linear_map = blocks_std.Linear(6) x1 = tf.random_normal(shape=[1, 5]) x2 = tf.random_normal(shape=[1, 5]) diff --git a/compression/entropy_coder/model/entropy_coder_model.py b/compression/entropy_coder/model/entropy_coder_model.py index 6d40e77cc..67f7eb5bc 100644 --- a/compression/entropy_coder/model/entropy_coder_model.py +++ b/compression/entropy_coder/model/entropy_coder_model.py @@ -43,7 +43,7 @@ class EntropyCoderModel(object): corresponding to the codes to compress. The input codes are {-1, +1} codes. """ - # TODO(damienv): + # TODO: # - consider switching to {0, 1} codes. # - consider passing an extra tensor which gives for each (b, y, x) # what is the actual depth (which would allow to use more or less bits -- GitLab From 99f9442b0f1f5225c7ef725c32e55afb41186e66 Mon Sep 17 00:00:00 2001 From: Neal Wu Date: Mon, 22 May 2017 18:01:56 -0700 Subject: [PATCH 014/110] Remove barrier, add tf.identity where appropriate, and make sure tests pass --- inception/inception/slim/ops_test.py | 14 ++++---------- slim/deployment/model_deploy.py | 2 +- slim/train_image_classifier.py | 2 +- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/inception/inception/slim/ops_test.py b/inception/inception/slim/ops_test.py index cf5afbba9..13dc5d9aa 100644 --- a/inception/inception/slim/ops_test.py +++ b/inception/inception/slim/ops_test.py @@ -418,7 +418,7 @@ class DropoutTest(tf.test.TestCase): with self.test_session(): images = tf.random_uniform((5, height, width, 3), seed=1) output = ops.dropout(images) - self.assertEquals(output.op.name, 'Dropout/dropout/mul_1') + self.assertEquals(output.op.name, 'Dropout/dropout/mul') output.get_shape().assert_is_compatible_with(images.get_shape()) def testCreateDropoutNoTraining(self): @@ -599,9 +599,7 @@ class BatchNormTest(tf.test.TestCase): output = ops.batch_norm(images, decay=0.1) update_ops = tf.get_collection(ops.UPDATE_OPS_COLLECTION) with tf.control_dependencies(update_ops): - barrier = tf.no_op(name='gradient_barrier') - with tf.control_dependencies([barrier]): - output = tf.identity(output) + output = tf.identity(output) # Initialize all variables sess.run(tf.global_variables_initializer()) moving_mean = variables.get_variables('BatchNorm/moving_mean')[0] @@ -630,9 +628,7 @@ class BatchNormTest(tf.test.TestCase): output = ops.batch_norm(images, decay=0.1, is_training=False) update_ops = tf.get_collection(ops.UPDATE_OPS_COLLECTION) with tf.control_dependencies(update_ops): - barrier = tf.no_op(name='gradient_barrier') - with tf.control_dependencies([barrier]): - output = tf.identity(output) + output = tf.identity(output) # Initialize all variables sess.run(tf.global_variables_initializer()) moving_mean = variables.get_variables('BatchNorm/moving_mean')[0] @@ -665,9 +661,7 @@ class BatchNormTest(tf.test.TestCase): output = ops.batch_norm(images, decay=0.1, is_training=False) update_ops = tf.get_collection(ops.UPDATE_OPS_COLLECTION) with tf.control_dependencies(update_ops): - barrier = tf.no_op(name='gradient_barrier') - with tf.control_dependencies([barrier]): - output = tf.identity(output) + output = tf.identity(output) # Initialize all variables sess.run(tf.global_variables_initializer()) moving_mean = variables.get_variables('BatchNorm/moving_mean')[0] diff --git a/slim/deployment/model_deploy.py b/slim/deployment/model_deploy.py index 24dd5c34a..96b762bae 100644 --- a/slim/deployment/model_deploy.py +++ b/slim/deployment/model_deploy.py @@ -379,7 +379,7 @@ def deploy(config, update_op = tf.group(*update_ops) with tf.control_dependencies([update_op]): - train_op = total_loss + train_op = tf.identity(total_loss, name='train_op') else: clones_losses = [] regularization_losses = tf.get_collection( diff --git a/slim/train_image_classifier.py b/slim/train_image_classifier.py index 5aa674f41..57049a1a2 100755 --- a/slim/train_image_classifier.py +++ b/slim/train_image_classifier.py @@ -540,7 +540,7 @@ def main(_): update_op = tf.group(*update_ops) with tf.control_dependencies([update_op]): - train_tensor = total_loss + train_tensor = tf.identity(total_loss, name='train_op') # Add the summaries from the first clone. These contain the summaries # created by model_fn and either optimize_clones() or _gather_clone_loss(). -- GitLab From 72262b5d7f9178f6a97baa841a99f5816a1897e7 Mon Sep 17 00:00:00 2001 From: Sunghyo Chung Date: Wed, 24 May 2017 03:16:42 +0900 Subject: [PATCH 015/110] Fix typo --- inception/inception/imagenet_eval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inception/inception/imagenet_eval.py b/inception/inception/imagenet_eval.py index 5444f1927..e6f8bac2e 100644 --- a/inception/inception/imagenet_eval.py +++ b/inception/inception/imagenet_eval.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""A binary to evaluate Inception on the flowers data set. +"""A binary to evaluate Inception on the ImageNet data set. Note that using the supplied pre-trained inception checkpoint, the eval should achieve: -- GitLab From e41999f846338f42f82b88dd8713b7fb9c95f377 Mon Sep 17 00:00:00 2001 From: Damien Vincent Date: Wed, 24 May 2017 15:05:15 +0200 Subject: [PATCH 016/110] Entropy coder for images: remove deprecated functions and update README. --- compression/README.md | 1 + compression/entropy_coder/README.md | 9 ++++++++- compression/entropy_coder/core/entropy_coder_train.py | 2 +- compression/entropy_coder/progressive/progressive.py | 5 ++--- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/compression/README.md b/compression/README.md index 44fd4ccec..2ae52f6fc 100644 --- a/compression/README.md +++ b/compression/README.md @@ -8,6 +8,7 @@ code for the following papers: ## Organization [Image Encoder](image_encoder/): Encoding and decoding images into their binary representation. +[Entropy Coder](entropy_coder/): Lossless compression of the binary representation. ## Contact Info Model repository maintained by Nick Johnston ([nickj-google](https://github.com/nickj-google)). diff --git a/compression/entropy_coder/README.md b/compression/entropy_coder/README.md index 806743304..59e889990 100644 --- a/compression/entropy_coder/README.md +++ b/compression/entropy_coder/README.md @@ -14,6 +14,11 @@ the width of the binary codes, sliced into N groups of K, where each additional group is used by the image decoder to add more details to the reconstructed image. +The code in this directory only contains the underlying code probability model +but does not perform the actual compression using arithmetic coding. +The code probability model is enough to compute the theoretical compression +ratio. + ## Prerequisites The only software requirements for running the encoder and decoder is having @@ -22,7 +27,7 @@ Tensorflow installed. You will also need to add the top level source directory of the entropy coder to your `PYTHONPATH`, for example: -`export PYTHONPATH=${PYTHONPATH}:/tmp/compression/entropy_coder` +`export PYTHONPATH=${PYTHONPATH}:/tmp/models/compression` ## Training the entropy coder @@ -38,6 +43,8 @@ less. To generate a synthetic dataset with 20000 samples: +`mkdir -p /tmp/dataset` + `python ./dataset/gen_synthetic_dataset.py --dataset_dir=/tmp/dataset/ --count=20000` diff --git a/compression/entropy_coder/core/entropy_coder_train.py b/compression/entropy_coder/core/entropy_coder_train.py index fd7266153..248935e3c 100644 --- a/compression/entropy_coder/core/entropy_coder_train.py +++ b/compression/entropy_coder/core/entropy_coder_train.py @@ -111,7 +111,7 @@ def train(): decay_steps=decay_steps, decay_rate=decay_rate, staircase=True) - tf.contrib.deprecated.scalar_summary('Learning Rate', learning_rate) + tf.summary.scalar('Learning Rate', learning_rate) optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate, epsilon=1.0) diff --git a/compression/entropy_coder/progressive/progressive.py b/compression/entropy_coder/progressive/progressive.py index 8560ab540..98777d8d5 100644 --- a/compression/entropy_coder/progressive/progressive.py +++ b/compression/entropy_coder/progressive/progressive.py @@ -202,11 +202,10 @@ class ProgressiveModel(entropy_coder_model.EntropyCoderModel): code_length.append(code_length_block( blocks.ConvertSignCodeToZeroOneCode(x), blocks.ConvertSignCodeToZeroOneCode(predicted_x))) - tf.contrib.deprecated.scalar_summary('code_length_layer_{:02d}'.format(k), - code_length[-1]) + tf.summary.scalar('code_length_layer_{:02d}'.format(k), code_length[-1]) code_length = tf.stack(code_length) self.loss = tf.reduce_mean(code_length) - tf.contrib.deprecated.scalar_summary('loss', self.loss) + tf.summary.scalar('loss', self.loss) # Loop over all the remaining layers just to make sure they are # instantiated. Otherwise, loading model params could fail. -- GitLab From 0f12d4a4188fa7fe4e2fe943d1035eec64a630df Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 26 May 2017 23:07:13 -0700 Subject: [PATCH 017/110] Fix Bazel incantations in docs Fixes tensorflow/tensorflow#10239 --- im2txt/README.md | 9 ++++++--- inception/README.md | 27 ++++++++++++++++++--------- skip_thoughts/README.md | 12 ++++++++---- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/im2txt/README.md b/im2txt/README.md index 510ee544e..223cf91fb 100644 --- a/im2txt/README.md +++ b/im2txt/README.md @@ -145,7 +145,8 @@ available space for storing the downloaded and processed data. MSCOCO_DIR="${HOME}/im2txt/data/mscoco" # Build the preprocessing script. -bazel build im2txt/download_and_preprocess_mscoco +cd tensorflow-models/im2txt +bazel build //im2txt:download_and_preprocess_mscoco # Run the preprocessing script. bazel-bin/im2txt/download_and_preprocess_mscoco "${MSCOCO_DIR}" @@ -211,7 +212,8 @@ INCEPTION_CHECKPOINT="${HOME}/im2txt/data/inception_v3.ckpt" MODEL_DIR="${HOME}/im2txt/model" # Build the model. -bazel build -c opt im2txt/... +cd tensorflow-models/im2txt +bazel build -c opt //im2txt/... # Run the training script. bazel-bin/im2txt/train \ @@ -304,7 +306,8 @@ VOCAB_FILE="${HOME}/im2txt/data/mscoco/word_counts.txt" IMAGE_FILE="${HOME}/im2txt/data/mscoco/raw-data/val2014/COCO_val2014_000000224477.jpg" # Build the inference binary. -bazel build -c opt im2txt/run_inference +cd tensorflow-models/im2txt +bazel build -c opt //im2txt:run_inference # Ignore GPU devices (only necessary if your GPU is currently memory # constrained, for example, by running the training script). diff --git a/inception/README.md b/inception/README.md index 446415308..f47312137 100644 --- a/inception/README.md +++ b/inception/README.md @@ -86,7 +86,8 @@ you will not need to interact with the script again. DATA_DIR=$HOME/imagenet-data # build the preprocessing script. -bazel build inception/download_and_preprocess_imagenet +cd tensorflow-models/inception +bazel build //inception:download_and_preprocess_imagenet # run it bazel-bin/inception/download_and_preprocess_imagenet "${DATA_DIR}" @@ -153,7 +154,8 @@ To train this model, you simply need to specify the following: ```shell # Build the model. Note that we need to make sure the TensorFlow is ready to # use before this as this command will not build TensorFlow. -bazel build inception/imagenet_train +cd tensorflow-models/inception +bazel build //inception:imagenet_train # run it bazel-bin/inception/imagenet_train --num_gpus=1 --batch_size=32 --train_dir=/tmp/imagenet_train --data_dir=/tmp/imagenet_data @@ -189,7 +191,8 @@ GPU cards. ```shell # Build the model. Note that we need to make sure the TensorFlow is ready to # use before this as this command will not build TensorFlow. -bazel build inception/imagenet_train +cd tensorflow-models/inception +bazel build //inception:imagenet_train # run it bazel-bin/inception/imagenet_train --num_gpus=2 --batch_size=64 --train_dir=/tmp/imagenet_train @@ -288,7 +291,8 @@ running. Several things to note here: ```shell # Build the model. Note that we need to make sure the TensorFlow is ready to # use before this as this command will not build TensorFlow. -bazel build inception/imagenet_distributed_train +cd tensorflow-models/inception +bazel build //inception:imagenet_distributed_train # To start worker 0, go to the worker0 host and run the following (Note that # task_id should be in the range [0, num_worker_tasks): @@ -395,7 +399,8 @@ Briefly, one can evaluate the model by running: ```shell # Build the model. Note that we need to make sure the TensorFlow is ready to # use before this as this command will not build TensorFlow. -bazel build inception/imagenet_eval +cd tensorflow-models/inception +bazel build //inception:imagenet_eval # run it bazel-bin/inception/imagenet_eval --checkpoint_dir=/tmp/imagenet_train --eval_dir=/tmp/imagenet_eval @@ -450,7 +455,8 @@ but feel free to edit accordingly. FLOWERS_DATA_DIR=/tmp/flowers-data/ # build the preprocessing script. -bazel build inception/download_and_preprocess_flowers +cd tensorflow-models/inception +bazel build //inception:download_and_preprocess_flowers # run it bazel-bin/inception/download_and_preprocess_flowers "${FLOWERS_DATA_DIR}" @@ -530,7 +536,8 @@ the flowers data set with the following command. ```shell # Build the model. Note that we need to make sure the TensorFlow is ready to # use before this as this command will not build TensorFlow. -bazel build inception/flowers_train +cd tensorflow-models/inception +bazel build //inception:flowers_train # Path to the downloaded Inception-v3 model. MODEL_PATH="${INCEPTION_MODEL_DIR}/inception-v3/model.ckpt-157585" @@ -566,7 +573,8 @@ fine-tuned model, you will need to run `flowers_eval`: ```shell # Build the model. Note that we need to make sure the TensorFlow is ready to # use before this as this command will not build TensorFlow. -bazel build inception/flowers_eval +cd tensorflow-models/inception +bazel build //inception:flowers_eval # Directory where we saved the fine-tuned checkpoint and events files. TRAIN_DIR=/tmp/flowers_train/ @@ -654,7 +662,8 @@ To run `build_image_data.py`, you can run the following command line: OUTPUT_DIRECTORY=$HOME/my-custom-data/ # build the preprocessing script. -bazel build inception/build_image_data +cd tensorflow-models/inception +bazel build //inception:build_image_data # convert the data. bazel-bin/inception/build_image_data \ diff --git a/skip_thoughts/README.md b/skip_thoughts/README.md index 68cc45e6e..cdcffe7c5 100644 --- a/skip_thoughts/README.md +++ b/skip_thoughts/README.md @@ -133,7 +133,8 @@ INPUT_FILES="${HOME}/skip_thoughts/bookcorpus/*.txt" DATA_DIR="${HOME}/skip_thoughts/data" # Build the preprocessing script. -bazel build -c opt skip_thoughts/data/preprocess_dataset +cd tensorflow-models/skip_thoughts +bazel build -c opt //skip_thoughts/data:preprocess_dataset # Run the preprocessing script. bazel-bin/skip_thoughts/data/preprocess_dataset \ @@ -164,7 +165,8 @@ DATA_DIR="${HOME}/skip_thoughts/data" MODEL_DIR="${HOME}/skip_thoughts/model" # Build the model. -bazel build -c opt skip_thoughts/... +cd tensorflow-models/skip_thoughts +bazel build -c opt //skip_thoughts/... # Run the training script. bazel-bin/skip_thoughts/train \ @@ -269,7 +271,8 @@ WORD2VEC_MODEL="${HOME}/skip_thoughts/googlenews/GoogleNews-vectors-negative300. EXP_VOCAB_DIR="${HOME}/skip_thoughts/exp_vocab" # Build the vocabulary expansion script. -bazel build -c opt skip_thoughts/vocabulary_expansion +cd tensorflow-models/skip_thoughts +bazel build -c opt //skip_thoughts:vocabulary_expansion # Run the vocabulary expansion script. bazel-bin/skip_thoughts/vocabulary_expansion \ @@ -343,7 +346,8 @@ EMBEDDINGS_FILE="${HOME}/skip_thoughts/exp_vocab/embeddings.npy" EVAL_DATA_DIR="${HOME}/skip_thoughts/eval_data" # Build the evaluation script. -bazel build -c opt skip_thoughts/evaluate +cd tensorflow-models/skip_thoughts +bazel build -c opt //skip_thoughts:evaluate # Run the evaluation script. bazel-bin/skip_thoughts/evaluate \ -- GitLab From c015f6962e82379d7d4b887ece19be85066928b0 Mon Sep 17 00:00:00 2001 From: Peter Glerup Ericson Date: Sun, 28 May 2017 14:59:12 +0200 Subject: [PATCH 018/110] Fix arxiv links in README of neural_gpu model As the links was [[link]] the last `]` was included in the url. --- neural_gpu/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neural_gpu/README.md b/neural_gpu/README.md index b73dd85ef..510f1c5e0 100644 --- a/neural_gpu/README.md +++ b/neural_gpu/README.md @@ -1,6 +1,6 @@ # NeuralGPU -Code for the Neural GPU model described in [[http://arxiv.org/abs/1511.08228]]. -The extended version was described in [[https://arxiv.org/abs/1610.08613]]. +Code for the Neural GPU model described in http://arxiv.org/abs/1511.08228. +The extended version was described in https://arxiv.org/abs/1610.08613. Requirements: * TensorFlow (see tensorflow.org for how to install) -- GitLab From 444310f2fda7bd3dc420ccb32ee234366621cd60 Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Tue, 30 May 2017 17:47:24 -0700 Subject: [PATCH 019/110] SYNSETS contains the full path and does not need pwd prepended. Tested on GCE with ubuntu 16.04. Mirrors change 157536266. --- inception/inception/data/download_imagenet.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/inception/inception/data/download_imagenet.sh b/inception/inception/data/download_imagenet.sh index 576c99a2b..49b3b7d56 100755 --- a/inception/inception/data/download_imagenet.sh +++ b/inception/inception/data/download_imagenet.sh @@ -40,7 +40,6 @@ fi OUTDIR="${1:-./imagenet-data}" SYNSETS_FILE="${2:-./synsets.txt}" -SYNSETS_FILE="${PWD}/${SYNSETS_FILE}" echo "Saving downloaded files to $OUTDIR" mkdir -p "${OUTDIR}" -- GitLab From b7e77995ac31d87cd34320440302aee3ec479ad8 Mon Sep 17 00:00:00 2001 From: Frank Chen Date: Wed, 31 May 2017 17:02:05 -0700 Subject: [PATCH 020/110] Add executable flag to models so that they can be run from the download_and_preprocess_imagenet.sh script automatically --- inception/inception/data/preprocess_imagenet_validation_data.py | 0 inception/inception/data/process_bounding_boxes.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 inception/inception/data/preprocess_imagenet_validation_data.py mode change 100644 => 100755 inception/inception/data/process_bounding_boxes.py diff --git a/inception/inception/data/preprocess_imagenet_validation_data.py b/inception/inception/data/preprocess_imagenet_validation_data.py old mode 100644 new mode 100755 diff --git a/inception/inception/data/process_bounding_boxes.py b/inception/inception/data/process_bounding_boxes.py old mode 100644 new mode 100755 -- GitLab From 0ce70bddc56821dbf09e7d4642f861026d858822 Mon Sep 17 00:00:00 2001 From: None Date: Fri, 2 Jun 2017 15:58:37 +0800 Subject: [PATCH 021/110] Update resnet_model.py 'elipson' should be epsilon --- resnet/resnet_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resnet/resnet_model.py b/resnet/resnet_model.py index a8b7f10ca..2be68a132 100644 --- a/resnet/resnet_model.py +++ b/resnet/resnet_model.py @@ -185,7 +185,7 @@ class ResNet(object): trainable=False) tf.summary.histogram(mean.op.name, mean) tf.summary.histogram(variance.op.name, variance) - # elipson used to be 1e-5. Maybe 0.001 solves NaN problem in deeper net. + # epsilon used to be 1e-5. Maybe 0.001 solves NaN problem in deeper net. y = tf.nn.batch_normalization( x, mean, variance, beta, gamma, 0.001) y.set_shape(x.get_shape()) -- GitLab From 01db54e508af57177853521f04b3cb51522b21d4 Mon Sep 17 00:00:00 2001 From: xiangjinwu Date: Sun, 4 Jun 2017 23:32:31 -0500 Subject: [PATCH 022/110] To pass test, add VariableV2 in _PSDeviceChooser --- slim/deployment/model_deploy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slim/deployment/model_deploy.py b/slim/deployment/model_deploy.py index 96b762bae..67b6f9a38 100644 --- a/slim/deployment/model_deploy.py +++ b/slim/deployment/model_deploy.py @@ -663,7 +663,7 @@ class DeploymentConfig(object): if op.device: return op.device node_def = op if isinstance(op, tf.NodeDef) else op.node_def - if node_def.op == 'Variable': + if node_def.op.startswith('Variable'): t = self._task self._task = (self._task + 1) % self._tasks d = '%s/task:%d' % (self._device, t) -- GitLab From 514a10de736aa9591250062551721be59ac620c3 Mon Sep 17 00:00:00 2001 From: Andrew Gilbert Date: Tue, 6 Jun 2017 18:45:14 +0900 Subject: [PATCH 023/110] Fixed calls to concat and convolution2d --- adversarial_crypto/train_eval.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/adversarial_crypto/train_eval.py b/adversarial_crypto/train_eval.py index 1e67be96a..570de938f 100644 --- a/adversarial_crypto/train_eval.py +++ b/adversarial_crypto/train_eval.py @@ -128,13 +128,13 @@ class AdversarialCrypto(object): """ if key is not None: - combined_message = tf.concat(1, [message, key]) + combined_message = tf.concat([message, key], 1) else: combined_message = message # Ensure that all variables created are in the specified collection. with tf.contrib.framework.arg_scope( - [tf.contrib.layers.fully_connected, tf.contrib.layers.convolution], + [tf.contrib.layers.fully_connected, tf.contrib.layers.convolution2d], variables_collections=[collection]): fc = tf.contrib.layers.fully_connected( @@ -147,13 +147,13 @@ class AdversarialCrypto(object): # and then squeezing it back down). fc = tf.expand_dims(fc, 2) # 2,1 -> 1,2 - conv = tf.contrib.layers.convolution( + conv = tf.contrib.layers.convolution2d( fc, 2, 2, 2, 'SAME', activation_fn=tf.nn.sigmoid) # 1,2 -> 1, 2 - conv = tf.contrib.layers.convolution( + conv = tf.contrib.layers.convolution2d( conv, 2, 1, 1, 'SAME', activation_fn=tf.nn.sigmoid) # 1,2 -> 1, 1 - conv = tf.contrib.layers.convolution( + conv = tf.contrib.layers.convolution2d( conv, 1, 1, 1, 'SAME', activation_fn=tf.nn.tanh) conv = tf.squeeze(conv, 2) return conv -- GitLab From 76cf35de36b3d5a10255c54b2e54500048c79b47 Mon Sep 17 00:00:00 2001 From: Neal Wu Date: Tue, 6 Jun 2017 12:39:45 -0700 Subject: [PATCH 024/110] Add named arguments to tf.concat and use conv2d instead of convolution2d --- adversarial_crypto/train_eval.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/adversarial_crypto/train_eval.py b/adversarial_crypto/train_eval.py index 570de938f..09de7e513 100644 --- a/adversarial_crypto/train_eval.py +++ b/adversarial_crypto/train_eval.py @@ -128,13 +128,13 @@ class AdversarialCrypto(object): """ if key is not None: - combined_message = tf.concat([message, key], 1) + combined_message = tf.concat(axis=1, values=[message, key]) else: combined_message = message # Ensure that all variables created are in the specified collection. with tf.contrib.framework.arg_scope( - [tf.contrib.layers.fully_connected, tf.contrib.layers.convolution2d], + [tf.contrib.layers.fully_connected, tf.contrib.layers.conv2d], variables_collections=[collection]): fc = tf.contrib.layers.fully_connected( @@ -147,13 +147,13 @@ class AdversarialCrypto(object): # and then squeezing it back down). fc = tf.expand_dims(fc, 2) # 2,1 -> 1,2 - conv = tf.contrib.layers.convolution2d( + conv = tf.contrib.layers.conv2d( fc, 2, 2, 2, 'SAME', activation_fn=tf.nn.sigmoid) # 1,2 -> 1, 2 - conv = tf.contrib.layers.convolution2d( + conv = tf.contrib.layers.conv2d( conv, 2, 1, 1, 'SAME', activation_fn=tf.nn.sigmoid) # 1,2 -> 1, 1 - conv = tf.contrib.layers.convolution2d( + conv = tf.contrib.layers.conv2d( conv, 1, 1, 1, 'SAME', activation_fn=tf.nn.tanh) conv = tf.squeeze(conv, 2) return conv -- GitLab From 0cde63327088b6fc27e832d65f8c48f3037f154f Mon Sep 17 00:00:00 2001 From: Ashley Williamson <11356993@students.lincoln.ac.uk> Date: Wed, 7 Jun 2017 18:51:58 +0100 Subject: [PATCH 025/110] Implemented LRN for AlexNet tutorial A TODO was stated for adding LRN - Pending GPU support. LRN was implemented by tensorflow/tensorflow@35df3ed43edabbc4ad1b2439bbc7de8917026d6e Hyper-parameters taken from http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks --- tutorials/image/alexnet/alexnet_benchmark.py | 25 ++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/tutorials/image/alexnet/alexnet_benchmark.py b/tutorials/image/alexnet/alexnet_benchmark.py index ed723055c..04c394ad4 100644 --- a/tutorials/image/alexnet/alexnet_benchmark.py +++ b/tutorials/image/alexnet/alexnet_benchmark.py @@ -73,11 +73,18 @@ def inference(images): print_activations(conv1) parameters += [kernel, biases] - # lrn1 - # TODO(shlens, jiayq): Add a GPU version of local response normalization. + + with tf.name_scope('lrn1') as scope: + lrn1 = tf.nn.local_response_normalization( + conv1, + alpha=1e-04, + beta=0.75, + depth_radius=5, + bias=2.0 + ) # pool1 - pool1 = tf.nn.max_pool(conv1, + pool1 = tf.nn.max_pool(lrn1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', @@ -96,8 +103,18 @@ def inference(images): parameters += [kernel, biases] print_activations(conv2) + + with tf.name_scope('lrn2') as scope: + lrn2 = tf.nn.local_response_normalization( + conv2, + alpha=1e-04, + beta=0.75, + depth_radius=5, + bias=2.0 + ) + # pool2 - pool2 = tf.nn.max_pool(conv2, + pool2 = tf.nn.max_pool(lrn2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', -- GitLab From 082e65c9342683e6dcb6df4e9922c99f0f966e90 Mon Sep 17 00:00:00 2001 From: Toby Boyd Date: Thu, 8 Jun 2017 10:08:13 -0700 Subject: [PATCH 026/110] iput pipeline on CPU. 1700 images/sec to 8000 on GTX 1080 --- tutorials/image/cifar10/cifar10_multi_gpu_train.py | 13 +++++++++---- tutorials/image/cifar10/cifar10_train.py | 5 ++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/tutorials/image/cifar10/cifar10_multi_gpu_train.py b/tutorials/image/cifar10/cifar10_multi_gpu_train.py index 16033eeff..e97e1f8fa 100644 --- a/tutorials/image/cifar10/cifar10_multi_gpu_train.py +++ b/tutorials/image/cifar10/cifar10_multi_gpu_train.py @@ -62,7 +62,7 @@ tf.app.flags.DEFINE_boolean('log_device_placement', False, """Whether to log device placement.""") -def tower_loss(scope): +def tower_loss(scope, images, labels): """Calculate the total loss on a single tower running the CIFAR model. Args: @@ -71,8 +71,7 @@ def tower_loss(scope): Returns: Tensor of shape [] containing the total loss for a batch of data """ - # Get images and labels for CIFAR-10. - images, labels = cifar10.distorted_inputs() + # Build inference Graph. logits = cifar10.inference(images) @@ -160,6 +159,12 @@ def train(): # Create an optimizer that performs gradient descent. opt = tf.train.GradientDescentOptimizer(lr) + # Get images and labels for CIFAR-10. + # Force input pipeline to CPU:0 to avoid opertaios sometimes ending up on GPU + # and resulting in a slow down. + with tf.device('/CPU:0'): + images, labels = cifar10.distorted_inputs() + # Calculate the gradients for each model tower. tower_grads = [] with tf.variable_scope(tf.get_variable_scope()): @@ -169,7 +174,7 @@ def train(): # Calculate the loss for one tower of the CIFAR model. This function # constructs the entire CIFAR model but shares the variables across # all towers. - loss = tower_loss(scope) + loss = tower_loss(scope, images, labels) # Reuse variables for the next tower. tf.get_variable_scope().reuse_variables() diff --git a/tutorials/image/cifar10/cifar10_train.py b/tutorials/image/cifar10/cifar10_train.py index fec64ec22..da01d5001 100644 --- a/tutorials/image/cifar10/cifar10_train.py +++ b/tutorials/image/cifar10/cifar10_train.py @@ -62,7 +62,10 @@ def train(): global_step = tf.contrib.framework.get_or_create_global_step() # Get images and labels for CIFAR-10. - images, labels = cifar10.distorted_inputs() + # Force input pipeline to CPU:0 to avoid opertaios sometimes ending up + # on GPU and resulting in a slow down. + with tf.device('/CPU:0'): + images, labels = cifar10.distorted_inputs() # Build a Graph that computes the logits predictions from the # inference model. -- GitLab From 3909e4bdff25c952713a08b4ecc31fff1fdf2cb4 Mon Sep 17 00:00:00 2001 From: Toby Boyd Date: Thu, 8 Jun 2017 10:15:06 -0700 Subject: [PATCH 027/110] pydoc update to match method signature --- tutorials/image/cifar10/cifar10_multi_gpu_train.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tutorials/image/cifar10/cifar10_multi_gpu_train.py b/tutorials/image/cifar10/cifar10_multi_gpu_train.py index e97e1f8fa..05d92cc27 100644 --- a/tutorials/image/cifar10/cifar10_multi_gpu_train.py +++ b/tutorials/image/cifar10/cifar10_multi_gpu_train.py @@ -67,6 +67,8 @@ def tower_loss(scope, images, labels): Args: scope: unique prefix string identifying the CIFAR tower, e.g. 'tower_0' + images: Images. 4D tensor of [batch_size, height, width, 3] size. + labels: Labels. 1D tensor of [batch_size] size. Returns: Tensor of shape [] containing the total loss for a batch of data -- GitLab From c3e2ae5ec1b0164ddd3895680c249d0adb1f11a8 Mon Sep 17 00:00:00 2001 From: Toby Boyd Date: Thu, 8 Jun 2017 13:49:27 -0700 Subject: [PATCH 028/110] Fixed typos and redudant with CPU:0 --- tutorials/image/cifar10/cifar10_multi_gpu_train.py | 5 +---- tutorials/image/cifar10/cifar10_train.py | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/tutorials/image/cifar10/cifar10_multi_gpu_train.py b/tutorials/image/cifar10/cifar10_multi_gpu_train.py index 05d92cc27..9f269cc04 100644 --- a/tutorials/image/cifar10/cifar10_multi_gpu_train.py +++ b/tutorials/image/cifar10/cifar10_multi_gpu_train.py @@ -162,10 +162,7 @@ def train(): opt = tf.train.GradientDescentOptimizer(lr) # Get images and labels for CIFAR-10. - # Force input pipeline to CPU:0 to avoid opertaios sometimes ending up on GPU - # and resulting in a slow down. - with tf.device('/CPU:0'): - images, labels = cifar10.distorted_inputs() + images, labels = cifar10.distorted_inputs() # Calculate the gradients for each model tower. tower_grads = [] diff --git a/tutorials/image/cifar10/cifar10_train.py b/tutorials/image/cifar10/cifar10_train.py index da01d5001..e32435279 100644 --- a/tutorials/image/cifar10/cifar10_train.py +++ b/tutorials/image/cifar10/cifar10_train.py @@ -62,8 +62,8 @@ def train(): global_step = tf.contrib.framework.get_or_create_global_step() # Get images and labels for CIFAR-10. - # Force input pipeline to CPU:0 to avoid opertaios sometimes ending up - # on GPU and resulting in a slow down. + # Force input pipeline to CPU:0 to avoid operations sometimes ending up on + # GPU and resulting in a slow down. with tf.device('/CPU:0'): images, labels = cifar10.distorted_inputs() -- GitLab From 9e8fd6d90c84df1f7444b055dcc3b653f6b7e14c Mon Sep 17 00:00:00 2001 From: Toby Boyd Date: Thu, 8 Jun 2017 15:05:06 -0700 Subject: [PATCH 029/110] Fixed typo and multi-gpu processing same batch on each gpu --- tutorials/image/cifar10/cifar10_multi_gpu_train.py | 6 +++++- tutorials/image/cifar10/cifar10_train.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tutorials/image/cifar10/cifar10_multi_gpu_train.py b/tutorials/image/cifar10/cifar10_multi_gpu_train.py index 9f269cc04..bc90711d7 100644 --- a/tutorials/image/cifar10/cifar10_multi_gpu_train.py +++ b/tutorials/image/cifar10/cifar10_multi_gpu_train.py @@ -138,6 +138,7 @@ def average_gradients(tower_grads): def train(): + print(FLAGS.batch_size) """Train CIFAR-10 for a number of steps.""" with tf.Graph().as_default(), tf.device('/cpu:0'): # Create a variable to count the number of train() calls. This equals the @@ -163,13 +164,16 @@ def train(): # Get images and labels for CIFAR-10. images, labels = cifar10.distorted_inputs() - + batch_queue = tf.contrib.slim.prefetch_queue.prefetch_queue( + [images, labels], capacity=2 * FLAGS.num_gpus) # Calculate the gradients for each model tower. tower_grads = [] with tf.variable_scope(tf.get_variable_scope()): for i in xrange(FLAGS.num_gpus): with tf.device('/gpu:%d' % i): with tf.name_scope('%s_%d' % (cifar10.TOWER_NAME, i)) as scope: + # Dequeues one batch for the GPU + images, labels = batch_queue.dequeue() # Calculate the loss for one tower of the CIFAR model. This function # constructs the entire CIFAR model but shares the variables across # all towers. diff --git a/tutorials/image/cifar10/cifar10_train.py b/tutorials/image/cifar10/cifar10_train.py index e32435279..cc1dc0d14 100644 --- a/tutorials/image/cifar10/cifar10_train.py +++ b/tutorials/image/cifar10/cifar10_train.py @@ -64,7 +64,7 @@ def train(): # Get images and labels for CIFAR-10. # Force input pipeline to CPU:0 to avoid operations sometimes ending up on # GPU and resulting in a slow down. - with tf.device('/CPU:0'): + with tf.device('/cpu:0'): images, labels = cifar10.distorted_inputs() # Build a Graph that computes the logits predictions from the -- GitLab From b5acc005968d37495f0e7d83d2dd2ef3d3674211 Mon Sep 17 00:00:00 2001 From: Neal Wu Date: Thu, 8 Jun 2017 16:44:02 -0700 Subject: [PATCH 030/110] Code cleanup --- tutorials/image/cifar10/cifar10_multi_gpu_train.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tutorials/image/cifar10/cifar10_multi_gpu_train.py b/tutorials/image/cifar10/cifar10_multi_gpu_train.py index bc90711d7..fb15faca2 100644 --- a/tutorials/image/cifar10/cifar10_multi_gpu_train.py +++ b/tutorials/image/cifar10/cifar10_multi_gpu_train.py @@ -67,14 +67,13 @@ def tower_loss(scope, images, labels): Args: scope: unique prefix string identifying the CIFAR tower, e.g. 'tower_0' - images: Images. 4D tensor of [batch_size, height, width, 3] size. - labels: Labels. 1D tensor of [batch_size] size. + images: Images. 4D tensor of shape [batch_size, height, width, 3]. + labels: Labels. 1D tensor of shape [batch_size]. Returns: Tensor of shape [] containing the total loss for a batch of data """ - # Build inference Graph. logits = cifar10.inference(images) @@ -138,7 +137,6 @@ def average_gradients(tower_grads): def train(): - print(FLAGS.batch_size) """Train CIFAR-10 for a number of steps.""" with tf.Graph().as_default(), tf.device('/cpu:0'): # Create a variable to count the number of train() calls. This equals the -- GitLab From 7d238c5ee69e722a9ca4cc65b9f737171b7cb75d Mon Sep 17 00:00:00 2001 From: Neal Wu Date: Fri, 9 Jun 2017 12:24:36 -0700 Subject: [PATCH 031/110] Rename the image/label batch variables --- tutorials/image/cifar10/cifar10_multi_gpu_train.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/image/cifar10/cifar10_multi_gpu_train.py b/tutorials/image/cifar10/cifar10_multi_gpu_train.py index fb15faca2..d139f1315 100644 --- a/tutorials/image/cifar10/cifar10_multi_gpu_train.py +++ b/tutorials/image/cifar10/cifar10_multi_gpu_train.py @@ -171,11 +171,11 @@ def train(): with tf.device('/gpu:%d' % i): with tf.name_scope('%s_%d' % (cifar10.TOWER_NAME, i)) as scope: # Dequeues one batch for the GPU - images, labels = batch_queue.dequeue() + image_batch, label_batch = batch_queue.dequeue() # Calculate the loss for one tower of the CIFAR model. This function # constructs the entire CIFAR model but shares the variables across # all towers. - loss = tower_loss(scope, images, labels) + loss = tower_loss(scope, image_batch, label_batch) # Reuse variables for the next tower. tf.get_variable_scope().reuse_variables() -- GitLab From 5ecced399b0d78e32ff952d01d217a1098d17201 Mon Sep 17 00:00:00 2001 From: Mindos Cheng Date: Mon, 12 Jun 2017 00:22:05 +0800 Subject: [PATCH 032/110] Fixed error message for inception/imagenet. --- inception/inception/data/preprocess_imagenet_validation_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inception/inception/data/preprocess_imagenet_validation_data.py b/inception/inception/data/preprocess_imagenet_validation_data.py index 8308277a0..ae1576fff 100755 --- a/inception/inception/data/preprocess_imagenet_validation_data.py +++ b/inception/inception/data/preprocess_imagenet_validation_data.py @@ -76,7 +76,7 @@ if __name__ == '__main__': basename = 'ILSVRC2012_val_000%.5d.JPEG' % (i + 1) original_filename = os.path.join(data_dir, basename) if not os.path.exists(original_filename): - print('Failed to find: ' % original_filename) + print('Failed to find: %s' % original_filename) sys.exit(-1) new_filename = os.path.join(data_dir, labels[i], basename) os.rename(original_filename, new_filename) -- GitLab From 286bacf2543a3357e91380af48db1ff9d7d84c13 Mon Sep 17 00:00:00 2001 From: ngovanmao Date: Tue, 13 Jun 2017 23:37:02 +0800 Subject: [PATCH 033/110] Fix a small bug of Python3 compatibility in tutorial example /rnn/ptb --- tutorials/rnn/ptb/reader.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tutorials/rnn/ptb/reader.py b/tutorials/rnn/ptb/reader.py index 995b628c0..a14ecc390 100644 --- a/tutorials/rnn/ptb/reader.py +++ b/tutorials/rnn/ptb/reader.py @@ -21,13 +21,17 @@ from __future__ import print_function import collections import os +import sys import tensorflow as tf def _read_words(filename): with tf.gfile.GFile(filename, "r") as f: - return f.read().decode("utf-8").replace("\n", "").split() + if sys.version_info[0] >= 3: + return f.read().replace("\n", "").split() + else: + return f.read().decode("utf-8").replace("\n", "").split() def _build_vocab(filename): -- GitLab From 5eab06018c51d0ec8bf85919bf50438e2c4056a3 Mon Sep 17 00:00:00 2001 From: andrewghoward Date: Tue, 13 Jun 2017 21:57:48 -0700 Subject: [PATCH 034/110] MobileNet V1 commit (#1551) * MobileNet V1 commit * updates to README --- slim/BUILD | 18 + slim/README.md | 26 +- slim/nets/mobilenet_v1.md | 47 ++ slim/nets/mobilenet_v1.png | Bin 0 -> 100916 bytes slim/nets/mobilenet_v1.py | 397 +++++++++++++++++ slim/nets/mobilenet_v1_test.py | 450 ++++++++++++++++++++ slim/nets/nets_factory.py | 5 +- slim/preprocessing/preprocessing_factory.py | 4 +- 8 files changed, 931 insertions(+), 16 deletions(-) create mode 100644 slim/nets/mobilenet_v1.md create mode 100644 slim/nets/mobilenet_v1.png create mode 100644 slim/nets/mobilenet_v1.py create mode 100644 slim/nets/mobilenet_v1_test.py diff --git a/slim/BUILD b/slim/BUILD index 77a1ae503..348ca7595 100644 --- a/slim/BUILD +++ b/slim/BUILD @@ -132,6 +132,7 @@ py_library( ":cifarnet", ":inception", ":lenet", + ":mobilenet_v1", ":overfeat", ":resnet_v1", ":resnet_v2", @@ -269,6 +270,23 @@ py_library( srcs = ["nets/lenet.py"], ) +py_library( + name = "mobilenet_v1", + srcs = ["nets/mobilenet_v1.py"], + srcs_version = "PY2AND3", +) + +py_test( + name = "mobilenet_v1_test", + size = "large", + srcs = ["nets/mobilenet_v1_test.py"], + shard_count = 3, + srcs_version = "PY2AND3", + deps = [ + ":mobilenet_v1", + ], +) + py_library( name = "overfeat", srcs = ["nets/overfeat.py"], diff --git a/slim/README.md b/slim/README.md index 85275e8d1..628931f7c 100644 --- a/slim/README.md +++ b/slim/README.md @@ -194,21 +194,24 @@ Model | TF-Slim File | Checkpoint | Top-1 Accuracy| Top-5 Accuracy | [Inception V2](http://arxiv.org/abs/1502.03167)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/inception_v2.py)|[inception_v2_2016_08_28.tar.gz](http://download.tensorflow.org/models/inception_v2_2016_08_28.tar.gz)|73.9|91.8| [Inception V3](http://arxiv.org/abs/1512.00567)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/inception_v3.py)|[inception_v3_2016_08_28.tar.gz](http://download.tensorflow.org/models/inception_v3_2016_08_28.tar.gz)|78.0|93.9| [Inception V4](http://arxiv.org/abs/1602.07261)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/inception_v4.py)|[inception_v4_2016_09_09.tar.gz](http://download.tensorflow.org/models/inception_v4_2016_09_09.tar.gz)|80.2|95.2| -[Inception-ResNet-v2](http://arxiv.org/abs/1602.07261)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/inception_resnet_v2.py)|[inception_resnet_v2.tar.gz](http://download.tensorflow.org/models/inception_resnet_v2_2016_08_30.tar.gz)|80.4|95.3| -[ResNet V1 50](https://arxiv.org/abs/1512.03385)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v1.py)|[resnet_v1_50.tar.gz](http://download.tensorflow.org/models/resnet_v1_50_2016_08_28.tar.gz)|75.2|92.2| -[ResNet V1 101](https://arxiv.org/abs/1512.03385)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v1.py)|[resnet_v1_101.tar.gz](http://download.tensorflow.org/models/resnet_v1_101_2016_08_28.tar.gz)|76.4|92.9| -[ResNet V1 152](https://arxiv.org/abs/1512.03385)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v1.py)|[resnet_v1_152.tar.gz](http://download.tensorflow.org/models/resnet_v1_152_2016_08_28.tar.gz)|76.8|93.2| -[ResNet V2 50](https://arxiv.org/abs/1603.05027)^|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v2.py)|[resnet_v2_50.tar.gz](http://download.tensorflow.org/models/resnet_v2_50_2017_04_14.tar.gz)|75.6|92.8| -[ResNet V2 101](https://arxiv.org/abs/1603.05027)^|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v2.py)|[resnet_v2_101.tar.gz](http://download.tensorflow.org/models/resnet_v2_101_2017_04_14.tar.gz)|77.0|93.7| -[ResNet V2 152](https://arxiv.org/abs/1603.05027)^|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v2.py)|[resnet_v2_152.tar.gz](http://download.tensorflow.org/models/resnet_v2_152_2017_04_14.tar.gz)|77.8|94.1| -[VGG 16](http://arxiv.org/abs/1409.1556.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/vgg.py)|[vgg_16.tar.gz](http://download.tensorflow.org/models/vgg_16_2016_08_28.tar.gz)|71.5|89.8| -[VGG 19](http://arxiv.org/abs/1409.1556.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/vgg.py)|[vgg_19.tar.gz](http://download.tensorflow.org/models/vgg_19_2016_08_28.tar.gz)|71.1|89.8| - +[Inception-ResNet-v2](http://arxiv.org/abs/1602.07261)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/inception_resnet_v2.py)|[inception_resnet_v2_2016_08_30.tar.gz](http://download.tensorflow.org/models/inception_resnet_v2_2016_08_30.tar.gz)|80.4|95.3| +[ResNet 50](https://arxiv.org/abs/1512.03385)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v1.py)|[resnet_v1_50_2016_08_28.tar.gz](http://download.tensorflow.org/models/resnet_v1_50_2016_08_28.tar.gz)|75.2|92.2| +[ResNet 101](https://arxiv.org/abs/1512.03385)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v1.py)|[resnet_v1_101_2016_08_28.tar.gz](http://download.tensorflow.org/models/resnet_v1_101_2016_08_28.tar.gz)|76.4|92.9| +[ResNet 152](https://arxiv.org/abs/1512.03385)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v1.py)|[resnet_v1_152_2016_08_28.tar.gz](http://download.tensorflow.org/models/resnet_v1_152_2016_08_28.tar.gz)|76.8|93.2| +[ResNet V2 200](https://arxiv.org/abs/1603.05027)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v2.py)|[TBA]()|79.9\*|95.2\*| +[VGG 16](http://arxiv.org/abs/1409.1556.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/vgg.py)|[vgg_16_2016_08_28.tar.gz](http://download.tensorflow.org/models/vgg_16_2016_08_28.tar.gz)|71.5|89.8| +[VGG 19](http://arxiv.org/abs/1409.1556.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/vgg.py)|[vgg_19_2016_08_28.tar.gz](http://download.tensorflow.org/models/vgg_19_2016_08_28.tar.gz)|71.1|89.8| +[MobileNet_v1_1.0_224](https://arxiv.org/pdf/1704.04861.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/mobilenet_v1.py)|[mobilenet_v1_1.0_224_2017_06_14.tar.gz](http://download.tensorflow.org/models/mobilenet_v1_1.0_224_2017_06_14.tar.gz)|70.7|89.5| +[MobileNet_v1_0.50_160](https://arxiv.org/pdf/1704.04861.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/mobilenet_v1.py)|[mobilenet_v1_0.50_160_2017_06_14.tar.gz](http://download.tensorflow.org/models/mobilenet_v1_0.50_160_2017_06_14.tar.gz)|59.9|82.5| +[MobileNet_v1_0.25_128](https://arxiv.org/pdf/1704.04861.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/mobilenet_v1.py)|[mobilenet_v1_0.25_128_2017_06_14.tar.gz](http://download.tensorflow.org/models/mobilenet_v1_0.25_128_2017_06_14.tar.gz)|41.3|66.2| ^ ResNet V2 models use Inception pre-processing and input image size of 299 (use `--preprocessing_name inception --eval_image_size 299` when using `eval_image_classifier.py`). Performance numbers for ResNet V2 models are -reported on ImageNet valdiation set. +reported on ImageNet valdiation set. + +All 16 MobileNet Models reported in the [MobileNet Paper](https://arxiv.org/abs/1704.04861) can be found [here](https://github.com/tensorflow/models/tree/master/slim/nets/mobilenet_v1.md). +(\*): Results quoted from the [paper](https://arxiv.org/abs/1603.05027). Here is an example of how to download the Inception V3 checkpoint: ```shell @@ -375,4 +378,3 @@ image_preprocessing_fn = preprocessing_factory.get_preprocessing( See [Hardware Specifications](https://github.com/tensorflow/models/tree/master/inception#what-hardware-specification-are-these-hyper-parameters-targeted-for). - diff --git a/slim/nets/mobilenet_v1.md b/slim/nets/mobilenet_v1.md new file mode 100644 index 000000000..3ce231176 --- /dev/null +++ b/slim/nets/mobilenet_v1.md @@ -0,0 +1,47 @@ +# MobileNet_v1 + +[MobileNets](https://arxiv.org/abs/1704.04861) are small, low-latency, low-power models parameterized to meet the resource constraints of a variety of use cases. They can be built upon for classification, detection, embeddings and segmentation similar to how other popular large scale models, such as Inception, are used. MobileNets can be run efficiently on mobile devices with [TensorFlow Mobile](https://www.tensorflow.org/mobile/). + +MobileNets trade off between latency, size and accuracy while comparing favorably with popular models from the literature. + +![alt text](https://github.com/tensorflow/models/tree/master/slim/nets/mobilenet_v1.png, "MobileNet Graph") + +# Pre-trained Models + +Choose the right MobileNet model to fit your latency and size budget. The size of the network in memory and on disk is proportional to the number of parameters. The latency and power usage of the network scales with the number of Multiply-Accumulates (MACs) which measures the number of fused Multiplication and Addition operations. These MobileNet models have been trained on the +[ILSVRC-2012-CLS](http://www.image-net.org/challenges/LSVRC/2012/) +image classification dataset. Accuracies were computed by evaluating using a single image crop. + +Model Checkpoint | Million MACs | Million Parameters | Top-1 Accuracy| Top-5 Accuracy | +:----:|:------------:|:----------:|:-------:|:-------:| +[MobileNet_v1_1.0_224](http://download.tensorflow.org/models/mobilenet_v1_1.0_224_2017_06_14.tar.gz)|569|4.24|70.7|89.5| +[MobileNet_v1_1.0_192](http://download.tensorflow.org/models/mobilenet_v1_1.0_192_2017_06_14.tar.gz)|418|4.24|69.3|88.9| +[MobileNet_v1_1.0_160](http://download.tensorflow.org/models/mobilenet_v1_1.0_160_2017_06_14.tar.gz)|291|4.24|67.2|87.5| +[MobileNet_v1_1.0_128](http://download.tensorflow.org/models/mobilenet_v1_1.0_128_2017_06_14.tar.gz)|186|4.24|64.1|85.3| +[MobileNet_v1_0.75_224](http://download.tensorflow.org/models/mobilenet_v1_0.75_224_2017_06_14.tar.gz)|317|2.59|68.4|88.2| +[MobileNet_v1_0.75_192](http://download.tensorflow.org/models/mobilenet_v1_0.75_192_2017_06_14.tar.gz)|233|2.59|67.4|87.3| +[MobileNet_v1_0.75_160](http://download.tensorflow.org/models/mobilenet_v1_0.75_160_2017_06_14.tar.gz)|162|2.59|65.2|86.1| +[MobileNet_v1_0.75_128](http://download.tensorflow.org/models/mobilenet_v1_0.75_128_2017_06_14.tar.gz)|104|2.59|61.8|83.6| +[MobileNet_v1_0.50_224](http://download.tensorflow.org/models/mobilenet_v1_0.50_224_2017_06_14.tar.gz)|150|1.34|64.0|85.4| +[MobileNet_v1_0.50_192](http://download.tensorflow.org/models/mobilenet_v1_0.50_192_2017_06_14.tar.gz)|110|1.34|62.1|84.0| +[MobileNet_v1_0.50_160](http://download.tensorflow.org/models/mobilenet_v1_0.50_160_2017_06_14.tar.gz)|77|1.34|59.9|82.5| +[MobileNet_v1_0.50_128](http://download.tensorflow.org/models/mobilenet_v1_0.50_128_2017_06_14.tar.gz)|49|1.34|56.2|79.6| +[MobileNet_v1_0.25_224](http://download.tensorflow.org/models/mobilenet_v1_0.25_224_2017_06_14.tar.gz)|41|0.47|50.6|75.0| +[MobileNet_v1_0.25_192](http://download.tensorflow.org/models/mobilenet_v1_0.25_192_2017_06_14.tar.gz)|34|0.47|49.0|73.6| +[MobileNet_v1_0.25_160](http://download.tensorflow.org/models/mobilenet_v1_0.25_160_2017_06_14.tar.gz)|21|0.47|46.0|70.7| +[MobileNet_v1_0.25_128](http://download.tensorflow.org/models/mobilenet_v1_0.25_128_2017_06_14.tar.gz)|14|0.47|41.3|66.2| + + +Here is an example of how to download the MobileNet_v1_1.0_224 checkpoint: + +```shell +$ CHECKPOINT_DIR=/tmp/checkpoints +$ mkdir ${CHECKPOINT_DIR} +$ wget http://download.tensorflow.org/models/mobilenet_v1_1.0_224_2017_06_14.tar.gz +$ tar -xvf mobilenet_v1_1.0_224_2017_06_14.tar.gz +$ mv mobilenet_v1_1.0_224.ckpt.* ${CHECKPOINT_DIR} +$ rm mobilenet_v1_1.0_224_2017_06_14.tar.gz +``` +More information on integrating MobileNets into your project can be found at the [TF-Slim Image Classification Library](https://github.com/tensorflow/models/blob/master/slim/README.md). + +To get started running models on-device go to [TensorFlow Mobile](https://www.tensorflow.org/mobile/). diff --git a/slim/nets/mobilenet_v1.png b/slim/nets/mobilenet_v1.png new file mode 100644 index 0000000000000000000000000000000000000000..a458345174a12073a653e26d6747914a4e58e516 GIT binary patch literal 100916 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 z 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
28S;~r`@&Z*|%K=(VQZy942UO zhl$jx^mqjC;Dd(0xV6l>tAqrvc7L#RS_1PT%FG--rbqPfFb$ofS|y#BQ(9I)zR`7= z%IJ2RA1}?)&F<87$q>0qy!Up0R)hKy;WD&yGG>3bg<>)^WdF~MrGrvkz{ z#e0xj97Y4(nR(3XS9OM6SV~*-vfZq0L?mHasNT!3!&c2WhO<-!Eeu1CVvaBk>bUGfVUSkHEAc{ z7P`OQbzoRJ02BMB%ieQn4)MBzUaMz?KLakKcCA%j`GC3K`BSKFzdxfyvOH6PovYvG zhdPbVl%9Bhd)c=;NvBHqZ>A#j9XkBUOXhAF5QU%l7al*3;!Aw-Ng@bW`J^dWoP;2Zt0%=RmVB0k7b+{;?Gg>XMIHd)(PW4Mb-ic#2QX|=9gi`U4yZ-ckk0Yj zu4=T&2!dkyU6eqGOldh?rd0o0&@&g-3Ai!UF8odBiNZfO2&1P0fvDhOD(VywdwIe3 zd6+BN^?pjnR%RsKtELin^8kQbG}my17_7H5Egp#gzt>mWAQEsdK__I@ZJEu_XMGRN z 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 1. + depth_multiplier: Float multiplier for the depth (number of channels) + for all convolution ops. The value must be greater than zero. Typical + usage will be to set this value in (0, 1) to reduce the number of + parameters or computation cost of the model. + conv_defs: A list of ConvDef namedtuples specifying the net architecture. + output_stride: An integer that specifies the requested ratio of input to + output spatial resolution. If not None, then we invoke atrous convolution + if necessary to prevent the network from reducing the spatial resolution + of the activation maps. Allowed values are 8 (accurate fully convolutional + mode), 16 (fast fully convolutional mode), 32 (classification mode). + scope: Optional variable_scope. + + Returns: + tensor_out: output tensor corresponding to the final_endpoint. + end_points: a set of activations for external use, for example summaries or + losses. + + Raises: + ValueError: if final_endpoint is not set to one of the predefined values, + or depth_multiplier <= 0, or the target output_stride is not + allowed. + """ + depth = lambda d: max(int(d * depth_multiplier), min_depth) + end_points = {} + + # Used to find thinned depths for each layer. + if depth_multiplier <= 0: + raise ValueError('depth_multiplier is not greater than zero.') + + if conv_defs is None: + conv_defs = _CONV_DEFS + + if output_stride is not None and output_stride not in [8, 16, 32]: + raise ValueError('Only allowed output_stride values are 8, 16, 32.') + + with tf.variable_scope(scope, 'MobilenetV1', [inputs]): + with slim.arg_scope([slim.conv2d, slim.separable_conv2d], padding='SAME'): + # The current_stride variable keeps track of the output stride of the + # activations, i.e., the running product of convolution strides up to the + # current network layer. This allows us to invoke atrous convolution + # whenever applying the next convolution would result in the activations + # having output stride larger than the target output_stride. + current_stride = 1 + + # The atrous convolution rate parameter. + rate = 1 + + net = inputs + for i, conv_def in enumerate(conv_defs): + end_point_base = 'Conv2d_%d' % i + + if output_stride is not None and current_stride == output_stride: + # If we have reached the target output_stride, then we need to employ + # atrous convolution with stride=1 and multiply the atrous rate by the + # current unit's stride for use in subsequent layers. + layer_stride = 1 + layer_rate = rate + rate *= conv_def.stride + else: + layer_stride = conv_def.stride + layer_rate = 1 + current_stride *= conv_def.stride + + if isinstance(conv_def, Conv): + end_point = end_point_base + net = slim.conv2d(net, depth(conv_def.depth), conv_def.kernel, + stride=conv_def.stride, + normalizer_fn=slim.batch_norm, + scope=end_point) + end_points[end_point] = net + if end_point == final_endpoint: + return net, end_points + + elif isinstance(conv_def, DepthSepConv): + end_point = end_point_base + '_depthwise' + + # By passing filters=None + # separable_conv2d produces only a depthwise convolution layer + net = slim.separable_conv2d(net, None, conv_def.kernel, + depth_multiplier=1, + stride=layer_stride, + rate=layer_rate, + normalizer_fn=slim.batch_norm, + scope=end_point) + + end_points[end_point] = net + if end_point == final_endpoint: + return net, end_points + + end_point = end_point_base + '_pointwise' + + net = slim.conv2d(net, depth(conv_def.depth), [1, 1], + stride=1, + normalizer_fn=slim.batch_norm, + scope=end_point) + + end_points[end_point] = net + if end_point == final_endpoint: + return net, end_points + else: + raise ValueError('Unknown convolution type %s for layer %d' + % (conv_def.ltype, i)) + raise ValueError('Unknown final endpoint %s' % final_endpoint) + + +def mobilenet_v1(inputs, + num_classes=1000, + dropout_keep_prob=0.999, + is_training=True, + min_depth=8, + depth_multiplier=1.0, + conv_defs=None, + prediction_fn=tf.contrib.layers.softmax, + spatial_squeeze=True, + reuse=None, + scope='MobilenetV1'): + """Mobilenet v1 model for classification. + + Args: + inputs: a tensor of shape [batch_size, height, width, channels]. + num_classes: number of predicted classes. + dropout_keep_prob: the percentage of activation values that are retained. + is_training: whether is training or not. + min_depth: Minimum depth value (number of channels) for all convolution ops. + Enforced when depth_multiplier < 1, and not an active constraint when + depth_multiplier >= 1. + depth_multiplier: Float multiplier for the depth (number of channels) + for all convolution ops. The value must be greater than zero. Typical + usage will be to set this value in (0, 1) to reduce the number of + parameters or computation cost of the model. + conv_defs: A list of ConvDef namedtuples specifying the net architecture. + prediction_fn: a function to get predictions out of logits. + spatial_squeeze: if True, logits is of shape is [B, C], if false logits is + of shape [B, 1, 1, C], where B is batch_size and C is number of classes. + reuse: whether or not the network and its variables should be reused. To be + able to reuse 'scope' must be given. + scope: Optional variable_scope. + + Returns: + logits: the pre-softmax activations, a tensor of size + [batch_size, num_classes] + end_points: a dictionary from components of the network to the corresponding + activation. + + Raises: + ValueError: Input rank is invalid. + """ + input_shape = inputs.get_shape().as_list() + if len(input_shape) != 4: + raise ValueError('Invalid input tensor rank, expected 4, was: %d' % + len(input_shape)) + + with tf.variable_scope(scope, 'MobilenetV1', [inputs, num_classes], + reuse=reuse) as scope: + with slim.arg_scope([slim.batch_norm, slim.dropout], + is_training=is_training): + net, end_points = mobilenet_v1_base(inputs, scope=scope, + min_depth=min_depth, + depth_multiplier=depth_multiplier, + conv_defs=conv_defs) + with tf.variable_scope('Logits'): + kernel_size = _reduced_kernel_size_for_small_input(net, [7, 7]) + net = slim.avg_pool2d(net, kernel_size, padding='VALID', + scope='AvgPool_1a') + end_points['AvgPool_1a'] = net + # 1 x 1 x 1024 + net = slim.dropout(net, keep_prob=dropout_keep_prob, scope='Dropout_1b') + logits = slim.conv2d(net, num_classes, [1, 1], activation_fn=None, + normalizer_fn=None, scope='Conv2d_1c_1x1') + if spatial_squeeze: + logits = tf.squeeze(logits, [1, 2], name='SpatialSqueeze') + end_points['Logits'] = logits + if prediction_fn: + end_points['Predictions'] = prediction_fn(logits, scope='Predictions') + return logits, end_points + +mobilenet_v1.default_image_size = 224 + + +def _reduced_kernel_size_for_small_input(input_tensor, kernel_size): + """Define kernel size which is automatically reduced for small input. + + If the shape of the input images is unknown at graph construction time this + function assumes that the input images are large enough. + + Args: + input_tensor: input tensor of size [batch_size, height, width, channels]. + kernel_size: desired kernel size of length 2: [kernel_height, kernel_width] + + Returns: + a tensor with the kernel size. + """ + shape = input_tensor.get_shape().as_list() + if shape[1] is None or shape[2] is None: + kernel_size_out = kernel_size + else: + kernel_size_out = [min(shape[1], kernel_size[0]), + min(shape[2], kernel_size[1])] + return kernel_size_out + + +def mobilenet_v1_arg_scope(is_training=True, + weight_decay=0.00004, + stddev=0.09, + regularize_depthwise=False): + """Defines the default MobilenetV1 arg scope. + + Args: + is_training: Whether or not we're training the model. + weight_decay: The weight decay to use for regularizing the model. + stddev: The standard deviation of the trunctated normal weight initializer. + regularize_depthwise: Whether or not apply regularization on depthwise. + + Returns: + An `arg_scope` to use for the mobilenet v1 model. + """ + batch_norm_params = { + 'is_training': is_training, + 'center': True, + 'scale': True, + 'decay': 0.9997, + 'epsilon': 0.001, + } + + # Set weight_decay for weights in Conv and DepthSepConv layers. + weights_init = tf.truncated_normal_initializer(stddev=stddev) + regularizer = tf.contrib.layers.l2_regularizer(weight_decay) + if regularize_depthwise: + depthwise_regularizer = regularizer + else: + depthwise_regularizer = None + with slim.arg_scope([slim.conv2d, slim.separable_conv2d], + weights_initializer=weights_init, + activation_fn=tf.nn.relu6, normalizer_fn=slim.batch_norm): + with slim.arg_scope([slim.batch_norm], **batch_norm_params): + with slim.arg_scope([slim.conv2d], weights_regularizer=regularizer): + with slim.arg_scope([slim.separable_conv2d], + weights_regularizer=depthwise_regularizer) as sc: + return sc diff --git a/slim/nets/mobilenet_v1_test.py b/slim/nets/mobilenet_v1_test.py new file mode 100644 index 000000000..44e66446b --- /dev/null +++ b/slim/nets/mobilenet_v1_test.py @@ -0,0 +1,450 @@ +# 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 MobileNet v1.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +import tensorflow as tf + +from nets import mobilenet_v1 + +slim = tf.contrib.slim + + +class MobilenetV1Test(tf.test.TestCase): + + def testBuildClassificationNetwork(self): + batch_size = 5 + height, width = 224, 224 + num_classes = 1000 + + inputs = tf.random_uniform((batch_size, height, width, 3)) + logits, end_points = mobilenet_v1.mobilenet_v1(inputs, num_classes) + self.assertTrue(logits.op.name.startswith('MobilenetV1/Logits')) + self.assertListEqual(logits.get_shape().as_list(), + [batch_size, num_classes]) + self.assertTrue('Predictions' in end_points) + self.assertListEqual(end_points['Predictions'].get_shape().as_list(), + [batch_size, num_classes]) + + def testBuildBaseNetwork(self): + batch_size = 5 + height, width = 224, 224 + + inputs = tf.random_uniform((batch_size, height, width, 3)) + net, end_points = mobilenet_v1.mobilenet_v1_base(inputs) + self.assertTrue(net.op.name.startswith('MobilenetV1/Conv2d_13')) + self.assertListEqual(net.get_shape().as_list(), + [batch_size, 7, 7, 1024]) + expected_endpoints = ['Conv2d_0', + 'Conv2d_1_depthwise', 'Conv2d_1_pointwise', + 'Conv2d_2_depthwise', 'Conv2d_2_pointwise', + 'Conv2d_3_depthwise', 'Conv2d_3_pointwise', + 'Conv2d_4_depthwise', 'Conv2d_4_pointwise', + 'Conv2d_5_depthwise', 'Conv2d_5_pointwise', + 'Conv2d_6_depthwise', 'Conv2d_6_pointwise', + 'Conv2d_7_depthwise', 'Conv2d_7_pointwise', + 'Conv2d_8_depthwise', 'Conv2d_8_pointwise', + 'Conv2d_9_depthwise', 'Conv2d_9_pointwise', + 'Conv2d_10_depthwise', 'Conv2d_10_pointwise', + 'Conv2d_11_depthwise', 'Conv2d_11_pointwise', + 'Conv2d_12_depthwise', 'Conv2d_12_pointwise', + 'Conv2d_13_depthwise', 'Conv2d_13_pointwise'] + self.assertItemsEqual(end_points.keys(), expected_endpoints) + + def testBuildOnlyUptoFinalEndpoint(self): + batch_size = 5 + height, width = 224, 224 + endpoints = ['Conv2d_0', + 'Conv2d_1_depthwise', 'Conv2d_1_pointwise', + 'Conv2d_2_depthwise', 'Conv2d_2_pointwise', + 'Conv2d_3_depthwise', 'Conv2d_3_pointwise', + 'Conv2d_4_depthwise', 'Conv2d_4_pointwise', + 'Conv2d_5_depthwise', 'Conv2d_5_pointwise', + 'Conv2d_6_depthwise', 'Conv2d_6_pointwise', + 'Conv2d_7_depthwise', 'Conv2d_7_pointwise', + 'Conv2d_8_depthwise', 'Conv2d_8_pointwise', + 'Conv2d_9_depthwise', 'Conv2d_9_pointwise', + 'Conv2d_10_depthwise', 'Conv2d_10_pointwise', + 'Conv2d_11_depthwise', 'Conv2d_11_pointwise', + 'Conv2d_12_depthwise', 'Conv2d_12_pointwise', + 'Conv2d_13_depthwise', 'Conv2d_13_pointwise'] + for index, endpoint in enumerate(endpoints): + with tf.Graph().as_default(): + inputs = tf.random_uniform((batch_size, height, width, 3)) + out_tensor, end_points = mobilenet_v1.mobilenet_v1_base( + inputs, final_endpoint=endpoint) + self.assertTrue(out_tensor.op.name.startswith( + 'MobilenetV1/' + endpoint)) + self.assertItemsEqual(endpoints[:index+1], end_points) + + def testBuildCustomNetworkUsingConvDefs(self): + batch_size = 5 + height, width = 224, 224 + conv_defs = [ + mobilenet_v1.Conv(kernel=[3, 3], stride=2, depth=32), + mobilenet_v1.DepthSepConv(kernel=[3, 3], stride=1, depth=64), + mobilenet_v1.DepthSepConv(kernel=[3, 3], stride=2, depth=128), + mobilenet_v1.DepthSepConv(kernel=[3, 3], stride=1, depth=512) + ] + + inputs = tf.random_uniform((batch_size, height, width, 3)) + net, end_points = mobilenet_v1.mobilenet_v1_base( + inputs, final_endpoint='Conv2d_3_pointwise', conv_defs=conv_defs) + self.assertTrue(net.op.name.startswith('MobilenetV1/Conv2d_3')) + self.assertListEqual(net.get_shape().as_list(), + [batch_size, 56, 56, 512]) + expected_endpoints = ['Conv2d_0', + 'Conv2d_1_depthwise', 'Conv2d_1_pointwise', + 'Conv2d_2_depthwise', 'Conv2d_2_pointwise', + 'Conv2d_3_depthwise', 'Conv2d_3_pointwise'] + self.assertItemsEqual(end_points.keys(), expected_endpoints) + + def testBuildAndCheckAllEndPointsUptoConv2d_13(self): + batch_size = 5 + height, width = 224, 224 + + inputs = tf.random_uniform((batch_size, height, width, 3)) + with slim.arg_scope([slim.conv2d, slim.separable_conv2d], + normalizer_fn=slim.batch_norm): + _, end_points = mobilenet_v1.mobilenet_v1_base( + inputs, final_endpoint='Conv2d_13_pointwise') + endpoints_shapes = {'Conv2d_0': [batch_size, 112, 112, 32], + 'Conv2d_1_depthwise': [batch_size, 112, 112, 32], + 'Conv2d_1_pointwise': [batch_size, 112, 112, 64], + 'Conv2d_2_depthwise': [batch_size, 56, 56, 64], + 'Conv2d_2_pointwise': [batch_size, 56, 56, 128], + 'Conv2d_3_depthwise': [batch_size, 56, 56, 128], + 'Conv2d_3_pointwise': [batch_size, 56, 56, 128], + 'Conv2d_4_depthwise': [batch_size, 28, 28, 128], + 'Conv2d_4_pointwise': [batch_size, 28, 28, 256], + 'Conv2d_5_depthwise': [batch_size, 28, 28, 256], + 'Conv2d_5_pointwise': [batch_size, 28, 28, 256], + 'Conv2d_6_depthwise': [batch_size, 14, 14, 256], + 'Conv2d_6_pointwise': [batch_size, 14, 14, 512], + 'Conv2d_7_depthwise': [batch_size, 14, 14, 512], + 'Conv2d_7_pointwise': [batch_size, 14, 14, 512], + 'Conv2d_8_depthwise': [batch_size, 14, 14, 512], + 'Conv2d_8_pointwise': [batch_size, 14, 14, 512], + 'Conv2d_9_depthwise': [batch_size, 14, 14, 512], + 'Conv2d_9_pointwise': [batch_size, 14, 14, 512], + 'Conv2d_10_depthwise': [batch_size, 14, 14, 512], + 'Conv2d_10_pointwise': [batch_size, 14, 14, 512], + 'Conv2d_11_depthwise': [batch_size, 14, 14, 512], + 'Conv2d_11_pointwise': [batch_size, 14, 14, 512], + 'Conv2d_12_depthwise': [batch_size, 7, 7, 512], + 'Conv2d_12_pointwise': [batch_size, 7, 7, 1024], + 'Conv2d_13_depthwise': [batch_size, 7, 7, 1024], + 'Conv2d_13_pointwise': [batch_size, 7, 7, 1024]} + self.assertItemsEqual(endpoints_shapes.keys(), end_points.keys()) + for endpoint_name, expected_shape in endpoints_shapes.iteritems(): + self.assertTrue(endpoint_name in end_points) + self.assertListEqual(end_points[endpoint_name].get_shape().as_list(), + expected_shape) + + def testOutputStride16BuildAndCheckAllEndPointsUptoConv2d_13(self): + batch_size = 5 + height, width = 224, 224 + output_stride = 16 + + inputs = tf.random_uniform((batch_size, height, width, 3)) + with slim.arg_scope([slim.conv2d, slim.separable_conv2d], + normalizer_fn=slim.batch_norm): + _, end_points = mobilenet_v1.mobilenet_v1_base( + inputs, output_stride=output_stride, + final_endpoint='Conv2d_13_pointwise') + endpoints_shapes = {'Conv2d_0': [batch_size, 112, 112, 32], + 'Conv2d_1_depthwise': [batch_size, 112, 112, 32], + 'Conv2d_1_pointwise': [batch_size, 112, 112, 64], + 'Conv2d_2_depthwise': [batch_size, 56, 56, 64], + 'Conv2d_2_pointwise': [batch_size, 56, 56, 128], + 'Conv2d_3_depthwise': [batch_size, 56, 56, 128], + 'Conv2d_3_pointwise': [batch_size, 56, 56, 128], + 'Conv2d_4_depthwise': [batch_size, 28, 28, 128], + 'Conv2d_4_pointwise': [batch_size, 28, 28, 256], + 'Conv2d_5_depthwise': [batch_size, 28, 28, 256], + 'Conv2d_5_pointwise': [batch_size, 28, 28, 256], + 'Conv2d_6_depthwise': [batch_size, 14, 14, 256], + 'Conv2d_6_pointwise': [batch_size, 14, 14, 512], + 'Conv2d_7_depthwise': [batch_size, 14, 14, 512], + 'Conv2d_7_pointwise': [batch_size, 14, 14, 512], + 'Conv2d_8_depthwise': [batch_size, 14, 14, 512], + 'Conv2d_8_pointwise': [batch_size, 14, 14, 512], + 'Conv2d_9_depthwise': [batch_size, 14, 14, 512], + 'Conv2d_9_pointwise': [batch_size, 14, 14, 512], + 'Conv2d_10_depthwise': [batch_size, 14, 14, 512], + 'Conv2d_10_pointwise': [batch_size, 14, 14, 512], + 'Conv2d_11_depthwise': [batch_size, 14, 14, 512], + 'Conv2d_11_pointwise': [batch_size, 14, 14, 512], + 'Conv2d_12_depthwise': [batch_size, 14, 14, 512], + 'Conv2d_12_pointwise': [batch_size, 14, 14, 1024], + 'Conv2d_13_depthwise': [batch_size, 14, 14, 1024], + 'Conv2d_13_pointwise': [batch_size, 14, 14, 1024]} + self.assertItemsEqual(endpoints_shapes.keys(), end_points.keys()) + for endpoint_name, expected_shape in endpoints_shapes.iteritems(): + self.assertTrue(endpoint_name in end_points) + self.assertListEqual(end_points[endpoint_name].get_shape().as_list(), + expected_shape) + + def testOutputStride8BuildAndCheckAllEndPointsUptoConv2d_13(self): + batch_size = 5 + height, width = 224, 224 + output_stride = 8 + + inputs = tf.random_uniform((batch_size, height, width, 3)) + with slim.arg_scope([slim.conv2d, slim.separable_conv2d], + normalizer_fn=slim.batch_norm): + _, end_points = mobilenet_v1.mobilenet_v1_base( + inputs, output_stride=output_stride, + final_endpoint='Conv2d_13_pointwise') + endpoints_shapes = {'Conv2d_0': [batch_size, 112, 112, 32], + 'Conv2d_1_depthwise': [batch_size, 112, 112, 32], + 'Conv2d_1_pointwise': [batch_size, 112, 112, 64], + 'Conv2d_2_depthwise': [batch_size, 56, 56, 64], + 'Conv2d_2_pointwise': [batch_size, 56, 56, 128], + 'Conv2d_3_depthwise': [batch_size, 56, 56, 128], + 'Conv2d_3_pointwise': [batch_size, 56, 56, 128], + 'Conv2d_4_depthwise': [batch_size, 28, 28, 128], + 'Conv2d_4_pointwise': [batch_size, 28, 28, 256], + 'Conv2d_5_depthwise': [batch_size, 28, 28, 256], + 'Conv2d_5_pointwise': [batch_size, 28, 28, 256], + 'Conv2d_6_depthwise': [batch_size, 28, 28, 256], + 'Conv2d_6_pointwise': [batch_size, 28, 28, 512], + 'Conv2d_7_depthwise': [batch_size, 28, 28, 512], + 'Conv2d_7_pointwise': [batch_size, 28, 28, 512], + 'Conv2d_8_depthwise': [batch_size, 28, 28, 512], + 'Conv2d_8_pointwise': [batch_size, 28, 28, 512], + 'Conv2d_9_depthwise': [batch_size, 28, 28, 512], + 'Conv2d_9_pointwise': [batch_size, 28, 28, 512], + 'Conv2d_10_depthwise': [batch_size, 28, 28, 512], + 'Conv2d_10_pointwise': [batch_size, 28, 28, 512], + 'Conv2d_11_depthwise': [batch_size, 28, 28, 512], + 'Conv2d_11_pointwise': [batch_size, 28, 28, 512], + 'Conv2d_12_depthwise': [batch_size, 28, 28, 512], + 'Conv2d_12_pointwise': [batch_size, 28, 28, 1024], + 'Conv2d_13_depthwise': [batch_size, 28, 28, 1024], + 'Conv2d_13_pointwise': [batch_size, 28, 28, 1024]} + self.assertItemsEqual(endpoints_shapes.keys(), end_points.keys()) + for endpoint_name, expected_shape in endpoints_shapes.iteritems(): + self.assertTrue(endpoint_name in end_points) + self.assertListEqual(end_points[endpoint_name].get_shape().as_list(), + expected_shape) + + def testBuildAndCheckAllEndPointsApproximateFaceNet(self): + batch_size = 5 + height, width = 128, 128 + + inputs = tf.random_uniform((batch_size, height, width, 3)) + with slim.arg_scope([slim.conv2d, slim.separable_conv2d], + normalizer_fn=slim.batch_norm): + _, end_points = mobilenet_v1.mobilenet_v1_base( + inputs, final_endpoint='Conv2d_13_pointwise', depth_multiplier=0.75) + # For the Conv2d_0 layer FaceNet has depth=16 + endpoints_shapes = {'Conv2d_0': [batch_size, 64, 64, 24], + 'Conv2d_1_depthwise': [batch_size, 64, 64, 24], + 'Conv2d_1_pointwise': [batch_size, 64, 64, 48], + 'Conv2d_2_depthwise': [batch_size, 32, 32, 48], + 'Conv2d_2_pointwise': [batch_size, 32, 32, 96], + 'Conv2d_3_depthwise': [batch_size, 32, 32, 96], + 'Conv2d_3_pointwise': [batch_size, 32, 32, 96], + 'Conv2d_4_depthwise': [batch_size, 16, 16, 96], + 'Conv2d_4_pointwise': [batch_size, 16, 16, 192], + 'Conv2d_5_depthwise': [batch_size, 16, 16, 192], + 'Conv2d_5_pointwise': [batch_size, 16, 16, 192], + 'Conv2d_6_depthwise': [batch_size, 8, 8, 192], + 'Conv2d_6_pointwise': [batch_size, 8, 8, 384], + 'Conv2d_7_depthwise': [batch_size, 8, 8, 384], + 'Conv2d_7_pointwise': [batch_size, 8, 8, 384], + 'Conv2d_8_depthwise': [batch_size, 8, 8, 384], + 'Conv2d_8_pointwise': [batch_size, 8, 8, 384], + 'Conv2d_9_depthwise': [batch_size, 8, 8, 384], + 'Conv2d_9_pointwise': [batch_size, 8, 8, 384], + 'Conv2d_10_depthwise': [batch_size, 8, 8, 384], + 'Conv2d_10_pointwise': [batch_size, 8, 8, 384], + 'Conv2d_11_depthwise': [batch_size, 8, 8, 384], + 'Conv2d_11_pointwise': [batch_size, 8, 8, 384], + 'Conv2d_12_depthwise': [batch_size, 4, 4, 384], + 'Conv2d_12_pointwise': [batch_size, 4, 4, 768], + 'Conv2d_13_depthwise': [batch_size, 4, 4, 768], + 'Conv2d_13_pointwise': [batch_size, 4, 4, 768]} + self.assertItemsEqual(endpoints_shapes.keys(), end_points.keys()) + for endpoint_name, expected_shape in endpoints_shapes.iteritems(): + self.assertTrue(endpoint_name in end_points) + self.assertListEqual(end_points[endpoint_name].get_shape().as_list(), + expected_shape) + + def testModelHasExpectedNumberOfParameters(self): + batch_size = 5 + height, width = 224, 224 + inputs = tf.random_uniform((batch_size, height, width, 3)) + with slim.arg_scope([slim.conv2d, slim.separable_conv2d], + normalizer_fn=slim.batch_norm): + mobilenet_v1.mobilenet_v1_base(inputs) + total_params, _ = slim.model_analyzer.analyze_vars( + slim.get_model_variables()) + self.assertAlmostEqual(3217920L, total_params) + + def testBuildEndPointsWithDepthMultiplierLessThanOne(self): + batch_size = 5 + height, width = 224, 224 + num_classes = 1000 + + inputs = tf.random_uniform((batch_size, height, width, 3)) + _, end_points = mobilenet_v1.mobilenet_v1(inputs, num_classes) + + endpoint_keys = [key for key in end_points.keys() if key.startswith('Conv')] + + _, end_points_with_multiplier = mobilenet_v1.mobilenet_v1( + inputs, num_classes, scope='depth_multiplied_net', + depth_multiplier=0.5) + + for key in endpoint_keys: + original_depth = end_points[key].get_shape().as_list()[3] + new_depth = end_points_with_multiplier[key].get_shape().as_list()[3] + self.assertEqual(0.5 * original_depth, new_depth) + + def testBuildEndPointsWithDepthMultiplierGreaterThanOne(self): + batch_size = 5 + height, width = 224, 224 + num_classes = 1000 + + inputs = tf.random_uniform((batch_size, height, width, 3)) + _, end_points = mobilenet_v1.mobilenet_v1(inputs, num_classes) + + endpoint_keys = [key for key in end_points.keys() + if key.startswith('Mixed') or key.startswith('Conv')] + + _, end_points_with_multiplier = mobilenet_v1.mobilenet_v1( + inputs, num_classes, scope='depth_multiplied_net', + depth_multiplier=2.0) + + for key in endpoint_keys: + original_depth = end_points[key].get_shape().as_list()[3] + new_depth = end_points_with_multiplier[key].get_shape().as_list()[3] + self.assertEqual(2.0 * original_depth, new_depth) + + def testRaiseValueErrorWithInvalidDepthMultiplier(self): + batch_size = 5 + height, width = 224, 224 + num_classes = 1000 + + inputs = tf.random_uniform((batch_size, height, width, 3)) + with self.assertRaises(ValueError): + _ = mobilenet_v1.mobilenet_v1( + inputs, num_classes, depth_multiplier=-0.1) + with self.assertRaises(ValueError): + _ = mobilenet_v1.mobilenet_v1( + inputs, num_classes, depth_multiplier=0.0) + + def testHalfSizeImages(self): + batch_size = 5 + height, width = 112, 112 + num_classes = 1000 + + inputs = tf.random_uniform((batch_size, height, width, 3)) + logits, end_points = mobilenet_v1.mobilenet_v1(inputs, num_classes) + self.assertTrue(logits.op.name.startswith('MobilenetV1/Logits')) + self.assertListEqual(logits.get_shape().as_list(), + [batch_size, num_classes]) + pre_pool = end_points['Conv2d_13_pointwise'] + self.assertListEqual(pre_pool.get_shape().as_list(), + [batch_size, 4, 4, 1024]) + + def testUnknownImageShape(self): + tf.reset_default_graph() + batch_size = 2 + height, width = 224, 224 + num_classes = 1000 + input_np = np.random.uniform(0, 1, (batch_size, height, width, 3)) + with self.test_session() as sess: + inputs = tf.placeholder(tf.float32, shape=(batch_size, None, None, 3)) + logits, end_points = mobilenet_v1.mobilenet_v1(inputs, num_classes) + self.assertTrue(logits.op.name.startswith('MobilenetV1/Logits')) + self.assertListEqual(logits.get_shape().as_list(), + [batch_size, num_classes]) + pre_pool = end_points['Conv2d_13_pointwise'] + feed_dict = {inputs: input_np} + tf.global_variables_initializer().run() + pre_pool_out = sess.run(pre_pool, feed_dict=feed_dict) + self.assertListEqual(list(pre_pool_out.shape), [batch_size, 7, 7, 1024]) + + def testUnknowBatchSize(self): + batch_size = 1 + height, width = 224, 224 + num_classes = 1000 + + inputs = tf.placeholder(tf.float32, (None, height, width, 3)) + logits, _ = mobilenet_v1.mobilenet_v1(inputs, num_classes) + self.assertTrue(logits.op.name.startswith('MobilenetV1/Logits')) + self.assertListEqual(logits.get_shape().as_list(), + [None, num_classes]) + images = tf.random_uniform((batch_size, height, width, 3)) + + with self.test_session() as sess: + sess.run(tf.global_variables_initializer()) + output = sess.run(logits, {inputs: images.eval()}) + self.assertEquals(output.shape, (batch_size, num_classes)) + + def testEvaluation(self): + batch_size = 2 + height, width = 224, 224 + num_classes = 1000 + + eval_inputs = tf.random_uniform((batch_size, height, width, 3)) + logits, _ = mobilenet_v1.mobilenet_v1(eval_inputs, num_classes, + is_training=False) + predictions = tf.argmax(logits, 1) + + with self.test_session() as sess: + sess.run(tf.global_variables_initializer()) + output = sess.run(predictions) + self.assertEquals(output.shape, (batch_size,)) + + def testTrainEvalWithReuse(self): + train_batch_size = 5 + eval_batch_size = 2 + height, width = 150, 150 + num_classes = 1000 + + train_inputs = tf.random_uniform((train_batch_size, height, width, 3)) + mobilenet_v1.mobilenet_v1(train_inputs, num_classes) + eval_inputs = tf.random_uniform((eval_batch_size, height, width, 3)) + logits, _ = mobilenet_v1.mobilenet_v1(eval_inputs, num_classes, + reuse=True) + predictions = tf.argmax(logits, 1) + + with self.test_session() as sess: + sess.run(tf.global_variables_initializer()) + output = sess.run(predictions) + self.assertEquals(output.shape, (eval_batch_size,)) + + def testLogitsNotSqueezed(self): + num_classes = 25 + images = tf.random_uniform([1, 224, 224, 3]) + logits, _ = mobilenet_v1.mobilenet_v1(images, + num_classes=num_classes, + spatial_squeeze=False) + + with self.test_session() as sess: + tf.global_variables_initializer().run() + logits_out = sess.run(logits) + self.assertListEqual(list(logits_out.shape), [1, 1, 1, num_classes]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/slim/nets/nets_factory.py b/slim/nets/nets_factory.py index bd8d7127a..7c0416167 100644 --- a/slim/nets/nets_factory.py +++ b/slim/nets/nets_factory.py @@ -25,6 +25,7 @@ from nets import alexnet from nets import cifarnet from nets import inception from nets import lenet +from nets import mobilenet_v1 from nets import overfeat from nets import resnet_v1 from nets import resnet_v2 @@ -52,6 +53,7 @@ networks_map = {'alexnet_v2': alexnet.alexnet_v2, 'resnet_v2_101': resnet_v2.resnet_v2_101, 'resnet_v2_152': resnet_v2.resnet_v2_152, 'resnet_v2_200': resnet_v2.resnet_v2_200, + 'mobilenet_v1': mobilenet_v1.mobilenet_v1, } arg_scopes_map = {'alexnet_v2': alexnet.alexnet_v2_arg_scope, @@ -75,6 +77,7 @@ arg_scopes_map = {'alexnet_v2': alexnet.alexnet_v2_arg_scope, 'resnet_v2_101': resnet_v2.resnet_arg_scope, 'resnet_v2_152': resnet_v2.resnet_arg_scope, 'resnet_v2_200': resnet_v2.resnet_arg_scope, + 'mobilenet_v1': mobilenet_v1.mobilenet_v1_arg_scope, } @@ -97,10 +100,10 @@ def get_network_fn(name, num_classes, weight_decay=0.0, is_training=False): """ if name not in networks_map: raise ValueError('Name of network unknown %s' % name) + arg_scope = arg_scopes_map[name](weight_decay=weight_decay) func = networks_map[name] @functools.wraps(func) def network_fn(images): - arg_scope = arg_scopes_map[name](weight_decay=weight_decay) with slim.arg_scope(arg_scope): return func(images, num_classes, is_training=is_training) if hasattr(func, 'default_image_size'): diff --git a/slim/preprocessing/preprocessing_factory.py b/slim/preprocessing/preprocessing_factory.py index 35f8645ef..3ab79a012 100644 --- a/slim/preprocessing/preprocessing_factory.py +++ b/slim/preprocessing/preprocessing_factory.py @@ -53,12 +53,10 @@ def get_preprocessing(name, is_training=False): 'inception_v4': inception_preprocessing, 'inception_resnet_v2': inception_preprocessing, 'lenet': lenet_preprocessing, + 'mobilenet_v1': inception_preprocessing, 'resnet_v1_50': vgg_preprocessing, 'resnet_v1_101': vgg_preprocessing, 'resnet_v1_152': vgg_preprocessing, - 'resnet_v2_50': vgg_preprocessing, - 'resnet_v2_101': vgg_preprocessing, - 'resnet_v2_152': vgg_preprocessing, 'vgg': vgg_preprocessing, 'vgg_a': vgg_preprocessing, 'vgg_16': vgg_preprocessing, -- GitLab From 7ad450b84309b82bccb0e8f2e40e9559f33cd258 Mon Sep 17 00:00:00 2001 From: Sergio Guadarrama Date: Tue, 13 Jun 2017 22:05:03 -0700 Subject: [PATCH 035/110] Update README.md (#1552) --- slim/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/slim/README.md b/slim/README.md index 628931f7c..6fe5a7183 100644 --- a/slim/README.md +++ b/slim/README.md @@ -204,6 +204,7 @@ Model | TF-Slim File | Checkpoint | Top-1 Accuracy| Top-5 Accuracy | [MobileNet_v1_1.0_224](https://arxiv.org/pdf/1704.04861.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/mobilenet_v1.py)|[mobilenet_v1_1.0_224_2017_06_14.tar.gz](http://download.tensorflow.org/models/mobilenet_v1_1.0_224_2017_06_14.tar.gz)|70.7|89.5| [MobileNet_v1_0.50_160](https://arxiv.org/pdf/1704.04861.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/mobilenet_v1.py)|[mobilenet_v1_0.50_160_2017_06_14.tar.gz](http://download.tensorflow.org/models/mobilenet_v1_0.50_160_2017_06_14.tar.gz)|59.9|82.5| [MobileNet_v1_0.25_128](https://arxiv.org/pdf/1704.04861.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/mobilenet_v1.py)|[mobilenet_v1_0.25_128_2017_06_14.tar.gz](http://download.tensorflow.org/models/mobilenet_v1_0.25_128_2017_06_14.tar.gz)|41.3|66.2| + ^ ResNet V2 models use Inception pre-processing and input image size of 299 (use `--preprocessing_name inception --eval_image_size 299` when using `eval_image_classifier.py`). Performance numbers for ResNet V2 models are -- GitLab From cb31aeffd6176df1a5da18ef9dd1ec78279a856f Mon Sep 17 00:00:00 2001 From: James Pruegsanusak Date: Thu, 15 Jun 2017 02:10:05 +0800 Subject: [PATCH 036/110] Fix link to make image show up in mobilenet_v1.md (#1554) Use a relative link instead --- slim/nets/mobilenet_v1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slim/nets/mobilenet_v1.md b/slim/nets/mobilenet_v1.md index 3ce231176..342f30561 100644 --- a/slim/nets/mobilenet_v1.md +++ b/slim/nets/mobilenet_v1.md @@ -4,7 +4,7 @@ MobileNets trade off between latency, size and accuracy while comparing favorably with popular models from the literature. -![alt text](https://github.com/tensorflow/models/tree/master/slim/nets/mobilenet_v1.png, "MobileNet Graph") +![alt text](mobilenet_v1.png "MobileNet Graph") # Pre-trained Models -- GitLab From 7e0016c5654ba6d251034834dcf8154fa4c36f70 Mon Sep 17 00:00:00 2001 From: derekjchow Date: Wed, 14 Jun 2017 11:11:12 -0700 Subject: [PATCH 037/110] Update inception_resnet_v2.py (#1555) - Add inception_resnet_v2_base - Provide option to use SAME padding for all inception resnet v2 layers. This is to align feature maps sizes. --- slim/nets/inception.py | 1 + slim/nets/inception_resnet_v2.py | 321 ++++++++++++++++---------- slim/nets/inception_resnet_v2_test.py | 135 ++++++++++- 3 files changed, 332 insertions(+), 125 deletions(-) diff --git a/slim/nets/inception.py b/slim/nets/inception.py index 806c30bee..b69cd2aac 100644 --- a/slim/nets/inception.py +++ b/slim/nets/inception.py @@ -21,6 +21,7 @@ from __future__ import print_function # pylint: disable=unused-import from nets.inception_resnet_v2 import inception_resnet_v2 from nets.inception_resnet_v2 import inception_resnet_v2_arg_scope +from nets.inception_resnet_v2 import inception_resnet_v2_base from nets.inception_v1 import inception_v1 from nets.inception_v1 import inception_v1_arg_scope from nets.inception_v1 import inception_v1_base diff --git a/slim/nets/inception_resnet_v2.py b/slim/nets/inception_resnet_v2.py index b5a54c5b6..ec8387a33 100644 --- a/slim/nets/inception_resnet_v2.py +++ b/slim/nets/inception_resnet_v2.py @@ -91,10 +91,187 @@ def block8(net, scale=1.0, activation_fn=tf.nn.relu, scope=None, reuse=None): return net +def inception_resnet_v2_base(inputs, + final_endpoint='Conv2d_7b_1x1', + output_stride=16, + align_feature_maps=False, + scope=None): + """Inception model from http://arxiv.org/abs/1602.07261. + + Constructs an Inception Resnet v2 network from inputs to the given final + endpoint. This method can construct the network up to the final inception + block Conv2d_7b_1x1. + + Args: + inputs: a tensor of size [batch_size, height, width, channels]. + final_endpoint: specifies the endpoint to construct the network up to. It + can be one of ['Conv2d_1a_3x3', 'Conv2d_2a_3x3', 'Conv2d_2b_3x3', + 'MaxPool_3a_3x3', 'Conv2d_3b_1x1', 'Conv2d_4a_3x3', 'MaxPool_5a_3x3', + 'Mixed_5b', 'Mixed_6a', 'PreAuxLogits', 'Mixed_7a', 'Conv2d_7b_1x1'] + output_stride: A scalar that specifies the requested ratio of input to + output spatial resolution. Only supports 8 and 16. + align_feature_maps: When true, changes all the VALID paddings in the network + to SAME padding so that the feature maps are aligned. + scope: Optional variable_scope. + + Returns: + tensor_out: output tensor corresponding to the final_endpoint. + end_points: a set of activations for external use, for example summaries or + losses. + + Raises: + ValueError: if final_endpoint is not set to one of the predefined values, + or if the output_stride is not 8 or 16, or if the output_stride is 8 and + we request an end point after 'PreAuxLogits'. + """ + if output_stride != 8 and output_stride != 16: + raise ValueError('output_stride must be 8 or 16.') + + padding = 'SAME' if align_feature_maps else 'VALID' + + end_points = {} + + def add_and_check_final(name, net): + end_points[name] = net + return name == final_endpoint + + with tf.variable_scope(scope, 'InceptionResnetV2', [inputs]): + with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d], + stride=1, padding='SAME'): + # 149 x 149 x 32 + net = slim.conv2d(inputs, 32, 3, stride=2, padding=padding, + scope='Conv2d_1a_3x3') + if add_and_check_final('Conv2d_1a_3x3', net): return net, end_points + + # 147 x 147 x 32 + net = slim.conv2d(net, 32, 3, padding=padding, + scope='Conv2d_2a_3x3') + if add_and_check_final('Conv2d_2a_3x3', net): return net, end_points + # 147 x 147 x 64 + net = slim.conv2d(net, 64, 3, scope='Conv2d_2b_3x3') + if add_and_check_final('Conv2d_2b_3x3', net): return net, end_points + # 73 x 73 x 64 + net = slim.max_pool2d(net, 3, stride=2, padding=padding, + scope='MaxPool_3a_3x3') + if add_and_check_final('MaxPool_3a_3x3', net): return net, end_points + # 73 x 73 x 80 + net = slim.conv2d(net, 80, 1, padding=padding, + scope='Conv2d_3b_1x1') + if add_and_check_final('Conv2d_3b_1x1', net): return net, end_points + # 71 x 71 x 192 + net = slim.conv2d(net, 192, 3, padding=padding, + scope='Conv2d_4a_3x3') + if add_and_check_final('Conv2d_4a_3x3', net): return net, end_points + # 35 x 35 x 192 + net = slim.max_pool2d(net, 3, stride=2, padding=padding, + scope='MaxPool_5a_3x3') + if add_and_check_final('MaxPool_5a_3x3', net): return net, end_points + + # 35 x 35 x 320 + with tf.variable_scope('Mixed_5b'): + with tf.variable_scope('Branch_0'): + tower_conv = slim.conv2d(net, 96, 1, scope='Conv2d_1x1') + with tf.variable_scope('Branch_1'): + tower_conv1_0 = slim.conv2d(net, 48, 1, scope='Conv2d_0a_1x1') + tower_conv1_1 = slim.conv2d(tower_conv1_0, 64, 5, + scope='Conv2d_0b_5x5') + with tf.variable_scope('Branch_2'): + tower_conv2_0 = slim.conv2d(net, 64, 1, scope='Conv2d_0a_1x1') + tower_conv2_1 = slim.conv2d(tower_conv2_0, 96, 3, + scope='Conv2d_0b_3x3') + tower_conv2_2 = slim.conv2d(tower_conv2_1, 96, 3, + scope='Conv2d_0c_3x3') + with tf.variable_scope('Branch_3'): + tower_pool = slim.avg_pool2d(net, 3, stride=1, padding='SAME', + scope='AvgPool_0a_3x3') + tower_pool_1 = slim.conv2d(tower_pool, 64, 1, + scope='Conv2d_0b_1x1') + net = tf.concat( + [tower_conv, tower_conv1_1, tower_conv2_2, tower_pool_1], 3) + + if add_and_check_final('Mixed_5b', net): return net, end_points + # TODO(alemi): Register intermediate endpoints + net = slim.repeat(net, 10, block35, scale=0.17) + + # 17 x 17 x 1088 if output_stride == 8, + # 33 x 33 x 1088 if output_stride == 16 + use_atrous = output_stride == 8 + + with tf.variable_scope('Mixed_6a'): + with tf.variable_scope('Branch_0'): + tower_conv = slim.conv2d(net, 384, 3, stride=1 if use_atrous else 2, + padding=padding, + scope='Conv2d_1a_3x3') + with tf.variable_scope('Branch_1'): + tower_conv1_0 = slim.conv2d(net, 256, 1, scope='Conv2d_0a_1x1') + tower_conv1_1 = slim.conv2d(tower_conv1_0, 256, 3, + scope='Conv2d_0b_3x3') + tower_conv1_2 = slim.conv2d(tower_conv1_1, 384, 3, + stride=1 if use_atrous else 2, + padding=padding, + scope='Conv2d_1a_3x3') + with tf.variable_scope('Branch_2'): + tower_pool = slim.max_pool2d(net, 3, stride=1 if use_atrous else 2, + padding=padding, + scope='MaxPool_1a_3x3') + net = tf.concat([tower_conv, tower_conv1_2, tower_pool], 3) + + if add_and_check_final('Mixed_6a', net): return net, end_points + + # TODO(alemi): register intermediate endpoints + with slim.arg_scope([slim.conv2d], rate=2 if use_atrous else 1): + net = slim.repeat(net, 20, block17, scale=0.10) + if add_and_check_final('PreAuxLogits', net): return net, end_points + + if output_stride == 8: + # TODO(gpapan): Properly support output_stride for the rest of the net. + raise ValueError('output_stride==8 is only supported up to the ' + 'PreAuxlogits end_point for now.') + + # 8 x 8 x 2080 + with tf.variable_scope('Mixed_7a'): + with tf.variable_scope('Branch_0'): + tower_conv = slim.conv2d(net, 256, 1, scope='Conv2d_0a_1x1') + tower_conv_1 = slim.conv2d(tower_conv, 384, 3, stride=2, + padding=padding, + scope='Conv2d_1a_3x3') + with tf.variable_scope('Branch_1'): + tower_conv1 = slim.conv2d(net, 256, 1, scope='Conv2d_0a_1x1') + tower_conv1_1 = slim.conv2d(tower_conv1, 288, 3, stride=2, + padding=padding, + scope='Conv2d_1a_3x3') + with tf.variable_scope('Branch_2'): + tower_conv2 = slim.conv2d(net, 256, 1, scope='Conv2d_0a_1x1') + tower_conv2_1 = slim.conv2d(tower_conv2, 288, 3, + scope='Conv2d_0b_3x3') + tower_conv2_2 = slim.conv2d(tower_conv2_1, 320, 3, stride=2, + padding=padding, + scope='Conv2d_1a_3x3') + with tf.variable_scope('Branch_3'): + tower_pool = slim.max_pool2d(net, 3, stride=2, + padding=padding, + scope='MaxPool_1a_3x3') + net = tf.concat( + [tower_conv_1, tower_conv1_1, tower_conv2_2, tower_pool], 3) + + if add_and_check_final('Mixed_7a', net): return net, end_points + + # TODO(alemi): register intermediate endpoints + net = slim.repeat(net, 9, block8, scale=0.20) + net = block8(net, activation_fn=None) + + # 8 x 8 x 1536 + net = slim.conv2d(net, 1536, 1, scope='Conv2d_7b_1x1') + if add_and_check_final('Conv2d_7b_1x1', net): return net, end_points + + raise ValueError('final_endpoint (%s) not recognized', final_endpoint) + + def inception_resnet_v2(inputs, num_classes=1001, is_training=True, dropout_keep_prob=0.8, reuse=None, - scope='InceptionResnetV2'): + scope='InceptionResnetV2', + create_aux_logits=True): """Creates the Inception Resnet V2 model. Args: @@ -105,6 +282,7 @@ def inception_resnet_v2(inputs, num_classes=1001, is_training=True, reuse: whether or not the network and its variables should be reused. To be able to reuse 'scope' must be given. scope: Optional variable_scope. + create_aux_logits: Whether to include the auxilliary logits. Returns: logits: the logits outputs of the model. @@ -112,88 +290,17 @@ def inception_resnet_v2(inputs, num_classes=1001, is_training=True, """ end_points = {} - with tf.variable_scope(scope, 'InceptionResnetV2', [inputs], reuse=reuse): + with tf.variable_scope(scope, 'InceptionResnetV2', [inputs, num_classes], + reuse=reuse) as scope: with slim.arg_scope([slim.batch_norm, slim.dropout], is_training=is_training): - with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d], - stride=1, padding='SAME'): - - # 149 x 149 x 32 - net = slim.conv2d(inputs, 32, 3, stride=2, padding='VALID', - scope='Conv2d_1a_3x3') - end_points['Conv2d_1a_3x3'] = net - # 147 x 147 x 32 - net = slim.conv2d(net, 32, 3, padding='VALID', - scope='Conv2d_2a_3x3') - end_points['Conv2d_2a_3x3'] = net - # 147 x 147 x 64 - net = slim.conv2d(net, 64, 3, scope='Conv2d_2b_3x3') - end_points['Conv2d_2b_3x3'] = net - # 73 x 73 x 64 - net = slim.max_pool2d(net, 3, stride=2, padding='VALID', - scope='MaxPool_3a_3x3') - end_points['MaxPool_3a_3x3'] = net - # 73 x 73 x 80 - net = slim.conv2d(net, 80, 1, padding='VALID', - scope='Conv2d_3b_1x1') - end_points['Conv2d_3b_1x1'] = net - # 71 x 71 x 192 - net = slim.conv2d(net, 192, 3, padding='VALID', - scope='Conv2d_4a_3x3') - end_points['Conv2d_4a_3x3'] = net - # 35 x 35 x 192 - net = slim.max_pool2d(net, 3, stride=2, padding='VALID', - scope='MaxPool_5a_3x3') - end_points['MaxPool_5a_3x3'] = net - - # 35 x 35 x 320 - with tf.variable_scope('Mixed_5b'): - with tf.variable_scope('Branch_0'): - tower_conv = slim.conv2d(net, 96, 1, scope='Conv2d_1x1') - with tf.variable_scope('Branch_1'): - tower_conv1_0 = slim.conv2d(net, 48, 1, scope='Conv2d_0a_1x1') - tower_conv1_1 = slim.conv2d(tower_conv1_0, 64, 5, - scope='Conv2d_0b_5x5') - with tf.variable_scope('Branch_2'): - tower_conv2_0 = slim.conv2d(net, 64, 1, scope='Conv2d_0a_1x1') - tower_conv2_1 = slim.conv2d(tower_conv2_0, 96, 3, - scope='Conv2d_0b_3x3') - tower_conv2_2 = slim.conv2d(tower_conv2_1, 96, 3, - scope='Conv2d_0c_3x3') - with tf.variable_scope('Branch_3'): - tower_pool = slim.avg_pool2d(net, 3, stride=1, padding='SAME', - scope='AvgPool_0a_3x3') - tower_pool_1 = slim.conv2d(tower_pool, 64, 1, - scope='Conv2d_0b_1x1') - net = tf.concat(axis=3, values=[tower_conv, tower_conv1_1, - tower_conv2_2, tower_pool_1]) - - end_points['Mixed_5b'] = net - net = slim.repeat(net, 10, block35, scale=0.17) - - # 17 x 17 x 1088 - with tf.variable_scope('Mixed_6a'): - with tf.variable_scope('Branch_0'): - tower_conv = slim.conv2d(net, 384, 3, stride=2, padding='VALID', - scope='Conv2d_1a_3x3') - with tf.variable_scope('Branch_1'): - tower_conv1_0 = slim.conv2d(net, 256, 1, scope='Conv2d_0a_1x1') - tower_conv1_1 = slim.conv2d(tower_conv1_0, 256, 3, - scope='Conv2d_0b_3x3') - tower_conv1_2 = slim.conv2d(tower_conv1_1, 384, 3, - stride=2, padding='VALID', - scope='Conv2d_1a_3x3') - with tf.variable_scope('Branch_2'): - tower_pool = slim.max_pool2d(net, 3, stride=2, padding='VALID', - scope='MaxPool_1a_3x3') - net = tf.concat(axis=3, values=[tower_conv, tower_conv1_2, tower_pool]) - - end_points['Mixed_6a'] = net - net = slim.repeat(net, 20, block17, scale=0.10) - # Auxiliary tower + net, end_points = inception_resnet_v2_base(inputs, scope=scope) + + if create_aux_logits: with tf.variable_scope('AuxLogits'): - aux = slim.avg_pool2d(net, 5, stride=3, padding='VALID', + aux = end_points['PreAuxLogits'] + aux = slim.avg_pool2d(aux, 5, stride=3, padding='VALID', scope='Conv2d_1a_3x3') aux = slim.conv2d(aux, 128, 1, scope='Conv2d_1b_1x1') aux = slim.conv2d(aux, 768, aux.get_shape()[1:3], @@ -203,49 +310,19 @@ def inception_resnet_v2(inputs, num_classes=1001, is_training=True, scope='Logits') end_points['AuxLogits'] = aux - with tf.variable_scope('Mixed_7a'): - with tf.variable_scope('Branch_0'): - tower_conv = slim.conv2d(net, 256, 1, scope='Conv2d_0a_1x1') - tower_conv_1 = slim.conv2d(tower_conv, 384, 3, stride=2, - padding='VALID', scope='Conv2d_1a_3x3') - with tf.variable_scope('Branch_1'): - tower_conv1 = slim.conv2d(net, 256, 1, scope='Conv2d_0a_1x1') - tower_conv1_1 = slim.conv2d(tower_conv1, 288, 3, stride=2, - padding='VALID', scope='Conv2d_1a_3x3') - with tf.variable_scope('Branch_2'): - tower_conv2 = slim.conv2d(net, 256, 1, scope='Conv2d_0a_1x1') - tower_conv2_1 = slim.conv2d(tower_conv2, 288, 3, - scope='Conv2d_0b_3x3') - tower_conv2_2 = slim.conv2d(tower_conv2_1, 320, 3, stride=2, - padding='VALID', scope='Conv2d_1a_3x3') - with tf.variable_scope('Branch_3'): - tower_pool = slim.max_pool2d(net, 3, stride=2, padding='VALID', - scope='MaxPool_1a_3x3') - net = tf.concat(axis=3, values=[tower_conv_1, tower_conv1_1, - tower_conv2_2, tower_pool]) - - end_points['Mixed_7a'] = net - - net = slim.repeat(net, 9, block8, scale=0.20) - net = block8(net, activation_fn=None) - - net = slim.conv2d(net, 1536, 1, scope='Conv2d_7b_1x1') - end_points['Conv2d_7b_1x1'] = net - - with tf.variable_scope('Logits'): - end_points['PrePool'] = net - net = slim.avg_pool2d(net, net.get_shape()[1:3], padding='VALID', - scope='AvgPool_1a_8x8') - net = slim.flatten(net) - - net = slim.dropout(net, dropout_keep_prob, is_training=is_training, - scope='Dropout') - - end_points['PreLogitsFlatten'] = net - logits = slim.fully_connected(net, num_classes, activation_fn=None, - scope='Logits') - end_points['Logits'] = logits - end_points['Predictions'] = tf.nn.softmax(logits, name='Predictions') + with tf.variable_scope('Logits'): + net = slim.avg_pool2d(net, net.get_shape()[1:3], padding='VALID', + scope='AvgPool_1a_8x8') + net = slim.flatten(net) + + net = slim.dropout(net, dropout_keep_prob, is_training=is_training, + scope='Dropout') + + end_points['PreLogitsFlatten'] = net + logits = slim.fully_connected(net, num_classes, activation_fn=None, + scope='Logits') + end_points['Logits'] = logits + end_points['Predictions'] = tf.nn.softmax(logits, name='Predictions') return logits, end_points inception_resnet_v2.default_image_size = 299 diff --git a/slim/nets/inception_resnet_v2_test.py b/slim/nets/inception_resnet_v2_test.py index b1560fb01..c369ed9f7 100644 --- a/slim/nets/inception_resnet_v2_test.py +++ b/slim/nets/inception_resnet_v2_test.py @@ -30,7 +30,26 @@ class InceptionTest(tf.test.TestCase): num_classes = 1000 with self.test_session(): inputs = tf.random_uniform((batch_size, height, width, 3)) - logits, _ = inception.inception_resnet_v2(inputs, num_classes) + logits, endpoints = inception.inception_resnet_v2(inputs, num_classes) + self.assertTrue('AuxLogits' in endpoints) + auxlogits = endpoints['AuxLogits'] + self.assertTrue( + auxlogits.op.name.startswith('InceptionResnetV2/AuxLogits')) + self.assertListEqual(auxlogits.get_shape().as_list(), + [batch_size, num_classes]) + self.assertTrue(logits.op.name.startswith('InceptionResnetV2/Logits')) + self.assertListEqual(logits.get_shape().as_list(), + [batch_size, num_classes]) + + def testBuildWithoutAuxLogits(self): + batch_size = 5 + height, width = 299, 299 + num_classes = 1000 + with self.test_session(): + inputs = tf.random_uniform((batch_size, height, width, 3)) + logits, endpoints = inception.inception_resnet_v2(inputs, num_classes, + create_aux_logits=False) + self.assertTrue('AuxLogits' not in endpoints) self.assertTrue(logits.op.name.startswith('InceptionResnetV2/Logits')) self.assertListEqual(logits.get_shape().as_list(), [batch_size, num_classes]) @@ -50,10 +69,120 @@ class InceptionTest(tf.test.TestCase): aux_logits = end_points['AuxLogits'] self.assertListEqual(aux_logits.get_shape().as_list(), [batch_size, num_classes]) - pre_pool = end_points['PrePool'] + pre_pool = end_points['Conv2d_7b_1x1'] self.assertListEqual(pre_pool.get_shape().as_list(), [batch_size, 8, 8, 1536]) + def testBuildBaseNetwork(self): + batch_size = 5 + height, width = 299, 299 + + inputs = tf.random_uniform((batch_size, height, width, 3)) + net, end_points = inception.inception_resnet_v2_base(inputs) + self.assertTrue(net.op.name.startswith('InceptionResnetV2/Conv2d_7b_1x1')) + self.assertListEqual(net.get_shape().as_list(), + [batch_size, 8, 8, 1536]) + expected_endpoints = ['Conv2d_1a_3x3', 'Conv2d_2a_3x3', 'Conv2d_2b_3x3', + 'MaxPool_3a_3x3', 'Conv2d_3b_1x1', 'Conv2d_4a_3x3', + 'MaxPool_5a_3x3', 'Mixed_5b', 'Mixed_6a', + 'PreAuxLogits', 'Mixed_7a', 'Conv2d_7b_1x1'] + self.assertItemsEqual(end_points.keys(), expected_endpoints) + + def testBuildOnlyUptoFinalEndpoint(self): + batch_size = 5 + height, width = 299, 299 + endpoints = ['Conv2d_1a_3x3', 'Conv2d_2a_3x3', 'Conv2d_2b_3x3', + 'MaxPool_3a_3x3', 'Conv2d_3b_1x1', 'Conv2d_4a_3x3', + 'MaxPool_5a_3x3', 'Mixed_5b', 'Mixed_6a', + 'PreAuxLogits', 'Mixed_7a', 'Conv2d_7b_1x1'] + for index, endpoint in enumerate(endpoints): + with tf.Graph().as_default(): + inputs = tf.random_uniform((batch_size, height, width, 3)) + out_tensor, end_points = inception.inception_resnet_v2_base( + inputs, final_endpoint=endpoint) + if endpoint != 'PreAuxLogits': + self.assertTrue(out_tensor.op.name.startswith( + 'InceptionResnetV2/' + endpoint)) + self.assertItemsEqual(endpoints[:index+1], end_points) + + def testBuildAndCheckAllEndPointsUptoPreAuxLogits(self): + batch_size = 5 + height, width = 299, 299 + + inputs = tf.random_uniform((batch_size, height, width, 3)) + _, end_points = inception.inception_resnet_v2_base( + inputs, final_endpoint='PreAuxLogits') + endpoints_shapes = {'Conv2d_1a_3x3': [5, 149, 149, 32], + 'Conv2d_2a_3x3': [5, 147, 147, 32], + 'Conv2d_2b_3x3': [5, 147, 147, 64], + 'MaxPool_3a_3x3': [5, 73, 73, 64], + 'Conv2d_3b_1x1': [5, 73, 73, 80], + 'Conv2d_4a_3x3': [5, 71, 71, 192], + 'MaxPool_5a_3x3': [5, 35, 35, 192], + 'Mixed_5b': [5, 35, 35, 320], + 'Mixed_6a': [5, 17, 17, 1088], + 'PreAuxLogits': [5, 17, 17, 1088] + } + + self.assertItemsEqual(endpoints_shapes.keys(), end_points.keys()) + for endpoint_name in endpoints_shapes: + expected_shape = endpoints_shapes[endpoint_name] + self.assertTrue(endpoint_name in end_points) + self.assertListEqual(end_points[endpoint_name].get_shape().as_list(), + expected_shape) + + def testBuildAndCheckAllEndPointsUptoPreAuxLogitsWithAlignedFeatureMaps(self): + batch_size = 5 + height, width = 299, 299 + + inputs = tf.random_uniform((batch_size, height, width, 3)) + _, end_points = inception.inception_resnet_v2_base( + inputs, final_endpoint='PreAuxLogits', align_feature_maps=True) + endpoints_shapes = {'Conv2d_1a_3x3': [5, 150, 150, 32], + 'Conv2d_2a_3x3': [5, 150, 150, 32], + 'Conv2d_2b_3x3': [5, 150, 150, 64], + 'MaxPool_3a_3x3': [5, 75, 75, 64], + 'Conv2d_3b_1x1': [5, 75, 75, 80], + 'Conv2d_4a_3x3': [5, 75, 75, 192], + 'MaxPool_5a_3x3': [5, 38, 38, 192], + 'Mixed_5b': [5, 38, 38, 320], + 'Mixed_6a': [5, 19, 19, 1088], + 'PreAuxLogits': [5, 19, 19, 1088] + } + + self.assertItemsEqual(endpoints_shapes.keys(), end_points.keys()) + for endpoint_name in endpoints_shapes: + expected_shape = endpoints_shapes[endpoint_name] + self.assertTrue(endpoint_name in end_points) + self.assertListEqual(end_points[endpoint_name].get_shape().as_list(), + expected_shape) + + def testBuildAndCheckAllEndPointsUptoPreAuxLogitsWithOutputStrideEight(self): + batch_size = 5 + height, width = 299, 299 + + inputs = tf.random_uniform((batch_size, height, width, 3)) + _, end_points = inception.inception_resnet_v2_base( + inputs, final_endpoint='PreAuxLogits', output_stride=8) + endpoints_shapes = {'Conv2d_1a_3x3': [5, 149, 149, 32], + 'Conv2d_2a_3x3': [5, 147, 147, 32], + 'Conv2d_2b_3x3': [5, 147, 147, 64], + 'MaxPool_3a_3x3': [5, 73, 73, 64], + 'Conv2d_3b_1x1': [5, 73, 73, 80], + 'Conv2d_4a_3x3': [5, 71, 71, 192], + 'MaxPool_5a_3x3': [5, 35, 35, 192], + 'Mixed_5b': [5, 35, 35, 320], + 'Mixed_6a': [5, 33, 33, 1088], + 'PreAuxLogits': [5, 33, 33, 1088] + } + + self.assertItemsEqual(endpoints_shapes.keys(), end_points.keys()) + for endpoint_name in endpoints_shapes: + expected_shape = endpoints_shapes[endpoint_name] + self.assertTrue(endpoint_name in end_points) + self.assertListEqual(end_points[endpoint_name].get_shape().as_list(), + expected_shape) + def testVariablesSetDevice(self): batch_size = 5 height, width = 299, 299 @@ -80,7 +209,7 @@ class InceptionTest(tf.test.TestCase): self.assertTrue(logits.op.name.startswith('InceptionResnetV2/Logits')) self.assertListEqual(logits.get_shape().as_list(), [batch_size, num_classes]) - pre_pool = end_points['PrePool'] + pre_pool = end_points['Conv2d_7b_1x1'] self.assertListEqual(pre_pool.get_shape().as_list(), [batch_size, 3, 3, 1536]) -- GitLab From 2bb1baad7445b31c27b8a0ee4e1753371f4d0581 Mon Sep 17 00:00:00 2001 From: xiangjinwu Date: Wed, 14 Jun 2017 13:11:42 -0500 Subject: [PATCH 038/110] slim Python 3 compatibility: cPickle and str/bytes (#1534) --- slim/datasets/dataset_utils.py | 2 +- slim/datasets/download_and_convert_cifar10.py | 15 +++++++++------ slim/datasets/download_and_convert_flowers.py | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/slim/datasets/dataset_utils.py b/slim/datasets/dataset_utils.py index 9c79aadfb..6f7a1c207 100644 --- a/slim/datasets/dataset_utils.py +++ b/slim/datasets/dataset_utils.py @@ -124,7 +124,7 @@ def read_label_file(dataset_dir, filename=LABELS_FILENAME): A map from a label (integer) to class name. """ labels_filename = os.path.join(dataset_dir, filename) - with tf.gfile.Open(labels_filename, 'r') as f: + with tf.gfile.Open(labels_filename, 'rb') as f: lines = f.read().decode() lines = lines.split('\n') lines = filter(None, lines) diff --git a/slim/datasets/download_and_convert_cifar10.py b/slim/datasets/download_and_convert_cifar10.py index 2cb787d08..0e0abe3c0 100644 --- a/slim/datasets/download_and_convert_cifar10.py +++ b/slim/datasets/download_and_convert_cifar10.py @@ -26,7 +26,7 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function -import cPickle +from six.moves import cPickle import os import sys import tarfile @@ -72,14 +72,17 @@ def _add_to_tfrecord(filename, tfrecord_writer, offset=0): Returns: The new offset. """ - with tf.gfile.Open(filename, 'r') as f: - data = cPickle.load(f) + with tf.gfile.Open(filename, 'rb') as f: + if sys.version_info < (3,): + data = cPickle.load(f) + else: + data = cPickle.load(f, encoding='bytes') - images = data['data'] + images = data[b'data'] num_images = images.shape[0] images = images.reshape((num_images, 3, 32, 32)) - labels = data['labels'] + labels = data[b'labels'] with tf.Graph().as_default(): image_placeholder = tf.placeholder(dtype=tf.uint8) @@ -99,7 +102,7 @@ def _add_to_tfrecord(filename, tfrecord_writer, offset=0): feed_dict={image_placeholder: image}) example = dataset_utils.image_to_tfexample( - png_string, 'png', _IMAGE_SIZE, _IMAGE_SIZE, label) + png_string, b'png', _IMAGE_SIZE, _IMAGE_SIZE, label) tfrecord_writer.write(example.SerializeToString()) return offset + num_images diff --git a/slim/datasets/download_and_convert_flowers.py b/slim/datasets/download_and_convert_flowers.py index 347a4df29..2c11ead41 100644 --- a/slim/datasets/download_and_convert_flowers.py +++ b/slim/datasets/download_and_convert_flowers.py @@ -136,14 +136,14 @@ def _convert_dataset(split_name, filenames, class_names_to_ids, dataset_dir): sys.stdout.flush() # Read the filename: - image_data = tf.gfile.FastGFile(filenames[i], 'r').read() + image_data = tf.gfile.FastGFile(filenames[i], 'rb').read() height, width = image_reader.read_image_dims(sess, image_data) class_name = os.path.basename(os.path.dirname(filenames[i])) class_id = class_names_to_ids[class_name] example = dataset_utils.image_to_tfexample( - image_data, 'jpg', height, width, class_id) + image_data, b'jpg', height, width, class_id) tfrecord_writer.write(example.SerializeToString()) sys.stdout.write('\n') -- GitLab From 001a260214ba34f36e149bbd24f7f5d6a6634500 Mon Sep 17 00:00:00 2001 From: g21589 Date: Thu, 15 Jun 2017 02:12:54 +0800 Subject: [PATCH 039/110] Fixed the device specification for dequeue (#1480) This patch assigns dequeue node to inputs_device. And nolonger shows "Ignoring device specification /device:GPU:X for node 'clone_X/fifo_queue_Dequeue'" message. --- slim/train_image_classifier.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/slim/train_image_classifier.py b/slim/train_image_classifier.py index 57049a1a2..21180edb9 100755 --- a/slim/train_image_classifier.py +++ b/slim/train_image_classifier.py @@ -450,7 +450,8 @@ def main(_): #################### def clone_fn(batch_queue): """Allows data parallelism by creating multiple clones of network_fn.""" - images, labels = batch_queue.dequeue() + with tf.device(deploy_config.inputs_device()): + images, labels = batch_queue.dequeue() logits, end_points = network_fn(images) ############################# -- GitLab From fc7342bf047ec5fc7a707202adaf108661bd373d Mon Sep 17 00:00:00 2001 From: derekjchow Date: Wed, 14 Jun 2017 13:31:35 -0700 Subject: [PATCH 040/110] Update model_deploy. (#1557) Fix slow down with only 1 GPU --- slim/deployment/model_deploy.py | 5 +---- slim/deployment/model_deploy_test.py | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/slim/deployment/model_deploy.py b/slim/deployment/model_deploy.py index 67b6f9a38..c6820769d 100644 --- a/slim/deployment/model_deploy.py +++ b/slim/deployment/model_deploy.py @@ -103,8 +103,6 @@ import collections import tensorflow as tf -from tensorflow.python.ops import control_flow_ops - slim = tf.contrib.slim @@ -594,8 +592,7 @@ class DeploymentConfig(object): if self._clone_on_cpu: device += '/device:CPU:0' else: - if self._num_clones > 1: - device += '/device:GPU:%d' % clone_index + device += '/device:GPU:%d' % clone_index return device def clone_scope(self, clone_index): diff --git a/slim/deployment/model_deploy_test.py b/slim/deployment/model_deploy_test.py index 57951db96..48982eda7 100644 --- a/slim/deployment/model_deploy_test.py +++ b/slim/deployment/model_deploy_test.py @@ -33,7 +33,7 @@ class DeploymentConfigTest(tf.test.TestCase): self.assertEqual(slim.get_variables(), []) self.assertEqual(deploy_config.caching_device(), None) - self.assertDeviceEqual(deploy_config.clone_device(0), '') + self.assertDeviceEqual(deploy_config.clone_device(0), 'GPU:0') self.assertEqual(deploy_config.clone_scope(0), '') self.assertDeviceEqual(deploy_config.optimizer_device(), 'CPU:0') self.assertDeviceEqual(deploy_config.inputs_device(), 'CPU:0') @@ -65,7 +65,7 @@ class DeploymentConfigTest(tf.test.TestCase): deploy_config = model_deploy.DeploymentConfig(num_clones=1, num_ps_tasks=1) self.assertDeviceEqual(deploy_config.clone_device(0), - '/job:worker') + '/job:worker/device:GPU:0') self.assertEqual(deploy_config.clone_scope(0), '') self.assertDeviceEqual(deploy_config.optimizer_device(), '/job:worker/device:CPU:0') @@ -105,7 +105,7 @@ class DeploymentConfigTest(tf.test.TestCase): num_ps_tasks=2) self.assertDeviceEqual(deploy_config.clone_device(0), - '/job:worker') + '/job:worker/device:GPU:0') self.assertEqual(deploy_config.clone_scope(0), '') self.assertDeviceEqual(deploy_config.optimizer_device(), '/job:worker/device:CPU:0') @@ -201,7 +201,7 @@ class CreatecloneTest(tf.test.TestCase): self.assertEqual(clone.outputs.op.name, 'LogisticClassifier/fully_connected/Sigmoid') self.assertEqual(clone.scope, '') - self.assertDeviceEqual(clone.device, '') + self.assertDeviceEqual(clone.device, 'GPU:0') self.assertEqual(len(slim.losses.get_losses()), 1) update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) self.assertEqual(update_ops, []) @@ -227,7 +227,7 @@ class CreatecloneTest(tf.test.TestCase): self.assertEqual(clone.outputs.op.name, 'BatchNormClassifier/fully_connected/Sigmoid') self.assertEqual(clone.scope, '') - self.assertDeviceEqual(clone.device, '') + self.assertDeviceEqual(clone.device, 'GPU:0') self.assertEqual(len(slim.losses.get_losses()), 1) update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) self.assertEqual(len(update_ops), 2) @@ -278,7 +278,7 @@ class CreatecloneTest(tf.test.TestCase): clone = clones[0] self.assertEqual(clone.outputs.op.name, 'BatchNormClassifier/fully_connected/Sigmoid') - self.assertDeviceEqual(clone.device, '/job:worker') + self.assertDeviceEqual(clone.device, '/job:worker/device:GPU:0') self.assertEqual(clone.scope, '') self.assertEqual(len(slim.get_variables()), 5) for v in slim.get_variables(): @@ -350,7 +350,7 @@ class OptimizeclonesTest(tf.test.TestCase): self.assertEqual(len(grads_and_vars), len(tf.trainable_variables())) self.assertEqual(total_loss.op.name, 'total_loss') for g, v in grads_and_vars: - self.assertDeviceEqual(g.device, '') + self.assertDeviceEqual(g.device, 'GPU:0') self.assertDeviceEqual(v.device, 'CPU:0') def testCreateSingleclone(self): @@ -376,7 +376,7 @@ class OptimizeclonesTest(tf.test.TestCase): self.assertEqual(len(grads_and_vars), len(tf.trainable_variables())) self.assertEqual(total_loss.op.name, 'total_loss') for g, v in grads_and_vars: - self.assertDeviceEqual(g.device, '') + self.assertDeviceEqual(g.device, 'GPU:0') self.assertDeviceEqual(v.device, 'CPU:0') def testCreateMulticlone(self): @@ -458,7 +458,7 @@ class OptimizeclonesTest(tf.test.TestCase): self.assertEqual(len(grads_and_vars), len(tf.trainable_variables())) self.assertEqual(total_loss.op.name, 'total_loss') for g, v in grads_and_vars: - self.assertDeviceEqual(g.device, '/job:worker') + self.assertDeviceEqual(g.device, '/job:worker/device:GPU:0') self.assertDeviceEqual(v.device, '/job:ps/task:0/CPU:0') @@ -515,7 +515,7 @@ class DeployTest(tf.test.TestCase): for _ in range(10): sess.run(model.train_op) final_loss = sess.run(model.total_loss) - self.assertLess(final_loss, initial_loss / 10.0) + self.assertLess(final_loss, initial_loss / 5.0) final_mean, final_variance = sess.run([moving_mean, moving_variance]) -- GitLab From 34af79db12577f2039c4f88bfae50734d8ddd2c6 Mon Sep 17 00:00:00 2001 From: Mohammad Babaeizadeh Date: Wed, 14 Jun 2017 16:01:29 -0700 Subject: [PATCH 041/110] Fixing the initialization/loading bug. The code currently loads the checkpoint and then initializes the variables resulting to random weights. Swapping the order fixes the loading checkpoint issue. --- video_prediction/prediction_train.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/video_prediction/prediction_train.py b/video_prediction/prediction_train.py index 46f881426..09625bbf1 100644 --- a/video_prediction/prediction_train.py +++ b/video_prediction/prediction_train.py @@ -204,6 +204,8 @@ def main(unused_argv): # Make training session. sess = tf.InteractiveSession() + sess.run(tf.global_variables_initializer()) + summary_writer = tf.summary.FileWriter( FLAGS.event_log_dir, graph=sess.graph, flush_secs=10) @@ -211,7 +213,6 @@ def main(unused_argv): saver.restore(sess, FLAGS.pretrained_model) tf.train.start_queue_runners(sess) - sess.run(tf.global_variables_initializer()) tf.logging.info('iteration number, cost') -- GitLab From 1c3408026f28b5b9d28cba73562894686824a3a0 Mon Sep 17 00:00:00 2001 From: Neal Wu Date: Wed, 14 Jun 2017 18:09:09 -0700 Subject: [PATCH 042/110] Changes for consistency --- tutorials/image/alexnet/alexnet_benchmark.py | 24 ++++++++------------ 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/tutorials/image/alexnet/alexnet_benchmark.py b/tutorials/image/alexnet/alexnet_benchmark.py index 04c394ad4..39991501c 100644 --- a/tutorials/image/alexnet/alexnet_benchmark.py +++ b/tutorials/image/alexnet/alexnet_benchmark.py @@ -75,13 +75,11 @@ def inference(images): with tf.name_scope('lrn1') as scope: - lrn1 = tf.nn.local_response_normalization( - conv1, - alpha=1e-04, - beta=0.75, - depth_radius=5, - bias=2.0 - ) + lrn1 = tf.nn.local_response_normalization(conv1, + alpha=1e-4, + beta=0.75, + depth_radius=5, + bias=2.0) # pool1 pool1 = tf.nn.max_pool(lrn1, @@ -105,13 +103,11 @@ def inference(images): with tf.name_scope('lrn2') as scope: - lrn2 = tf.nn.local_response_normalization( - conv2, - alpha=1e-04, - beta=0.75, - depth_radius=5, - bias=2.0 - ) + lrn2 = tf.nn.local_response_normalization(conv2, + alpha=1e-4, + beta=0.75, + depth_radius=5, + bias=2.0) # pool2 pool2 = tf.nn.max_pool(lrn2, -- GitLab From d877b13a4e1bc180967bdb5dde0d7d6298510913 Mon Sep 17 00:00:00 2001 From: Neal Wu Date: Wed, 14 Jun 2017 18:09:48 -0700 Subject: [PATCH 043/110] Missed section comments --- tutorials/image/alexnet/alexnet_benchmark.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/image/alexnet/alexnet_benchmark.py b/tutorials/image/alexnet/alexnet_benchmark.py index 39991501c..047ac3dce 100644 --- a/tutorials/image/alexnet/alexnet_benchmark.py +++ b/tutorials/image/alexnet/alexnet_benchmark.py @@ -73,7 +73,7 @@ def inference(images): print_activations(conv1) parameters += [kernel, biases] - + # lrn1 with tf.name_scope('lrn1') as scope: lrn1 = tf.nn.local_response_normalization(conv1, alpha=1e-4, @@ -101,7 +101,7 @@ def inference(images): parameters += [kernel, biases] print_activations(conv2) - + # lrn2 with tf.name_scope('lrn2') as scope: lrn2 = tf.nn.local_response_normalization(conv2, alpha=1e-4, -- GitLab From b5afddbefe9d27d368ace5ed9430290d9be41d8e Mon Sep 17 00:00:00 2001 From: Ryan Sepassi Date: Thu, 8 Jun 2017 12:50:41 -0700 Subject: [PATCH 044/110] Reproduce reported virtual adversarial text results --- adversarial_text/BUILD | 21 ++++++ adversarial_text/README.md | 19 +++--- adversarial_text/adversarial_losses.py | 68 ++++++++------------ adversarial_text/data/BUILD | 11 ++++ adversarial_text/data/data_utils.py | 20 ++++-- adversarial_text/data/data_utils_test.py | 18 ++++-- adversarial_text/data/document_generators.py | 37 +++++++---- adversarial_text/data/gen_data.py | 6 +- adversarial_text/data/gen_vocab.py | 6 +- adversarial_text/evaluate.py | 6 +- adversarial_text/graphs.py | 15 +++-- adversarial_text/graphs_test.py | 5 +- adversarial_text/inputs.py | 44 ++++++++++--- adversarial_text/layers.py | 21 ++++-- adversarial_text/pretrain.py | 11 ++-- adversarial_text/train_classifier.py | 11 ++-- adversarial_text/train_utils.py | 5 +- 17 files changed, 208 insertions(+), 116 deletions(-) diff --git a/adversarial_text/BUILD b/adversarial_text/BUILD index b0fdc6332..476865f96 100644 --- a/adversarial_text/BUILD +++ b/adversarial_text/BUILD @@ -1,3 +1,5 @@ +licenses(["notice"]) # Apache 2.0 + # Binaries # ============================================================================== py_binary( @@ -5,6 +7,8 @@ py_binary( srcs = ["evaluate.py"], deps = [ ":graphs", + # google3 file dep, + # tensorflow dep, ], ) @@ -14,6 +18,8 @@ py_binary( deps = [ ":graphs", ":train_utils", + # google3 file dep, + # tensorflow dep, ], ) @@ -25,6 +31,8 @@ py_binary( deps = [ ":graphs", ":train_utils", + # google3 file dep, + # tensorflow dep, ], ) @@ -37,18 +45,23 @@ py_library( ":adversarial_losses", ":inputs", ":layers", + # tensorflow dep, ], ) py_library( name = "adversarial_losses", srcs = ["adversarial_losses.py"], + deps = [ + # tensorflow dep, + ], ) py_library( name = "inputs", srcs = ["inputs.py"], deps = [ + # tensorflow dep, "//adversarial_text/data:data_utils", ], ) @@ -56,11 +69,18 @@ py_library( py_library( name = "layers", srcs = ["layers.py"], + deps = [ + # tensorflow dep, + ], ) py_library( name = "train_utils", srcs = ["train_utils.py"], + deps = [ + # numpy dep, + # tensorflow dep, + ], ) # Tests @@ -71,6 +91,7 @@ py_test( srcs = ["graphs_test.py"], deps = [ ":graphs", + # tensorflow dep, "//adversarial_text/data:data_utils", ], ) diff --git a/adversarial_text/README.md b/adversarial_text/README.md index a27d56c9e..bfddc7088 100644 --- a/adversarial_text/README.md +++ b/adversarial_text/README.md @@ -56,7 +56,6 @@ $ bazel run :pretrain -- \ --embedding_dims=256 \ --rnn_cell_size=1024 \ --num_candidate_samples=1024 \ - --optimizer=adam \ --batch_size=256 \ --learning_rate=0.001 \ --learning_rate_decay_factor=0.9999 \ @@ -87,7 +86,6 @@ $ bazel run :train_classifier -- \ --rnn_cell_size=1024 \ --cl_num_layers=1 \ --cl_hidden_size=30 \ - --optimizer=adam \ --batch_size=64 \ --learning_rate=0.0005 \ --learning_rate_decay_factor=0.9998 \ @@ -96,7 +94,8 @@ $ bazel run :train_classifier -- \ --num_timesteps=400 \ --keep_prob_emb=0.5 \ --normalize_embeddings \ - --adv_training_method=vat + --adv_training_method=vat \ + --perturb_norm_length=5.0 ``` ### Evaluate on test data @@ -136,21 +135,21 @@ adversarial training losses). The training loop itself is defined in ### Command-Line Flags Flags related to distributed training and the training loop itself are defined -in `train_utils.py`. +in [`train_utils.py`](https://github.com/tensorflow/models/tree/master/adversarial_text/train_utils.py). -Flags related to model hyperparameters are defined in `graphs.py`. +Flags related to model hyperparameters are defined in [`graphs.py`](https://github.com/tensorflow/models/tree/master/adversarial_text/graphs.py). -Flags related to adversarial training are defined in `adversarial_losses.py`. +Flags related to adversarial training are defined in [`adversarial_losses.py`](https://github.com/tensorflow/models/tree/master/adversarial_text/adversarial_losses.py). Flags particular to each job are defined in the main binary files. ### Data Generation -* Vocabulary generation: `gen_vocab.py` -* Data generation: `gen_data.py` +* Vocabulary generation: [`gen_vocab.py`](https://github.com/tensorflow/models/tree/master/adversarial_text/data/gen_vocab.py) +* Data generation: [`gen_data.py`](https://github.com/tensorflow/models/tree/master/adversarial_text/data/gen_data.py) -Command-line flags defined in `document_generators.py` control which dataset is -processed and how. +Command-line flags defined in [`document_generators.py`](https://github.com/tensorflow/models/tree/master/adversarial_text/data/document_generators.py) +control which dataset is processed and how. ## Contact for Issues diff --git a/adversarial_text/adversarial_losses.py b/adversarial_text/adversarial_losses.py index f8fba6d35..46a0b371b 100644 --- a/adversarial_text/adversarial_losses.py +++ b/adversarial_text/adversarial_losses.py @@ -1,4 +1,4 @@ -# Copyright 2017 Google, Inc. All Rights Reserved. +# Copyright 2017 Google Inc. 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. @@ -12,25 +12,27 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== - """Adversarial losses for text models.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function +# Dependency imports + import tensorflow as tf flags = tf.app.flags FLAGS = flags.FLAGS # Adversarial and virtual adversarial training parameters. -flags.DEFINE_float('perturb_norm_length', 0.1, +flags.DEFINE_float('perturb_norm_length', 5.0, 'Norm length of adversarial perturbation to be ' - 'optimized with validation') + 'optimized with validation. ' + '5.0 is optimal on IMDB with virtual adversarial training. ') # Virtual adversarial training parameters flags.DEFINE_integer('num_power_iteration', 1, 'The number of power iteration') -flags.DEFINE_float('small_constant_for_finite_diff', 1e-3, +flags.DEFINE_float('small_constant_for_finite_diff', 1e-1, 'Small constant for finite difference method') # Parameters for building the graph @@ -83,19 +85,22 @@ def virtual_adversarial_loss(logits, embedded, inputs, """ # Stop gradient of logits. See https://arxiv.org/abs/1507.00677 for details. logits = tf.stop_gradient(logits) + # Only care about the KL divergence on the final timestep. - weights = _end_of_seq_mask(inputs.labels) + weights = inputs.eos_weights + assert weights is not None # Initialize perturbation with random noise. # shape(embedded) = (batch_size, num_timesteps, embedding_dim) - d = _mask_by_length(tf.random_normal(shape=tf.shape(embedded)), inputs.length) + d = tf.random_normal(shape=tf.shape(embedded)) # Perform finite difference method and power iteration. # See Eq.(8) in the paper http://arxiv.org/pdf/1507.00677.pdf, # Adding small noise to input and taking gradient with respect to the noise # corresponds to 1 power iteration. for _ in xrange(FLAGS.num_power_iteration): - d = _scale_l2(d, FLAGS.small_constant_for_finite_diff) + d = _scale_l2( + _mask_by_length(d, inputs.length), FLAGS.small_constant_for_finite_diff) d_logits = logits_from_embedding_fn(embedded + d) kl = _kl_divergence_with_logits(logits, d_logits, weights) d, = tf.gradients( @@ -104,8 +109,7 @@ def virtual_adversarial_loss(logits, embedded, inputs, aggregation_method=tf.AggregationMethod.EXPERIMENTAL_ACCUMULATE_N) d = tf.stop_gradient(d) - perturb = _scale_l2( - _mask_by_length(d, inputs.length), FLAGS.perturb_norm_length) + perturb = _scale_l2(d, FLAGS.perturb_norm_length) vadv_logits = logits_from_embedding_fn(embedded + perturb) return _kl_divergence_with_logits(logits, vadv_logits, weights) @@ -136,7 +140,8 @@ def virtual_adversarial_loss_bidir(logits, embedded, inputs, """Virtual adversarial loss for bidirectional models.""" logits = tf.stop_gradient(logits) f_inputs, _ = inputs - weights = _end_of_seq_mask(f_inputs.labels) + weights = f_inputs.eos_weights + assert weights is not None perturbs = [ _mask_by_length(tf.random_normal(shape=tf.shape(emb)), f_inputs.length) @@ -155,10 +160,7 @@ def virtual_adversarial_loss_bidir(logits, embedded, inputs, aggregation_method=tf.AggregationMethod.EXPERIMENTAL_ACCUMULATE_N) perturbs = [tf.stop_gradient(d) for d in perturbs] - perturbs = [ - _scale_l2(_mask_by_length(d, f_inputs.length), FLAGS.perturb_norm_length) - for d in perturbs - ] + perturbs = [_scale_l2(d, FLAGS.perturb_norm_length) for d in perturbs] vadv_logits = logits_from_embedding_fn( [emb + d for (emb, d) in zip(embedded, perturbs)]) return _kl_divergence_with_logits(logits, vadv_logits, weights) @@ -167,7 +169,9 @@ def virtual_adversarial_loss_bidir(logits, embedded, inputs, def _mask_by_length(t, length): """Mask t, 3-D [batch, time, dim], by length, 1-D [batch,].""" maxlen = t.get_shape().as_list()[1] - mask = tf.sequence_mask(length, maxlen=maxlen) + + # Subtract 1 from length to prevent the perturbation from going on 'eos' + mask = tf.sequence_mask(length - 1, maxlen=maxlen) mask = tf.expand_dims(tf.cast(mask, tf.float32), -1) # shape(mask) = (batch, num_timesteps, 1) return t * mask @@ -175,32 +179,16 @@ def _mask_by_length(t, length): def _scale_l2(x, norm_length): # shape(x) = (batch, num_timesteps, d) - # Divide x by max(abs(x)) for a numerically stable L2 norm. # 2norm(x) = a * 2norm(x/a) # Scale over the full sequence, dims (1, 2) alpha = tf.reduce_max(tf.abs(x), (1, 2), keep_dims=True) + 1e-12 - l2_norm = alpha * tf.sqrt(tf.reduce_sum(tf.pow(x / alpha, 2), (1, 2), - keep_dims=True) + 1e-6) + l2_norm = alpha * tf.sqrt( + tf.reduce_sum(tf.pow(x / alpha, 2), (1, 2), keep_dims=True) + 1e-6) x_unit = x / l2_norm return norm_length * x_unit -def _end_of_seq_mask(tokens): - """Generate a mask for the EOS token (1.0 on EOS, 0.0 otherwise). - - Args: - tokens: 1-D integer tensor [num_timesteps*batch_size]. Each element is an - id from the vocab. - - Returns: - Float tensor same shape as tokens, whose values are 1.0 on the end of - sequence and 0.0 on the others. - """ - eos_id = FLAGS.vocab_size - 1 - return tf.cast(tf.equal(tokens, eos_id), tf.float32) - - def _kl_divergence_with_logits(q_logits, p_logits, weights): """Returns weighted KL divergence between distributions q and p. @@ -218,21 +206,19 @@ def _kl_divergence_with_logits(q_logits, p_logits, weights): # For logistic regression if FLAGS.num_classes == 2: q = tf.nn.sigmoid(q_logits) - p = tf.nn.sigmoid(p_logits) kl = (-tf.nn.sigmoid_cross_entropy_with_logits(logits=q_logits, labels=q) + tf.nn.sigmoid_cross_entropy_with_logits(logits=p_logits, labels=q)) + kl = tf.squeeze(kl) # For softmax regression else: - q = tf.nn.softmax(q_logits) - p = tf.nn.softmax(p_logits) - kl = tf.reduce_sum(q * (tf.log(q) - tf.log(p)), 1) + kl = tf.reduce_sum( + q * (tf.nn.log_softmax(q_logits) - tf.nn.log_softmax(p_logits)), 1) num_labels = tf.reduce_sum(weights) num_labels = tf.where(tf.equal(num_labels, 0.), 1., num_labels) - kl.get_shape().assert_has_rank(2) + kl.get_shape().assert_has_rank(1) weights.get_shape().assert_has_rank(1) - loss = tf.identity(tf.reduce_sum(tf.expand_dims(weights, -1) * kl) / - num_labels, name='kl') + loss = tf.identity(tf.reduce_sum(weights * kl) / num_labels, name='kl') return loss diff --git a/adversarial_text/data/BUILD b/adversarial_text/data/BUILD index 33d46bcc1..b59f7a30e 100644 --- a/adversarial_text/data/BUILD +++ b/adversarial_text/data/BUILD @@ -1,3 +1,5 @@ +licenses(["notice"]) # Apache 2.0 + package( default_visibility = [ "//adversarial_text:__subpackages__", @@ -10,6 +12,7 @@ py_binary( deps = [ ":data_utils", ":document_generators", + # tensorflow dep, ], ) @@ -19,17 +22,24 @@ py_binary( deps = [ ":data_utils", ":document_generators", + # tensorflow dep, ], ) py_library( name = "document_generators", srcs = ["document_generators.py"], + deps = [ + # tensorflow dep, + ], ) py_library( name = "data_utils", srcs = ["data_utils.py"], + deps = [ + # tensorflow dep, + ], ) py_test( @@ -37,5 +47,6 @@ py_test( srcs = ["data_utils_test.py"], deps = [ ":data_utils", + # tensorflow dep, ], ) diff --git a/adversarial_text/data/data_utils.py b/adversarial_text/data/data_utils.py index 1c31ab96d..d458caadd 100644 --- a/adversarial_text/data/data_utils.py +++ b/adversarial_text/data/data_utils.py @@ -1,4 +1,4 @@ -# Copyright 2017 Google, Inc. All Rights Reserved. +# Copyright 2017 Google Inc. 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. @@ -12,13 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== - """Utilities for generating/preprocessing data for adversarial text models.""" import operator import os import random import re + +# Dependency imports + import tensorflow as tf EOS_TOKEN = '' @@ -215,13 +217,17 @@ def build_lm_sequence(seq): Returns: SequenceWrapper with `seq` tokens copied over to output sequence tokens and - labels (offset by 1, i.e. predict next token) with weights set to 1.0. + labels (offset by 1, i.e. predict next token) with weights set to 1.0, + except for token. """ lm_seq = SequenceWrapper() - for i, timestep in enumerate(seq[:-1]): - lm_seq.add_timestep().set_token(timestep.token).set_label( - seq[i + 1].token).set_weight(1.0) - + for i, timestep in enumerate(seq): + if i == len(seq) - 1: + lm_seq.add_timestep().set_token(timestep.token).set_label( + seq[i].token).set_weight(0.0) + else: + lm_seq.add_timestep().set_token(timestep.token).set_label( + seq[i + 1].token).set_weight(1.0) return lm_seq diff --git a/adversarial_text/data/data_utils_test.py b/adversarial_text/data/data_utils_test.py index 614b12953..59b7f4e66 100644 --- a/adversarial_text/data/data_utils_test.py +++ b/adversarial_text/data/data_utils_test.py @@ -1,4 +1,4 @@ -# Copyright 2017 Google, Inc. All Rights Reserved. +# Copyright 2017 Google Inc. 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. @@ -12,12 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== - """Tests for data_utils.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function +# Dependency imports + import tensorflow as tf from adversarial_text.data import data_utils @@ -91,9 +92,16 @@ class DataUtilsTest(tf.test.TestCase): seq = self._buildDummySequence() lm_seq = data.build_lm_sequence(seq) for i, ts in enumerate(lm_seq): - self.assertEqual(ts.token, i) - self.assertEqual(ts.label, i + 1) - self.assertEqual(ts.weight, 1.0) + # For end of sequence, the token and label should be same, and weight + # should be 0.0. + if i == len(lm_seq) - 1: + self.assertEqual(ts.token, i) + self.assertEqual(ts.label, i) + self.assertEqual(ts.weight, 0.0) + else: + self.assertEqual(ts.token, i) + self.assertEqual(ts.label, i + 1) + self.assertEqual(ts.weight, 1.0) def testBuildSAESeq(self): seq = self._buildDummySequence() diff --git a/adversarial_text/data/document_generators.py b/adversarial_text/data/document_generators.py index 990dae775..aee7fc76a 100644 --- a/adversarial_text/data/document_generators.py +++ b/adversarial_text/data/document_generators.py @@ -1,4 +1,4 @@ -# Copyright 2017 Google, Inc. All Rights Reserved. +# Copyright 2017 Google Inc. 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. @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== - """Input readers and document/token generators for datasets.""" from __future__ import absolute_import from __future__ import division @@ -23,6 +22,8 @@ import csv import os import random +# Dependency imports + import tensorflow as tf from adversarial_text.data import data_utils @@ -60,7 +61,6 @@ flags.DEFINE_string('rcv1_input_dir', '', flags.DEFINE_string('rt_input_dir', '', 'The Rotten Tomatoes dataset input directory.') - # The amazon reviews input file to use in either the RT or IMDB datasets. flags.DEFINE_string('amazon_unlabeled_input_file', '', 'The unlabeled Amazon Reviews dataset input file. If set, ' @@ -211,8 +211,12 @@ def imdb_documents(dataset='train', if FLAGS.amazon_unlabeled_input_file and include_unlabeled: with open(FLAGS.amazon_unlabeled_input_file) as rt_f: for content in rt_f: - yield Document(content=content, is_validation=False, is_test=False, - label=None, add_tokens=False) + yield Document( + content=content, + is_validation=False, + is_test=False, + label=None, + add_tokens=False) def dbpedia_documents(dataset='train', @@ -265,7 +269,8 @@ def rcv1_documents(dataset='train', # pylint:disable=line-too-long """Generates Documents for Reuters Corpus (rcv1) dataset. - Dataset described at http://www.ai.mit.edu/projects/jmlr/papers/volume5/lewis04a/lyrl2004_rcv1v2_README.htm + Dataset described at + http://www.ai.mit.edu/projects/jmlr/papers/volume5/lewis04a/lyrl2004_rcv1v2_README.htm Args: dataset: str, identifies the csv file within the rcv1 data directory. @@ -354,17 +359,25 @@ def rt_documents(dataset='train', if class_label is None: # Process Amazon Review data for unlabeled dataset if content.startswith('review/text'): - yield Document(content=content, is_validation=False, - is_test=False, label=None, add_tokens=False) + yield Document( + content=content, + is_validation=False, + is_test=False, + label=None, + add_tokens=False) else: # 10% of the data is randomly held out for the validation set and # another 10% of it is randomly held out for the test set random_int = random.randint(1, 10) is_validation = random_int == 1 is_test = random_int == 2 - if (is_test and dataset != 'test') or ( - is_validation and not include_validation): + if (is_test and dataset != 'test') or (is_validation and + not include_validation): continue - yield Document(content=content, is_validation=is_validation, - is_test=is_test, label=class_label, add_tokens=True) + yield Document( + content=content, + is_validation=is_validation, + is_test=is_test, + label=class_label, + add_tokens=True) diff --git a/adversarial_text/data/gen_data.py b/adversarial_text/data/gen_data.py index 0631de8e7..66aa141a1 100644 --- a/adversarial_text/data/gen_data.py +++ b/adversarial_text/data/gen_data.py @@ -1,4 +1,4 @@ -# Copyright 2017 Google, Inc. All Rights Reserved. +# Copyright 2017 Google Inc. 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. @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== - """Create TFRecord files of SequenceExample protos from dataset. Constructs 3 datasets: @@ -31,6 +30,8 @@ from __future__ import print_function import os import string +# Dependency imports + import tensorflow as tf from adversarial_text.data import data_utils @@ -197,6 +198,7 @@ def generate_test_data(vocab_ids, writer_lm_all, writer_seq_ae_all): def main(_): + tf.logging.set_verbosity(tf.logging.INFO) tf.logging.info('Assigning vocabulary ids...') vocab_ids = make_vocab_ids( FLAGS.vocab_file or os.path.join(FLAGS.output_dir, 'vocab.txt')) diff --git a/adversarial_text/data/gen_vocab.py b/adversarial_text/data/gen_vocab.py index 43a8688fa..2ee3e2cd0 100644 --- a/adversarial_text/data/gen_vocab.py +++ b/adversarial_text/data/gen_vocab.py @@ -1,4 +1,4 @@ -# Copyright 2017 Google, Inc. All Rights Reserved. +# Copyright 2017 Google Inc. 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. @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== - """Generates vocabulary and term frequency files for datasets.""" from __future__ import absolute_import from __future__ import division @@ -20,6 +19,8 @@ from __future__ import print_function from collections import defaultdict +# Dependency imports + import tensorflow as tf from adversarial_text.data import data_utils @@ -66,6 +67,7 @@ def fill_vocab_from_doc(doc, vocab_freqs, doc_counts): def main(_): + tf.logging.set_verbosity(tf.logging.INFO) vocab_freqs = defaultdict(int) doc_counts = defaultdict(int) diff --git a/adversarial_text/evaluate.py b/adversarial_text/evaluate.py index 2c96b7990..a6480ca74 100644 --- a/adversarial_text/evaluate.py +++ b/adversarial_text/evaluate.py @@ -1,4 +1,4 @@ -# Copyright 2017 Google, Inc. All Rights Reserved. +# Copyright 2017 Google Inc. 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. @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== - """Evaluates text classification model.""" from __future__ import absolute_import @@ -22,6 +21,8 @@ from __future__ import print_function import math import time +# Dependency imports + import tensorflow as tf import graphs @@ -100,6 +101,7 @@ def run_eval(eval_ops, summary_writer, saver): def _log_values(sess, value_ops, summary_writer=None): + """Evaluate, log, and write summaries of the eval metrics in value_ops.""" metric_names, value_ops = zip(*value_ops.items()) values = sess.run(value_ops) diff --git a/adversarial_text/graphs.py b/adversarial_text/graphs.py index 4d5dce8d0..f6d049f17 100644 --- a/adversarial_text/graphs.py +++ b/adversarial_text/graphs.py @@ -1,4 +1,4 @@ -# Copyright 2017 Google, Inc. All Rights Reserved. +# Copyright 2017 Google Inc. 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. @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== - """Virtual adversarial text models.""" from __future__ import absolute_import from __future__ import division @@ -20,6 +19,9 @@ from __future__ import print_function import csv import os + +# Dependency imports + import tensorflow as tf import adversarial_losses as adv_lib @@ -81,7 +83,8 @@ flags.DEFINE_integer('replicas_to_aggregate', 1, # Regularization flags.DEFINE_float('max_grad_norm', 1.0, 'Clip the global gradient norm to this value.') -flags.DEFINE_float('keep_prob_emb', 1.0, 'keep probability on embedding layer') +flags.DEFINE_float('keep_prob_emb', 1.0, 'keep probability on embedding layer. ' + '0.5 is optimal on IMDB with virtual adversarial training.') flags.DEFINE_float('keep_prob_lstm_out', 1.0, 'keep probability on lstm output.') flags.DEFINE_float('keep_prob_cl_hidden', 1.0, @@ -249,8 +252,7 @@ class VatxtModel(object): eval_ops = { 'accuracy': tf.contrib.metrics.streaming_accuracy( - layers_lib.predictions(logits), inputs.labels, - inputs.weights) + layers_lib.predictions(logits), inputs.labels, inputs.weights) } with tf.control_dependencies([inputs.save_state(next_state)]): @@ -610,7 +612,8 @@ def _inputs(dataset='train', pretrain=False, bidir=False): state_size=FLAGS.rnn_cell_size, num_layers=FLAGS.rnn_num_layers, batch_size=FLAGS.batch_size, - unroll_steps=FLAGS.num_timesteps) + unroll_steps=FLAGS.num_timesteps, + eos_id=FLAGS.vocab_size - 1) def _get_vocab_freqs(): diff --git a/adversarial_text/graphs_test.py b/adversarial_text/graphs_test.py index 849e3d06f..433afbe74 100644 --- a/adversarial_text/graphs_test.py +++ b/adversarial_text/graphs_test.py @@ -1,4 +1,4 @@ -# Copyright 2017 Google, Inc. All Rights Reserved. +# Copyright 2017 Google Inc. 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. @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== - """Tests for graphs.""" from __future__ import absolute_import from __future__ import division @@ -26,6 +25,8 @@ import shutil import string import tempfile +# Dependency imports + import tensorflow as tf import graphs diff --git a/adversarial_text/inputs.py b/adversarial_text/inputs.py index ec99eded0..5a2e462cb 100644 --- a/adversarial_text/inputs.py +++ b/adversarial_text/inputs.py @@ -1,4 +1,4 @@ -# Copyright 2017 Google, Inc. All Rights Reserved. +# Copyright 2017 Google Inc. 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. @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== - """Input utils for virtual adversarial text classification.""" from __future__ import absolute_import @@ -20,6 +19,9 @@ from __future__ import division from __future__ import print_function import os + +# Dependency imports + import tensorflow as tf from adversarial_text.data import data_utils @@ -28,7 +30,12 @@ from adversarial_text.data import data_utils class VatxtInput(object): """Wrapper around NextQueuedSequenceBatch.""" - def __init__(self, batch, state_name=None, tokens=None, num_states=0): + def __init__(self, + batch, + state_name=None, + tokens=None, + num_states=0, + eos_id=None): """Construct VatxtInput. Args: @@ -36,6 +43,7 @@ class VatxtInput(object): state_name: str, name of state to fetch and save. tokens: int Tensor, tokens. Defaults to batch's F_TOKEN_ID sequence. num_states: int The number of states to store. + eos_id: int Id of end of Sequence. """ self._batch = batch self._state_name = state_name @@ -58,6 +66,14 @@ class VatxtInput(object): l = tf.reshape(l, [-1]) self._labels = l + # eos weights + self._eos_weights = None + if eos_id: + ew = tf.cast(tf.equal(self._tokens, eos_id), tf.float32) + ew = tf.transpose(ew, [1, 0]) + ew = tf.reshape(ew, [-1]) + self._eos_weights = ew + @property def tokens(self): return self._tokens @@ -66,6 +82,10 @@ class VatxtInput(object): def weights(self): return self._weights + @property + def eos_weights(self): + return self._eos_weights + @property def labels(self): return self._labels @@ -246,7 +266,8 @@ def inputs(data_dir=None, state_size=None, num_layers=0, batch_size=32, - unroll_steps=100): + unroll_steps=100, + eos_id=None): """Inputs for text model. Args: @@ -260,7 +281,7 @@ def inputs(data_dir=None, num_layers: int, the number of LSTM layers. batch_size: int, batch size. unroll_steps: int, number of timesteps to unroll for TBTT. - + eos_id: int, id of end of sequence. used for the kl weights on vat Returns: Instance of VatxtInput (x2 if bidir=True and pretrain=True, i.e. forward and reverse). @@ -280,9 +301,15 @@ def inputs(data_dir=None, state_size, num_layers, unroll_steps, batch_size) forward_input = VatxtInput( - forward_batch, state_name=state_name, num_states=num_layers) + forward_batch, + state_name=state_name, + num_states=num_layers, + eos_id=eos_id) reverse_input = VatxtInput( - reverse_batch, state_name=state_name_rev, num_states=num_layers) + reverse_batch, + state_name=state_name_rev, + num_states=num_layers, + eos_id=eos_id) return forward_input, reverse_input elif bidir: @@ -322,4 +349,5 @@ def inputs(data_dir=None, unroll_steps, batch_size, bidir_input=False) - return VatxtInput(batch, state_name=state_name, num_states=num_layers) + return VatxtInput( + batch, state_name=state_name, num_states=num_layers, eos_id=eos_id) diff --git a/adversarial_text/layers.py b/adversarial_text/layers.py index c560be306..f99f8e27f 100644 --- a/adversarial_text/layers.py +++ b/adversarial_text/layers.py @@ -1,4 +1,4 @@ -# Copyright 2017 Google, Inc. All Rights Reserved. +# Copyright 2017 Google Inc. 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. @@ -12,14 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== - """Layers for VatxtModel.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -import tensorflow as tf +# Dependency imports +import tensorflow as tf K = tf.contrib.keras @@ -34,7 +34,7 @@ def cl_logits_subgraph(layer_sizes, input_size, num_classes, keep_prob=1.): subgraph.add(K.layers.Dense(layer_size, activation='relu')) if keep_prob < 1.: - subgraph.add(K.layers.Dropout(keep_prob)) + subgraph.add(K.layers.Dropout(1. - keep_prob)) subgraph.add(K.layers.Dense(1 if num_classes == 2 else num_classes)) return subgraph @@ -76,7 +76,14 @@ class Embedding(K.layers.Layer): def call(self, x): embedded = tf.nn.embedding_lookup(self.var, x) if self.keep_prob < 1.: - embedded = tf.nn.dropout(embedded, self.keep_prob) + shape = embedded.get_shape().as_list() + + # Use same dropout masks at each timestep with specifying noise_shape. + # This slightly improves performance. + # Please see https://arxiv.org/abs/1512.05287 for the theoretical + # explanation. + embedded = tf.nn.dropout( + embedded, self.keep_prob, noise_shape=(shape[0], 1, shape[2])) return embedded def _normalize(self, emb): @@ -153,11 +160,11 @@ class SoftmaxLoss(K.layers.Layer): self.lin_w = self.add_weight( shape=(input_shape[-1], self.vocab_size), name='lm_lin_w', - initializer='glorot_uniform') + initializer=K.initializers.glorot_uniform()) self.lin_b = self.add_weight( shape=(self.vocab_size,), name='lm_lin_b', - initializer='glorot_uniform') + initializer=K.initializers.glorot_uniform()) super(SoftmaxLoss, self).build(input_shape) diff --git a/adversarial_text/pretrain.py b/adversarial_text/pretrain.py index 25d6a4766..4e1fa6a4c 100644 --- a/adversarial_text/pretrain.py +++ b/adversarial_text/pretrain.py @@ -1,4 +1,4 @@ -# Copyright 2017 Google, Inc. All Rights Reserved. +# Copyright 2017 Google Inc. 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. @@ -12,18 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== - """Pretrains a recurrent language model. Computational time: - 5 days to train 100000 steps on 1 layer 1024 hidden units LSTM, - 256 embeddings, 400 truncated BP, 64 minibatch and on 4 GPU with - SyncReplicasOptimizer, that is the total minibatch is 256. + 2 days to train 100000 steps on 1 layer 1024 hidden units LSTM, + 256 embeddings, 400 truncated BP, 256 minibatch and on single GPU (Pascal + Titan X, cuDNNv5). """ from __future__ import absolute_import from __future__ import division from __future__ import print_function +# Dependency imports + import tensorflow as tf import graphs diff --git a/adversarial_text/train_classifier.py b/adversarial_text/train_classifier.py index 94fba3f6f..f498d2c2f 100644 --- a/adversarial_text/train_classifier.py +++ b/adversarial_text/train_classifier.py @@ -1,4 +1,4 @@ -# Copyright 2017 Google, Inc. All Rights Reserved. +# Copyright 2017 Google Inc. 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. @@ -12,17 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== - """Trains LSTM text classification model. Model trains with adversarial or virtual adversarial training. Computational time: - 6 hours to train 10000 steps without adversarial or virtual adversarial + 1.8 hours to train 10000 steps without adversarial or virtual adversarial training, on 1 layer 1024 hidden units LSTM, 256 embeddings, 400 truncated - BP, 64 minibatch and on single GPU. + BP, 64 minibatch and on single GPU (Pascal Titan X, cuDNNv5). - 12 hours to train 10000 steps with adversarial or virtual adversarial + 4 hours to train 10000 steps with adversarial or virtual adversarial training, with above condition. To initialize embedding and LSTM cell weights from a pretrained model, set @@ -32,6 +31,8 @@ from __future__ import absolute_import from __future__ import division from __future__ import print_function +# Dependency imports + import tensorflow as tf import graphs diff --git a/adversarial_text/train_utils.py b/adversarial_text/train_utils.py index 91104a135..2c09d7ae3 100644 --- a/adversarial_text/train_utils.py +++ b/adversarial_text/train_utils.py @@ -1,4 +1,4 @@ -# Copyright 2017 Google, Inc. All Rights Reserved. +# Copyright 2017 Google Inc. 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. @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== - """Utilities for training adversarial text models.""" from __future__ import absolute_import from __future__ import division @@ -20,6 +19,8 @@ from __future__ import print_function import time +# Dependency imports + import numpy as np import tensorflow as tf -- GitLab From 60c3ed2e34efbe428ae04ab99ccebd795187c12f Mon Sep 17 00:00:00 2001 From: derekjchow Date: Wed, 14 Jun 2017 19:33:20 -0700 Subject: [PATCH 045/110] Update resnet (#1559) --- slim/nets/resnet_utils.py | 18 ++------ slim/nets/resnet_v1.py | 79 +++++++++++++++++-------------- slim/nets/resnet_v1_test.py | 50 ++++++++------------ slim/nets/resnet_v2.py | 92 ++++++++++++++++++++++--------------- slim/nets/resnet_v2_test.py | 50 ++++++++------------ 5 files changed, 143 insertions(+), 146 deletions(-) diff --git a/slim/nets/resnet_utils.py b/slim/nets/resnet_utils.py index 1e1dd8292..20d7789a6 100644 --- a/slim/nets/resnet_utils.py +++ b/slim/nets/resnet_utils.py @@ -178,26 +178,16 @@ def stack_blocks_dense(net, blocks, output_stride=None, raise ValueError('The target output_stride cannot be reached.') with tf.variable_scope('unit_%d' % (i + 1), values=[net]): - unit_depth, unit_depth_bottleneck, unit_stride = unit - # If we have reached the target output_stride, then we need to employ # atrous convolution with stride=1 and multiply the atrous rate by the # current unit's stride for use in subsequent layers. if output_stride is not None and current_stride == output_stride: - net = block.unit_fn(net, - depth=unit_depth, - depth_bottleneck=unit_depth_bottleneck, - stride=1, - rate=rate) - rate *= unit_stride + net = block.unit_fn(net, rate=rate, **dict(unit, stride=1)) + rate *= unit.get('stride', 1) else: - net = block.unit_fn(net, - depth=unit_depth, - depth_bottleneck=unit_depth_bottleneck, - stride=unit_stride, - rate=1) - current_stride *= unit_stride + net = block.unit_fn(net, rate=1, **unit) + current_stride *= unit.get('stride', 1) net = slim.utils.collect_named_outputs(outputs_collections, sc.name, net) if output_stride is not None and current_stride != output_stride: diff --git a/slim/nets/resnet_v1.py b/slim/nets/resnet_v1.py index 3cb3121e9..19ae0a241 100644 --- a/slim/nets/resnet_v1.py +++ b/slim/nets/resnet_v1.py @@ -119,7 +119,7 @@ def resnet_v1(inputs, global_pool=True, output_stride=None, include_root_block=True, - spatial_squeeze=True, + spatial_squeeze=False, reuse=None, scope=None): """Generator for v1 ResNet models. @@ -205,13 +205,38 @@ def resnet_v1(inputs, else: logits = net # Convert end_points_collection into a dictionary of end_points. - end_points = slim.utils.convert_collection_to_dict(end_points_collection) + end_points = slim.utils.convert_collection_to_dict( + end_points_collection) if num_classes is not None: end_points['predictions'] = slim.softmax(logits, scope='predictions') return logits, end_points resnet_v1.default_image_size = 224 +def resnet_v1_block(scope, base_depth, num_units, stride): + """Helper function for creating a resnet_v1 bottleneck block. + + Args: + scope: The scope of the block. + base_depth: The depth of the bottleneck layer for each unit. + num_units: The number of units in the block. + stride: The stride of the block, implemented as a stride in the last unit. + All other units have stride=1. + + Returns: + A resnet_v1 bottleneck block. + """ + return resnet_utils.Block(scope, bottleneck, [{ + 'depth': base_depth * 4, + 'depth_bottleneck': base_depth, + 'stride': 1 + }] * (num_units - 1) + [{ + 'depth': base_depth * 4, + 'depth_bottleneck': base_depth, + 'stride': stride + }]) + + def resnet_v1_50(inputs, num_classes=None, is_training=True, @@ -222,14 +247,10 @@ def resnet_v1_50(inputs, scope='resnet_v1_50'): """ResNet-50 model of [1]. See resnet_v1() for arg and return description.""" blocks = [ - resnet_utils.Block( - 'block1', bottleneck, [(256, 64, 1)] * 2 + [(256, 64, 2)]), - resnet_utils.Block( - 'block2', bottleneck, [(512, 128, 1)] * 3 + [(512, 128, 2)]), - resnet_utils.Block( - 'block3', bottleneck, [(1024, 256, 1)] * 5 + [(1024, 256, 2)]), - resnet_utils.Block( - 'block4', bottleneck, [(2048, 512, 1)] * 3) + resnet_v1_block('block1', base_depth=64, num_units=3, stride=2), + resnet_v1_block('block2', base_depth=128, num_units=4, stride=2), + resnet_v1_block('block3', base_depth=256, num_units=6, stride=2), + resnet_v1_block('block4', base_depth=512, num_units=3, stride=1), ] return resnet_v1(inputs, blocks, num_classes, is_training, global_pool=global_pool, output_stride=output_stride, @@ -248,14 +269,10 @@ def resnet_v1_101(inputs, scope='resnet_v1_101'): """ResNet-101 model of [1]. See resnet_v1() for arg and return description.""" blocks = [ - resnet_utils.Block( - 'block1', bottleneck, [(256, 64, 1)] * 2 + [(256, 64, 2)]), - resnet_utils.Block( - 'block2', bottleneck, [(512, 128, 1)] * 3 + [(512, 128, 2)]), - resnet_utils.Block( - 'block3', bottleneck, [(1024, 256, 1)] * 22 + [(1024, 256, 2)]), - resnet_utils.Block( - 'block4', bottleneck, [(2048, 512, 1)] * 3) + resnet_v1_block('block1', base_depth=64, num_units=3, stride=2), + resnet_v1_block('block2', base_depth=128, num_units=4, stride=2), + resnet_v1_block('block3', base_depth=256, num_units=23, stride=2), + resnet_v1_block('block4', base_depth=512, num_units=3, stride=1), ] return resnet_v1(inputs, blocks, num_classes, is_training, global_pool=global_pool, output_stride=output_stride, @@ -274,14 +291,11 @@ def resnet_v1_152(inputs, scope='resnet_v1_152'): """ResNet-152 model of [1]. See resnet_v1() for arg and return description.""" blocks = [ - resnet_utils.Block( - 'block1', bottleneck, [(256, 64, 1)] * 2 + [(256, 64, 2)]), - resnet_utils.Block( - 'block2', bottleneck, [(512, 128, 1)] * 7 + [(512, 128, 2)]), - resnet_utils.Block( - 'block3', bottleneck, [(1024, 256, 1)] * 35 + [(1024, 256, 2)]), - resnet_utils.Block( - 'block4', bottleneck, [(2048, 512, 1)] * 3)] + resnet_v1_block('block1', base_depth=64, num_units=3, stride=2), + resnet_v1_block('block2', base_depth=128, num_units=8, stride=2), + resnet_v1_block('block3', base_depth=256, num_units=36, stride=2), + resnet_v1_block('block4', base_depth=512, num_units=3, stride=1), + ] return resnet_v1(inputs, blocks, num_classes, is_training, global_pool=global_pool, output_stride=output_stride, include_root_block=True, spatial_squeeze=spatial_squeeze, @@ -299,14 +313,11 @@ def resnet_v1_200(inputs, scope='resnet_v1_200'): """ResNet-200 model of [2]. See resnet_v1() for arg and return description.""" blocks = [ - resnet_utils.Block( - 'block1', bottleneck, [(256, 64, 1)] * 2 + [(256, 64, 2)]), - resnet_utils.Block( - 'block2', bottleneck, [(512, 128, 1)] * 23 + [(512, 128, 2)]), - resnet_utils.Block( - 'block3', bottleneck, [(1024, 256, 1)] * 35 + [(1024, 256, 2)]), - resnet_utils.Block( - 'block4', bottleneck, [(2048, 512, 1)] * 3)] + resnet_v1_block('block1', base_depth=64, num_units=3, stride=2), + resnet_v1_block('block2', base_depth=128, num_units=24, stride=2), + resnet_v1_block('block3', base_depth=256, num_units=36, stride=2), + resnet_v1_block('block4', base_depth=512, num_units=3, stride=1), + ] return resnet_v1(inputs, blocks, num_classes, is_training, global_pool=global_pool, output_stride=output_stride, include_root_block=True, spatial_squeeze=spatial_squeeze, diff --git a/slim/nets/resnet_v1_test.py b/slim/nets/resnet_v1_test.py index 5c229a516..6bee51914 100644 --- a/slim/nets/resnet_v1_test.py +++ b/slim/nets/resnet_v1_test.py @@ -156,14 +156,17 @@ class ResnetUtilsTest(tf.test.TestCase): with tf.variable_scope(scope, values=[inputs]): with slim.arg_scope([slim.conv2d], outputs_collections='end_points'): net = resnet_utils.stack_blocks_dense(inputs, blocks, output_stride) - end_points = dict(tf.get_collection('end_points')) + end_points = slim.utils.convert_collection_to_dict('end_points') return net, end_points def testEndPointsV1(self): """Test the end points of a tiny v1 bottleneck network.""" - bottleneck = resnet_v1.bottleneck - blocks = [resnet_utils.Block('block1', bottleneck, [(4, 1, 1), (4, 1, 2)]), - resnet_utils.Block('block2', bottleneck, [(8, 2, 1), (8, 2, 1)])] + blocks = [ + resnet_v1.resnet_v1_block( + 'block1', base_depth=1, num_units=2, stride=2), + resnet_v1.resnet_v1_block( + 'block2', base_depth=2, num_units=2, stride=1), + ] inputs = create_test_input(2, 32, 16, 3) with slim.arg_scope(resnet_utils.resnet_arg_scope()): _, end_points = self._resnet_plain(inputs, blocks, scope='tiny') @@ -189,30 +192,23 @@ class ResnetUtilsTest(tf.test.TestCase): for block in blocks: with tf.variable_scope(block.scope, 'block', [net]): for i, unit in enumerate(block.args): - depth, depth_bottleneck, stride = unit with tf.variable_scope('unit_%d' % (i + 1), values=[net]): - net = block.unit_fn(net, - depth=depth, - depth_bottleneck=depth_bottleneck, - stride=stride, - rate=1) + net = block.unit_fn(net, rate=1, **unit) return net - def _atrousValues(self, bottleneck): + def testAtrousValuesBottleneck(self): """Verify the values of dense feature extraction by atrous convolution. Make sure that dense feature extraction by stack_blocks_dense() followed by subsampling gives identical results to feature extraction at the nominal network output stride using the simple self._stack_blocks_nondense() above. - - Args: - bottleneck: The bottleneck function. """ + block = resnet_v1.resnet_v1_block blocks = [ - resnet_utils.Block('block1', bottleneck, [(4, 1, 1), (4, 1, 2)]), - resnet_utils.Block('block2', bottleneck, [(8, 2, 1), (8, 2, 2)]), - resnet_utils.Block('block3', bottleneck, [(16, 4, 1), (16, 4, 2)]), - resnet_utils.Block('block4', bottleneck, [(32, 8, 1), (32, 8, 1)]) + block('block1', base_depth=1, num_units=2, stride=2), + block('block2', base_depth=2, num_units=2, stride=2), + block('block3', base_depth=4, num_units=2, stride=2), + block('block4', base_depth=8, num_units=2, stride=1), ] nominal_stride = 8 @@ -244,9 +240,6 @@ class ResnetUtilsTest(tf.test.TestCase): output, expected = sess.run([output, expected]) self.assertAllClose(output, expected, atol=1e-4, rtol=1e-4) - def testAtrousValuesBottleneck(self): - self._atrousValues(resnet_v1.bottleneck) - class ResnetCompleteNetworkTest(tf.test.TestCase): """Tests with complete small ResNet v1 networks.""" @@ -261,16 +254,13 @@ class ResnetCompleteNetworkTest(tf.test.TestCase): reuse=None, scope='resnet_v1_small'): """A shallow and thin ResNet v1 for faster tests.""" - bottleneck = resnet_v1.bottleneck + block = resnet_v1.resnet_v1_block blocks = [ - resnet_utils.Block( - 'block1', bottleneck, [(4, 1, 1)] * 2 + [(4, 1, 2)]), - resnet_utils.Block( - 'block2', bottleneck, [(8, 2, 1)] * 2 + [(8, 2, 2)]), - resnet_utils.Block( - 'block3', bottleneck, [(16, 4, 1)] * 2 + [(16, 4, 2)]), - resnet_utils.Block( - 'block4', bottleneck, [(32, 8, 1)] * 2)] + block('block1', base_depth=1, num_units=3, stride=2), + block('block2', base_depth=2, num_units=3, stride=2), + block('block3', base_depth=4, num_units=3, stride=2), + block('block4', base_depth=8, num_units=2, stride=1), + ] return resnet_v1.resnet_v1(inputs, blocks, num_classes, is_training=is_training, global_pool=global_pool, diff --git a/slim/nets/resnet_v2.py b/slim/nets/resnet_v2.py index 87a1df67d..e8e798345 100644 --- a/slim/nets/resnet_v2.py +++ b/slim/nets/resnet_v2.py @@ -25,6 +25,8 @@ introduced by: The key difference of the full preactivation 'v2' variant compared to the 'v1' variant in [1] is the use of batch normalization before every weight layer. +Another difference is that 'v2' ResNets do not include an activation function in +the main pathway. Also see [2; Fig. 4e]. Typical use: @@ -115,7 +117,7 @@ def resnet_v2(inputs, global_pool=True, output_stride=None, include_root_block=True, - spatial_squeeze=True, + spatial_squeeze=False, reuse=None, scope=None): """Generator for v2 (preactivation) ResNet models. @@ -212,31 +214,54 @@ def resnet_v2(inputs, else: logits = net # Convert end_points_collection into a dictionary of end_points. - end_points = slim.utils.convert_collection_to_dict(end_points_collection) + end_points = slim.utils.convert_collection_to_dict( + end_points_collection) if num_classes is not None: end_points['predictions'] = slim.softmax(logits, scope='predictions') return logits, end_points resnet_v2.default_image_size = 224 +def resnet_v2_block(scope, base_depth, num_units, stride): + """Helper function for creating a resnet_v2 bottleneck block. + + Args: + scope: The scope of the block. + base_depth: The depth of the bottleneck layer for each unit. + num_units: The number of units in the block. + stride: The stride of the block, implemented as a stride in the last unit. + All other units have stride=1. + + Returns: + A resnet_v2 bottleneck block. + """ + return resnet_utils.Block(scope, bottleneck, [{ + 'depth': base_depth * 4, + 'depth_bottleneck': base_depth, + 'stride': 1 + }] * (num_units - 1) + [{ + 'depth': base_depth * 4, + 'depth_bottleneck': base_depth, + 'stride': stride + }]) +resnet_v2.default_image_size = 224 + + def resnet_v2_50(inputs, num_classes=None, is_training=True, global_pool=True, output_stride=None, - spatial_squeeze=True, + spatial_squeeze=False, reuse=None, scope='resnet_v2_50'): """ResNet-50 model of [1]. See resnet_v2() for arg and return description.""" blocks = [ - resnet_utils.Block( - 'block1', bottleneck, [(256, 64, 1)] * 2 + [(256, 64, 2)]), - resnet_utils.Block( - 'block2', bottleneck, [(512, 128, 1)] * 3 + [(512, 128, 2)]), - resnet_utils.Block( - 'block3', bottleneck, [(1024, 256, 1)] * 5 + [(1024, 256, 2)]), - resnet_utils.Block( - 'block4', bottleneck, [(2048, 512, 1)] * 3)] + resnet_v2_block('block1', base_depth=64, num_units=3, stride=2), + resnet_v2_block('block2', base_depth=128, num_units=4, stride=2), + resnet_v2_block('block3', base_depth=256, num_units=6, stride=2), + resnet_v2_block('block4', base_depth=512, num_units=3, stride=1), + ] return resnet_v2(inputs, blocks, num_classes, is_training=is_training, global_pool=global_pool, output_stride=output_stride, include_root_block=True, spatial_squeeze=spatial_squeeze, @@ -249,19 +274,16 @@ def resnet_v2_101(inputs, is_training=True, global_pool=True, output_stride=None, - spatial_squeeze=True, + spatial_squeeze=False, reuse=None, scope='resnet_v2_101'): """ResNet-101 model of [1]. See resnet_v2() for arg and return description.""" blocks = [ - resnet_utils.Block( - 'block1', bottleneck, [(256, 64, 1)] * 2 + [(256, 64, 2)]), - resnet_utils.Block( - 'block2', bottleneck, [(512, 128, 1)] * 3 + [(512, 128, 2)]), - resnet_utils.Block( - 'block3', bottleneck, [(1024, 256, 1)] * 22 + [(1024, 256, 2)]), - resnet_utils.Block( - 'block4', bottleneck, [(2048, 512, 1)] * 3)] + resnet_v2_block('block1', base_depth=64, num_units=3, stride=2), + resnet_v2_block('block2', base_depth=128, num_units=4, stride=2), + resnet_v2_block('block3', base_depth=256, num_units=23, stride=2), + resnet_v2_block('block4', base_depth=512, num_units=3, stride=1), + ] return resnet_v2(inputs, blocks, num_classes, is_training=is_training, global_pool=global_pool, output_stride=output_stride, include_root_block=True, spatial_squeeze=spatial_squeeze, @@ -274,19 +296,16 @@ def resnet_v2_152(inputs, is_training=True, global_pool=True, output_stride=None, - spatial_squeeze=True, + spatial_squeeze=False, reuse=None, scope='resnet_v2_152'): """ResNet-152 model of [1]. See resnet_v2() for arg and return description.""" blocks = [ - resnet_utils.Block( - 'block1', bottleneck, [(256, 64, 1)] * 2 + [(256, 64, 2)]), - resnet_utils.Block( - 'block2', bottleneck, [(512, 128, 1)] * 7 + [(512, 128, 2)]), - resnet_utils.Block( - 'block3', bottleneck, [(1024, 256, 1)] * 35 + [(1024, 256, 2)]), - resnet_utils.Block( - 'block4', bottleneck, [(2048, 512, 1)] * 3)] + resnet_v2_block('block1', base_depth=64, num_units=3, stride=2), + resnet_v2_block('block2', base_depth=128, num_units=8, stride=2), + resnet_v2_block('block3', base_depth=256, num_units=36, stride=2), + resnet_v2_block('block4', base_depth=512, num_units=3, stride=1), + ] return resnet_v2(inputs, blocks, num_classes, is_training=is_training, global_pool=global_pool, output_stride=output_stride, include_root_block=True, spatial_squeeze=spatial_squeeze, @@ -299,19 +318,16 @@ def resnet_v2_200(inputs, is_training=True, global_pool=True, output_stride=None, - spatial_squeeze=True, + spatial_squeeze=False, reuse=None, scope='resnet_v2_200'): """ResNet-200 model of [2]. See resnet_v2() for arg and return description.""" blocks = [ - resnet_utils.Block( - 'block1', bottleneck, [(256, 64, 1)] * 2 + [(256, 64, 2)]), - resnet_utils.Block( - 'block2', bottleneck, [(512, 128, 1)] * 23 + [(512, 128, 2)]), - resnet_utils.Block( - 'block3', bottleneck, [(1024, 256, 1)] * 35 + [(1024, 256, 2)]), - resnet_utils.Block( - 'block4', bottleneck, [(2048, 512, 1)] * 3)] + resnet_v2_block('block1', base_depth=64, num_units=3, stride=2), + resnet_v2_block('block2', base_depth=128, num_units=24, stride=2), + resnet_v2_block('block3', base_depth=256, num_units=36, stride=2), + resnet_v2_block('block4', base_depth=512, num_units=3, stride=1), + ] return resnet_v2(inputs, blocks, num_classes, is_training=is_training, global_pool=global_pool, output_stride=output_stride, include_root_block=True, spatial_squeeze=spatial_squeeze, diff --git a/slim/nets/resnet_v2_test.py b/slim/nets/resnet_v2_test.py index 141937d1e..8efe33878 100644 --- a/slim/nets/resnet_v2_test.py +++ b/slim/nets/resnet_v2_test.py @@ -156,14 +156,17 @@ class ResnetUtilsTest(tf.test.TestCase): with tf.variable_scope(scope, values=[inputs]): with slim.arg_scope([slim.conv2d], outputs_collections='end_points'): net = resnet_utils.stack_blocks_dense(inputs, blocks, output_stride) - end_points = dict(tf.get_collection('end_points')) + end_points = slim.utils.convert_collection_to_dict('end_points') return net, end_points def testEndPointsV2(self): """Test the end points of a tiny v2 bottleneck network.""" - bottleneck = resnet_v2.bottleneck - blocks = [resnet_utils.Block('block1', bottleneck, [(4, 1, 1), (4, 1, 2)]), - resnet_utils.Block('block2', bottleneck, [(8, 2, 1), (8, 2, 1)])] + blocks = [ + resnet_v2.resnet_v2_block( + 'block1', base_depth=1, num_units=2, stride=2), + resnet_v2.resnet_v2_block( + 'block2', base_depth=2, num_units=2, stride=1), + ] inputs = create_test_input(2, 32, 16, 3) with slim.arg_scope(resnet_utils.resnet_arg_scope()): _, end_points = self._resnet_plain(inputs, blocks, scope='tiny') @@ -189,30 +192,23 @@ class ResnetUtilsTest(tf.test.TestCase): for block in blocks: with tf.variable_scope(block.scope, 'block', [net]): for i, unit in enumerate(block.args): - depth, depth_bottleneck, stride = unit with tf.variable_scope('unit_%d' % (i + 1), values=[net]): - net = block.unit_fn(net, - depth=depth, - depth_bottleneck=depth_bottleneck, - stride=stride, - rate=1) + net = block.unit_fn(net, rate=1, **unit) return net - def _atrousValues(self, bottleneck): + def testAtrousValuesBottleneck(self): """Verify the values of dense feature extraction by atrous convolution. Make sure that dense feature extraction by stack_blocks_dense() followed by subsampling gives identical results to feature extraction at the nominal network output stride using the simple self._stack_blocks_nondense() above. - - Args: - bottleneck: The bottleneck function. """ + block = resnet_v2.resnet_v2_block blocks = [ - resnet_utils.Block('block1', bottleneck, [(4, 1, 1), (4, 1, 2)]), - resnet_utils.Block('block2', bottleneck, [(8, 2, 1), (8, 2, 2)]), - resnet_utils.Block('block3', bottleneck, [(16, 4, 1), (16, 4, 2)]), - resnet_utils.Block('block4', bottleneck, [(32, 8, 1), (32, 8, 1)]) + block('block1', base_depth=1, num_units=2, stride=2), + block('block2', base_depth=2, num_units=2, stride=2), + block('block3', base_depth=4, num_units=2, stride=2), + block('block4', base_depth=8, num_units=2, stride=1), ] nominal_stride = 8 @@ -244,9 +240,6 @@ class ResnetUtilsTest(tf.test.TestCase): output, expected = sess.run([output, expected]) self.assertAllClose(output, expected, atol=1e-4, rtol=1e-4) - def testAtrousValuesBottleneck(self): - self._atrousValues(resnet_v2.bottleneck) - class ResnetCompleteNetworkTest(tf.test.TestCase): """Tests with complete small ResNet v2 networks.""" @@ -261,16 +254,13 @@ class ResnetCompleteNetworkTest(tf.test.TestCase): reuse=None, scope='resnet_v2_small'): """A shallow and thin ResNet v2 for faster tests.""" - bottleneck = resnet_v2.bottleneck + block = resnet_v2.resnet_v2_block blocks = [ - resnet_utils.Block( - 'block1', bottleneck, [(4, 1, 1)] * 2 + [(4, 1, 2)]), - resnet_utils.Block( - 'block2', bottleneck, [(8, 2, 1)] * 2 + [(8, 2, 2)]), - resnet_utils.Block( - 'block3', bottleneck, [(16, 4, 1)] * 2 + [(16, 4, 2)]), - resnet_utils.Block( - 'block4', bottleneck, [(32, 8, 1)] * 2)] + block('block1', base_depth=1, num_units=3, stride=2), + block('block2', base_depth=2, num_units=3, stride=2), + block('block3', base_depth=4, num_units=3, stride=2), + block('block4', base_depth=8, num_units=2, stride=1), + ] return resnet_v2.resnet_v2(inputs, blocks, num_classes, is_training=is_training, global_pool=global_pool, -- GitLab From a4944a57ad2811e1f6a7a87589a9fc8a776e8d3c Mon Sep 17 00:00:00 2001 From: derekjchow Date: Wed, 14 Jun 2017 21:06:38 -0700 Subject: [PATCH 046/110] Add Tensorflow Object Detection API. (#1561) For details see our paper: "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 https://arxiv.org/abs/1611.10012 --- object_detection/BUILD | 178 ++ object_detection/CONTRIBUTING.md | 13 + object_detection/README.md | 81 + object_detection/__init__.py | 0 object_detection/anchor_generators/BUILD | 56 + .../anchor_generators/__init__.py | 0 .../grid_anchor_generator.py | 194 ++ .../grid_anchor_generator_test.py | 76 + .../multiple_grid_anchor_generator.py | 273 +++ .../multiple_grid_anchor_generator_test.py | 253 +++ object_detection/box_coders/BUILD | 102 + object_detection/box_coders/__init__.py | 0 .../box_coders/faster_rcnn_box_coder.py | 118 + .../box_coders/faster_rcnn_box_coder_test.py | 94 + .../box_coders/keypoint_box_coder.py | 171 ++ .../box_coders/keypoint_box_coder_test.py | 140 ++ .../box_coders/mean_stddev_box_coder.py | 70 + .../box_coders/mean_stddev_box_coder_test.py | 58 + .../box_coders/square_box_coder.py | 126 ++ .../box_coders/square_box_coder_test.py | 97 + object_detection/builders/BUILD | 296 +++ object_detection/builders/__init__.py | 0 .../builders/anchor_generator_builder.py | 66 + .../builders/anchor_generator_builder_test.py | 194 ++ .../builders/box_coder_builder.py | 55 + .../builders/box_coder_builder_test.py | 107 + .../builders/box_predictor_builder.py | 106 + .../builders/box_predictor_builder_test.py | 391 ++++ .../builders/hyperparams_builder.py | 169 ++ .../builders/hyperparams_builder_test.py | 450 ++++ .../builders/image_resizer_builder.py | 62 + .../builders/image_resizer_builder_test.py | 70 + .../builders/input_reader_builder.py | 65 + .../builders/input_reader_builder_test.py | 92 + object_detection/builders/losses_builder.py | 161 ++ .../builders/losses_builder_test.py | 323 +++ object_detection/builders/matcher_builder.py | 51 + .../builders/matcher_builder_test.py | 97 + object_detection/builders/model_builder.py | 303 +++ .../builders/model_builder_test.py | 456 ++++ .../builders/optimizer_builder.py | 112 + .../builders/optimizer_builder_test.py | 197 ++ .../builders/post_processing_builder.py | 111 + .../builders/post_processing_builder_test.py | 73 + .../builders/preprocessor_builder.py | 277 +++ .../builders/preprocessor_builder_test.py | 452 ++++ .../region_similarity_calculator_builder.py | 56 + ...gion_similarity_calculator_builder_test.py | 67 + object_detection/core/BUILD | 362 ++++ object_detection/core/__init__.py | 0 object_detection/core/anchor_generator.py | 142 ++ .../balanced_positive_negative_sampler.py | 92 + ...balanced_positive_negative_sampler_test.py | 83 + object_detection/core/batcher.py | 133 ++ object_detection/core/batcher_test.py | 158 ++ object_detection/core/box_coder.py | 151 ++ object_detection/core/box_coder_test.py | 61 + object_detection/core/box_list.py | 207 ++ object_detection/core/box_list_ops.py | 975 +++++++++ object_detection/core/box_list_ops_test.py | 962 +++++++++ object_detection/core/box_list_test.py | 134 ++ object_detection/core/box_predictor.py | 546 +++++ object_detection/core/box_predictor_test.py | 323 +++ object_detection/core/data_decoder.py | 42 + object_detection/core/keypoint_ops.py | 231 ++ object_detection/core/keypoint_ops_test.py | 168 ++ object_detection/core/losses.py | 551 +++++ object_detection/core/losses_test.py | 562 +++++ object_detection/core/matcher.py | 213 ++ object_detection/core/matcher_test.py | 150 ++ object_detection/core/minibatch_sampler.py | 90 + .../core/minibatch_sampler_test.py | 82 + object_detection/core/model.py | 252 +++ object_detection/core/post_processing.py | 298 +++ object_detection/core/post_processing_test.py | 673 ++++++ object_detection/core/prefetcher.py | 61 + object_detection/core/prefetcher_test.py | 101 + object_detection/core/preprocessor.py | 1922 +++++++++++++++++ object_detection/core/preprocessor_test.py | 1746 +++++++++++++++ .../core/region_similarity_calculator.py | 114 + .../core/region_similarity_calculator_test.py | 75 + object_detection/core/standard_fields.py | 150 ++ object_detection/core/target_assigner.py | 449 ++++ object_detection/core/target_assigner_test.py | 682 ++++++ object_detection/create_pascal_tf_record.py | 174 ++ .../create_pascal_tf_record_test.py | 118 + object_detection/create_pet_tf_record.py | 211 ++ object_detection/data/mscoco_label_map.pbtxt | 400 ++++ object_detection/data/pascal_label_map.pbtxt | 104 + object_detection/data/pet_label_map.pbtxt | 189 ++ object_detection/data_decoders/BUILD | 28 + object_detection/data_decoders/__init__.py | 0 .../data_decoders/tf_example_decoder.py | 147 ++ .../data_decoders/tf_example_decoder_test.py | 288 +++ object_detection/eval.py | 161 ++ object_detection/eval_util.py | 524 +++++ object_detection/evaluator.py | 211 ++ object_detection/export_inference_graph.py | 90 + object_detection/exporter.py | 230 ++ object_detection/exporter_test.py | 225 ++ object_detection/g3doc/configuring_jobs.md | 162 ++ .../g3doc/defining_your_own_model.md | 137 ++ object_detection/g3doc/detection_model_zoo.md | 43 + object_detection/g3doc/exporting_models.md | 22 + .../g3doc/img/dogs_detections_output.jpg | Bin 0 -> 372894 bytes .../g3doc/img/kites_detections_output.jpg | Bin 0 -> 386066 bytes object_detection/g3doc/img/oxford_pet.png | Bin 0 -> 276715 bytes object_detection/g3doc/img/tensorboard.png | Bin 0 -> 79342 bytes object_detection/g3doc/img/tensorboard2.png | Bin 0 -> 236549 bytes object_detection/g3doc/installation.md | 79 + object_detection/g3doc/preparing_inputs.md | 45 + object_detection/g3doc/running_locally.md | 81 + object_detection/g3doc/running_notebook.md | 15 + object_detection/g3doc/running_on_cloud.md | 128 ++ object_detection/g3doc/running_pets.md | 303 +++ object_detection/matchers/BUILD | 51 + object_detection/matchers/__init__.py | 0 object_detection/matchers/argmax_matcher.py | 189 ++ .../matchers/argmax_matcher_test.py | 237 ++ .../matchers/bipartite_matcher.py | 53 + .../matchers/bipartite_matcher_test.py | 71 + object_detection/meta_architectures/BUILD | 109 + .../meta_architectures/__init__.py | 0 .../faster_rcnn_meta_arch.py | 1451 +++++++++++++ .../faster_rcnn_meta_arch_test.py | 84 + .../faster_rcnn_meta_arch_test_lib.py | 1035 +++++++++ .../meta_architectures/rfcn_meta_arch.py | 267 +++ .../meta_architectures/rfcn_meta_arch_test.py | 56 + .../meta_architectures/ssd_meta_arch.py | 594 +++++ .../meta_architectures/ssd_meta_arch_test.py | 258 +++ object_detection/models/BUILD | 135 ++ object_detection/models/__init__.py | 0 ...n_inception_resnet_v2_feature_extractor.py | 216 ++ ...eption_resnet_v2_feature_extractor_test.py | 108 + ...faster_rcnn_resnet_v1_feature_extractor.py | 235 ++ ...r_rcnn_resnet_v1_feature_extractor_test.py | 136 ++ .../models/feature_map_generators.py | 179 ++ .../models/feature_map_generators_test.py | 114 + .../models/ssd_feature_extractor_test.py | 96 + .../ssd_inception_v2_feature_extractor.py | 99 + ...ssd_inception_v2_feature_extractor_test.py | 95 + .../ssd_mobilenet_v1_feature_extractor.py | 101 + ...ssd_mobilenet_v1_feature_extractor_test.py | 94 + object_detection/object_detection.blueprint | 56 + .../object_detection_tutorial.ipynb | 263 +++ object_detection/protos/BUILD | 329 +++ object_detection/protos/__init__.py | 0 .../protos/anchor_generator.proto | 15 + object_detection/protos/argmax_matcher.proto | 25 + .../protos/bipartite_matcher.proto | 8 + object_detection/protos/box_coder.proto | 17 + object_detection/protos/box_predictor.proto | 99 + object_detection/protos/eval.proto | 47 + object_detection/protos/faster_rcnn.proto | 131 ++ .../protos/faster_rcnn_box_coder.proto | 17 + .../protos/grid_anchor_generator.proto | 34 + object_detection/protos/hyperparams.proto | 103 + object_detection/protos/image_resizer.proto | 32 + object_detection/protos/input_reader.proto | 60 + object_detection/protos/losses.proto | 116 + object_detection/protos/matcher.proto | 15 + .../protos/mean_stddev_box_coder.proto | 8 + object_detection/protos/model.proto | 14 + object_detection/protos/optimizer.proto | 73 + object_detection/protos/pipeline.proto | 18 + object_detection/protos/post_processing.proto | 42 + object_detection/protos/preprocessor.proto | 326 +++ .../protos/region_similarity_calculator.proto | 25 + .../protos/square_box_coder.proto | 14 + object_detection/protos/ssd.proto | 65 + .../protos/ssd_anchor_generator.proto | 25 + .../protos/string_int_label_map.proto | 24 + object_detection/protos/train.proto | 64 + object_detection/samples/cloud/cloud.yml | 11 + ...cnn_inception_resnet_v2_atrous_pets.config | 136 ++ .../configs/faster_rcnn_resnet101_pets.config | 134 ++ .../faster_rcnn_resnet101_voc07.config | 135 ++ .../configs/faster_rcnn_resnet152_pets.config | 134 ++ .../configs/faster_rcnn_resnet50_pets.config | 134 ++ .../configs/rfcn_resnet101_pets.config | 131 ++ .../configs/ssd_inception_v2_pets.config | 180 ++ .../configs/ssd_mobilenet_v1_pets.config | 186 ++ object_detection/test_images/image1.jpg | Bin 0 -> 129862 bytes object_detection/test_images/image2.jpg | Bin 0 -> 1415684 bytes object_detection/test_images/image_info.txt | 6 + object_detection/train.py | 198 ++ object_detection/trainer.py | 290 +++ object_detection/trainer_test.py | 205 ++ object_detection/utils/BUILD | 287 +++ object_detection/utils/__init__.py | 0 object_detection/utils/category_util.py | 72 + object_detection/utils/category_util_test.py | 54 + object_detection/utils/dataset_util.py | 86 + object_detection/utils/dataset_util_test.py | 37 + object_detection/utils/label_map_util.py | 126 ++ object_detection/utils/label_map_util_test.py | 147 ++ object_detection/utils/learning_schedules.py | 103 + .../utils/learning_schedules_test.py | 59 + object_detection/utils/metrics.py | 144 ++ object_detection/utils/metrics_test.py | 79 + object_detection/utils/np_box_list.py | 133 ++ object_detection/utils/np_box_list_ops.py | 555 +++++ .../utils/np_box_list_ops_test.py | 414 ++++ object_detection/utils/np_box_list_test.py | 135 ++ object_detection/utils/np_box_ops.py | 97 + object_detection/utils/np_box_ops_test.py | 68 + .../utils/object_detection_evaluation.py | 233 ++ .../utils/object_detection_evaluation_test.py | 125 ++ object_detection/utils/ops.py | 650 ++++++ object_detection/utils/ops_test.py | 1033 +++++++++ .../utils/per_image_evaluation.py | 260 +++ .../utils/per_image_evaluation_test.py | 212 ++ object_detection/utils/shape_utils.py | 113 + object_detection/utils/shape_utils_test.py | 120 + object_detection/utils/static_shape.py | 71 + object_detection/utils/static_shape_test.py | 50 + object_detection/utils/test_utils.py | 137 ++ object_detection/utils/test_utils_test.py | 73 + object_detection/utils/variables_helper.py | 133 ++ .../utils/variables_helper_test.py | 185 ++ object_detection/utils/visualization_utils.py | 422 ++++ .../utils/visualization_utils_test.py | 151 ++ setup.py | 16 + slim/setup.py | 13 + 224 files changed, 40616 insertions(+) create mode 100644 object_detection/BUILD create mode 100644 object_detection/CONTRIBUTING.md create mode 100644 object_detection/README.md create mode 100644 object_detection/__init__.py create mode 100644 object_detection/anchor_generators/BUILD create mode 100644 object_detection/anchor_generators/__init__.py create mode 100644 object_detection/anchor_generators/grid_anchor_generator.py create mode 100644 object_detection/anchor_generators/grid_anchor_generator_test.py create mode 100644 object_detection/anchor_generators/multiple_grid_anchor_generator.py create mode 100644 object_detection/anchor_generators/multiple_grid_anchor_generator_test.py create mode 100644 object_detection/box_coders/BUILD create mode 100644 object_detection/box_coders/__init__.py create mode 100644 object_detection/box_coders/faster_rcnn_box_coder.py create mode 100644 object_detection/box_coders/faster_rcnn_box_coder_test.py create mode 100644 object_detection/box_coders/keypoint_box_coder.py create mode 100644 object_detection/box_coders/keypoint_box_coder_test.py create mode 100644 object_detection/box_coders/mean_stddev_box_coder.py create mode 100644 object_detection/box_coders/mean_stddev_box_coder_test.py create mode 100644 object_detection/box_coders/square_box_coder.py create mode 100644 object_detection/box_coders/square_box_coder_test.py create mode 100644 object_detection/builders/BUILD create mode 100644 object_detection/builders/__init__.py create mode 100644 object_detection/builders/anchor_generator_builder.py create mode 100644 object_detection/builders/anchor_generator_builder_test.py create mode 100644 object_detection/builders/box_coder_builder.py create mode 100644 object_detection/builders/box_coder_builder_test.py create mode 100644 object_detection/builders/box_predictor_builder.py create mode 100644 object_detection/builders/box_predictor_builder_test.py create mode 100644 object_detection/builders/hyperparams_builder.py create mode 100644 object_detection/builders/hyperparams_builder_test.py create mode 100644 object_detection/builders/image_resizer_builder.py create mode 100644 object_detection/builders/image_resizer_builder_test.py create mode 100644 object_detection/builders/input_reader_builder.py create mode 100644 object_detection/builders/input_reader_builder_test.py create mode 100644 object_detection/builders/losses_builder.py create mode 100644 object_detection/builders/losses_builder_test.py create mode 100644 object_detection/builders/matcher_builder.py create mode 100644 object_detection/builders/matcher_builder_test.py create mode 100644 object_detection/builders/model_builder.py create mode 100644 object_detection/builders/model_builder_test.py create mode 100644 object_detection/builders/optimizer_builder.py create mode 100644 object_detection/builders/optimizer_builder_test.py create mode 100644 object_detection/builders/post_processing_builder.py create mode 100644 object_detection/builders/post_processing_builder_test.py create mode 100644 object_detection/builders/preprocessor_builder.py create mode 100644 object_detection/builders/preprocessor_builder_test.py create mode 100644 object_detection/builders/region_similarity_calculator_builder.py create mode 100644 object_detection/builders/region_similarity_calculator_builder_test.py create mode 100644 object_detection/core/BUILD create mode 100644 object_detection/core/__init__.py create mode 100644 object_detection/core/anchor_generator.py create mode 100644 object_detection/core/balanced_positive_negative_sampler.py create mode 100644 object_detection/core/balanced_positive_negative_sampler_test.py create mode 100644 object_detection/core/batcher.py create mode 100644 object_detection/core/batcher_test.py create mode 100644 object_detection/core/box_coder.py create mode 100644 object_detection/core/box_coder_test.py create mode 100644 object_detection/core/box_list.py create mode 100644 object_detection/core/box_list_ops.py create mode 100644 object_detection/core/box_list_ops_test.py create mode 100644 object_detection/core/box_list_test.py create mode 100644 object_detection/core/box_predictor.py create mode 100644 object_detection/core/box_predictor_test.py create mode 100644 object_detection/core/data_decoder.py create mode 100644 object_detection/core/keypoint_ops.py create mode 100644 object_detection/core/keypoint_ops_test.py create mode 100644 object_detection/core/losses.py create mode 100644 object_detection/core/losses_test.py create mode 100644 object_detection/core/matcher.py create mode 100644 object_detection/core/matcher_test.py create mode 100644 object_detection/core/minibatch_sampler.py create mode 100644 object_detection/core/minibatch_sampler_test.py create mode 100644 object_detection/core/model.py create mode 100644 object_detection/core/post_processing.py create mode 100644 object_detection/core/post_processing_test.py create mode 100644 object_detection/core/prefetcher.py create mode 100644 object_detection/core/prefetcher_test.py create mode 100644 object_detection/core/preprocessor.py create mode 100644 object_detection/core/preprocessor_test.py create mode 100644 object_detection/core/region_similarity_calculator.py create mode 100644 object_detection/core/region_similarity_calculator_test.py create mode 100644 object_detection/core/standard_fields.py create mode 100644 object_detection/core/target_assigner.py create mode 100644 object_detection/core/target_assigner_test.py create mode 100644 object_detection/create_pascal_tf_record.py create mode 100644 object_detection/create_pascal_tf_record_test.py create mode 100644 object_detection/create_pet_tf_record.py create mode 100644 object_detection/data/mscoco_label_map.pbtxt create mode 100644 object_detection/data/pascal_label_map.pbtxt create mode 100644 object_detection/data/pet_label_map.pbtxt create mode 100644 object_detection/data_decoders/BUILD create mode 100644 object_detection/data_decoders/__init__.py create mode 100644 object_detection/data_decoders/tf_example_decoder.py create mode 100644 object_detection/data_decoders/tf_example_decoder_test.py create mode 100644 object_detection/eval.py create mode 100644 object_detection/eval_util.py create mode 100644 object_detection/evaluator.py create mode 100644 object_detection/export_inference_graph.py create mode 100644 object_detection/exporter.py create mode 100644 object_detection/exporter_test.py create mode 100644 object_detection/g3doc/configuring_jobs.md create mode 100644 object_detection/g3doc/defining_your_own_model.md create mode 100644 object_detection/g3doc/detection_model_zoo.md create mode 100644 object_detection/g3doc/exporting_models.md create mode 100644 object_detection/g3doc/img/dogs_detections_output.jpg create mode 100644 object_detection/g3doc/img/kites_detections_output.jpg create mode 100644 object_detection/g3doc/img/oxford_pet.png create mode 100644 object_detection/g3doc/img/tensorboard.png create mode 100644 object_detection/g3doc/img/tensorboard2.png create mode 100644 object_detection/g3doc/installation.md create mode 100644 object_detection/g3doc/preparing_inputs.md create mode 100644 object_detection/g3doc/running_locally.md create mode 100644 object_detection/g3doc/running_notebook.md create mode 100644 object_detection/g3doc/running_on_cloud.md create mode 100644 object_detection/g3doc/running_pets.md create mode 100644 object_detection/matchers/BUILD create mode 100644 object_detection/matchers/__init__.py create mode 100644 object_detection/matchers/argmax_matcher.py create mode 100644 object_detection/matchers/argmax_matcher_test.py create mode 100644 object_detection/matchers/bipartite_matcher.py create mode 100644 object_detection/matchers/bipartite_matcher_test.py create mode 100644 object_detection/meta_architectures/BUILD create mode 100644 object_detection/meta_architectures/__init__.py create mode 100644 object_detection/meta_architectures/faster_rcnn_meta_arch.py create mode 100644 object_detection/meta_architectures/faster_rcnn_meta_arch_test.py create mode 100644 object_detection/meta_architectures/faster_rcnn_meta_arch_test_lib.py create mode 100644 object_detection/meta_architectures/rfcn_meta_arch.py create mode 100644 object_detection/meta_architectures/rfcn_meta_arch_test.py create mode 100644 object_detection/meta_architectures/ssd_meta_arch.py create mode 100644 object_detection/meta_architectures/ssd_meta_arch_test.py create mode 100644 object_detection/models/BUILD create mode 100644 object_detection/models/__init__.py create mode 100644 object_detection/models/faster_rcnn_inception_resnet_v2_feature_extractor.py create mode 100644 object_detection/models/faster_rcnn_inception_resnet_v2_feature_extractor_test.py create mode 100644 object_detection/models/faster_rcnn_resnet_v1_feature_extractor.py create mode 100644 object_detection/models/faster_rcnn_resnet_v1_feature_extractor_test.py create mode 100644 object_detection/models/feature_map_generators.py create mode 100644 object_detection/models/feature_map_generators_test.py create mode 100644 object_detection/models/ssd_feature_extractor_test.py create mode 100644 object_detection/models/ssd_inception_v2_feature_extractor.py create mode 100644 object_detection/models/ssd_inception_v2_feature_extractor_test.py create mode 100644 object_detection/models/ssd_mobilenet_v1_feature_extractor.py create mode 100644 object_detection/models/ssd_mobilenet_v1_feature_extractor_test.py create mode 100644 object_detection/object_detection.blueprint create mode 100644 object_detection/object_detection_tutorial.ipynb create mode 100644 object_detection/protos/BUILD create mode 100644 object_detection/protos/__init__.py create mode 100644 object_detection/protos/anchor_generator.proto create mode 100644 object_detection/protos/argmax_matcher.proto create mode 100644 object_detection/protos/bipartite_matcher.proto create mode 100644 object_detection/protos/box_coder.proto create mode 100644 object_detection/protos/box_predictor.proto create mode 100644 object_detection/protos/eval.proto create mode 100644 object_detection/protos/faster_rcnn.proto create mode 100644 object_detection/protos/faster_rcnn_box_coder.proto create mode 100644 object_detection/protos/grid_anchor_generator.proto create mode 100644 object_detection/protos/hyperparams.proto create mode 100644 object_detection/protos/image_resizer.proto create mode 100644 object_detection/protos/input_reader.proto create mode 100644 object_detection/protos/losses.proto create mode 100644 object_detection/protos/matcher.proto create mode 100644 object_detection/protos/mean_stddev_box_coder.proto create mode 100644 object_detection/protos/model.proto create mode 100644 object_detection/protos/optimizer.proto create mode 100644 object_detection/protos/pipeline.proto create mode 100644 object_detection/protos/post_processing.proto create mode 100644 object_detection/protos/preprocessor.proto create mode 100644 object_detection/protos/region_similarity_calculator.proto create mode 100644 object_detection/protos/square_box_coder.proto create mode 100644 object_detection/protos/ssd.proto create mode 100644 object_detection/protos/ssd_anchor_generator.proto create mode 100644 object_detection/protos/string_int_label_map.proto create mode 100644 object_detection/protos/train.proto create mode 100644 object_detection/samples/cloud/cloud.yml create mode 100644 object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_pets.config create mode 100644 object_detection/samples/configs/faster_rcnn_resnet101_pets.config create mode 100644 object_detection/samples/configs/faster_rcnn_resnet101_voc07.config create mode 100644 object_detection/samples/configs/faster_rcnn_resnet152_pets.config create mode 100644 object_detection/samples/configs/faster_rcnn_resnet50_pets.config create mode 100644 object_detection/samples/configs/rfcn_resnet101_pets.config create mode 100644 object_detection/samples/configs/ssd_inception_v2_pets.config create mode 100644 object_detection/samples/configs/ssd_mobilenet_v1_pets.config create mode 100644 object_detection/test_images/image1.jpg create mode 100644 object_detection/test_images/image2.jpg create mode 100644 object_detection/test_images/image_info.txt create mode 100644 object_detection/train.py create mode 100644 object_detection/trainer.py create mode 100644 object_detection/trainer_test.py create mode 100644 object_detection/utils/BUILD create mode 100644 object_detection/utils/__init__.py create mode 100644 object_detection/utils/category_util.py create mode 100644 object_detection/utils/category_util_test.py create mode 100644 object_detection/utils/dataset_util.py create mode 100644 object_detection/utils/dataset_util_test.py create mode 100644 object_detection/utils/label_map_util.py create mode 100644 object_detection/utils/label_map_util_test.py create mode 100644 object_detection/utils/learning_schedules.py create mode 100644 object_detection/utils/learning_schedules_test.py create mode 100644 object_detection/utils/metrics.py create mode 100644 object_detection/utils/metrics_test.py create mode 100644 object_detection/utils/np_box_list.py create mode 100644 object_detection/utils/np_box_list_ops.py create mode 100644 object_detection/utils/np_box_list_ops_test.py create mode 100644 object_detection/utils/np_box_list_test.py create mode 100644 object_detection/utils/np_box_ops.py create mode 100644 object_detection/utils/np_box_ops_test.py create mode 100644 object_detection/utils/object_detection_evaluation.py create mode 100644 object_detection/utils/object_detection_evaluation_test.py create mode 100644 object_detection/utils/ops.py create mode 100644 object_detection/utils/ops_test.py create mode 100644 object_detection/utils/per_image_evaluation.py create mode 100644 object_detection/utils/per_image_evaluation_test.py create mode 100644 object_detection/utils/shape_utils.py create mode 100644 object_detection/utils/shape_utils_test.py create mode 100644 object_detection/utils/static_shape.py create mode 100644 object_detection/utils/static_shape_test.py create mode 100644 object_detection/utils/test_utils.py create mode 100644 object_detection/utils/test_utils_test.py create mode 100644 object_detection/utils/variables_helper.py create mode 100644 object_detection/utils/variables_helper_test.py create mode 100644 object_detection/utils/visualization_utils.py create mode 100644 object_detection/utils/visualization_utils_test.py create mode 100644 setup.py create mode 100644 slim/setup.py diff --git a/object_detection/BUILD b/object_detection/BUILD new file mode 100644 index 000000000..f77e3d644 --- /dev/null +++ b/object_detection/BUILD @@ -0,0 +1,178 @@ +# Tensorflow Object Detection API: main runnables. + +package( + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) + +# Apache 2.0 + +py_binary( + name = "train", + srcs = [ + "train.py", + ], + deps = [ + ":trainer", + "//tensorflow", + "//tensorflow_models/object_detection/builders:input_reader_builder", + "//tensorflow_models/object_detection/builders:model_builder", + "//tensorflow_models/object_detection/protos:input_reader_py_pb2", + "//tensorflow_models/object_detection/protos:model_py_pb2", + "//tensorflow_models/object_detection/protos:pipeline_py_pb2", + "//tensorflow_models/object_detection/protos:train_py_pb2", + ], +) + +py_library( + name = "trainer", + srcs = ["trainer.py"], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/builders:optimizer_builder", + "//tensorflow_models/object_detection/builders:preprocessor_builder", + "//tensorflow_models/object_detection/core:batcher", + "//tensorflow_models/object_detection/core:standard_fields", + "//tensorflow_models/object_detection/utils:ops", + "//tensorflow_models/object_detection/utils:variables_helper", + "//tensorflow_models/slim:model_deploy", + ], +) + +py_test( + name = "trainer_test", + srcs = ["trainer_test.py"], + deps = [ + ":trainer", + "//tensorflow", + "//tensorflow_models/object_detection/core:losses", + "//tensorflow_models/object_detection/core:model", + "//tensorflow_models/object_detection/core:standard_fields", + "//tensorflow_models/object_detection/protos:train_py_pb2", + ], +) + +py_library( + name = "eval_util", + srcs = [ + "eval_util.py", + ], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/utils:label_map_util", + "//tensorflow_models/object_detection/utils:object_detection_evaluation", + "//tensorflow_models/object_detection/utils:visualization_utils", + ], +) + +py_library( + name = "evaluator", + srcs = ["evaluator.py"], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection:eval_util", + "//tensorflow_models/object_detection/core:box_list", + "//tensorflow_models/object_detection/core:box_list_ops", + "//tensorflow_models/object_detection/core:prefetcher", + "//tensorflow_models/object_detection/core:standard_fields", + "//tensorflow_models/object_detection/protos:eval_py_pb2", + ], +) + +py_binary( + name = "eval", + srcs = [ + "eval.py", + ], + deps = [ + ":evaluator", + "//tensorflow", + "//tensorflow_models/object_detection/builders:input_reader_builder", + "//tensorflow_models/object_detection/builders:model_builder", + "//tensorflow_models/object_detection/protos:eval_py_pb2", + "//tensorflow_models/object_detection/protos:input_reader_py_pb2", + "//tensorflow_models/object_detection/protos:model_py_pb2", + "//tensorflow_models/object_detection/protos:pipeline_py_pb2", + "//tensorflow_models/object_detection/utils:label_map_util", + ], +) + +py_library( + name = "exporter", + srcs = [ + "exporter.py", + ], + deps = [ + "//tensorflow", + "//tensorflow/python/tools:freeze_graph_lib", + "//tensorflow_models/object_detection/builders:model_builder", + "//tensorflow_models/object_detection/core:standard_fields", + "//tensorflow_models/object_detection/data_decoders:tf_example_decoder", + ], +) + +py_test( + name = "exporter_test", + srcs = [ + "exporter_test.py", + ], + deps = [ + ":exporter", + "//tensorflow", + "//tensorflow_models/object_detection/builders:model_builder", + "//tensorflow_models/object_detection/core:model", + "//tensorflow_models/object_detection/protos:pipeline_py_pb2", + ], +) + +py_binary( + name = "export_inference_graph", + srcs = [ + "export_inference_graph.py", + ], + deps = [ + ":exporter", + "//tensorflow", + "//tensorflow_models/object_detection/protos:pipeline_py_pb2", + ], +) + +py_binary( + name = "create_pascal_tf_record", + srcs = [ + "create_pascal_tf_record.py", + ], + deps = [ + "//third_party/py/PIL:pil", + "//third_party/py/lxml", + "//tensorflow", + "//tensorflow_models/object_detection/utils:dataset_util", + "//tensorflow_models/object_detection/utils:label_map_util", + ], +) + +py_test( + name = "create_pascal_tf_record_test", + srcs = [ + "create_pascal_tf_record_test.py", + ], + deps = [ + ":create_pascal_tf_record", + "//tensorflow", + ], +) + +py_binary( + name = "create_pet_tf_record", + srcs = [ + "create_pet_tf_record.py", + ], + deps = [ + "//third_party/py/PIL:pil", + "//third_party/py/lxml", + "//tensorflow", + "//tensorflow_models/object_detection/utils:dataset_util", + "//tensorflow_models/object_detection/utils:label_map_util", + ], +) diff --git a/object_detection/CONTRIBUTING.md b/object_detection/CONTRIBUTING.md new file mode 100644 index 000000000..e3d87e3ce --- /dev/null +++ b/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/object_detection/README.md b/object_detection/README.md new file mode 100644 index 000000000..833f94cec --- /dev/null +++ b/object_detection/README.md @@ -0,0 +1,81 @@ +# Tensorflow Object Detection API +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)\] + +## Maintainers + +* Jonathan Huang, github: [jch1](https://github.com/jch1) +* Vivek Rathod, github: [tombstone](https://github.com/tombstone) +* Derek Chow, github: [derekjchow](https://github.com/derekjchow) +* Chen Sun, github: [jesu9](https://github.com/jesu9) +* Menglong Zhu, github: [dreamdragon](https://github.com/dreamdragon) + + +## Table of contents + +Quick Start: +*
+ Quick Start: Jupyter notebook for off-the-shelf inference
+* Quick Start: Training on a pet detector
+ +Setup: +* Installation
+* + Configuring an object detection pipeline
+* Preparing inputs
+ +Running: +* Running locally
+* Running on the cloud
+ +Extras: +* Tensorflow detection model zoo
+* + Exporting a trained model for inference
+* + Defining your own model architecture
+ +## Release information + +### June 15, 2017 + +In addition to our base Tensorflow detection model definitions, this +release includes: + +* A selection of trainable detection models, including: + * Single Shot Multibox Detector (SSD) with MobileNet, + * SSD with Inception V2, + * Region-Based Fully Convolutional Networks (R-FCN) with Resnet 101, + * Faster RCNN with Resnet 101, + * Faster RCNN with Inception Resnet v2 + * Mask R-CNN with Resnet 101. +* Frozen weights (trained on the COCO dataset) for each of the above models to + be used for out-of-the-box inference purposes. +* A [Jupyter notebook](object_detection_tutorial.ipynb) for performing + out-of-the-box inference with one of our released models +* Convenient [local training](g3doc/running_locally.md) scripts as well as + distributed training and evaluation pipelines via + [Google Cloud](g3doc/running_on_cloud.md). + + +Thanks to contributors: Jonathan Huang, Vivek Rathod, Derek Chow, +Chen Sun, Menglong Zhu, Matthew Tang, Anoop Korattikara, Alireza Fathi, Ian Fischer, Zbigniew Wojna, Yang Song, Sergio Guadarrama, Jasper Uijlings, +Viacheslav Kovalevskyi, Kevin Murphy diff --git a/object_detection/__init__.py b/object_detection/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/object_detection/anchor_generators/BUILD b/object_detection/anchor_generators/BUILD new file mode 100644 index 000000000..cb421a0c1 --- /dev/null +++ b/object_detection/anchor_generators/BUILD @@ -0,0 +1,56 @@ +# Tensorflow Object Detection API: Anchor Generator implementations. + +package( + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) + +# Apache 2.0 +py_library( + name = "grid_anchor_generator", + srcs = [ + "grid_anchor_generator.py", + ], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/core:anchor_generator", + "//tensorflow_models/object_detection/core:box_list", + "//tensorflow_models/object_detection/utils:ops", + ], +) + +py_test( + name = "grid_anchor_generator_test", + srcs = [ + "grid_anchor_generator_test.py", + ], + deps = [ + ":grid_anchor_generator", + "//tensorflow", + ], +) + +py_library( + name = "multiple_grid_anchor_generator", + srcs = [ + "multiple_grid_anchor_generator.py", + ], + deps = [ + ":grid_anchor_generator", + "//tensorflow", + "//tensorflow_models/object_detection/core:anchor_generator", + "//tensorflow_models/object_detection/core:box_list_ops", + ], +) + +py_test( + name = "multiple_grid_anchor_generator_test", + srcs = [ + "multiple_grid_anchor_generator_test.py", + ], + deps = [ + ":multiple_grid_anchor_generator", + "//third_party/py/numpy", + ], +) diff --git a/object_detection/anchor_generators/__init__.py b/object_detection/anchor_generators/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/object_detection/anchor_generators/grid_anchor_generator.py b/object_detection/anchor_generators/grid_anchor_generator.py new file mode 100644 index 000000000..d2ea2c07d --- /dev/null +++ b/object_detection/anchor_generators/grid_anchor_generator.py @@ -0,0 +1,194 @@ +# 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 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, default=[256, 256]) + anchor_stride: difference in centers between base anchors for adjacent + grid positions (length-2 float32 list, 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 tensor, default=[0, 0]) + """ + # Handle argument defaults + if base_anchor_size is None: + base_anchor_size = [256, 256] + base_anchor_size = tf.constant(base_anchor_size, tf.float32) + if anchor_stride is None: + anchor_stride = [16, 16] + anchor_stride = tf.constant(anchor_stride, dtype=tf.float32) + if anchor_offset is None: + anchor_offset = [0, 0] + anchor_offset = tf.constant(anchor_offset, dtype=tf.float32) + + 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: a BoxList holding a collection of N anchor boxes + 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.') + 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]) + return tile_anchors(grid_height, + grid_width, + scales_grid, + aspect_ratios_grid, + self._base_anchor_size, + self._anchor_stride, + self._anchor_offset) + + +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.to_float(tf.range(grid_height)) + y_centers = y_centers * anchor_stride[0] + anchor_offset[0] + x_centers = tf.to_float(tf.range(grid_width)) + 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/object_detection/anchor_generators/grid_anchor_generator_test.py b/object_detection/anchor_generators/grid_anchor_generator_test.py new file mode 100644 index 000000000..80a82a390 --- /dev/null +++ b/object_detection/anchor_generators/grid_anchor_generator_test.py @@ -0,0 +1,76 @@ +# 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 tensorflow as tf + +from object_detection.anchor_generators import grid_anchor_generator + + +class GridAnchorGeneratorTest(tf.test.TestCase): + + def test_construct_single_anchor(self): + """Builds a 1x1 anchor grid to test the size of the output boxes.""" + scales = [0.5, 1.0, 2.0] + aspect_ratios = [0.25, 1.0, 4.0] + anchor_offset = [7, -3] + 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_generator = grid_anchor_generator.GridAnchorGenerator( + scales, aspect_ratios, + anchor_offset=anchor_offset) + anchors = anchor_generator.generate(feature_map_shape_list=[(1, 1)]) + anchor_corners = anchors.get() + + with self.test_session(): + anchor_corners_out = anchor_corners.eval() + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_anchor_grid(self): + base_anchor_size = [10, 10] + anchor_stride = [19, 19] + anchor_offset = [0, 0] + scales = [0.5, 1.0, 2.0] + aspect_ratios = [1.0] + + 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_generator = grid_anchor_generator.GridAnchorGenerator( + scales, + aspect_ratios, + base_anchor_size=base_anchor_size, + anchor_stride=anchor_stride, + anchor_offset=anchor_offset) + + anchors = anchor_generator.generate(feature_map_shape_list=[(2, 2)]) + anchor_corners = anchors.get() + + with self.test_session(): + anchor_corners_out = anchor_corners.eval() + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/anchor_generators/multiple_grid_anchor_generator.py b/object_detection/anchor_generators/multiple_grid_anchor_generator.py new file mode 100644 index 000000000..655d99f19 --- /dev/null +++ b/object_detection/anchor_generators/multiple_grid_anchor_generator.py @@ -0,0 +1,273 @@ +# 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 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, + 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 tensor, default=[256, 256]). + 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 = tf.constant([256, 256], dtype=tf.float32) + self._base_anchor_size = base_anchor_size + 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) + + 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, + anchor_strides=None, + anchor_offsets=None): + """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 + normalized coordinates, otherwise absolute coordinates are used for the + grid. + 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 + normalized coordinates, otherwise absolute coordinates are used for the + grid. + anchor_strides: list of pairs of strides (in y and x directions + respectively). For example, setting + anchor_strides=[(.25, .25), (.5, .5)] means that we want the anchors + corresponding to the first layer to be strided by .25 and those in the + second layer to be strided by .5 in both y and x directions. By + default, if anchor_strides=None, then they are set to be the reciprocal + of the corresponding grid sizes. The pairs can also be specified as + dynamic tf.int or tf.float numbers, e.g. for variable shape input + images. + anchor_offsets: list of pairs of offsets (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=[(.125, .125), (.25, .25)]) means that we want the + (0, 0)-th anchor of the first layer to lie at (.125, .125) in image + space and likewise that we want the (0, 0)-th anchor of the second + layer to lie at (.25, .25) in image space. By default, if + anchor_offsets=None, then they are set to be half of the corresponding + anchor stride. The pairs can also be specified as dynamic tf.int or + tf.float numbers, e.g. for variable shape input images. + + Returns: + boxes: a BoxList holding a collection of N anchor boxes + 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.') + if not anchor_strides: + anchor_strides = [(tf.to_float(im_height) / tf.to_float(pair[0]), + tf.to_float(im_width) / tf.to_float(pair[1])) + for pair in feature_map_shape_list] + if not anchor_offsets: + anchor_offsets = [(0.5 * stride[0], 0.5 * stride[1]) + for stride in anchor_strides] + 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.to_float(tf.minimum(im_height, im_width)) + base_anchor_size = min_im_shape * self._base_anchor_size + for grid_size, scales, aspect_ratios, stride, offset in zip( + feature_map_shape_list, self._scales, self._aspect_ratios, + anchor_strides, anchor_offsets): + anchor_grid_list.append( + 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)) + concatenated_anchors = box_list_ops.concatenate(anchor_grid_list) + num_anchors = concatenated_anchors.num_boxes_static() + if num_anchors is None: + num_anchors = concatenated_anchors.num_boxes() + if self._clip_window is not None: + clip_window = tf.multiply( + tf.to_float([im_height, im_width, im_height, im_width]), + self._clip_window) + concatenated_anchors = box_list_ops.clip_to_window( + concatenated_anchors, clip_window, filter_nonoverlapping=False) + # TODO: make reshape an option for the clip_to_window op + concatenated_anchors.set( + tf.reshape(concatenated_anchors.get(), [num_anchors, 4])) + + stddevs_tensor = 0.01 * tf.ones( + [num_anchors, 4], dtype=tf.float32, name='stddevs') + concatenated_anchors.add_field('stddev', stddevs_tensor) + + return concatenated_anchors + + +def create_ssd_anchors(num_layers=6, + min_scale=0.2, + max_scale=0.95, + aspect_ratios=(1.0, 2.0, 3.0, 1.0/2, 1.0/3), + base_anchor_size=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) + aspect_ratios: list or tuple of (float) aspect ratios to place on each + grid point. + base_anchor_size: base anchor size as [height, width]. + 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] + base_anchor_size = tf.constant(base_anchor_size, dtype=tf.float32) + box_specs_list = [] + scales = [min_scale + (max_scale - min_scale) * i / (num_layers - 1) + for i in range(num_layers)] + [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)) + if aspect_ratio == 1.0: + layer_box_specs.append((np.sqrt(scale*scale_next), 1.0)) + box_specs_list.append(layer_box_specs) + return MultipleGridAnchorGenerator(box_specs_list, base_anchor_size) diff --git a/object_detection/anchor_generators/multiple_grid_anchor_generator_test.py b/object_detection/anchor_generators/multiple_grid_anchor_generator_test.py new file mode 100644 index 000000000..a7f0346b6 --- /dev/null +++ b/object_detection/anchor_generators/multiple_grid_anchor_generator_test.py @@ -0,0 +1,253 @@ +# 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.multiple_grid_anchor_generator_test.py.""" + +import numpy as np + +import tensorflow as tf + +from object_detection.anchor_generators import multiple_grid_anchor_generator as ag + + +class MultipleGridAnchorGeneratorTest(tf.test.TestCase): + + def test_construct_single_anchor_grid(self): + """Builds a 1x1 anchor grid to test the size of the output boxes.""" + 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]] + + base_anchor_size = tf.constant([256, 256], dtype=tf.float32) + box_specs_list = [[(.5, .25), (1.0, .25), (2.0, .25), + (.5, 1.0), (1.0, 1.0), (2.0, 1.0), + (.5, 4.0), (1.0, 4.0), (2.0, 4.0)]] + anchor_generator = ag.MultipleGridAnchorGenerator( + box_specs_list, base_anchor_size) + anchors = anchor_generator.generate(feature_map_shape_list=[(1, 1)], + anchor_strides=[(16, 16)], + anchor_offsets=[(7, -3)]) + anchor_corners = anchors.get() + with self.test_session(): + anchor_corners_out = anchor_corners.eval() + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_anchor_grid(self): + base_anchor_size = tf.constant([10, 10], dtype=tf.float32) + box_specs_list = [[(0.5, 1.0), (1.0, 1.0), (2.0, 1.0)]] + + 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_generator = ag.MultipleGridAnchorGenerator( + box_specs_list, base_anchor_size) + anchors = anchor_generator.generate(feature_map_shape_list=[(2, 2)], + anchor_strides=[(19, 19)], + anchor_offsets=[(0, 0)]) + anchor_corners = anchors.get() + + with self.test_session(): + anchor_corners_out = anchor_corners.eval() + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_anchor_grid_non_square(self): + base_anchor_size = tf.constant([1, 1], dtype=tf.float32) + box_specs_list = [[(1.0, 1.0)]] + + exp_anchor_corners = [[0., -0.25, 1., 0.75], [0., 0.25, 1., 1.25]] + + anchor_generator = ag.MultipleGridAnchorGenerator(box_specs_list, + base_anchor_size) + anchors = anchor_generator.generate(feature_map_shape_list=[(tf.constant( + 1, dtype=tf.int32), tf.constant(2, dtype=tf.int32))]) + anchor_corners = anchors.get() + + with self.test_session(): + anchor_corners_out = anchor_corners.eval() + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_anchor_grid_unnormalized(self): + base_anchor_size = tf.constant([1, 1], dtype=tf.float32) + box_specs_list = [[(1.0, 1.0)]] + + exp_anchor_corners = [[0., 0., 320., 320.], [0., 320., 320., 640.]] + + anchor_generator = ag.MultipleGridAnchorGenerator(box_specs_list, + base_anchor_size) + anchors = anchor_generator.generate( + feature_map_shape_list=[(tf.constant(1, dtype=tf.int32), tf.constant( + 2, dtype=tf.int32))], + im_height=320, + im_width=640) + anchor_corners = anchors.get() + + with self.test_session(): + anchor_corners_out = anchor_corners.eval() + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_multiple_grids(self): + base_anchor_size = tf.constant([1.0, 1.0], dtype=tf.float32) + box_specs_list = [[(1.0, 1.0), (2.0, 1.0), (1.0, 0.5)], + [(1.0, 1.0), (1.0, 0.5)]] + + # height and width of box with .5 aspect ratio + h = np.sqrt(2) + w = 1.0/np.sqrt(2) + exp_small_grid_corners = [[-.25, -.25, .75, .75], + [.25-.5*h, .25-.5*w, .25+.5*h, .25+.5*w], + [-.25, .25, .75, 1.25], + [.25-.5*h, .75-.5*w, .25+.5*h, .75+.5*w], + [.25, -.25, 1.25, .75], + [.75-.5*h, .25-.5*w, .75+.5*h, .25+.5*w], + [.25, .25, 1.25, 1.25], + [.75-.5*h, .75-.5*w, .75+.5*h, .75+.5*w]] + # only test first entry of larger set of anchors + exp_big_grid_corners = [[.125-.5, .125-.5, .125+.5, .125+.5], + [.125-1.0, .125-1.0, .125+1.0, .125+1.0], + [.125-.5*h, .125-.5*w, .125+.5*h, .125+.5*w],] + + anchor_generator = ag.MultipleGridAnchorGenerator( + box_specs_list, base_anchor_size) + anchors = anchor_generator.generate(feature_map_shape_list=[(4, 4), (2, 2)], + anchor_strides=[(.25, .25), (.5, .5)], + anchor_offsets=[(.125, .125), + (.25, .25)]) + anchor_corners = anchors.get() + + with self.test_session(): + anchor_corners_out = anchor_corners.eval() + self.assertEquals(anchor_corners_out.shape, (56, 4)) + big_grid_corners = anchor_corners_out[0:3, :] + small_grid_corners = anchor_corners_out[48:, :] + self.assertAllClose(small_grid_corners, exp_small_grid_corners) + self.assertAllClose(big_grid_corners, exp_big_grid_corners) + + def test_construct_multiple_grids_with_clipping(self): + base_anchor_size = tf.constant([1.0, 1.0], dtype=tf.float32) + box_specs_list = [[(1.0, 1.0), (2.0, 1.0), (1.0, 0.5)], + [(1.0, 1.0), (1.0, 0.5)]] + + # height and width of box with .5 aspect ratio + h = np.sqrt(2) + w = 1.0/np.sqrt(2) + exp_small_grid_corners = [[0, 0, .75, .75], + [0, 0, .25+.5*h, .25+.5*w], + [0, .25, .75, 1], + [0, .75-.5*w, .25+.5*h, 1], + [.25, 0, 1, .75], + [.75-.5*h, 0, 1, .25+.5*w], + [.25, .25, 1, 1], + [.75-.5*h, .75-.5*w, 1, 1]] + + clip_window = tf.constant([0, 0, 1, 1], dtype=tf.float32) + anchor_generator = ag.MultipleGridAnchorGenerator( + box_specs_list, base_anchor_size, clip_window=clip_window) + anchors = anchor_generator.generate(feature_map_shape_list=[(4, 4), (2, 2)]) + anchor_corners = anchors.get() + + with self.test_session(): + anchor_corners_out = anchor_corners.eval() + small_grid_corners = anchor_corners_out[48:, :] + self.assertAllClose(small_grid_corners, exp_small_grid_corners) + + def test_invalid_box_specs(self): + # not all box specs are pairs + box_specs_list = [[(1.0, 1.0), (2.0, 1.0), (1.0, 0.5)], + [(1.0, 1.0), (1.0, 0.5, .3)]] + with self.assertRaises(ValueError): + ag.MultipleGridAnchorGenerator(box_specs_list) + + # box_specs_list is not a list of lists + box_specs_list = [(1.0, 1.0), (2.0, 1.0), (1.0, 0.5)] + with self.assertRaises(ValueError): + ag.MultipleGridAnchorGenerator(box_specs_list) + + def test_invalid_generate_arguments(self): + base_anchor_size = tf.constant([1.0, 1.0], dtype=tf.float32) + box_specs_list = [[(1.0, 1.0), (2.0, 1.0), (1.0, 0.5)], + [(1.0, 1.0), (1.0, 0.5)]] + anchor_generator = ag.MultipleGridAnchorGenerator( + box_specs_list, base_anchor_size) + + # incompatible lengths with box_specs_list + with self.assertRaises(ValueError): + anchor_generator.generate(feature_map_shape_list=[(4, 4), (2, 2)], + anchor_strides=[(.25, .25)], + anchor_offsets=[(.125, .125), (.25, .25)]) + with self.assertRaises(ValueError): + anchor_generator.generate(feature_map_shape_list=[(4, 4), (2, 2), (1, 1)], + anchor_strides=[(.25, .25), (.5, .5)], + anchor_offsets=[(.125, .125), (.25, .25)]) + with self.assertRaises(ValueError): + anchor_generator.generate(feature_map_shape_list=[(4, 4), (2, 2)], + anchor_strides=[(.5, .5)], + anchor_offsets=[(.25, .25)]) + + # not pairs + with self.assertRaises(ValueError): + anchor_generator.generate(feature_map_shape_list=[(4, 4, 4), (2, 2)], + anchor_strides=[(.25, .25), (.5, .5)], + anchor_offsets=[(.125, .125), (.25, .25)]) + with self.assertRaises(ValueError): + anchor_generator.generate(feature_map_shape_list=[(4, 4), (2, 2)], + anchor_strides=[(.25, .25, .1), (.5, .5)], + anchor_offsets=[(.125, .125), + (.25, .25)]) + with self.assertRaises(ValueError): + anchor_generator.generate(feature_map_shape_list=[(4), (2, 2)], + anchor_strides=[(.25, .25), (.5, .5)], + anchor_offsets=[(.125), (.25)]) + + +class CreateSSDAnchorsTest(tf.test.TestCase): + + def test_create_ssd_anchors_returns_correct_shape(self): + anchor_generator = ag.create_ssd_anchors( + num_layers=6, min_scale=0.2, max_scale=0.95, + aspect_ratios=(1.0, 2.0, 3.0, 1.0/2, 1.0/3), + reduce_boxes_in_lowest_layer=True) + + feature_map_shape_list = [(38, 38), (19, 19), (10, 10), + (5, 5), (3, 3), (1, 1)] + anchors = anchor_generator.generate( + feature_map_shape_list=feature_map_shape_list) + anchor_corners = anchors.get() + with self.test_session(): + anchor_corners_out = anchor_corners.eval() + self.assertEquals(anchor_corners_out.shape, (7308, 4)) + + anchor_generator = ag.create_ssd_anchors( + num_layers=6, min_scale=0.2, max_scale=0.95, + aspect_ratios=(1.0, 2.0, 3.0, 1.0/2, 1.0/3), + reduce_boxes_in_lowest_layer=False) + + feature_map_shape_list = [(38, 38), (19, 19), (10, 10), + (5, 5), (3, 3), (1, 1)] + anchors = anchor_generator.generate( + feature_map_shape_list=feature_map_shape_list) + anchor_corners = anchors.get() + with self.test_session(): + anchor_corners_out = anchor_corners.eval() + self.assertEquals(anchor_corners_out.shape, (11640, 4)) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/box_coders/BUILD b/object_detection/box_coders/BUILD new file mode 100644 index 000000000..ecb3cc7aa --- /dev/null +++ b/object_detection/box_coders/BUILD @@ -0,0 +1,102 @@ +# Tensorflow Object Detection API: Box Coder implementations. + +package( + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) + +# Apache 2.0 +py_library( + name = "faster_rcnn_box_coder", + srcs = [ + "faster_rcnn_box_coder.py", + ], + deps = [ + "//tensorflow_models/object_detection/core:box_coder", + "//tensorflow_models/object_detection/core:box_list", + ], +) + +py_test( + name = "faster_rcnn_box_coder_test", + srcs = [ + "faster_rcnn_box_coder_test.py", + ], + deps = [ + ":faster_rcnn_box_coder", + "//tensorflow", + "//tensorflow_models/object_detection/core:box_list", + ], +) + +py_library( + name = "keypoint_box_coder", + srcs = [ + "keypoint_box_coder.py", + ], + deps = [ + "//tensorflow_models/object_detection/core:box_coder", + "//tensorflow_models/object_detection/core:box_list", + "//tensorflow_models/object_detection/core:standard_fields", + ], +) + +py_test( + name = "keypoint_box_coder_test", + srcs = [ + "keypoint_box_coder_test.py", + ], + deps = [ + ":keypoint_box_coder", + "//tensorflow", + "//tensorflow_models/object_detection/core:box_list", + "//tensorflow_models/object_detection/core:standard_fields", + ], +) + +py_library( + name = "mean_stddev_box_coder", + srcs = [ + "mean_stddev_box_coder.py", + ], + deps = [ + "//tensorflow_models/object_detection/core:box_coder", + "//tensorflow_models/object_detection/core:box_list", + ], +) + +py_test( + name = "mean_stddev_box_coder_test", + srcs = [ + "mean_stddev_box_coder_test.py", + ], + deps = [ + ":mean_stddev_box_coder", + "//tensorflow", + "//tensorflow_models/object_detection/core:box_list", + ], +) + +py_library( + name = "square_box_coder", + srcs = [ + "square_box_coder.py", + ], + deps = [ + "//tensorflow_models/object_detection/core:box_coder", + "//tensorflow_models/object_detection/core:box_list", + ], +) + +py_test( + name = "square_box_coder_test", + srcs = [ + "square_box_coder_test.py", + ], + deps = [ + ":square_box_coder", + "//tensorflow", + "//tensorflow_models/object_detection/core:box_list", + ], +) diff --git a/object_detection/box_coders/__init__.py b/object_detection/box_coders/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/object_detection/box_coders/faster_rcnn_box_coder.py b/object_detection/box_coders/faster_rcnn_box_coder.py new file mode 100644 index 000000000..af25e21a1 --- /dev/null +++ b/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 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/object_detection/box_coders/faster_rcnn_box_coder_test.py b/object_detection/box_coders/faster_rcnn_box_coder_test.py new file mode 100644 index 000000000..b2135f06e --- /dev/null +++ b/object_detection/box_coders/faster_rcnn_box_coder_test.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. +# ============================================================================== + +"""Tests for object_detection.box_coder.faster_rcnn_box_coder.""" + +import tensorflow as tf + +from object_detection.box_coders import faster_rcnn_box_coder +from object_detection.core import box_list + + +class FasterRcnnBoxCoderTest(tf.test.TestCase): + + def test_get_correct_relative_codes_after_encoding(self): + boxes = [[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]] + anchors = [[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]] + expected_rel_codes = [[-0.5, -0.416666, -0.405465, -0.182321], + [-0.083333, -0.222222, -0.693147, -1.098612]] + boxes = box_list.BoxList(tf.constant(boxes)) + anchors = box_list.BoxList(tf.constant(anchors)) + coder = faster_rcnn_box_coder.FasterRcnnBoxCoder() + rel_codes = coder.encode(boxes, anchors) + with self.test_session() as sess: + rel_codes_out, = sess.run([rel_codes]) + self.assertAllClose(rel_codes_out, expected_rel_codes) + + def test_get_correct_relative_codes_after_encoding_with_scaling(self): + boxes = [[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]] + anchors = [[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]] + scale_factors = [2, 3, 4, 5] + expected_rel_codes = [[-1., -1.25, -1.62186, -0.911608], + [-0.166667, -0.666667, -2.772588, -5.493062]] + boxes = box_list.BoxList(tf.constant(boxes)) + anchors = box_list.BoxList(tf.constant(anchors)) + coder = faster_rcnn_box_coder.FasterRcnnBoxCoder( + scale_factors=scale_factors) + rel_codes = coder.encode(boxes, anchors) + with self.test_session() as sess: + rel_codes_out, = sess.run([rel_codes]) + self.assertAllClose(rel_codes_out, expected_rel_codes) + + def test_get_correct_boxes_after_decoding(self): + anchors = [[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]] + rel_codes = [[-0.5, -0.416666, -0.405465, -0.182321], + [-0.083333, -0.222222, -0.693147, -1.098612]] + expected_boxes = [[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]] + anchors = box_list.BoxList(tf.constant(anchors)) + coder = faster_rcnn_box_coder.FasterRcnnBoxCoder() + boxes = coder.decode(rel_codes, anchors) + with self.test_session() as sess: + boxes_out, = sess.run([boxes.get()]) + self.assertAllClose(boxes_out, expected_boxes) + + def test_get_correct_boxes_after_decoding_with_scaling(self): + anchors = [[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]] + rel_codes = [[-1., -1.25, -1.62186, -0.911608], + [-0.166667, -0.666667, -2.772588, -5.493062]] + scale_factors = [2, 3, 4, 5] + expected_boxes = [[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]] + anchors = box_list.BoxList(tf.constant(anchors)) + coder = faster_rcnn_box_coder.FasterRcnnBoxCoder( + scale_factors=scale_factors) + boxes = coder.decode(rel_codes, anchors) + with self.test_session() as sess: + boxes_out, = sess.run([boxes.get()]) + self.assertAllClose(boxes_out, expected_boxes) + + def test_very_small_Width_nan_after_encoding(self): + boxes = [[10.0, 10.0, 10.0000001, 20.0]] + anchors = [[15.0, 12.0, 30.0, 18.0]] + expected_rel_codes = [[-0.833333, 0., -21.128731, 0.510826]] + boxes = box_list.BoxList(tf.constant(boxes)) + anchors = box_list.BoxList(tf.constant(anchors)) + coder = faster_rcnn_box_coder.FasterRcnnBoxCoder() + rel_codes = coder.encode(boxes, anchors) + with self.test_session() as sess: + rel_codes_out, = sess.run([rel_codes]) + self.assertAllClose(rel_codes_out, expected_rel_codes) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/box_coders/keypoint_box_coder.py b/object_detection/box_coders/keypoint_box_coder.py new file mode 100644 index 000000000..34ed1af23 --- /dev/null +++ b/object_detection/box_coders/keypoint_box_coder.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. +# ============================================================================== + +"""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) / ha + tky1 = (ky1 - ya) / ha + tkx1 = (kx1 - xa) / ha + ... + 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 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.to_float(scale_factors[0]), tf.to_float(scale_factors[1])], + [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/object_detection/box_coders/keypoint_box_coder_test.py b/object_detection/box_coders/keypoint_box_coder_test.py new file mode 100644 index 000000000..330641e58 --- /dev/null +++ b/object_detection/box_coders/keypoint_box_coder_test.py @@ -0,0 +1,140 @@ +# 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 tensorflow 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 + + +class KeypointBoxCoderTest(tf.test.TestCase): + + def test_get_correct_relative_codes_after_encoding(self): + boxes = [[10., 10., 20., 15.], + [0.2, 0.1, 0.5, 0.4]] + keypoints = [[[15., 12.], [10., 15.]], + [[0.5, 0.3], [0.2, 0.4]]] + num_keypoints = len(keypoints[0]) + anchors = [[15., 12., 30., 18.], + [0.1, 0.0, 0.7, 0.9]] + 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] + ] + boxes = box_list.BoxList(tf.constant(boxes)) + boxes.add_field(fields.BoxListFields.keypoints, tf.constant(keypoints)) + anchors = box_list.BoxList(tf.constant(anchors)) + coder = keypoint_box_coder.KeypointBoxCoder(num_keypoints) + rel_codes = coder.encode(boxes, anchors) + with self.test_session() as sess: + rel_codes_out, = sess.run([rel_codes]) + self.assertAllClose(rel_codes_out, expected_rel_codes) + + def test_get_correct_relative_codes_after_encoding_with_scaling(self): + boxes = [[10., 10., 20., 15.], + [0.2, 0.1, 0.5, 0.4]] + keypoints = [[[15., 12.], [10., 15.]], + [[0.5, 0.3], [0.2, 0.4]]] + num_keypoints = len(keypoints[0]) + anchors = [[15., 12., 30., 18.], + [0.1, 0.0, 0.7, 0.9]] + scale_factors = [2, 3, 4, 5] + 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] + ] + boxes = box_list.BoxList(tf.constant(boxes)) + boxes.add_field(fields.BoxListFields.keypoints, tf.constant(keypoints)) + anchors = box_list.BoxList(tf.constant(anchors)) + coder = keypoint_box_coder.KeypointBoxCoder( + num_keypoints, scale_factors=scale_factors) + rel_codes = coder.encode(boxes, anchors) + with self.test_session() as sess: + rel_codes_out, = sess.run([rel_codes]) + self.assertAllClose(rel_codes_out, expected_rel_codes) + + def test_get_correct_boxes_after_decoding(self): + anchors = [[15., 12., 30., 18.], + [0.1, 0.0, 0.7, 0.9]] + 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] + ] + 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]) + anchors = box_list.BoxList(tf.constant(anchors)) + coder = keypoint_box_coder.KeypointBoxCoder(num_keypoints) + boxes = coder.decode(rel_codes, anchors) + with self.test_session() as sess: + boxes_out, keypoints_out = sess.run( + [boxes.get(), boxes.get_field(fields.BoxListFields.keypoints)]) + self.assertAllClose(boxes_out, expected_boxes) + self.assertAllClose(keypoints_out, expected_keypoints) + + def test_get_correct_boxes_after_decoding_with_scaling(self): + anchors = [[15., 12., 30., 18.], + [0.1, 0.0, 0.7, 0.9]] + 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] + ] + scale_factors = [2, 3, 4, 5] + 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]) + anchors = box_list.BoxList(tf.constant(anchors)) + coder = keypoint_box_coder.KeypointBoxCoder( + num_keypoints, scale_factors=scale_factors) + boxes = coder.decode(rel_codes, anchors) + with self.test_session() as sess: + boxes_out, keypoints_out = sess.run( + [boxes.get(), boxes.get_field(fields.BoxListFields.keypoints)]) + self.assertAllClose(boxes_out, expected_boxes) + self.assertAllClose(keypoints_out, expected_keypoints) + + def test_very_small_width_nan_after_encoding(self): + boxes = [[10., 10., 10.0000001, 20.]] + keypoints = [[[10., 10.], [10.0000001, 20.]]] + anchors = [[15., 12., 30., 18.]] + expected_rel_codes = [[-0.833333, 0., -21.128731, 0.510826, + -0.833333, -0.833333, -0.833333, 0.833333]] + boxes = box_list.BoxList(tf.constant(boxes)) + boxes.add_field(fields.BoxListFields.keypoints, tf.constant(keypoints)) + anchors = box_list.BoxList(tf.constant(anchors)) + coder = keypoint_box_coder.KeypointBoxCoder(2) + rel_codes = coder.encode(boxes, anchors) + with self.test_session() as sess: + rel_codes_out, = sess.run([rel_codes]) + self.assertAllClose(rel_codes_out, expected_rel_codes) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/box_coders/mean_stddev_box_coder.py b/object_detection/box_coders/mean_stddev_box_coder.py new file mode 100644 index 000000000..726b4a61c --- /dev/null +++ b/object_detection/box_coders/mean_stddev_box_coder.py @@ -0,0 +1,70 @@ +# 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.""" + + @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. We assume that anchors has an associated + stddev field. + + Returns: + a tensor representing N anchor-encoded boxes + Raises: + ValueError: if the anchors BoxList does not have a stddev field + """ + if not anchors.has_field('stddev'): + raise ValueError('anchors must have a stddev field') + box_corners = boxes.get() + means = anchors.get() + stddev = anchors.get_field('stddev') + return (box_corners - means) / stddev + + def _decode(self, rel_codes, anchors): + """Decode. + + Args: + rel_codes: a tensor representing N anchor-encoded boxes. + anchors: BoxList of anchors. We assume that anchors has an associated + stddev field. + + Returns: + boxes: BoxList holding N bounding boxes + Raises: + ValueError: if the anchors BoxList does not have a stddev field + """ + if not anchors.has_field('stddev'): + raise ValueError('anchors must have a stddev field') + means = anchors.get() + stddevs = anchors.get_field('stddev') + box_corners = rel_codes * stddevs + means + return box_list.BoxList(box_corners) diff --git a/object_detection/box_coders/mean_stddev_box_coder_test.py b/object_detection/box_coders/mean_stddev_box_coder_test.py new file mode 100644 index 000000000..0d3a89528 --- /dev/null +++ b/object_detection/box_coders/mean_stddev_box_coder_test.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. +# ============================================================================== + +"""Tests for object_detection.box_coder.mean_stddev_boxcoder.""" + +import tensorflow as tf + +from object_detection.box_coders import mean_stddev_box_coder +from object_detection.core import box_list + + +class MeanStddevBoxCoderTest(tf.test.TestCase): + + def testGetCorrectRelativeCodesAfterEncoding(self): + box_corners = [[0.0, 0.0, 0.5, 0.5], [0.0, 0.0, 0.5, 0.5]] + boxes = box_list.BoxList(tf.constant(box_corners)) + expected_rel_codes = [[0.0, 0.0, 0.0, 0.0], [-5.0, -5.0, -5.0, -3.0]] + prior_means = tf.constant([[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 1.0, 0.8]]) + prior_stddevs = tf.constant(2 * [4 * [.1]]) + priors = box_list.BoxList(prior_means) + priors.add_field('stddev', prior_stddevs) + + coder = mean_stddev_box_coder.MeanStddevBoxCoder() + rel_codes = coder.encode(boxes, priors) + with self.test_session() as sess: + rel_codes_out = sess.run(rel_codes) + self.assertAllClose(rel_codes_out, expected_rel_codes) + + def testGetCorrectBoxesAfterDecoding(self): + rel_codes = tf.constant([[0.0, 0.0, 0.0, 0.0], [-5.0, -5.0, -5.0, -3.0]]) + expected_box_corners = [[0.0, 0.0, 0.5, 0.5], [0.0, 0.0, 0.5, 0.5]] + prior_means = tf.constant([[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 1.0, 0.8]]) + prior_stddevs = tf.constant(2 * [4 * [.1]]) + priors = box_list.BoxList(prior_means) + priors.add_field('stddev', prior_stddevs) + + coder = mean_stddev_box_coder.MeanStddevBoxCoder() + decoded_boxes = coder.decode(rel_codes, priors) + decoded_box_corners = decoded_boxes.get() + with self.test_session() as sess: + decoded_out = sess.run(decoded_box_corners) + self.assertAllClose(decoded_out, expected_box_corners) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/box_coders/square_box_coder.py b/object_detection/box_coders/square_box_coder.py new file mode 100644 index 000000000..ee46b6895 --- /dev/null +++ b/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 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/object_detection/box_coders/square_box_coder_test.py b/object_detection/box_coders/square_box_coder_test.py new file mode 100644 index 000000000..7f739c6b4 --- /dev/null +++ b/object_detection/box_coders/square_box_coder_test.py @@ -0,0 +1,97 @@ +# 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 tensorflow as tf + +from object_detection.box_coders import square_box_coder +from object_detection.core import box_list + + +class SquareBoxCoderTest(tf.test.TestCase): + + def test_correct_relative_codes_with_default_scale(self): + boxes = [[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]] + anchors = [[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]] + scale_factors = None + expected_rel_codes = [[-0.790569, -0.263523, -0.293893], + [-0.068041, -0.272166, -0.89588]] + + boxes = box_list.BoxList(tf.constant(boxes)) + anchors = box_list.BoxList(tf.constant(anchors)) + coder = square_box_coder.SquareBoxCoder(scale_factors=scale_factors) + rel_codes = coder.encode(boxes, anchors) + with self.test_session() as sess: + (rel_codes_out,) = sess.run([rel_codes]) + self.assertAllClose(rel_codes_out, expected_rel_codes) + + def test_correct_relative_codes_with_non_default_scale(self): + boxes = [[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]] + anchors = [[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]] + scale_factors = [2, 3, 4] + expected_rel_codes = [[-1.581139, -0.790569, -1.175573], + [-0.136083, -0.816497, -3.583519]] + boxes = box_list.BoxList(tf.constant(boxes)) + anchors = box_list.BoxList(tf.constant(anchors)) + coder = square_box_coder.SquareBoxCoder(scale_factors=scale_factors) + rel_codes = coder.encode(boxes, anchors) + with self.test_session() as sess: + (rel_codes_out,) = sess.run([rel_codes]) + self.assertAllClose(rel_codes_out, expected_rel_codes) + + def test_correct_relative_codes_with_small_width(self): + boxes = [[10.0, 10.0, 10.0000001, 20.0]] + anchors = [[15.0, 12.0, 30.0, 18.0]] + scale_factors = None + expected_rel_codes = [[-1.317616, 0., -20.670586]] + boxes = box_list.BoxList(tf.constant(boxes)) + anchors = box_list.BoxList(tf.constant(anchors)) + coder = square_box_coder.SquareBoxCoder(scale_factors=scale_factors) + rel_codes = coder.encode(boxes, anchors) + with self.test_session() as sess: + (rel_codes_out,) = sess.run([rel_codes]) + self.assertAllClose(rel_codes_out, expected_rel_codes) + + def test_correct_boxes_with_default_scale(self): + anchors = [[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]] + rel_codes = [[-0.5, -0.416666, -0.405465], + [-0.083333, -0.222222, -0.693147]] + scale_factors = None + expected_boxes = [[14.594306, 7.884875, 20.918861, 14.209432], + [0.155051, 0.102989, 0.522474, 0.470412]] + anchors = box_list.BoxList(tf.constant(anchors)) + coder = square_box_coder.SquareBoxCoder(scale_factors=scale_factors) + boxes = coder.decode(rel_codes, anchors) + with self.test_session() as sess: + (boxes_out,) = sess.run([boxes.get()]) + self.assertAllClose(boxes_out, expected_boxes) + + def test_correct_boxes_with_non_default_scale(self): + anchors = [[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]] + rel_codes = [[-1., -1.25, -1.62186], [-0.166667, -0.666667, -2.772588]] + scale_factors = [2, 3, 4] + expected_boxes = [[14.594306, 7.884875, 20.918861, 14.209432], + [0.155051, 0.102989, 0.522474, 0.470412]] + anchors = box_list.BoxList(tf.constant(anchors)) + coder = square_box_coder.SquareBoxCoder(scale_factors=scale_factors) + boxes = coder.decode(rel_codes, anchors) + with self.test_session() as sess: + (boxes_out,) = sess.run([boxes.get()]) + self.assertAllClose(boxes_out, expected_boxes) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/builders/BUILD b/object_detection/builders/BUILD new file mode 100644 index 000000000..bb40de5b5 --- /dev/null +++ b/object_detection/builders/BUILD @@ -0,0 +1,296 @@ +# Tensorflow Object Detection API: component builders. + +package( + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) + +# Apache 2.0 +py_library( + name = "model_builder", + srcs = ["model_builder.py"], + deps = [ + ":anchor_generator_builder", + ":box_coder_builder", + ":box_predictor_builder", + ":hyperparams_builder", + ":image_resizer_builder", + ":losses_builder", + ":matcher_builder", + ":post_processing_builder", + ":region_similarity_calculator_builder", + "//tensorflow_models/object_detection/core:box_predictor", + "//tensorflow_models/object_detection/meta_architectures:faster_rcnn_meta_arch", + "//tensorflow_models/object_detection/meta_architectures:rfcn_meta_arch", + "//tensorflow_models/object_detection/meta_architectures:ssd_meta_arch", + "//tensorflow_models/object_detection/models:faster_rcnn_inception_resnet_v2_feature_extractor", + "//tensorflow_models/object_detection/models:faster_rcnn_resnet_v1_feature_extractor", + "//tensorflow_models/object_detection/models:ssd_inception_v2_feature_extractor", + "//tensorflow_models/object_detection/models:ssd_mobilenet_v1_feature_extractor", + "//tensorflow_models/object_detection/protos:model_py_pb2", + ], +) + +py_test( + name = "model_builder_test", + srcs = ["model_builder_test.py"], + deps = [ + ":model_builder", + "//tensorflow", + "//tensorflow_models/object_detection/meta_architectures:faster_rcnn_meta_arch", + "//tensorflow_models/object_detection/meta_architectures:ssd_meta_arch", + "//tensorflow_models/object_detection/models:ssd_inception_v2_feature_extractor", + "//tensorflow_models/object_detection/models:ssd_mobilenet_v1_feature_extractor", + "//tensorflow_models/object_detection/protos:model_py_pb2", + ], +) + +py_library( + name = "matcher_builder", + srcs = ["matcher_builder.py"], + deps = [ + "//tensorflow_models/object_detection/matchers:argmax_matcher", + "//tensorflow_models/object_detection/matchers:bipartite_matcher", + "//tensorflow_models/object_detection/protos:matcher_py_pb2", + ], +) + +py_test( + name = "matcher_builder_test", + srcs = ["matcher_builder_test.py"], + deps = [ + ":matcher_builder", + "//tensorflow_models/object_detection/matchers:argmax_matcher", + "//tensorflow_models/object_detection/matchers:bipartite_matcher", + "//tensorflow_models/object_detection/protos:matcher_py_pb2", + ], +) + +py_library( + name = "box_coder_builder", + srcs = ["box_coder_builder.py"], + deps = [ + "//tensorflow_models/object_detection/box_coders:faster_rcnn_box_coder", + "//tensorflow_models/object_detection/box_coders:mean_stddev_box_coder", + "//tensorflow_models/object_detection/box_coders:square_box_coder", + "//tensorflow_models/object_detection/protos:box_coder_py_pb2", + ], +) + +py_test( + name = "box_coder_builder_test", + srcs = ["box_coder_builder_test.py"], + deps = [ + ":box_coder_builder", + "//tensorflow", + "//tensorflow_models/object_detection/box_coders:faster_rcnn_box_coder", + "//tensorflow_models/object_detection/box_coders:mean_stddev_box_coder", + "//tensorflow_models/object_detection/box_coders:square_box_coder", + "//tensorflow_models/object_detection/protos:box_coder_py_pb2", + ], +) + +py_library( + name = "anchor_generator_builder", + srcs = ["anchor_generator_builder.py"], + deps = [ + "//tensorflow_models/object_detection/anchor_generators:grid_anchor_generator", + "//tensorflow_models/object_detection/anchor_generators:multiple_grid_anchor_generator", + "//tensorflow_models/object_detection/protos:anchor_generator_py_pb2", + ], +) + +py_test( + name = "anchor_generator_builder_test", + srcs = ["anchor_generator_builder_test.py"], + deps = [ + ":anchor_generator_builder", + "//tensorflow", + "//tensorflow_models/object_detection/anchor_generators:grid_anchor_generator", + "//tensorflow_models/object_detection/anchor_generators:multiple_grid_anchor_generator", + "//tensorflow_models/object_detection/protos:anchor_generator_py_pb2", + ], +) + +py_library( + name = "input_reader_builder", + srcs = ["input_reader_builder.py"], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/data_decoders:tf_example_decoder", + "//tensorflow_models/object_detection/protos:input_reader_py_pb2", + ], +) + +py_test( + name = "input_reader_builder_test", + srcs = [ + "input_reader_builder_test.py", + ], + deps = [ + ":input_reader_builder", + "//tensorflow", + "//tensorflow_models/object_detection/core:standard_fields", + "//tensorflow_models/object_detection/protos:input_reader_py_pb2", + ], +) + +py_library( + name = "losses_builder", + srcs = ["losses_builder.py"], + deps = [ + "//tensorflow_models/object_detection/core:losses", + "//tensorflow_models/object_detection/protos:losses_py_pb2", + ], +) + +py_test( + name = "losses_builder_test", + srcs = ["losses_builder_test.py"], + deps = [ + ":losses_builder", + "//tensorflow_models/object_detection/core:losses", + "//tensorflow_models/object_detection/protos:losses_py_pb2", + ], +) + +py_library( + name = "optimizer_builder", + srcs = ["optimizer_builder.py"], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/utils:learning_schedules", + ], +) + +py_test( + name = "optimizer_builder_test", + srcs = ["optimizer_builder_test.py"], + deps = [ + ":optimizer_builder", + "//tensorflow", + "//tensorflow_models/object_detection/protos:optimizer_py_pb2", + ], +) + +py_library( + name = "post_processing_builder", + srcs = ["post_processing_builder.py"], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/core:post_processing", + "//tensorflow_models/object_detection/protos:post_processing_py_pb2", + ], +) + +py_test( + name = "post_processing_builder_test", + srcs = ["post_processing_builder_test.py"], + deps = [ + ":post_processing_builder", + "//tensorflow", + "//tensorflow_models/object_detection/protos:post_processing_py_pb2", + ], +) + +py_library( + name = "hyperparams_builder", + srcs = ["hyperparams_builder.py"], + deps = [ + "//tensorflow_models/object_detection/protos:hyperparams_py_pb2", + ], +) + +py_test( + name = "hyperparams_builder_test", + srcs = ["hyperparams_builder_test.py"], + deps = [ + ":hyperparams_builder", + "//tensorflow", + "//tensorflow_models/object_detection/protos:hyperparams_py_pb2", + ], +) + +py_library( + name = "box_predictor_builder", + srcs = ["box_predictor_builder.py"], + deps = [ + ":hyperparams_builder", + "//tensorflow_models/object_detection/core:box_predictor", + "//tensorflow_models/object_detection/protos:box_predictor_py_pb2", + ], +) + +py_test( + name = "box_predictor_builder_test", + srcs = ["box_predictor_builder_test.py"], + deps = [ + ":box_predictor_builder", + ":hyperparams_builder", + "//tensorflow", + "//tensorflow_models/object_detection/protos:box_predictor_py_pb2", + "//tensorflow_models/object_detection/protos:hyperparams_py_pb2", + ], +) + +py_library( + name = "region_similarity_calculator_builder", + srcs = ["region_similarity_calculator_builder.py"], + deps = [ + "//tensorflow_models/object_detection/core:region_similarity_calculator", + "//tensorflow_models/object_detection/protos:region_similarity_calculator_py_pb2", + ], +) + +py_test( + name = "region_similarity_calculator_builder_test", + srcs = ["region_similarity_calculator_builder_test.py"], + deps = [ + ":region_similarity_calculator_builder", + "//tensorflow", + ], +) + +py_library( + name = "preprocessor_builder", + srcs = ["preprocessor_builder.py"], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/core:preprocessor", + "//tensorflow_models/object_detection/protos:preprocessor_py_pb2", + ], +) + +py_test( + name = "preprocessor_builder_test", + srcs = [ + "preprocessor_builder_test.py", + ], + deps = [ + ":preprocessor_builder", + "//tensorflow", + "//tensorflow_models/object_detection/core:preprocessor", + "//tensorflow_models/object_detection/protos:preprocessor_py_pb2", + ], +) + +py_library( + name = "image_resizer_builder", + srcs = ["image_resizer_builder.py"], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/core:preprocessor", + "//tensorflow_models/object_detection/protos:image_resizer_py_pb2", + ], +) + +py_test( + name = "image_resizer_builder_test", + srcs = ["image_resizer_builder_test.py"], + deps = [ + ":image_resizer_builder", + "//tensorflow", + "//tensorflow_models/object_detection/protos:image_resizer_py_pb2", + ], +) diff --git a/object_detection/builders/__init__.py b/object_detection/builders/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/object_detection/builders/anchor_generator_builder.py b/object_detection/builders/anchor_generator_builder.py new file mode 100644 index 000000000..7b08deddb --- /dev/null +++ b/object_detection/builders/anchor_generator_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 anchor generator from config.""" + +from object_detection.anchor_generators import grid_anchor_generator +from object_detection.anchor_generators import multiple_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 + 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, + aspect_ratios=ssd_anchor_generator_config.aspect_ratios, + reduce_boxes_in_lowest_layer=(ssd_anchor_generator_config + .reduce_boxes_in_lowest_layer)) + else: + raise ValueError('Empty anchor generator.') + diff --git a/object_detection/builders/anchor_generator_builder_test.py b/object_detection/builders/anchor_generator_builder_test.py new file mode 100644 index 000000000..657be18ef --- /dev/null +++ b/object_detection/builders/anchor_generator_builder_test.py @@ -0,0 +1,194 @@ +# 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.""" + +import tensorflow as tf + +from google.protobuf import text_format +from object_detection.anchor_generators import grid_anchor_generator +from object_detection.anchor_generators import multiple_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.assertTrue(isinstance(anchor_generator_object, + grid_anchor_generator.GridAnchorGenerator)) + self.assertListEqual(anchor_generator_object._scales, []) + self.assertListEqual(anchor_generator_object._aspect_ratios, []) + with self.test_session() as sess: + base_anchor_size, anchor_offset, anchor_stride = sess.run( + [anchor_generator_object._base_anchor_size, + anchor_generator_object._anchor_offset, + anchor_generator_object._anchor_stride]) + self.assertAllEqual(anchor_offset, [0, 0]) + self.assertAllEqual(anchor_stride, [16, 16]) + self.assertAllEqual(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.assertTrue(isinstance(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]) + with self.test_session() as sess: + base_anchor_size, anchor_offset, anchor_stride = sess.run( + [anchor_generator_object._base_anchor_size, + anchor_generator_object._anchor_offset, + anchor_generator_object._anchor_stride]) + self.assertAllEqual(anchor_offset, [30, 40]) + self.assertAllEqual(anchor_stride, [10, 20]) + self.assertAllEqual(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.assertTrue(isinstance(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) + + with self.test_session() as sess: + base_anchor_size = sess.run(anchor_generator_object._base_anchor_size) + self.assertAllClose(base_anchor_size, [1.0, 1.0]) + + def test_build_ssd_anchor_generator_withoud_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.assertTrue(isinstance(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) + + with self.test_session() as sess: + base_anchor_size = sess.run(anchor_generator_object._base_anchor_size) + self.assertAllClose(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] + } + """ + 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.assertTrue(isinstance(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,)]): + 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,)]): + self.assert_almost_list_equal(expected_aspect_ratio, actual_aspect_ratio) + + with self.test_session() as sess: + base_anchor_size = sess.run(anchor_generator_object._base_anchor_size) + self.assertAllClose(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) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/builders/box_coder_builder.py b/object_detection/builders/box_coder_builder.py new file mode 100644 index 000000000..ff7ac01fe --- /dev/null +++ b/object_detection/builders/box_coder_builder.py @@ -0,0 +1,55 @@ +# 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 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') == + 'mean_stddev_box_coder'): + return mean_stddev_box_coder.MeanStddevBoxCoder() + 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/object_detection/builders/box_coder_builder_test.py b/object_detection/builders/box_coder_builder_test.py new file mode 100644 index 000000000..b5adcad51 --- /dev/null +++ b/object_detection/builders/box_coder_builder_test.py @@ -0,0 +1,107 @@ +# 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 as tf + +from google.protobuf import text_format +from object_detection.box_coders import faster_rcnn_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.assertTrue(isinstance(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.assertTrue(isinstance(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_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/object_detection/builders/box_predictor_builder.py b/object_detection/builders/box_predictor_builder.py new file mode 100644 index 000000000..4f7c5045e --- /dev/null +++ b/object_detection/builders/box_predictor_builder.py @@ -0,0 +1,106 @@ +# 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.""" + +from object_detection.core import box_predictor +from object_detection.protos import box_predictor_pb2 + + +def build(argscope_fn, box_predictor_config, is_training, num_classes): + """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. + + 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': + conv_box_predictor = box_predictor_config.convolutional_box_predictor + conv_hyperparams = argscope_fn(conv_box_predictor.conv_hyperparams, + is_training) + box_predictor_object = box_predictor.ConvolutionalBoxPredictor( + is_training=is_training, + num_classes=num_classes, + conv_hyperparams=conv_hyperparams, + min_depth=conv_box_predictor.min_depth, + max_depth=conv_box_predictor.max_depth, + num_layers_before_predictor=(conv_box_predictor. + num_layers_before_predictor), + use_dropout=conv_box_predictor.use_dropout, + dropout_keep_prob=conv_box_predictor.dropout_keep_probability, + kernel_size=conv_box_predictor.kernel_size, + box_code_size=conv_box_predictor.box_code_size, + apply_sigmoid_to_scores=conv_box_predictor.apply_sigmoid_to_scores) + return box_predictor_object + + if box_predictor_oneof == 'mask_rcnn_box_predictor': + mask_rcnn_box_predictor = box_predictor_config.mask_rcnn_box_predictor + fc_hyperparams = argscope_fn(mask_rcnn_box_predictor.fc_hyperparams, + is_training) + conv_hyperparams = None + if mask_rcnn_box_predictor.HasField('conv_hyperparams'): + conv_hyperparams = argscope_fn(mask_rcnn_box_predictor.conv_hyperparams, + is_training) + box_predictor_object = box_predictor.MaskRCNNBoxPredictor( + is_training=is_training, + num_classes=num_classes, + fc_hyperparams=fc_hyperparams, + use_dropout=mask_rcnn_box_predictor.use_dropout, + dropout_keep_prob=mask_rcnn_box_predictor.dropout_keep_probability, + box_code_size=mask_rcnn_box_predictor.box_code_size, + conv_hyperparams=conv_hyperparams, + predict_instance_masks=mask_rcnn_box_predictor.predict_instance_masks, + mask_prediction_conv_depth=(mask_rcnn_box_predictor. + mask_prediction_conv_depth), + predict_keypoints=mask_rcnn_box_predictor.predict_keypoints) + return box_predictor_object + + if box_predictor_oneof == 'rfcn_box_predictor': + rfcn_box_predictor = box_predictor_config.rfcn_box_predictor + conv_hyperparams = argscope_fn(rfcn_box_predictor.conv_hyperparams, + is_training) + box_predictor_object = box_predictor.RfcnBoxPredictor( + is_training=is_training, + num_classes=num_classes, + conv_hyperparams=conv_hyperparams, + crop_size=[rfcn_box_predictor.crop_height, + rfcn_box_predictor.crop_width], + num_spatial_bins=[rfcn_box_predictor.num_spatial_bins_height, + rfcn_box_predictor.num_spatial_bins_width], + depth=rfcn_box_predictor.depth, + box_code_size=rfcn_box_predictor.box_code_size) + return box_predictor_object + raise ValueError('Unknown box predictor: {}'.format(box_predictor_oneof)) diff --git a/object_detection/builders/box_predictor_builder_test.py b/object_detection/builders/box_predictor_builder_test.py new file mode 100644 index 000000000..3f6a574a2 --- /dev/null +++ b/object_detection/builders/box_predictor_builder_test.py @@ -0,0 +1,391 @@ +# 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 mock +import tensorflow 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.protos import box_predictor_pb2 +from object_detection.protos import hyperparams_pb2 + + +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 + 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 + } + """ + 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) + 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(box_predictor._use_dropout) + self.assertAlmostEqual(box_predictor._dropout_keep_prob, 0.4) + self.assertTrue(box_predictor._apply_sigmoid_to_scores) + 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 = """ + 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._min_depth, 0) + self.assertEqual(box_predictor._max_depth, 0) + self.assertEqual(box_predictor._num_layers_before_predictor, 0) + self.assertTrue(box_predictor._use_dropout) + self.assertAlmostEqual(box_predictor._dropout_keep_prob, 0.8) + self.assertFalse(box_predictor._apply_sigmoid_to_scores) + self.assertEqual(box_predictor.num_classes, 90) + self.assertTrue(box_predictor._is_training) + + +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._fc_hyperparams, '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 + } + """ + 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) + self.assertTrue(box_predictor._use_dropout) + self.assertAlmostEqual(box_predictor._dropout_keep_prob, 0.8) + self.assertEqual(box_predictor.num_classes, 90) + self.assertTrue(box_predictor._is_training) + self.assertEqual(box_predictor._box_code_size, 3) + + 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) + self.assertFalse(box_predictor._use_dropout) + self.assertAlmostEqual(box_predictor._dropout_keep_prob, 0.5) + self.assertEqual(box_predictor.num_classes, 90) + self.assertTrue(box_predictor._is_training) + self.assertEqual(box_predictor._box_code_size, 4) + self.assertFalse(box_predictor._predict_instance_masks) + self.assertFalse(box_predictor._predict_keypoints) + + 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 + 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) + self.assertFalse(box_predictor._use_dropout) + self.assertAlmostEqual(box_predictor._dropout_keep_prob, 0.5) + self.assertEqual(box_predictor.num_classes, 90) + self.assertTrue(box_predictor._is_training) + self.assertEqual(box_predictor._box_code_size, 4) + self.assertTrue(box_predictor._predict_instance_masks) + self.assertEqual(box_predictor._mask_prediction_conv_depth, 512) + self.assertFalse(box_predictor._predict_keypoints) + + +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 + 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/object_detection/builders/hyperparams_builder.py b/object_detection/builders/hyperparams_builder.py new file mode 100644 index 000000000..6fc62a944 --- /dev/null +++ b/object_detection/builders/hyperparams_builder.py @@ -0,0 +1,169 @@ +# 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 function to construct tf-slim arg_scope for convolution, fc ops.""" +import tensorflow as tf + +from object_detection.protos import hyperparams_pb2 + +slim = tf.contrib.slim + + +def build(hyperparams_config, is_training): + """Builds tf-slim arg_scope for convolution ops based on the config. + + Returns an arg_scope to use for convolution ops containing weights + initializer, weights regularizer, activation function, batch norm function + and batch norm parameters based on the configuration. + + Note that if the batch_norm parameteres are not specified in the config + (i.e. left to default) then batch norm is excluded from the arg_scope. + + The batch norm parameters are set for updates based on `is_training` argument + and conv_hyperparams_config.batch_norm.train parameter. During training, they + are updated only if batch_norm.train parameter is true. However, during eval, + no updates are made to the batch norm variables. In both cases, their current + values are used during forward pass. + + Args: + hyperparams_config: hyperparams.proto object containing + hyperparameters. + is_training: Whether the network is in training mode. + + Returns: + arg_scope: tf-slim arg_scope containing hyperparameters for ops. + + Raises: + ValueError: if hyperparams_config is not of type hyperparams.Hyperparams. + """ + if not isinstance(hyperparams_config, + hyperparams_pb2.Hyperparams): + raise ValueError('hyperparams_config not of type ' + 'hyperparams_pb.Hyperparams.') + + batch_norm = None + batch_norm_params = None + if hyperparams_config.HasField('batch_norm'): + batch_norm = slim.batch_norm + batch_norm_params = _build_batch_norm_params( + hyperparams_config.batch_norm, is_training) + + affected_ops = [slim.conv2d, slim.separable_conv2d, slim.conv2d_transpose] + if hyperparams_config.HasField('op') and ( + hyperparams_config.op == hyperparams_pb2.Hyperparams.FC): + affected_ops = [slim.fully_connected] + with slim.arg_scope( + affected_ops, + weights_regularizer=_build_regularizer( + hyperparams_config.regularizer), + weights_initializer=_build_initializer( + hyperparams_config.initializer), + activation_fn=_build_activation_fn(hyperparams_config.activation), + normalizer_fn=batch_norm, + normalizer_params=batch_norm_params) as sc: + return sc + + +def _build_activation_fn(activation_fn): + """Builds a callable activation from config. + + Args: + activation_fn: hyperparams_pb2.Hyperparams.activation + + Returns: + Callable activation function. + + Raises: + ValueError: On unknown activation function. + """ + if activation_fn == hyperparams_pb2.Hyperparams.NONE: + return None + if activation_fn == hyperparams_pb2.Hyperparams.RELU: + return tf.nn.relu + if activation_fn == hyperparams_pb2.Hyperparams.RELU_6: + return tf.nn.relu6 + raise ValueError('Unknown activation function: {}'.format(activation_fn)) + + +def _build_regularizer(regularizer): + """Builds a tf-slim regularizer from config. + + Args: + regularizer: hyperparams_pb2.Hyperparams.regularizer proto. + + Returns: + tf-slim regularizer. + + Raises: + ValueError: On unknown regularizer. + """ + regularizer_oneof = regularizer.WhichOneof('regularizer_oneof') + if regularizer_oneof == 'l1_regularizer': + return slim.l1_regularizer(scale=regularizer.l1_regularizer.weight) + if regularizer_oneof == 'l2_regularizer': + return slim.l2_regularizer(scale=regularizer.l2_regularizer.weight) + raise ValueError('Unknown regularizer function: {}'.format(regularizer_oneof)) + + +def _build_initializer(initializer): + """Build a tf initializer from config. + + Args: + initializer: hyperparams_pb2.Hyperparams.regularizer proto. + + Returns: + tf initializer. + + Raises: + ValueError: On unknown initializer. + """ + initializer_oneof = initializer.WhichOneof('initializer_oneof') + if initializer_oneof == 'truncated_normal_initializer': + return tf.truncated_normal_initializer( + mean=initializer.truncated_normal_initializer.mean, + stddev=initializer.truncated_normal_initializer.stddev) + if initializer_oneof == 'variance_scaling_initializer': + enum_descriptor = (hyperparams_pb2.VarianceScalingInitializer. + DESCRIPTOR.enum_types_by_name['Mode']) + mode = enum_descriptor.values_by_number[initializer. + variance_scaling_initializer. + mode].name + return slim.variance_scaling_initializer( + factor=initializer.variance_scaling_initializer.factor, + mode=mode, + uniform=initializer.variance_scaling_initializer.uniform) + raise ValueError('Unknown initializer function: {}'.format( + initializer_oneof)) + + +def _build_batch_norm_params(batch_norm, is_training): + """Build a dictionary of batch_norm params from config. + + Args: + batch_norm: hyperparams_pb2.ConvHyperparams.batch_norm proto. + is_training: Whether the models is in training mode. + + Returns: + A dictionary containing batch_norm parameters. + """ + batch_norm_params = { + 'decay': batch_norm.decay, + 'center': batch_norm.center, + 'scale': batch_norm.scale, + 'epsilon': batch_norm.epsilon, + 'fused': True, + 'is_training': is_training and batch_norm.train, + } + return batch_norm_params diff --git a/object_detection/builders/hyperparams_builder_test.py b/object_detection/builders/hyperparams_builder_test.py new file mode 100644 index 000000000..7b0572a03 --- /dev/null +++ b/object_detection/builders/hyperparams_builder_test.py @@ -0,0 +1,450 @@ +# 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 object_detection.core.hyperparams_builder.""" + +import numpy as np +import tensorflow as tf + +from google.protobuf import text_format + +# TODO: Rewrite third_party imports. +from object_detection.builders import hyperparams_builder +from object_detection.protos import hyperparams_pb2 + +slim = tf.contrib.slim + + +class HyperparamsBuilderTest(tf.test.TestCase): + + # TODO: Make this a public api in slim arg_scope.py. + def _get_scope_key(self, op): + return getattr(op, '_key_op', str(op)) + + def test_default_arg_scope_has_conv2d_op(self): + conv_hyperparams_text_proto = """ + regularizer { + l1_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + self.assertTrue(self._get_scope_key(slim.conv2d) in scope) + + def test_default_arg_scope_has_separable_conv2d_op(self): + conv_hyperparams_text_proto = """ + regularizer { + l1_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + self.assertTrue(self._get_scope_key(slim.separable_conv2d) in scope) + + def test_default_arg_scope_has_conv2d_transpose_op(self): + conv_hyperparams_text_proto = """ + regularizer { + l1_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + self.assertTrue(self._get_scope_key(slim.conv2d_transpose) in scope) + + def test_explicit_fc_op_arg_scope_has_fully_connected_op(self): + conv_hyperparams_text_proto = """ + op: FC + regularizer { + l1_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + self.assertTrue(self._get_scope_key(slim.fully_connected) in scope) + + def test_separable_conv2d_and_conv2d_and_transpose_have_same_parameters(self): + conv_hyperparams_text_proto = """ + regularizer { + l1_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + kwargs_1, kwargs_2, kwargs_3 = scope.values() + self.assertDictEqual(kwargs_1, kwargs_2) + self.assertDictEqual(kwargs_1, kwargs_3) + + def test_return_l1_regularized_weights(self): + conv_hyperparams_text_proto = """ + regularizer { + l1_regularizer { + weight: 0.5 + } + } + initializer { + truncated_normal_initializer { + } + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + conv_scope_arguments = scope.values()[0] + regularizer = conv_scope_arguments['weights_regularizer'] + weights = np.array([1., -1, 4., 2.]) + with self.test_session() as sess: + result = sess.run(regularizer(tf.constant(weights))) + self.assertAllClose(np.abs(weights).sum() * 0.5, result) + + def test_return_l2_regularizer_weights(self): + conv_hyperparams_text_proto = """ + regularizer { + l2_regularizer { + weight: 0.42 + } + } + initializer { + truncated_normal_initializer { + } + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + conv_scope_arguments = scope.values()[0] + + regularizer = conv_scope_arguments['weights_regularizer'] + weights = np.array([1., -1, 4., 2.]) + with self.test_session() as sess: + result = sess.run(regularizer(tf.constant(weights))) + self.assertAllClose(np.power(weights, 2).sum() / 2.0 * 0.42, result) + + def test_return_non_default_batch_norm_params_with_train_during_train(self): + conv_hyperparams_text_proto = """ + regularizer { + l2_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + batch_norm { + decay: 0.7 + center: false + scale: true + epsilon: 0.03 + train: true + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + conv_scope_arguments = scope.values()[0] + self.assertEqual(conv_scope_arguments['normalizer_fn'], slim.batch_norm) + batch_norm_params = conv_scope_arguments['normalizer_params'] + self.assertAlmostEqual(batch_norm_params['decay'], 0.7) + self.assertAlmostEqual(batch_norm_params['epsilon'], 0.03) + self.assertFalse(batch_norm_params['center']) + self.assertTrue(batch_norm_params['scale']) + self.assertTrue(batch_norm_params['is_training']) + + def test_return_batch_norm_params_with_notrain_during_eval(self): + conv_hyperparams_text_proto = """ + regularizer { + l2_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + batch_norm { + decay: 0.7 + center: false + scale: true + epsilon: 0.03 + train: true + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=False) + conv_scope_arguments = scope.values()[0] + self.assertEqual(conv_scope_arguments['normalizer_fn'], slim.batch_norm) + batch_norm_params = conv_scope_arguments['normalizer_params'] + self.assertAlmostEqual(batch_norm_params['decay'], 0.7) + self.assertAlmostEqual(batch_norm_params['epsilon'], 0.03) + self.assertFalse(batch_norm_params['center']) + self.assertTrue(batch_norm_params['scale']) + self.assertFalse(batch_norm_params['is_training']) + + def test_return_batch_norm_params_with_notrain_when_train_is_false(self): + conv_hyperparams_text_proto = """ + regularizer { + l2_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + batch_norm { + decay: 0.7 + center: false + scale: true + epsilon: 0.03 + train: false + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + conv_scope_arguments = scope.values()[0] + self.assertEqual(conv_scope_arguments['normalizer_fn'], slim.batch_norm) + batch_norm_params = conv_scope_arguments['normalizer_params'] + self.assertAlmostEqual(batch_norm_params['decay'], 0.7) + self.assertAlmostEqual(batch_norm_params['epsilon'], 0.03) + self.assertFalse(batch_norm_params['center']) + self.assertTrue(batch_norm_params['scale']) + self.assertFalse(batch_norm_params['is_training']) + + def test_do_not_use_batch_norm_if_default(self): + conv_hyperparams_text_proto = """ + regularizer { + l2_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + conv_scope_arguments = scope.values()[0] + self.assertEqual(conv_scope_arguments['normalizer_fn'], None) + self.assertEqual(conv_scope_arguments['normalizer_params'], None) + + def test_use_none_activation(self): + conv_hyperparams_text_proto = """ + regularizer { + l2_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + activation: NONE + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + conv_scope_arguments = scope.values()[0] + self.assertEqual(conv_scope_arguments['activation_fn'], None) + + def test_use_relu_activation(self): + conv_hyperparams_text_proto = """ + regularizer { + l2_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + activation: RELU + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + conv_scope_arguments = scope.values()[0] + self.assertEqual(conv_scope_arguments['activation_fn'], tf.nn.relu) + + def test_use_relu_6_activation(self): + conv_hyperparams_text_proto = """ + regularizer { + l2_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + activation: RELU_6 + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + conv_scope_arguments = scope.values()[0] + self.assertEqual(conv_scope_arguments['activation_fn'], tf.nn.relu6) + + def _assert_variance_in_range(self, initializer, shape, variance, + tol=1e-2): + with tf.Graph().as_default() as g: + with self.test_session(graph=g) as sess: + var = tf.get_variable( + name='test', + shape=shape, + dtype=tf.float32, + initializer=initializer) + sess.run(tf.global_variables_initializer()) + values = sess.run(var) + self.assertAllClose(np.var(values), variance, tol, tol) + + def test_variance_in_range_with_variance_scaling_initializer_fan_in(self): + conv_hyperparams_text_proto = """ + regularizer { + l2_regularizer { + } + } + initializer { + variance_scaling_initializer { + factor: 2.0 + mode: FAN_IN + uniform: false + } + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + conv_scope_arguments = scope.values()[0] + initializer = conv_scope_arguments['weights_initializer'] + self._assert_variance_in_range(initializer, shape=[100, 40], + variance=2. / 100.) + + def test_variance_in_range_with_variance_scaling_initializer_fan_out(self): + conv_hyperparams_text_proto = """ + regularizer { + l2_regularizer { + } + } + initializer { + variance_scaling_initializer { + factor: 2.0 + mode: FAN_OUT + uniform: false + } + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + conv_scope_arguments = scope.values()[0] + initializer = conv_scope_arguments['weights_initializer'] + self._assert_variance_in_range(initializer, shape=[100, 40], + variance=2. / 40.) + + def test_variance_in_range_with_variance_scaling_initializer_fan_avg(self): + conv_hyperparams_text_proto = """ + regularizer { + l2_regularizer { + } + } + initializer { + variance_scaling_initializer { + factor: 2.0 + mode: FAN_AVG + uniform: false + } + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + conv_scope_arguments = scope.values()[0] + initializer = conv_scope_arguments['weights_initializer'] + self._assert_variance_in_range(initializer, shape=[100, 40], + variance=4. / (100. + 40.)) + + def test_variance_in_range_with_variance_scaling_initializer_uniform(self): + conv_hyperparams_text_proto = """ + regularizer { + l2_regularizer { + } + } + initializer { + variance_scaling_initializer { + factor: 2.0 + mode: FAN_IN + uniform: true + } + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + conv_scope_arguments = scope.values()[0] + initializer = conv_scope_arguments['weights_initializer'] + self._assert_variance_in_range(initializer, shape=[100, 40], + variance=2. / 100.) + + def test_variance_in_range_with_truncated_normal_initializer(self): + conv_hyperparams_text_proto = """ + regularizer { + l2_regularizer { + } + } + initializer { + truncated_normal_initializer { + mean: 0.0 + stddev: 0.8 + } + } + """ + conv_hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams_proto) + scope = hyperparams_builder.build(conv_hyperparams_proto, is_training=True) + conv_scope_arguments = scope.values()[0] + initializer = conv_scope_arguments['weights_initializer'] + self._assert_variance_in_range(initializer, shape=[100, 40], + variance=0.49, tol=1e-1) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/builders/image_resizer_builder.py b/object_detection/builders/image_resizer_builder.py new file mode 100644 index 000000000..542e2de03 --- /dev/null +++ b/object_detection/builders/image_resizer_builder.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. +# ============================================================================== + +"""Builder function for image resizing operations.""" +import functools + +from object_detection.core import preprocessor +from object_detection.protos import image_resizer_pb2 + + +def build(image_resizer_config): + """Builds callable for image resizing operations. + + Args: + image_resizer_config: image_resizer.proto object containing parameters for + an image resizing operation. + + Returns: + image_resizer_fn: Callable for image resizing. This callable always takes + a rank-3 image tensor (corresponding to a single image) and returns a + rank-3 image tensor, possibly with new spatial dimensions. + + Raises: + ValueError: if `image_resizer_config` is of incorrect type. + ValueError: if `image_resizer_config.image_resizer_oneof` is of expected + type. + ValueError: if min_dimension > 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.') + + if image_resizer_config.WhichOneof( + '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') + return functools.partial( + preprocessor.resize_to_range, + min_dimension=keep_aspect_ratio_config.min_dimension, + max_dimension=keep_aspect_ratio_config.max_dimension) + if image_resizer_config.WhichOneof( + 'image_resizer_oneof') == 'fixed_shape_resizer': + fixed_shape_resizer_config = image_resizer_config.fixed_shape_resizer + return functools.partial(preprocessor.resize_image, + new_height=fixed_shape_resizer_config.height, + new_width=fixed_shape_resizer_config.width) + raise ValueError('Invalid image resizer option.') diff --git a/object_detection/builders/image_resizer_builder_test.py b/object_detection/builders/image_resizer_builder_test.py new file mode 100644 index 000000000..79c6287d4 --- /dev/null +++ b/object_detection/builders/image_resizer_builder_test.py @@ -0,0 +1,70 @@ +# 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.builders.image_resizer_builder.""" +import tensorflow as tf +from google.protobuf import text_format +from object_detection.builders import image_resizer_builder +from object_detection.protos import image_resizer_pb2 + + +class ImageResizerBuilderTest(tf.test.TestCase): + + def _shape_of_resized_random_image_given_text_proto( + self, input_shape, text_proto): + image_resizer_config = image_resizer_pb2.ImageResizer() + text_format.Merge(text_proto, image_resizer_config) + image_resizer_fn = image_resizer_builder.build(image_resizer_config) + images = tf.to_float(tf.random_uniform( + input_shape, minval=0, maxval=255, dtype=tf.int32)) + resized_images = image_resizer_fn(images) + with self.test_session() as sess: + return sess.run(resized_images).shape + + def test_built_keep_aspect_ratio_resizer_returns_expected_shape(self): + image_resizer_text_proto = """ + keep_aspect_ratio_resizer { + min_dimension: 10 + max_dimension: 20 + } + """ + input_shape = (50, 25, 3) + expected_output_shape = (20, 10, 3) + output_shape = self._shape_of_resized_random_image_given_text_proto( + input_shape, image_resizer_text_proto) + self.assertEqual(output_shape, expected_output_shape) + + def test_built_fixed_shape_resizer_returns_expected_shape(self): + image_resizer_text_proto = """ + fixed_shape_resizer { + height: 10 + width: 20 + } + """ + input_shape = (50, 25, 3) + expected_output_shape = (10, 20, 3) + output_shape = self._shape_of_resized_random_image_given_text_proto( + input_shape, image_resizer_text_proto) + self.assertEqual(output_shape, expected_output_shape) + + def test_raises_error_on_invalid_input(self): + invalid_input = 'invalid_input' + with self.assertRaises(ValueError): + image_resizer_builder.build(invalid_input) + + +if __name__ == '__main__': + tf.test.main() + diff --git a/object_detection/builders/input_reader_builder.py b/object_detection/builders/input_reader_builder.py new file mode 100644 index 000000000..98ad6127a --- /dev/null +++ b/object_detection/builders/input_reader_builder.py @@ -0,0 +1,65 @@ +# 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. +# ============================================================================== + +"""Input reader builder. + +Creates data sources for DetectionModels from an InputReader config. See +input_reader.proto for options. + +Note: If users wishes to also use their own InputReaders with the Object +Detection configuration framework, they should define their own builder function +that wraps the build function. +""" + +import tensorflow as tf + +from object_detection.data_decoders import tf_example_decoder +from object_detection.protos import input_reader_pb2 + +parallel_reader = tf.contrib.slim.parallel_reader + + +def build(input_reader_config): + """Builds a tensor dictionary based on the InputReader config. + + Args: + input_reader_config: A input_reader_pb2.InputReader object. + + Returns: + A tensor dict 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': + config = input_reader_config.tf_record_input_reader + _, string_tensor = parallel_reader.parallel_read( + config.input_path, + reader_class=tf.TFRecordReader, + num_epochs=(input_reader_config.num_epochs + if input_reader_config.num_epochs else None), + num_readers=input_reader_config.num_readers, + shuffle=input_reader_config.shuffle, + dtypes=[tf.string, tf.string], + capacity=input_reader_config.queue_capacity, + min_after_dequeue=input_reader_config.min_after_dequeue) + + return tf_example_decoder.TfExampleDecoder().Decode(string_tensor) + + raise ValueError('Unsupported input_reader_config.') diff --git a/object_detection/builders/input_reader_builder_test.py b/object_detection/builders/input_reader_builder_test.py new file mode 100644 index 000000000..05b8a95e5 --- /dev/null +++ b/object_detection/builders/input_reader_builder_test.py @@ -0,0 +1,92 @@ +# 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 input_reader_builder.""" + +import os +import numpy as np +import tensorflow as tf + +from google.protobuf import text_format + +from tensorflow.core.example import example_pb2 +from tensorflow.core.example import feature_pb2 +from object_detection.builders import input_reader_builder +from object_detection.core import standard_fields as fields +from object_detection.protos import input_reader_pb2 + + +class InputReaderBuilderTest(tf.test.TestCase): + + def create_tf_record(self): + path = os.path.join(self.get_temp_dir(), 'tfrecord') + writer = tf.python_io.TFRecordWriter(path) + + image_tensor = np.random.randint(255, size=(4, 5, 3)).astype(np.uint8) + with self.test_session(): + encoded_jpeg = tf.image.encode_jpeg(tf.constant(image_tensor)).eval() + example = example_pb2.Example(features=feature_pb2.Features(feature={ + 'image/encoded': feature_pb2.Feature( + bytes_list=feature_pb2.BytesList(value=[encoded_jpeg])), + 'image/format': feature_pb2.Feature( + bytes_list=feature_pb2.BytesList(value=['jpeg'.encode('utf-8')])), + 'image/object/bbox/xmin': feature_pb2.Feature( + float_list=feature_pb2.FloatList(value=[0.0])), + 'image/object/bbox/xmax': feature_pb2.Feature( + float_list=feature_pb2.FloatList(value=[1.0])), + 'image/object/bbox/ymin': feature_pb2.Feature( + float_list=feature_pb2.FloatList(value=[0.0])), + 'image/object/bbox/ymax': feature_pb2.Feature( + float_list=feature_pb2.FloatList(value=[1.0])), + 'image/object/class/label': feature_pb2.Feature( + int64_list=feature_pb2.Int64List(value=[2])), + })) + writer.write(example.SerializeToString()) + 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) + tensor_dict = input_reader_builder.build(input_reader_proto) + + sv = tf.train.Supervisor(logdir=self.get_temp_dir()) + with sv.prepare_or_wait_for_session() as sess: + sv.start_queue_runners(sess) + output_dict = sess.run(tensor_dict) + + self.assertEquals( + (4, 5, 3), output_dict[fields.InputDataFields.image].shape) + self.assertEquals( + [2], output_dict[fields.InputDataFields.groundtruth_classes]) + self.assertEquals( + (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]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/builders/losses_builder.py b/object_detection/builders/losses_builder.py new file mode 100644 index 000000000..7163e4877 --- /dev/null +++ b/object_detection/builders/losses_builder.py @@ -0,0 +1,161 @@ +# 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 localization and classification losses from config.""" + +from object_detection.core import losses +from object_detection.protos import losses_pb2 + + +def build(loss_config): + """Build losses based on the config. + + Builds classification, localization losses and optionally a hard example miner + based on the config. + + Args: + loss_config: A losses_pb2.Loss object. + + Returns: + classification_loss: Classification loss object. + localization_loss: Localization loss object. + classification_weight: Classification loss weight. + localization_weight: Localization loss weight. + hard_example_miner: Hard example miner object. + """ + classification_loss = _build_classification_loss( + loss_config.classification_loss) + localization_loss = _build_localization_loss( + loss_config.localization_loss) + classification_weight = loss_config.classification_weight + localization_weight = loss_config.localization_weight + hard_example_miner = None + if loss_config.HasField('hard_example_miner'): + hard_example_miner = build_hard_example_miner( + loss_config.hard_example_miner, + classification_weight, + localization_weight) + return (classification_loss, localization_loss, + classification_weight, + localization_weight, hard_example_miner) + + +def build_hard_example_miner(config, + classification_weight, + localization_weight): + """Builds hard example miner based on the config. + + Args: + config: A losses_pb2.HardExampleMiner object. + classification_weight: Classification loss weight. + localization_weight: Localization loss weight. + + Returns: + Hard example miner. + + """ + loss_type = None + if config.loss_type == losses_pb2.HardExampleMiner.BOTH: + loss_type = 'both' + if config.loss_type == losses_pb2.HardExampleMiner.CLASSIFICATION: + loss_type = 'cls' + if config.loss_type == losses_pb2.HardExampleMiner.LOCALIZATION: + loss_type = 'loc' + + max_negatives_per_positive = None + num_hard_examples = None + if config.max_negatives_per_positive > 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_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': + config = loss_config.weighted_l2 + return losses.WeightedL2LocalizationLoss( + anchorwise_output=config.anchorwise_output) + + if loss_type == 'weighted_smooth_l1': + config = loss_config.weighted_smooth_l1 + return losses.WeightedSmoothL1LocalizationLoss( + anchorwise_output=config.anchorwise_output) + + if loss_type == 'weighted_iou': + return losses.WeightedIOULocalizationLoss() + + 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': + config = loss_config.weighted_sigmoid + return losses.WeightedSigmoidClassificationLoss( + anchorwise_output=config.anchorwise_output) + + if loss_type == 'weighted_softmax': + config = loss_config.weighted_softmax + return losses.WeightedSoftmaxClassificationLoss( + anchorwise_output=config.anchorwise_output) + + 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'), + anchorwise_output=config.anchorwise_output) + + raise ValueError('Empty loss config.') diff --git a/object_detection/builders/losses_builder_test.py b/object_detection/builders/losses_builder_test.py new file mode 100644 index 000000000..90e5d639c --- /dev/null +++ b/object_detection/builders/losses_builder_test.py @@ -0,0 +1,323 @@ +# 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 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 + + +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.assertTrue(isinstance(localization_loss, + losses.WeightedL2LocalizationLoss)) + + def test_build_weighted_smooth_l1_localization_loss(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.assertTrue(isinstance(localization_loss, + losses.WeightedSmoothL1LocalizationLoss)) + + 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.assertTrue(isinstance(localization_loss, + losses.WeightedIOULocalizationLoss)) + + def test_anchorwise_output(self): + losses_text_proto = """ + localization_loss { + weighted_smooth_l1 { + anchorwise_output: true + } + } + 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.assertTrue(isinstance(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.assertTrue(isinstance(classification_loss, + losses.WeightedSigmoidClassificationLoss)) + + 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.assertTrue(isinstance(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.assertTrue(isinstance(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.assertTrue(isinstance(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]]) + loss = classification_loss(predictions, targets, weights=weights) + self.assertEqual(loss.shape, [1, 2]) + + 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.assertTrue(isinstance(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.assertTrue(isinstance(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.assertTrue(isinstance(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.assertTrue(isinstance(hard_example_miner, losses.HardExampleMiner)) + self.assertTrue(isinstance(classification_loss, + losses.WeightedSoftmaxClassificationLoss)) + self.assertTrue(isinstance(localization_loss, + losses.WeightedL2LocalizationLoss)) + self.assertAlmostEqual(classification_weight, 0.8) + self.assertAlmostEqual(localization_weight, 0.2) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/builders/matcher_builder.py b/object_detection/builders/matcher_builder.py new file mode 100644 index 000000000..6ec49da97 --- /dev/null +++ b/object_detection/builders/matcher_builder.py @@ -0,0 +1,51 @@ +# 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.matchers import bipartite_matcher +from object_detection.protos import matcher_pb2 + + +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) + if matcher_config.WhichOneof('matcher_oneof') == 'bipartite_matcher': + return bipartite_matcher.GreedyBipartiteMatcher() + raise ValueError('Empty matcher.') diff --git a/object_detection/builders/matcher_builder_test.py b/object_detection/builders/matcher_builder_test.py new file mode 100644 index 000000000..c4275aaef --- /dev/null +++ b/object_detection/builders/matcher_builder_test.py @@ -0,0 +1,97 @@ +# 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 matcher_builder.""" + +import tensorflow as tf + +from google.protobuf import text_format +from object_detection.builders import matcher_builder +from object_detection.matchers import argmax_matcher +from object_detection.matchers import bipartite_matcher +from object_detection.protos import matcher_pb2 + + +class MatcherBuilderTest(tf.test.TestCase): + + def test_build_arg_max_matcher_with_defaults(self): + matcher_text_proto = """ + argmax_matcher { + } + """ + matcher_proto = matcher_pb2.Matcher() + text_format.Merge(matcher_text_proto, matcher_proto) + matcher_object = matcher_builder.build(matcher_proto) + self.assertTrue(isinstance(matcher_object, argmax_matcher.ArgMaxMatcher)) + self.assertAlmostEqual(matcher_object._matched_threshold, 0.5) + self.assertAlmostEqual(matcher_object._unmatched_threshold, 0.5) + self.assertTrue(matcher_object._negatives_lower_than_unmatched) + self.assertFalse(matcher_object._force_match_for_each_row) + + def test_build_arg_max_matcher_without_thresholds(self): + matcher_text_proto = """ + argmax_matcher { + ignore_thresholds: true + } + """ + matcher_proto = matcher_pb2.Matcher() + text_format.Merge(matcher_text_proto, matcher_proto) + matcher_object = matcher_builder.build(matcher_proto) + self.assertTrue(isinstance(matcher_object, argmax_matcher.ArgMaxMatcher)) + self.assertEqual(matcher_object._matched_threshold, None) + self.assertEqual(matcher_object._unmatched_threshold, None) + self.assertTrue(matcher_object._negatives_lower_than_unmatched) + self.assertFalse(matcher_object._force_match_for_each_row) + + def test_build_arg_max_matcher_with_non_default_parameters(self): + matcher_text_proto = """ + argmax_matcher { + matched_threshold: 0.7 + unmatched_threshold: 0.3 + negatives_lower_than_unmatched: false + force_match_for_each_row: true + } + """ + matcher_proto = matcher_pb2.Matcher() + text_format.Merge(matcher_text_proto, matcher_proto) + matcher_object = matcher_builder.build(matcher_proto) + self.assertTrue(isinstance(matcher_object, argmax_matcher.ArgMaxMatcher)) + self.assertAlmostEqual(matcher_object._matched_threshold, 0.7) + self.assertAlmostEqual(matcher_object._unmatched_threshold, 0.3) + self.assertFalse(matcher_object._negatives_lower_than_unmatched) + self.assertTrue(matcher_object._force_match_for_each_row) + + def test_build_bipartite_matcher(self): + matcher_text_proto = """ + bipartite_matcher { + } + """ + matcher_proto = matcher_pb2.Matcher() + text_format.Merge(matcher_text_proto, matcher_proto) + matcher_object = matcher_builder.build(matcher_proto) + self.assertTrue( + isinstance(matcher_object, bipartite_matcher.GreedyBipartiteMatcher)) + + def test_raise_error_on_empty_matcher(self): + matcher_text_proto = """ + """ + matcher_proto = matcher_pb2.Matcher() + text_format.Merge(matcher_text_proto, matcher_proto) + with self.assertRaises(ValueError): + matcher_builder.build(matcher_proto) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/builders/model_builder.py b/object_detection/builders/model_builder.py new file mode 100644 index 000000000..7df3959c3 --- /dev/null +++ b/object_detection/builders/model_builder.py @@ -0,0 +1,303 @@ +# 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 a DetectionModel from configuration.""" +from object_detection.builders import anchor_generator_builder +from object_detection.builders import box_coder_builder +from object_detection.builders import box_predictor_builder +from object_detection.builders import hyperparams_builder +from object_detection.builders import image_resizer_builder +from object_detection.builders import losses_builder +from object_detection.builders import matcher_builder +from object_detection.builders import post_processing_builder +from object_detection.builders import region_similarity_calculator_builder as sim_calc +from object_detection.core import box_predictor +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 faster_rcnn_inception_resnet_v2_feature_extractor as frcnn_inc_res +from object_detection.models import faster_rcnn_resnet_v1_feature_extractor as frcnn_resnet_v1 +from object_detection.models.ssd_inception_v2_feature_extractor import SSDInceptionV2FeatureExtractor +from object_detection.models.ssd_mobilenet_v1_feature_extractor import SSDMobileNetV1FeatureExtractor +from object_detection.protos import model_pb2 + +# A map of names to SSD feature extractors. +SSD_FEATURE_EXTRACTOR_CLASS_MAP = { + 'ssd_inception_v2': SSDInceptionV2FeatureExtractor, + 'ssd_mobilenet_v1': SSDMobileNetV1FeatureExtractor, +} + +# A map of names to Faster R-CNN feature extractors. +FASTER_RCNN_FEATURE_EXTRACTOR_CLASS_MAP = { + 'faster_rcnn_resnet50': + frcnn_resnet_v1.FasterRCNNResnet50FeatureExtractor, + 'faster_rcnn_resnet101': + frcnn_resnet_v1.FasterRCNNResnet101FeatureExtractor, + 'faster_rcnn_resnet152': + frcnn_resnet_v1.FasterRCNNResnet152FeatureExtractor, + 'faster_rcnn_inception_resnet_v2': + frcnn_inc_res.FasterRCNNInceptionResnetV2FeatureExtractor +} + + +def build(model_config, is_training): + """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. + + 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 == 'ssd': + return _build_ssd_model(model_config.ssd, is_training) + if meta_architecture == 'faster_rcnn': + return _build_faster_rcnn_model(model_config.faster_rcnn, is_training) + raise ValueError('Unknown meta architecture: {}'.format(meta_architecture)) + + +def _build_ssd_feature_extractor(feature_extractor_config, is_training, + 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. + 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 + conv_hyperparams = hyperparams_builder.build( + feature_extractor_config.conv_hyperparams, is_training) + + if feature_type not in SSD_FEATURE_EXTRACTOR_CLASS_MAP: + raise ValueError('Unknown ssd feature_extractor: {}'.format(feature_type)) + + feature_extractor_class = SSD_FEATURE_EXTRACTOR_CLASS_MAP[feature_type] + return feature_extractor_class(depth_multiplier, min_depth, conv_hyperparams, + reuse_weights) + + +def _build_ssd_model(ssd_config, is_training): + """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. + + 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 + + # Feature extractor + feature_extractor = _build_ssd_feature_extractor(ssd_config.feature_extractor, + 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) + ssd_box_predictor = box_predictor_builder.build(hyperparams_builder.build, + ssd_config.box_predictor, + is_training, num_classes) + anchor_generator = anchor_generator_builder.build( + ssd_config.anchor_generator) + 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) = losses_builder.build(ssd_config.loss) + normalize_loss_by_num_matches = ssd_config.normalize_loss_by_num_matches + + return ssd_meta_arch.SSDMetaArch( + is_training, + anchor_generator, + ssd_box_predictor, + box_coder, + feature_extractor, + matcher, + region_similarity_calculator, + image_resizer_fn, + non_max_suppression_fn, + score_conversion_fn, + classification_loss, + localization_loss, + classification_weight, + localization_weight, + normalize_loss_by_num_matches, + hard_example_miner) + + +def _build_faster_rcnn_feature_extractor( + feature_extractor_config, is_training, reuse_weights=None): + """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. + + Returns: + faster_rcnn_meta_arch.FasterRCNNFeatureExtractor based on config. + + Raises: + ValueError: On invalid feature extractor type. + """ + feature_type = feature_extractor_config.type + first_stage_features_stride = ( + feature_extractor_config.first_stage_features_stride) + + 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, reuse_weights) + + +def _build_faster_rcnn_model(frcnn_config, is_training): + """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. + + 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) + + feature_extractor = _build_faster_rcnn_feature_extractor( + frcnn_config.feature_extractor, is_training) + + first_stage_only = frcnn_config.first_stage_only + first_stage_anchor_generator = anchor_generator_builder.build( + frcnn_config.first_stage_anchor_generator) + + first_stage_atrous_rate = frcnn_config.first_stage_atrous_rate + first_stage_box_predictor_arg_scope = 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 + first_stage_positive_balance_fraction = ( + frcnn_config.first_stage_positive_balance_fraction) + first_stage_nms_score_threshold = frcnn_config.first_stage_nms_score_threshold + first_stage_nms_iou_threshold = frcnn_config.first_stage_nms_iou_threshold + first_stage_max_proposals = frcnn_config.first_stage_max_proposals + 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_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_balance_fraction = frcnn_config.second_stage_balance_fraction + (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_weight = ( + frcnn_config.second_stage_classification_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) + + common_kwargs = { + 'is_training': is_training, + 'num_classes': num_classes, + 'image_resizer_fn': image_resizer_fn, + 'feature_extractor': feature_extractor, + 'first_stage_only': first_stage_only, + 'first_stage_anchor_generator': first_stage_anchor_generator, + 'first_stage_atrous_rate': first_stage_atrous_rate, + 'first_stage_box_predictor_arg_scope': + first_stage_box_predictor_arg_scope, + '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_positive_balance_fraction': + first_stage_positive_balance_fraction, + 'first_stage_nms_score_threshold': first_stage_nms_score_threshold, + 'first_stage_nms_iou_threshold': first_stage_nms_iou_threshold, + '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_batch_size': second_stage_batch_size, + 'second_stage_balance_fraction': second_stage_balance_fraction, + '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_weight': + second_stage_classification_loss_weight, + 'hard_example_miner': hard_example_miner} + + if isinstance(second_stage_box_predictor, box_predictor.RfcnBoxPredictor): + return rfcn_meta_arch.RFCNMetaArch( + second_stage_rfcn_box_predictor=second_stage_box_predictor, + **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, + **common_kwargs) diff --git a/object_detection/builders/model_builder_test.py b/object_detection/builders/model_builder_test.py new file mode 100644 index 000000000..513c5fab3 --- /dev/null +++ b/object_detection/builders/model_builder_test.py @@ -0,0 +1,456 @@ +# 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.""" + +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 faster_rcnn_inception_resnet_v2_feature_extractor as frcnn_inc_res +from object_detection.models import faster_rcnn_resnet_v1_feature_extractor as frcnn_resnet_v1 +from object_detection.models.ssd_inception_v2_feature_extractor import SSDInceptionV2FeatureExtractor +from object_detection.models.ssd_mobilenet_v1_feature_extractor import SSDMobileNetV1FeatureExtractor +from object_detection.protos import model_pb2 + +FEATURE_EXTRACTOR_MAPS = { + 'faster_rcnn_resnet50': + frcnn_resnet_v1.FasterRCNNResnet50FeatureExtractor, + 'faster_rcnn_resnet101': + frcnn_resnet_v1.FasterRCNNResnet101FeatureExtractor, + 'faster_rcnn_resnet152': + frcnn_resnet_v1.FasterRCNNResnet152FeatureExtractor +} + + +class ModelBuilderTest(tf.test.TestCase): + + def create_model(self, model_config): + """Builds a DetectionModel based on the model config. + + Args: + model_config: A model.proto object containing the config for the desired + DetectionModel. + + Returns: + DetectionModel based on the config. + """ + return model_builder.build(model_config, is_training=True) + + def test_create_ssd_inception_v2_model_from_config(self): + model_text_proto = """ + ssd { + feature_extractor { + type: 'ssd_inception_v2' + conv_hyperparams { + regularizer { + l2_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + } + } + 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) + model = self.create_model(model_proto) + self.assertIsInstance(model, ssd_meta_arch.SSDMetaArch) + self.assertIsInstance(model._feature_extractor, + SSDInceptionV2FeatureExtractor) + + def test_create_ssd_mobilenet_v1_model_from_config(self): + model_text_proto = """ + ssd { + feature_extractor { + type: 'ssd_mobilenet_v1' + conv_hyperparams { + regularizer { + l2_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + } + } + 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) + model = self.create_model(model_proto) + self.assertIsInstance(model, ssd_meta_arch.SSDMetaArch) + self.assertIsInstance(model._feature_extractor, + SSDMobileNetV1FeatureExtractor) + + def test_create_faster_rcnn_resnet_v1_models_from_config(self): + model_text_proto = """ + faster_rcnn { + 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 { + 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) + for extractor_type, extractor_class in FEATURE_EXTRACTOR_MAPS.iteritems(): + model_proto.faster_rcnn.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) + + def test_create_faster_rcnn_inception_resnet_v2_model_from_config(self): + model_text_proto = """ + faster_rcnn { + num_classes: 3 + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 600 + max_dimension: 1024 + } + } + feature_extractor { + type: 'faster_rcnn_inception_resnet_v2' + } + 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: 17 + maxpool_kernel_size: 1 + maxpool_stride: 1 + second_stage_box_predictor { + mask_rcnn_box_predictor { + 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) + model = model_builder.build(model_proto, is_training=True) + self.assertIsInstance(model, faster_rcnn_meta_arch.FasterRCNNMetaArch) + self.assertIsInstance( + model._feature_extractor, + frcnn_inc_res.FasterRCNNInceptionResnetV2FeatureExtractor) + + def test_create_faster_rcnn_model_from_config_with_example_miner(self): + model_text_proto = """ + faster_rcnn { + num_classes: 3 + feature_extractor { + type: 'faster_rcnn_inception_resnet_v2' + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 600 + max_dimension: 1024 + } + } + 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 { + } + } + } + second_stage_box_predictor { + mask_rcnn_box_predictor { + fc_hyperparams { + op: FC + regularizer { + l2_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + } + } + } + hard_example_miner { + num_hard_examples: 10 + iou_threshold: 0.99 + } + }""" + model_proto = model_pb2.DetectionModel() + text_format.Merge(model_text_proto, model_proto) + model = model_builder.build(model_proto, is_training=True) + self.assertIsNotNone(model._hard_example_miner) + + def test_create_rfcn_resnet_v1_model_from_config(self): + model_text_proto = """ + faster_rcnn { + 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 { + rfcn_box_predictor { + conv_hyperparams { + op: CONV + 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) + for extractor_type, extractor_class in FEATURE_EXTRACTOR_MAPS.iteritems(): + 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) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/builders/optimizer_builder.py b/object_detection/builders/optimizer_builder.py new file mode 100644 index 000000000..f74b05620 --- /dev/null +++ b/object_detection/builders/optimizer_builder.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. +# ============================================================================== + +"""Functions to build DetectionModel training optimizers.""" + +import tensorflow as tf +from object_detection.utils import learning_schedules + +slim = tf.contrib.slim + + +def build(optimizer_config, global_summaries): + """Create optimizer based on config. + + Args: + optimizer_config: A Optimizer proto message. + global_summaries: A set to attach learning rate summary to. + + Returns: + An optimizer. + + Raises: + ValueError: when using an unsupported input data type. + """ + optimizer_type = optimizer_config.WhichOneof('optimizer') + optimizer = None + + if optimizer_type == 'rms_prop_optimizer': + config = optimizer_config.rms_prop_optimizer + optimizer = tf.train.RMSPropOptimizer( + _create_learning_rate(config.learning_rate, global_summaries), + decay=config.decay, + momentum=config.momentum_optimizer_value, + epsilon=config.epsilon) + + if optimizer_type == 'momentum_optimizer': + config = optimizer_config.momentum_optimizer + optimizer = tf.train.MomentumOptimizer( + _create_learning_rate(config.learning_rate, global_summaries), + momentum=config.momentum_optimizer_value) + + if optimizer_type == 'adam_optimizer': + config = optimizer_config.adam_optimizer + optimizer = tf.train.AdamOptimizer( + _create_learning_rate(config.learning_rate, global_summaries)) + + if optimizer is None: + raise ValueError('Optimizer %s not supported.' % optimizer_type) + + if optimizer_config.use_moving_average: + optimizer = tf.contrib.opt.MovingAverageOptimizer( + optimizer, average_decay=optimizer_config.moving_average_decay) + + return optimizer + + +def _create_learning_rate(learning_rate_config, global_summaries): + """Create optimizer learning rate based on config. + + Args: + learning_rate_config: A LearningRate proto message. + global_summaries: A set to attach learning rate summary to. + + Returns: + A learning rate. + + Raises: + ValueError: when using an unsupported input data type. + """ + learning_rate = None + learning_rate_type = learning_rate_config.WhichOneof('learning_rate') + if learning_rate_type == 'constant_learning_rate': + config = learning_rate_config.constant_learning_rate + learning_rate = config.learning_rate + + if learning_rate_type == 'exponential_decay_learning_rate': + config = learning_rate_config.exponential_decay_learning_rate + learning_rate = tf.train.exponential_decay( + config.initial_learning_rate, + slim.get_or_create_global_step(), + config.decay_steps, + config.decay_factor, + staircase=config.staircase) + + if learning_rate_type == 'manual_step_learning_rate': + config = learning_rate_config.manual_step_learning_rate + if not config.schedule: + raise ValueError('Empty learning rate schedule.') + learning_rate_step_boundaries = [x.step for x in config.schedule] + learning_rate_sequence = [config.initial_learning_rate] + learning_rate_sequence += [x.learning_rate for x in config.schedule] + learning_rate = learning_schedules.manual_stepping( + slim.get_or_create_global_step(), learning_rate_step_boundaries, + learning_rate_sequence) + + if learning_rate is None: + raise ValueError('Learning_rate %s not supported.' % learning_rate_type) + + global_summaries.add(tf.summary.scalar('Learning Rate', learning_rate)) + return learning_rate diff --git a/object_detection/builders/optimizer_builder_test.py b/object_detection/builders/optimizer_builder_test.py new file mode 100644 index 000000000..958d2e1d2 --- /dev/null +++ b/object_detection/builders/optimizer_builder_test.py @@ -0,0 +1,197 @@ +# 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 optimizer_builder.""" + +import tensorflow as tf + +from google.protobuf import text_format + +from object_detection.builders import optimizer_builder +from object_detection.protos import optimizer_pb2 + + +class LearningRateBuilderTest(tf.test.TestCase): + + def testBuildConstantLearningRate(self): + learning_rate_text_proto = """ + constant_learning_rate { + learning_rate: 0.004 + } + """ + global_summaries = set([]) + learning_rate_proto = optimizer_pb2.LearningRate() + text_format.Merge(learning_rate_text_proto, learning_rate_proto) + learning_rate = optimizer_builder._create_learning_rate( + learning_rate_proto, global_summaries) + self.assertAlmostEqual(learning_rate, 0.004) + + def testBuildExponentialDecayLearningRate(self): + learning_rate_text_proto = """ + exponential_decay_learning_rate { + initial_learning_rate: 0.004 + decay_steps: 99999 + decay_factor: 0.85 + staircase: false + } + """ + global_summaries = set([]) + learning_rate_proto = optimizer_pb2.LearningRate() + text_format.Merge(learning_rate_text_proto, learning_rate_proto) + learning_rate = optimizer_builder._create_learning_rate( + learning_rate_proto, global_summaries) + self.assertTrue(isinstance(learning_rate, tf.Tensor)) + + def testBuildManualStepLearningRate(self): + learning_rate_text_proto = """ + manual_step_learning_rate { + schedule { + step: 0 + learning_rate: 0.006 + } + schedule { + step: 90000 + learning_rate: 0.00006 + } + } + """ + global_summaries = set([]) + learning_rate_proto = optimizer_pb2.LearningRate() + text_format.Merge(learning_rate_text_proto, learning_rate_proto) + learning_rate = optimizer_builder._create_learning_rate( + learning_rate_proto, global_summaries) + self.assertTrue(isinstance(learning_rate, tf.Tensor)) + + def testRaiseErrorOnEmptyLearningRate(self): + learning_rate_text_proto = """ + """ + global_summaries = set([]) + learning_rate_proto = optimizer_pb2.LearningRate() + text_format.Merge(learning_rate_text_proto, learning_rate_proto) + with self.assertRaises(ValueError): + optimizer_builder._create_learning_rate( + learning_rate_proto, global_summaries) + + +class OptimizerBuilderTest(tf.test.TestCase): + + def testBuildRMSPropOptimizer(self): + optimizer_text_proto = """ + rms_prop_optimizer: { + learning_rate: { + exponential_decay_learning_rate { + initial_learning_rate: 0.004 + decay_steps: 800720 + decay_factor: 0.95 + } + } + momentum_optimizer_value: 0.9 + decay: 0.9 + epsilon: 1.0 + } + use_moving_average: false + """ + global_summaries = set([]) + optimizer_proto = optimizer_pb2.Optimizer() + text_format.Merge(optimizer_text_proto, optimizer_proto) + optimizer = optimizer_builder.build(optimizer_proto, global_summaries) + self.assertTrue(isinstance(optimizer, tf.train.RMSPropOptimizer)) + + def testBuildMomentumOptimizer(self): + optimizer_text_proto = """ + momentum_optimizer: { + learning_rate: { + constant_learning_rate { + learning_rate: 0.001 + } + } + momentum_optimizer_value: 0.99 + } + use_moving_average: false + """ + global_summaries = set([]) + optimizer_proto = optimizer_pb2.Optimizer() + text_format.Merge(optimizer_text_proto, optimizer_proto) + optimizer = optimizer_builder.build(optimizer_proto, global_summaries) + self.assertTrue(isinstance(optimizer, tf.train.MomentumOptimizer)) + + def testBuildAdamOptimizer(self): + optimizer_text_proto = """ + adam_optimizer: { + learning_rate: { + constant_learning_rate { + learning_rate: 0.002 + } + } + } + use_moving_average: false + """ + global_summaries = set([]) + optimizer_proto = optimizer_pb2.Optimizer() + text_format.Merge(optimizer_text_proto, optimizer_proto) + optimizer = optimizer_builder.build(optimizer_proto, global_summaries) + self.assertTrue(isinstance(optimizer, tf.train.AdamOptimizer)) + + def testBuildMovingAverageOptimizer(self): + optimizer_text_proto = """ + adam_optimizer: { + learning_rate: { + constant_learning_rate { + learning_rate: 0.002 + } + } + } + use_moving_average: True + """ + global_summaries = set([]) + optimizer_proto = optimizer_pb2.Optimizer() + text_format.Merge(optimizer_text_proto, optimizer_proto) + optimizer = optimizer_builder.build(optimizer_proto, global_summaries) + self.assertTrue( + isinstance(optimizer, tf.contrib.opt.MovingAverageOptimizer)) + + def testBuildMovingAverageOptimizerWithNonDefaultDecay(self): + optimizer_text_proto = """ + adam_optimizer: { + learning_rate: { + constant_learning_rate { + learning_rate: 0.002 + } + } + } + use_moving_average: True + moving_average_decay: 0.2 + """ + global_summaries = set([]) + optimizer_proto = optimizer_pb2.Optimizer() + text_format.Merge(optimizer_text_proto, optimizer_proto) + optimizer = optimizer_builder.build(optimizer_proto, global_summaries) + self.assertTrue( + isinstance(optimizer, tf.contrib.opt.MovingAverageOptimizer)) + # TODO: Find a way to not depend on the private members. + self.assertAlmostEqual(optimizer._ema._decay, 0.2) + + def testBuildEmptyOptimizer(self): + optimizer_text_proto = """ + """ + global_summaries = set([]) + optimizer_proto = optimizer_pb2.Optimizer() + text_format.Merge(optimizer_text_proto, optimizer_proto) + with self.assertRaises(ValueError): + optimizer_builder.build(optimizer_proto, global_summaries) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/builders/post_processing_builder.py b/object_detection/builders/post_processing_builder.py new file mode 100644 index 000000000..ab8c04ef9 --- /dev/null +++ b/object_detection/builders/post_processing_builder.py @@ -0,0 +1,111 @@ +# 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 function for post processing operations.""" +import functools + +import tensorflow as tf +from object_detection.core import post_processing +from object_detection.protos import post_processing_pb2 + + +def build(post_processing_config): + """Builds callables for post-processing operations. + + Builds callables for non-max suppression and score conversion based on the + configuration. + + Non-max suppression callable takes `boxes`, `scores`, and optionally + `clip_window`, `parallel_iterations` and `scope` as inputs. It returns + `nms_boxes`, `nms_scores`, `nms_nms_classes` and `num_detections`. See + post_processing.batch_multiclass_non_max_suppression for the type and shape + of these tensors. + + Score converter callable should be called with `input` tensor. The callable + returns the output from one of 3 tf operations based on the configuration - + tf.identity, tf.sigmoid or tf.nn.softmax. See tensorflow documentation for + argument and return value descriptions. + + Args: + post_processing_config: post_processing.proto object containing the + parameters for the post-processing operations. + + Returns: + non_max_suppressor_fn: Callable for non-max suppression. + score_converter_fn: Callable for score conversion. + + Raises: + ValueError: if the post_processing_config is of incorrect type. + """ + if not isinstance(post_processing_config, post_processing_pb2.PostProcessing): + raise ValueError('post_processing_config not of type ' + 'post_processing_pb2.Postprocessing.') + non_max_suppressor_fn = _build_non_max_suppressor( + post_processing_config.batch_non_max_suppression) + score_converter_fn = _build_score_converter( + post_processing_config.score_converter) + return non_max_suppressor_fn, score_converter_fn + + +def _build_non_max_suppressor(nms_config): + """Builds non-max suppresson based on the nms config. + + Args: + nms_config: post_processing_pb2.PostProcessing.BatchNonMaxSuppression proto. + + Returns: + non_max_suppressor_fn: Callable non-max suppressor. + + Raises: + ValueError: On incorrect iou_threshold or on incompatible values of + max_total_detections and max_detections_per_class. + """ + if nms_config.iou_threshold < 0 or nms_config.iou_threshold > 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.') + + 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) + return non_max_suppressor_fn + + +def _build_score_converter(score_converter_config): + """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. + + Returns: + Callable score converter op. + + Raises: + ValueError: On unknown score converter. + """ + if score_converter_config == post_processing_pb2.PostProcessing.IDENTITY: + return tf.identity + if score_converter_config == post_processing_pb2.PostProcessing.SIGMOID: + return tf.sigmoid + if score_converter_config == post_processing_pb2.PostProcessing.SOFTMAX: + return tf.nn.softmax + raise ValueError('Unknown score converter.') diff --git a/object_detection/builders/post_processing_builder_test.py b/object_detection/builders/post_processing_builder_test.py new file mode 100644 index 000000000..514ce6d2b --- /dev/null +++ b/object_detection/builders/post_processing_builder_test.py @@ -0,0 +1,73 @@ +# 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 as tf +from google.protobuf import text_format +from object_detection.builders import post_processing_builder +from object_detection.protos import post_processing_pb2 + + +class PostProcessingBuilderTest(tf.test.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 + } + """ + 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) + + 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, tf.identity) + + 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, tf.sigmoid) + + 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, tf.nn.softmax) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/builders/preprocessor_builder.py b/object_detection/builders/preprocessor_builder.py new file mode 100644 index 000000000..d88b31b23 --- /dev/null +++ b/object_detection/builders/preprocessor_builder.py @@ -0,0 +1,277 @@ +# 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 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_horizontal_flip': preprocessor.random_horizontal_flip, + '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, + 'scale_boxes_to_pixel_coordinates': ( + preprocessor.scale_boxes_to_pixel_coordinates), + 'subtract_channel_mean': preprocessor.subtract_channel_mean, +} + + +# 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_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, + '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 + if pad_color and len(pad_color) != 3: + raise ValueError('pad_color should have 3 elements (RGB) if set!') + if not pad_color: + pad_color = None + 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_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 3 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 3 elements if set!') + pad_color = config.pad_color + if pad_color and len(pad_color) != 3: + raise ValueError('pad_color should have 3 elements if set!') + return (preprocessor.random_crop_pad_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, + 'random_coef': config.random_coef, + 'min_padded_size_ratio': (min_padded_size_ratio if + min_padded_size_ratio else None), + 'max_padded_size_ratio': (max_padded_size_ratio if + max_padded_size_ratio else None), + 'pad_color': (pad_color if pad_color else None), + }) + + 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 == '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] + 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, + 'random_coef': random_coef, + }) + return (preprocessor.ssd_random_crop, {}) + + 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] + random_coef = [op.random_coef for op in config.operations] + min_padded_size_ratio = [ + (op.min_padded_size_ratio[0], op.min_padded_size_ratio[1]) + for op in config.operations] + max_padded_size_ratio = [ + (op.max_padded_size_ratio[0], op.max_padded_size_ratio[1]) + 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, + '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] + 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, + 'random_coef': random_coef, + }) + return (preprocessor.ssd_random_crop_fixed_aspect_ratio, {}) + + raise ValueError('Unknown preprocessing step.') diff --git a/object_detection/builders/preprocessor_builder_test.py b/object_detection/builders/preprocessor_builder_test.py new file mode 100644 index 000000000..8f8ba253d --- /dev/null +++ b/object_detection/builders/preprocessor_builder_test.py @@ -0,0 +1,452 @@ +# 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 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 { + } + """ + 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, {}) + + 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 + 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, + '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_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 + 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, + 'random_coef': 0.125, + 'min_padded_size_ratio': None, + 'max_padded_size_ratio': None, + '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 + } + """ + 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}) + + 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_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_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_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 + 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 + 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], + '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 + random_coef: 0.375 + min_padded_size_ratio: [0.0, 0.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 + random_coef: 0.375 + min_padded_size_ratio: [0.0, 0.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], + 'random_coef': [0.375, 0.375], + 'min_padded_size_ratio': [(0.0, 0.0), (0.0, 0.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 + random_coef: 0.375 + } + operations { + min_object_covered: 0.25 + min_area: 0.5 + max_area: 1.0 + overlap_thresh: 0.25 + 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], + 'random_coef': [0.375, 0.375]}) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/builders/region_similarity_calculator_builder.py b/object_detection/builders/region_similarity_calculator_builder.py new file mode 100644 index 000000000..fa1d67175 --- /dev/null +++ b/object_detection/builders/region_similarity_calculator_builder.py @@ -0,0 +1,56 @@ +# 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() + + raise ValueError('Unknown region similarity calculator.') + diff --git a/object_detection/builders/region_similarity_calculator_builder_test.py b/object_detection/builders/region_similarity_calculator_builder_test.py new file mode 100644 index 000000000..ca3a5512e --- /dev/null +++ b/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 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/object_detection/core/BUILD b/object_detection/core/BUILD new file mode 100644 index 000000000..a3384bcc0 --- /dev/null +++ b/object_detection/core/BUILD @@ -0,0 +1,362 @@ +# Tensorflow Object Detection API: Core. + +package( + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) +# Apache 2.0 + +py_library( + name = "batcher", + srcs = ["batcher.py"], + deps = [ + ":prefetcher", + ":preprocessor", + ":standard_fields", + "//tensorflow", + ], +) + +py_test( + name = "batcher_test", + srcs = ["batcher_test.py"], + deps = [ + ":batcher", + "//tensorflow", + ], +) + +py_library( + name = "box_list", + srcs = [ + "box_list.py", + ], + deps = [ + "//tensorflow", + ], +) + +py_test( + name = "box_list_test", + srcs = ["box_list_test.py"], + deps = [ + ":box_list", + ], +) + +py_library( + name = "box_list_ops", + srcs = [ + "box_list_ops.py", + ], + deps = [ + ":box_list", + "//tensorflow", + "//tensorflow_models/object_detection/utils:shape_utils", + ], +) + +py_test( + name = "box_list_ops_test", + srcs = ["box_list_ops_test.py"], + deps = [ + ":box_list", + ":box_list_ops", + ], +) + +py_library( + name = "box_coder", + srcs = [ + "box_coder.py", + ], + deps = [ + "//tensorflow", + ], +) + +py_test( + name = "box_coder_test", + srcs = [ + "box_coder_test.py", + ], + deps = [ + ":box_coder", + ":box_list", + "//tensorflow", + ], +) + +py_library( + name = "keypoint_ops", + srcs = [ + "keypoint_ops.py", + ], + deps = [ + "//tensorflow", + ], +) + +py_test( + name = "keypoint_ops_test", + srcs = ["keypoint_ops_test.py"], + deps = [ + ":keypoint_ops", + ], +) + +py_library( + name = "losses", + srcs = ["losses.py"], + deps = [ + ":box_list", + ":box_list_ops", + "//tensorflow", + "//tensorflow_models/object_detection/utils:ops", + ], +) + +py_library( + name = "matcher", + srcs = [ + "matcher.py", + ], + deps = [ + ], +) + +py_library( + name = "model", + srcs = ["model.py"], + deps = [ + ":standard_fields", + ], +) + +py_test( + name = "matcher_test", + srcs = [ + "matcher_test.py", + ], + deps = [ + ":matcher", + "//tensorflow", + ], +) + +py_library( + name = "prefetcher", + srcs = ["prefetcher.py"], + deps = ["//tensorflow"], +) + +py_library( + name = "preprocessor", + srcs = [ + "preprocessor.py", + ], + deps = [ + ":box_list", + ":box_list_ops", + ":keypoint_ops", + ":standard_fields", + "//tensorflow", + ], +) + +py_test( + name = "preprocessor_test", + srcs = [ + "preprocessor_test.py", + ], + deps = [ + ":preprocessor", + "//tensorflow", + ], +) + +py_test( + name = "losses_test", + srcs = ["losses_test.py"], + deps = [ + ":box_list", + ":losses", + ":matcher", + "//tensorflow", + ], +) + +py_test( + name = "prefetcher_test", + srcs = ["prefetcher_test.py"], + deps = [ + ":prefetcher", + "//tensorflow", + ], +) + +py_library( + name = "standard_fields", + srcs = [ + "standard_fields.py", + ], +) + +py_library( + name = "post_processing", + srcs = ["post_processing.py"], + deps = [ + ":box_list", + ":box_list_ops", + ":standard_fields", + "//tensorflow", + ], +) + +py_test( + name = "post_processing_test", + srcs = ["post_processing_test.py"], + deps = [ + ":box_list", + ":box_list_ops", + ":post_processing", + "//tensorflow", + ], +) + +py_library( + name = "target_assigner", + srcs = [ + "target_assigner.py", + ], + deps = [ + ":box_list", + ":box_list_ops", + ":matcher", + ":region_similarity_calculator", + "//tensorflow", + "//tensorflow_models/object_detection/box_coders:faster_rcnn_box_coder", + "//tensorflow_models/object_detection/box_coders:mean_stddev_box_coder", + "//tensorflow_models/object_detection/core:box_coder", + "//tensorflow_models/object_detection/matchers:argmax_matcher", + "//tensorflow_models/object_detection/matchers:bipartite_matcher", + ], +) + +py_test( + name = "target_assigner_test", + size = "large", + timeout = "long", + srcs = ["target_assigner_test.py"], + deps = [ + ":box_list", + ":region_similarity_calculator", + ":target_assigner", + "//tensorflow", + "//tensorflow_models/object_detection/box_coders:mean_stddev_box_coder", + "//tensorflow_models/object_detection/matchers:bipartite_matcher", + ], +) + +py_library( + name = "data_decoder", + srcs = ["data_decoder.py"], +) + +py_library( + name = "box_predictor", + srcs = ["box_predictor.py"], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/utils:ops", + "//tensorflow_models/object_detection/utils:static_shape", + ], +) + +py_test( + name = "box_predictor_test", + srcs = ["box_predictor_test.py"], + deps = [ + ":box_predictor", + "//tensorflow", + "//tensorflow_models/object_detection/builders:hyperparams_builder", + "//tensorflow_models/object_detection/protos:hyperparams_py_pb2", + ], +) + +py_library( + name = "region_similarity_calculator", + srcs = [ + "region_similarity_calculator.py", + ], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/core:box_list_ops", + ], +) + +py_test( + name = "region_similarity_calculator_test", + srcs = [ + "region_similarity_calculator_test.py", + ], + deps = [ + ":region_similarity_calculator", + "//tensorflow_models/object_detection/core:box_list", + ], +) + +py_library( + name = "anchor_generator", + srcs = [ + "anchor_generator.py", + ], + deps = [ + "//tensorflow", + ], +) + +py_library( + name = "minibatch_sampler", + srcs = [ + "minibatch_sampler.py", + ], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/utils:ops", + ], +) + +py_test( + name = "minibatch_sampler_test", + srcs = [ + "minibatch_sampler_test.py", + ], + deps = [ + ":minibatch_sampler", + "//tensorflow", + ], +) + +py_library( + name = "balanced_positive_negative_sampler", + srcs = [ + "balanced_positive_negative_sampler.py", + ], + deps = [ + ":minibatch_sampler", + "//tensorflow", + ], +) + +py_test( + name = "balanced_positive_negative_sampler_test", + srcs = [ + "balanced_positive_negative_sampler_test.py", + ], + deps = [ + ":balanced_positive_negative_sampler", + "//tensorflow", + ], +) diff --git a/object_detection/core/__init__.py b/object_detection/core/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/object_detection/core/anchor_generator.py b/object_detection/core/anchor_generator.py new file mode 100644 index 000000000..ed6a2bc54 --- /dev/null +++ b/object_detection/core/anchor_generator.py @@ -0,0 +1,142 @@ +# 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 abc import ABCMeta +from abc import abstractmethod + +import tensorflow as tf + + +class AnchorGenerator(object): + """Abstract base class for anchor generators.""" + __metaclass__ = ABCMeta + + @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: 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: a BoxList holding a collection of N anchor boxes + 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 = self._generate(feature_map_shape_list, **params) + if self.check_num_anchors: + with tf.control_dependencies([ + self._assert_correct_number_of_anchors( + anchors, feature_map_shape_list)]): + anchors.set(tf.identity(anchors.get())) + return anchors + + @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: a BoxList holding a collection of N anchor boxes + """ + pass + + def _assert_correct_number_of_anchors(self, anchors, feature_map_shape_list): + """Assert that correct number of anchors was generated. + + Args: + anchors: 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 + for num_anchors_per_location, feature_map_shape in zip( + self.num_anchors_per_location(), feature_map_shape_list): + expected_num_anchors += (num_anchors_per_location + * feature_map_shape[0] + * feature_map_shape[1]) + return tf.assert_equal(expected_num_anchors, anchors.num_boxes()) diff --git a/object_detection/core/balanced_positive_negative_sampler.py b/object_detection/core/balanced_positive_negative_sampler.py new file mode 100644 index 000000000..68844c4f9 --- /dev/null +++ b/object_detection/core/balanced_positive_negative_sampler.py @@ -0,0 +1,92 @@ +# 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. +# ============================================================================== + +"""Class to subsample minibatches by balancing positives and negatives. + +Subsamples minibatches based on a pre-specified positive fraction in range +[0,1]. The class presumes there are many more negatives than positive examples: +if the desired batch_size cannot be achieved with the pre-specified positive +fraction, it fills the rest with negative examples. If this is not sufficient +for obtaining the desired batch_size, it returns fewer examples. + +The main function to call is Subsample(self, indicator, labels). For convenience +one can also call SubsampleWeights(self, weights, labels) which is defined in +the minibatch_sampler base class. +""" + +import tensorflow as tf + +from object_detection.core import minibatch_sampler + + +class BalancedPositiveNegativeSampler(minibatch_sampler.MinibatchSampler): + """Subsamples minibatches to a desired balance of positives and negatives.""" + + def __init__(self, positive_fraction=0.5): + """Constructs a minibatch sampler. + + Args: + positive_fraction: desired fraction of positive examples (scalar in [0,1]) + + Raises: + ValueError: if positive_fraction < 0, or positive_fraction > 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 + + def subsample(self, indicator, batch_size, labels): + """Returns subsampled minibatch. + + Args: + indicator: boolean tensor of shape [N] whose True entries can be sampled. + batch_size: desired batch size. + labels: boolean tensor of shape [N] denoting positive(=True) and negative + (=False) examples. + + Returns: + is_sampled: 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) + + # 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 + max_num_pos = int(self._positive_fraction * batch_size) + sampled_pos_idx = self.subsample_indicator(positive_idx, max_num_pos) + max_num_neg = batch_size - tf.reduce_sum(tf.cast(sampled_pos_idx, tf.int32)) + sampled_neg_idx = self.subsample_indicator(negative_idx, max_num_neg) + + sampled_idx = tf.logical_or(sampled_pos_idx, sampled_neg_idx) + return sampled_idx diff --git a/object_detection/core/balanced_positive_negative_sampler_test.py b/object_detection/core/balanced_positive_negative_sampler_test.py new file mode 100644 index 000000000..23991cf56 --- /dev/null +++ b/object_detection/core/balanced_positive_negative_sampler_test.py @@ -0,0 +1,83 @@ +# 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 as tf + +from object_detection.core import balanced_positive_negative_sampler + + +class BalancedPositiveNegativeSamplerTest(tf.test.TestCase): + + def test_subsample_all_examples(self): + numpy_labels = np.random.permutation(300) + indicator = tf.constant(np.ones(300) == 1) + numpy_labels = (numpy_labels - 200) > 0 + + labels = tf.constant(numpy_labels) + + sampler = (balanced_positive_negative_sampler. + BalancedPositiveNegativeSampler()) + is_sampled = sampler.subsample(indicator, 64, labels) + with self.test_session() as sess: + is_sampled = sess.run(is_sampled) + self.assertTrue(sum(is_sampled) == 64) + self.assertTrue(sum(np.logical_and(numpy_labels, is_sampled)) == 32) + self.assertTrue(sum(np.logical_and( + np.logical_not(numpy_labels), is_sampled)) == 32) + + def test_subsample_selection(self): + # 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 = tf.constant(numpy_indicator) + numpy_labels = (numpy_labels - 80) >= 0 + + labels = tf.constant(numpy_labels) + + sampler = (balanced_positive_negative_sampler. + BalancedPositiveNegativeSampler()) + is_sampled = sampler.subsample(indicator, 64, labels) + with self.test_session() as sess: + is_sampled = sess.run(is_sampled) + self.assertTrue(sum(is_sampled) == 64) + self.assertTrue(sum(np.logical_and(numpy_labels, is_sampled)) == 10) + self.assertTrue(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_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/object_detection/core/batcher.py b/object_detection/core/batcher.py new file mode 100644 index 000000000..fdd698c43 --- /dev/null +++ b/object_detection/core/batcher.py @@ -0,0 +1,133 @@ +# 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.""" +import collections + +import tensorflow as tf + +from object_detection.core import prefetcher + + +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.iteritems()}) + # Remember runtime shapes to unpad tensors after batching. + runtime_shapes = collections.OrderedDict( + {(key, 'runtime_shapes'): tf.shape(tensor) + for key, tensor in tensor_dict.iteritems()}) + 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.iteritems(): + unbatched_tensor_list = tf.unstack(batched_tensor) + for i, unbatched_tensor in enumerate(unbatched_tensor_list): + if isinstance(key, tuple) and key[1] == 'runtime_shapes': + shapes[(key[0], 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/object_detection/core/batcher_test.py b/object_detection/core/batcher_test.py new file mode 100644 index 000000000..61b4390b4 --- /dev/null +++ b/object_detection/core/batcher_test.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. +# ============================================================================== + +"""Tests for object_detection.core.batcher.""" + +import numpy as np +import tensorflow as tf + +from object_detection.core import batcher + +slim = tf.contrib.slim + + +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/object_detection/core/box_coder.py b/object_detection/core/box_coder.py new file mode 100644 index 000000000..f20ac956d --- /dev/null +++ b/object_detection/core/box_coder.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. +# ============================================================================== + +"""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 abc import ABCMeta +from abc import abstractmethod +from abc import abstractproperty + +import tensorflow as tf + + +# Box coder types. +FASTER_RCNN = 'faster_rcnn' +KEYPOINT = 'keypoint' +MEAN_STDDEV = 'mean_stddev' +SQUARE = 'square' + + +class BoxCoder(object): + """Abstract base class for box coder.""" + __metaclass__ = ABCMeta + + @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 encoded_boxes.get_shape()[1].value != 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.' % + (encoded_boxes.get_shape()[1].value, + 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/object_detection/core/box_coder_test.py b/object_detection/core/box_coder_test.py new file mode 100644 index 000000000..c087a3252 --- /dev/null +++ b/object_detection/core/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.core.box_coder.""" + +import tensorflow as tf + +from object_detection.core import box_coder +from object_detection.core import box_list + + +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(tf.test.TestCase): + + def test_batch_decode(self): + 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() + + 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]]] + + 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) + + with self.test_session() as sess: + decoded_boxes_result = sess.run(decoded_boxes) + self.assertAllClose(expected_boxes, decoded_boxes_result) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/core/box_list.py b/object_detection/core/box_list.py new file mode 100644 index 000000000..c0196f053 --- /dev/null +++ b/object_detection/core/box_list.py @@ -0,0 +1,207 @@ +# 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 as tf + + +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.') + 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 self.data['boxes'].get_shape()[0].value + + 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/object_detection/core/box_list_ops.py b/object_detection/core/box_list_ops.py new file mode 100644 index 000000000..b083fabfb --- /dev/null +++ b/object_detection/core/box_list_ops.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. +# ============================================================================== + +"""Bounding Box List operations. + +Example box operations that are supported: + * areas: compute bounding box areas + * iou: pairwise intersection-over-union scores + * sq_dist: pairwise distances between bounding boxes + +Whenever box_list_ops functions output a BoxList, the fields of the incoming +BoxList are retained unless documented otherwise. +""" +import tensorflow as tf + +from object_detection.core import box_list +from object_detection.utils import shape_utils + + +class SortOrder(object): + """Enum class for sort order. + + Attributes: + ascend: ascend order. + descend: descend order. + """ + ascend = 1 + descend = 2 + + +def area(boxlist, scope=None): + """Computes area of boxes. + + Args: + boxlist: BoxList holding N boxes + scope: name scope. + + Returns: + a tensor with shape [N] representing box areas. + """ + with tf.name_scope(scope, 'Area'): + y_min, x_min, y_max, x_max = tf.split( + value=boxlist.get(), num_or_size_splits=4, axis=1) + return tf.squeeze((y_max - y_min) * (x_max - x_min), [1]) + + +def height_width(boxlist, scope=None): + """Computes height and width of boxes in boxlist. + + Args: + boxlist: BoxList holding N boxes + scope: name scope. + + Returns: + Height: A tensor with shape [N] representing box heights. + Width: A tensor with shape [N] representing box widths. + """ + with tf.name_scope(scope, 'HeightWidth'): + y_min, x_min, y_max, x_max = tf.split( + value=boxlist.get(), num_or_size_splits=4, axis=1) + return tf.squeeze(y_max - y_min, [1]), tf.squeeze(x_max - x_min, [1]) + + +def scale(boxlist, y_scale, x_scale, scope=None): + """scale box coordinates in x and y dimensions. + + Args: + boxlist: BoxList holding N boxes + y_scale: (float) scalar tensor + x_scale: (float) scalar tensor + scope: name scope. + + Returns: + boxlist: BoxList holding N boxes + """ + with tf.name_scope(scope, 'Scale'): + y_scale = tf.cast(y_scale, tf.float32) + x_scale = tf.cast(x_scale, tf.float32) + y_min, x_min, y_max, x_max = tf.split( + value=boxlist.get(), num_or_size_splits=4, axis=1) + y_min = y_scale * y_min + y_max = y_scale * y_max + x_min = x_scale * x_min + x_max = x_scale * x_max + scaled_boxlist = box_list.BoxList( + tf.concat([y_min, x_min, y_max, x_max], 1)) + return _copy_extra_fields(scaled_boxlist, boxlist) + + +def clip_to_window(boxlist, window, filter_nonoverlapping=True, scope=None): + """Clip bounding boxes to a window. + + This op clips any input bounding boxes (represented by bounding box + corners) to a window, optionally filtering out boxes that do not + overlap at all with the window. + + Args: + boxlist: BoxList holding M_in boxes + window: a tensor of shape [4] representing the [y_min, x_min, y_max, x_max] + window to which the op should clip boxes. + filter_nonoverlapping: whether to filter out boxes that do not overlap at + all with the window. + scope: name scope. + + Returns: + a BoxList holding M_out boxes where M_out <= M_in + """ + with tf.name_scope(scope, 'ClipToWindow'): + y_min, x_min, y_max, x_max = tf.split( + value=boxlist.get(), num_or_size_splits=4, axis=1) + win_y_min, win_x_min, win_y_max, win_x_max = tf.unstack(window) + y_min_clipped = tf.maximum(tf.minimum(y_min, win_y_max), win_y_min) + y_max_clipped = tf.maximum(tf.minimum(y_max, win_y_max), win_y_min) + x_min_clipped = tf.maximum(tf.minimum(x_min, win_x_max), win_x_min) + x_max_clipped = tf.maximum(tf.minimum(x_max, win_x_max), win_x_min) + clipped = box_list.BoxList( + tf.concat([y_min_clipped, x_min_clipped, y_max_clipped, x_max_clipped], + 1)) + clipped = _copy_extra_fields(clipped, boxlist) + if filter_nonoverlapping: + areas = area(clipped) + nonzero_area_indices = tf.cast( + tf.reshape(tf.where(tf.greater(areas, 0.0)), [-1]), tf.int32) + clipped = gather(clipped, nonzero_area_indices) + return clipped + + +def prune_outside_window(boxlist, window, scope=None): + """Prunes bounding boxes that fall outside a given window. + + This function prunes bounding boxes that even partially fall outside the given + window. See also clip_to_window which only prunes bounding boxes that fall + completely outside the window, and clips any bounding boxes that partially + overflow. + + Args: + boxlist: a BoxList holding M_in boxes. + window: a float tensor of shape [4] representing [ymin, xmin, ymax, xmax] + of the window + scope: name scope. + + Returns: + pruned_corners: a tensor with shape [M_out, 4] where M_out <= M_in + valid_indices: a tensor with shape [M_out] indexing the valid bounding boxes + in the input tensor. + """ + with tf.name_scope(scope, 'PruneOutsideWindow'): + y_min, x_min, y_max, x_max = tf.split( + value=boxlist.get(), num_or_size_splits=4, axis=1) + win_y_min, win_x_min, win_y_max, win_x_max = tf.unstack(window) + coordinate_violations = tf.concat([ + tf.less(y_min, win_y_min), tf.less(x_min, win_x_min), + tf.greater(y_max, win_y_max), tf.greater(x_max, win_x_max) + ], 1) + valid_indices = tf.reshape( + tf.where(tf.logical_not(tf.reduce_any(coordinate_violations, 1))), [-1]) + return gather(boxlist, valid_indices), valid_indices + + +def prune_completely_outside_window(boxlist, window, scope=None): + """Prunes bounding boxes that fall completely outside of the given window. + + The function clip_to_window prunes bounding boxes that fall + completely outside the window, but also clips any bounding boxes that + partially overflow. This function does not clip partially overflowing boxes. + + Args: + boxlist: a BoxList holding M_in boxes. + window: a float tensor of shape [4] representing [ymin, xmin, ymax, xmax] + of the window + scope: name scope. + + Returns: + pruned_corners: a tensor with shape [M_out, 4] where M_out <= M_in + valid_indices: a tensor with shape [M_out] indexing the valid bounding boxes + in the input tensor. + """ + with tf.name_scope(scope, 'PruneCompleteleyOutsideWindow'): + y_min, x_min, y_max, x_max = tf.split( + value=boxlist.get(), num_or_size_splits=4, axis=1) + win_y_min, win_x_min, win_y_max, win_x_max = tf.unstack(window) + coordinate_violations = tf.concat([ + tf.greater_equal(y_min, win_y_max), tf.greater_equal(x_min, win_x_max), + tf.less_equal(y_max, win_y_min), tf.less_equal(x_max, win_x_min) + ], 1) + valid_indices = tf.reshape( + tf.where(tf.logical_not(tf.reduce_any(coordinate_violations, 1))), [-1]) + return gather(boxlist, valid_indices), valid_indices + + +def intersection(boxlist1, boxlist2, scope=None): + """Compute pairwise intersection areas between boxes. + + Args: + boxlist1: BoxList holding N boxes + boxlist2: BoxList holding M boxes + scope: name scope. + + Returns: + a tensor with shape [N, M] representing pairwise intersections + """ + with tf.name_scope(scope, 'Intersection'): + y_min1, x_min1, y_max1, x_max1 = tf.split( + value=boxlist1.get(), num_or_size_splits=4, axis=1) + y_min2, x_min2, y_max2, x_max2 = tf.split( + value=boxlist2.get(), num_or_size_splits=4, axis=1) + all_pairs_min_ymax = tf.minimum(y_max1, tf.transpose(y_max2)) + all_pairs_max_ymin = tf.maximum(y_min1, tf.transpose(y_min2)) + intersect_heights = tf.maximum(0.0, all_pairs_min_ymax - all_pairs_max_ymin) + all_pairs_min_xmax = tf.minimum(x_max1, tf.transpose(x_max2)) + all_pairs_max_xmin = tf.maximum(x_min1, tf.transpose(x_min2)) + intersect_widths = tf.maximum(0.0, all_pairs_min_xmax - all_pairs_max_xmin) + return intersect_heights * intersect_widths + + +def matched_intersection(boxlist1, boxlist2, scope=None): + """Compute intersection areas between corresponding boxes in two boxlists. + + Args: + boxlist1: BoxList holding N boxes + boxlist2: BoxList holding N boxes + scope: name scope. + + Returns: + a tensor with shape [N] representing pairwise intersections + """ + with tf.name_scope(scope, 'MatchedIntersection'): + y_min1, x_min1, y_max1, x_max1 = tf.split( + value=boxlist1.get(), num_or_size_splits=4, axis=1) + y_min2, x_min2, y_max2, x_max2 = tf.split( + value=boxlist2.get(), num_or_size_splits=4, axis=1) + min_ymax = tf.minimum(y_max1, y_max2) + max_ymin = tf.maximum(y_min1, y_min2) + intersect_heights = tf.maximum(0.0, min_ymax - max_ymin) + min_xmax = tf.minimum(x_max1, x_max2) + max_xmin = tf.maximum(x_min1, x_min2) + intersect_widths = tf.maximum(0.0, min_xmax - max_xmin) + return tf.reshape(intersect_heights * intersect_widths, [-1]) + + +def iou(boxlist1, boxlist2, scope=None): + """Computes pairwise intersection-over-union between box collections. + + Args: + boxlist1: BoxList holding N boxes + boxlist2: BoxList holding M boxes + scope: name scope. + + Returns: + a tensor with shape [N, M] representing pairwise iou scores. + """ + with tf.name_scope(scope, 'IOU'): + intersections = intersection(boxlist1, boxlist2) + areas1 = area(boxlist1) + areas2 = area(boxlist2) + unions = ( + tf.expand_dims(areas1, 1) + tf.expand_dims(areas2, 0) - intersections) + return tf.where( + tf.equal(intersections, 0.0), + tf.zeros_like(intersections), tf.truediv(intersections, unions)) + + +def matched_iou(boxlist1, boxlist2, scope=None): + """Compute intersection-over-union between corresponding boxes in boxlists. + + Args: + boxlist1: BoxList holding N boxes + boxlist2: BoxList holding N boxes + scope: name scope. + + Returns: + a tensor with shape [N] representing pairwise iou scores. + """ + with tf.name_scope(scope, 'MatchedIOU'): + intersections = matched_intersection(boxlist1, boxlist2) + areas1 = area(boxlist1) + areas2 = area(boxlist2) + unions = areas1 + areas2 - intersections + return tf.where( + tf.equal(intersections, 0.0), + tf.zeros_like(intersections), tf.truediv(intersections, unions)) + + +def ioa(boxlist1, boxlist2, scope=None): + """Computes pairwise intersection-over-area between box collections. + + intersection-over-area (IOA) between two boxes box1 and box2 is defined as + their intersection area over box2's area. Note that ioa is not symmetric, + that is, ioa(box1, box2) != ioa(box2, box1). + + Args: + boxlist1: BoxList holding N boxes + boxlist2: BoxList holding M boxes + scope: name scope. + + Returns: + a tensor with shape [N, M] representing pairwise ioa scores. + """ + with tf.name_scope(scope, 'IOA'): + intersections = intersection(boxlist1, boxlist2) + areas = tf.expand_dims(area(boxlist2), 0) + return tf.truediv(intersections, areas) + + +def prune_non_overlapping_boxes( + boxlist1, boxlist2, min_overlap=0.0, scope=None): + """Prunes the boxes in boxlist1 that overlap less than thresh with boxlist2. + + For each box in boxlist1, we want its IOA to be more than minoverlap with + at least one of the boxes in boxlist2. If it does not, we remove it. + + Args: + boxlist1: BoxList holding N boxes. + boxlist2: BoxList holding M boxes. + min_overlap: Minimum required overlap between boxes, to count them as + overlapping. + scope: name scope. + + Returns: + new_boxlist1: A pruned boxlist with size [N', 4]. + keep_inds: A tensor with shape [N'] indexing kept bounding boxes in the + first input BoxList `boxlist1`. + """ + with tf.name_scope(scope, 'PruneNonOverlappingBoxes'): + ioa_ = ioa(boxlist2, boxlist1) # [M, N] tensor + ioa_ = tf.reduce_max(ioa_, reduction_indices=[0]) # [N] tensor + keep_bool = tf.greater_equal(ioa_, tf.constant(min_overlap)) + keep_inds = tf.squeeze(tf.where(keep_bool), squeeze_dims=[1]) + new_boxlist1 = gather(boxlist1, keep_inds) + return new_boxlist1, keep_inds + + +def prune_small_boxes(boxlist, min_side, scope=None): + """Prunes small boxes in the boxlist which have a side smaller than min_side. + + Args: + boxlist: BoxList holding N boxes. + min_side: Minimum width AND height of box to survive pruning. + scope: name scope. + + Returns: + A pruned boxlist. + """ + with tf.name_scope(scope, 'PruneSmallBoxes'): + height, width = height_width(boxlist) + is_valid = tf.logical_and(tf.greater_equal(width, min_side), + tf.greater_equal(height, min_side)) + return gather(boxlist, tf.reshape(tf.where(is_valid), [-1])) + + +def change_coordinate_frame(boxlist, window, scope=None): + """Change coordinate frame of the boxlist to be relative to window's frame. + + Given a window of the form [ymin, xmin, ymax, xmax], + changes bounding box coordinates from boxlist to be relative to this window + (e.g., the min corner maps to (0,0) and the max corner maps to (1,1)). + + An example use case is data augmentation: where we are given groundtruth + boxes (boxlist) and would like to randomly crop the image to some + window (window). In this case we need to change the coordinate frame of + each groundtruth box to be relative to this new window. + + Args: + boxlist: A BoxList object holding N boxes. + window: A rank 1 tensor [4]. + scope: name scope. + + Returns: + Returns a BoxList object with N boxes. + """ + with tf.name_scope(scope, 'ChangeCoordinateFrame'): + win_height = window[2] - window[0] + win_width = window[3] - window[1] + boxlist_new = scale(box_list.BoxList( + boxlist.get() - [window[0], window[1], window[0], window[1]]), + 1.0 / win_height, 1.0 / win_width) + boxlist_new = _copy_extra_fields(boxlist_new, boxlist) + return boxlist_new + + +def sq_dist(boxlist1, boxlist2, scope=None): + """Computes the pairwise squared distances between box corners. + + This op treats each box as if it were a point in a 4d Euclidean space and + computes pairwise squared distances. + + Mathematically, we are given two matrices of box coordinates X and Y, + where X(i,:) is the i'th row of X, containing the 4 numbers defining the + corners of the i'th box in boxlist1. Similarly Y(j,:) corresponds to + boxlist2. We compute + Z(i,j) = ||X(i,:) - Y(j,:)||^2 + = ||X(i,:)||^2 + ||Y(j,:)||^2 - 2 X(i,:)' * Y(j,:), + + Args: + boxlist1: BoxList holding N boxes + boxlist2: BoxList holding M boxes + scope: name scope. + + Returns: + a tensor with shape [N, M] representing pairwise distances + """ + with tf.name_scope(scope, 'SqDist'): + sqnorm1 = tf.reduce_sum(tf.square(boxlist1.get()), 1, keep_dims=True) + sqnorm2 = tf.reduce_sum(tf.square(boxlist2.get()), 1, keep_dims=True) + innerprod = tf.matmul(boxlist1.get(), boxlist2.get(), + transpose_a=False, transpose_b=True) + return sqnorm1 + tf.transpose(sqnorm2) - 2.0 * innerprod + + +def boolean_mask(boxlist, indicator, fields=None, scope=None): + """Select boxes from BoxList according to indicator and return new BoxList. + + `boolean_mask` returns the subset of boxes that are marked as "True" by the + indicator tensor. By default, `boolean_mask` returns boxes corresponding to + the input index list, as well as all additional fields stored in the boxlist + (indexing into the first dimension). However one can optionally only draw + from a subset of fields. + + Args: + boxlist: BoxList holding N boxes + indicator: a rank-1 boolean tensor + fields: (optional) list of fields to also gather from. If None (default), + all fields are gathered from. Pass an empty fields list to only gather + the box coordinates. + scope: name scope. + + Returns: + subboxlist: a BoxList corresponding to the subset of the input BoxList + specified by indicator + Raises: + ValueError: if `indicator` is not a rank-1 boolean tensor. + """ + with tf.name_scope(scope, 'BooleanMask'): + if indicator.shape.ndims != 1: + raise ValueError('indicator should have rank 1') + if indicator.dtype != tf.bool: + raise ValueError('indicator should be a boolean tensor') + subboxlist = box_list.BoxList(tf.boolean_mask(boxlist.get(), indicator)) + if fields is None: + fields = boxlist.get_extra_fields() + for field in fields: + if not boxlist.has_field(field): + raise ValueError('boxlist must contain all specified fields') + subfieldlist = tf.boolean_mask(boxlist.get_field(field), indicator) + subboxlist.add_field(field, subfieldlist) + return subboxlist + + +def gather(boxlist, indices, fields=None, scope=None): + """Gather boxes from BoxList according to indices and return new BoxList. + + By default, `gather` returns boxes corresponding to the input index list, as + well as all additional fields stored in the boxlist (indexing into the + first dimension). However one can optionally only gather from a + subset of fields. + + Args: + boxlist: BoxList holding N boxes + indices: a rank-1 tensor of type int32 / int64 + fields: (optional) list of fields to also gather from. If None (default), + all fields are gathered from. Pass an empty fields list to only gather + the box coordinates. + scope: name scope. + + Returns: + subboxlist: a BoxList corresponding to the subset of the input BoxList + specified by indices + Raises: + ValueError: if specified field is not contained in boxlist or if the + indices are not of type int32 + """ + with tf.name_scope(scope, 'Gather'): + if len(indices.shape.as_list()) != 1: + raise ValueError('indices should have rank 1') + if indices.dtype != tf.int32 and indices.dtype != tf.int64: + raise ValueError('indices should be an int32 / int64 tensor') + subboxlist = box_list.BoxList(tf.gather(boxlist.get(), indices)) + if fields is None: + fields = boxlist.get_extra_fields() + for field in fields: + if not boxlist.has_field(field): + raise ValueError('boxlist must contain all specified fields') + subfieldlist = tf.gather(boxlist.get_field(field), indices) + subboxlist.add_field(field, subfieldlist) + return subboxlist + + +def concatenate(boxlists, fields=None, scope=None): + """Concatenate list of BoxLists. + + This op concatenates a list of input BoxLists into a larger BoxList. It also + handles concatenation of BoxList fields as long as the field tensor shapes + are equal except for the first dimension. + + Args: + boxlists: list of BoxList objects + fields: optional list of fields to also concatenate. By default, all + fields from the first BoxList in the list are included in the + concatenation. + scope: name scope. + + Returns: + a BoxList with number of boxes equal to + sum([boxlist.num_boxes() for boxlist in BoxList]) + Raises: + ValueError: if boxlists is invalid (i.e., is not a list, is empty, or + contains non BoxList objects), or if requested fields are not contained in + all boxlists + """ + with tf.name_scope(scope, 'Concatenate'): + if not isinstance(boxlists, list): + raise ValueError('boxlists should be a list') + if not boxlists: + raise ValueError('boxlists should have nonzero length') + for boxlist in boxlists: + if not isinstance(boxlist, box_list.BoxList): + raise ValueError('all elements of boxlists should be BoxList objects') + concatenated = box_list.BoxList( + tf.concat([boxlist.get() for boxlist in boxlists], 0)) + if fields is None: + fields = boxlists[0].get_extra_fields() + for field in fields: + first_field_shape = boxlists[0].get_field(field).get_shape().as_list() + first_field_shape[0] = -1 + if None in first_field_shape: + raise ValueError('field %s must have fully defined shape except for the' + ' 0th dimension.' % field) + for boxlist in boxlists: + if not boxlist.has_field(field): + raise ValueError('boxlist must contain all requested fields') + field_shape = boxlist.get_field(field).get_shape().as_list() + field_shape[0] = -1 + if field_shape != first_field_shape: + raise ValueError('field %s must have same shape for all boxlists ' + 'except for the 0th dimension.' % field) + concatenated_field = tf.concat( + [boxlist.get_field(field) for boxlist in boxlists], 0) + concatenated.add_field(field, concatenated_field) + return concatenated + + +def sort_by_field(boxlist, field, order=SortOrder.descend, scope=None): + """Sort boxes and associated fields according to a scalar field. + + A common use case is reordering the boxes according to descending scores. + + Args: + boxlist: BoxList holding N boxes. + field: A BoxList field for sorting and reordering the BoxList. + order: (Optional) descend or ascend. Default is descend. + scope: name scope. + + Returns: + sorted_boxlist: A sorted BoxList with the field in the specified order. + + Raises: + ValueError: if specified field does not exist + ValueError: if the order is not either descend or ascend + """ + with tf.name_scope(scope, 'SortByField'): + if order != SortOrder.descend and order != SortOrder.ascend: + raise ValueError('Invalid sort order') + + field_to_sort = boxlist.get_field(field) + if len(field_to_sort.shape.as_list()) != 1: + raise ValueError('Field should have rank 1') + + num_boxes = boxlist.num_boxes() + num_entries = tf.size(field_to_sort) + length_assert = tf.Assert( + tf.equal(num_boxes, num_entries), + ['Incorrect field size: actual vs expected.', num_entries, num_boxes]) + + with tf.control_dependencies([length_assert]): + # TODO: Remove with tf.device when top_k operation runs correctly on GPU. + with tf.device('/cpu:0'): + _, sorted_indices = tf.nn.top_k(field_to_sort, num_boxes, sorted=True) + + if order == SortOrder.ascend: + sorted_indices = tf.reverse_v2(sorted_indices, [0]) + + return gather(boxlist, sorted_indices) + + +def visualize_boxes_in_image(image, boxlist, normalized=False, scope=None): + """Overlay bounding box list on image. + + Currently this visualization plots a 1 pixel thick red bounding box on top + of the image. Note that tf.image.draw_bounding_boxes essentially is + 1 indexed. + + Args: + image: an image tensor with shape [height, width, 3] + boxlist: a BoxList + normalized: (boolean) specify whether corners are to be interpreted + as absolute coordinates in image space or normalized with respect to the + image size. + scope: name scope. + + Returns: + image_and_boxes: an image tensor with shape [height, width, 3] + """ + with tf.name_scope(scope, 'VisualizeBoxesInImage'): + if not normalized: + height, width, _ = tf.unstack(tf.shape(image)) + boxlist = scale(boxlist, + 1.0 / tf.cast(height, tf.float32), + 1.0 / tf.cast(width, tf.float32)) + corners = tf.expand_dims(boxlist.get(), 0) + image = tf.expand_dims(image, 0) + return tf.squeeze(tf.image.draw_bounding_boxes(image, corners), [0]) + + +def filter_field_value_equals(boxlist, field, value, scope=None): + """Filter to keep only boxes with field entries equal to the given value. + + Args: + boxlist: BoxList holding N boxes. + field: field name for filtering. + value: scalar value. + scope: name scope. + + Returns: + a BoxList holding M boxes where M <= N + + Raises: + ValueError: if boxlist not a BoxList object or if it does not have + the specified field. + """ + with tf.name_scope(scope, 'FilterFieldValueEquals'): + if not isinstance(boxlist, box_list.BoxList): + raise ValueError('boxlist must be a BoxList') + if not boxlist.has_field(field): + raise ValueError('boxlist must contain the specified field') + filter_field = boxlist.get_field(field) + gather_index = tf.reshape(tf.where(tf.equal(filter_field, value)), [-1]) + return gather(boxlist, gather_index) + + +def filter_greater_than(boxlist, thresh, scope=None): + """Filter to keep only boxes with score exceeding a given threshold. + + This op keeps the collection of boxes whose corresponding scores are + greater than the input threshold. + + TODO: Change function name to FilterScoresGreaterThan + + Args: + boxlist: BoxList holding N boxes. Must contain a 'scores' field + representing detection scores. + thresh: scalar threshold + scope: name scope. + + Returns: + a BoxList holding M boxes where M <= N + + Raises: + ValueError: if boxlist not a BoxList object or if it does not + have a scores field + """ + with tf.name_scope(scope, 'FilterGreaterThan'): + 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') + scores = boxlist.get_field('scores') + if len(scores.shape.as_list()) > 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, 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 1.01 (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. + 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(1.01, box_maximum), + ['maximum box coordinate value is larger ' + 'than 1.01: ', 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.to_float(tf.greater(iou_, iou_thresh)) + num_matches = tf.reduce_sum(match_indicator, 1) + # TODO: 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 diff --git a/object_detection/core/box_list_ops_test.py b/object_detection/core/box_list_ops_test.py new file mode 100644 index 000000000..467bb3c67 --- /dev/null +++ b/object_detection/core/box_list_ops_test.py @@ -0,0 +1,962 @@ +# 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 as tf +from tensorflow.python.framework import errors + +from object_detection.core import box_list +from object_detection.core import box_list_ops + + +class BoxListOpsTest(tf.test.TestCase): + """Tests for common bounding box operations.""" + + def test_area(self): + corners = tf.constant([[0.0, 0.0, 10.0, 20.0], [1.0, 2.0, 3.0, 4.0]]) + exp_output = [200.0, 4.0] + boxes = box_list.BoxList(corners) + areas = box_list_ops.area(boxes) + with self.test_session() as sess: + areas_output = sess.run(areas) + self.assertAllClose(areas_output, exp_output) + + def test_height_width(self): + corners = tf.constant([[0.0, 0.0, 10.0, 20.0], [1.0, 2.0, 3.0, 4.0]]) + exp_output_heights = [10., 2.] + exp_output_widths = [20., 2.] + boxes = box_list.BoxList(corners) + heights, widths = box_list_ops.height_width(boxes) + with self.test_session() as sess: + output_heights, output_widths = sess.run([heights, widths]) + self.assertAllClose(output_heights, exp_output_heights) + self.assertAllClose(output_widths, exp_output_widths) + + def test_scale(self): + 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) + exp_output = [[0, 0, 1, 1], [0.5, 0.6, 1.0, 0.7]] + with self.test_session() as sess: + scaled_corners_out = sess.run(scaled_boxes.get()) + self.assertAllClose(scaled_corners_out, exp_output) + extra_data_out = sess.run(scaled_boxes.get_field('extra_data')) + self.assertAllEqual(extra_data_out, [[1], [2]]) + + def test_clip_to_window_filter_boxes_which_fall_outside_the_window( + self): + 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]])) + 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 = box_list_ops.clip_to_window( + boxes, window, filter_nonoverlapping=True) + with self.test_session() as sess: + pruned_output = sess.run(pruned.get()) + self.assertAllClose(pruned_output, exp_output) + extra_data_out = sess.run(pruned.get_field('extra_data')) + self.assertAllEqual(extra_data_out, [[1], [2], [3], [4], [5]]) + + def test_clip_to_window_without_filtering_boxes_which_fall_outside_the_window( + self): + 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]])) + 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]] + pruned = box_list_ops.clip_to_window( + boxes, window, filter_nonoverlapping=False) + with self.test_session() as sess: + pruned_output = sess.run(pruned.get()) + self.assertAllClose(pruned_output, exp_output) + extra_data_out = sess.run(pruned.get_field('extra_data')) + self.assertAllEqual(extra_data_out, [[1], [2], [3], [4], [5], [6]]) + + def test_prune_outside_window_filters_boxes_which_fall_outside_the_window( + self): + 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]])) + 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]] + pruned, keep_indices = box_list_ops.prune_outside_window(boxes, window) + with self.test_session() as sess: + pruned_output = sess.run(pruned.get()) + self.assertAllClose(pruned_output, exp_output) + keep_indices_out = sess.run(keep_indices) + self.assertAllEqual(keep_indices_out, [0, 2, 3]) + extra_data_out = sess.run(pruned.get_field('extra_data')) + self.assertAllEqual(extra_data_out, [[1], [3], [4]]) + + def test_prune_completely_outside_window(self): + 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]])) + 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]] + pruned, keep_indices = box_list_ops.prune_completely_outside_window(boxes, + window) + with self.test_session() as sess: + pruned_output = sess.run(pruned.get()) + self.assertAllClose(pruned_output, exp_output) + keep_indices_out = sess.run(keep_indices) + self.assertAllEqual(keep_indices_out, [0, 1, 2, 3, 5]) + extra_data_out = sess.run(pruned.get_field('extra_data')) + self.assertAllEqual(extra_data_out, [[1], [2], [3], [4], [6]]) + + def test_intersection(self): + 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]]) + exp_output = [[2.0, 0.0, 6.0], [1.0, 0.0, 5.0]] + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + intersect = box_list_ops.intersection(boxes1, boxes2) + with self.test_session() as sess: + intersect_output = sess.run(intersect) + self.assertAllClose(intersect_output, exp_output) + + def test_matched_intersection(self): + 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]]) + exp_output = [2.0, 0.0] + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + intersect = box_list_ops.matched_intersection(boxes1, boxes2) + with self.test_session() as sess: + intersect_output = sess.run(intersect) + self.assertAllClose(intersect_output, exp_output) + + def test_iou(self): + 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]]) + exp_output = [[2.0 / 16.0, 0, 6.0 / 400.0], [1.0 / 16.0, 0.0, 5.0 / 400.0]] + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + iou = box_list_ops.iou(boxes1, boxes2) + with self.test_session() as sess: + iou_output = sess.run(iou) + self.assertAllClose(iou_output, exp_output) + + def test_matched_iou(self): + 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]]) + exp_output = [2.0 / 16.0, 0] + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + iou = box_list_ops.matched_iou(boxes1, boxes2) + with self.test_session() as sess: + iou_output = sess.run(iou) + self.assertAllClose(iou_output, exp_output) + + def test_iouworks_on_empty_inputs(self): + 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) + with self.test_session() as sess: + iou_output_1, iou_output_2, iou_output_3 = sess.run( + [iou_empty_1, iou_empty_2, iou_empty_3]) + 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): + 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]]) + 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]] + 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) + with self.test_session() as sess: + ioa_output_1, ioa_output_2 = sess.run([ioa_1, ioa_2]) + self.assertAllClose(ioa_output_1, exp_output_1) + self.assertAllClose(ioa_output_2, exp_output_2) + + def test_prune_non_overlapping_boxes(self): + 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) + with self.test_session() as sess: + (output_1_, keep_indices_1_, output_2_, keep_indices_2_, exp_output_1_, + exp_output_2_) = sess.run( + [output_1.get(), keep_indices_1, + output_2.get(), keep_indices_2, + exp_output_1.get(), exp_output_2.get()]) + 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): + 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]]) + exp_boxes = [[3.0, 4.0, 6.0, 8.0], + [0.0, 0.0, 20.0, 20.0]] + boxes = box_list.BoxList(boxes) + pruned_boxes = box_list_ops.prune_small_boxes(boxes, 3) + with self.test_session() as sess: + pruned_boxes = sess.run(pruned_boxes.get()) + self.assertAllEqual(pruned_boxes, exp_boxes) + + def test_prune_small_boxes_prunes_boxes_with_negative_side(self): + 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 + exp_boxes = [[3.0, 4.0, 6.0, 8.0], + [0.0, 0.0, 20.0, 20.0]] + boxes = box_list.BoxList(boxes) + pruned_boxes = box_list_ops.prune_small_boxes(boxes, 3) + with self.test_session() as sess: + pruned_boxes = sess.run(pruned_boxes.get()) + self.assertAllEqual(pruned_boxes, exp_boxes) + + def test_change_coordinate_frame(self): + 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) + + with self.test_session() as sess: + output_, expected_boxes_ = sess.run([output.get(), expected_boxes.get()]) + self.assertAllClose(output_, expected_boxes_) + + def test_ioaworks_on_empty_inputs(self): + 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) + with self.test_session() as sess: + ioa_output_1, ioa_output_2, ioa_output_3 = sess.run( + [ioa_empty_1, ioa_empty_2, ioa_empty_3]) + 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): + 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]]) + exp_output = [[26, 25, 0], [18, 27, 6]] + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + dist_matrix = box_list_ops.sq_dist(boxes1, boxes2) + with self.test_session() as sess: + dist_output = sess.run(dist_matrix) + self.assertAllClose(dist_output, exp_output) + + def test_boolean_mask(self): + 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) + expected_subset = [4 * [0.0], 4 * [2.0], 4 * [4.0]] + boxes = box_list.BoxList(corners) + subset = box_list_ops.boolean_mask(boxes, indicator) + with self.test_session() as sess: + subset_output = sess.run(subset.get()) + self.assertAllClose(subset_output, expected_subset) + + def test_boolean_mask_with_field(self): + 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) + weights = tf.constant([[.1], [.3], [.5], [.7], [.9]], tf.float32) + expected_subset = [4 * [0.0], 4 * [2.0], 4 * [4.0]] + expected_weights = [[.1], [.5], [.9]] + + boxes = box_list.BoxList(corners) + boxes.add_field('weights', weights) + subset = box_list_ops.boolean_mask(boxes, indicator, ['weights']) + with self.test_session() as sess: + subset_output, weights_output = sess.run( + [subset.get(), subset.get_field('weights')]) + self.assertAllClose(subset_output, expected_subset) + self.assertAllClose(weights_output, expected_weights) + + def test_gather(self): + 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) + expected_subset = [4 * [0.0], 4 * [2.0], 4 * [4.0]] + boxes = box_list.BoxList(corners) + subset = box_list_ops.gather(boxes, indices) + with self.test_session() as sess: + subset_output = sess.run(subset.get()) + self.assertAllClose(subset_output, expected_subset) + + def test_gather_with_field(self): + 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) + weights = tf.constant([[.1], [.3], [.5], [.7], [.9]], tf.float32) + expected_subset = [4 * [0.0], 4 * [2.0], 4 * [4.0]] + expected_weights = [[.1], [.5], [.9]] + + boxes = box_list.BoxList(corners) + boxes.add_field('weights', weights) + subset = box_list_ops.gather(boxes, indices, ['weights']) + with self.test_session() as sess: + subset_output, weights_output = sess.run( + [subset.get(), subset.get_field('weights')]) + self.assertAllClose(subset_output, expected_subset) + self.assertAllClose(weights_output, 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): + 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]) + expected_subset = [4 * [0.0], 4 * [2.0], 4 * [4.0]] + expected_weights = [.5, .7, .9] + + boxes = box_list.BoxList(corners) + boxes.add_field('weights', weights) + subset = box_list_ops.gather(boxes, indices, ['weights']) + with self.test_session() as sess: + subset_output, weights_output = sess.run([subset.get(), subset.get_field( + 'weights')]) + 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] + 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) + with self.test_session() as sess: + corners_out, scores_out, weights_out = sess.run([ + sort_by_weight.get(), + sort_by_weight.get_field('scores'), + sort_by_weight.get_field('weights')]) + 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] + 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') + with self.test_session() as sess: + corners_out, scores_out, weights_out = sess.run([sort_by_score.get( + ), sort_by_score.get_field('scores'), sort_by_score.get_field('weights')]) + 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.test_session() as sess: + 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.assertRaisesWithPredicateMatch(errors.InvalidArgumentError, + 'Incorrect field size'): + sess.run(box_list_ops.sort_by_field(boxes, 'weights').get()) + + def test_visualize_boxes_in_image(self): + 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.to_float( + tf.greater(tf.reduce_sum(image_and_boxes, 2), 0.0)) + 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]] + with self.test_session() as sess: + output = sess.run(image_and_boxes_bw) + self.assertAllEqual(output.astype(int), exp_result) + + def test_filter_field_value_equals(self): + 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])) + 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_boxes1 = box_list_ops.filter_field_value_equals( + boxes, 'classes', 1) + filtered_boxes2 = box_list_ops.filter_field_value_equals( + boxes, 'classes', 2) + with self.test_session() as sess: + filtered_output1, filtered_output2 = sess.run([filtered_boxes1.get(), + filtered_boxes2.get()]) + self.assertAllClose(filtered_output1, exp_output1) + self.assertAllClose(filtered_output2, exp_output2) + + def test_filter_greater_than(self): + 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 + exp_output = [[0, 0.1, 1, 1.1], [0, -0.1, 1, 0.9], [0, 100, 1, 101]] + + filtered_boxes = box_list_ops.filter_greater_than(boxes, thresh) + with self.test_session() as sess: + filtered_output = sess.run(filtered_boxes.get()) + self.assertAllClose(filtered_output, exp_output) + + def test_clip_box_list(self): + 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) + + 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] + with self.test_session() as sess: + boxes_out, classes_out, scores_out = sess.run( + [clipped_boxlist.get(), clipped_boxlist.get_field('classes'), + clipped_boxlist.get_field('scores')]) + + self.assertAllClose(expected_boxes, boxes_out) + self.assertAllEqual(expected_classes, classes_out) + self.assertAllClose(expected_scores, scores_out) + + def test_pad_box_list(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])) + num_boxes = 4 + padded_boxlist = box_list_ops.pad_or_clip_box_list(boxlist, num_boxes) + + 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] + with self.test_session() as sess: + boxes_out, classes_out, scores_out = sess.run( + [padded_boxlist.get(), padded_boxlist.get_field('classes'), + padded_boxlist.get_field('scores')]) + + self.assertAllClose(expected_boxes, boxes_out) + self.assertAllEqual(expected_classes, classes_out) + self.assertAllClose(expected_scores, scores_out) + + +class ConcatenateTest(tf.test.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): + 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]) + + 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] + + 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]) + with self.test_session() as sess: + corners_output, scores_output = sess.run( + [result.get(), result.get_field('scores')]) + self.assertAllClose(corners_output, exp_corners) + self.assertAllClose(scores_output, exp_scores) + + +class NonMaxSuppressionTest(tf.test.TestCase): + + def test_with_invalid_scores_field(self): + 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])) + iou_thresh = .5 + max_output_size = 3 + nms = box_list_ops.non_max_suppression( + boxes, iou_thresh, max_output_size) + with self.test_session() as sess: + with self.assertRaisesWithPredicateMatch( + errors.InvalidArgumentError, 'scores has incompatible shape'): + sess.run(nms.get()) + + def test_select_from_three_clusters(self): + 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 + + exp_nms = [[0, 10, 1, 11], + [0, 0, 1, 1], + [0, 100, 1, 101]] + nms = box_list_ops.non_max_suppression( + boxes, iou_thresh, max_output_size) + with self.test_session() as sess: + nms_output = sess.run(nms.get()) + self.assertAllClose(nms_output, exp_nms) + + def test_select_at_most_two_boxes_from_three_clusters(self): + 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 + + exp_nms = [[0, 10, 1, 11], + [0, 0, 1, 1]] + nms = box_list_ops.non_max_suppression( + boxes, iou_thresh, max_output_size) + with self.test_session() as sess: + nms_output = sess.run(nms.get()) + self.assertAllClose(nms_output, exp_nms) + + def test_select_at_most_thirty_boxes_from_three_clusters(self): + 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 + + exp_nms = [[0, 10, 1, 11], + [0, 0, 1, 1], + [0, 100, 1, 101]] + nms = box_list_ops.non_max_suppression( + boxes, iou_thresh, max_output_size) + with self.test_session() as sess: + nms_output = sess.run(nms.get()) + self.assertAllClose(nms_output, exp_nms) + + def test_select_single_box(self): + 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 + + exp_nms = [[0, 0, 1, 1]] + nms = box_list_ops.non_max_suppression( + boxes, iou_thresh, max_output_size) + with self.test_session() as sess: + nms_output = sess.run(nms.get()) + self.assertAllClose(nms_output, exp_nms) + + def test_select_from_ten_identical_boxes(self): + 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 + + exp_nms = [[0, 0, 1, 1]] + nms = box_list_ops.non_max_suppression( + boxes, iou_thresh, max_output_size) + with self.test_session() as sess: + nms_output = sess.run(nms.get()) + self.assertAllClose(nms_output, exp_nms) + + def test_copy_extra_fields(self): + corners = tf.constant([[0, 0, 1, 1], + [0, 0.1, 1, 1.1]], tf.float32) + boxes = box_list.BoxList(corners) + tensor1 = np.array([[1], [4]]) + tensor2 = np.array([[1, 1], [2, 2]]) + 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) + with self.test_session() as sess: + self.assertAllClose(tensor1, sess.run(new_boxes.get_field('tensor1'))) + self.assertAllClose(tensor2, sess.run(new_boxes.get_field('tensor2'))) + + +class CoordinatesConversionTest(tf.test.TestCase): + + def test_to_normalized_coordinates(self): + 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]) + expected_boxes = [[0, 0, 1, 1], + [0.25, 0.25, 0.75, 0.75]] + + with self.test_session() as sess: + normalized_boxes = sess.run(normalized_boxlist.get()) + self.assertAllClose(normalized_boxes, expected_boxes) + + def test_to_normalized_coordinates_already_normalized(self): + 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]) + + with self.test_session() as sess: + with self.assertRaisesOpError('assertion failed'): + sess.run(normalized_boxlist.get()) + + def test_to_absolute_coordinates(self): + 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]) + expected_boxes = [[0, 0, 100, 100], + [25, 25, 75, 75]] + + with self.test_session() as sess: + absolute_boxes = sess.run(absolute_boxlist.get()) + self.assertAllClose(absolute_boxes, expected_boxes) + + def test_to_absolute_coordinates_already_abolute(self): + 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]) + + with self.test_session() as sess: + with self.assertRaisesOpError('assertion failed'): + sess.run(absolute_boxlist.get()) + + 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] + 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]) + + with self.test_session() as sess: + out = sess.run(boxlist.get()) + 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] + 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]) + + with self.test_session() as sess: + out = sess.run(boxlist.get()) + self.assertAllClose(out, coordinates) + + +class BoxRefinementTest(tf.test.TestCase): + + def test_box_voting(self): + 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) + expected_boxes = [[0.1, 0.1, 0.425, 0.425], [0.6, 0.6, 0.8, 0.8]] + expected_scores = [0.5, 0.3] + with self.test_session() as sess: + boxes_out, scores_out, extra_field_out = sess.run( + [averaged_boxes.get(), averaged_boxes.get_field('scores'), + averaged_boxes.get_field('ExtraField')]) + + 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): + 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) + + with self.test_session() as sess: + with self.assertRaisesOpError('Scores must be non negative'): + sess.run([averaged_boxes.get()]) + + def test_box_voting_fails_when_unmatched(self): + 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) + + with self.test_session() as sess: + with self.assertRaisesOpError('Each box in selected_boxes must match ' + 'with at least one box in pool_boxes.'): + sess.run([averaged_boxes.get()]) + + def test_refine_boxes(self): + 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])) + refined_boxes = box_list_ops.refine_boxes(pool, 0.5, 10) + + expected_boxes = [[0.1, 0.1, 0.425, 0.425], [0.6, 0.6, 0.8, 0.8]] + expected_scores = [0.5, 0.3] + with self.test_session() as sess: + boxes_out, scores_out, extra_field_out = sess.run( + [refined_boxes.get(), refined_boxes.get_field('scores'), + refined_boxes.get_field('ExtraField')]) + + 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): + 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])) + refined_boxes = box_list_ops.refine_boxes_multi_class(pool, 3, 0.5, 10) + + 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] + with self.test_session() as sess: + boxes_out, scores_out, extra_field_out = sess.run( + [refined_boxes.get(), refined_boxes.get_field('scores'), + refined_boxes.get_field('classes')]) + + self.assertAllClose(expected_boxes, boxes_out) + self.assertAllClose(expected_scores, scores_out) + self.assertAllEqual(extra_field_out, [0, 1, 1]) + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/core/box_list_test.py b/object_detection/core/box_list_test.py new file mode 100644 index 000000000..edc00ebbc --- /dev/null +++ b/object_detection/core/box_list_test.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. +# ============================================================================== + +"""Tests for object_detection.core.box_list.""" + +import tensorflow as tf + +from object_detection.core import box_list + + +class BoxListTest(tf.test.TestCase): + """Tests for BoxList class.""" + + def test_num_boxes(self): + data = tf.constant([[0, 0, 1, 1], [1, 1, 2, 3], [3, 4, 5, 5]], tf.float32) + expected_num_boxes = 3 + + boxes = box_list.BoxList(data) + with self.test_session() as sess: + num_boxes_output = sess.run(boxes.num_boxes()) + self.assertEquals(num_boxes_output, expected_num_boxes) + + def test_get_correct_center_coordinates_and_sizes(self): + boxes = [[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]] + boxes = box_list.BoxList(tf.constant(boxes)) + centers_sizes = boxes.get_center_coordinates_and_sizes() + expected_centers_sizes = [[15, 0.35], [12.5, 0.25], [10, 0.3], [5, 0.3]] + with self.test_session() as sess: + centers_sizes_out = sess.run(centers_sizes) + self.assertAllClose(centers_sizes_out, expected_centers_sizes) + + def test_create_box_list_with_dynamic_shape(self): + 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] + expected_num_boxes = 2 + + boxes = box_list.BoxList(data) + with self.test_session() as sess: + num_boxes_output = sess.run(boxes.num_boxes()) + self.assertEquals(num_boxes_output, expected_num_boxes) + + def test_transpose_coordinates(self): + boxes = [[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]] + boxes = box_list.BoxList(tf.constant(boxes)) + boxes.transpose_coordinates() + expected_corners = [[10.0, 10.0, 15.0, 20.0], [0.1, 0.2, 0.4, 0.5]] + with self.test_session() as sess: + corners_out = sess.run(boxes.get()) + self.assertAllClose(corners_out, 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.assertEquals(boxes.num_boxes_static(), 2) + self.assertEquals(type(boxes.num_boxes_static()), int) + + def test_num_boxes_static_for_uninferrable_shape(self): + placeholder = tf.placeholder(tf.float32, shape=[None, 4]) + boxes = box_list.BoxList(placeholder) + self.assertEquals(boxes.num_boxes_static(), None) + + def test_as_tensor_dict(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])) + tensor_dict = boxlist.as_tensor_dict() + + expected_boxes = [[0.1, 0.1, 0.4, 0.4], [0.1, 0.1, 0.5, 0.5]] + expected_classes = [0, 1] + expected_scores = [0.75, 0.2] + + with self.test_session() as sess: + tensor_dict_out = sess.run(tensor_dict) + self.assertAllEqual(3, len(tensor_dict_out)) + self.assertAllClose(expected_boxes, tensor_dict_out['boxes']) + self.assertAllEqual(expected_classes, tensor_dict_out['classes']) + self.assertAllClose(expected_scores, tensor_dict_out['scores']) + + def test_as_tensor_dict_with_features(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])) + tensor_dict = boxlist.as_tensor_dict(['boxes', 'classes', 'scores']) + + expected_boxes = [[0.1, 0.1, 0.4, 0.4], [0.1, 0.1, 0.5, 0.5]] + expected_classes = [0, 1] + expected_scores = [0.75, 0.2] + + with self.test_session() as sess: + tensor_dict_out = sess.run(tensor_dict) + self.assertAllEqual(3, len(tensor_dict_out)) + self.assertAllClose(expected_boxes, tensor_dict_out['boxes']) + self.assertAllEqual(expected_classes, tensor_dict_out['classes']) + self.assertAllClose(expected_scores, tensor_dict_out['scores']) + + 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/object_detection/core/box_predictor.py b/object_detection/core/box_predictor.py new file mode 100644 index 000000000..71540c11f --- /dev/null +++ b/object_detection/core/box_predictor.py @@ -0,0 +1,546 @@ +# 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 as tf +from object_detection.utils import ops +from object_detection.utils import static_shape + +slim = tf.contrib.slim + +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 num_classes(self): + return self._num_classes + + def predict(self, image_features, num_predictions_per_location, scope, + **params): + """Computes encoded object locations and corresponding confidences. + + Takes a high level image feature map as input and produce two predictions, + (1) a tensor encoding box locations, and + (2) a tensor encoding class scores for each corresponding box. + In this interface, we only assume that two tensors are returned as output + and do not assume anything about their shapes. + + Args: + image_features: A float tensor of shape [batch_size, height, width, + channels] containing features for a batch of images. + num_predictions_per_location: an integer representing the number of box + predictions to be made per spatial location in the 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 float tensor of shape + [batch_size, num_anchors, q, code_size] representing the location of + the objects, where q is 1 or the number of classes. + class_predictions_with_background: A float tensor of shape + [batch_size, num_anchors, num_classes + 1] representing the class + predictions for the proposals. + """ + with tf.variable_scope(scope): + return self._predict(image_features, num_predictions_per_location, + **params) + + # TODO: 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 float tensor of shape [batch_size, height, width, + channels] containing features for a batch of images. + num_predictions_per_location: an integer representing the number of box + predictions to be made per spatial location in the feature map. + **params: Additional keyword arguments for specific implementations of + BoxPredictor. + + Returns: + A dictionary containing at least the following tensors. + box_encodings: A float tensor of shape + [batch_size, num_anchors, q, code_size] representing the location of + the objects, where q is 1 or the number of classes. + class_predictions_with_background: A float tensor of shape + [batch_size, num_anchors, num_classes + 1] representing the class + predictions for the proposals. + """ + pass + + +class RfcnBoxPredictor(BoxPredictor): + """RFCN Box Predictor. + + Applies a position sensitve ROI pooling on position sensitive feature maps to + predict classes and refined locations. See https://arxiv.org/abs/1605.06409 + for details. + + This is used for the second stage of the RFCN meta architecture. Notice that + locations are *not* shared across classes, thus for each anchor, a separate + prediction is made for each class. + """ + + def __init__(self, + is_training, + num_classes, + conv_hyperparams, + num_spatial_bins, + depth, + crop_size, + box_code_size): + """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}). + conv_hyperparams: Slim arg_scope with hyperparameters for conolutional + layers. + num_spatial_bins: A list of two integers `[spatial_bins_y, + spatial_bins_x]`. + depth: Target depth to reduce the input feature maps to. + crop_size: A list of two integers `[crop_height, crop_width]`. + box_code_size: Size of encoding for each box. + """ + super(RfcnBoxPredictor, self).__init__(is_training, num_classes) + self._conv_hyperparams = conv_hyperparams + self._num_spatial_bins = num_spatial_bins + self._depth = depth + self._crop_size = crop_size + self._box_code_size = box_code_size + + @property + def num_classes(self): + return self._num_classes + + def _predict(self, image_features, num_predictions_per_location, + proposal_boxes): + """Computes encoded object locations and corresponding confidences. + + Args: + image_features: A float tensor of shape [batch_size, height, width, + channels] containing features for a batch of images. + num_predictions_per_location: an integer representing the number of box + predictions to be made per spatial location in the feature map. + Currently, this must be set to 1, or an error will be raised. + proposal_boxes: A float tensor of shape [batch_size, num_proposals, + box_code_size]. + + Returns: + box_encodings: A float tensor of shape + [batch_size, 1, num_classes, code_size] representing the + location of the objects. + class_predictions_with_background: A float tensor of shape + [batch_size, 1, num_classes + 1] representing the class + predictions for the proposals. + Raises: + ValueError: if num_predictions_per_location is not 1. + """ + if num_predictions_per_location != 1: + raise ValueError('Currently RfcnBoxPredictor only supports ' + 'predicting a single box per class per location.') + + batch_size = tf.shape(proposal_boxes)[0] + num_boxes = tf.shape(proposal_boxes)[1] + def get_box_indices(proposals): + proposals_shape = proposals.get_shape().as_list() + if any(dim is None for dim in proposals_shape): + proposals_shape = tf.shape(proposals) + ones_mat = tf.ones(proposals_shape[:2], dtype=tf.int32) + multiplier = tf.expand_dims( + tf.range(start=0, limit=proposals_shape[0]), 1) + return tf.reshape(ones_mat * multiplier, [-1]) + + net = image_features + with slim.arg_scope(self._conv_hyperparams): + net = slim.conv2d(net, self._depth, [1, 1], scope='reduce_depth') + # Location predictions. + location_feature_map_depth = (self._num_spatial_bins[0] * + self._num_spatial_bins[1] * + self.num_classes * + self._box_code_size) + location_feature_map = slim.conv2d(net, location_feature_map_depth, + [1, 1], activation_fn=None, + scope='refined_locations') + box_encodings = ops.position_sensitive_crop_regions( + location_feature_map, + boxes=tf.reshape(proposal_boxes, [-1, self._box_code_size]), + box_ind=get_box_indices(proposal_boxes), + crop_size=self._crop_size, + num_spatial_bins=self._num_spatial_bins, + global_pool=True) + box_encodings = tf.squeeze(box_encodings, squeeze_dims=[1, 2]) + box_encodings = tf.reshape(box_encodings, + [batch_size * num_boxes, 1, self.num_classes, + self._box_code_size]) + + # Class predictions. + total_classes = self.num_classes + 1 # Account for background class. + class_feature_map_depth = (self._num_spatial_bins[0] * + self._num_spatial_bins[1] * + total_classes) + class_feature_map = slim.conv2d(net, class_feature_map_depth, [1, 1], + activation_fn=None, + scope='class_predictions') + class_predictions_with_background = ops.position_sensitive_crop_regions( + class_feature_map, + boxes=tf.reshape(proposal_boxes, [-1, self._box_code_size]), + box_ind=get_box_indices(proposal_boxes), + crop_size=self._crop_size, + num_spatial_bins=self._num_spatial_bins, + global_pool=True) + class_predictions_with_background = tf.squeeze( + class_predictions_with_background, squeeze_dims=[1, 2]) + class_predictions_with_background = tf.reshape( + class_predictions_with_background, + [batch_size * num_boxes, 1, total_classes]) + + return {BOX_ENCODINGS: box_encodings, + CLASS_PREDICTIONS_WITH_BACKGROUND: + class_predictions_with_background} + + +class MaskRCNNBoxPredictor(BoxPredictor): + """Mask R-CNN Box Predictor. + + See Mask R-CNN: He, K., Gkioxari, G., Dollar, P., & Girshick, R. (2017). + Mask R-CNN. arXiv preprint arXiv:1703.06870. + + This is used for the second stage of the Mask R-CNN detector where proposals + cropped from an image are arranged along the batch dimension of the input + image_features tensor. Notice that locations are *not* shared across classes, + thus for each anchor, a separate prediction is made for each class. + + In addition to predicting boxes and classes, optionally this class allows + predicting masks and/or keypoints inside detection boxes. + + Currently this box predictor makes per-class predictions; that is, each + anchor makes a separate box prediction for each class. + """ + + def __init__(self, + is_training, + num_classes, + fc_hyperparams, + use_dropout, + dropout_keep_prob, + box_code_size, + conv_hyperparams=None, + predict_instance_masks=False, + mask_prediction_conv_depth=256, + predict_keypoints=False): + """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}). + fc_hyperparams: 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. + conv_hyperparams: Slim arg_scope with hyperparameters for convolution + ops. + predict_instance_masks: Whether to predict object masks inside detection + boxes. + mask_prediction_conv_depth: The depth for the first conv2d_transpose op + applied to the image_features in the mask prediciton branch. + predict_keypoints: Whether to predict keypoints insde detection boxes. + + + Raises: + ValueError: If predict_instance_masks or predict_keypoints is true. + """ + super(MaskRCNNBoxPredictor, self).__init__(is_training, num_classes) + self._fc_hyperparams = fc_hyperparams + self._use_dropout = use_dropout + self._box_code_size = box_code_size + self._dropout_keep_prob = dropout_keep_prob + self._conv_hyperparams = conv_hyperparams + self._predict_instance_masks = predict_instance_masks + self._mask_prediction_conv_depth = mask_prediction_conv_depth + self._predict_keypoints = predict_keypoints + if self._predict_keypoints: + raise ValueError('Keypoint prediction is unimplemented.') + if ((self._predict_instance_masks or self._predict_keypoints) and + self._conv_hyperparams is None): + raise ValueError('`conv_hyperparams` must be provided when predicting ' + 'masks.') + + @property + def num_classes(self): + return self._num_classes + + def _predict(self, image_features, num_predictions_per_location): + """Computes encoded object locations and corresponding confidences. + + Flattens image_features and applies fully connected ops (with no + non-linearity) to predict box encodings and class predictions. In this + setting, anchors are not spatially arranged in any way and are assumed to + have been folded into the batch dimension. Thus we output 1 for the + anchors dimension. + + Args: + image_features: A float tensor of shape [batch_size, height, width, + channels] containing features for a batch of images. + num_predictions_per_location: an integer representing the number of box + predictions to be made per spatial location in the feature map. + Currently, this must be set to 1, or an error will be raised. + + Returns: + A dictionary containing the following tensors. + box_encodings: A float tensor of shape + [batch_size, 1, num_classes, code_size] representing the + location of the objects. + class_predictions_with_background: A float tensor of shape + [batch_size, 1, num_classes + 1] representing the class + predictions for the proposals. + If predict_masks is True the dictionary also contains: + instance_masks: A float tensor of shape + [batch_size, 1, num_classes, image_height, image_width] + If predict_keypoints is True the dictionary also contains: + keypoints: [batch_size, 1, num_keypoints, 2] + + Raises: + ValueError: if num_predictions_per_location is not 1. + """ + if num_predictions_per_location != 1: + raise ValueError('Currently FullyConnectedBoxPredictor only supports ' + 'predicting a single box per class per location.') + spatial_averaged_image_features = tf.reduce_mean(image_features, [1, 2], + keep_dims=True, + name='AvgPool') + flattened_image_features = slim.flatten(spatial_averaged_image_features) + if self._use_dropout: + flattened_image_features = slim.dropout(flattened_image_features, + keep_prob=self._dropout_keep_prob, + is_training=self._is_training) + with slim.arg_scope(self._fc_hyperparams): + box_encodings = slim.fully_connected( + flattened_image_features, + self._num_classes * self._box_code_size, + activation_fn=None, + scope='BoxEncodingPredictor') + class_predictions_with_background = slim.fully_connected( + flattened_image_features, + self._num_classes + 1, + activation_fn=None, + scope='ClassPredictor') + box_encodings = tf.reshape( + box_encodings, [-1, 1, self._num_classes, self._box_code_size]) + class_predictions_with_background = tf.reshape( + class_predictions_with_background, [-1, 1, self._num_classes + 1]) + + predictions_dict = { + BOX_ENCODINGS: box_encodings, + CLASS_PREDICTIONS_WITH_BACKGROUND: class_predictions_with_background + } + + if self._predict_instance_masks: + with slim.arg_scope(self._conv_hyperparams): + upsampled_features = slim.conv2d_transpose( + image_features, + num_outputs=self._mask_prediction_conv_depth, + kernel_size=[2, 2], + stride=2) + mask_predictions = slim.conv2d(upsampled_features, + num_outputs=self.num_classes, + activation_fn=None, + kernel_size=[1, 1]) + instance_masks = tf.expand_dims(tf.transpose(mask_predictions, + perm=[0, 3, 1, 2]), + axis=1, + name='MaskPredictor') + predictions_dict[MASK_PREDICTIONS] = instance_masks + return predictions_dict + + +class ConvolutionalBoxPredictor(BoxPredictor): + """Convolutional Box Predictor. + + Optionally add an intermediate 1x1 convolutional layer after features and + predict in parallel branches box_encodings and + class_predictions_with_background. + + Currently this box predictor assumes that predictions are "shared" across + classes --- that is each anchor makes box predictions which do not depend + on class. + """ + + def __init__(self, + is_training, + num_classes, + conv_hyperparams, + min_depth, + max_depth, + num_layers_before_predictor, + use_dropout, + dropout_keep_prob, + kernel_size, + box_code_size, + apply_sigmoid_to_scores=False): + """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}). + conv_hyperparams: Slim arg_scope with hyperparameters for convolution ops. + min_depth: Minumum 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 for class prediction or not. + 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. + + Raises: + ValueError: if min_depth > max_depth. + """ + super(ConvolutionalBoxPredictor, self).__init__(is_training, num_classes) + if min_depth > max_depth: + raise ValueError('min_depth should be less than or equal to max_depth') + self._conv_hyperparams = conv_hyperparams + self._min_depth = min_depth + self._max_depth = max_depth + self._num_layers_before_predictor = num_layers_before_predictor + self._use_dropout = use_dropout + self._kernel_size = kernel_size + self._box_code_size = box_code_size + self._dropout_keep_prob = dropout_keep_prob + self._apply_sigmoid_to_scores = apply_sigmoid_to_scores + + def _predict(self, image_features, num_predictions_per_location): + """Computes encoded object locations and corresponding confidences. + + Args: + image_features: A float tensor of shape [batch_size, height, width, + channels] containing features for a batch of images. + num_predictions_per_location: an integer representing the number of box + predictions to be made per spatial location in the feature map. + + Returns: + A dictionary containing the following tensors. + box_encodings: A float tensor of shape [batch_size, num_anchors, 1, + code_size] representing the location of the objects, where + num_anchors = feat_height * feat_width * num_predictions_per_location + class_predictions_with_background: A float tensor of shape + [batch_size, num_anchors, num_classes + 1] representing the class + predictions for the proposals. + """ + features_depth = static_shape.get_depth(image_features.get_shape()) + depth = max(min(features_depth, self._max_depth), self._min_depth) + + # Add a slot for the background class. + num_class_slots = self.num_classes + 1 + net = image_features + with slim.arg_scope(self._conv_hyperparams), \ + slim.arg_scope([slim.dropout], is_training=self._is_training): + # Add additional conv layers before the predictor. + if depth > 0 and self._num_layers_before_predictor > 0: + for i in range(self._num_layers_before_predictor): + net = slim.conv2d( + net, depth, [1, 1], scope='Conv2d_%d_1x1_%d' % (i, depth)) + with slim.arg_scope([slim.conv2d], activation_fn=None, + normalizer_fn=None, normalizer_params=None): + box_encodings = slim.conv2d( + net, num_predictions_per_location * self._box_code_size, + [self._kernel_size, self._kernel_size], + scope='BoxEncodingPredictor') + if self._use_dropout: + net = slim.dropout(net, keep_prob=self._dropout_keep_prob) + class_predictions_with_background = slim.conv2d( + net, num_predictions_per_location * num_class_slots, + [self._kernel_size, self._kernel_size], scope='ClassPredictor') + if self._apply_sigmoid_to_scores: + class_predictions_with_background = tf.sigmoid( + class_predictions_with_background) + + batch_size = static_shape.get_batch_size(image_features.get_shape()) + if batch_size is None: + features_height = static_shape.get_height(image_features.get_shape()) + features_width = static_shape.get_width(image_features.get_shape()) + flattened_predictions_size = (features_height * features_width * + num_predictions_per_location) + box_encodings = tf.reshape( + box_encodings, + [-1, flattened_predictions_size, 1, self._box_code_size]) + class_predictions_with_background = tf.reshape( + class_predictions_with_background, + [-1, flattened_predictions_size, num_class_slots]) + else: + box_encodings = tf.reshape( + box_encodings, [batch_size, -1, 1, self._box_code_size]) + class_predictions_with_background = tf.reshape( + class_predictions_with_background, [batch_size, -1, num_class_slots]) + return {BOX_ENCODINGS: box_encodings, + CLASS_PREDICTIONS_WITH_BACKGROUND: + class_predictions_with_background} diff --git a/object_detection/core/box_predictor_test.py b/object_detection/core/box_predictor_test.py new file mode 100644 index 000000000..e5e5a3c9a --- /dev/null +++ b/object_detection/core/box_predictor_test.py @@ -0,0 +1,323 @@ +# 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_predictor.""" + +import numpy as np +import tensorflow as tf + +from google.protobuf import text_format +from object_detection.builders import hyperparams_builder +from object_detection.core import box_predictor +from object_detection.protos import hyperparams_pb2 + + +class MaskRCNNBoxPredictorTest(tf.test.TestCase): + + def _build_arg_scope_with_hyperparams(self, + op_type=hyperparams_pb2.Hyperparams.FC): + hyperparams = hyperparams_pb2.Hyperparams() + hyperparams_text_proto = """ + activation: NONE + regularizer { + l2_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + """ + text_format.Merge(hyperparams_text_proto, hyperparams) + hyperparams.op = op_type + return hyperparams_builder.build(hyperparams, is_training=True) + + def test_get_boxes_with_five_classes(self): + image_features = tf.random_uniform([2, 7, 7, 3], dtype=tf.float32) + mask_box_predictor = box_predictor.MaskRCNNBoxPredictor( + is_training=False, + num_classes=5, + fc_hyperparams=self._build_arg_scope_with_hyperparams(), + use_dropout=False, + dropout_keep_prob=0.5, + box_code_size=4, + ) + box_predictions = mask_box_predictor.predict( + image_features, num_predictions_per_location=1, scope='BoxPredictor') + box_encodings = box_predictions[box_predictor.BOX_ENCODINGS] + class_predictions_with_background = box_predictions[ + box_predictor.CLASS_PREDICTIONS_WITH_BACKGROUND] + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + (box_encodings_shape, + class_predictions_with_background_shape) = sess.run( + [tf.shape(box_encodings), + tf.shape(class_predictions_with_background)]) + self.assertAllEqual(box_encodings_shape, [2, 1, 5, 4]) + self.assertAllEqual(class_predictions_with_background_shape, [2, 1, 6]) + + def test_value_error_on_predict_instance_masks_with_no_conv_hyperparms(self): + with self.assertRaises(ValueError): + box_predictor.MaskRCNNBoxPredictor( + is_training=False, + num_classes=5, + fc_hyperparams=self._build_arg_scope_with_hyperparams(), + use_dropout=False, + dropout_keep_prob=0.5, + box_code_size=4, + predict_instance_masks=True) + + def test_get_instance_masks(self): + image_features = tf.random_uniform([2, 7, 7, 3], dtype=tf.float32) + mask_box_predictor = box_predictor.MaskRCNNBoxPredictor( + is_training=False, + num_classes=5, + fc_hyperparams=self._build_arg_scope_with_hyperparams(), + use_dropout=False, + dropout_keep_prob=0.5, + box_code_size=4, + conv_hyperparams=self._build_arg_scope_with_hyperparams( + op_type=hyperparams_pb2.Hyperparams.CONV), + predict_instance_masks=True) + box_predictions = mask_box_predictor.predict( + image_features, num_predictions_per_location=1, scope='BoxPredictor') + mask_predictions = box_predictions[box_predictor.MASK_PREDICTIONS] + self.assertListEqual([2, 1, 5, 14, 14], + mask_predictions.get_shape().as_list()) + + def test_do_not_return_instance_masks_and_keypoints_without_request(self): + image_features = tf.random_uniform([2, 7, 7, 3], dtype=tf.float32) + mask_box_predictor = box_predictor.MaskRCNNBoxPredictor( + is_training=False, + num_classes=5, + fc_hyperparams=self._build_arg_scope_with_hyperparams(), + use_dropout=False, + dropout_keep_prob=0.5, + box_code_size=4) + box_predictions = mask_box_predictor.predict( + image_features, num_predictions_per_location=1, scope='BoxPredictor') + self.assertEqual(len(box_predictions), 2) + self.assertTrue(box_predictor.BOX_ENCODINGS in box_predictions) + self.assertTrue(box_predictor.CLASS_PREDICTIONS_WITH_BACKGROUND + in box_predictions) + + def test_value_error_on_predict_keypoints(self): + with self.assertRaises(ValueError): + box_predictor.MaskRCNNBoxPredictor( + is_training=False, + num_classes=5, + fc_hyperparams=self._build_arg_scope_with_hyperparams(), + use_dropout=False, + dropout_keep_prob=0.5, + box_code_size=4, + predict_keypoints=True) + + +class RfcnBoxPredictorTest(tf.test.TestCase): + + def _build_arg_scope_with_conv_hyperparams(self): + conv_hyperparams = hyperparams_pb2.Hyperparams() + conv_hyperparams_text_proto = """ + regularizer { + l2_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + """ + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams) + return hyperparams_builder.build(conv_hyperparams, is_training=True) + + def test_get_correct_box_encoding_and_class_prediction_shapes(self): + image_features = tf.random_uniform([4, 8, 8, 64], dtype=tf.float32) + proposal_boxes = tf.random_normal([4, 2, 4], dtype=tf.float32) + rfcn_box_predictor = box_predictor.RfcnBoxPredictor( + is_training=False, + num_classes=2, + conv_hyperparams=self._build_arg_scope_with_conv_hyperparams(), + num_spatial_bins=[3, 3], + depth=4, + crop_size=[12, 12], + box_code_size=4 + ) + box_predictions = rfcn_box_predictor.predict( + image_features, num_predictions_per_location=1, scope='BoxPredictor', + proposal_boxes=proposal_boxes) + box_encodings = box_predictions[box_predictor.BOX_ENCODINGS] + class_predictions_with_background = box_predictions[ + box_predictor.CLASS_PREDICTIONS_WITH_BACKGROUND] + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + (box_encodings_shape, + class_predictions_shape) = sess.run( + [tf.shape(box_encodings), + tf.shape(class_predictions_with_background)]) + self.assertAllEqual(box_encodings_shape, [8, 1, 2, 4]) + self.assertAllEqual(class_predictions_shape, [8, 1, 3]) + + +class ConvolutionalBoxPredictorTest(tf.test.TestCase): + + def _build_arg_scope_with_conv_hyperparams(self): + conv_hyperparams = hyperparams_pb2.Hyperparams() + conv_hyperparams_text_proto = """ + activation: RELU_6 + regularizer { + l2_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + """ + text_format.Merge(conv_hyperparams_text_proto, conv_hyperparams) + return hyperparams_builder.build(conv_hyperparams, is_training=True) + + def test_get_boxes_for_five_aspect_ratios_per_location(self): + image_features = tf.random_uniform([4, 8, 8, 64], dtype=tf.float32) + conv_box_predictor = box_predictor.ConvolutionalBoxPredictor( + is_training=False, + num_classes=0, + conv_hyperparams=self._build_arg_scope_with_conv_hyperparams(), + min_depth=0, + max_depth=32, + num_layers_before_predictor=1, + use_dropout=True, + dropout_keep_prob=0.8, + kernel_size=1, + box_code_size=4 + ) + box_predictions = conv_box_predictor.predict( + image_features, num_predictions_per_location=5, scope='BoxPredictor') + box_encodings = box_predictions[box_predictor.BOX_ENCODINGS] + objectness_predictions = box_predictions[ + box_predictor.CLASS_PREDICTIONS_WITH_BACKGROUND] + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + (box_encodings_shape, + objectness_predictions_shape) = sess.run( + [tf.shape(box_encodings), tf.shape(objectness_predictions)]) + self.assertAllEqual(box_encodings_shape, [4, 320, 1, 4]) + self.assertAllEqual(objectness_predictions_shape, [4, 320, 1]) + + def test_get_boxes_for_one_aspect_ratio_per_location(self): + image_features = tf.random_uniform([4, 8, 8, 64], dtype=tf.float32) + conv_box_predictor = box_predictor.ConvolutionalBoxPredictor( + is_training=False, + num_classes=0, + conv_hyperparams=self._build_arg_scope_with_conv_hyperparams(), + min_depth=0, + max_depth=32, + num_layers_before_predictor=1, + use_dropout=True, + dropout_keep_prob=0.8, + kernel_size=1, + box_code_size=4 + ) + box_predictions = conv_box_predictor.predict( + image_features, num_predictions_per_location=1, scope='BoxPredictor') + box_encodings = box_predictions[box_predictor.BOX_ENCODINGS] + objectness_predictions = box_predictions[ + box_predictor.CLASS_PREDICTIONS_WITH_BACKGROUND] + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + (box_encodings_shape, + objectness_predictions_shape) = sess.run( + [tf.shape(box_encodings), tf.shape(objectness_predictions)]) + self.assertAllEqual(box_encodings_shape, [4, 64, 1, 4]) + self.assertAllEqual(objectness_predictions_shape, [4, 64, 1]) + + def test_get_multi_class_predictions_for_five_aspect_ratios_per_location( + self): + num_classes_without_background = 6 + image_features = tf.random_uniform([4, 8, 8, 64], dtype=tf.float32) + conv_box_predictor = box_predictor.ConvolutionalBoxPredictor( + is_training=False, + num_classes=num_classes_without_background, + conv_hyperparams=self._build_arg_scope_with_conv_hyperparams(), + min_depth=0, + max_depth=32, + num_layers_before_predictor=1, + use_dropout=True, + dropout_keep_prob=0.8, + kernel_size=1, + box_code_size=4 + ) + box_predictions = conv_box_predictor.predict( + image_features, + num_predictions_per_location=5, + scope='BoxPredictor') + box_encodings = box_predictions[box_predictor.BOX_ENCODINGS] + class_predictions_with_background = box_predictions[ + box_predictor.CLASS_PREDICTIONS_WITH_BACKGROUND] + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + (box_encodings_shape, class_predictions_with_background_shape + ) = sess.run([ + tf.shape(box_encodings), tf.shape(class_predictions_with_background)]) + self.assertAllEqual(box_encodings_shape, [4, 320, 1, 4]) + self.assertAllEqual(class_predictions_with_background_shape, + [4, 320, num_classes_without_background+1]) + + def test_get_boxes_for_five_aspect_ratios_per_location_fully_convolutional( + self): + image_features = tf.placeholder(dtype=tf.float32, shape=[4, None, None, 64]) + conv_box_predictor = box_predictor.ConvolutionalBoxPredictor( + is_training=False, + num_classes=0, + conv_hyperparams=self._build_arg_scope_with_conv_hyperparams(), + min_depth=0, + max_depth=32, + num_layers_before_predictor=1, + use_dropout=True, + dropout_keep_prob=0.8, + kernel_size=1, + box_code_size=4 + ) + box_predictions = conv_box_predictor.predict( + image_features, num_predictions_per_location=5, scope='BoxPredictor') + box_encodings = box_predictions[box_predictor.BOX_ENCODINGS] + objectness_predictions = box_predictions[ + box_predictor.CLASS_PREDICTIONS_WITH_BACKGROUND] + init_op = tf.global_variables_initializer() + + resolution = 32 + expected_num_anchors = resolution*resolution*5 + with self.test_session() as sess: + sess.run(init_op) + (box_encodings_shape, + objectness_predictions_shape) = sess.run( + [tf.shape(box_encodings), tf.shape(objectness_predictions)], + feed_dict={image_features: + np.random.rand(4, resolution, resolution, 64)}) + self.assertAllEqual(box_encodings_shape, [4, expected_num_anchors, 1, 4]) + self.assertAllEqual(objectness_predictions_shape, + [4, expected_num_anchors, 1]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/core/data_decoder.py b/object_detection/core/data_decoder.py new file mode 100644 index 000000000..84be4db59 --- /dev/null +++ b/object_detection/core/data_decoder.py @@ -0,0 +1,42 @@ +# 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 abc import ABCMeta +from abc import abstractmethod + + +class DataDecoder(object): + """Interface for data decoders.""" + __metaclass__ = ABCMeta + + # TODO: snake_case this method. + @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/object_detection/core/keypoint_ops.py b/object_detection/core/keypoint_ops.py new file mode 100644 index 000000000..4a550d3c9 --- /dev/null +++ b/object_detection/core/keypoint_ops.py @@ -0,0 +1,231 @@ +# 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 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 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, 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: 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. + 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]) + 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 diff --git a/object_detection/core/keypoint_ops_test.py b/object_detection/core/keypoint_ops_test.py new file mode 100644 index 000000000..27c227bcf --- /dev/null +++ b/object_detection/core/keypoint_ops_test.py @@ -0,0 +1,168 @@ +# 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 as tf + +from object_detection.core import keypoint_ops + + +class KeypointOpsTest(tf.test.TestCase): + """Tests for common keypoint operations.""" + + def test_scale(self): + 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) + + with self.test_session() as sess: + output_, expected_keypoints_ = sess.run([output, expected_keypoints]) + self.assertAllClose(output_, expected_keypoints_) + + def test_clip_to_window(self): + 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) + + with self.test_session() as sess: + output_, expected_keypoints_ = sess.run([output, expected_keypoints]) + self.assertAllClose(output_, expected_keypoints_) + + def test_prune_outside_window(self): + 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) + + with self.test_session() as sess: + output_, expected_keypoints_ = sess.run([output, expected_keypoints]) + self.assertAllClose(output_, expected_keypoints_) + + def test_change_coordinate_frame(self): + 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) + + with self.test_session() as sess: + output_, expected_keypoints_ = sess.run([output, expected_keypoints]) + self.assertAllClose(output_, expected_keypoints_) + + def test_to_normalized_coordinates(self): + 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]] + ]) + + with self.test_session() as sess: + output_, expected_keypoints_ = sess.run([output, expected_keypoints]) + self.assertAllClose(output_, expected_keypoints_) + + def test_to_normalized_coordinates_already_normalized(self): + 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) + + with self.test_session() as sess: + with self.assertRaisesOpError('assertion failed'): + sess.run(output) + + def test_to_absolute_coordinates(self): + 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.]] + ]) + + with self.test_session() as sess: + output_, expected_keypoints_ = sess.run([output, expected_keypoints]) + self.assertAllClose(output_, expected_keypoints_) + + def test_to_absolute_coordinates_already_absolute(self): + keypoints = tf.constant([ + [[10., 30.], [30., 45.]], + [[20., 0.], [40., 60.]] + ]) + output = keypoint_ops.to_absolute_coordinates( + keypoints, 40, 60) + + with self.test_session() as sess: + with self.assertRaisesOpError('assertion failed'): + sess.run(output) + + def test_flip_horizontal(self): + 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) + + with self.test_session() as sess: + output_, expected_keypoints_ = sess.run([output, expected_keypoints]) + self.assertAllClose(output_, expected_keypoints_) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/core/losses.py b/object_detection/core/losses.py new file mode 100644 index 000000000..75c7b5fc4 --- /dev/null +++ b/object_detection/core/losses.py @@ -0,0 +1,551 @@ +# 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 + * BootstrappedSigmoidClassificationLoss +""" +from abc import ABCMeta +from abc import abstractmethod + +import tensorflow as tf + +from object_detection.core import box_list +from object_detection.core import box_list_ops +from object_detection.utils import ops + +slim = tf.contrib.slim + + +class Loss(object): + """Abstract base class for loss functions.""" + __metaclass__ = ABCMeta + + def __call__(self, + prediction_tensor, + target_tensor, + ignore_nan_targets=False, + scope=None, + **params): + """Call the loss function. + + Args: + prediction_tensor: a tensor representing predicted quantities. + target_tensor: a tensor 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. + 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) + return self._compute_loss(prediction_tensor, target_tensor, **params) + + @abstractmethod + def _compute_loss(self, prediction_tensor, target_tensor, **params): + """Method to be overriden 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: a tensor representing the value of the loss function + """ + 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 __init__(self, anchorwise_output=False): + """Constructor. + + Args: + anchorwise_output: Outputs loss per anchor. (default False) + + """ + self._anchorwise_output = anchorwise_output + + 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 (scalar) tensor representing the value of the loss function + or a float tensor of shape [batch_size, num_anchors] + """ + weighted_diff = (prediction_tensor - target_tensor) * tf.expand_dims( + weights, 2) + square_diff = 0.5 * tf.square(weighted_diff) + if self._anchorwise_output: + return tf.reduce_sum(square_diff, 2) + return tf.reduce_sum(square_diff) + + +class WeightedSmoothL1LocalizationLoss(Loss): + """Smooth L1 localization loss function. + + The smooth L1_loss is defined elementwise as .5 x^2 if |x|<1 and |x|-.5 + 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, anchorwise_output=False): + """Constructor. + + Args: + anchorwise_output: Outputs loss per anchor. (default False) + + """ + self._anchorwise_output = anchorwise_output + + 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 (scalar) tensor representing the value of the loss function + """ + diff = prediction_tensor - target_tensor + abs_diff = tf.abs(diff) + abs_diff_lt_1 = tf.less(abs_diff, 1) + anchorwise_smooth_l1norm = tf.reduce_sum( + tf.where(abs_diff_lt_1, 0.5 * tf.square(abs_diff), abs_diff - 0.5), + 2) * weights + if self._anchorwise_output: + return anchorwise_smooth_l1norm + return tf.reduce_sum(anchorwise_smooth_l1norm) + + +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 (scalar) 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.reduce_sum(tf.reshape(weights, [-1]) * per_anchor_iou_loss) + + +class WeightedSigmoidClassificationLoss(Loss): + """Sigmoid cross entropy classification loss function.""" + + def __init__(self, anchorwise_output=False): + """Constructor. + + Args: + anchorwise_output: Outputs loss per anchor. (default False) + + """ + self._anchorwise_output = anchorwise_output + + 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 [batch_size, num_anchors] + class_indices: (Optional) A 1-D integer tensor of class indices. + If provided, computes loss only for the specified class indices. + + Returns: + loss: a (scalar) tensor representing the value of the loss function + or a float tensor of shape [batch_size, num_anchors] + """ + weights = tf.expand_dims(weights, 2) + 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)) + if self._anchorwise_output: + return tf.reduce_sum(per_entry_cross_ent * weights, 2) + return tf.reduce_sum(per_entry_cross_ent * weights) + + +class WeightedSoftmaxClassificationLoss(Loss): + """Softmax loss function.""" + + def __init__(self, anchorwise_output=False): + """Constructor. + + Args: + anchorwise_output: Whether to output loss per anchor (default False) + + """ + self._anchorwise_output = anchorwise_output + + 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 [batch_size, num_anchors] + + Returns: + loss: a (scalar) tensor representing the value of the loss function + """ + num_classes = prediction_tensor.get_shape().as_list()[-1] + 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]))) + if self._anchorwise_output: + return tf.reshape(per_row_cross_ent, tf.shape(weights)) * weights + return tf.reduce_sum(per_row_cross_ent * tf.reshape(weights, [-1])) + + +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', anchorwise_output=False): + """Constructor. + + Args: + alpha: a float32 scalar tensor between 0 and 1 representing interpolation + weight + bootstrap_type: set to either 'hard' or 'soft' (default) + anchorwise_output: Outputs loss per anchor. (default False) + + Raises: + ValueError: if bootstrap_type is not either 'hard' or 'soft' + """ + 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 + self._anchorwise_output = anchorwise_output + + 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 [batch_size, num_anchors] + + Returns: + loss: a (scalar) tensor representing the value of the loss function + or a float tensor of shape [batch_size, num_anchors] + """ + 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)) + if self._anchorwise_output: + return tf.reduce_sum(per_entry_cross_ent * tf.expand_dims(weights, 2), 2) + return tf.reduce_sum(per_entry_cross_ent * tf.expand_dims(weights, 2)) + + +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.to_float(self._num_positives_list)) + avg_num_negatives = tf.reduce_mean(tf.to_float(self._num_negatives_list)) + 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 M=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. + + 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 + + @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( + tf.gather(self._match_results, self.matched_column_indices())) + + def _reshape_and_cast(self, t): + return tf.cast(tf.reshape(t, [-1]), tf.int32) + + +class Matcher(object): + """Abstract base class for matcher. + """ + __metaclass__ = ABCMeta + + def match(self, similarity_matrix, scope=None, **params): + """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. + scope: Op scope name. Defaults to 'Match' if None. + **params: Additional keyword arguments for specific implementations of + the Matcher. + + Returns: + A Match object with the results of matching. + """ + with tf.name_scope(scope, 'Match', [similarity_matrix, params]) as scope: + return Match(self._match(similarity_matrix, **params)) + + @abstractmethod + def _match(self, similarity_matrix, **params): + """Method to be overriden by implementations. + + Args: + similarity_matrix: Float tensor of shape [N, M] with pairwise similarity + where higher value means more similar. + **params: Additional keyword arguments for specific implementations of + the Matcher. + + 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/object_detection/core/matcher_test.py b/object_detection/core/matcher_test.py new file mode 100644 index 000000000..7054015f2 --- /dev/null +++ b/object_detection/core/matcher_test.py @@ -0,0 +1,150 @@ +# 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 as tf + +from object_detection.core import matcher + + +class AnchorMatcherTest(tf.test.TestCase): + + def test_get_correct_matched_columnIndices(self): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + expected_column_indices = [0, 1, 3, 5] + matched_column_indices = match.matched_column_indices() + self.assertEquals(matched_column_indices.dtype, tf.int32) + with self.test_session() as sess: + matched_column_indices = sess.run(matched_column_indices) + self.assertAllEqual(matched_column_indices, expected_column_indices) + + def test_get_correct_counts(self): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + exp_num_matched_columns = 4 + exp_num_unmatched_columns = 2 + exp_num_ignored_columns = 1 + num_matched_columns = match.num_matched_columns() + num_unmatched_columns = match.num_unmatched_columns() + num_ignored_columns = match.num_ignored_columns() + self.assertEquals(num_matched_columns.dtype, tf.int32) + self.assertEquals(num_unmatched_columns.dtype, tf.int32) + self.assertEquals(num_ignored_columns.dtype, tf.int32) + with self.test_session() as sess: + (num_matched_columns_out, num_unmatched_columns_out, + num_ignored_columns_out) = sess.run( + [num_matched_columns, num_unmatched_columns, num_ignored_columns]) + 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) + + def testGetCorrectUnmatchedColumnIndices(self): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + expected_column_indices = [2, 4] + unmatched_column_indices = match.unmatched_column_indices() + self.assertEquals(unmatched_column_indices.dtype, tf.int32) + with self.test_session() as sess: + unmatched_column_indices = sess.run(unmatched_column_indices) + self.assertAllEqual(unmatched_column_indices, expected_column_indices) + + def testGetCorrectMatchedRowIndices(self): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + expected_row_indices = [3, 1, 0, 5] + matched_row_indices = match.matched_row_indices() + self.assertEquals(matched_row_indices.dtype, tf.int32) + with self.test_session() as sess: + matched_row_inds = sess.run(matched_row_indices) + self.assertAllEqual(matched_row_inds, expected_row_indices) + + def test_get_correct_ignored_column_indices(self): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + expected_column_indices = [6] + ignored_column_indices = match.ignored_column_indices() + self.assertEquals(ignored_column_indices.dtype, tf.int32) + with self.test_session() as sess: + ignored_column_indices = sess.run(ignored_column_indices) + self.assertAllEqual(ignored_column_indices, expected_column_indices) + + def test_get_correct_matched_column_indicator(self): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + expected_column_indicator = [True, True, False, True, False, True, False] + matched_column_indicator = match.matched_column_indicator() + self.assertEquals(matched_column_indicator.dtype, tf.bool) + with self.test_session() as sess: + matched_column_indicator = sess.run(matched_column_indicator) + self.assertAllEqual(matched_column_indicator, expected_column_indicator) + + def test_get_correct_unmatched_column_indicator(self): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + expected_column_indicator = [False, False, True, False, True, False, False] + unmatched_column_indicator = match.unmatched_column_indicator() + self.assertEquals(unmatched_column_indicator.dtype, tf.bool) + with self.test_session() as sess: + unmatched_column_indicator = sess.run(unmatched_column_indicator) + self.assertAllEqual(unmatched_column_indicator, expected_column_indicator) + + def test_get_correct_ignored_column_indicator(self): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + expected_column_indicator = [False, False, False, False, False, False, True] + ignored_column_indicator = match.ignored_column_indicator() + self.assertEquals(ignored_column_indicator.dtype, tf.bool) + with self.test_session() as sess: + ignored_column_indicator = sess.run(ignored_column_indicator) + self.assertAllEqual(ignored_column_indicator, expected_column_indicator) + + def test_get_correct_unmatched_ignored_column_indices(self): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + expected_column_indices = [2, 4, 6] + unmatched_ignored_column_indices = (match. + unmatched_or_ignored_column_indices()) + self.assertEquals(unmatched_ignored_column_indices.dtype, tf.int32) + with self.test_session() as sess: + unmatched_ignored_column_indices = sess.run( + unmatched_ignored_column_indices) + 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) + num_matches = 10 + 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() + with self.test_session() as sess: + matched, unmatched, ignored = sess.run([ + matched_column_indices, unmatched_column_indices, + ignored_column_indices + ]) + 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)) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/core/minibatch_sampler.py b/object_detection/core/minibatch_sampler.py new file mode 100644 index 000000000..dc622221a --- /dev/null +++ b/object_detection/core/minibatch_sampler.py @@ -0,0 +1,90 @@ +# 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 abc import ABCMeta +from abc import abstractmethod + +import tensorflow as tf + +from object_detection.utils import ops + + +class MinibatchSampler(object): + """Abstract base class for subsampling minibatches.""" + __metaclass__ = ABCMeta + + 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/object_detection/core/minibatch_sampler_test.py b/object_detection/core/minibatch_sampler_test.py new file mode 100644 index 000000000..7420ae5d0 --- /dev/null +++ b/object_detection/core/minibatch_sampler_test.py @@ -0,0 +1,82 @@ +# 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 as tf + +from object_detection.core import minibatch_sampler + + +class MinibatchSamplerTest(tf.test.TestCase): + + def test_subsample_indicator_when_more_true_elements_than_num_samples(self): + np_indicator = [True, False, True, False, True, True, False] + indicator = tf.constant(np_indicator) + samples = minibatch_sampler.MinibatchSampler.subsample_indicator( + indicator, 3) + with self.test_session() as sess: + samples_out = sess.run(samples) + self.assertTrue(np.sum(samples_out), 3) + self.assertAllEqual(samples_out, + np.logical_and(samples_out, np_indicator)) + + def test_subsample_when_more_true_elements_than_num_samples_no_shape(self): + np_indicator = [True, False, True, False, True, True, False] + indicator = tf.placeholder(tf.bool) + feed_dict = {indicator: np_indicator} + + samples = minibatch_sampler.MinibatchSampler.subsample_indicator( + indicator, 3) + with self.test_session() as sess: + samples_out = sess.run(samples, feed_dict=feed_dict) + 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 = [True, False, True, False, True, True, False] + indicator = tf.constant(np_indicator) + samples = minibatch_sampler.MinibatchSampler.subsample_indicator( + indicator, 5) + with self.test_session() as sess: + samples_out = sess.run(samples) + 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 = [True, False, True, False, True, True, False] + indicator = tf.constant(np_indicator) + samples_none = minibatch_sampler.MinibatchSampler.subsample_indicator( + indicator, 0) + with self.test_session() as sess: + samples_none_out = sess.run(samples_none) + self.assertAllEqual( + np.zeros_like(samples_none_out, dtype=bool), + samples_none_out) + + def test_subsample_indicator_when_indicator_all_false(self): + indicator_empty = tf.zeros([0], dtype=tf.bool) + samples_empty = minibatch_sampler.MinibatchSampler.subsample_indicator( + indicator_empty, 4) + with self.test_session() as sess: + samples_empty_out = sess.run(samples_empty) + self.assertEqual(0, samples_empty_out.size) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/core/model.py b/object_detection/core/model.py new file mode 100644 index 000000000..b8a448b65 --- /dev/null +++ b/object_detection/core/model.py @@ -0,0 +1,252 @@ +# 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. + +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 abc import ABCMeta +from abc import abstractmethod + +from object_detection.core import standard_fields as fields + + +class DetectionModel(object): + """Abstract base class for detection models.""" + __metaclass__ = ABCMeta + + 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 be predicted in various + implementations. + """ + self._num_classes = num_classes + self._groundtruth_lists = {} + + @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} + + 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 %s has not been provided', field) + return self._groundtruth_lists[field] + + @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 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. + """ + pass + + @abstractmethod + def predict(self, preprocessed_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. + + Returns: + prediction_dict: a dictionary holding prediction tensors to be + passed to the Loss or Postprocess functions. + """ + pass + + @abstractmethod + def postprocess(self, prediction_dict, **params): + """Convert predicted output tensors to final detections. + + 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. + * 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. + **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] + instance_masks: [batch, max_detections, image_height, image_width] + (optional) + keypoints: [batch, max_detections, num_keypoints, 2] (optional) + num_detections: [batch] + """ + pass + + @abstractmethod + def loss(self, prediction_dict): + """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 + + 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): + """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 2-D tf.float32 tensors of + shape [max_detections, 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 2-D tf.float32 tensors of + shape [batch, max_detections, num_keypoints, 2] containing keypoints. + Keypoints are assumed to be provided in normalized coordinates and + missing keypoints should be encoded as NaN. + """ + self._groundtruth_lists[fields.BoxListFields.boxes] = groundtruth_boxes_list + self._groundtruth_lists[ + fields.BoxListFields.classes] = groundtruth_classes_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 + + @abstractmethod + def restore_fn(self, checkpoint_path, from_detection_checkpoint=True): + """Return callable for loading a foreign checkpoint into tensorflow graph. + + Loads variables from a different tensorflow graph (typically feature + extractor variables). 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: + checkpoint_path: path to checkpoint to restore. + from_detection_checkpoint: whether to restore from a full detection + checkpoint (with compatible variable names) or to restore from a + classification checkpoint for initialization prior to training. + + Returns: + a callable which takes a tf.Session as input and loads a checkpoint when + run. + """ + pass diff --git a/object_detection/core/post_processing.py b/object_detection/core/post_processing.py new file mode 100644 index 000000000..cda26f25e --- /dev/null +++ b/object_detection/core/post_processing.py @@ -0,0 +1,298 @@ +# 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. +# ============================================================================== + +"""Post-processing operations on detected boxes.""" + +import tensorflow as tf + +from object_detection.core import box_list +from object_detection.core import box_list_ops +from object_detection.core import standard_fields as fields + + +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, + additional_fields=None, + 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. + + 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. + 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. + 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. + scope: name scope. + + Returns: + a BoxList holding 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. + If masks, keypoints, keypoint_heatmaps is not None, the boxlist will + contain masks, keypoints, keypoint_heatmaps corresponding to boxes. + + 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 scores.shape[1].value 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 (boxes.shape[1].value == scores.shape[1].value or + boxes.shape[1].value == 1): + raise ValueError('second dimension of boxes must be either 1 or equal ' + 'to the second dimension of scores') + if boxes.shape[2].value != 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.') + + with tf.name_scope(scope, 'MultiClassNonMaxSuppression'): + num_boxes = tf.shape(boxes)[0] + num_scores = tf.shape(scores)[0] + num_classes = scores.get_shape()[1] + + length_assert = tf.Assert( + tf.equal(num_boxes, num_scores), + ['Incorrect scores field length: actual vs expected.', + num_scores, num_boxes]) + + selected_boxes_list = [] + per_class_boxes_list = tf.unstack(boxes, axis=1) + if masks is not None: + per_class_masks_list = tf.unstack(masks, 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) + with tf.control_dependencies([length_assert]): + 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 additional_fields is not None: + for key, tensor in additional_fields.iteritems(): + boxlist_and_class_scores.add_field(key, tensor) + boxlist_filtered = box_list_ops.filter_greater_than( + boxlist_and_class_scores, score_thresh) + if clip_window is not None: + boxlist_filtered = box_list_ops.clip_to_window( + boxlist_filtered, clip_window) + if change_coordinate_frame: + boxlist_filtered = box_list_ops.change_coordinate_frame( + boxlist_filtered, clip_window) + max_selection_size = tf.minimum(max_size_per_class, + boxlist_filtered.num_boxes()) + selected_indices = tf.image.non_max_suppression( + boxlist_filtered.get(), + boxlist_filtered.get_field(fields.BoxListFields.scores), + max_selection_size, + iou_threshold=iou_thresh) + nms_result = box_list_ops.gather(boxlist_filtered, selected_indices) + 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 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)) + return sorted_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, + scope=None): + """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. + 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 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) + 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. + scope: tf scope name. + + Returns: + A dictionary containing the following entries: + 'detection_boxes': A [batch_size, max_detections, 4] float32 tensor + containing the non-max suppressed boxes. + 'detection_scores': A [bath_size, max_detections] float32 tensor containing + the scores for the boxes. + 'detection_classes': A [batch_size, max_detections] float32 tensor + containing the class for boxes. + 'num_detections': A [batchsize] float32 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. + 'detection_masks': (optional) a + [batch_size, max_detections, mask_height, mask_width] float32 tensor + containing masks for each selected box. + + Raises: + ValueError: if iou_thresh is not in [0, 1] or if input boxlist does not have + a valid scores field. + """ + q = boxes.shape[2].value + num_classes = scores.shape[2].value + 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') + + with tf.name_scope(scope, 'BatchMultiClassNonMaxSuppression'): + per_image_boxes_list = tf.unstack(boxes) + per_image_scores_list = tf.unstack(scores) + num_valid_boxes_list = len(per_image_boxes_list) * [None] + per_image_masks_list = len(per_image_boxes_list) * [None] + if num_valid_boxes is not None: + num_valid_boxes_list = tf.unstack(num_valid_boxes) + if masks is not None: + per_image_masks_list = tf.unstack(masks) + + detection_boxes_list = [] + detection_scores_list = [] + detection_classes_list = [] + num_detections_list = [] + detection_masks_list = [] + for (per_image_boxes, per_image_scores, per_image_masks, num_valid_boxes + ) in zip(per_image_boxes_list, per_image_scores_list, + per_image_masks_list, num_valid_boxes_list): + if num_valid_boxes is not None: + per_image_boxes = tf.reshape( + tf.slice(per_image_boxes, 3*[0], + tf.stack([num_valid_boxes, -1, -1])), [-1, q, 4]) + per_image_scores = tf.reshape( + tf.slice(per_image_scores, [0, 0], + tf.stack([num_valid_boxes, -1])), [-1, num_classes]) + if masks is not None: + per_image_masks = tf.reshape( + tf.slice(per_image_masks, 4*[0], + tf.stack([num_valid_boxes, -1, -1, -1])), + [-1, q, masks.shape[3].value, masks.shape[4].value]) + nmsed_boxlist = multiclass_non_max_suppression( + per_image_boxes, + per_image_scores, + score_thresh, + iou_thresh, + max_size_per_class, + max_total_size, + masks=per_image_masks, + clip_window=clip_window, + change_coordinate_frame=change_coordinate_frame) + num_detections_list.append(tf.to_float(nmsed_boxlist.num_boxes())) + padded_boxlist = box_list_ops.pad_or_clip_box_list(nmsed_boxlist, + max_total_size) + detection_boxes_list.append(padded_boxlist.get()) + detection_scores_list.append( + padded_boxlist.get_field(fields.BoxListFields.scores)) + detection_classes_list.append( + padded_boxlist.get_field(fields.BoxListFields.classes)) + if masks is not None: + detection_masks_list.append( + padded_boxlist.get_field(fields.BoxListFields.masks)) + + nms_dict = { + 'detection_boxes': tf.stack(detection_boxes_list), + 'detection_scores': tf.stack(detection_scores_list), + 'detection_classes': tf.stack(detection_classes_list), + 'num_detections': tf.stack(num_detections_list) + } + if masks is not None: + nms_dict['detection_masks'] = tf.stack(detection_masks_list) + return nms_dict diff --git a/object_detection/core/post_processing_test.py b/object_detection/core/post_processing_test.py new file mode 100644 index 000000000..d2fccec73 --- /dev/null +++ b/object_detection/core/post_processing_test.py @@ -0,0 +1,673 @@ +# 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 tensorflow_models.object_detection.core.post_processing.""" +import numpy as np +import tensorflow as tf +from object_detection.core import post_processing +from object_detection.core import standard_fields as fields + + +class MulticlassNonMaxSuppressionTest(tf.test.TestCase): + + def test_with_invalid_scores_size(self): + 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]]], tf.float32) + scores = tf.constant([[.9], [.75], [.6], [.95], [.5]]) + iou_thresh = .5 + score_thresh = 0.6 + max_output_size = 3 + nms = post_processing.multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, max_output_size) + with self.test_session() as sess: + with self.assertRaisesWithPredicateMatch( + tf.errors.InvalidArgumentError, 'Incorrect scores field length'): + sess.run(nms.get()) + + def test_multiclass_nms_select_with_shared_boxes(self): + 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 + + 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 = post_processing.multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, max_output_size) + with self.test_session() as sess: + nms_corners_output, nms_scores_output, nms_classes_output = sess.run( + [nms.get(), nms.get_field(fields.BoxListFields.scores), + nms.get_field(fields.BoxListFields.classes)]) + 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_multiclass_nms_select_with_shared_boxes_given_keypoints(self): + 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]]) + num_keypoints = 6 + keypoints = tf.tile( + tf.reshape(tf.range(8), [8, 1, 1]), + [1, num_keypoints, 2]) + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 4 + + 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] + exp_nms_keypoints_tensor = tf.tile( + tf.reshape(tf.constant([3, 0, 6, 5], dtype=tf.float32), [4, 1, 1]), + [1, num_keypoints, 2]) + + nms = post_processing.multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, max_output_size, + additional_fields={ + fields.BoxListFields.keypoints: keypoints}) + + with self.test_session() as sess: + (nms_corners_output, + nms_scores_output, + nms_classes_output, + nms_keypoints, + exp_nms_keypoints) = sess.run([ + nms.get(), + nms.get_field(fields.BoxListFields.scores), + nms.get_field(fields.BoxListFields.classes), + nms.get_field(fields.BoxListFields.keypoints), + exp_nms_keypoints_tensor + ]) + self.assertAllClose(nms_corners_output, exp_nms_corners) + self.assertAllClose(nms_scores_output, exp_nms_scores) + self.assertAllClose(nms_classes_output, exp_nms_classes) + self.assertAllEqual(nms_keypoints, exp_nms_keypoints) + + def test_multiclass_nms_with_shared_boxes_given_keypoint_heatmaps(self): + 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]]) + + num_boxes = tf.shape(boxes)[0] + heatmap_height = 5 + heatmap_width = 5 + num_keypoints = 17 + keypoint_heatmaps = tf.ones( + [num_boxes, heatmap_height, heatmap_width, num_keypoints], + dtype=tf.float32) + + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 4 + 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] + exp_nms_keypoint_heatmaps = np.ones( + (4, heatmap_height, heatmap_width, num_keypoints), dtype=np.float32) + + nms = post_processing.multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, max_output_size, + additional_fields={ + fields.BoxListFields.keypoint_heatmaps: keypoint_heatmaps}) + + with self.test_session() as sess: + (nms_corners_output, + nms_scores_output, + nms_classes_output, + nms_keypoint_heatmaps) = sess.run( + [nms.get(), + nms.get_field(fields.BoxListFields.scores), + nms.get_field(fields.BoxListFields.classes), + nms.get_field(fields.BoxListFields.keypoint_heatmaps)]) + + self.assertAllClose(nms_corners_output, exp_nms_corners) + self.assertAllClose(nms_scores_output, exp_nms_scores) + self.assertAllClose(nms_classes_output, exp_nms_classes) + self.assertAllEqual(nms_keypoint_heatmaps, exp_nms_keypoint_heatmaps) + + def test_multiclass_nms_with_additional_fields(self): + 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]]) + + coarse_boxes_key = 'coarse_boxes' + coarse_boxes = tf.constant([[0.1, 0.1, 1.1, 1.1], + [0.1, 0.2, 1.1, 1.2], + [0.1, -0.2, 1.1, 1.0], + [0.1, 10.1, 1.1, 11.1], + [0.1, 10.2, 1.1, 11.2], + [0.1, 100.1, 1.1, 101.1], + [0.1, 1000.1, 1.1, 1002.1], + [0.1, 1000.1, 1.1, 1002.2]], tf.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, 1000, 1, 1002], + [0, 100, 1, 101]], dtype=np.float32) + + exp_nms_coarse_corners = np.array([[0.1, 10.1, 1.1, 11.1], + [0.1, 0.1, 1.1, 1.1], + [0.1, 1000.1, 1.1, 1002.1], + [0.1, 100.1, 1.1, 101.1]], + dtype=np.float32) + + exp_nms_scores = [.95, .9, .85, .3] + exp_nms_classes = [0, 0, 1, 0] + + nms = post_processing.multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, max_output_size, + additional_fields={coarse_boxes_key: coarse_boxes}) + + with self.test_session() as sess: + (nms_corners_output, + nms_scores_output, + nms_classes_output, + nms_coarse_corners) = sess.run( + [nms.get(), + nms.get_field(fields.BoxListFields.scores), + nms.get_field(fields.BoxListFields.classes), + nms.get_field(coarse_boxes_key)]) + + self.assertAllClose(nms_corners_output, exp_nms_corners) + self.assertAllClose(nms_scores_output, exp_nms_scores) + self.assertAllClose(nms_classes_output, exp_nms_classes) + self.assertAllEqual(nms_coarse_corners, exp_nms_coarse_corners) + + def test_multiclass_nms_select_with_shared_boxes_given_masks(self): + 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]]) + num_classes = 2 + mask_height = 3 + mask_width = 3 + masks = tf.tile( + tf.reshape(tf.range(8), [8, 1, 1, 1]), + [1, num_classes, mask_height, mask_width]) + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 4 + + 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] + exp_nms_masks_tensor = tf.tile( + tf.reshape(tf.constant([3, 0, 6, 5], dtype=tf.float32), [4, 1, 1]), + [1, mask_height, mask_width]) + + nms = post_processing.multiclass_non_max_suppression(boxes, scores, + score_thresh, + iou_thresh, + max_output_size, + masks=masks) + with self.test_session() as sess: + (nms_corners_output, + nms_scores_output, + nms_classes_output, + nms_masks, + exp_nms_masks) = sess.run([nms.get(), + nms.get_field(fields.BoxListFields.scores), + nms.get_field(fields.BoxListFields.classes), + nms.get_field(fields.BoxListFields.masks), + exp_nms_masks_tensor]) + self.assertAllClose(nms_corners_output, exp_nms_corners) + self.assertAllClose(nms_scores_output, exp_nms_scores) + self.assertAllClose(nms_classes_output, exp_nms_classes) + self.assertAllEqual(nms_masks, exp_nms_masks) + + def test_multiclass_nms_select_with_clip_window(self): + boxes = tf.constant([[[0, 0, 10, 10]], + [[1, 1, 11, 11]]], tf.float32) + scores = tf.constant([[.9], [.75]]) + clip_window = tf.constant([5, 4, 8, 7], tf.float32) + score_thresh = 0.0 + iou_thresh = 0.5 + max_output_size = 100 + + exp_nms_corners = [[5, 4, 8, 7]] + exp_nms_scores = [.9] + exp_nms_classes = [0] + + nms = post_processing.multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, max_output_size, + clip_window=clip_window) + with self.test_session() as sess: + nms_corners_output, nms_scores_output, nms_classes_output = sess.run( + [nms.get(), nms.get_field(fields.BoxListFields.scores), + nms.get_field(fields.BoxListFields.classes)]) + 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_multiclass_nms_select_with_clip_window_change_coordinate_frame(self): + boxes = tf.constant([[[0, 0, 10, 10]], + [[1, 1, 11, 11]]], tf.float32) + scores = tf.constant([[.9], [.75]]) + clip_window = tf.constant([5, 4, 8, 7], tf.float32) + score_thresh = 0.0 + iou_thresh = 0.5 + max_output_size = 100 + + exp_nms_corners = [[0, 0, 1, 1]] + exp_nms_scores = [.9] + exp_nms_classes = [0] + + nms = post_processing.multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, max_output_size, + clip_window=clip_window, change_coordinate_frame=True) + with self.test_session() as sess: + nms_corners_output, nms_scores_output, nms_classes_output = sess.run( + [nms.get(), nms.get_field(fields.BoxListFields.scores), + nms.get_field(fields.BoxListFields.classes)]) + 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_multiclass_nms_select_with_per_class_cap(self): + 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_size_per_class = 2 + + exp_nms_corners = [[0, 10, 1, 11], + [0, 0, 1, 1], + [0, 1000, 1, 1002]] + exp_nms_scores = [.95, .9, .85] + exp_nms_classes = [0, 0, 1] + + nms = post_processing.multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, max_size_per_class) + with self.test_session() as sess: + nms_corners_output, nms_scores_output, nms_classes_output = sess.run( + [nms.get(), nms.get_field(fields.BoxListFields.scores), + nms.get_field(fields.BoxListFields.classes)]) + 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_multiclass_nms_select_with_total_cap(self): + 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_size_per_class = 4 + max_total_size = 2 + + exp_nms_corners = [[0, 10, 1, 11], + [0, 0, 1, 1]] + exp_nms_scores = [.95, .9] + exp_nms_classes = [0, 0] + + nms = post_processing.multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, max_size_per_class, + max_total_size) + with self.test_session() as sess: + nms_corners_output, nms_scores_output, nms_classes_output = sess.run( + [nms.get(), nms.get_field(fields.BoxListFields.scores), + nms.get_field(fields.BoxListFields.classes)]) + 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_multiclass_nms_threshold_then_select_with_shared_boxes(self): + 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], [.75], [.6], [.95], [.5], [.3], [.01], [.01]]) + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 3 + + exp_nms = [[0, 10, 1, 11], + [0, 0, 1, 1], + [0, 100, 1, 101]] + nms = post_processing.multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, max_output_size) + with self.test_session() as sess: + nms_output = sess.run(nms.get()) + self.assertAllClose(nms_output, exp_nms) + + def test_multiclass_nms_select_with_separate_boxes(self): + boxes = tf.constant([[[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]]], + 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 + + 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] + + nms = post_processing.multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, max_output_size) + with self.test_session() as sess: + nms_corners_output, nms_scores_output, nms_classes_output = sess.run( + [nms.get(), nms.get_field(fields.BoxListFields.scores), + nms.get_field(fields.BoxListFields.classes)]) + 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_batch_multiclass_nms_with_batch_size_1(self): + boxes = tf.constant([[[[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]]]], + 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 + + 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]] + + nms_dict = 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) + with self.test_session() as sess: + nms_output = sess.run(nms_dict) + self.assertAllClose(nms_output['detection_boxes'], exp_nms_corners) + self.assertAllClose(nms_output['detection_scores'], exp_nms_scores) + self.assertAllClose(nms_output['detection_classes'], exp_nms_classes) + self.assertEqual(nms_output['num_detections'], [4]) + + def test_batch_multiclass_nms_with_batch_size_2(self): + boxes = tf.constant([[[[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]]]], + 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 + + exp_nms_corners = [[[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 = [[.95, .9, 0, 0], + [.85, .5, .3, 0]] + exp_nms_classes = [[0, 0, 0, 0], + [1, 0, 0, 0]] + + nms_dict = 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) + with self.test_session() as sess: + nms_output = sess.run(nms_dict) + self.assertAllClose(nms_output['detection_boxes'], exp_nms_corners) + self.assertAllClose(nms_output['detection_scores'], exp_nms_scores) + self.assertAllClose(nms_output['detection_classes'], exp_nms_classes) + self.assertAllClose(nms_output['num_detections'], [2, 3]) + + def test_batch_multiclass_nms_with_masks(self): + boxes = tf.constant([[[[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]]]], + 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]]]) + masks = tf.constant([[[[[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]]]]], + tf.float32) + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 4 + + exp_nms_corners = [[[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 = [[.95, .9, 0, 0], + [.85, .5, .3, 0]] + exp_nms_classes = [[0, 0, 0, 0], + [1, 0, 0, 0]] + exp_nms_masks = [[[[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]]]] + + nms_dict = 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) + with self.test_session() as sess: + nms_output = sess.run(nms_dict) + self.assertAllClose(nms_output['detection_boxes'], exp_nms_corners) + self.assertAllClose(nms_output['detection_scores'], exp_nms_scores) + self.assertAllClose(nms_output['detection_classes'], exp_nms_classes) + self.assertAllClose(nms_output['num_detections'], [2, 3]) + self.assertAllClose(nms_output['detection_masks'], exp_nms_masks) + + def test_batch_multiclass_nms_with_masks_and_num_valid_boxes(self): + boxes = tf.constant([[[[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]]]], + 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]]]) + masks = tf.constant([[[[[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]]]]], + tf.float32) + num_valid_boxes = tf.constant([1, 1], tf.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]]]] + + nms_dict = 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, masks=masks) + with self.test_session() as sess: + nms_output = sess.run(nms_dict) + self.assertAllClose(nms_output['detection_boxes'], exp_nms_corners) + self.assertAllClose(nms_output['detection_scores'], exp_nms_scores) + self.assertAllClose(nms_output['detection_classes'], exp_nms_classes) + self.assertAllClose(nms_output['num_detections'], [1, 1]) + self.assertAllClose(nms_output['detection_masks'], exp_nms_masks) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/core/prefetcher.py b/object_detection/core/prefetcher.py new file mode 100644 index 000000000..ba5958f62 --- /dev/null +++ b/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 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 = 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.to_float(prefetch_queue.size()) * (1. / capacity)) + return prefetch_queue diff --git a/object_detection/core/prefetcher_test.py b/object_detection/core/prefetcher_test.py new file mode 100644 index 000000000..63f557e33 --- /dev/null +++ b/object_detection/core/prefetcher_test.py @@ -0,0 +1,101 @@ +# 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.prefetcher.""" +import tensorflow as tf + +from object_detection.core import prefetcher + +slim = tf.contrib.slim + + +class PrefetcherTest(tf.test.TestCase): + + def test_prefetch_tensors_with_fully_defined_shapes(self): + with self.test_session() as sess: + batch_size = 10 + image_size = 32 + num_batches = 5 + examples = tf.Variable(tf.constant(0, dtype=tf.int64)) + counter = examples.count_up_to(num_batches) + image = tf.random_normal([batch_size, image_size, + image_size, 3], + dtype=tf.float32, + name='images') + label = tf.random_uniform([batch_size, 1], 0, 10, + dtype=tf.int32, name='labels') + + prefetch_queue = prefetcher.prefetch(tensor_dict={'counter': counter, + 'image': image, + 'label': label}, + capacity=100) + tensor_dict = prefetch_queue.dequeue() + + self.assertAllEqual(tensor_dict['image'].get_shape().as_list(), + [batch_size, image_size, image_size, 3]) + self.assertAllEqual(tensor_dict['label'].get_shape().as_list(), + [batch_size, 1]) + + tf.initialize_all_variables().run() + with slim.queues.QueueRunners(sess): + for _ in range(num_batches): + results = sess.run(tensor_dict) + self.assertEquals(results['image'].shape, + (batch_size, image_size, image_size, 3)) + self.assertEquals(results['label'].shape, (batch_size, 1)) + with self.assertRaises(tf.errors.OutOfRangeError): + sess.run(tensor_dict) + + def test_prefetch_tensors_with_partially_defined_shapes(self): + with self.test_session() as sess: + batch_size = 10 + image_size = 32 + num_batches = 5 + examples = tf.Variable(tf.constant(0, dtype=tf.int64)) + counter = examples.count_up_to(num_batches) + image = tf.random_normal([batch_size, + tf.Variable(image_size), + tf.Variable(image_size), 3], + dtype=tf.float32, + name='image') + image.set_shape([batch_size, None, None, 3]) + label = tf.random_uniform([batch_size, tf.Variable(1)], 0, + 10, dtype=tf.int32, name='label') + label.set_shape([batch_size, None]) + + prefetch_queue = prefetcher.prefetch(tensor_dict={'counter': counter, + 'image': image, + 'label': label}, + capacity=100) + tensor_dict = prefetch_queue.dequeue() + + self.assertAllEqual(tensor_dict['image'].get_shape().as_list(), + [batch_size, None, None, 3]) + self.assertAllEqual(tensor_dict['label'].get_shape().as_list(), + [batch_size, None]) + + tf.initialize_all_variables().run() + with slim.queues.QueueRunners(sess): + for _ in range(num_batches): + results = sess.run(tensor_dict) + self.assertEquals(results['image'].shape, + (batch_size, image_size, image_size, 3)) + self.assertEquals(results['label'].shape, (batch_size, 1)) + with self.assertRaises(tf.errors.OutOfRangeError): + sess.run(tensor_dict) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/core/preprocessor.py b/object_detection/core/preprocessor.py new file mode 100644 index 000000000..3fdcb6138 --- /dev/null +++ b/object_detection/core/preprocessor.py @@ -0,0 +1,1922 @@ +# 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. +# ============================================================================== + +"""Preprocess images and bounding boxes for detection. + +We perform two sets of operations in preprocessing stage: +(a) operations that are applied to both training and testing data, +(b) operations that are applied only to training data for the purpose of + data augmentation. + +A preprocessing function receives a set of inputs, +e.g. an image and bounding boxes, +performs an operation on them, and returns them. +Some examples are: randomly cropping the image, randomly mirroring the image, + randomly changing the brightness, contrast, hue and + randomly jittering the bounding boxes. + +The preprocess function receives a tensor_dict which is a dictionary that maps +different field names to their tensors. For example, +tensor_dict[fields.InputDataFields.image] holds the image tensor. +The image is a rank 4 tensor: [1, height, width, channels] with +dtype=tf.float32. The groundtruth_boxes is a rank 2 tensor: [N, 4] where +in each row there is a box with [ymin xmin ymax xmax]. +Boxes are in normalized coordinates meaning +their coordinate values range in [0, 1] + +Important Note: In tensor_dict, images is a rank 4 tensor, but preprocessing +functions receive a rank 3 tensor for processing the image. Thus, inside the +preprocess function we squeeze the image to become a rank 3 tensor and then +we pass it to the functions. At the end of the preprocess we expand the image +back to rank 4. +""" + +import sys +import tensorflow as tf + +from tensorflow.python.ops import control_flow_ops + +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 + + +def _apply_with_random_selector(x, func, num_cases): + """Computes func(x, sel), with sel sampled from [0...num_cases-1]. + + Args: + x: input Tensor. + func: Python function to apply. + num_cases: Python int32, number of cases to sample sel from. + + Returns: + The result of func(x, sel), where func receives the value of the + selector as a python integer, but sel is sampled dynamically. + """ + rand_sel = tf.random_uniform([], maxval=num_cases, dtype=tf.int32) + # Pass the real x only to one of the func calls. + return control_flow_ops.merge([func( + control_flow_ops.switch(x, tf.equal(rand_sel, case))[1], case) + for case in range(num_cases)])[0] + + +def _apply_with_random_selector_tuples(x, func, num_cases): + """Computes func(x, sel), with sel sampled from [0...num_cases-1]. + + Args: + x: A tuple of input tensors. + func: Python function to apply. + num_cases: Python int32, number of cases to sample sel from. + + Returns: + The result of func(x, sel), where func receives the value of the + selector as a python integer, but sel is sampled dynamically. + """ + num_inputs = len(x) + rand_sel = tf.random_uniform([], maxval=num_cases, dtype=tf.int32) + # Pass the real x only to one of the func calls. + + tuples = [list() for t in x] + for case in range(num_cases): + new_x = [control_flow_ops.switch(t, tf.equal(rand_sel, case))[1] for t in x] + output = func(tuple(new_x), case) + for j in range(num_inputs): + tuples[j].append(output[j]) + + for i in range(num_inputs): + tuples[i] = control_flow_ops.merge(tuples[i])[0] + return tuple(tuples) + + +def _random_integer(minval, maxval, seed): + """Returns a random 0-D tensor between minval and maxval. + + Args: + minval: minimum value of the random tensor. + maxval: maximum value of the random tensor. + seed: random seed. + + Returns: + A random 0-D tensor between minval and maxval. + """ + return tf.random_uniform( + [], minval=minval, maxval=maxval, dtype=tf.int32, seed=seed) + + +def normalize_image(image, original_minval, original_maxval, target_minval, + target_maxval): + """Normalizes pixel values in the image. + + Moves the pixel values from the current [original_minval, original_maxval] + range to a the [target_minval, target_maxval] range. + + Args: + image: rank 3 float32 tensor containing 1 + image -> [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.to_float(image) + 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 flip_boxes(boxes): + """Left-right 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. + """ + # Flip 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], 1) + return flipped_boxes + + +def retain_boxes_above_threshold( + boxes, labels, label_scores, masks=None, keypoints=None, threshold=0.0): + """Retains boxes whose label score is above a given threshold. + + If the label score 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_scores: float32 tensor of shape [num_instance] representing the + score 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. + threshold: scalar python float. + + Returns: + retained_boxes: [num_retained_instance, 4] + retianed_labels: [num_retained_instance] + retained_label_scores: [num_retained_instance] + + If masks, or keypoints are not None, the function also returns: + + retained_masks: [num_retained_instance, height, width] + retained_keypoints: [num_retained_instance, num_keypoints, 2] + """ + with tf.name_scope('RetainBoxesAboveThreshold', + values=[boxes, labels, label_scores]): + indices = tf.where( + tf.logical_or(label_scores > threshold, tf.is_nan(label_scores))) + indices = tf.squeeze(indices, axis=1) + retained_boxes = tf.gather(boxes, indices) + retained_labels = tf.gather(labels, indices) + retained_label_scores = tf.gather(label_scores, indices) + result = [retained_boxes, retained_labels, retained_label_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 _flip_masks(masks): + """Left-right flips 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 random_horizontal_flip( + image, + boxes=None, + masks=None, + keypoints=None, + keypoint_flip_permutation=None, + seed=None): + """Randomly decides whether to mirror the image and detections or not. + + 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 keypoint flip + permutation. + seed: random seed + + Returns: + image: image which is the same shape as input image. + + If boxes, masks, keypoints, and keypoint_flip_permutation is 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_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') + + with tf.name_scope('RandomHorizontalFlip', values=[image, boxes]): + result = [] + # random variable defining whether to do flip or not + do_a_flip_random = tf.random_uniform([], seed=seed) + # flip only if there are bounding boxes in image! + do_a_flip_random = tf.logical_and( + tf.greater(tf.size(boxes), 0), tf.greater(do_a_flip_random, 0.5)) + + # 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(boxes), lambda: boxes) + result.append(boxes) + + # flip masks + if masks is not None: + masks = tf.cond( + do_a_flip_random, lambda: _flip_masks(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) + + return tuple(result) + + +def random_pixel_value_scale(image, minval=0.9, maxval=1.1, seed=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, 1]. + minval: lower ratio of scaling pixel values. + maxval: upper ratio of scaling pixel values. + seed: random seed. + + 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('RandomPixelValueScale', values=[image]): + color_coef = tf.random_uniform( + tf.shape(image), + minval=minval, + maxval=maxval, + dtype=tf.float32, + seed=seed) + image = tf.multiply(image, color_coef) + image = tf.clip_by_value(image, 0.0, 1.0) + + return image + + +def random_image_scale(image, + masks=None, + min_scale_ratio=0.5, + max_scale_ratio=2.0, + seed=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. + + 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] + size_coef = tf.random_uniform([], + minval=min_scale_ratio, + maxval=max_scale_ratio, + dtype=tf.float32, seed=seed) + image_newysize = tf.to_int32( + tf.multiply(tf.to_float(image_height), size_coef)) + image_newxsize = tf.to_int32( + tf.multiply(tf.to_float(image_width), size_coef)) + image = tf.image.resize_images( + image, [image_newysize, image_newxsize], align_corners=True) + result.append(image) + if masks: + masks = tf.image.resize_nearest_neighbor( + masks, [image_newysize, image_newxsize], align_corners=True) + result.append(masks) + return tuple(result) + + +def random_rgb_to_gray(image, probability=0.1, seed=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, 1]. + probability: the probability of returning a grayscale image. + The probability should be a number between [0, 1]. + seed: random seed. + + Returns: + image: image which is the same shape as input image. + """ + def _image_to_gray(image): + image_gray1 = tf.image.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 do flip or not + do_gray_random = tf.random_uniform([], seed=seed) + + image = tf.cond( + tf.greater(do_gray_random, probability), lambda: image, + lambda: _image_to_gray(image)) + + return image + + +def random_adjust_brightness(image, max_delta=0.2): + """Randomly adjusts brightness. + + Makes sure the output image is still between 0 and 1. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + max_delta: how much to change the brightness. A value between [0, 1). + + 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]): + image = tf.image.random_brightness(image, max_delta) + image = tf.clip_by_value(image, clip_value_min=0.0, clip_value_max=1.0) + return image + + +def random_adjust_contrast(image, min_delta=0.8, max_delta=1.25): + """Randomly adjusts contrast. + + Makes sure the output image is still between 0 and 1. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + 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. + + Returns: + image: image which is the same shape as input image. + """ + with tf.name_scope('RandomAdjustContrast', values=[image]): + image = tf.image.random_contrast(image, min_delta, max_delta) + image = tf.clip_by_value(image, clip_value_min=0.0, clip_value_max=1.0) + return image + + +def random_adjust_hue(image, max_delta=0.02): + """Randomly adjusts hue. + + Makes sure the output image is still between 0 and 1. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + max_delta: change hue randomly with a value between 0 and max_delta. + + Returns: + image: image which is the same shape as input image. + """ + with tf.name_scope('RandomAdjustHue', values=[image]): + image = tf.image.random_hue(image, max_delta) + image = tf.clip_by_value(image, clip_value_min=0.0, clip_value_max=1.0) + return image + + +def random_adjust_saturation(image, min_delta=0.8, max_delta=1.25): + """Randomly adjusts saturation. + + Makes sure the output image is still between 0 and 1. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + 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. + + Returns: + image: image which is the same shape as input image. + """ + with tf.name_scope('RandomAdjustSaturation', values=[image]): + image = tf.image.random_saturation(image, min_delta, max_delta) + image = tf.clip_by_value(image, clip_value_min=0.0, clip_value_max=1.0) + return image + + +def random_distort_color(image, color_ordering=0): + """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 1. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + color_ordering: Python int, a type of distortion (valid values: 0, 1). + + 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 = tf.image.random_brightness(image, max_delta=32. / 255.) + image = tf.image.random_saturation(image, lower=0.5, upper=1.5) + image = tf.image.random_hue(image, max_delta=0.2) + image = tf.image.random_contrast(image, lower=0.5, upper=1.5) + elif color_ordering == 1: + image = tf.image.random_brightness(image, max_delta=32. / 255.) + image = tf.image.random_contrast(image, lower=0.5, upper=1.5) + image = tf.image.random_saturation(image, lower=0.5, upper=1.5) + image = tf.image.random_hue(image, max_delta=0.2) + else: + raise ValueError('color_ordering must be in {0, 1}') + + # The random_* ops do not necessarily clamp. + image = tf.clip_by_value(image, 0.0, 1.0) + 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, + masks=None, + keypoints=None, + min_object_covered=1.0, + aspect_ratio_range=(0.75, 1.33), + area_range=(0.1, 1.0), + overlap_thresh=0.3): + """Performs random crop. + + Note: boxes will be clipped to the crop. 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 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. + 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. + + 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] + """ + with tf.name_scope('RandomCropImage', values=[image, boxes]): + 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) + + sample_distorted_bounding_box = 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) + + im_box_begin, im_box_size, im_box = sample_distorted_bounding_box + + new_image = tf.slice(image, im_box_begin, im_box_size) + new_image.set_shape([None, None, image.get_shape()[2]]) + + # [1, 4] + im_box_rank2 = tf.squeeze(im_box, squeeze_dims=[0]) + # [4] + im_box_rank1 = tf.squeeze(im_box) + + boxlist = box_list.BoxList(boxes) + boxlist.add_field('labels', labels) + + 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() + 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 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) + masks_box_begin = [im_box_begin[2], im_box_begin[0], im_box_begin[1]] + masks_box_size = [im_box_size[2], im_box_size[0], im_box_size[1]] + new_masks = tf.slice( + masks_of_boxes_completely_inside_window, + masks_box_begin, masks_box_size) + 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) + 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_crop_image(image, + boxes, + labels, + masks=None, + keypoints=None, + min_object_covered=1.0, + aspect_ratio_range=(0.75, 1.33), + area_range=(0.1, 1.0), + overlap_thresh=0.3, + random_coef=0.0, + seed=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: boxes will be clipped to the crop. Keypoint coordinates that are + outside the crop will be set to NaN, which is consistent with the original + keypoint encoding for non-existing keypoints. + + 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. + 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. + 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. + + 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 masks, or keypoints are 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] + """ + + def strict_random_crop_image_fn(): + return _strict_random_crop_image( + image, + boxes, + labels, + masks=masks, + keypoints=keypoints, + min_object_covered=min_object_covered, + aspect_ratio_range=aspect_ratio_range, + area_range=area_range, + overlap_thresh=overlap_thresh) + + # 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: + do_a_crop_random = tf.random_uniform([], seed=seed) + do_a_crop_random = tf.greater(do_a_crop_random, random_coef) + + outputs = [image, boxes, labels] + if masks is not None: + outputs.append(masks) + if keypoints is not None: + outputs.append(keypoints) + + result = tf.cond(do_a_crop_random, + strict_random_crop_image_fn, + lambda: tuple(outputs)) + return result + + +def random_pad_image(image, + boxes, + min_image_size=None, + max_image_size=None, + pad_color=None, + seed=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]. + 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 [3] with dtype=tf.float32. + if set as None, it will be set to average color of the input + image. + + seed: random seed. + + 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 pad_color is None: + pad_color = tf.reduce_mean(image, reduction_indices=[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)) + + 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_paded = (1.0 - image_ones_padded) * pad_color + new_image += image_color_paded + + # setting boxes + new_window = tf.to_float( + tf.stack([ + -offset_height, -offset_width, target_height - offset_height, + target_width - offset_width + ])) + new_window /= tf.to_float( + tf.stack([image_height, image_width, image_height, image_width])) + boxlist = box_list.BoxList(boxes) + new_boxlist = box_list_ops.change_coordinate_frame(boxlist, new_window) + new_boxes = new_boxlist.get() + + return new_image, new_boxes + + +def random_crop_pad_image(image, + boxes, + labels, + min_object_covered=1.0, + aspect_ratio_range=(0.75, 1.33), + area_range=(0.1, 1.0), + overlap_thresh=0.3, + random_coef=0.0, + min_padded_size_ratio=None, + max_padded_size_ratio=None, + pad_color=None, + seed=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. + 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. + 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. If None, it will + be set to [0.0, 0.0]. + max_padded_size_ratio: max ratio of padded image height and width to the + input image's height and width. If None, it will + be set to [2.0, 2.0]. + 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. + + 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. + """ + image_size = tf.shape(image) + image_height = image_size[0] + image_width = image_size[1] + if min_padded_size_ratio is None: + min_padded_size_ratio = tf.constant([0.0, 0.0], tf.float32) + if max_padded_size_ratio is None: + max_padded_size_ratio = tf.constant([2.0, 2.0], tf.float32) + cropped_image, cropped_boxes, cropped_labels = random_crop_image( + image=image, + boxes=boxes, + labels=labels, + min_object_covered=min_object_covered, + aspect_ratio_range=aspect_ratio_range, + area_range=area_range, + overlap_thresh=overlap_thresh, + random_coef=random_coef, + seed=seed) + + min_image_size = tf.to_int32( + tf.to_float(tf.stack([image_height, image_width])) * + min_padded_size_ratio) + max_image_size = tf.to_int32( + tf.to_float(tf.stack([image_height, image_width])) * + max_padded_size_ratio) + + 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) + + return padded_image, padded_boxes, cropped_labels + + +def random_crop_to_aspect_ratio(image, + boxes, + labels, + masks=None, + keypoints=None, + aspect_ratio=1.0, + overlap_thresh=0.3, + seed=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. + 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. + 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. + + 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('RandomCropToAspectRatio', values=[image]): + image_shape = tf.shape(image) + orig_height = image_shape[0] + orig_width = image_shape[1] + orig_aspect_ratio = tf.to_float(orig_width) / tf.to_float(orig_height) + new_aspect_ratio = tf.constant(aspect_ratio, dtype=tf.float32) + def target_height_fn(): + return tf.to_int32( + tf.round( + tf.to_float(orig_height) * orig_aspect_ratio / new_aspect_ratio)) + target_height = tf.cond( + orig_aspect_ratio >= new_aspect_ratio, + lambda: orig_height, + target_height_fn) + def target_width_fn(): + return tf.to_int32( + tf.round( + tf.to_float(orig_width) * new_aspect_ratio / orig_aspect_ratio)) + 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) + new_image = tf.image.crop_to_bounding_box( + image, offset_height, offset_width, target_height, target_width) + + im_box = tf.stack([ + tf.to_float(offset_height) / tf.to_float(orig_height), + tf.to_float(offset_width) / tf.to_float(orig_width), + tf.to_float(offset_height + target_height) / tf.to_float(orig_height), + tf.to_float(offset_width + target_width) / tf.to_float(orig_width) + ]) + + boxlist = box_list.BoxList(boxes) + boxlist.add_field('labels', labels) + + 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) + 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] + + 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) + 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_black_patches(image, + max_black_patches=10, + probability=0.5, + size_to_image_ratio=0.1, + random_seed=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. + + Returns: + image + """ + def add_black_patch_to_image(image): + """Function for adding one patch to the image. + + Args: + image: image + + 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.to_int32( + tf.multiply( + tf.minimum(tf.to_float(image_height), tf.to_float(image_width)), + size_to_image_ratio)) + normalized_y_min = tf.random_uniform( + [], minval=0.0, maxval=(1.0 - size_to_image_ratio), seed=random_seed) + normalized_x_min = tf.random_uniform( + [], minval=0.0, maxval=(1.0 - size_to_image_ratio), seed=random_seed) + y_min = tf.to_int32(normalized_y_min * tf.to_float(image_height)) + x_min = tf.to_int32(normalized_x_min * tf.to_float(image_width)) + 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 _ in range(max_black_patches): + random_prob = tf.random_uniform([], minval=0.0, maxval=1.0, + dtype=tf.float32, seed=random_seed) + image = tf.cond( + tf.greater(random_prob, probability), lambda: image, + lambda: add_black_patch_to_image(image)) + + return image + + +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.to_float(image) + return image + + +def random_resize_method(image, target_size): + """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] + + Returns: + resized image. + """ + + resized_image = _apply_with_random_selector( + image, + lambda x, method: tf.image.resize_images(x, target_size, method), + num_cases=4) + + return resized_image + + +def resize_to_range(image, + masks=None, + min_dimension=None, + max_dimension=None, + align_corners=False): + """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. + align_corners: bool. If true, exactly align all 4 corners of the input + and output. Defaults to False. + + Returns: + 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. + + If masks is not None, also outputs masks: + A 3D tensor of shape [num_instances, new_height, new_width] + + 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('ResizeToRange', values=[image, min_dimension]): + image_shape = tf.shape(image) + orig_height = tf.to_float(image_shape[0]) + orig_width = tf.to_float(image_shape[1]) + orig_min_dim = tf.minimum(orig_height, orig_width) + + # Calculates the larger of the possible sizes + min_dimension = tf.constant(min_dimension, dtype=tf.float32) + large_scale_factor = min_dimension / orig_min_dim + # Scaling orig_(height|width) by large_scale_factor will make the smaller + # dimension equal to min_dimension, save for floating point rounding errors. + # For reasonably-sized images, taking the nearest integer will reliably + # eliminate this error. + large_height = tf.to_int32(tf.round(orig_height * large_scale_factor)) + large_width = tf.to_int32(tf.round(orig_width * large_scale_factor)) + large_size = tf.stack([large_height, large_width]) + + if max_dimension: + # Calculates the smaller of the possible sizes, use that if the larger + # is too big. + orig_max_dim = tf.maximum(orig_height, orig_width) + max_dimension = tf.constant(max_dimension, dtype=tf.float32) + small_scale_factor = max_dimension / orig_max_dim + # Scaling orig_(height|width) by small_scale_factor will make the larger + # dimension equal to max_dimension, save for floating point rounding + # errors. For reasonably-sized images, taking the nearest integer will + # reliably eliminate this error. + small_height = tf.to_int32(tf.round(orig_height * small_scale_factor)) + small_width = tf.to_int32(tf.round(orig_width * small_scale_factor)) + small_size = tf.stack([small_height, small_width]) + + new_size = tf.cond( + tf.to_float(tf.reduce_max(large_size)) > max_dimension, + lambda: small_size, lambda: large_size) + else: + new_size = large_size + + new_image = tf.image.resize_images(image, new_size, + align_corners=align_corners) + + result = new_image + if masks is not None: + num_instances = tf.shape(masks)[0] + + 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(): + new_masks = tf.reshape(masks, [0, new_size[0], new_size[1]]) + return new_masks + + masks = tf.cond(num_instances > 0, + resize_masks_branch, + reshape_masks_branch) + result = [new_image, masks] + + 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) + + +# 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): + """See `tf.image.resize_images` for detailed doc.""" + with tf.name_scope( + 'ResizeImage', + values=[image, new_height, new_width, method, align_corners]): + new_image = tf.image.resize_images(image, [new_height, new_width], + method=method, + align_corners=align_corners) + result = new_image + if masks is not None: + num_instances = tf.shape(masks)[0] + new_size = tf.constant([new_height, new_width], dtype=tf.int32) + 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(): + new_masks = tf.reshape(masks, [0, new_size[0], new_size[1]]) + return new_masks + + masks = tf.cond(num_instances > 0, + resize_masks_branch, + reshape_masks_branch) + result = [new_image, masks] + + 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 tf.image.rgb_to_grayscale(image) + + +def ssd_random_crop(image, + boxes, + labels, + 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), + random_coef=(0.15,) * 7, + seed=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. + 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. + 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. + + 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] + """ + 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_masks = None + selected_keypoints = None + 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, + 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], + random_coef=random_coef[index], + seed=seed) + + result = _apply_with_random_selector_tuples( + tuple( + t for t in (image, boxes, labels, masks, keypoints) if t is not None), + random_crop_selector, + num_cases=len(min_object_covered)) + return result + + +def ssd_random_crop_pad(image, + boxes, + labels, + 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), + random_coef=(0.15,) * 6, + min_padded_size_ratio=(None,) * 6, + max_padded_size_ratio=(None,) * 6, + pad_color=(None,) * 6, + seed=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. + 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. + 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. If None, it will + be set to [0.0, 0.0]. + max_padded_size_ratio: max ratio of padded image height and width to the + input image's height and width. If None, it will + be set to [2.0, 2.0]. + 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. + + 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. + """ + def random_crop_pad_selector(image_boxes_labels, index): + image, boxes, labels = image_boxes_labels + + return random_crop_pad_image( + image, + boxes, + labels, + min_object_covered=min_object_covered[index], + aspect_ratio_range=aspect_ratio_range[index], + area_range=area_range[index], + overlap_thresh=overlap_thresh[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) + + new_image, new_boxes, new_labels = _apply_with_random_selector_tuples( + (image, boxes, labels), + random_crop_pad_selector, + num_cases=len(min_object_covered)) + return new_image, new_boxes, new_labels + + +def ssd_random_crop_fixed_aspect_ratio( + image, + boxes, + labels, + 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), + random_coef=(0.15,) * 7, + seed=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. + 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. + 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. + + 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] + + """ + aspect_ratio_range = ((aspect_ratio, aspect_ratio),) * len(area_range) + + crop_result = ssd_random_crop(image, boxes, labels, masks, keypoints, + min_object_covered, aspect_ratio_range, + area_range, overlap_thresh, random_coef, seed) + i = 3 + new_image, new_boxes, new_labels = crop_result[:i] + new_masks = None + new_keypoints = None + 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, + new_masks, + new_keypoints, + aspect_ratio=aspect_ratio, + seed=seed) + + return result + + +def get_default_func_arg_map(include_instance_masks=False, + include_keypoints=False): + """Returns the default mapping from a preprocessor function to its args. + + Args: + include_instance_masks: If True, preprocessing functions will modify the + instance masks, too. + include_keypoints: If True, preprocessing functions will modify the + keypoints, too. + + Returns: + A map from preprocessing functions to the arguments they receive. + """ + 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 + + prep_func_arg_map = { + normalize_image: (fields.InputDataFields.image,), + random_horizontal_flip: (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_instance_masks, + groundtruth_keypoints,), + random_pad_image: (fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes), + random_crop_pad_image: (fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes), + random_crop_to_aspect_ratio: (fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + groundtruth_instance_masks, + groundtruth_keypoints,), + random_black_patches: (fields.InputDataFields.image,), + retain_boxes_above_threshold: ( + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + fields.InputDataFields.groundtruth_label_scores, + groundtruth_instance_masks, + groundtruth_keypoints,), + image_to_float: (fields.InputDataFields.image,), + random_resize_method: (fields.InputDataFields.image,), + resize_to_range: (fields.InputDataFields.image, + groundtruth_instance_masks,), + scale_boxes_to_pixel_coordinates: ( + fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + groundtruth_keypoints,), + flip_boxes: (fields.InputDataFields.groundtruth_boxes,), + 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,), + ssd_random_crop: (fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + groundtruth_instance_masks, + groundtruth_keypoints,), + ssd_random_crop_pad: (fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes), + ssd_random_crop_fixed_aspect_ratio: ( + fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + groundtruth_instance_masks, + groundtruth_keypoints,), + } + + return prep_func_arg_map + + +def preprocess(tensor_dict, preprocess_options, func_arg_map=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. + + 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, squeeze_dims=[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] + 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/object_detection/core/preprocessor_test.py b/object_detection/core/preprocessor_test.py new file mode 100644 index 000000000..109df7a6c --- /dev/null +++ b/object_detection/core/preprocessor_test.py @@ -0,0 +1,1746 @@ +# 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.preprocessor.""" + +import mock +import numpy as np + +import tensorflow as tf + +from object_detection.core import preprocessor +from object_detection.core import standard_fields as fields + + +class PreprocessorTest(tf.test.TestCase): + + def createColorfulTestImage(self): + ch255 = tf.fill([1, 100, 200, 1], tf.constant(255, dtype=tf.uint8)) + ch128 = tf.fill([1, 100, 200, 1], tf.constant(128, dtype=tf.uint8)) + ch0 = tf.fill([1, 100, 200, 1], tf.constant(0, dtype=tf.uint8)) + imr = tf.concat([ch255, ch0, ch0], 3) + img = tf.concat([ch255, ch255, ch0], 3) + imb = tf.concat([ch255, ch0, ch255], 3) + imw = tf.concat([ch128, ch128, ch128], 3) + imu = tf.concat([imr, img], 2) + imd = tf.concat([imb, imw], 2) + im = tf.concat([imu, imd], 1) + return im + + def createTestImages(self): + images_r = tf.constant([[[128, 128, 128, 128], [0, 0, 128, 128], + [0, 128, 128, 128], [192, 192, 128, 128]]], + dtype=tf.uint8) + images_r = tf.expand_dims(images_r, 3) + images_g = tf.constant([[[0, 0, 128, 128], [0, 0, 128, 128], + [0, 128, 192, 192], [192, 192, 128, 192]]], + dtype=tf.uint8) + images_g = tf.expand_dims(images_g, 3) + images_b = tf.constant([[[128, 128, 192, 0], [0, 0, 128, 192], + [0, 128, 128, 0], [192, 192, 192, 128]]], + dtype=tf.uint8) + images_b = tf.expand_dims(images_b, 3) + images = tf.concat([images_r, images_g, images_b], 3) + return images + + def createTestBoxes(self): + boxes = tf.constant( + [[0.0, 0.25, 0.75, 1.0], [0.25, 0.5, 0.75, 1.0]], dtype=tf.float32) + return boxes + + def createTestLabelScores(self): + return tf.constant([1.0, 0.5], dtype=tf.float32) + + def createTestLabelScoresWithMissingScore(self): + return tf.constant([0.5, np.nan], dtype=tf.float32) + + def createTestMasks(self): + mask = np.array([ + [[255.0, 0.0, 0.0], + [255.0, 0.0, 0.0], + [255.0, 0.0, 0.0]], + [[255.0, 255.0, 0.0], + [255.0, 255.0, 0.0], + [255.0, 255.0, 0.0]]]) + return tf.constant(mask, dtype=tf.float32) + + def createTestKeypoints(self): + keypoints = np.array([ + [[0.1, 0.1], [0.2, 0.2], [0.3, 0.3]], + [[0.4, 0.4], [0.5, 0.5], [0.6, 0.6]], + ]) + return tf.constant(keypoints, dtype=tf.float32) + + def createTestKeypointsInsideCrop(self): + keypoints = np.array([ + [[0.4, 0.4], [0.5, 0.5], [0.6, 0.6]], + [[0.4, 0.4], [0.5, 0.5], [0.6, 0.6]], + ]) + return tf.constant(keypoints, dtype=tf.float32) + + def createTestKeypointsOutsideCrop(self): + keypoints = np.array([ + [[0.1, 0.1], [0.2, 0.2], [0.3, 0.3]], + [[0.1, 0.1], [0.2, 0.2], [0.3, 0.3]], + ]) + return tf.constant(keypoints, dtype=tf.float32) + + def createKeypointFlipPermutation(self): + return np.array([0, 2, 1], dtype=np.int32) + + def createTestLabels(self): + labels = tf.constant([1, 2], dtype=tf.int32) + return labels + + def createTestBoxesOutOfImage(self): + boxes = tf.constant( + [[-0.1, 0.25, 0.75, 1], [0.25, 0.5, 0.75, 1.1]], dtype=tf.float32) + return boxes + + def expectedImagesAfterNormalization(self): + images_r = tf.constant([[[0, 0, 0, 0], [-1, -1, 0, 0], + [-1, 0, 0, 0], [0.5, 0.5, 0, 0]]], + dtype=tf.float32) + images_r = tf.expand_dims(images_r, 3) + images_g = tf.constant([[[-1, -1, 0, 0], [-1, -1, 0, 0], + [-1, 0, 0.5, 0.5], [0.5, 0.5, 0, 0.5]]], + dtype=tf.float32) + images_g = tf.expand_dims(images_g, 3) + images_b = tf.constant([[[0, 0, 0.5, -1], [-1, -1, 0, 0.5], + [-1, 0, 0, -1], [0.5, 0.5, 0.5, 0]]], + dtype=tf.float32) + images_b = tf.expand_dims(images_b, 3) + images = tf.concat([images_r, images_g, images_b], 3) + return images + + def expectedMaxImageAfterColorScale(self): + images_r = tf.constant([[[0.1, 0.1, 0.1, 0.1], [-0.9, -0.9, 0.1, 0.1], + [-0.9, 0.1, 0.1, 0.1], [0.6, 0.6, 0.1, 0.1]]], + dtype=tf.float32) + images_r = tf.expand_dims(images_r, 3) + images_g = tf.constant([[[-0.9, -0.9, 0.1, 0.1], [-0.9, -0.9, 0.1, 0.1], + [-0.9, 0.1, 0.6, 0.6], [0.6, 0.6, 0.1, 0.6]]], + dtype=tf.float32) + images_g = tf.expand_dims(images_g, 3) + images_b = tf.constant([[[0.1, 0.1, 0.6, -0.9], [-0.9, -0.9, 0.1, 0.6], + [-0.9, 0.1, 0.1, -0.9], [0.6, 0.6, 0.6, 0.1]]], + dtype=tf.float32) + images_b = tf.expand_dims(images_b, 3) + images = tf.concat([images_r, images_g, images_b], 3) + return images + + def expectedMinImageAfterColorScale(self): + images_r = tf.constant([[[-0.1, -0.1, -0.1, -0.1], [-1, -1, -0.1, -0.1], + [-1, -0.1, -0.1, -0.1], [0.4, 0.4, -0.1, -0.1]]], + dtype=tf.float32) + images_r = tf.expand_dims(images_r, 3) + images_g = tf.constant([[[-1, -1, -0.1, -0.1], [-1, -1, -0.1, -0.1], + [-1, -0.1, 0.4, 0.4], [0.4, 0.4, -0.1, 0.4]]], + dtype=tf.float32) + images_g = tf.expand_dims(images_g, 3) + images_b = tf.constant([[[-0.1, -0.1, 0.4, -1], [-1, -1, -0.1, 0.4], + [-1, -0.1, -0.1, -1], [0.4, 0.4, 0.4, -0.1]]], + dtype=tf.float32) + images_b = tf.expand_dims(images_b, 3) + images = tf.concat([images_r, images_g, images_b], 3) + return images + + def expectedImagesAfterMirroring(self): + images_r = tf.constant([[[0, 0, 0, 0], [0, 0, -1, -1], + [0, 0, 0, -1], [0, 0, 0.5, 0.5]]], + dtype=tf.float32) + images_r = tf.expand_dims(images_r, 3) + images_g = tf.constant([[[0, 0, -1, -1], [0, 0, -1, -1], + [0.5, 0.5, 0, -1], [0.5, 0, 0.5, 0.5]]], + dtype=tf.float32) + images_g = tf.expand_dims(images_g, 3) + images_b = tf.constant([[[-1, 0.5, 0, 0], [0.5, 0, -1, -1], + [-1, 0, 0, -1], [0, 0.5, 0.5, 0.5]]], + dtype=tf.float32) + images_b = tf.expand_dims(images_b, 3) + images = tf.concat([images_r, images_g, images_b], 3) + return images + + def expectedBoxesAfterMirroring(self): + boxes = tf.constant([[0.0, 0.0, 0.75, 0.75], [0.25, 0.0, 0.75, 0.5]], + dtype=tf.float32) + return boxes + + def expectedBoxesAfterXY(self): + boxes = tf.constant([[0.25, 0.0, 1.0, 0.75], [0.5, 0.25, 1, 0.75]], + dtype=tf.float32) + return boxes + + def expectedMasksAfterMirroring(self): + mask = np.array([ + [[0.0, 0.0, 255.0], + [0.0, 0.0, 255.0], + [0.0, 0.0, 255.0]], + [[0.0, 255.0, 255.0], + [0.0, 255.0, 255.0], + [0.0, 255.0, 255.0]]]) + return tf.constant(mask, dtype=tf.float32) + + def expectedLabelScoresAfterThresholding(self): + return tf.constant([1.0], dtype=tf.float32) + + def expectedBoxesAfterThresholding(self): + return tf.constant([[0.0, 0.25, 0.75, 1.0]], dtype=tf.float32) + + def expectedLabelsAfterThresholding(self): + return tf.constant([1], dtype=tf.float32) + + def expectedMasksAfterThresholding(self): + mask = np.array([ + [[255.0, 0.0, 0.0], + [255.0, 0.0, 0.0], + [255.0, 0.0, 0.0]]]) + return tf.constant(mask, dtype=tf.float32) + + def expectedKeypointsAfterThresholding(self): + keypoints = np.array([ + [[0.1, 0.1], [0.2, 0.2], [0.3, 0.3]] + ]) + return tf.constant(keypoints, dtype=tf.float32) + + def expectedLabelScoresAfterThresholdingWithMissingScore(self): + return tf.constant([np.nan], dtype=tf.float32) + + def expectedBoxesAfterThresholdingWithMissingScore(self): + return tf.constant([[0.25, 0.5, 0.75, 1]], dtype=tf.float32) + + def expectedLabelsAfterThresholdingWithMissingScore(self): + return tf.constant([2], dtype=tf.float32) + + def testNormalizeImage(self): + preprocess_options = [(preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 256, + 'target_minval': -1, + 'target_maxval': 1 + })] + images = self.createTestImages() + tensor_dict = {fields.InputDataFields.image: images} + tensor_dict = preprocessor.preprocess(tensor_dict, preprocess_options) + images = tensor_dict[fields.InputDataFields.image] + images_expected = self.expectedImagesAfterNormalization() + + with self.test_session() as sess: + (images_, images_expected_) = sess.run( + [images, images_expected]) + images_shape_ = images_.shape + images_expected_shape_ = images_expected_.shape + expected_shape = [1, 4, 4, 3] + self.assertAllEqual(images_expected_shape_, images_shape_) + self.assertAllEqual(images_shape_, expected_shape) + self.assertAllClose(images_, images_expected_) + + def testRetainBoxesAboveThreshold(self): + boxes = self.createTestBoxes() + labels = self.createTestLabels() + label_scores = self.createTestLabelScores() + (retained_boxes, retained_labels, + retained_label_scores) = preprocessor.retain_boxes_above_threshold( + boxes, labels, label_scores, threshold=0.6) + with self.test_session() as sess: + (retained_boxes_, retained_labels_, retained_label_scores_, + expected_retained_boxes_, expected_retained_labels_, + expected_retained_label_scores_) = sess.run([ + retained_boxes, retained_labels, retained_label_scores, + self.expectedBoxesAfterThresholding(), + self.expectedLabelsAfterThresholding(), + self.expectedLabelScoresAfterThresholding()]) + self.assertAllClose( + retained_boxes_, expected_retained_boxes_) + self.assertAllClose( + retained_labels_, expected_retained_labels_) + self.assertAllClose( + retained_label_scores_, expected_retained_label_scores_) + + def testRetainBoxesAboveThresholdWithMasks(self): + boxes = self.createTestBoxes() + labels = self.createTestLabels() + label_scores = self.createTestLabelScores() + masks = self.createTestMasks() + _, _, _, retained_masks = preprocessor.retain_boxes_above_threshold( + boxes, labels, label_scores, masks, threshold=0.6) + with self.test_session() as sess: + retained_masks_, expected_retained_masks_ = sess.run([ + retained_masks, self.expectedMasksAfterThresholding()]) + + self.assertAllClose( + retained_masks_, expected_retained_masks_) + + def testRetainBoxesAboveThresholdWithKeypoints(self): + boxes = self.createTestBoxes() + labels = self.createTestLabels() + label_scores = self.createTestLabelScores() + keypoints = self.createTestKeypoints() + (_, _, _, retained_keypoints) = preprocessor.retain_boxes_above_threshold( + boxes, labels, label_scores, keypoints=keypoints, threshold=0.6) + with self.test_session() as sess: + (retained_keypoints_, + expected_retained_keypoints_) = sess.run([ + retained_keypoints, + self.expectedKeypointsAfterThresholding()]) + + self.assertAllClose( + retained_keypoints_, expected_retained_keypoints_) + + def testRetainBoxesAboveThresholdWithMissingScore(self): + boxes = self.createTestBoxes() + labels = self.createTestLabels() + label_scores = self.createTestLabelScoresWithMissingScore() + (retained_boxes, retained_labels, + retained_label_scores) = preprocessor.retain_boxes_above_threshold( + boxes, labels, label_scores, threshold=0.6) + with self.test_session() as sess: + (retained_boxes_, retained_labels_, retained_label_scores_, + expected_retained_boxes_, expected_retained_labels_, + expected_retained_label_scores_) = sess.run([ + retained_boxes, retained_labels, retained_label_scores, + self.expectedBoxesAfterThresholdingWithMissingScore(), + self.expectedLabelsAfterThresholdingWithMissingScore(), + self.expectedLabelScoresAfterThresholdingWithMissingScore()]) + self.assertAllClose( + retained_boxes_, expected_retained_boxes_) + self.assertAllClose( + retained_labels_, expected_retained_labels_) + self.assertAllClose( + retained_label_scores_, expected_retained_label_scores_) + + def testRandomFlipBoxes(self): + boxes = self.createTestBoxes() + + # Case where the boxes are flipped. + boxes_expected1 = self.expectedBoxesAfterMirroring() + + # Case where the boxes are not flipped. + boxes_expected2 = boxes + + # After elementwise multiplication, the result should be all-zero since one + # of them is all-zero. + boxes_diff = tf.multiply( + tf.squared_difference(boxes, boxes_expected1), + tf.squared_difference(boxes, boxes_expected2)) + expected_result = tf.zeros_like(boxes_diff) + + with self.test_session() as sess: + (boxes_diff, expected_result) = sess.run([boxes_diff, expected_result]) + self.assertAllEqual(boxes_diff, expected_result) + + def testFlipMasks(self): + test_mask = self.createTestMasks() + flipped_mask = preprocessor._flip_masks(test_mask) + expected_mask = self.expectedMasksAfterMirroring() + with self.test_session() as sess: + flipped_mask, expected_mask = sess.run([flipped_mask, expected_mask]) + self.assertAllEqual(flipped_mask.flatten(), expected_mask.flatten()) + + def testRandomHorizontalFlip(self): + preprocess_options = [(preprocessor.random_horizontal_flip, {})] + images = self.expectedImagesAfterNormalization() + boxes = self.createTestBoxes() + tensor_dict = {fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes} + images_expected1 = self.expectedImagesAfterMirroring() + boxes_expected1 = self.expectedBoxesAfterMirroring() + images_expected2 = images + boxes_expected2 = boxes + tensor_dict = preprocessor.preprocess(tensor_dict, preprocess_options) + images = tensor_dict[fields.InputDataFields.image] + boxes = tensor_dict[fields.InputDataFields.groundtruth_boxes] + + boxes_diff1 = tf.squared_difference(boxes, boxes_expected1) + boxes_diff2 = tf.squared_difference(boxes, boxes_expected2) + boxes_diff = tf.multiply(boxes_diff1, boxes_diff2) + boxes_diff_expected = tf.zeros_like(boxes_diff) + + images_diff1 = tf.squared_difference(images, images_expected1) + images_diff2 = tf.squared_difference(images, images_expected2) + images_diff = tf.multiply(images_diff1, images_diff2) + images_diff_expected = tf.zeros_like(images_diff) + + with self.test_session() as sess: + (images_diff_, images_diff_expected_, boxes_diff_, + boxes_diff_expected_) = sess.run([images_diff, images_diff_expected, + boxes_diff, boxes_diff_expected]) + self.assertAllClose(boxes_diff_, boxes_diff_expected_) + self.assertAllClose(images_diff_, images_diff_expected_) + + def testRunRandomHorizontalFlipWithMaskAndKeypoints(self): + preprocess_options = [(preprocessor.random_horizontal_flip, {})] + image_height = 3 + image_width = 3 + images = tf.random_uniform([1, image_height, image_width, 3]) + boxes = self.createTestBoxes() + masks = self.createTestMasks() + keypoints = self.createTestKeypoints() + keypoint_flip_permutation = self.createKeypointFlipPermutation() + tensor_dict = { + fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_instance_masks: masks, + fields.InputDataFields.groundtruth_keypoints: keypoints + } + preprocess_options = [ + (preprocessor.random_horizontal_flip, + {'keypoint_flip_permutation': keypoint_flip_permutation})] + preprocessor_arg_map = preprocessor.get_default_func_arg_map( + include_instance_masks=True, include_keypoints=True) + tensor_dict = preprocessor.preprocess( + tensor_dict, preprocess_options, func_arg_map=preprocessor_arg_map) + boxes = tensor_dict[fields.InputDataFields.groundtruth_boxes] + masks = tensor_dict[fields.InputDataFields.groundtruth_instance_masks] + keypoints = tensor_dict[fields.InputDataFields.groundtruth_keypoints] + with self.test_session() as sess: + boxes, masks, keypoints = sess.run([boxes, masks, keypoints]) + self.assertTrue(boxes is not None) + self.assertTrue(masks is not None) + self.assertTrue(keypoints is not None) + + def testRandomPixelValueScale(self): + 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_pixel_value_scale, {})) + images = self.createTestImages() + tensor_dict = {fields.InputDataFields.image: images} + tensor_dict = preprocessor.preprocess(tensor_dict, preprocessing_options) + images_min = tf.to_float(images) * 0.9 / 255.0 + images_max = tf.to_float(images) * 1.1 / 255.0 + images = tensor_dict[fields.InputDataFields.image] + values_greater = tf.greater_equal(images, images_min) + values_less = tf.less_equal(images, images_max) + values_true = tf.fill([1, 4, 4, 3], True) + with self.test_session() as sess: + (values_greater_, values_less_, values_true_) = sess.run( + [values_greater, values_less, values_true]) + self.assertAllClose(values_greater_, values_true_) + self.assertAllClose(values_less_, values_true_) + + def testRandomImageScale(self): + preprocess_options = [(preprocessor.random_image_scale, {})] + images_original = self.createTestImages() + tensor_dict = {fields.InputDataFields.image: images_original} + tensor_dict = preprocessor.preprocess(tensor_dict, preprocess_options) + images_scaled = tensor_dict[fields.InputDataFields.image] + images_original_shape = tf.shape(images_original) + images_scaled_shape = tf.shape(images_scaled) + with self.test_session() as sess: + (images_original_shape_, images_scaled_shape_) = sess.run( + [images_original_shape, images_scaled_shape]) + self.assertTrue( + images_original_shape_[1] * 0.5 <= images_scaled_shape_[1]) + self.assertTrue( + images_original_shape_[1] * 2.0 >= images_scaled_shape_[1]) + self.assertTrue( + images_original_shape_[2] * 0.5 <= images_scaled_shape_[2]) + self.assertTrue( + images_original_shape_[2] * 2.0 >= images_scaled_shape_[2]) + + def testRandomRGBtoGray(self): + preprocess_options = [(preprocessor.random_rgb_to_gray, {})] + images_original = self.createTestImages() + tensor_dict = {fields.InputDataFields.image: images_original} + tensor_dict = preprocessor.preprocess(tensor_dict, preprocess_options) + images_gray = tensor_dict[fields.InputDataFields.image] + images_gray_r, images_gray_g, images_gray_b = tf.split( + value=images_gray, num_or_size_splits=3, axis=3) + images_r, images_g, images_b = tf.split( + value=images_original, num_or_size_splits=3, axis=3) + images_r_diff1 = tf.squared_difference(tf.to_float(images_r), + tf.to_float(images_gray_r)) + images_r_diff2 = tf.squared_difference(tf.to_float(images_gray_r), + tf.to_float(images_gray_g)) + images_r_diff = tf.multiply(images_r_diff1, images_r_diff2) + images_g_diff1 = tf.squared_difference(tf.to_float(images_g), + tf.to_float(images_gray_g)) + images_g_diff2 = tf.squared_difference(tf.to_float(images_gray_g), + tf.to_float(images_gray_b)) + images_g_diff = tf.multiply(images_g_diff1, images_g_diff2) + images_b_diff1 = tf.squared_difference(tf.to_float(images_b), + tf.to_float(images_gray_b)) + images_b_diff2 = tf.squared_difference(tf.to_float(images_gray_b), + tf.to_float(images_gray_r)) + images_b_diff = tf.multiply(images_b_diff1, images_b_diff2) + image_zero1 = tf.constant(0, dtype=tf.float32, shape=[1, 4, 4, 1]) + with self.test_session() as sess: + (images_r_diff_, images_g_diff_, images_b_diff_, image_zero1_) = sess.run( + [images_r_diff, images_g_diff, images_b_diff, image_zero1]) + self.assertAllClose(images_r_diff_, image_zero1_) + self.assertAllClose(images_g_diff_, image_zero1_) + self.assertAllClose(images_b_diff_, image_zero1_) + + def testRandomAdjustBrightness(self): + 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_adjust_brightness, {})) + images_original = self.createTestImages() + tensor_dict = {fields.InputDataFields.image: images_original} + tensor_dict = preprocessor.preprocess(tensor_dict, preprocessing_options) + images_bright = tensor_dict[fields.InputDataFields.image] + image_original_shape = tf.shape(images_original) + image_bright_shape = tf.shape(images_bright) + with self.test_session() as sess: + (image_original_shape_, image_bright_shape_) = sess.run( + [image_original_shape, image_bright_shape]) + self.assertAllEqual(image_original_shape_, image_bright_shape_) + + def testRandomAdjustContrast(self): + 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_adjust_contrast, {})) + images_original = self.createTestImages() + tensor_dict = {fields.InputDataFields.image: images_original} + tensor_dict = preprocessor.preprocess(tensor_dict, preprocessing_options) + images_contrast = tensor_dict[fields.InputDataFields.image] + image_original_shape = tf.shape(images_original) + image_contrast_shape = tf.shape(images_contrast) + with self.test_session() as sess: + (image_original_shape_, image_contrast_shape_) = sess.run( + [image_original_shape, image_contrast_shape]) + self.assertAllEqual(image_original_shape_, image_contrast_shape_) + + def testRandomAdjustHue(self): + 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_adjust_hue, {})) + images_original = self.createTestImages() + tensor_dict = {fields.InputDataFields.image: images_original} + tensor_dict = preprocessor.preprocess(tensor_dict, preprocessing_options) + images_hue = tensor_dict[fields.InputDataFields.image] + image_original_shape = tf.shape(images_original) + image_hue_shape = tf.shape(images_hue) + with self.test_session() as sess: + (image_original_shape_, image_hue_shape_) = sess.run( + [image_original_shape, image_hue_shape]) + self.assertAllEqual(image_original_shape_, image_hue_shape_) + + def testRandomDistortColor(self): + 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_distort_color, {})) + images_original = self.createTestImages() + images_original_shape = tf.shape(images_original) + tensor_dict = {fields.InputDataFields.image: images_original} + tensor_dict = preprocessor.preprocess(tensor_dict, preprocessing_options) + images_distorted_color = tensor_dict[fields.InputDataFields.image] + images_distorted_color_shape = tf.shape(images_distorted_color) + with self.test_session() as sess: + (images_original_shape_, images_distorted_color_shape_) = sess.run( + [images_original_shape, images_distorted_color_shape]) + self.assertAllEqual(images_original_shape_, images_distorted_color_shape_) + + def testRandomJitterBoxes(self): + preprocessing_options = [] + preprocessing_options.append((preprocessor.random_jitter_boxes, {})) + boxes = self.createTestBoxes() + boxes_shape = tf.shape(boxes) + tensor_dict = {fields.InputDataFields.groundtruth_boxes: boxes} + tensor_dict = preprocessor.preprocess(tensor_dict, preprocessing_options) + distorted_boxes = tensor_dict[fields.InputDataFields.groundtruth_boxes] + distorted_boxes_shape = tf.shape(distorted_boxes) + + with self.test_session() as sess: + (boxes_shape_, distorted_boxes_shape_) = sess.run( + [boxes_shape, distorted_boxes_shape]) + self.assertAllEqual(boxes_shape_, distorted_boxes_shape_) + + def testRandomCropImage(self): + 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_crop_image, {})) + 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} + 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] + boxes_rank = tf.rank(boxes) + distorted_boxes_rank = tf.rank(distorted_boxes) + images_rank = tf.rank(images) + distorted_images_rank = tf.rank(distorted_images) + self.assertEqual(3, distorted_images.get_shape()[3]) + + with self.test_session() as sess: + (boxes_rank_, distorted_boxes_rank_, images_rank_, + distorted_images_rank_) = sess.run([ + boxes_rank, distorted_boxes_rank, images_rank, distorted_images_rank + ]) + self.assertAllEqual(boxes_rank_, distorted_boxes_rank_) + self.assertAllEqual(images_rank_, distorted_images_rank_) + + def testRandomCropImageGrayscale(self): + preprocessing_options = [(preprocessor.rgb_to_gray, {}), + (preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 255, + 'target_minval': 0, + 'target_maxval': 1, + }), + (preprocessor.random_crop_image, {})] + 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 + } + 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] + boxes_rank = tf.rank(boxes) + distorted_boxes_rank = tf.rank(distorted_boxes) + images_rank = tf.rank(images) + distorted_images_rank = tf.rank(distorted_images) + self.assertEqual(1, distorted_images.get_shape()[3]) + + with self.test_session() as sess: + session_results = sess.run([ + boxes_rank, distorted_boxes_rank, images_rank, distorted_images_rank + ]) + (boxes_rank_, distorted_boxes_rank_, images_rank_, + distorted_images_rank_) = session_results + self.assertAllEqual(boxes_rank_, distorted_boxes_rank_) + self.assertAllEqual(images_rank_, distorted_images_rank_) + + def testRandomCropImageWithBoxOutOfImage(self): + 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_crop_image, {})) + images = self.createTestImages() + boxes = self.createTestBoxesOutOfImage() + labels = self.createTestLabels() + tensor_dict = {fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels} + 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] + boxes_rank = tf.rank(boxes) + distorted_boxes_rank = tf.rank(distorted_boxes) + images_rank = tf.rank(images) + distorted_images_rank = tf.rank(distorted_images) + + with self.test_session() as sess: + (boxes_rank_, distorted_boxes_rank_, images_rank_, + distorted_images_rank_) = sess.run( + [boxes_rank, distorted_boxes_rank, images_rank, + distorted_images_rank]) + self.assertAllEqual(boxes_rank_, distorted_boxes_rank_) + self.assertAllEqual(images_rank_, distorted_images_rank_) + + def testRandomCropImageWithRandomCoefOne(self): + 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() + tensor_dict = {fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels} + tensor_dict = preprocessor.preprocess(tensor_dict, preprocessing_options) + images = tensor_dict[fields.InputDataFields.image] + + preprocessing_options = [(preprocessor.random_crop_image, { + 'random_coef': 1.0 + })] + 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] + distorted_labels = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_classes] + boxes_shape = tf.shape(boxes) + distorted_boxes_shape = tf.shape(distorted_boxes) + images_shape = tf.shape(images) + distorted_images_shape = tf.shape(distorted_images) + + with self.test_session() as sess: + (boxes_shape_, distorted_boxes_shape_, images_shape_, + distorted_images_shape_, images_, distorted_images_, + boxes_, distorted_boxes_, labels_, distorted_labels_) = sess.run( + [boxes_shape, distorted_boxes_shape, images_shape, + distorted_images_shape, images, distorted_images, + boxes, distorted_boxes, labels, distorted_labels]) + self.assertAllEqual(boxes_shape_, distorted_boxes_shape_) + self.assertAllEqual(images_shape_, distorted_images_shape_) + self.assertAllClose(images_, distorted_images_) + self.assertAllClose(boxes_, distorted_boxes_) + self.assertAllEqual(labels_, distorted_labels_) + + def testRandomCropWithMockSampleDistortedBoundingBox(self): + preprocessing_options = [(preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 255, + 'target_minval': 0, + 'target_maxval': 1 + })] + + images = self.createColorfulTestImage() + boxes = tf.constant([[0.1, 0.1, 0.8, 0.3], + [0.2, 0.4, 0.75, 0.75], + [0.3, 0.1, 0.4, 0.7]], dtype=tf.float32) + labels = tf.constant([1, 7, 11], dtype=tf.int32) + tensor_dict = {fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels} + tensor_dict = preprocessor.preprocess(tensor_dict, preprocessing_options) + images = tensor_dict[fields.InputDataFields.image] + + preprocessing_options = [(preprocessor.random_crop_image, {})] + with mock.patch.object( + tf.image, + 'sample_distorted_bounding_box') as mock_sample_distorted_bounding_box: + mock_sample_distorted_bounding_box.return_value = (tf.constant( + [6, 143, 0], dtype=tf.int32), tf.constant( + [190, 237, -1], dtype=tf.int32), tf.constant( + [[[0.03, 0.3575, 0.98, 0.95]]], dtype=tf.float32)) + + distorted_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + + distorted_boxes = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + distorted_labels = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_classes] + expected_boxes = tf.constant([[0.178947, 0.07173, 0.75789469, 0.66244733], + [0.28421, 0.0, 0.38947365, 0.57805908]], + dtype=tf.float32) + expected_labels = tf.constant([7, 11], dtype=tf.int32) + + with self.test_session() as sess: + (distorted_boxes_, distorted_labels_, + expected_boxes_, expected_labels_) = sess.run( + [distorted_boxes, distorted_labels, + expected_boxes, expected_labels]) + self.assertAllClose(distorted_boxes_, expected_boxes_) + self.assertAllEqual(distorted_labels_, expected_labels_) + + def testStrictRandomCropImageWithMasks(self): + image = self.createColorfulTestImage()[0] + boxes = self.createTestBoxes() + labels = self.createTestLabels() + masks = tf.random_uniform([2, 200, 400], dtype=tf.float32) + with mock.patch.object( + tf.image, + 'sample_distorted_bounding_box' + ) as mock_sample_distorted_bounding_box: + mock_sample_distorted_bounding_box.return_value = ( + tf.constant([6, 143, 0], dtype=tf.int32), + tf.constant([190, 237, -1], dtype=tf.int32), + tf.constant([[[0.03, 0.3575, 0.98, 0.95]]], dtype=tf.float32)) + (new_image, new_boxes, new_labels, + new_masks) = preprocessor._strict_random_crop_image( + image, boxes, labels, masks=masks) + with self.test_session() as sess: + new_image, new_boxes, new_labels, new_masks = sess.run([ + new_image, new_boxes, new_labels, new_masks]) + + expected_boxes = np.array([ + [0.0, 0.0, 0.75789469, 1.0], + [0.23157893, 0.24050637, 0.75789469, 1.0], + ], dtype=np.float32) + self.assertAllEqual(new_image.shape, [190, 237, 3]) + self.assertAllEqual(new_masks.shape, [2, 190, 237]) + self.assertAllClose( + new_boxes.flatten(), expected_boxes.flatten()) + + def testStrictRandomCropImageWithKeypoints(self): + image = self.createColorfulTestImage()[0] + boxes = self.createTestBoxes() + labels = self.createTestLabels() + keypoints = self.createTestKeypoints() + with mock.patch.object( + tf.image, + 'sample_distorted_bounding_box' + ) as mock_sample_distorted_bounding_box: + mock_sample_distorted_bounding_box.return_value = ( + tf.constant([6, 143, 0], dtype=tf.int32), + tf.constant([190, 237, -1], dtype=tf.int32), + tf.constant([[[0.03, 0.3575, 0.98, 0.95]]], dtype=tf.float32)) + (new_image, new_boxes, new_labels, + new_keypoints) = preprocessor._strict_random_crop_image( + image, boxes, labels, keypoints=keypoints) + with self.test_session() as sess: + new_image, new_boxes, new_labels, new_keypoints = sess.run([ + new_image, new_boxes, new_labels, new_keypoints]) + + expected_boxes = np.array([ + [0.0, 0.0, 0.75789469, 1.0], + [0.23157893, 0.24050637, 0.75789469, 1.0], + ], dtype=np.float32) + expected_keypoints = np.array([ + [[np.nan, np.nan], + [np.nan, np.nan], + [np.nan, np.nan]], + [[0.38947368, 0.07173], + [0.49473682, 0.24050637], + [0.60000002, 0.40928277]] + ], dtype=np.float32) + self.assertAllEqual(new_image.shape, [190, 237, 3]) + self.assertAllClose( + new_boxes.flatten(), expected_boxes.flatten()) + self.assertAllClose( + new_keypoints.flatten(), expected_keypoints.flatten()) + + def testRunRandomCropImageWithMasks(self): + image = self.createColorfulTestImage() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + masks = tf.random_uniform([2, 200, 400], dtype=tf.float32) + + tensor_dict = { + fields.InputDataFields.image: image, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_instance_masks: masks, + } + + preprocessor_arg_map = preprocessor.get_default_func_arg_map( + include_instance_masks=True) + + preprocessing_options = [(preprocessor.random_crop_image, {})] + + with mock.patch.object( + tf.image, + 'sample_distorted_bounding_box' + ) as mock_sample_distorted_bounding_box: + mock_sample_distorted_bounding_box.return_value = ( + tf.constant([6, 143, 0], dtype=tf.int32), + tf.constant([190, 237, -1], dtype=tf.int32), + tf.constant([[[0.03, 0.3575, 0.98, 0.95]]], dtype=tf.float32)) + distorted_tensor_dict = preprocessor.preprocess( + tensor_dict, preprocessing_options, func_arg_map=preprocessor_arg_map) + distorted_image = distorted_tensor_dict[fields.InputDataFields.image] + distorted_boxes = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + distorted_labels = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_classes] + distorted_masks = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_instance_masks] + with self.test_session() as sess: + (distorted_image_, distorted_boxes_, distorted_labels_, + distorted_masks_) = sess.run( + [distorted_image, distorted_boxes, distorted_labels, + distorted_masks]) + + expected_boxes = np.array([ + [0.0, 0.0, 0.75789469, 1.0], + [0.23157893, 0.24050637, 0.75789469, 1.0], + ], dtype=np.float32) + self.assertAllEqual(distorted_image_.shape, [1, 190, 237, 3]) + self.assertAllEqual(distorted_masks_.shape, [2, 190, 237]) + self.assertAllEqual(distorted_labels_, [1, 2]) + self.assertAllClose( + distorted_boxes_.flatten(), expected_boxes.flatten()) + + def testRunRandomCropImageWithKeypointsInsideCrop(self): + image = self.createColorfulTestImage() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + keypoints = self.createTestKeypointsInsideCrop() + + tensor_dict = { + fields.InputDataFields.image: image, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_keypoints: keypoints + } + + preprocessor_arg_map = preprocessor.get_default_func_arg_map( + include_keypoints=True) + + preprocessing_options = [(preprocessor.random_crop_image, {})] + + with mock.patch.object( + tf.image, + 'sample_distorted_bounding_box' + ) as mock_sample_distorted_bounding_box: + mock_sample_distorted_bounding_box.return_value = ( + tf.constant([6, 143, 0], dtype=tf.int32), + tf.constant([190, 237, -1], dtype=tf.int32), + tf.constant([[[0.03, 0.3575, 0.98, 0.95]]], dtype=tf.float32)) + distorted_tensor_dict = preprocessor.preprocess( + tensor_dict, preprocessing_options, func_arg_map=preprocessor_arg_map) + distorted_image = distorted_tensor_dict[fields.InputDataFields.image] + distorted_boxes = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + distorted_labels = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_classes] + distorted_keypoints = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_keypoints] + with self.test_session() as sess: + (distorted_image_, distorted_boxes_, distorted_labels_, + distorted_keypoints_) = sess.run( + [distorted_image, distorted_boxes, distorted_labels, + distorted_keypoints]) + + expected_boxes = np.array([ + [0.0, 0.0, 0.75789469, 1.0], + [0.23157893, 0.24050637, 0.75789469, 1.0], + ], dtype=np.float32) + expected_keypoints = np.array([ + [[0.38947368, 0.07173], + [0.49473682, 0.24050637], + [0.60000002, 0.40928277]], + [[0.38947368, 0.07173], + [0.49473682, 0.24050637], + [0.60000002, 0.40928277]] + ]) + self.assertAllEqual(distorted_image_.shape, [1, 190, 237, 3]) + self.assertAllEqual(distorted_labels_, [1, 2]) + self.assertAllClose( + distorted_boxes_.flatten(), expected_boxes.flatten()) + self.assertAllClose( + distorted_keypoints_.flatten(), expected_keypoints.flatten()) + + def testRunRandomCropImageWithKeypointsOutsideCrop(self): + image = self.createColorfulTestImage() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + keypoints = self.createTestKeypointsOutsideCrop() + + tensor_dict = { + fields.InputDataFields.image: image, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_keypoints: keypoints + } + + preprocessor_arg_map = preprocessor.get_default_func_arg_map( + include_keypoints=True) + + preprocessing_options = [(preprocessor.random_crop_image, {})] + + with mock.patch.object( + tf.image, + 'sample_distorted_bounding_box' + ) as mock_sample_distorted_bounding_box: + mock_sample_distorted_bounding_box.return_value = ( + tf.constant([6, 143, 0], dtype=tf.int32), + tf.constant([190, 237, -1], dtype=tf.int32), + tf.constant([[[0.03, 0.3575, 0.98, 0.95]]], dtype=tf.float32)) + distorted_tensor_dict = preprocessor.preprocess( + tensor_dict, preprocessing_options, func_arg_map=preprocessor_arg_map) + distorted_image = distorted_tensor_dict[fields.InputDataFields.image] + distorted_boxes = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + distorted_labels = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_classes] + distorted_keypoints = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_keypoints] + with self.test_session() as sess: + (distorted_image_, distorted_boxes_, distorted_labels_, + distorted_keypoints_) = sess.run( + [distorted_image, distorted_boxes, distorted_labels, + distorted_keypoints]) + + expected_boxes = np.array([ + [0.0, 0.0, 0.75789469, 1.0], + [0.23157893, 0.24050637, 0.75789469, 1.0], + ], dtype=np.float32) + expected_keypoints = np.array([ + [[np.nan, np.nan], + [np.nan, np.nan], + [np.nan, np.nan]], + [[np.nan, np.nan], + [np.nan, np.nan], + [np.nan, np.nan]], + ]) + self.assertAllEqual(distorted_image_.shape, [1, 190, 237, 3]) + self.assertAllEqual(distorted_labels_, [1, 2]) + self.assertAllClose( + distorted_boxes_.flatten(), expected_boxes.flatten()) + self.assertAllClose( + distorted_keypoints_.flatten(), expected_keypoints.flatten()) + + def testRunRetainBoxesAboveThreshold(self): + boxes = self.createTestBoxes() + labels = self.createTestLabels() + label_scores = self.createTestLabelScores() + + tensor_dict = { + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_label_scores: label_scores + } + + preprocessing_options = [ + (preprocessor.retain_boxes_above_threshold, {'threshold': 0.6}) + ] + + retained_tensor_dict = preprocessor.preprocess( + tensor_dict, preprocessing_options) + retained_boxes = retained_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + retained_labels = retained_tensor_dict[ + fields.InputDataFields.groundtruth_classes] + retained_label_scores = retained_tensor_dict[ + fields.InputDataFields.groundtruth_label_scores] + + with self.test_session() as sess: + (retained_boxes_, retained_labels_, + retained_label_scores_, expected_retained_boxes_, + expected_retained_labels_, expected_retained_label_scores_) = sess.run( + [retained_boxes, retained_labels, retained_label_scores, + self.expectedBoxesAfterThresholding(), + self.expectedLabelsAfterThresholding(), + self.expectedLabelScoresAfterThresholding()]) + + self.assertAllClose(retained_boxes_, expected_retained_boxes_) + self.assertAllClose(retained_labels_, expected_retained_labels_) + self.assertAllClose( + retained_label_scores_, expected_retained_label_scores_) + + def testRunRetainBoxesAboveThresholdWithMasks(self): + boxes = self.createTestBoxes() + labels = self.createTestLabels() + label_scores = self.createTestLabelScores() + masks = self.createTestMasks() + + tensor_dict = { + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_label_scores: label_scores, + fields.InputDataFields.groundtruth_instance_masks: masks + } + + preprocessor_arg_map = preprocessor.get_default_func_arg_map( + include_instance_masks=True) + + preprocessing_options = [ + (preprocessor.retain_boxes_above_threshold, {'threshold': 0.6}) + ] + + retained_tensor_dict = preprocessor.preprocess( + tensor_dict, preprocessing_options, func_arg_map=preprocessor_arg_map) + retained_masks = retained_tensor_dict[ + fields.InputDataFields.groundtruth_instance_masks] + + with self.test_session() as sess: + (retained_masks_, expected_masks_) = sess.run( + [retained_masks, + self.expectedMasksAfterThresholding()]) + self.assertAllClose(retained_masks_, expected_masks_) + + def testRunRetainBoxesAboveThresholdWithKeypoints(self): + boxes = self.createTestBoxes() + labels = self.createTestLabels() + label_scores = self.createTestLabelScores() + keypoints = self.createTestKeypoints() + + tensor_dict = { + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_label_scores: label_scores, + fields.InputDataFields.groundtruth_keypoints: keypoints + } + + preprocessor_arg_map = preprocessor.get_default_func_arg_map( + include_keypoints=True) + + preprocessing_options = [ + (preprocessor.retain_boxes_above_threshold, {'threshold': 0.6}) + ] + + retained_tensor_dict = preprocessor.preprocess( + tensor_dict, preprocessing_options, func_arg_map=preprocessor_arg_map) + retained_keypoints = retained_tensor_dict[ + fields.InputDataFields.groundtruth_keypoints] + + with self.test_session() as sess: + (retained_keypoints_, expected_keypoints_) = sess.run( + [retained_keypoints, + self.expectedKeypointsAfterThresholding()]) + self.assertAllClose(retained_keypoints_, expected_keypoints_) + + def testRunRandomCropToAspectRatioWithMasks(self): + image = self.createColorfulTestImage() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + masks = tf.random_uniform([2, 200, 400], dtype=tf.float32) + + tensor_dict = { + fields.InputDataFields.image: image, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_instance_masks: masks + } + + preprocessor_arg_map = preprocessor.get_default_func_arg_map( + include_instance_masks=True) + + preprocessing_options = [(preprocessor.random_crop_to_aspect_ratio, {})] + + with mock.patch.object(preprocessor, + '_random_integer') as mock_random_integer: + mock_random_integer.return_value = tf.constant(0, dtype=tf.int32) + distorted_tensor_dict = preprocessor.preprocess( + tensor_dict, preprocessing_options, func_arg_map=preprocessor_arg_map) + distorted_image = distorted_tensor_dict[fields.InputDataFields.image] + distorted_boxes = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + distorted_labels = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_classes] + distorted_masks = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_instance_masks] + with self.test_session() as sess: + (distorted_image_, distorted_boxes_, distorted_labels_, + distorted_masks_) = sess.run([ + distorted_image, distorted_boxes, distorted_labels, distorted_masks + ]) + + expected_boxes = np.array([0.0, 0.5, 0.75, 1.0], dtype=np.float32) + self.assertAllEqual(distorted_image_.shape, [1, 200, 200, 3]) + self.assertAllEqual(distorted_labels_, [1]) + self.assertAllClose(distorted_boxes_.flatten(), + expected_boxes.flatten()) + self.assertAllEqual(distorted_masks_.shape, [1, 200, 200]) + + def testRunRandomCropToAspectRatioWithKeypoints(self): + image = self.createColorfulTestImage() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + keypoints = self.createTestKeypoints() + + tensor_dict = { + fields.InputDataFields.image: image, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_keypoints: keypoints + } + + preprocessor_arg_map = preprocessor.get_default_func_arg_map( + include_keypoints=True) + + preprocessing_options = [(preprocessor.random_crop_to_aspect_ratio, {})] + + with mock.patch.object(preprocessor, + '_random_integer') as mock_random_integer: + mock_random_integer.return_value = tf.constant(0, dtype=tf.int32) + distorted_tensor_dict = preprocessor.preprocess( + tensor_dict, preprocessing_options, func_arg_map=preprocessor_arg_map) + distorted_image = distorted_tensor_dict[fields.InputDataFields.image] + distorted_boxes = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + distorted_labels = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_classes] + distorted_keypoints = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_keypoints] + with self.test_session() as sess: + (distorted_image_, distorted_boxes_, distorted_labels_, + distorted_keypoints_) = sess.run([ + distorted_image, distorted_boxes, distorted_labels, + distorted_keypoints + ]) + + expected_boxes = np.array([0.0, 0.5, 0.75, 1.0], dtype=np.float32) + expected_keypoints = np.array( + [[0.1, 0.2], [0.2, 0.4], [0.3, 0.6]], dtype=np.float32) + self.assertAllEqual(distorted_image_.shape, [1, 200, 200, 3]) + self.assertAllEqual(distorted_labels_, [1]) + self.assertAllClose(distorted_boxes_.flatten(), + expected_boxes.flatten()) + self.assertAllClose(distorted_keypoints_.flatten(), + expected_keypoints.flatten()) + + def testRandomPadImage(self): + 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() + tensor_dict = {fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels} + tensor_dict = preprocessor.preprocess(tensor_dict, preprocessing_options) + images = tensor_dict[fields.InputDataFields.image] + + preprocessing_options = [(preprocessor.random_pad_image, {})] + 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) + + with self.test_session() as sess: + (boxes_shape_, padded_boxes_shape_, images_shape_, + padded_images_shape_, boxes_, padded_boxes_) = sess.run( + [boxes_shape, padded_boxes_shape, images_shape, + padded_images_shape, boxes, padded_boxes]) + 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 testRandomCropPadImageWithRandomCoefOne(self): + 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() + tensor_dict = {fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels} + 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) + + with self.test_session() as sess: + (boxes_shape_, padded_boxes_shape_, images_shape_, + padded_images_shape_, boxes_, padded_boxes_) = sess.run( + [boxes_shape, padded_boxes_shape, images_shape, + padded_images_shape, boxes, padded_boxes]) + 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): + 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() + tensor_dict = { + fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels + } + tensor_dict = preprocessor.preprocess(tensor_dict, preprocessing_options) + 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) + + with self.test_session() as sess: + (boxes_shape_, cropped_boxes_shape_, images_shape_, + cropped_images_shape_) = sess.run([ + boxes_shape, cropped_boxes_shape, images_shape, cropped_images_shape + ]) + 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 testRandomBlackPatches(self): + 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) + + with self.test_session() as sess: + (images_shape_, blacked_images_shape_) = sess.run( + [images_shape, blacked_images_shape]) + self.assertAllEqual(images_shape_, blacked_images_shape_) + + def testRandomResizeMethod(self): + 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) + + with self.test_session() as sess: + (expected_images_shape_, resized_images_shape_) = sess.run( + [expected_images_shape, resized_images_shape]) + self.assertAllEqual(expected_images_shape_, + resized_images_shape_) + + def testResizeToRange(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) + out_image_shape = tf.shape(out_image) + + with self.test_session() as sess: + out_image_shape = sess.run(out_image_shape) + self.assertAllEqual(out_image_shape, expected_shape) + + def testResizeToRangeWithMasks(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) + out_image_shape = tf.shape(out_image) + out_masks_shape = tf.shape(out_masks) + + with self.test_session() as sess: + out_image_shape, out_masks_shape = sess.run( + [out_image_shape, out_masks_shape]) + self.assertAllEqual(out_image_shape, expected_image_shape) + self.assertAllEqual(out_masks_shape, expected_mask_shape) + + def testResizeToRangeWithNoInstanceMask(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]] + + 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) + out_image_shape = tf.shape(out_image) + out_masks_shape = tf.shape(out_masks) + + with self.test_session() as sess: + out_image_shape, out_masks_shape = sess.run( + [out_image_shape, out_masks_shape]) + self.assertAllEqual(out_image_shape, expected_image_shape) + self.assertAllEqual(out_masks_shape, expected_mask_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]] + + 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_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) + + with self.test_session() as sess: + out_image_shape, out_masks_shape = sess.run( + [out_image_shape, out_masks_shape]) + 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]] + + 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_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) + + with self.test_session() as sess: + out_image_shape, out_masks_shape = sess.run( + [out_image_shape, out_masks_shape]) + 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]] + + 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) + out_image_shape = tf.shape(out_image) + + with self.test_session() as sess: + out_image_shape = sess.run(out_image_shape) + self.assertAllEqual(out_image_shape, expected_shape) + + def testScaleBoxesToPixelCoordinates(self): + """Tests box scaling, checking scaled values.""" + in_shape = [60, 40, 3] + in_boxes = [[0.1, 0.2, 0.4, 0.6], + [0.5, 0.3, 0.9, 0.7]] + + expected_boxes = [[6., 8., 24., 24.], + [30., 12., 54., 28.]] + + 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) + with self.test_session() as sess: + out_boxes = sess.run(out_boxes) + self.assertAllClose(out_boxes, expected_boxes) + + def testScaleBoxesToPixelCoordinatesWithKeypoints(self): + """Tests box and keypoint scaling, checking scaled values.""" + in_shape = [60, 40, 3] + in_boxes = self.createTestBoxes() + in_keypoints = self.createTestKeypoints() + + expected_boxes = [[0., 10., 45., 40.], + [15., 20., 45., 40.]] + expected_keypoints = [ + [[6., 4.], [12., 8.], [18., 12.]], + [[24., 16.], [30., 20.], [36., 24.]], + ] + + 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) + with self.test_session() as sess: + out_boxes_, out_keypoints_ = sess.run([out_boxes, out_keypoints]) + self.assertAllClose(out_boxes_, expected_boxes) + self.assertAllClose(out_keypoints_, expected_keypoints) + + def testSubtractChannelMean(self): + """Tests whether channel means have been subtracted.""" + with self.test_session(): + image = tf.zeros((240, 320, 3)) + means = [1, 2, 3] + actual = preprocessor.subtract_channel_mean(image, means=means) + actual = actual.eval() + + self.assertTrue((actual[:, :, 0] == -1).all()) + self.assertTrue((actual[:, :, 1] == -2).all()) + self.assertTrue((actual[:, :, 2] == -3).all()) + + def testOneHotEncoding(self): + """Tests one hot encoding of multiclass labels.""" + with self.test_session(): + labels = tf.constant([1, 4, 2], dtype=tf.int32) + one_hot = preprocessor.one_hot_encoding(labels, num_classes=5) + one_hot = one_hot.eval() + + self.assertAllEqual([0, 1, 1, 0, 1], one_hot) + + def testSSDRandomCrop(self): + 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() + tensor_dict = {fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels} + 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) + + with self.test_session() as sess: + (boxes_rank_, distorted_boxes_rank_, images_rank_, + distorted_images_rank_) = sess.run( + [boxes_rank, distorted_boxes_rank, images_rank, + distorted_images_rank]) + self.assertAllEqual(boxes_rank_, distorted_boxes_rank_) + self.assertAllEqual(images_rank_, distorted_images_rank_) + + def testSSDRandomCropPad(self): + images = self.createTestImages() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + 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} + 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) + + with self.test_session() as sess: + (boxes_rank_, distorted_boxes_rank_, images_rank_, + distorted_images_rank_) = sess.run([ + boxes_rank, distorted_boxes_rank, images_rank, distorted_images_rank + ]) + self.assertAllEqual(boxes_rank_, distorted_boxes_rank_) + self.assertAllEqual(images_rank_, distorted_images_rank_) + + def testSSDRandomCropFixedAspectRatio(self): + images = self.createTestImages() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + 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 + } + 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) + + with self.test_session() as sess: + (boxes_rank_, distorted_boxes_rank_, images_rank_, + distorted_images_rank_) = sess.run( + [boxes_rank, distorted_boxes_rank, images_rank, + distorted_images_rank]) + self.assertAllEqual(boxes_rank_, distorted_boxes_rank_) + self.assertAllEqual(images_rank_, distorted_images_rank_) + + def testSSDRandomCropFixedAspectRatioWithMasksAndKeypoints(self): + images = self.createTestImages() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + masks = self.createTestMasks() + keypoints = self.createTestKeypoints() + 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_instance_masks: masks, + fields.InputDataFields.groundtruth_keypoints: keypoints, + } + preprocessor_arg_map = preprocessor.get_default_func_arg_map( + include_instance_masks=True, include_keypoints=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] + + images_rank = tf.rank(images) + distorted_images_rank = tf.rank(distorted_images) + boxes_rank = tf.rank(boxes) + distorted_boxes_rank = tf.rank(distorted_boxes) + + with self.test_session() as sess: + (boxes_rank_, distorted_boxes_rank_, images_rank_, + distorted_images_rank_) = sess.run( + [boxes_rank, distorted_boxes_rank, images_rank, + distorted_images_rank]) + self.assertAllEqual(boxes_rank_, distorted_boxes_rank_) + self.assertAllEqual(images_rank_, distorted_images_rank_) + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/core/region_similarity_calculator.py b/object_detection/core/region_similarity_calculator.py new file mode 100644 index 000000000..f344006a3 --- /dev/null +++ b/object_detection/core/region_similarity_calculator.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. +# ============================================================================== + +"""Region Similarity Calculators for BoxLists. + +Region Similarity Calculators compare a pairwise measure of similarity +between the boxes in two BoxLists. +""" +from abc import ABCMeta +from abc import abstractmethod + +import tensorflow as tf + +from object_detection.core import box_list_ops + + +class RegionSimilarityCalculator(object): + """Abstract base class for region similarity calculator.""" + __metaclass__ = ABCMeta + + def compare(self, boxlist1, boxlist2, scope=None): + """Computes matrix of pairwise similarity between BoxLists. + + This op (to be overriden) 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 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) diff --git a/object_detection/core/region_similarity_calculator_test.py b/object_detection/core/region_similarity_calculator_test.py new file mode 100644 index 000000000..162151a3b --- /dev/null +++ b/object_detection/core/region_similarity_calculator_test.py @@ -0,0 +1,75 @@ +# 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 as tf + +from object_detection.core import box_list +from object_detection.core import region_similarity_calculator + + +class RegionSimilarityCalculatorTest(tf.test.TestCase): + + def test_get_correct_pairwise_similarity_based_on_iou(self): + 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]]) + exp_output = [[2.0 / 16.0, 0, 6.0 / 400.0], [1.0 / 16.0, 0.0, 5.0 / 400.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) + with self.test_session() as sess: + iou_output = sess.run(iou_similarity) + self.assertAllClose(iou_output, exp_output) + + def test_get_correct_pairwise_similarity_based_on_squared_distances(self): + 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]]) + exp_output = [[-26, -25, 0], [-18, -27, -6]] + 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) + with self.test_session() as sess: + dist_output = sess.run(dist_similarity) + self.assertAllClose(dist_output, exp_output) + + def test_get_correct_pairwise_similarity_based_on_ioa(self): + 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]]) + 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]] + 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) + with self.test_session() as sess: + iou_output_1, iou_output_2 = sess.run( + [ioa_similarity_1, ioa_similarity_2]) + self.assertAllClose(iou_output_1, exp_output_1) + self.assertAllClose(iou_output_2, exp_output_2) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/core/standard_fields.py b/object_detection/core/standard_fields.py new file mode 100644 index 000000000..978aad3d8 --- /dev/null +++ b/object_detection/core/standard_fields.py @@ -0,0 +1,150 @@ +# 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. + 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. + original_image: 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_boxes: coordinates of the ground truth boxes in the image. + groundtruth_classes: box-level class labels. + groundtruth_label_types: box-level label types (e.g. explicit negative). + groundtruth_is_crowd: is the groundtruth a single object or a crowd. + groundtruth_area: area of a groundtruth segment. + groundtruth_difficult: is a `difficult` object + proposal_boxes: coordinates of object proposal boxes. + proposal_objectness: objectness score of each proposal. + groundtruth_instance_masks: ground truth instance masks. + groundtruth_instance_classes: instance mask-level class labels. + groundtruth_keypoints: ground truth keypoints. + groundtruth_keypoint_visibilities: ground truth keypoint visibilities. + groundtruth_label_scores: groundtruth label scores. + """ + image = 'image' + original_image = 'original_image' + key = 'key' + source_id = 'source_id' + filename = 'filename' + groundtruth_image_classes = 'groundtruth_image_classes' + groundtruth_boxes = 'groundtruth_boxes' + groundtruth_classes = 'groundtruth_classes' + groundtruth_label_types = 'groundtruth_label_types' + groundtruth_is_crowd = 'groundtruth_is_crowd' + groundtruth_area = 'groundtruth_area' + groundtruth_difficult = 'groundtruth_difficult' + proposal_boxes = 'proposal_boxes' + proposal_objectness = 'proposal_objectness' + groundtruth_instance_masks = 'groundtruth_instance_masks' + groundtruth_instance_classes = 'groundtruth_instance_classes' + groundtruth_keypoints = 'groundtruth_keypoints' + groundtruth_keypoint_visibilities = 'groundtruth_keypoint_visibilities' + groundtruth_label_scores = 'groundtruth_label_scores' + + +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. + keypoints: keypoints per bounding box. + keypoint_heatmaps: keypoint heatmaps per bounding box. + """ + boxes = 'boxes' + classes = 'classes' + scores = 'scores' + weights = 'weights' + objectness = 'objectness' + masks = 'masks' + keypoints = 'keypoints' + keypoint_heatmaps = 'keypoint_heatmaps' + + +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 + object_class_text: labels in text format, e.g. ["person", "cat"] + object_class_text: 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_is_crowd: is the object a single object or a crowd + object_segment_area: the area of the segment. + instance_masks: instance segmentation masks. + instance_classes: Classes for each instance segmentation mask. + """ + 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' + 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_is_crowd = 'image/object/is_crowd' + object_segment_area = 'image/object/segment/area' + instance_masks = 'image/segmentation/object' + instance_classes = 'image/segmentation/object/class' diff --git a/object_detection/core/target_assigner.py b/object_detection/core/target_assigner.py new file mode 100644 index 000000000..a9f3f5aea --- /dev/null +++ b/object_detection/core/target_assigner.py @@ -0,0 +1,449 @@ +# 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. +""" +import tensorflow as tf + +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 as bcoder +from object_detection.core import box_list +from object_detection.core import box_list_ops +from object_detection.core import matcher as mat +from object_detection.core import region_similarity_calculator as sim_calc +from object_detection.matchers import argmax_matcher +from object_detection.matchers import bipartite_matcher + + +class TargetAssigner(object): + """Target assigner to compute classification and regression targets.""" + + def __init__(self, similarity_calc, matcher, box_coder, + positive_class_weight=1.0, negative_class_weight=1.0, + unmatched_cls_target=None): + """Construct Multibox Target Assigner. + + Args: + similarity_calc: a RegionSimilarityCalculator + matcher: an object_detection.core.Matcher used to match groundtruth to + anchors. + box_coder: an object_detection.core.BoxCoder used to encode matching + groundtruth boxes with respect to anchors. + positive_class_weight: classification weight to be associated to positive + anchors (default: 1.0) + negative_class_weight: classification weight to be associated to negative + anchors (default: 1.0) + unmatched_cls_target: 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. + + 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, bcoder.BoxCoder): + raise ValueError('box_coder must be a BoxCoder') + self._similarity_calc = similarity_calc + self._matcher = matcher + self._box_coder = box_coder + self._positive_class_weight = positive_class_weight + self._negative_class_weight = negative_class_weight + if unmatched_cls_target is None: + self._unmatched_cls_target = tf.constant([0], tf.float32) + else: + self._unmatched_cls_target = unmatched_cls_target + + @property + def box_coder(self): + return self._box_coder + + def assign(self, anchors, groundtruth_boxes, groundtruth_labels=None, + **params): + """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 [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 inputs). When set + to None, groundtruth_labels assumes a binary problem where all + ground_truth boxes get a positive label (of 1). + **params: Additional keyword arguments for specific implementations of + the Matcher. + + 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] + reg_targets: a float32 tensor with shape [num_anchors, box_code_dimension] + reg_weights: a float32 tensor with shape [num_anchors] + match: a matcher.Match object encoding the match between anchors and + groundtruth boxes, with rows corresponding to groundtruth boxes + and columns corresponding to anchors. + + 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 groundtruth_labels is None: + groundtruth_labels = tf.ones(tf.expand_dims(groundtruth_boxes.num_boxes(), + 0)) + groundtruth_labels = tf.expand_dims(groundtruth_labels, -1) + shape_assert = tf.assert_equal(tf.shape(groundtruth_labels)[1:], + tf.shape(self._unmatched_cls_target)) + + with tf.control_dependencies([shape_assert]): + match_quality_matrix = self._similarity_calc.compare(groundtruth_boxes, + anchors) + match = self._matcher.match(match_quality_matrix, **params) + reg_targets = self._create_regression_targets(anchors, + groundtruth_boxes, + match) + cls_targets = self._create_classification_targets(groundtruth_labels, + match) + reg_weights = self._create_regression_weights(match) + cls_weights = self._create_classification_weights( + match, self._positive_class_weight, self._negative_class_weight) + + 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 + + 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_anchor_indices = match.matched_column_indices() + unmatched_ignored_anchor_indices = (match. + unmatched_or_ignored_column_indices()) + matched_gt_indices = match.matched_row_indices() + matched_anchors = box_list_ops.gather(anchors, + matched_anchor_indices) + matched_gt_boxes = box_list_ops.gather(groundtruth_boxes, + matched_gt_indices) + matched_reg_targets = self._box_coder.encode(matched_gt_boxes, + matched_anchors) + unmatched_ignored_reg_targets = tf.tile( + self._default_regression_target(), + tf.stack([tf.size(unmatched_ignored_anchor_indices), 1])) + reg_targets = tf.dynamic_stitch( + [matched_anchor_indices, unmatched_ignored_anchor_indices], + [matched_reg_targets, unmatched_ignored_reg_targets]) + # TODO: summarize the number of matches on average. + 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, 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). + match: a matcher.Match object that provides a matching between anchors + and groundtruth boxes. + + 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]. + """ + matched_anchor_indices = match.matched_column_indices() + unmatched_ignored_anchor_indices = (match. + unmatched_or_ignored_column_indices()) + matched_gt_indices = match.matched_row_indices() + matched_cls_targets = tf.gather(groundtruth_labels, matched_gt_indices) + + ones = self._unmatched_cls_target.shape.ndims * [1] + unmatched_ignored_cls_targets = tf.tile( + tf.expand_dims(self._unmatched_cls_target, 0), + tf.stack([tf.size(unmatched_ignored_anchor_indices)] + ones)) + + cls_targets = tf.dynamic_stitch( + [matched_anchor_indices, unmatched_ignored_anchor_indices], + [matched_cls_targets, unmatched_ignored_cls_targets]) + return cls_targets + + def _create_regression_weights(self, match): + """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. + + Returns: + reg_weights: a float32 tensor with shape [num_anchors] representing + regression weights + """ + reg_weights = tf.cast(match.matched_column_indicator(), tf.float32) + return reg_weights + + def _create_classification_weights(self, + match, + positive_class_weight=1.0, + negative_class_weight=1.0): + """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. + positive_class_weight: weight to be associated to positive anchors + negative_class_weight: weight to be associated to negative anchors + + Returns: + cls_weights: a float32 tensor with shape [num_anchors] representing + classification weights. + """ + matched_indicator = tf.cast(match.matched_column_indicator(), tf.float32) + ignore_indicator = tf.cast(match.ignored_column_indicator(), tf.float32) + unmatched_indicator = 1.0 - matched_indicator - ignore_indicator + cls_weights = (positive_class_weight * matched_indicator + + negative_class_weight * unmatched_indicator) + return cls_weights + + def get_box_coder(self): + """Get BoxCoder of this TargetAssigner. + + Returns: + BoxCoder: BoxCoder object. + """ + return self._box_coder + + +# TODO: 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, + positive_class_weight=1.0, + negative_class_weight=1.0, + unmatched_cls_target=None): + """Factory function for creating standard target assigners. + + Args: + reference: string referencing the type of TargetAssigner. + stage: string denoting stage: {proposal, detection}. + positive_class_weight: classification weight to be associated to positive + anchors (default: 1.0) + negative_class_weight: classification weight to be associated to negative + anchors (default: 1.0) + unmatched_cls_target: 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. + + Returns: + TargetAssigner: desired target assigner. + + Raises: + ValueError: if combination reference+stage is invalid. + """ + if reference == 'Multibox' and stage == 'proposal': + similarity_calc = sim_calc.NegSqDistSimilarity() + matcher = bipartite_matcher.GreedyBipartiteMatcher() + box_coder = 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) + box_coder = 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) + box_coder = 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) + box_coder = faster_rcnn_box_coder.FasterRcnnBoxCoder() + + else: + raise ValueError('No valid combination of reference and stage.') + + return TargetAssigner(similarity_calc, matcher, box_coder, + positive_class_weight=positive_class_weight, + negative_class_weight=negative_class_weight, + unmatched_cls_target=unmatched_cls_target) + + +def batch_assign_targets(target_assigner, + anchors_batch, + gt_box_batch, + gt_class_targets_batch): + """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. + + 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], + 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_list: a 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. + 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 = [] + for anchors, gt_boxes, gt_class_targets in zip( + anchors_batch, gt_box_batch, gt_class_targets_batch): + (cls_targets, cls_weights, reg_targets, + reg_weights, match) = target_assigner.assign( + anchors, gt_boxes, gt_class_targets) + 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) + return (batch_cls_targets, batch_cls_weights, batch_reg_targets, + batch_reg_weights, match_list) diff --git a/object_detection/core/target_assigner_test.py b/object_detection/core/target_assigner_test.py new file mode 100644 index 000000000..92c756489 --- /dev/null +++ b/object_detection/core/target_assigner_test.py @@ -0,0 +1,682 @@ +# 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 as tf + +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 target_assigner as targetassigner +from object_detection.matchers import argmax_matcher +from object_detection.matchers import bipartite_matcher + + +class TargetAssignerTest(tf.test.TestCase): + + def test_assign_agnostic(self): + similarity_calc = region_similarity_calculator.NegSqDistSimilarity() + matcher = bipartite_matcher.GreedyBipartiteMatcher() + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder() + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder, unmatched_cls_target=None) + + 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]]) + prior_stddevs = tf.constant(3 * [4 * [.1]]) + priors = box_list.BoxList(prior_means) + priors.add_field('stddev', prior_stddevs) + + box_corners = [[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.9, 0.9]] + boxes = box_list.BoxList(tf.constant(box_corners)) + 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] + exp_matching_anchors = [0, 1] + + result = target_assigner.assign(priors, boxes, num_valid_rows=2) + (cls_targets, cls_weights, reg_targets, reg_weights, match) = result + + with self.test_session() as sess: + (cls_targets_out, cls_weights_out, + reg_targets_out, reg_weights_out, matching_anchors_out) = sess.run( + [cls_targets, cls_weights, reg_targets, reg_weights, + match.matched_column_indices()]) + + 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.assertAllClose(matching_anchors_out, exp_matching_anchors) + self.assertEquals(cls_targets_out.dtype, np.float32) + self.assertEquals(cls_weights_out.dtype, np.float32) + self.assertEquals(reg_targets_out.dtype, np.float32) + self.assertEquals(reg_weights_out.dtype, np.float32) + self.assertEquals(matching_anchors_out.dtype, np.int32) + + def test_assign_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]. + 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() + 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, 0.5, .9, 1.0]]) + prior_stddevs = tf.constant(3 * [4 * [.1]]) + priors = box_list.BoxList(prior_means) + priors.add_field('stddev', prior_stddevs) + + box_corners = [[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.9, 0.9]] + boxes = box_list.BoxList(tf.constant(box_corners)) + 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] + exp_matching_anchors = [0, 1] + + result = target_assigner.assign(priors, boxes) + (cls_targets, cls_weights, reg_targets, reg_weights, match) = result + with self.test_session() as sess: + (cls_targets_out, cls_weights_out, + reg_targets_out, reg_weights_out, matching_anchors_out) = sess.run( + [cls_targets, cls_weights, reg_targets, reg_weights, + match.matched_column_indices()]) + + 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.assertAllClose(matching_anchors_out, exp_matching_anchors) + self.assertEquals(cls_targets_out.dtype, np.float32) + self.assertEquals(cls_weights_out.dtype, np.float32) + self.assertEquals(reg_targets_out.dtype, np.float32) + self.assertEquals(reg_weights_out.dtype, np.float32) + self.assertEquals(matching_anchors_out.dtype, np.int32) + + def test_assign_multiclass(self): + similarity_calc = region_similarity_calculator.NegSqDistSimilarity() + matcher = bipartite_matcher.GreedyBipartiteMatcher() + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder() + unmatched_cls_target = tf.constant([1, 0, 0, 0, 0, 0, 0], tf.float32) + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder, + unmatched_cls_target=unmatched_cls_target) + + 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]]) + prior_stddevs = tf.constant(4 * [4 * [.1]]) + priors = box_list.BoxList(prior_means) + priors.add_field('stddev', prior_stddevs) + + 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, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 1, 0, 0, 0]], tf.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] + 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] + exp_matching_anchors = [0, 1, 3] + + result = target_assigner.assign(priors, boxes, groundtruth_labels, + num_valid_rows=3) + (cls_targets, cls_weights, reg_targets, reg_weights, match) = result + with self.test_session() as sess: + (cls_targets_out, cls_weights_out, + reg_targets_out, reg_weights_out, matching_anchors_out) = sess.run( + [cls_targets, cls_weights, reg_targets, reg_weights, + match.matched_column_indices()]) + + 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.assertAllClose(matching_anchors_out, exp_matching_anchors) + self.assertEquals(cls_targets_out.dtype, np.float32) + self.assertEquals(cls_weights_out.dtype, np.float32) + self.assertEquals(reg_targets_out.dtype, np.float32) + self.assertEquals(reg_weights_out.dtype, np.float32) + self.assertEquals(matching_anchors_out.dtype, np.int32) + + def test_assign_multiclass_unequal_class_weights(self): + similarity_calc = region_similarity_calculator.NegSqDistSimilarity() + matcher = bipartite_matcher.GreedyBipartiteMatcher() + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder() + unmatched_cls_target = tf.constant([1, 0, 0, 0, 0, 0, 0], tf.float32) + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder, + positive_class_weight=1.0, negative_class_weight=0.5, + unmatched_cls_target=unmatched_cls_target) + + 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]]) + prior_stddevs = tf.constant(4 * [4 * [.1]]) + priors = box_list.BoxList(prior_means) + priors.add_field('stddev', prior_stddevs) + + 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, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 1, 0, 0, 0]], tf.float32) + + exp_cls_weights = [1, 1, .5, 1] + result = target_assigner.assign(priors, boxes, groundtruth_labels, + num_valid_rows=3) + (_, cls_weights, _, _, _) = result + with self.test_session() as sess: + cls_weights_out = sess.run(cls_weights) + self.assertAllClose(cls_weights_out, exp_cls_weights) + + def test_assign_multidimensional_class_targets(self): + similarity_calc = region_similarity_calculator.NegSqDistSimilarity() + matcher = bipartite_matcher.GreedyBipartiteMatcher() + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder() + unmatched_cls_target = tf.constant([[0, 0], [0, 0]], tf.float32) + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder, + unmatched_cls_target=unmatched_cls_target) + + 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]]) + prior_stddevs = tf.constant(4 * [4 * [.1]]) + priors = box_list.BoxList(prior_means) + priors.add_field('stddev', prior_stddevs) + + 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]], + [[1, 0], [0, 1]], + [[0, 1], [1, .5]]], tf.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] + 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] + exp_matching_anchors = [0, 1, 3] + + result = target_assigner.assign(priors, boxes, groundtruth_labels, + num_valid_rows=3) + (cls_targets, cls_weights, reg_targets, reg_weights, match) = result + with self.test_session() as sess: + (cls_targets_out, cls_weights_out, + reg_targets_out, reg_weights_out, matching_anchors_out) = sess.run( + [cls_targets, cls_weights, reg_targets, reg_weights, + match.matched_column_indices()]) + + 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.assertAllClose(matching_anchors_out, exp_matching_anchors) + self.assertEquals(cls_targets_out.dtype, np.float32) + self.assertEquals(cls_weights_out.dtype, np.float32) + self.assertEquals(reg_targets_out.dtype, np.float32) + self.assertEquals(reg_weights_out.dtype, np.float32) + self.assertEquals(matching_anchors_out.dtype, np.int32) + + def test_assign_empty_groundtruth(self): + similarity_calc = region_similarity_calculator.NegSqDistSimilarity() + matcher = bipartite_matcher.GreedyBipartiteMatcher() + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder() + unmatched_cls_target = tf.constant([0, 0, 0], tf.float32) + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder, + unmatched_cls_target=unmatched_cls_target) + + 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]]) + prior_stddevs = tf.constant(4 * [4 * [.1]]) + priors = box_list.BoxList(prior_means) + priors.add_field('stddev', prior_stddevs) + + box_corners_expanded = tf.constant([[0.0, 0.0, 0.0, 0.0]]) + box_corners = tf.slice(box_corners_expanded, [0, 0], [0, 4]) + boxes = box_list.BoxList(box_corners) + + groundtruth_labels_expanded = tf.constant([[0, 0, 0]], tf.float32) + groundtruth_labels = tf.slice(groundtruth_labels_expanded, [0, 0], [0, 3]) + + exp_cls_targets = [[0, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0]] + exp_cls_weights = [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] + exp_matching_anchors = [] + + result = target_assigner.assign(priors, boxes, groundtruth_labels) + (cls_targets, cls_weights, reg_targets, reg_weights, match) = result + with self.test_session() as sess: + (cls_targets_out, cls_weights_out, + reg_targets_out, reg_weights_out, matching_anchors_out) = sess.run( + [cls_targets, cls_weights, reg_targets, reg_weights, + match.matched_column_indices()]) + + 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.assertAllClose(matching_anchors_out, exp_matching_anchors) + self.assertEquals(cls_targets_out.dtype, np.float32) + self.assertEquals(cls_weights_out.dtype, np.float32) + self.assertEquals(reg_targets_out.dtype, np.float32) + self.assertEquals(reg_weights_out.dtype, np.float32) + self.assertEquals(matching_anchors_out.dtype, np.int32) + + def test_raises_error_on_invalid_groundtruth_labels(self): + similarity_calc = region_similarity_calculator.NegSqDistSimilarity() + matcher = bipartite_matcher.GreedyBipartiteMatcher() + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder() + unmatched_cls_target = tf.constant([[0, 0], [0, 0], [0, 0]], tf.float32) + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder, + unmatched_cls_target=unmatched_cls_target) + + prior_means = tf.constant([[0.0, 0.0, 0.5, 0.5]]) + prior_stddevs = tf.constant([[1.0, 1.0, 1.0, 1.0]]) + priors = box_list.BoxList(prior_means) + priors.add_field('stddev', prior_stddevs) + + 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, + num_valid_rows=3) + + +class BatchTargetAssignerTest(tf.test.TestCase): + + def _get_agnostic_target_assigner(self): + similarity_calc = region_similarity_calculator.NegSqDistSimilarity() + matcher = bipartite_matcher.GreedyBipartiteMatcher() + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder() + return targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder, + positive_class_weight=1.0, + negative_class_weight=1.0, + unmatched_cls_target=None) + + def _get_multi_class_target_assigner(self, num_classes): + similarity_calc = region_similarity_calculator.NegSqDistSimilarity() + matcher = bipartite_matcher.GreedyBipartiteMatcher() + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder() + unmatched_cls_target = tf.constant([1] + num_classes * [0], tf.float32) + return targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder, + positive_class_weight=1.0, + negative_class_weight=1.0, + unmatched_cls_target=unmatched_cls_target) + + def _get_multi_dimensional_target_assigner(self, target_dimensions): + similarity_calc = region_similarity_calculator.NegSqDistSimilarity() + matcher = bipartite_matcher.GreedyBipartiteMatcher() + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder() + unmatched_cls_target = tf.constant(np.zeros(target_dimensions), + tf.float32) + return targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder, + positive_class_weight=1.0, + negative_class_weight=1.0, + unmatched_cls_target=unmatched_cls_target) + + def test_batch_assign_targets(self): + box_list1 = box_list.BoxList(tf.constant([[0., 0., 0.2, 0.2]])) + box_list2 = box_list.BoxList(tf.constant( + [[0, 0.25123152, 1, 1], + [0.015789, 0.0985, 0.55789, 0.3842]] + )) + + gt_box_batch = [box_list1, box_list2] + gt_class_targets = [None, None] + + prior_means = tf.constant([[0, 0, .25, .25], + [0, .25, 1, 1], + [0, .1, .5, .5], + [.75, .75, 1, 1]]) + prior_stddevs = tf.constant([[.1, .1, .1, .1], + [.1, .1, .1, .1], + [.1, .1, .1, .1], + [.1, .1, .1, .1]]) + priors = box_list.BoxList(prior_means) + priors.add_field('stddev', prior_stddevs) + + 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_cls_weights = [[1, 1, 1, 1], + [1, 1, 1, 1]] + exp_cls_targets = [[[1], [0], [0], [0]], + [[0], [1], [1], [0]]] + exp_reg_weights = [[1, 0, 0, 0], + [0, 1, 1, 0]] + exp_match_0 = [0] + exp_match_1 = [1, 2] + + agnostic_target_assigner = self._get_agnostic_target_assigner() + (cls_targets, cls_weights, reg_targets, reg_weights, + match_list) = targetassigner.batch_assign_targets( + agnostic_target_assigner, priors, gt_box_batch, gt_class_targets) + self.assertTrue(isinstance(match_list, list) and len(match_list) == 2) + with self.test_session() as sess: + (cls_targets_out, cls_weights_out, reg_targets_out, reg_weights_out, + match_out_0, match_out_1) = sess.run([ + cls_targets, cls_weights, reg_targets, reg_weights] + [ + match.matched_column_indices() for match in match_list]) + 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.assertAllClose(match_out_0, exp_match_0) + self.assertAllClose(match_out_1, exp_match_1) + + def test_batch_assign_multiclass_targets(self): + box_list1 = box_list.BoxList(tf.constant([[0., 0., 0.2, 0.2]])) + + box_list2 = box_list.BoxList(tf.constant( + [[0, 0.25123152, 1, 1], + [0.015789, 0.0985, 0.55789, 0.3842]] + )) + + gt_box_batch = [box_list1, box_list2] + + class_targets1 = tf.constant([[0, 1, 0, 0]], tf.float32) + class_targets2 = tf.constant([[0, 0, 0, 1], + [0, 0, 1, 0]], tf.float32) + + gt_class_targets = [class_targets1, class_targets2] + + prior_means = tf.constant([[0, 0, .25, .25], + [0, .25, 1, 1], + [0, .1, .5, .5], + [.75, .75, 1, 1]]) + prior_stddevs = tf.constant([[.1, .1, .1, .1], + [.1, .1, .1, .1], + [.1, .1, .1, .1], + [.1, .1, .1, .1]]) + priors = box_list.BoxList(prior_means) + priors.add_field('stddev', prior_stddevs) + + 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_cls_weights = [[1, 1, 1, 1], + [1, 1, 1, 1]] + 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_reg_weights = [[1, 0, 0, 0], + [0, 1, 1, 0]] + exp_match_0 = [0] + exp_match_1 = [1, 2] + + multiclass_target_assigner = self._get_multi_class_target_assigner( + num_classes=3) + + (cls_targets, cls_weights, reg_targets, reg_weights, + match_list) = targetassigner.batch_assign_targets( + multiclass_target_assigner, priors, gt_box_batch, gt_class_targets) + self.assertTrue(isinstance(match_list, list) and len(match_list) == 2) + with self.test_session() as sess: + (cls_targets_out, cls_weights_out, reg_targets_out, reg_weights_out, + match_out_0, match_out_1) = sess.run([ + cls_targets, cls_weights, reg_targets, reg_weights] + [ + match.matched_column_indices() for match in match_list]) + 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.assertAllClose(match_out_0, exp_match_0) + self.assertAllClose(match_out_1, exp_match_1) + + def test_batch_assign_multidimensional_targets(self): + box_list1 = box_list.BoxList(tf.constant([[0., 0., 0.2, 0.2]])) + + box_list2 = box_list.BoxList(tf.constant( + [[0, 0.25123152, 1, 1], + [0.015789, 0.0985, 0.55789, 0.3842]] + )) + + gt_box_batch = [box_list1, box_list2] + class_targets1 = tf.constant([[[0, 1, 1], + [1, 1, 0]]], tf.float32) + class_targets2 = tf.constant([[[0, 1, 1], + [1, 1, 0]], + [[0, 0, 1], + [0, 0, 1]]], tf.float32) + + gt_class_targets = [class_targets1, class_targets2] + + prior_means = tf.constant([[0, 0, .25, .25], + [0, .25, 1, 1], + [0, .1, .5, .5], + [.75, .75, 1, 1]]) + prior_stddevs = tf.constant([[.1, .1, .1, .1], + [.1, .1, .1, .1], + [.1, .1, .1, .1], + [.1, .1, .1, .1]]) + priors = box_list.BoxList(prior_means) + priors.add_field('stddev', prior_stddevs) + + 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_cls_weights = [[1, 1, 1, 1], + [1, 1, 1, 1]] + + 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_reg_weights = [[1, 0, 0, 0], + [0, 1, 1, 0]] + exp_match_0 = [0] + exp_match_1 = [1, 2] + + multiclass_target_assigner = self._get_multi_dimensional_target_assigner( + target_dimensions=(2, 3)) + + (cls_targets, cls_weights, reg_targets, reg_weights, + match_list) = targetassigner.batch_assign_targets( + multiclass_target_assigner, priors, gt_box_batch, gt_class_targets) + self.assertTrue(isinstance(match_list, list) and len(match_list) == 2) + with self.test_session() as sess: + (cls_targets_out, cls_weights_out, reg_targets_out, reg_weights_out, + match_out_0, match_out_1) = sess.run([ + cls_targets, cls_weights, reg_targets, reg_weights] + [ + match.matched_column_indices() for match in match_list]) + 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.assertAllClose(match_out_0, exp_match_0) + self.assertAllClose(match_out_1, exp_match_1) + + def test_batch_assign_empty_groundtruth(self): + box_coords_expanded = tf.zeros((1, 4), tf.float32) + box_coords = tf.slice(box_coords_expanded, [0, 0], [0, 4]) + box_list1 = box_list.BoxList(box_coords) + gt_box_batch = [box_list1] + + prior_means = tf.constant([[0, 0, .25, .25], + [0, .25, 1, 1]]) + prior_stddevs = tf.constant([[.1, .1, .1, .1], + [.1, .1, .1, .1]]) + priors = box_list.BoxList(prior_means) + priors.add_field('stddev', prior_stddevs) + + exp_reg_targets = [[[0, 0, 0, 0], + [0, 0, 0, 0]]] + exp_cls_weights = [[1, 1]] + exp_cls_targets = [[[1, 0, 0, 0], + [1, 0, 0, 0]]] + exp_reg_weights = [[0, 0]] + exp_match_0 = [] + + num_classes = 3 + pad = 1 + gt_class_targets = tf.zeros((0, num_classes + pad)) + gt_class_targets_batch = [gt_class_targets] + + multiclass_target_assigner = self._get_multi_class_target_assigner( + num_classes=3) + + (cls_targets, cls_weights, reg_targets, reg_weights, + match_list) = targetassigner.batch_assign_targets( + multiclass_target_assigner, priors, + gt_box_batch, gt_class_targets_batch) + self.assertTrue(isinstance(match_list, list) and len(match_list) == 1) + with self.test_session() as sess: + (cls_targets_out, cls_weights_out, reg_targets_out, reg_weights_out, + match_out_0) = sess.run([ + cls_targets, cls_weights, reg_targets, reg_weights] + [ + match.matched_column_indices() for match in match_list]) + 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.assertAllClose(match_out_0, exp_match_0) + + +class CreateTargetAssignerTest(tf.test.TestCase): + + def test_create_target_assigner(self): + """Tests that named constructor gives working target assigners. + + TODO: 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)) + prior_stddevs = tf.constant([[1.0, 1.0, 1.0, 1.0]]) + priors.add_field('stddev', prior_stddevs) + 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') + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/create_pascal_tf_record.py b/object_detection/create_pascal_tf_record.py new file mode 100644 index 000000000..b25980ece --- /dev/null +++ b/object_detection/create_pascal_tf_record.py @@ -0,0 +1,174 @@ +# 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: + ./create_pascal_tf_record --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 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_enum('set', 'train', ['train', 'val', 'trainval', 'test'], + 'Convert training set, validation set or merged set.') +flags.DEFINE_string('annotations_dir', 'Annotations', + '(Relative) path to annotations directory.') +flags.DEFINE_enum('year', 'VOC2007', ['VOC2007', 'VOC2012', 'merged'], + '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 + + +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) 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 = [] + 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']) + classes.append(label_map_dict[obj['name']]) + truncated.append(int(obj['truncated'])) + poses.append(obj['pose']) + + 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']), + 'image/source_id': dataset_util.bytes_feature(data['filename']), + 'image/key/sha256': dataset_util.bytes_feature(key), + 'image/encoded': dataset_util.bytes_feature(encoded_jpg), + 'image/format': dataset_util.bytes_feature('jpeg'), + '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(_): + 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/object_detection/create_pascal_tf_record_test.py b/object_detection/create_pascal_tf_record_test.py new file mode 100644 index 000000000..dd29c6c2b --- /dev/null +++ b/object_detection/create_pascal_tf_record_test.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. +# ============================================================================== + +"""Test for create_pascal_tf_record.py.""" + +import os + +import numpy as np +import PIL.Image +import tensorflow as tf + +from object_detection import create_pascal_tf_record + + +class DictToTFExampleTest(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, + [image_file_name]) + self._assertProtoEqual( + example.features.feature['image/source_id'].bytes_list.value, + [image_file_name]) + self._assertProtoEqual( + example.features.feature['image/format'].bytes_list.value, ['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, + ['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, ['']) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/create_pet_tf_record.py b/object_detection/create_pet_tf_record.py new file mode 100644 index 000000000..2bfbb4de3 --- /dev/null +++ b/object_detection/create_pet_tf_record.py @@ -0,0 +1,211 @@ +# 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: + ./create_pet_tf_record --data_dir=/home/user/pet \ + --output_dir=/home/user/pet/output +""" + +import hashlib +import io +import logging +import os +import random +import re + +from lxml import etree +import PIL.Image +import tensorflow 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 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 = 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: + example: The converted tf.Example. + """ + match = re.match(r'([A-Za-z_]+)(_[0-9]+\.jpg)', file_name, re.I) + return match.groups()[0] + + +def dict_to_tf_example(data, + label_map_dict, + image_subdirectory, + ignore_difficult_instances=False): + """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) + 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). + + 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) 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 = [] + 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) + class_name = get_class_name_from_filename(data['filename']) + classes_text.append(class_name) + classes.append(label_map_dict[class_name]) + truncated.append(int(obj['truncated'])) + poses.append(obj['pose']) + + 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']), + 'image/source_id': dataset_util.bytes_feature(data['filename']), + 'image/key/sha256': dataset_util.bytes_feature(key), + 'image/encoded': dataset_util.bytes_feature(encoded_jpg), + 'image/format': dataset_util.bytes_feature('jpeg'), + '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 create_tf_record(output_filename, + label_map_dict, + annotations_dir, + image_dir, + examples): + """Creates a TFRecord file from examples. + + Args: + output_filename: Path to where output file is saved. + 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. + """ + writer = tf.python_io.TFRecordWriter(output_filename) + for idx, example in enumerate(examples): + if idx % 100 == 0: + logging.info('On image %d of %d', idx, len(examples)) + path = os.path.join(annotations_dir, 'xmls', example + '.xml') + + if not os.path.exists(path): + logging.warning('Could not find %s, ignoring example.', path) + continue + 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, label_map_dict, image_dir) + writer.write(tf_example.SerializeToString()) + + writer.close() + + +# TODO: 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_train.record') + val_output_path = os.path.join(FLAGS.output_dir, 'pet_val.record') + create_tf_record(train_output_path, label_map_dict, annotations_dir, + image_dir, train_examples) + create_tf_record(val_output_path, label_map_dict, annotations_dir, + image_dir, val_examples) + +if __name__ == '__main__': + tf.app.run() diff --git a/object_detection/data/mscoco_label_map.pbtxt b/object_detection/data/mscoco_label_map.pbtxt new file mode 100644 index 000000000..1f4872bd0 --- /dev/null +++ b/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/object_detection/data/pascal_label_map.pbtxt b/object_detection/data/pascal_label_map.pbtxt new file mode 100644 index 000000000..f79d3d5e9 --- /dev/null +++ b/object_detection/data/pascal_label_map.pbtxt @@ -0,0 +1,104 @@ +item { + id: 0 + name: 'none_of_the_above' +} + +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/object_detection/data/pet_label_map.pbtxt b/object_detection/data/pet_label_map.pbtxt new file mode 100644 index 000000000..61813d687 --- /dev/null +++ b/object_detection/data/pet_label_map.pbtxt @@ -0,0 +1,189 @@ +item { + id: 0 + name: 'none_of_the_above' +} + +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/object_detection/data_decoders/BUILD b/object_detection/data_decoders/BUILD new file mode 100644 index 000000000..c857294a5 --- /dev/null +++ b/object_detection/data_decoders/BUILD @@ -0,0 +1,28 @@ +# Tensorflow Object Detection API: data decoders. + +package( + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) +# Apache 2.0 + +py_library( + name = "tf_example_decoder", + srcs = ["tf_example_decoder.py"], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/core:data_decoder", + "//tensorflow_models/object_detection/core:standard_fields", + ], +) + +py_test( + name = "tf_example_decoder_test", + srcs = ["tf_example_decoder_test.py"], + deps = [ + ":tf_example_decoder", + "//tensorflow", + "//tensorflow_models/object_detection/core:standard_fields", + ], +) diff --git a/object_detection/data_decoders/__init__.py b/object_detection/data_decoders/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/object_detection/data_decoders/tf_example_decoder.py b/object_detection/data_decoders/tf_example_decoder.py new file mode 100644 index 000000000..fcea12cb4 --- /dev/null +++ b/object_detection/data_decoders/tf_example_decoder.py @@ -0,0 +1,147 @@ +# 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. +""" +import tensorflow as tf + +from object_detection.core import data_decoder +from object_detection.core import standard_fields as fields + +slim_example_decoder = tf.contrib.slim.tfexample_decoder + + +class TfExampleDecoder(data_decoder.DataDecoder): + """Tensorflow Example proto decoder.""" + + def __init__(self): + """Constructor sets keys_to_features and items_to_handlers.""" + 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, 1), + 'image/width': tf.FixedLenFeature((), tf.int64, 1), + # 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/area': tf.VarLenFeature(tf.float32), + 'image/object/is_crowd': tf.VarLenFeature(tf.int64), + 'image/object/difficult': tf.VarLenFeature(tf.int64), + # Instance masks and classes. + 'image/segmentation/object': tf.VarLenFeature(tf.int64), + 'image/segmentation/object/class': tf.VarLenFeature(tf.int64) + } + self.items_to_handlers = { + fields.InputDataFields.image: slim_example_decoder.Image( + image_key='image/encoded', format_key='image/format', channels=3), + 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')), + # Object boxes and classes. + fields.InputDataFields.groundtruth_boxes: ( + slim_example_decoder.BoundingBox( + ['ymin', 'xmin', 'ymax', 'xmax'], 'image/object/bbox/')), + fields.InputDataFields.groundtruth_classes: ( + slim_example_decoder.Tensor('image/object/class/label')), + 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')), + # Instance masks and classes. + fields.InputDataFields.groundtruth_instance_masks: ( + slim_example_decoder.ItemHandlerCallback( + ['image/segmentation/object', 'image/height', 'image/width'], + self._reshape_instance_masks)), + fields.InputDataFields.groundtruth_instance_classes: ( + slim_example_decoder.Tensor('image/segmentation/object/class')), + } + + 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.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_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. + fields.InputDataFields.groundtruth_difficult - 1D bool tensor of shape + [None] indicating if the boxes represent `difficult` instances. + fields.InputDataFields.groundtruth_instance_masks - 3D int64 tensor of + shape [None, None, None] containing instance masks. + fields.InputDataFields.groundtruth_instance_classes - 1D int64 tensor + of shape [None] containing classes for the instance masks. + """ + + 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]) + return tensor_dict + + def _reshape_instance_masks(self, keys_to_tensors): + """Reshape instance segmentation masks. + + The instance segmentation masks are reshaped to [num_instances, height, + width] and cast to boolean type to save memory. + + Args: + keys_to_tensors: a dictionary from keys to tensors. + + Returns: + A 3-D boolean tensor of shape [num_instances, height, width]. + """ + masks = keys_to_tensors['image/segmentation/object'] + if isinstance(masks, tf.SparseTensor): + masks = tf.sparse_tensor_to_dense(masks) + height = keys_to_tensors['image/height'] + width = keys_to_tensors['image/width'] + to_shape = tf.cast(tf.stack([-1, height, width]), tf.int32) + + return tf.cast(tf.reshape(masks, to_shape), tf.bool) diff --git a/object_detection/data_decoders/tf_example_decoder_test.py b/object_detection/data_decoders/tf_example_decoder_test.py new file mode 100644 index 000000000..4a28419a7 --- /dev/null +++ b/object_detection/data_decoders/tf_example_decoder_test.py @@ -0,0 +1,288 @@ +# 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.data_decoders.tf_example_decoder.""" + +import numpy as np +import tensorflow as tf + +from object_detection.core import standard_fields as fields +from object_detection.data_decoders import tf_example_decoder + + +class TfExampleDecoderTest(tf.test.TestCase): + + def _EncodeImage(self, image_tensor, encoding_type='jpeg'): + with self.test_session(): + if encoding_type == 'jpeg': + image_encoded = tf.image.encode_jpeg(tf.constant(image_tensor)).eval() + elif encoding_type == 'png': + image_encoded = tf.image.encode_png(tf.constant(image_tensor)).eval() + else: + raise ValueError('Invalid encoding type.') + return image_encoded + + def _DecodeImage(self, image_encoded, encoding_type='jpeg'): + with self.test_session(): + if encoding_type == 'jpeg': + image_decoded = tf.image.decode_jpeg(tf.constant(image_encoded)).eval() + elif encoding_type == 'png': + image_decoded = tf.image.decode_png(tf.constant(image_encoded)).eval() + else: + raise ValueError('Invalid encoding type.') + return image_decoded + + def _Int64Feature(self, value): + return tf.train.Feature(int64_list=tf.train.Int64List(value=value)) + + def _FloatFeature(self, value): + return tf.train.Feature(float_list=tf.train.FloatList(value=value)) + + def _BytesFeature(self, value): + return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value])) + + def testDecodeJpegImage(self): + image_tensor = np.random.randint(255, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg = self._EncodeImage(image_tensor) + decoded_jpeg = self._DecodeImage(encoded_jpeg) + example = tf.train.Example(features=tf.train.Features(feature={ + 'image/encoded': self._BytesFeature(encoded_jpeg), + 'image/format': self._BytesFeature('jpeg'), + 'image/source_id': self._BytesFeature('image_id'), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((tensor_dict[fields.InputDataFields.image]. + get_shape().as_list()), [None, None, 3]) + with self.test_session() as sess: + tensor_dict = sess.run(tensor_dict) + + self.assertAllEqual(decoded_jpeg, tensor_dict[fields.InputDataFields.image]) + self.assertEqual('image_id', tensor_dict[fields.InputDataFields.source_id]) + + def testDecodeImageKeyAndFilename(self): + image_tensor = np.random.randint(255, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg = self._EncodeImage(image_tensor) + example = tf.train.Example(features=tf.train.Features(feature={ + 'image/encoded': self._BytesFeature(encoded_jpeg), + 'image/key/sha256': self._BytesFeature('abc'), + 'image/filename': self._BytesFeature('filename') + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + + with self.test_session() as sess: + tensor_dict = sess.run(tensor_dict) + + self.assertEqual('abc', tensor_dict[fields.InputDataFields.key]) + self.assertEqual('filename', tensor_dict[fields.InputDataFields.filename]) + + def testDecodePngImage(self): + image_tensor = np.random.randint(255, size=(4, 5, 3)).astype(np.uint8) + encoded_png = self._EncodeImage(image_tensor, encoding_type='png') + decoded_png = self._DecodeImage(encoded_png, encoding_type='png') + example = tf.train.Example(features=tf.train.Features(feature={ + 'image/encoded': self._BytesFeature(encoded_png), + 'image/format': self._BytesFeature('png'), + 'image/source_id': self._BytesFeature('image_id') + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((tensor_dict[fields.InputDataFields.image]. + get_shape().as_list()), [None, None, 3]) + with self.test_session() as sess: + tensor_dict = sess.run(tensor_dict) + + self.assertAllEqual(decoded_png, tensor_dict[fields.InputDataFields.image]) + self.assertEqual('image_id', tensor_dict[fields.InputDataFields.source_id]) + + def testDecodeBoundingBox(self): + image_tensor = np.random.randint(255, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg = self._EncodeImage(image_tensor) + bbox_ymins = [0.0, 4.0] + bbox_xmins = [1.0, 5.0] + bbox_ymaxs = [2.0, 6.0] + bbox_xmaxs = [3.0, 7.0] + example = tf.train.Example(features=tf.train.Features(feature={ + 'image/encoded': self._BytesFeature(encoded_jpeg), + 'image/format': self._BytesFeature('jpeg'), + 'image/object/bbox/ymin': self._FloatFeature(bbox_ymins), + 'image/object/bbox/xmin': self._FloatFeature(bbox_xmins), + 'image/object/bbox/ymax': self._FloatFeature(bbox_ymaxs), + 'image/object/bbox/xmax': self._FloatFeature(bbox_xmaxs), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((tensor_dict[fields.InputDataFields.groundtruth_boxes]. + get_shape().as_list()), [None, 4]) + with self.test_session() as sess: + tensor_dict = sess.run(tensor_dict) + + expected_boxes = np.vstack([bbox_ymins, bbox_xmins, + bbox_ymaxs, bbox_xmaxs]).transpose() + self.assertAllEqual(expected_boxes, + tensor_dict[fields.InputDataFields.groundtruth_boxes]) + + def testDecodeObjectLabel(self): + image_tensor = np.random.randint(255, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg = self._EncodeImage(image_tensor) + bbox_classes = [0, 1] + example = tf.train.Example(features=tf.train.Features(feature={ + 'image/encoded': self._BytesFeature(encoded_jpeg), + 'image/format': self._BytesFeature('jpeg'), + 'image/object/class/label': self._Int64Feature(bbox_classes), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((tensor_dict[ + fields.InputDataFields.groundtruth_classes].get_shape().as_list()), + [None]) + + with self.test_session() as sess: + tensor_dict = sess.run(tensor_dict) + + self.assertAllEqual(bbox_classes, + tensor_dict[fields.InputDataFields.groundtruth_classes]) + + def testDecodeObjectArea(self): + image_tensor = np.random.randint(255, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg = self._EncodeImage(image_tensor) + object_area = [100., 174.] + example = tf.train.Example(features=tf.train.Features(feature={ + 'image/encoded': self._BytesFeature(encoded_jpeg), + 'image/format': self._BytesFeature('jpeg'), + 'image/object/area': self._FloatFeature(object_area), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((tensor_dict[fields.InputDataFields.groundtruth_area]. + get_shape().as_list()), [None]) + with self.test_session() as sess: + tensor_dict = sess.run(tensor_dict) + + self.assertAllEqual(object_area, + tensor_dict[fields.InputDataFields.groundtruth_area]) + + def testDecodeObjectIsCrowd(self): + image_tensor = np.random.randint(255, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg = self._EncodeImage(image_tensor) + object_is_crowd = [0, 1] + example = tf.train.Example(features=tf.train.Features(feature={ + 'image/encoded': self._BytesFeature(encoded_jpeg), + 'image/format': self._BytesFeature('jpeg'), + 'image/object/is_crowd': self._Int64Feature(object_is_crowd), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((tensor_dict[ + fields.InputDataFields.groundtruth_is_crowd].get_shape().as_list()), + [None]) + with self.test_session() as sess: + tensor_dict = sess.run(tensor_dict) + + 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(255, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg = self._EncodeImage(image_tensor) + object_difficult = [0, 1] + example = tf.train.Example(features=tf.train.Features(feature={ + 'image/encoded': self._BytesFeature(encoded_jpeg), + 'image/format': self._BytesFeature('jpeg'), + 'image/object/difficult': self._Int64Feature(object_difficult), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((tensor_dict[ + fields.InputDataFields.groundtruth_difficult].get_shape().as_list()), + [None]) + with self.test_session() as sess: + tensor_dict = sess.run(tensor_dict) + + self.assertAllEqual([bool(item) for item in object_difficult], + tensor_dict[ + fields.InputDataFields.groundtruth_difficult]) + + def testDecodeInstanceSegmentation(self): + num_instances = 4 + image_height = 5 + image_width = 3 + + # Randomly generate image. + image_tensor = np.random.randint(255, size=(image_height, + image_width, + 3)).astype(np.uint8) + encoded_jpeg = self._EncodeImage(image_tensor) + + # Randomly generate instance segmentation masks. + instance_segmentation = ( + np.random.randint(2, size=(num_instances, + image_height, + image_width)).astype(np.int64)) + + # Randomly generate class labels for each instance. + instance_segmentation_classes = np.random.randint( + 100, size=(num_instances)).astype(np.int64) + + example = tf.train.Example(features=tf.train.Features(feature={ + 'image/encoded': self._BytesFeature(encoded_jpeg), + 'image/format': self._BytesFeature('jpeg'), + 'image/height': self._Int64Feature([image_height]), + 'image/width': self._Int64Feature([image_width]), + 'image/segmentation/object': self._Int64Feature( + instance_segmentation.flatten()), + 'image/segmentation/object/class': self._Int64Feature( + instance_segmentation_classes)})).SerializeToString() + example_decoder = tf_example_decoder.TfExampleDecoder() + tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + + self.assertAllEqual(( + tensor_dict[fields.InputDataFields.groundtruth_instance_masks]. + get_shape().as_list()), [None, None, None]) + + self.assertAllEqual(( + tensor_dict[fields.InputDataFields.groundtruth_instance_classes]. + get_shape().as_list()), [None]) + + with self.test_session() as sess: + tensor_dict = sess.run(tensor_dict) + + self.assertAllEqual( + instance_segmentation.astype(np.bool), + tensor_dict[fields.InputDataFields.groundtruth_instance_masks]) + self.assertAllEqual( + instance_segmentation_classes, + tensor_dict[fields.InputDataFields.groundtruth_instance_classes]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/eval.py b/object_detection/eval.py new file mode 100644 index 000000000..cf3ab0c56 --- /dev/null +++ b/object_detection/eval.py @@ -0,0 +1,161 @@ +# 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"""Evaluation executable for detection models. + +This executable is used to evaluate DetectionModels. There are two ways of +configuring the eval job. + +1) A single pipeline_pb2.TrainEvalPipelineConfig file maybe specified instead. +In this mode, the --eval_training_data flag may be given to force the pipeline +to evaluate on training data instead. + +Example usage: + ./eval \ + --logtostderr \ + --checkpoint_dir=path/to/checkpoint_dir \ + --eval_dir=path/to/eval_dir \ + --pipeline_config_path=pipeline_config.pbtxt + +2) Three configuration files may be provided: a model_pb2.DetectionModel +configuration file to define what type of DetectionModel is being evaulated, an +input_reader_pb2.InputReader file to specify what data the model is evaluating +and an eval_pb2.EvalConfig file to configure evaluation parameters. + +Example usage: + ./eval \ + --logtostderr \ + --checkpoint_dir=path/to/checkpoint_dir \ + --eval_dir=path/to/eval_dir \ + --eval_config_path=eval_config.pbtxt \ + --model_config_path=model_config.pbtxt \ + --input_config_path=eval_input_config.pbtxt +""" +import functools +import tensorflow as tf + +from google.protobuf import text_format +from object_detection import evaluator +from object_detection.builders import input_reader_builder +from object_detection.builders import model_builder +from object_detection.protos import eval_pb2 +from object_detection.protos import input_reader_pb2 +from object_detection.protos import model_pb2 +from object_detection.protos import pipeline_pb2 +from object_detection.utils import label_map_util + +tf.logging.set_verbosity(tf.logging.INFO) + +flags = tf.app.flags +flags.DEFINE_boolean('eval_training_data', False, + 'If training data should be evaluated for this job.') +flags.DEFINE_string('checkpoint_dir', '', + 'Directory containing checkpoints to evaluate, typically ' + 'set to `train_dir` used in the training job.') +flags.DEFINE_string('eval_dir', '', + 'Directory to write eval summaries to.') +flags.DEFINE_string('pipeline_config_path', '', + 'Path to a pipeline_pb2.TrainEvalPipelineConfig config ' + 'file. If provided, other configs are ignored') +flags.DEFINE_string('eval_config_path', '', + 'Path to an eval_pb2.EvalConfig config file.') +flags.DEFINE_string('input_config_path', '', + 'Path to an input_reader_pb2.InputReader config file.') +flags.DEFINE_string('model_config_path', '', + 'Path to a model_pb2.DetectionModel config file.') + +FLAGS = flags.FLAGS + + +def get_configs_from_pipeline_file(): + """Reads evaluation configuration from a pipeline_pb2.TrainEvalPipelineConfig. + + Reads evaluation config from file specified by pipeline_config_path flag. + + Returns: + model_config: a model_pb2.DetectionModel + eval_config: a eval_pb2.EvalConfig + input_config: a input_reader_pb2.InputReader + """ + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + with tf.gfile.GFile(FLAGS.pipeline_config_path, 'r') as f: + text_format.Merge(f.read(), pipeline_config) + + model_config = pipeline_config.model + if FLAGS.eval_training_data: + eval_config = pipeline_config.train_config + else: + eval_config = pipeline_config.eval_config + input_config = pipeline_config.eval_input_reader + + return model_config, eval_config, input_config + + +def get_configs_from_multiple_files(): + """Reads evaluation configuration from multiple config files. + + Reads the evaluation config from the following files: + model_config: Read from --model_config_path + eval_config: Read from --eval_config_path + input_config: Read from --input_config_path + + Returns: + model_config: a model_pb2.DetectionModel + eval_config: a eval_pb2.EvalConfig + input_config: a input_reader_pb2.InputReader + """ + eval_config = eval_pb2.EvalConfig() + with tf.gfile.GFile(FLAGS.eval_config_path, 'r') as f: + text_format.Merge(f.read(), eval_config) + + model_config = model_pb2.DetectionModel() + with tf.gfile.GFile(FLAGS.model_config_path, 'r') as f: + text_format.Merge(f.read(), model_config) + + input_config = input_reader_pb2.InputReader() + with tf.gfile.GFile(FLAGS.input_config_path, 'r') as f: + text_format.Merge(f.read(), input_config) + + return model_config, eval_config, input_config + + +def main(unused_argv): + assert FLAGS.checkpoint_dir, '`checkpoint_dir` is missing.' + assert FLAGS.eval_dir, '`eval_dir` is missing.' + if FLAGS.pipeline_config_path: + model_config, eval_config, input_config = get_configs_from_pipeline_file() + else: + model_config, eval_config, input_config = get_configs_from_multiple_files() + + model_fn = functools.partial( + model_builder.build, + model_config=model_config, + is_training=False) + + create_input_dict_fn = functools.partial( + input_reader_builder.build, + input_config) + + label_map = label_map_util.load_labelmap(input_config.label_map_path) + max_num_classes = max([item.id for item in label_map.item]) + categories = label_map_util.convert_label_map_to_categories( + label_map, max_num_classes) + + evaluator.evaluate(create_input_dict_fn, model_fn, eval_config, categories, + FLAGS.checkpoint_dir, FLAGS.eval_dir) + + +if __name__ == '__main__': + tf.app.run() diff --git a/object_detection/eval_util.py b/object_detection/eval_util.py new file mode 100644 index 000000000..51e6878ea --- /dev/null +++ b/object_detection/eval_util.py @@ -0,0 +1,524 @@ +# 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 functions for repeatedly evaluating a checkpoint. +""" +import copy +import logging +import os +import time + +import numpy as np +import tensorflow as tf + +from object_detection.utils import label_map_util +from object_detection.utils import object_detection_evaluation +from object_detection.utils import visualization_utils as vis_utils + +slim = tf.contrib.slim + + +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. + """ + logging.info('Writing metrics to tf summary.') + summary_writer = tf.summary.FileWriter(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) + logging.info('%s: %f', key, metrics[key]) + summary_writer.close() + logging.info('Metrics written to tf summary.') + + +def evaluate_detection_results_pascal_voc(result_lists, + categories, + label_id_offset=0, + iou_thres=0.5, + corloc_summary=False): + """Computes Pascal VOC detection metrics given groundtruth and detections. + + This function computes Pascal VOC metrics. This function by default + takes detections and groundtruth boxes encoded in result_lists and writes + evaluation results to tf summaries which can be viewed on tensorboard. + + Args: + result_lists: a dictionary holding lists of groundtruth and detection + data corresponding to each image being evaluated. The following keys + are required: + 'image_id': a list of string ids + 'detection_boxes': a list of float32 numpy arrays of shape [N, 4] + 'detection_scores': a list of float32 numpy arrays of shape [N] + 'detection_classes': a list of int32 numpy arrays of shape [N] + 'groundtruth_boxes': a list of float32 numpy arrays of shape [M, 4] + 'groundtruth_classes': a list of int32 numpy arrays of shape [M] + and the remaining fields below are optional: + 'difficult': a list of boolean arrays of shape [M] indicating the + difficulty of groundtruth boxes. Some datasets like PASCAL VOC provide + this information and it is used to remove difficult examples from eval + in order to not penalize the models on them. + Note that it is okay to have additional fields in result_lists --- they + are simply ignored. + 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' + label_id_offset: an integer offset for the label space. + iou_thres: float determining the IoU threshold at which a box is considered + correct. Defaults to the standard 0.5. + corloc_summary: boolean. If True, also outputs CorLoc metrics. + + Returns: + A dictionary of metric names to scalar values. + + Raises: + ValueError: if the set of keys in result_lists is not a superset of the + expected list of keys. Unexpected keys are ignored. + ValueError: if the lists in result_lists have inconsistent sizes. + """ + # check for expected keys in result_lists + expected_keys = [ + 'detection_boxes', 'detection_scores', 'detection_classes', 'image_id' + ] + expected_keys += ['groundtruth_boxes', 'groundtruth_classes'] + if not set(expected_keys).issubset(set(result_lists.keys())): + raise ValueError('result_lists does not have expected key set.') + num_results = len(result_lists[expected_keys[0]]) + for key in expected_keys: + if len(result_lists[key]) != num_results: + raise ValueError('Inconsistent list sizes in result_lists') + + # Pascal VOC evaluator assumes foreground index starts from zero. + categories = copy.deepcopy(categories) + for idx in range(len(categories)): + categories[idx]['id'] -= label_id_offset + + # num_classes (maybe encoded as categories) + num_classes = max([cat['id'] for cat in categories]) + 1 + logging.info('Computing Pascal VOC metrics on results.') + if all(image_id.isdigit() for image_id in result_lists['image_id']): + image_ids = [int(image_id) for image_id in result_lists['image_id']] + else: + image_ids = range(num_results) + + evaluator = object_detection_evaluation.ObjectDetectionEvaluation( + num_classes, matching_iou_threshold=iou_thres) + + difficult_lists = None + if 'difficult' in result_lists and result_lists['difficult']: + difficult_lists = result_lists['difficult'] + for idx, image_id in enumerate(image_ids): + difficult = None + if difficult_lists is not None and difficult_lists[idx].size: + difficult = difficult_lists[idx].astype(np.bool) + evaluator.add_single_ground_truth_image_info( + image_id, result_lists['groundtruth_boxes'][idx], + result_lists['groundtruth_classes'][idx] - label_id_offset, + difficult) + evaluator.add_single_detected_image_info( + image_id, result_lists['detection_boxes'][idx], + result_lists['detection_scores'][idx], + result_lists['detection_classes'][idx] - label_id_offset) + per_class_ap, mean_ap, _, _, per_class_corloc, mean_corloc = ( + evaluator.evaluate()) + + metrics = {'Precision/mAP@{}IOU'.format(iou_thres): mean_ap} + category_index = label_map_util.create_category_index(categories) + for idx in range(per_class_ap.size): + if idx in category_index: + display_name = ('PerformanceByCategory/mAP@{}IOU/{}' + .format(iou_thres, category_index[idx]['name'])) + metrics[display_name] = per_class_ap[idx] + + if corloc_summary: + metrics['CorLoc/CorLoc@{}IOU'.format(iou_thres)] = mean_corloc + for idx in range(per_class_corloc.size): + if idx in category_index: + display_name = ( + 'PerformanceByCategory/CorLoc@{}IOU/{}'.format( + iou_thres, category_index[idx]['name'])) + metrics[display_name] = per_class_corloc[idx] + return metrics + + +# TODO: Add tests. +def visualize_detection_results(result_dict, + tag, + global_step, + categories, + summary_dir='', + export_dir='', + agnostic_mode=False, + show_groundtruth=False, + min_score_thresh=.5, + max_num_predictions=20): + """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] + '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 + min_score_thresh: minimum score threshold for a box to be visualized + max_num_predictions: maximum number of detections to visualize + Raises: + ValueError: if result_dict does not contain the expected keys (i.e., + 'original_image', 'detection_boxes', 'detection_scores', + 'detection_classes') + """ + if not set([ + 'original_image', 'detection_boxes', 'detection_scores', + 'detection_classes' + ]).issubset(set(result_dict.keys())): + raise ValueError('result_dict does not contain all expected keys.') + if show_groundtruth and 'groundtruth_boxes' not in result_dict: + raise ValueError('If show_groundtruth is enabled, result_dict must contain ' + 'groundtruth_boxes.') + logging.info('Creating detection visualizations.') + category_index = label_map_util.create_category_index(categories) + + image = np.squeeze(result_dict['original_image'], axis=0) + detection_boxes = result_dict['detection_boxes'] + detection_scores = result_dict['detection_scores'] + detection_classes = np.int32((result_dict['detection_classes'])) + detection_keypoints = result_dict.get('detection_keypoints', None) + detection_masks = result_dict.get('detection_masks', None) + + # Plot groundtruth underneath detections + if show_groundtruth: + groundtruth_boxes = result_dict['groundtruth_boxes'] + groundtruth_keypoints = result_dict.get('groundtruth_keypoints', None) + vis_utils.visualize_boxes_and_labels_on_image_array( + image, + groundtruth_boxes, + None, + None, + category_index, + keypoints=groundtruth_keypoints, + use_normalized_coordinates=False, + max_boxes_to_draw=None) + vis_utils.visualize_boxes_and_labels_on_image_array( + image, + detection_boxes, + detection_classes, + detection_scores, + category_index, + instance_masks=detection_masks, + keypoints=detection_keypoints, + use_normalized_coordinates=False, + max_boxes_to_draw=max_num_predictions, + min_score_thresh=min_score_thresh, + agnostic_mode=agnostic_mode) + + if export_dir: + 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.FileWriter(summary_dir) + summary_writer.add_summary(summary, global_step) + summary_writer.close() + + logging.info('Detection visualizations written to summary with tag %s.', tag) + + +# TODO: Add tests. +# TODO: Have an argument called `aggregated_processor_tensor_keys` that contains +# a whitelist of tensors used by the `aggregated_result_processor` instead of a +# blacklist. This will prevent us from inadvertently adding any evaluated +# tensors into the `results_list` data structure that are not needed by +# `aggregated_result_preprocessor`. +def run_checkpoint_once(tensor_dict, + update_op, + summary_dir, + aggregated_result_processor=None, + batch_processor=None, + checkpoint_dirs=None, + variables_to_restore=None, + restore_fn=None, + num_batches=1, + master='', + save_graph=False, + save_graph_dir='', + metric_names_to_values=None, + keys_to_exclude_from_results=()): + """Evaluates both python metrics and tensorflow slim metrics. + + Python metrics are processed in batch by the aggregated_result_processor, + while tensorflow slim metrics statistics are computed by running + metric_names_to_updates tensors and aggregated using metric_names_to_values + tensor. + + Args: + tensor_dict: a dictionary holding tensors representing a batch of detections + and corresponding groundtruth annotations. + update_op: a tensorflow update op that will run for each batch along with + the tensors in tensor_dict.. + summary_dir: a directory to write metrics summaries. + aggregated_result_processor: a function taking one arguments: + 1. result_lists: a dictionary with keys matching those in tensor_dict + and corresponding values being the list of results for each tensor + in tensor_dict. The length of each such list is num_batches. + 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 + 4. update_op: a tensorflow update op that will run for each batch. + and returns result_dict, a dictionary of results for that batch. + 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. + metric_names_to_values: A dictionary containing metric names to tensors + which will be evaluated after processing all batches + of [tensor_dict, update_op]. If any metrics depend on statistics computed + during each batch ensure that `update_op` tensor has a control dependency + on the update ops that compute the statistics. + keys_to_exclude_from_results: keys in tensor_dict that will be excluded + from results_list. Note that the tensors corresponding to these keys will + still be evaluated for each batch, but won't be added to results_list. + + 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()) + 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') + + valid_keys = list(set(tensor_dict.keys()) - set(keys_to_exclude_from_results)) + result_lists = {key: [] for key in valid_keys} + counters = {'skipped': 0, 'success': 0} + other_metrics = None + with tf.contrib.slim.queues.QueueRunners(sess): + try: + for batch in range(int(num_batches)): + if (batch + 1) % 100 == 0: + logging.info('Running eval ops batch %d/%d', batch + 1, num_batches) + if not batch_processor: + try: + (result_dict, _) = sess.run([tensor_dict, update_op]) + counters['success'] += 1 + except tf.errors.InvalidArgumentError: + logging.info('Skipping image') + counters['skipped'] += 1 + result_dict = {} + else: + result_dict = batch_processor( + tensor_dict, sess, batch, counters, update_op) + for key in result_dict: + if key in valid_keys: + result_lists[key].append(result_dict[key]) + if metric_names_to_values is not None: + other_metrics = sess.run(metric_names_to_values) + logging.info('Running eval batches done.') + except tf.errors.OutOfRangeError: + logging.info('Done evaluating -- epoch limit reached') + finally: + # When done, ask the threads to stop. + metrics = aggregated_result_processor(result_lists) + if other_metrics is not None: + metrics.update(other_metrics) + global_step = tf.train.global_step(sess, slim.get_global_step()) + write_metrics(metrics, global_step, summary_dir) + logging.info('# success: %d', counters['success']) + logging.info('# skipped: %d', counters['skipped']) + sess.close() + + +# TODO: Add tests. +def repeated_checkpoint_run(tensor_dict, + update_op, + summary_dir, + aggregated_result_processor=None, + 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, + master='', + save_graph=False, + save_graph_dir='', + metric_names_to_values=None, + keys_to_exclude_from_results=()): + """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. + update_op: a tensorflow update op that will run for each batch along with + the tensors in tensor_dict. + summary_dir: a directory to write metrics summaries. + aggregated_result_processor: a function taking one argument: + 1. result_lists: a dictionary with keys matching those in tensor_dict + and corresponding values being the list of results for each tensor + in tensor_dict. The length of each such list is num_batches. + 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 + 4. update_op: a tensorflow update op that will run for each batch. + and returns result_dict, a dictionary of results for that batch. + 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. + 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. + metric_names_to_values: A dictionary containing metric names to tensors + which will be evaluated after processing all batches + of [tensor_dict, update_op]. If any metrics depend on statistics computed + during each batch ensure that `update_op` tensor has a control dependency + on the update ops that compute the statistics. + keys_to_exclude_from_results: keys in tensor_dict that will be excluded + from results_list. Note that the tensors corresponding to these keys will + still be evaluated for each batch, but won't be added to results_list. + + 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( + '`number_of_steps` must be either None or a positive number.') + + 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() + 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: + 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: + logging.info('Found already evaluated checkpoint. Will try again in %d ' + 'seconds', eval_interval_secs) + else: + last_evaluated_model_path = model_path + run_checkpoint_once(tensor_dict, update_op, summary_dir, + aggregated_result_processor, + batch_processor, checkpoint_dirs, + variables_to_restore, restore_fn, num_batches, master, + save_graph, save_graph_dir, metric_names_to_values, + keys_to_exclude_from_results) + number_of_evaluations += 1 + + if (max_number_of_evaluations and + number_of_evaluations >= max_number_of_evaluations): + 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) diff --git a/object_detection/evaluator.py b/object_detection/evaluator.py new file mode 100644 index 000000000..28ac1183d --- /dev/null +++ b/object_detection/evaluator.py @@ -0,0 +1,211 @@ +# 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. +# ============================================================================== + +"""Detection model evaluator. + +This file provides a generic evaluation method that can be used to evaluate a +DetectionModel. +""" +import logging +import tensorflow as tf + +from object_detection import eval_util +from object_detection.core import box_list +from object_detection.core import box_list_ops +from object_detection.core import prefetcher +from object_detection.core import standard_fields as fields +from object_detection.utils import ops + +slim = tf.contrib.slim + +EVAL_METRICS_FN_DICT = { + 'pascal_voc_metrics': eval_util.evaluate_detection_results_pascal_voc +} + + +def _extract_prediction_tensors(model, + create_input_dict_fn, + ignore_groundtruth=False): + """Restores the model in a tensorflow session. + + Args: + model: model to perform predictions with. + create_input_dict_fn: function to create input tensor dictionaries. + ignore_groundtruth: whether groundtruth should be ignored. + + Returns: + tensor_dict: A tensor dictionary with evaluations. + """ + input_dict = create_input_dict_fn() + prefetch_queue = prefetcher.prefetch(input_dict, capacity=500) + input_dict = prefetch_queue.dequeue() + original_image = tf.expand_dims(input_dict[fields.InputDataFields.image], 0) + preprocessed_image = model.preprocess(tf.to_float(original_image)) + prediction_dict = model.predict(preprocessed_image) + detections = model.postprocess(prediction_dict) + + original_image_shape = tf.shape(original_image) + absolute_detection_boxlist = box_list_ops.to_absolute_coordinates( + box_list.BoxList(tf.squeeze(detections['detection_boxes'], axis=0)), + original_image_shape[1], original_image_shape[2]) + label_id_offset = 1 + tensor_dict = { + 'original_image': original_image, + 'image_id': input_dict[fields.InputDataFields.source_id], + 'detection_boxes': absolute_detection_boxlist.get(), + 'detection_scores': tf.squeeze(detections['detection_scores'], axis=0), + 'detection_classes': ( + tf.squeeze(detections['detection_classes'], axis=0) + + label_id_offset), + } + if 'detection_masks' in detections: + detection_masks = tf.squeeze(detections['detection_masks'], + axis=0) + detection_boxes = tf.squeeze(detections['detection_boxes'], + axis=0) + # TODO: This should be done in model's postprocess function ideally. + detection_masks_reframed = ops.reframe_box_masks_to_image_masks( + detection_masks, + detection_boxes, + original_image_shape[1], original_image_shape[2]) + detection_masks_reframed = tf.to_float(tf.greater(detection_masks_reframed, + 0.5)) + + tensor_dict['detection_masks'] = detection_masks_reframed + # load groundtruth fields into tensor_dict + if not ignore_groundtruth: + normalized_gt_boxlist = box_list.BoxList( + input_dict[fields.InputDataFields.groundtruth_boxes]) + gt_boxlist = box_list_ops.scale(normalized_gt_boxlist, + tf.shape(original_image)[1], + tf.shape(original_image)[2]) + groundtruth_boxes = gt_boxlist.get() + groundtruth_classes = input_dict[fields.InputDataFields.groundtruth_classes] + tensor_dict['groundtruth_boxes'] = groundtruth_boxes + tensor_dict['groundtruth_classes'] = groundtruth_classes + tensor_dict['area'] = input_dict[fields.InputDataFields.groundtruth_area] + tensor_dict['is_crowd'] = input_dict[ + fields.InputDataFields.groundtruth_is_crowd] + tensor_dict['difficult'] = input_dict[ + fields.InputDataFields.groundtruth_difficult] + if 'detection_masks' in tensor_dict: + tensor_dict['groundtruth_instance_masks'] = input_dict[ + fields.InputDataFields.groundtruth_instance_masks] + return tensor_dict + + +def evaluate(create_input_dict_fn, create_model_fn, eval_config, categories, + checkpoint_dir, eval_dir): + """Evaluation function for detection models. + + Args: + create_input_dict_fn: a function to create a tensor input dictionary. + create_model_fn: a function that creates a DetectionModel. + eval_config: a eval_pb2.EvalConfig protobuf. + categories: a list of category dictionaries. Each dict in the list should + have an integer 'id' field and string 'name' field. + checkpoint_dir: directory to load the checkpoints to evaluate from. + eval_dir: directory to write evaluation metrics summary to. + """ + + model = create_model_fn() + + if eval_config.ignore_groundtruth and not eval_config.export_path: + logging.fatal('If ignore_groundtruth=True then an export_path is ' + 'required. Aborting!!!') + + tensor_dict = _extract_prediction_tensors( + model=model, + create_input_dict_fn=create_input_dict_fn, + ignore_groundtruth=eval_config.ignore_groundtruth) + + def _process_batch(tensor_dict, sess, batch_index, counters, update_op): + """Evaluates tensors in tensor_dict, visualizing the first K examples. + + This function calls sess.run on tensor_dict, evaluating the original_image + tensor only on the first K examples and visualizing detections overlaid + on this original_image. + + Args: + tensor_dict: a dictionary of tensors + sess: tensorflow session + batch_index: the index of the batch amongst all batches in the run. + counters: a dictionary holding 'success' and 'skipped' fields which can + be updated to keep track of number of successful and failed runs, + respectively. If these fields are not updated, then the success/skipped + counter values shown at the end of evaluation will be incorrect. + update_op: An update op that has to be run along with output tensors. For + example this could be an op to compute statistics for slim metrics. + + Returns: + result_dict: a dictionary of numpy arrays + """ + if batch_index >= eval_config.num_visualizations: + if 'original_image' in tensor_dict: + tensor_dict = {k: v for (k, v) in tensor_dict.iteritems() + if k != 'original_image'} + try: + (result_dict, _) = sess.run([tensor_dict, update_op]) + counters['success'] += 1 + except tf.errors.InvalidArgumentError: + logging.info('Skipping image') + counters['skipped'] += 1 + return {} + global_step = tf.train.global_step(sess, slim.get_global_step()) + if batch_index < eval_config.num_visualizations: + tag = 'image-{}'.format(batch_index) + eval_util.visualize_detection_results( + result_dict, tag, global_step, categories=categories, + summary_dir=eval_dir, + export_dir=eval_config.visualization_export_dir, + show_groundtruth=eval_config.visualization_export_dir) + return result_dict + + def _process_aggregated_results(result_lists): + eval_metric_fn_key = eval_config.metrics_set + if eval_metric_fn_key not in EVAL_METRICS_FN_DICT: + raise ValueError('Metric not found: {}'.format(eval_metric_fn_key)) + return EVAL_METRICS_FN_DICT[eval_metric_fn_key](result_lists, + categories=categories) + + variables_to_restore = tf.global_variables() + global_step = slim.get_or_create_global_step() + variables_to_restore.append(global_step) + if eval_config.use_moving_averages: + variable_averages = tf.train.ExponentialMovingAverage(0.0) + variables_to_restore = variable_averages.variables_to_restore() + saver = tf.train.Saver(variables_to_restore) + def _restore_latest_checkpoint(sess): + latest_checkpoint = tf.train.latest_checkpoint(checkpoint_dir) + saver.restore(sess, latest_checkpoint) + + eval_util.repeated_checkpoint_run( + tensor_dict=tensor_dict, + update_op=tf.no_op(), + summary_dir=eval_dir, + aggregated_result_processor=_process_aggregated_results, + batch_processor=_process_batch, + checkpoint_dirs=[checkpoint_dir], + variables_to_restore=None, + restore_fn=_restore_latest_checkpoint, + num_batches=eval_config.num_examples, + eval_interval_secs=eval_config.eval_interval_secs, + max_number_of_evaluations=( + 1 if eval_config.ignore_groundtruth else + eval_config.max_evals if eval_config.max_evals else + None), + master=eval_config.eval_master, + save_graph=eval_config.save_graph, + save_graph_dir=(eval_dir if eval_config.save_graph else '')) diff --git a/object_detection/export_inference_graph.py b/object_detection/export_inference_graph.py new file mode 100644 index 000000000..c6e8a827c --- /dev/null +++ b/object_detection/export_inference_graph.py @@ -0,0 +1,90 @@ +# 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 an optional trained checkpoint. + +The inference graph contains one of two input nodes depending on the user +specified option. + * `image_tensor`: Accepts a uint8 4-D tensor of shape [1, None, None, 3] + * `tf_example`: Accepts a serialized TFExample proto. The batch size in this + case is always 1. + +and the following output nodes: + * `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. + +Note that currently `batch` is always 1, but we will support `batch` > 1 in +the future. + +Optionally, one can freeze the graph by converting the weights in the provided +checkpoint as graph constants thereby eliminating the need to use a checkpoint +file during inference. + +Note that this tool uses `use_moving_averages` from eval_config to decide +which weights to freeze. + +Example Usage: +-------------- +python export_inference_graph \ + --input_type image_tensor \ + --pipeline_config_path path/to/ssd_inception_v2.config \ + --checkpoint_path path/to/model-ckpt \ + --inference_graph_path path/to/inference_graph.pb +""" +import tensorflow as tf +from google.protobuf import text_format +from object_detection import exporter +from object_detection.protos import pipeline_pb2 + +slim = tf.contrib.slim +flags = tf.app.flags + +flags.DEFINE_string('input_type', 'image_tensor', 'Type of input node. Can be ' + 'one of [`image_tensor` `tf_example_proto`]') +flags.DEFINE_string('pipeline_config_path', '', + 'Path to a pipeline_pb2.TrainEvalPipelineConfig config ' + 'file.') +flags.DEFINE_string('checkpoint_path', '', 'Optional path to checkpoint file. ' + 'If provided, bakes the weights from the checkpoint into ' + 'the graph.') +flags.DEFINE_string('inference_graph_path', '', 'Path to write the output ' + 'inference graph.') + +FLAGS = flags.FLAGS + + +def main(_): + assert FLAGS.pipeline_config_path, 'TrainEvalPipelineConfig missing.' + assert FLAGS.inference_graph_path, 'Inference graph path missing.' + assert FLAGS.input_type, 'Input type missing.' + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + with tf.gfile.GFile(FLAGS.pipeline_config_path, 'r') as f: + text_format.Merge(f.read(), pipeline_config) + exporter.export_inference_graph(FLAGS.input_type, pipeline_config, + FLAGS.checkpoint_path, + FLAGS.inference_graph_path) + + +if __name__ == '__main__': + tf.app.run() diff --git a/object_detection/exporter.py b/object_detection/exporter.py new file mode 100644 index 000000000..a57913f7c --- /dev/null +++ b/object_detection/exporter.py @@ -0,0 +1,230 @@ +# 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 logging +import os +import tensorflow as tf +from tensorflow.python import pywrap_tensorflow +from tensorflow.python.client import session +from tensorflow.python.framework import graph_util +from tensorflow.python.framework import importer +from tensorflow.python.platform import gfile +from tensorflow.python.training import saver as saver_lib +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 + +slim = tf.contrib.slim + + +# TODO: Replace with freeze_graph.freeze_graph_with_def_protos when newer +# version of Tensorflow becomes more common. +def freeze_graph_with_def_protos( + input_graph_def, + input_saver_def, + input_checkpoint, + output_node_names, + restore_op_name, + filename_tensor_name, + output_graph, + clear_devices, + initializer_nodes, + variable_names_blacklist=''): + """Converts all variables in a graph and checkpoint into constants.""" + del restore_op_name, filename_tensor_name # Unused by updated loading code. + + # 'input_checkpoint' may be a prefix if we're using Saver V2 format + if not saver_lib.checkpoint_exists(input_checkpoint): + logging.info('Input checkpoint "' + input_checkpoint + '" does not exist!') + return -1 + + if not output_node_names: + logging.info('You must supply the name of a node to --output_node_names.') + return -1 + + # Remove all the explicit device specifications for this node. This helps to + # make the graph more portable. + if clear_devices: + for node in input_graph_def.node: + node.device = '' + + _ = importer.import_graph_def(input_graph_def, name='') + + with session.Session() as sess: + if input_saver_def: + saver = saver_lib.Saver(saver_def=input_saver_def) + saver.restore(sess, input_checkpoint) + else: + var_list = {} + reader = pywrap_tensorflow.NewCheckpointReader(input_checkpoint) + var_to_shape_map = reader.get_variable_to_shape_map() + for key in var_to_shape_map: + try: + tensor = sess.graph.get_tensor_by_name(key + ':0') + except KeyError: + # This tensor doesn't exist in the graph (for example it's + # 'global_step' or a similar housekeeping element) so skip it. + continue + var_list[key] = tensor + saver = saver_lib.Saver(var_list=var_list) + saver.restore(sess, input_checkpoint) + if initializer_nodes: + sess.run(initializer_nodes) + + variable_names_blacklist = (variable_names_blacklist.split(',') if + variable_names_blacklist else None) + output_graph_def = graph_util.convert_variables_to_constants( + sess, + input_graph_def, + output_node_names.split(','), + variable_names_blacklist=variable_names_blacklist) + + with gfile.GFile(output_graph, 'wb') as f: + f.write(output_graph_def.SerializeToString()) + logging.info('%d ops in the final graph.', len(output_graph_def.node)) + + +# TODO: Support batch tf example inputs. +def _tf_example_input_placeholder(): + tf_example_placeholder = tf.placeholder( + tf.string, shape=[], name='tf_example') + tensor_dict = tf_example_decoder.TfExampleDecoder().Decode( + tf_example_placeholder) + image = tensor_dict[fields.InputDataFields.image] + return tf.expand_dims(image, axis=0) + + +def _image_tensor_input_placeholder(): + return tf.placeholder(dtype=tf.uint8, + shape=(1, None, None, 3), + name='image_tensor') + +input_placeholder_fn_map = { + 'tf_example': _tf_example_input_placeholder, + 'image_tensor': _image_tensor_input_placeholder +} + + +def _add_output_tensor_nodes(postprocessed_tensors): + """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_classes: float32 tensor of shape [batch_size, num_boxes] + containing class predictions for the detected boxes. + + Args: + postprocessed_tensors: a dictionary containing the following fields + 'detection_boxes': [batch, max_detections, 4] + 'detection_scores': [batch, max_detections] + 'detection_classes': [batch, max_detections] + 'num_detections': [batch] + """ + label_id_offset = 1 + boxes = postprocessed_tensors.get('detection_boxes') + scores = postprocessed_tensors.get('detection_scores') + classes = postprocessed_tensors.get('detection_classes') + label_id_offset + num_detections = postprocessed_tensors.get('num_detections') + tf.identity(boxes, name='detection_boxes') + tf.identity(scores, name='detection_scores') + tf.identity(classes, name='detection_classes') + tf.identity(num_detections, name='num_detections') + + +def _write_inference_graph(inference_graph_path, + checkpoint_path=None, + use_moving_averages=False, + output_node_names=( + 'num_detections,detection_scores,' + 'detection_boxes,detection_classes')): + """Writes inference graph to disk with the option to bake in weights. + + 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: + inference_graph_path: Path to write inference graph. + checkpoint_path: Optional path to the checkpoint file. + use_moving_averages: Whether to export the original or the moving averages + of the trainable variables from the checkpoint. + output_node_names: Output tensor names, defaults are: num_detections, + detection_scores, detection_boxes, detection_classes. + """ + inference_graph_def = tf.get_default_graph().as_graph_def() + if checkpoint_path: + saver = None + if use_moving_averages: + variable_averages = tf.train.ExponentialMovingAverage(0.0) + variables_to_restore = variable_averages.variables_to_restore() + saver = tf.train.Saver(variables_to_restore) + else: + saver = tf.train.Saver() + freeze_graph_with_def_protos( + input_graph_def=inference_graph_def, + input_saver_def=saver.as_saver_def(), + input_checkpoint=checkpoint_path, + 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='') + return + tf.train.write_graph(inference_graph_def, + os.path.dirname(inference_graph_path), + os.path.basename(inference_graph_path), + as_text=False) + + +def _export_inference_graph(input_type, + detection_model, + use_moving_averages, + checkpoint_path, + inference_graph_path): + if input_type not in input_placeholder_fn_map: + raise ValueError('Unknown input type: {}'.format(input_type)) + inputs = tf.to_float(input_placeholder_fn_map[input_type]()) + preprocessed_inputs = detection_model.preprocess(inputs) + output_tensors = detection_model.predict(preprocessed_inputs) + postprocessed_tensors = detection_model.postprocess(output_tensors) + _add_output_tensor_nodes(postprocessed_tensors) + _write_inference_graph(inference_graph_path, checkpoint_path, + use_moving_averages) + + +def export_inference_graph(input_type, pipeline_config, checkpoint_path, + inference_graph_path): + """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`, + `tf_example`]. + pipeline_config: pipeline_pb2.TrainAndEvalPipelineConfig proto. + checkpoint_path: Path to the checkpoint file to freeze. + inference_graph_path: Path to write inference graph to. + """ + detection_model = model_builder.build(pipeline_config.model, + is_training=False) + _export_inference_graph(input_type, detection_model, + pipeline_config.eval_config.use_moving_averages, + checkpoint_path, inference_graph_path) diff --git a/object_detection/exporter_test.py b/object_detection/exporter_test.py new file mode 100644 index 000000000..5b16fc8e9 --- /dev/null +++ b/object_detection/exporter_test.py @@ -0,0 +1,225 @@ +# 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.""" +import os +import mock +import numpy as np +import tensorflow as tf +from object_detection import exporter +from object_detection.builders import model_builder +from object_detection.core import model +from object_detection.protos import pipeline_pb2 + + +class FakeModel(model.DetectionModel): + + def preprocess(self, inputs): + return (tf.identity(inputs) * + tf.get_variable('dummy', shape=(), + initializer=tf.constant_initializer(2), + dtype=tf.float32)) + + def predict(self, preprocessed_inputs): + return {'image': tf.identity(preprocessed_inputs)} + + def postprocess(self, prediction_dict): + with tf.control_dependencies(prediction_dict.values()): + return { + 'detection_boxes': tf.constant([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]], tf.float32), + 'detection_scores': tf.constant([[0.7, 0.6]], tf.float32), + 'detection_classes': tf.constant([[0, 1]], tf.float32), + 'num_detections': tf.constant([2], tf.float32) + } + + def restore_fn(self, checkpoint_path, from_detection_checkpoint): + pass + + def loss(self, prediction_dict): + pass + + +class ExportInferenceGraphTest(tf.test.TestCase): + + def _save_checkpoint_from_mock_model(self, checkpoint_path, + use_moving_averages): + g = tf.Graph() + with g.as_default(): + mock_model = FakeModel(num_classes=1) + mock_model.preprocess(tf.constant([1, 3, 4, 3], tf.float32)) + if use_moving_averages: + tf.train.ExponentialMovingAverage(0.0).apply() + 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): + od_graph = tf.Graph() + with od_graph.as_default(): + od_graph_def = tf.GraphDef() + with tf.gfile.GFile(inference_graph_path) as fid: + serialized_graph = fid.read() + od_graph_def.ParseFromString(serialized_graph) + 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=[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): + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel(num_classes=1) + inference_graph_path = os.path.join(self.get_temp_dir(), + 'exported_graph.pbtxt') + + 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, + checkpoint_path=None, + inference_graph_path=inference_graph_path) + + def test_export_graph_with_tf_example_input(self): + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel(num_classes=1) + inference_graph_path = os.path.join(self.get_temp_dir(), + 'exported_graph.pbtxt') + 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, + checkpoint_path=None, + inference_graph_path=inference_graph_path) + + def test_export_frozen_graph(self): + checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') + self._save_checkpoint_from_mock_model(checkpoint_path, + use_moving_averages=False) + inference_graph_path = os.path.join(self.get_temp_dir(), + 'exported_graph.pb') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel(num_classes=1) + 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, + checkpoint_path=checkpoint_path, + inference_graph_path=inference_graph_path) + + def test_export_frozen_graph_with_moving_averages(self): + checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') + self._save_checkpoint_from_mock_model(checkpoint_path, + use_moving_averages=True) + inference_graph_path = os.path.join(self.get_temp_dir(), + 'exported_graph.pb') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel(num_classes=1) + 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, + checkpoint_path=checkpoint_path, + inference_graph_path=inference_graph_path) + + def test_export_and_run_inference_with_image_tensor(self): + checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') + self._save_checkpoint_from_mock_model(checkpoint_path, + use_moving_averages=False) + inference_graph_path = os.path.join(self.get_temp_dir(), + 'exported_graph.pb') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel(num_classes=1) + 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, + checkpoint_path=checkpoint_path, + inference_graph_path=inference_graph_path) + + 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') + num_detections = inference_graph.get_tensor_by_name('num_detections:0') + (boxes, scores, classes, num_detections) = sess.run( + [boxes, scores, classes, num_detections], + feed_dict={image_tensor: np.ones((1, 4, 4, 3)).astype(np.uint8)}) + self.assertAllClose(boxes, [[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]]) + self.assertAllClose(scores, [[0.7, 0.6]]) + self.assertAllClose(classes, [[1, 2]]) + self.assertAllClose(num_detections, [2]) + + def test_export_and_run_inference_with_tf_example(self): + checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') + self._save_checkpoint_from_mock_model(checkpoint_path, + use_moving_averages=False) + inference_graph_path = os.path.join(self.get_temp_dir(), + 'exported_graph.pb') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel(num_classes=1) + 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, + checkpoint_path=checkpoint_path, + inference_graph_path=inference_graph_path) + + inference_graph = self._load_inference_graph(inference_graph_path) + 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') + num_detections = inference_graph.get_tensor_by_name('num_detections:0') + (boxes, scores, classes, num_detections) = sess.run( + [boxes, scores, classes, num_detections], + feed_dict={tf_example: self._create_tf_example( + np.ones((4, 4, 3)).astype(np.uint8))}) + self.assertAllClose(boxes, [[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]]) + self.assertAllClose(scores, [[0.7, 0.6]]) + self.assertAllClose(classes, [[1, 2]]) + self.assertAllClose(num_detections, [2]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/g3doc/configuring_jobs.md b/object_detection/g3doc/configuring_jobs.md new file mode 100644 index 000000000..f4d345ffc --- /dev/null +++ b/object_detection/g3doc/configuring_jobs.md @@ -0,0 +1,162 @@ +# 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 (currently we only support the PASCAL VOC metrics). +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/model_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 +gradient_clipping_by_norm: 10.0 +data_augmentation_options { + random_horizontal_flip { + } +} +``` + +### Model Parameter Initialization + +While optional, it is highly recommended that users utilize other object +detection checkpoints. Training an object detector from scratch can take days. +To speed up the training process, it is recommended that users re-use the +feature extractor parameters from a pre-existing object classification or +detection checkpoint. `train_config` provides two fields to specify +pre-existing checkpoints: `fine_tune_checkpoint` and +`from_detection_checkpoint`. `fine_tune_checkpoint` should provide a path to +the pre-existing checkpoint +(ie:"/usr/home/username/checkpoint/model.ckpt-#####"). +`from_detection_checkpoint` is a boolean value. If false, it assumes the +checkpoint was from an object classification checkpoint. Note that starting +from a detection checkpoint will usually result in a faster training job than +a classification checkpoint. + +The list of provided checkpoints can be found [here](detection_model_zoo.md). + +### 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 + +Currently evaluation is fixed to generating metrics as defined by the PASCAL +VOC challenge. The parameters for `eval_config` are set to reasonable defaults +and typically do not need to be configured. diff --git a/object_detection/g3doc/defining_your_own_model.md b/object_detection/g3doc/defining_your_own_model.md new file mode 100644 index 000000000..6e36543b5 --- /dev/null +++ b/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/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/object_detection/g3doc/detection_model_zoo.md b/object_detection/g3doc/detection_model_zoo.md new file mode 100644 index 000000000..da2f8e146 --- /dev/null +++ b/object_detection/g3doc/detection_model_zoo.md @@ -0,0 +1,43 @@ +# Tensorflow detection model zoo + +We provide a collection of detection models pre-trained on the +[COCO dataset](mscoco.org). +These models can be useful for out-of-the-box inference if you are interested +in categories already in COCO (e.g., humans, cars, etc). +They are also useful for initializing your models when training on novel +datasets. + +In the table below, we list each such pre-trained model including: + +* a model name that corresponds to a config file that was used to train this + model in the `samples/configs` directory, +* a download link to a tar.gz file containing the pre-trained model, +* model speed (one of {slow, medium, fast}), +* detector performance on COCO data as measured by the COCO mAP measure. + Here, higher is better, and we only report bounding box mAP rounded to the + nearest integer. +* Output types (currently only `Boxes` or `Boxes, Masks`) + +You can un-tar each tar.gz file via, e.g.,: + +``` +tar -xzvf ssd_mobilenet_v1_coco.tar.gz +``` + +Inside the un-tar'ed directory, you will find: + +* a graph proto (`graph.pbtxt`) +* a checkpoint + (`model.ckpt.data-00000-of-00001`, `model.ckpt.index`, `model.ckpt.meta`) +* a frozen graph proto with weights baked into the graph as constants + (`frozen_inference_graph.pb`) to be used for out of the box inference + (try this out in the Jupyter notebook!) + +| Model name | Speed | COCO mAP | Outputs | +| ------------ | :--------------: | :--------------: | :-------------: | +| [ssd_mobilenet_v1_coco](http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_coco_11_06_2017.tar.gz) | fast | 21 | Boxes | +| [ssd_inception_v2_coco](http://download.tensorflow.org/models/object_detection/ssd_inception_v2_coco_11_06_2017.tar.gz) | fast | 24 | Boxes | +| [rfcn_resnet101_coco](http://download.tensorflow.org/models/object_detection/rfcn_resnet101_coco_11_06_2017.tar.gz) | medium | 30 | Boxes | +| [faster_rcnn_resnet101_coco](http://download.tensorflow.org/models/object_detection/faster_rcnn_resnet101_coco_11_06_2017.tar.gz) | medium | 32 | Boxes | +| [faster_rcnn_inception_resnet_v2_atrous_coco](http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_resnet_v2_atrous_coco_11_06_2017.tar.gz) | slow | 37 | Boxes | +| [mask_rcnn_resnet101_coco](http://download.tensorflow.org/models/object_detection/) | medium | | Boxes, Masks | diff --git a/object_detection/g3doc/exporting_models.md b/object_detection/g3doc/exporting_models.md new file mode 100644 index 000000000..5291d6b9f --- /dev/null +++ b/object_detection/g3doc/exporting_models.md @@ -0,0 +1,22 @@ +# Exporting a trained model for inference + +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/object_detection: + +``` bash +# From tensorflow/models +python object_detection/export_inference_graph \ + --input_type image_tensor \ + --pipeline_config_path ${PIPELINE_CONFIG_PATH} \ + --checkpoint_path model.ckpt-${CHECKPOINT_NUMBER} \ + --inference_graph_path output_inference_graph.pb +``` + +Afterwards, you should see a graph named output_inference_graph.pb. diff --git a/object_detection/g3doc/img/dogs_detections_output.jpg b/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 z@Do08a`0Ii&C+dsJ+$x9bh2QP!S$VN=F6ODBE1!urFa?F?0OJg?Lo5DU~0lDU{=wi0nQrFr7RotKBr2?Gii^bFeZFkjiL)tm!Trt}3 zXya!{4oOWN&nC|AC*x?kbjMNDMqlaWD)J;uOIKIJ2Xgn4Q!l!Y^cHFA$B+X1;L{@mGe zF*iAc!2VTQKTmZ0K@gbj{Kcsfe5q75H``>T@XYrvv2y!{;zX#SbJ6(y%L`#$y>UZ- zB>h?!2gxq`=jz6egOJrq;eDlc&$lu?6Vy7POP^7%QbYtTr+$$TBE2Yl-A?W&?BubE*S;AOkF6s?MX525y?V z0As0U(mz#ww|d+3Mda}OR@mY5oGJad7a6I{iJ#@UT^;nL=uakGd zL08;!u4L{~t`Pl25JFJG`+*!t-PI*|tcgRy1=KTA-WRGXf#eyf{et00TkB9K92<6< zr-TMQmeOU92xMOct}F`uxX;o1uRGQsF%8z;7+l2s$J1k#(;uRDG4eP-vS;c!cO7$A zzSWo53wTohba51fB4Ersn@d|6Qq$OsV(vBOtq=0%fn_QvDD}G1^C#=fdVseO&G^+! zDzRsHVwJx!ZVx+f_(-PmbM~upG2t=bn&A>fxT-Lt1!LsNY@2UO`DCxVqE$ee*-NT; zsv#0YK+terrzpe1!{df%c_>R%!@orkHxEx@c>2Eik zyJgbUMbZcE#H%0Xs>I}oj77|{seV7}fPDUYHalB`oRCs?XF<(ZR7;gLe%Sf>UMi4m z{7xC;2WidCE7r7+;q84aB6~bntSa2u>f}Q*Sf%6SI(bjbai4=wjhw>@3Gy~NI1XD- zKZB=&(qldM;=7*`Sz2k``-nhG{;bHw?(S8JY2GDu?Kd?!)sH%)HvNMLsBhfc#U{;8&%O!(WW?!mOP#YKJ~O? z#c=t#m3$JY;lIW!_GqW_mj#sq12O=6(G(;EE``wv9c{FEUG2{ea6?%%X#9S!yKrKO z87@`I((}3aB<1FNX<)Yr1YeA=nj4wbts(cGIBn*sto;%6*$i{-)W)*qZ^n#AM~%MI zKq7zY^mY;+-hwzv?uDn`M`jFtt@NW?294j--gdpGOr9!!w`*t|w`66_P3DOmOg%h4 zI5YOgX`WsbCZ$^<2noBEm?JI$8K*%u_l2k|aov`FbhHMkpvUkq=7hxSe5v9<&OJt| zguw;v)kcPhz}H4HTtW0sy3eM;lj+uvkYYmy6erA^s(ddoxi^Cxv`lwP2_H0w zFNGC5koLwA-$$WEpid{fiFl_=!5ZFy`n#$$x_G(^injInC#o+#3P46^6yTPTXFwfH zMiFts&{bJG6O2dcopec(e$o=e^^DK{yglN9ZgLCNmLq+6x>wTz(9&eb z6JB|k&0LKLsyxs&EywdJ?YzJ%d~d6l=kk8( zH#>dw%q|0kl`Jw*VeP@F$ZbQ9(&V5j*Bwu(S{iIW!=;_sG`c=em;21sa)ae7(#v?MI6cTD) zm}urHX#XaEPNy(^)U9K!y2^ilFthFWdQ#i|6db%Osvbv$okyjrO@=}!g<-O>~ zB}ECiD79*H@b~ zaf(rF#x%|9Bn8*IbJSA=_R*yw-q*-A@TD!=wwhE~PxGl|S@NvqB4UE%8i-G63hNPRvM_#+tII}~VAkSp*n6pvkOLbY5iyaKk>lO9zPyAi8ETeA zIq4^QiI6EvO|jKJ^fvP`l0?t}GCiiA^ZbV{t7qRefRR^;XC_VeVd4mq{H10?n~e&m z*7lz6wmHFz9+;GatdVoZJR}wY(%rt_T!A70dPcwflde7_7UMC2%}@+H%xBv&90<7o zzYGQNjx@>Sh{sL*cu(w{!Z&0t4W4ot=;@b#AoUX)Um6Mch;`s>w4F&ATRh=>Nv;#1 z3NR-zMdr$81zqW-%!~lmID1UVoa&G3q~(4lv1{G8n++qln;ib! z@jrep4KWBLPmzrtiav1F&oHG;qLojsJ}cy! zX$f{Tdl0grsIJM>Ade>h(VUgph|x9tOEX~i4_Mss-*0KKCH|zIFfd#`D%Ggt6N_^#(v!yk;a*5W~{^;R(C658MdZIS#Mw2^}_cJV-Xw z3sX`aXf7;I^>s&xwrA|E5y&vzDI=W_ba6*gRcU)z$+<;v3nUHF#4;-j0DT$~uBN*~~71_WWx*}rxFk9%|<(Va0lo`t7``a^Zp>o$dU%gNJK)bRN7RpWTK?H!mxW3KiZ zZPM4LgoNNEj`(|{6g8Y59a4}wSbcJHE2KL*kzA1lhHo#Kt`{JI-g4%|w`%?(HBD2M zJ7ChEM)g*gm!5Dm%WS7}VOe}~GpW%1jD_O%B|&Q1a?JG~D^Ofa{ogwDD^`ZOcq zpUNC(DJiyM2S9p%bJQsmxnv^ZCE93!$N1rBY{&dd5Q?5lC7ZE5c=t-Ry5pPOz_yIVNx|Q)~pL(G4 z7aqS8b4c`j=i#T{l=wm1nf1|LyUIht!@I|URNx;Ro!Aa@-&?%3<1@hd<$8?CPmd9? z6_H&t8N%Te<+F5Im_LBrqI|MjKE*7h^5G~bOL~p?olcof?RMoHPdkZ8tFYy3pKOOi zVwlLTr#}TYUTz1fda+Gni|6t*L1id+%D;p%UO^20E#E%XMAoqUV9IoOsP$X3(57i@ zHjb}86^UGirM&WyNqQ5$QLQ~2tC^hVsEex}TVq}D_R@!pek}!8;K%U7uZBoha45uG zgo<_fzpBfFOfpuZm%}m|tEr{qof3av#OnL~R&a<1`uat84tke-|@mJl1-S!0&ri#m2W$hPJn3v*l=z_F~uik(xI& z03+nniv06mg~EfYzJB4Y81Nm%zJOi2*J5-pS#Qx>iLK#vy}9o5N7^P~)@%)Nm zFX0vg|2O7uT%-o7QJRB|bTwwUl}rsaCQ13GpIs#9WGggXS%*xPATP5YQezqaT&o|> z#O6#40L?!_FyS<1;;$Pd742`_RWsLsNZgEytWATFO|Pf-6OEvqlfaqH>7t}nHQI!X zaK$6@D`b|AqvpotZj4`c7S7-sOD`h#{>efb4S}LoCj&EadG(qFL|yPd9If9Ch(=3C zR|2c*JTzAw@&OLAErU7QmCW6# zG;s1uTu5PSnO0l;^q_xCcj6)d_o_!^OTVuD>$+)5WUiv@R;K@0 zv1vG)*R9$UM!{fLb1vA!oR>Lnvz6$p$jtTpq{e`jOzGNmQVI!fJ@MBM=PLPB7n+EO@(J>XQqhbqo)9$*dKwH0TH+S_TyNJyK14F8*C>5sKs?Y zw2l`itj@XUW1#Ruu}fw(xUKU(%yS3^>JvP?60E5R4L6&o#gPQ0|1giYJf57Xp*@^I zCa0f4^Yk#AVg&qAlt~nmEeu;4iOeR_+wXeA`M(1dK*p{!j;Sr*KHRpeoR3AClZa-j zO~#P1@%O0*hD8-qPjBbx`Qws9i?&#&@4D_`{`#dD)lIf!c<1MZF#mdoD+}4KMyf#W z)jyWbbw5|K^UWTx`!4n6*wWI*r|Bl4Ri>z-kF@2z0E~vF@R>pcUgR1uiHi-?zM9rJfc2@b^KlwPgoc4MXzrT{6zHDz#fR-hGSoUV^C2AJalcX4QgRbplfFa zGCp*!J-;(T`E8XXZ{|?}RYk6c>v?3wAAO?(VOPxH?-9#~Zc_$lQ}skIN8ni_0G^6-XUL-MsQRc1e- z$^nmA0v1YblZ9c;hY* z;CYrTmC+mEgPpQf{f>6Vyc4N+ZO+5UU%XBzSP6>rFG725#JpZ@FLCpQ-ZDkkO(Z-d z1?o%i7^JSAwD&+s#I^dehd3Bop1y1+YpqgyJ?^SB@FWg4h4AK9U&+M7dt+nP(IpRf z@Qp?hFAu8pP{rMW)k@PWZDzP`a5!vM6lLz zptHbHn!rv$c^TpCN9)QUOuV7LHN)O{e?&H>VjYJ;G$c{sSU=EK+JoGmR{Ti}rSqv+ z{z73>>A_j!wqxCJ%2UbX-*$hl!_(S=^m!VqujIIG{VavRgDJB{onS|Qtk`DJZGu`e zGi^-42&7OeW7dqiEYlga3ELIO_0v~^XV^UUX0e^Dom3@P-_&{HX*!wd)ht{n*`1nL zRR5aA60dX@&a=s~CpNlh^;zi4?E62E@0s)iRKq8)SkTKLiea>ukfRtwJyzrUdXekg znHN843s0y`xEJ~agybOrvej2TSe-}b|8yR}F2fzQBtt6Fa;toc0cCzb>l@Tlzzl>0 zA1Wt$EjcpTm^AOgD)#gBx?-T81&mJ2TBeviDI=%W%Or|S zTxvZKPduthAHG?@Yq=-<=1Tp>7YV_6c(#~U?;Z63p5j^^zT8*eT=Pt+2g@NU$=SK# z6T2xVbZZ~kyL_D*H(PaNi7?I@X`4P-Pg$&f$RI8O9l-f1#c_X_$)+%5(Ja+;;ZM02 zZNOCaoHcT6gRgrmRKmIpiOzd7*Nb~wrX^^Ls;oTk;UGI#b#+^{zn`ph&4c)2{Qt`4 zd~E`IJ4MV}i?fiG17>p4LJBe7-DxjC`3K}8i5y)DRo~VhJf$C?EHMu$`J-abK_e`v z%A#~OP!?RkJlgB~cfd)@Cs&Vl`=*x9ifYt%-DI?%RMJwi;mfx-LZws6y{!8lqN<)j z^o)^p#9s_*)pdK(-uUF{g#)~6t>3_fT@v?MMl`=3uhUc7UeCa2HjB{px@LB$-pJ8e zT;c64;W275?qg?(+5%Yd5WpAR@I<80V!TeqMcqXL5}$cx&VT>wL!^tUtnnD89|h_a zt*J3qc=bog@f$%xSBs;RpPwhZ-}l16-{=#~GM<<~K{4|L53!wjw)A zB7mI3R_?5_queC`q%ox1a;t54lKLaAZF$Cwb8_XR+k81iZJiU9s1mIDG~<|=heU}# zP5cS-Y`L~6Bu`?wU%vzh@_vTX#lrp+w-T;78@7o^nP7JK;NN`+8oP+oeyK5gz{SAl z&b!rlcR2bTwqnjmUPCN|sv6}tY(0**<8XKIb!@A*NrO5H_+zfs1)lHajBH;K=mm_c ze=#&0awZV*-Be&prlG<*z?jCYY}G}Up{Wt>KLVi9kY;wb6YXXeWun5JKj}}rI^L`M zB^<+1xU*xuN06+X$cYlS6(2b6)TOHGisW(hyt7!@wW8WAB)CP{bz|)H$1=lV#bjQV z4mz>Q<$4BMUIBraL+iNC3L6rSof(FbG>|RypwCcgQBfPqZtF8Pb?ar30kY7#o@ij6 zp&k>HVdN#Fw%@+~(7$tUlKwQo`1Dz$--sQkUbd9vyS> z@WwV58xegtbvN*cGtuC#V)LKq_Ur?lA6rwJDKpqbWMSFywbj$xSXWDJjLDGQS+$&Z z+|SOvr_mT&^ayr? zzHwyL!X80I?HhT^=w|brOXc2%CWa-x!KIVDtgL{D_U#DI7?aeTGzFG!55G6`w-Ps& zeJmsNFeqE=eLMzhjp*eIze0-)u}!%BZj={ragTL-7WAnd>2x^_Xh$lpg{EtTAo^+* zf{yuo%)PgqUtl!1fZ?Z7bSSRwI^P~qPK$6qsOM@z?2{J%mEHgj?63fi5hv~-p7-v` zd%<&(Mp>JqYIMf*@kiA_x#{39ESw9i77?|Tr<-C0V}&Q1D{g1&rz$>@7l>(c3{<J8_*>`Yx{}gIdo1n%o(mo>jfQuN*$&hKG-nk`CvX;) z5x0e>mYE*Lp2Ks;Y^fRkH6aJR{2WCUY2n*dV2eJSg3mK%h!M7l9~6{SvrK}k)U$vp`z>=uj|uLGWmaFsMBYE^P}Ey}&35imiJ z^MB%=bWYgSKFj@Cpsnr`+>#n9aGW(X_Awp-)M-y+l`-(46e~CdpHxp0G#&;VHmlTg z9&6yxr<5I#XOu+s575YvC6Bg#WH$wrg85k+7FV@sz2Q8_;fa7Ip*YQH037T#fwspIzsp(%iWl=O>MLGpGw@I^~wa!+EoTz%|j40uX^1`=|@zDwCD?A&=H zX?WC;;ns;PnptPyWfM7V=eHn*%A3btsv2$TnH}5-8}DSQ)mrYbN7sO?&ikO9lJ>pf zQQRSx@F%P7pzi5}nJ-*#?$J!k#!_K|c^hVZ(8Hsd3ULAwDIvtbrgh$QM(@CAb7(_&7 zd^UpRQq^9s3C9-F%uhh57$4-7zK6K0|EDiTqW5^?$F&uSU`L7RAExUGrG9C3+PZnp z!84w(a2DU*Qk%h;#Jtf~KtH|kMD3BPUSd}hTx~9K|7s%W>!-J3_v>O;u!Lv|)l!qL zBYKkocJcnVfbTh9?hCZ%FOg)H3Mb5S{#50wnDov*KM~yb{rQ!Oz-doMyx!NsTfbvN z=Dsi1$`BvIH^#ySO2xVydET;%U$-75Vj58|W#6dde?^-ZvoJ2rUBWXuc1TjcPPAT% zj0lpczBplrShUrn*n8_lNf*H0x(e|dLk_ms`yEX=7~1P~rRi5@28Giknyej}rZsQg ztKjFK2^>?hYH+a~^@$QDO2ukj;$_}b=e_uBMlnKDb1vqZ`$d(4oqXEjqDH0<g5EN*mw#hr9uw_WV zbm{I_;{|S85Y8;atFoB{4akJ0%kSQez?x4-t#Hd!jL~Ku-C@x4%9G2JPu2JiKOTg? zBQviyqc9^SOPyuPtl8S@+eih&n>o;W%n1G5Mrfw4kYD$Yt%f6G+2;il>!>>la$N;A zm)NV*nWGta>g2C1Roy)@0=0CZETCZeeQ^P*wGZkol3GgHJ~97ZcWvhimPgpT`3Ktx zbLzGIh>FBsudV;Xxnm>Gquj8WDNcKu%qFvPq=-(LZc*fg1hJ(G_KkjX zS>0^4?ZSpWPir-42mFLXdO8KDyl$w^;Bn;M5&CWXTs0XpswzPrBmntgW#1~d%Zkgw z?7^90eyS!pa}kwNA3?W5c0cUvHO!{nG?}-J&x00@!F`$u9QgK0L(V%@dORIizzQ(@ zTor>?WHK0CEi#eYK%;2C(QdGiCko{du><%rIGrbkSi=2mZ2{DPXxXhl^$oUM6mF`}nw zE<(x(jOJvCD`o<#1WS5czy)biCUZ1=wN(@$X?udu@aU z(>TPK6ciANnWZx^K&`~}R@QTV;w{HZi|gA|;98L4`{G>+RxwkQBXp?$v1#}wTSifQ zGyoJ_kJRnl{K>P6=et&vr94W6O3oa6#Q7$xILg4>YU}K}Nd+wGIgRF9jJTCw>-1$y zTC8KG_TBh?&^4D4*hdYB-NFpG? zBy2?YKDxHlNh+w^Z1iV57j608X_6)eQ2HCAGJV~st7^M^&Mq;%TgXGBNQS!oh?0%; z*LUSjhiSyxzJ22qc;buIbT?#QVVu`(>J~@yr6$#4$+n%ZpcI=9b<`SVLLJs&-J}<_ezS zZ|8dxUbf2;;o>GItp8ijWi~Z+@!0v>@Rx*QRyvYolZ?%U!bx=c_4#CeQ<~V+aaC_T z$B0znZpL|h%1Vv7ps7Ya4F=pRpHpb5NG12R&klW7NJSN?kLM-nYIcFHGxqMNvi16h zb0E0pr0>*>FDg6Okl`sbRWSnX5gEiq_v>b!_}lU3l%el0MKdJ!vD|>Nr zW%&K+obiXQuu*=0$_ki}=}C8t63I;^uZE~qy{ed`_dOZ0we!xe402J{zN+Tj8+pf! zH$;^KYpozCv1%U0bgwJq;IJ92@-0HL))UCV;U! z#1fqz>p4>XUArtGoVU{pAky44+sN8@s3L`{i>Luc5Et+sRhtHkU*)pA+zW`3iL@(F z7{JS9{t#hK@3!M@rulF3j&Nqm2I_J$0ZC@KT7YByM!|u_y=y&kcFrE!=TzF+xKz#$ z2(U<9JL-M%s!z~`C7?R6TZSc}JBYj>(~4DNHK$Q{Dk7cdzhThOmEwtWmn@!o8AfA# z)#PykNw6=vvV;L=ze%#q@%zQKBDP`A1yz?`iQ(;{{P_g!;)*RHtn3E1BEUKF=g}7^ zu|@|8y=fhL#We7eaYFaAEkt5fy~|b!DA+j?4XFR*%=`F!I{E_U++COZw)xB%z&Oek zi77<#;s@|Asnv2caJ>)!?+Fj7F6yOA?CaYZHJYOV9N=I_KBLLl#ASt(`1f*fk!c}l z!zpXp3lkd+tdAg%xCI}s88|C$t8eihRMUneEJ?RGvw!y__LS9-v>1?%> z!PKW&zMstU>(CveM|(%uxXw4vp)n$|y0*~dZ{oWpHPa3kw{cbSQonsz^!X@26586r z+3T_h{5N?`Gvm3`DL@Ht+j5Mqk?H}-uBhRflzXoi1G1_w*|BcdgS*Aac8hFX26y%^ ze3i?@ucm?CRqM_crGY5;TK&_hbAig1eKtDjSD8oC^NAi6Cv8CE!P+Ys89QGMCz4Ik zj-gU%&@Ss*wW zxyS21`Cv5S+?noQ5$HmYQTe_5+uLQa*R(Ayz{zk~Y@5s%bgW2&=kGGP3=Rnr@ z@lg_<59-W#nuq%^&MtdShTOw@Xsv`I%^YW0 z1;n=yffv}@O@>1?_#-;l^WYx3(SFa?RJp9)2IouPO~~!j&{5gLc7^pIY%$K8Eas38 ztqj+Etbs4=$VJJ0Kc$c^3H+&Z#U0$j;Q8JoF`~XY++e$rWCINA2)x>Y=k>oWf-yI$P9_4+0J#3IL5Qf-Php4P-je^Av45!RY zVe9x}i@sl=X1|herMK$MTd2F*ri>R41hD!hC^FQla#Tf_mAZ*=)_FUaQAl@>_B~s* z(ES%!Wv*k0WiYr;|dg5ABCFo)6L_$9;s*2ar$$$vu)?>(Q?_d#&WlP z7n80#Qdpg4ZP8b6qJm9CyFrA%*O#J0+F*BgafJL*!3g$K&_$uk=T6<-cqIaDT2;J3 zQ2+XMG%#4ABnJXI->1ONjq*n7u=7dOJ?Ej^Gz2bhrfuda03Yf;GK*W-nn30D^2aL=2q1N8TeVgM?LfQ!sZ>#(`nO#2m`t7 z7B6(9TDY2)_ibP9%r*I4=*j@VIuRaxg~Hs_~-uU~$a zSDNqXo|$YWBs{PjT;*@fIkVOO2t)y076bY~$3uIal-c`gZNoSqX#GBNgoEVz?N%ZZ(dDMxG?@~R`*A#CcV$ld@AV#KVF-b4j>06H$ zQtkC-{+4ZZ<=7|KIt!*_bYb=YbMo+stpH?Fu>JtH>iqzy7dR*p5vijDgR|N(`UE#O z-j@4jo>9tBfbA#yBv*({t##j?Z@rBRJ(GA4zQ9gs!+g_{ZJj+K%T(lU?=>|*+viQS zte*x>0}QlZM$O+eZv+-ON-?|Jf*d2b&XUAswr1O~3VyKN{`vC0;8BuFZ*R}w^+`B9 zhQg$IyoJN&ivL+27fEFQ>OlV`yj^xZyNyAH7-LGfS$EXPlDB(w+qZHL+7#f_Da&$) zC0<!>U0&shXLu(($Voy#SN)BjF@9J>!c)hmaMQW2RowfW$_rU~`}X;I z0j1wPXWSa^<(T7T3fgtrIKkdKJ{BKdPp9Lxfp#^?F*-a?dgdf%Ga%PI0x4dy{fWK) zya5V&dnb&`_fOb*RNnYFdjx|06)`-U@HgO&)z+1+nzjWr!8(`6(LUGs1H)I%DQNvy z=3-7GR>J3#8`ZoO)jzAKZ|8l?z6Vqc>U6#&)5LHnMshnTFavSGp5wQ9b-}W7K&Bzs zsA6Wt2dCinQUQDIThW!U$(8vtSsJZAiJ$CXYMTJjq~77^Avuu0y;Ky_&RsV&2ksr;__w_L@LpQVq> zL=BlK%FH&F=S%yHlSS=eJSZ{Kr}n@|QwwyK(Wv0a5TLoLRD+wot@LGCV>LXF#hGfJ zcvu0K6PDY^_^6nc^Z;;jiO*rBLp-d(NlOC|+KaUv%n5xjUsWb|4qw|WZ-tsF&AnDT zZ0J{+3VU6Y7@?+bMN)IkUy1wG%|X;}*|B)0hrdXNv}1jOZW6XRz@|j-0!Ob{v`YTH z<@&S%%BgV`S4kZ5%&+Qiqg~~Rb#~xB5k}hHN+kVnoiJBCF`4<6%DI!ly33O96YYbv z^O(8B?L`3vE=lo$>Xd?7;fj-7Jc4v~I+5i6&Lku$A*|`}Vx4SLX1sX+yN=HR_I_pq zV9WGum`dDb;>3$yrTbB=RYbu`;O-6PfN@kaLC6hjj{1zv7oDS_V*iUFVfR-7+?xO8 zivYXqchnO3fdqG6+T-9WrGGe6|5u;(=pPP2Gm8D8hw%U9m9T}?=zlog+X+8mB~0e< zbJ>JEqC(dE+@iR3VnZh?KbZ|xtuvC}a#lMB+T5{W zIINL{@}xA`rltx-&_@H6%xIT6f5GBTkmWi|Tm$)OdHxpCegQXco58IwWlhn&j(dbF zAdLkF2?B|8Z4l+^lfl(8bF3Y@al+>1+}+$?=&Wo$=lRrK-01T-EfUwHMJw=R!^wUn za(i9jyLXNW8-S=56ycv9y$5OPyoBRirBm2}}T% zAaJg%L<5o|8I%J((mSOX%{!h{lgj(6zerk}#897NZPQA)LSv8vi)Q)eIxcK$26B8F zE=CtZ75HUJm@G@~2~C65wKNx>c8XMjZ$ExBSwxMidZAI9&q_S1t4p^>|ux|2jb)bg9Aeb4wfD2Obmnp%$OL_4oz_TpDD zK5Q_7+p6G-XASH2`D!8H^)@KJ?k~Oq=tf7p)!R4#CNO;tffwF)!*_x%)$gO?Z zzWSfb^Bj7eEXm(ZNkhuCYesu4bgCIeL2SPq{aYvc6ilk8+Qx#SLPcpTxv4ZxpOe`! zAwXZKdl4nfBeHv_(o1p6x@pT4DK6$lI4X}<_ybj4s5t6 z?R=p0IGQr{=hKnhE%qsheX)HP)wjB5i3y|9P=!6U>a-Oa^*o1+A@|jmORUs%6wsP5sfejGvH#-a_QqCi6iuL1o6WdD4m6Fq5f<++2|XX5B)fY4bK${SN1oR?`H@ zpez%ISCmAa$~IJm&%HWyv)<lz#~s%} zUL<~r`I1t7jRWU`b}r(auct5m7MS@K9F~x#{-wubZh9$MnjmJiBI!L3x#6yf4W6rM z!%dDBTexx?gVxEkdwBMB(lp?&$Uei}bKg5CMjHjutV7f{t3sY` z2O?c(+jW90&8(&5ZpAS{fVi2^5zoPk&5hmw>AL7hBKHB%4d z-eK7F`1@vi_7W7|TSjAe+os*M7?2bS$hs#U6xd%CcxcW(fuVne)+mg8ld+JP`6;FD zO&}S|{&ovuJn{J5hnPXiQ9w;?4iTgV`+|pvecnq|tvUOIG!?1dOi7v@gM3AImc>Y9f@ zT~CdFCF@N&`j;*ekVtd3_8uyWg8@%||C&&d$~*=7rN(s~)6^_NSJs4U`wY|nzWk=l z$$+4b(XTu$Y0A-h>E$r0Pr(yrmAf-ETu9*F2_@f;g#CXUU1dO1?b}B{LK>t?S{aQr zY>#wE$LP^9x}-srkS+mfkLHtzoIpuGfVe6+1n)bkcME}J*wuoa2}=n+GNu> z5ecg@N}3(Hi-|ihlShRy7CzO*Lf&Z=waBUDZPlQ;rP9hyDq923!Pbk9^r8cB*iNM3>s<|PN>dn$>%pcNv7*TALiI2r-(M6D5Ztb1IgE_>+`REL1i znRIL19E+q6$kx$m$CG@*9z2uVj zbS9y}N`kn3dh})`1&a;4+{b}qDRS6}sObH^HO^`F=|=?zZ^tUy6XG+#^phFP6WO(8 z^n}sVESz7ks@TU(U8o`84<2&UQ&Ay^j#YMB4YFN7hmq(GY|zt;Kc-fP(LXn#v=2hg zY%(kitgJ-c4M6mPDticN&Y}OUopmTV*FFYqtue<}Am9M__EWS@7WW^=w+WURH!fco z7{B)YLnGcekA3Q1em`k#h5fb1k})MnRnNFa6BW{2VgCCE9g4P$&@Dc+d^AE<1EVX~ zWKVUi8+D~OVpkhs#^T~DZm%kF~52#u}s|UG9rfrk_vKix!piC zKrv{uUW4r~T)eyH=+8Qyy`frNyFT;M(B!!rG20)SU$k?oA*i~Feg_lr`OURRC*r^Y zCpx!;^4#u5&|-JL2qOFO2JzE6tljZJC%29xPKaA%^X3cwk5&9QEb+%uW07Ot?S~19 zb%$F)TTk3Kj46RU5H&a*e7I~@f`J_vG)>EB)wW3bXvjYTQ)m4a@x&K_8c46sD7ouE z81FM}vQ8y%zFnO_1Rw2@AtejQaRN>()``;3D}E>;nrZuI#B<49Fn*nARl%$N86a)( zD*54XR-V?ole{6$>v!W&&QpoU(cDFd(t~Zky>oB4a0K@5Ums}YgVv#qIvvzqsOwdd zK$a)R8=a2ovu_-2$h4BdDS2|r(t9CKMzSCpk(g_GL$7#O-^GSN!=}qu-_OZAd>+V? z^-|40DDz+3Winty;ec#~W>V2=#NU!7ei1cI*)t@Bb9{SL&aj?wpd#Ip2;CjQSOHfa zWe}E{KS&rMO=kWaa^o<|Pfa)&Pmeh{hUSNg(j!}|J~o=`IN zdfpd8!>AQ7Z7Ub0g^ljZ)#)$_mxa6|sZGJ?ko;5?abxTAUS*wlwGStzt>}8<5ihFe z2^Pe*{3wP9ZP)#6C+ow^pM^g!c5c)UI?u2tgRYUbSd+=?=`UBwe}D;%kcULa3i)(& z(I@9XGIij0P<9mrtx^39?9|!qqDE9hLE^VRxX_1Z6{YmBQ582}WN_yP9F5FJ1<3r7 zR45u?8!b@^IC*kdDC&s#_>^^pLzS?mv;sOSyHB_I0Y$#jh@J&qfIyWA+k9fXALV3E zY>~$SlZbm%Y(8_J!H3mB_VyqIrh9$Oh|?vS81*g87`eEm$OFGq((Vm!&Dhfvx>S9! zQ?W|MZ;~n1bV*$PBsf=>EdM+1 zXpqbrTRZ1FaklK!NvvDz!5nx_J6<%2hnP^}Ij{`x8>!O}*GPvbAl?+ZmWwuBrNtIn zCzAs$T+7Kw{XI{tREvF@b3K7=?E&-hNJ1R;Se~!9(`w0&yse5#g?Z4?q;`!H^cX56y?-*|pl-II!w){$iOtWIPDIyIyk^&BJz{-JV%$SPM(|tQ6P5?dNs9 z8pB1B$HGCEam!DvQTnM)?0cz%L0dE3T6%@Ul1*97D#2vx#Mqc0dEbiPbI8um6$32) ztBPd;TDkVGo{4uceq>8Yo3u%K~xX8xb7}$~a{`5l;$CnBM`x23T z{+~J!$hrvB|BeD zd|C!_y&nje@p_UrijMx$IrtyM8NM}NE`D+QULjxEbVAu_@slqmx+>qt^H3=2k;U=AlF}7_1Bj>O#$~8%Zbyd8Rg$a$LQkY~68KeqQ z9o%&yvAO@*iK1UpaWIyhjOtDSby7F-d$C9-Yf5o7;9W<}XEsYQYMrA7)yEf`@2-E; z{iLB%wAkaIb`J2Tt}8IU}35Js!n1s=< z+FPdkEDT*e-rJJyy9fk`pIC&uy|+XQCUiLXs~w|xsoy;P{qENlhe~lsK+^)F^3xFt zdE&FWl%#z4uc7LHY!F8?tdlk~JjrPr^l|sI{MEdAa!rxHRr-rNj<(rect%m=F}MdumeyCrihD%_kG`90U`K5O;DXmM4ED{2&WuP0o~!bQNAZ}txjZuI=l zXYSeGI+R6@<(=x{!CDjTLs|v}e#d8_yEVoP!QEt#Evl%NQ}Jh7?efP+(ILymjE+Ft zmm8G)sw0H|38lDwie(m4U4Lurr-P_VEp|{!p^LibqE{gLcJB85eXEMYn}*N5L~8Kl ziqT49pBXxT9avfZ-B+6+nYh5RoGiJq^rRhfT{=?1=9Yan(UptRuFJKL!!w=}C)Rpa z*2^<6zoGgla7&`u;y0hW&%oK)+A;d>pD{SuJ{rM>Jv4eF`|0YbHBEy@(;0p8?-q`0 zocmUpFEyu%^sDj+Xs^vU*NWW$>qc@ec&~kZ?poZG>!*jTO!lbNn+yG>mW)QYvuL7O zD2zI|iu^qH@(qNCm86Aj!m_B^Q*k$!NMtZFCbF1SSpD^}#OUTilll#PSsJYaJBfX_ z(Mu1zHFp(>9#+5-(cVH|yx;o7brVSA1?rbvu!@djqnbQ=@ARH25wk_LF6~= zzXSgbf*@asek_4BQYSa90XVxFz=+UI+52j{ZwQb~%`@~&rj1)Gz?d-H2fkSSu$7JO zg1bIgBjZJxYRO56&2g`VRPP3zf9ptjHC|aX7|@HE1*?+q6zWY1&5)ggYN#n*ufIkH zt0CG7^f*+psEE-Ie>!hVKBGRS*C;ZF=q0j6)8R;pZ7T&_#`EH_d|8qN!J+cN@_uU-XBd~B_KPzmde57%;)`=r&UmtR32Pf?+J;*h@)|2k+8d~e;O;$(MG zmK>_PmrHbf(zq{-qFhhcjgZOV+>d_0VAAp1BD4-mpin{0Ur%(@%h71p=7)U+ zmkU)EZ+CpCcP`2Gh?0;>Y-)mHC8^nN6YLLY@%F!no@VaKnBhZatvsXYh67WQ2hm5a zD8=m=nu!s`S1G2xumaDK>$-O|FA_cnwEYIG4D)GrPqrNTidC6&$=>-fjBG}au)O-Q z*R|n3+gh}epHJT#_ocO^JEH=|{6T_G{;q*Y)8r3EIjn{D&XQ+^avUUHo^C??1mE z-yQo&3eqM zVNe|sNcTfLBM|oIE=aii#O-_TEsBw_NARlyeg(K+s8R>A z8Hv6p(Sl1uptsV;$BHQ>2q^)bYeAy^p%DuGLtCEc&8?klUQ=su**GE7s7sZ(sPm}o z{w!{_M=>t$b_5}LMp@G^;PirlqkN7j`ZdqTyAhQ^+X;IUM9Q!Cu4+KQzD*{flaflk zqlu=iNPyxl8yc}-i0sszObBwT{6A}x?k#CH`K7KjtcnVd9pw9C4?J*|>m!uMx>hoM zGC6jdHSWwx)Lku5Ri@misx;qtITB}-_1r#L`XV5tI;xy)wb#_CnF9-5FhjqnP%dAb zPa{ivJ@Xt0MVK^JBM4?V(Z45L(L1CeR4W=1UE+vw=%we45TtAJ2X%qJF48UEj`8+|P>5(1&D1avt@gl3QJ%!7Gq?85(G&d{1;Ehh%anIh5%OVutgiPDa#3 zZauW#PYui!hKCk&%-oN@q?s+o`G8W^vn&A71B9B+Z9-nPwEzT??u_X&t_X-!#dRNA zIc1YN1{ME8*HZlY$P=+}9zJ`PC*s&Tw zsV+XOcoi8hkDgP;Snvfw3e}PuQtQtzi1dn9X&ad-VpIVC&?v;g44hd-SCjOj_WFY@ zlUB{?8G`@NxbB05Vl3kP??zk;gawd8(nfEWRz+!;cJI?GSLvS76BPqFeDr98ah4!LtyP5RB0qfZg5W!h z7>`&1CWx;(b7#k_c!l{rcr3*3ieiJn*`p3Bp+Kz>xtc$xUg-J9ePfufn{T}Aj zk4Fj=pJ``W<@QLp?jxgvfXKThn8d3l=;*bB`cuw@ZSjHMUI$NVfP~r0R+);JCXRZg zGehxD*{=))Rt3`qZzCnA78TgCix&bySgS;n={axyntgp@-f+e^JsERcj&?k^*_n+C z*1I(S1{k!AG}_xv)-sawdLPYQ(kmNyr_CB)T7Fj0wq}*1E`*03y*n>GTwdIpc+k@A zC1(s~U?FbF>`ro!b#IqhlAn0}`4qHLm@_V6WZl%56?5?_%WCe@z8St+QM2q5c(ebt z4rSV~h9D*SXeyK?zmzWTO44k5q$oM$Ez7^&Y|l5F?InKtWgh!=XS-5k4j!nY6T%Tr zOqcZ%Z>gCc&~>3-V{rJ*8COgCWZ=V8?W1a?kAIi&)S+QYwDaGDeOjqYZ7u%q?|D*u z{PmJprdSZ1FK$Xxegk#!{`lz?c+(tfziF=a+2lw&cHr$|0M#HkWjtJ<3>=+ntkjI* zfT=4LJQw2bIU8Z^x-c-q>|V2$OO1HB1zVs-E|$>97}1svzkWN?T0MQl^Pn&Ac7t2` zU4R6QGS2YjSdh!#)FqUDBfyJ3WhRu`8jD$;PcZiY@{^HJh$Z-^!3e>e?4OAKwQSKR zhZCfLvtSl*d&g%WRp(%cI+w;g*0ZUg2G9soyT0V;rRVfFU}38#@-0 zwiQBEm%o=?=5M{tFXSuXv~8+x5VfI22COZurzLBn9p|abwkL#S4boh7J$dViy;ok}_@!S55ww z-OQH+j1z9GSh`kpENyS#gW!~1s*sOU!|c`ej=;`c0k?S(Ip~^*v&6(0}_S^yRk-&$tE^DzKpLcDz~k1VJU90$hgqY1kEPzX3Ha zM0j?iJCo(|P z!#Gt}4?!>4cVXAG=M;>+R>fWglAiY&!50^x`;QZ(sA(f>vnlr(*TE+>ASySnG+~s#PW^}n&Lxe84&pPws0lirTH3~4YB8c#f6@A5N21xQoF7lW5inh;ou*k+ zM;or&k}=Xaf1y0^jy0oE+e_Fcap%iyHt`%cH!f|Q`bW6UM47Sasx{?j2X={pq|Jb{ zA6X~pb)L7r9;}}pd^@R+RR7HDG3v+4Ss67;le4>x2Xp>D!bnL}H_!N#UTk8V0JKZV>x- z2^3FLRH>w&t;Hyk{7c#Xk>R~*n$UH3%|@7%oV6UG)i7NU`Ml33(G$mp{!hQ3dEn!B zzL~ocPH*9#RI66ipE9@uerHlnXY6rv=O;2)H`4z3g1v2`yWLkFTO%U3+`>53RC3~U zHL_T_K)H{W9HqUtt13mnI$Plf3cDuHl#j#mT%pX0g1dRu?ud$R$6b&4m;}4i@psV+ z8d;B^wSysBV?pJKvkp98K_cV!g)vqKpe+76I_=TvH=T64)VBJx_JQpiUZu>lZ{p9C zRsFTAPON-+`2Bk`b_>jNxpeh&6l+cu_r8|mnk0)3w8==#{SBx5w0$)7JC5C5oI@>h zp-xD3z)k}?);4m@64fUd?b}+JKKQVBp!c&mW!l2_X&X#)BA_c=7YQfWBE>}uj+}E`b7776#${dH?$j*kV_;zEF~xoe zld&Him>bpD^SZ38TfJ?mBDhpn-xb zj!sus*KGyK)_@)kt=;u%)^x`vAZ17{0N?$;QZF~n54`5|lP!hzbCb{n?q04bD>@hk zNVx&QEB~OAICK+&pNB;a#-Q5z-72-UXwwl2T>*8@5=DrsrET$&LwcyS%F5i7Pya)+RW)538SqQlPP&Wn+V}+HETkz#?dSbhWzzxQi_2`e z5e?mw3lnbSNqk-$7O97T0d7Yg9?ZEkyxl>g)Uzm5(Zz}%IO?p5rxKS87srwA(p0q) zDQcgfB2l{jr5{oZeE>3Saj5a6SYHBZBawP=`JK#?{2=e`WEgOBAn4m7C{@kIf z735X^VT%ALR-=6~$)o;o1o`jz4-|p4==V&pcA|S4&jv6sN9C|5{jEazFtYJ^Sd#Rn zW%iv=iQdltJK>eT4~P#X&^^qz2Mw(gd-(@{(=%m2n6&T51{sAutoQm|!*Y(j4&s)- zfk&)72A$!@j5Ux*<~bn8l991o`hR!3mRW&^cc$!{P~G3veVQg!fe-?ODJQ4C@`Op^ z={jwzZv|95rn=O=(yVdm+o(b9*9lOpYjXu)o15Uoa#XDcKm7=mEc7hpbGqJT%G}bj zLPOrv6T4yiRey~b8Ik9cjY`Cq7`*LR`z(5Nje2>&!+X?f90bZ#8UBpEi8ej~^6W)4 zwMwb>cqIC@^@%f<#whd0Qb^n!F zQ3r_h@k>$Gilcy@<$2sRM>#<74idv4NqzM(goH~MFeJytiopSpX@=2zG}*lsZ2RDZ zoTdC2#&sH6Qm?*SlX&~v71c|RHcc0RKYzVEVzZu(rF@@)3ih~vM8V7#-n@I>kLnS9 z@^Jj66p>#_yxH+2$OfU^^}aSjtmK_!EiE-)5FacbWM8^9-AYuj)CY)76%nd)=Zg__=UPc z-biql$nFd;Jf0j17JMH`<2v3~-E%eO-Q00vZ@xi9(h+gpX!&Y_86d_eGBz;Rq>g0g z$@uM~&@Y}f(uSURfD%Q;`lOxc%{pEu1Y+qzh*K<~iU5WE899XA& z+R6l3o*D7d#Hw#TXR}sFrq8lcOUb-2U?7}+ZxkO(V^wdh#JT$!W2$k2+~?~vG3s*8 zq4!VYKlvR>K4Hvxss5DpD1#jt#497!Hq-|%=XY)z&OmYv;6Yw;x_zD6pWAff>(xrm zIo|AWrY9E)CS8}YZ_85N*x5&pF;+)}ZBGYb9^S`}676+yg>z_q7~elT=q&AkX1fBb zy}i(0jP*tt=QMg4t=C%bhzB39`g(BLdL7lfx>Ba1p3cKr*;J|#Hunt>i|T%IV#RSr zbI+9RsmPpx34-@1T`i|BV%?hXn`&@d3NV90?vdr%=j^$bQ2e6O9EspegQ&zT>>Ny;QdoD`C0oZwNfDUVVyY(m0-XkFt{7u z+pTl!9qbM)25i}FlN$?jPKEkC75qaRL}qRkw%*%*D|GrmJLXGxCH3MSOy=Q6tGKLf ze`Mm_mrJ+g)6rME@?w`!H0i|(5d(bSx0=G?xXFE!N{Q{(N~ve59ljDyf%B5nB1fl2Q1om3|RUi9;(C27b< zbCBRDx4XG)Mx(&mUdfOnFF+}j@0UB%)+1O$K~c>AN0l(YL|#O1u$W$qJPE8C^PgQ z-nK=F&gWO0UMhy z+I#ZVyENW3_h`X#J9I#+v39tYlSpx5$4d(4yt8Rf zN0xeUKE9vJ_XK2$U@~^M+f)GLJXk)yZyN~O|2OkUiwJ8?TM|APR6G=cJ3qea ze*9PiaMeg}*LtakjDMj*z@UD%$d77V9@9cX{Mq8;t{zHOVw9FUuwVqN1(|M(chfA>qX@#!$_nZ!h3Nxg2s=LM*nKu)~-$!qxyRx319nb43>#+(Mw3!kl=>a^tA7kxLJR65|8ACC6g^-y zZAoNX=(c8$@EweKuH&{7_(W{wzzHfVD~?%zpilPdTQaNy1UrKXz1FGMI2KN*dLh_5 zLia)sB+uiY#Cei3MwbGDtT?H5`I9K%-G8E!T+HWTV&m^ion1R>DUZ{!LnYPmEyiVB zBRnYL^ZEjhHn?9(f`0aXc3h-lQ>Se_F%AwtZj~S`a7mpsYrcGyou3NR_;Vb)T4&X- z%!;`amjxc#yVg~n(zyXPpqHARSH~|M1_hyDCGB<=g>tC<=HM63-h)trOH2C`y_DC_Z2c;<=JF)^j09QOozHLSR@WvF32bnQn?% z$%>MCpW#Ot7>s}X=|WnVs@c4 zmvLM7IC-Cj2qim@?=$m0@6ShxuxsWosZo0%{JcKj9O2#b8%lY0<9$9``i<%6IhMlb ze*WqR;gK8q{EqRO>yYtYg)NvrxJEsm8EK2a>l7b+OX@_rMEyauQ+R0H?HDjV3RQle zuNqiywP$%nAhUd-a`m?sE8&Fg` zLER+GNUOod-@m42xyC)0U+g($56Dp;Wrr8RLVjm$=g(!-^ne91S~7p_;t>Z{_>2fA zWq{jzt2D8SIpyQ!K=Vi{)(phD##9}=XxH9I265TLTsihDGhA)@V%muPsDjSR7UkoUgoNAPDEe{=t z{PRV2oP5*gHN+y7H?xLSj`w4{izWKp%+|~?Y^e+D3y* zgEkBHnkq)1O59Es5JFQK4+pkZ0g5n%;jhUC;!iPbH;RS2mm~O=ORx{N!9~)%PI)B7 zGEot|-A1wEQnK{J#SZUIpRU7$3PrpMld0AXJLT7lkwJZ1p}h)0A!e|>-Z+z zTv(T50ln!NlLl(|qHgy)j&AW#Js_9T*&9p>TF@e_rp~G!!4c}&@?(ZJRA-s@XXjSU zcVFAmArsZU7zB1mB~`Hf0YzedFCPoXS)kZnlNeTIq2f|_zTjKSI$3OKheftY7U9Hn z3#T#SuH`>lO|$t zxrvY7Rnck)sp_SqDmT+W#i9Im!O)cS<-ev(H7#6e1n@3VEm!CxcutAMG0jpuMbG(b z)TPRa?Xh9RQsvG`#@uA946qb!?di$Bn5SPY#u`D1Qf$LQf%M3ybD3U7yjFD!o;S1a zhgd>y4TN_{kRgx>xry#Jm5uciMpyvToNPI7_%(TbthH*zFjdu~AAOr2_{z4x!4z6y z^YQb4EJAlfC{vIAQ?vz28gL@?GiWAfA7J@e5`(*lu(VR%@_1Y|G5EW zEjicmKauV>VBvcdKOZ4Y-wE#RwP>eAk|AK=^YMb_iSTPB$7?EUQgOicXcsnPVNi;6 zyfBTRNqqcGQofYM(LU5qBV;ompM4x=dL<7xMgMNy`3P_}=3 zr^}K=XD*yFlxfNfm!%$osj*Q|gAQSgzpA3$LMb+R>gatJ^Oo7FCAKwtV&oTvy@P3garn*35>PS5b=Y4>-kVpT_lDtnv$4E{$6mbxst-{F z=g?3qs|l`h`+mJ?=FMxvAP zw4Spq#s}jpb9w%+nnw2bZ9RD5iByEHa_V9Z-lXcz7z{X?y3`+7)8Far zcYI*4^<{LRE0u!c#foq8%#Sz{X@fbkr`BCVKP&DtT&V^-(qoncb4HFB>Hq!C#8Z3< zdZgjcMQU}>%Xl$JO}#+P2C`oF{tvpS?m^fi%AHh=ZGe)EP?uj4JrmF-vLbmAv)g|I z0t--fw3j5{gZ&e|!VYUxgc+yc#AmO>fhV6a;G}MxRxJZR5jfa(i1U zvGBY=wkGVO!w)`aG7KL;$`4<=o@pl^Sqf$H42@pun=ND=tROT_Zg$~OH=G-|-xac4HY?ke#=DWR_5I>w3+1EF z?8Z|k^cw)0taxb3&O6eI#8XcG&M}@`iA9sA9F?#k`h(`sXIU5K`as1>&JUu-+fh(M zDw?DL6jqi=%+vxasW_2F-7;+3*~K*?Sg;it*0Yo3)T*O?XTMZYAQ(x{$UOjVUl z5yx63SzI1RLT!&KZmnMi64jajF90t%cJ?w|>~|Lea*qTxOns|+**MVN5ol}CA^xm| z8Mqr{!6I1%gEjfs^7b+h{(Uq1h4*)7(9lsd)ZYO<259O28W=ieH z;y0=GT}dL7+<5@MJCD#xGoGX{YU=Q*pUg^ro~(-RPSjshytrHQJYoshFWG~aDy!1=ZL(DmUXbFP{|C7 z`JC6tSjiSwS~ZyX-p{AO;B}d8yAO<~oxYU~xv&`W!8S#i8Z<@uL zoo=eLRxA2#a~54i@A8;S1Q9f2c(ogHZ_)a7D>ZD}oGG`r;XtZzvin=-@U!e}JZXUU z`F?mMD?FtzQ29VHcD9M3?FoSWpmqtKW}YdYIpt|kYBJ-^vPB)nL?JO#-&23Z`qkwW zYoF(L)7XY{oj5VIq*qxb2QV@%VVkM(-_N%p@l|cTS&&~elE=UOE)Qtz8V@$S%kKh8 z%rGGe#5vWN_1rsEA33Jl)Bn^NWg@wS9KI(h#l7%;XxxbL(6>D2mOr6UpeQR>I#op^ zcdG_g9f+^ibez^lB!YJj{jXr<&txlQNck=+lh{U%b}5IRvpOXq02P5B| z$DFq6$oz?C276h3Uy|ewm(>AKiXAkFrR}^zUP_qMILlb@-FRSGM|i8j<^uiC25q{( zjUFwP#*>att(N5<8@w5Gq%Fv^uBl*y4w|mz&J*V#)jaJS$tiJA#Iw-Uqk3_$ZAzR$ z?d55JK(5AE;p2b#}aurfy76<=eMPG+cT_|o^L6ohv?7zlB*NMKGva*ai46f@zCSBCzSE=5rX2e zP$<9`q`lw&;!iQpXFbZ}@Hrz5wn@kY9^rHk}N&x_Cp=K~0I( zo&V6pUBkY%VcS84v{;;dADQ0T{X#!pXw)aZ+c56iZ|Vy6*ihC%L&5ntz=6F5->iL= z68p2l%K$9S=%=mf@ywQ%F9Cgn^ol27$E!(xMmvpGt3Xa8qu$G3B7Cf_0MydEa|q&G znei-&d^sNPMZTJcRu?4(an5emyGV-htmdSvo;4n?o%TFZ`FwKewle#m(_lB=O-YcW zDnGS=8N1num)-X;mA>7mb#oz)9+P+!=cYfhZU3cM_2{*|{a~4j$~*G+Y3XRPL0Y|} z4gz@VLfaXZ@K0CeZb#xeZtOFJG}0itIp`_aCP}kCCVMM=BjLBif7=+9G-{cWbI&Go z(i9tfeMF@Nr_$)vZ|)*4iiLkp8L{+_N@ut?8qvyI%Ze25I$a%Z!dh!bD$?Z%nP7zp6zkf zsDQSKj>$Q~m_x|+58;xEhsCbDeh5gZ^-baDsP?swk4JP*^#b63Xw2~C)a0VTvVv(9 z#>d)$HoH2~!JGi499MTZQL<#Q!_NuS=TT`=R=^4yLkgyE?k-M;LR zN#Pw~zk8Jr#PTC07H?va$y#Ia$*-J{5qpC+JR<0eJBZj{B14!2IhNRqg9Fv(8E4Cj z{6yx?akllLvKm#P-YS+vqTqI=O8aq-pquE)^!ZRSO;I2=$j*SUSoo|jl+f+LoGPc8-#m>ae=UK4637yiKKHStev!T_B=`+ z%ZDdbA!?2$52U9^9kn5C$(Cw)dgIXj+g8@0;3+Fd<(WXdsDHkndbIJWX`c-n-J zaZ5Pfe-kEt#h6mW@kvyu&(xf!1satG`# zXDidTSv;>gD^nStFu-pNoXN0bJ4G~koMVtHHumXef|N8Op3hVMf`=y9UG573EWLW; zg9vMVrmh`t7zJV=!iiSj#|dbo;diswUV-#4GqCTdbrgpWE4Ysk3X`H_ec3>cr!U(k zIe}B7y|mRA)ycPO@|4UO2EY+dhnK2cuDZFQGG-RZ7ZmMnW~u5Cv%|I@0X6fNBgvA<$A z74gxIdccV)L^vTp3W-~8mzwOGBv`+rnMwyUzp8?C-&af@Z1mvaIg_ z@QbNoc1Vx&0R==ySLvfu;h~B>rv&#{hx9-^0+yH;+izA)^79nFTz$Kk3Q613*)}K~ zP3v2i*)n-GzTOL|+Y60+r>F8q$r;P-%yj<2&3Mhe5_o{Y#s6~np4hh{82^#4{(S$0 zy6`AzA(EL=E?xN?9(;3!AP-wBEa`E1^?bpXSmxP<310d$W**bm*HEE(2RAOO!XwgD zG7)wN?vfxsZpZZbb^Kq?2?+(btLsidX06>HIi3v7rKm&xNFBR~ZEuY~hn0dXrKCKG z7M?G)@ES(zasQ#k@gnC(*4S0b*W<;VYMIQu7*ME>dHJ8tt)$+$%uC4_U0m&vyw~ui ztK1XE?t#tJL?>J-6K!J=Fc_}bVDwD`eUd<3x5}(<6tbs-%s%sR!rW*#~wMY8F%?NZF5y@2-!r|$M*%6 zpcC*EkX*a?d0>rEy>UvOHrEwkta9Vy$9Fn8UNS)PL+8L6nL`lh`&7KW>_A;0x!zaj zmp#%6`_cFGs;p{fh}`vJ$Rg`W_*m0FSrHf2nXrFg>RYHP&@^4e%q)gQ87Jq(Nan%?hkQGh zZB)&}p~odR{yW;dz)V-5(jxkcVpp+mb!+|p1-S?@-)#@M*l_Re#8sABn~N`}vv4-}>lh_{q0DovG~6{2g|oHUtnfe>U^ zOL6Ax6;DdZJ`O_;S?@p6&BD>5s4KFg;u^Oz?u6T`k#PP-q+FSC=VEMKBv8Z86|i%- zRmtmf^_ItO<=^b16tWQ0vb&w35v^P(zns+PjaZd`Cl3=MEP7Xw4DAKIkIE^>!s;rVhays0hoxEh0LcYxZzW5;O1D8<%Gg=jSm{ZAwJAMT1Joq^_=e zAC}*IwF2%-wu$Ni?bpSGijGPT5UFl(-ji-%?= z?y))keUgJEWaBa6=!CFXa=}HAiItk$^n@a>coFIMth9skd!bq@pRwDazn$tJ-rU@U zNp`7BVEJ>`z+5vVhLP+lK7tWPDBIQ`iK^COOOWd)#htp^)6o{^ctGpaVIaW4={QLD zc?}47o=4XDlV|@)^xllzxq@Y$vAZ$-$jzj}LLHB>jKsT$hIroV9ZarTR8%Y)T`)>j zLbLgC?5(cl;JCv=r-o+<;MC2+4Y%q8tGCyR-~8|AQ3xSulWCT)!5ix%VG&u5fZ&Yx z{{b*T&%UoEw6=TOsO_y+b4YN5)YsTQv#-M&zYq9YDB&CK9k()Z)9YNB{xj-$7M6|d zb#VR_<1yEIrDM=-^guMI{?HJx9IR`OmHM;)00zN9R=XYP37Fs;! z&4kwbS$I*NzP0*`{{RNgL*zs8sY>Aet3t^C0Je8^`Myt5bH}GG-Twe4ecux$43?xH z_kZ-Hewh4P8Te7+k?Y~DL;nCtq_5@Qz@H2*{A=M44A^MwR_SzWBNT{m!M@Iit$$G; z76yJ6c$|OKw}1Ztq|#UOmHRvBzt}o=g)T&SE27)4By!>uP(Lj5?Ox7rQ`!4B=6F0O zPBk!Cl-F2(Q$CFNE#e!$h@S!?(kHnWH{Kq(OIz%WoP_}O`WoeYbEK`j5<}(VartmM z8kgY}#Qy*Wem-g*3%o#@XNj#1&~|V~pSX><9q>hV9~Jy3HJ-6HoAz_G#2pSf>6-Z* z-wO#x+N6DUO^KBX-p!vnT_m>pn?T(_!9MiJmU}tUbCNe4R{sEt^hUG3Ny7C}lj>_d zAfDsPiD3kCSneEGn?hx z`B#IAq_(5`OJj9^yYF2vs?#|u(^u1Biw*7E>pV(NmC(~+nt0|pjDQK;Pvu6ZJ=KyG zw3~0tLkx7Pu}SkN`&nZD0DX3vsnm|h$y1WnCH+2VBYec54CLfildL!LB+!totI4eC zAh%tMX5q82EV(E{C%deM@_WR^J{c9GYb##JfCYS$d;MZ{*UaQ1)kCYnR#xB8+_ zKb=v%`xKU6WtMggA4LbL^{qb;>EiO-ESOe{n3x_-bC>d5K{Q412>Zm){jh$5g72rpUWkQ_C&wBMJi&-V2( z5>8K0x4lUnp$sDkw>ARkh}XZRTxo6DjLg_o9E#r3JXgLJQpzYnz!25QkHqW0LLHqbUWQUM-^lU`$C z;#n`^g{_b@c+OSDdIy0#V-)PVQE;G*#z1q1^!;iZQ;ScjHSqkj`IhMT!9*El^H(IF zrfb^|B-LfOc;Wq}y1V`di=X12CRCf~t@xkM_xv1p@bV8Cf5ArlK_sxOjsE}&=fjGi zs5rsD_4?Q3FT{U`diTSvQLpsq-9pNDp1te#_x=m1rpa;t00kGiLv6rn;O?{Z9_#@{30D`|8 zwXY28wz|iM?qrVk-0r|*^A6(``T_9Y?qEfg)pM3C}vjEwWvzcYR% zd_LEH8~BCpG@0c)yZCqrnfk#RjERE z==VOw@jinT8l}oc`#hnZe~al@4}H27-M~?uz4MyW&^&*n{9Djqze!_;<}O_W;J@ct z+NPW$l6lu8vf)!4174gcB2z$W(%Qa4XA=vOFGV@eYE%cQR3)G069DT+0rVD@l4j%wcpVXyX+I zkAE9Ej&Mdf6=9iThzEn?CR3Y^5qH``&e$>a=IHrgX(-uJmQyJ9FJB$fwpCZl_Ojpj%L z9>5MNAGJFlEDn478g}`kLZI#>W34HtV^}E5c1W-!jh4vgqMQn4+Q=PZ^1+O9GCBO} zC~nDvNkg>ccE$}ZS19?7LB>|OuG)k#K)dY2ZNSDV8;SJm=TP=|IPJwp zyt1nA3^=O~ZEE34&xXfLQnOo?XuTQxZT|oUkjE5%2R=KjIodpZq+lN3@hFPFQh#VT zW4QPU<9H?+NAQNGtO)82tbo_$&-@xnjGqTSI|g7+{3qH+Px}N#U#R~8v;hO)KaGoY z{{VzXQvU$bSu39y?Bv&P(E0vV_4{<@{ZGNj^$T%jZE0f|irO1U);y1yaD#w*3Ya2I zVz8;lm_B?~D0x!W284Nt*3pnUe+aAadGlKdc>zXvHTdos)vA=|yGrc-gs&PjXt=KW z6-%8fP!@4GU{i73&lP9F`t__bJF^KAIsE%qwMsmw@#;^V*!6qWis{o(hS^IIo(C09{5O>#;|(6*0O)zBd@FZz zsm~pxQifkIyJPv(^F-?;o>|^mN`dd`MQEu)(%6bgHtt|*aa>wyUU_UUUY+w;_t*9p zS~z7Ny*)@ctxpwP1EqN}xh$==MFWBO*EZT@scxu%=NTh4$I6n>-9BBJZrixRg}MFP4Mo*5ZOH|BU`jfYhxU0SdhiNj%rjdJDpLJ zO6=mLibx5zQS!eI&CUgR_lorFUNy41?TRuJk6iWjuT8Vljr7+F(-31qN|9WDjdaK? z+3w;}!*5VK*If&9vyN`0<#)G{>>sleL8*SqI?bC%zGj=M`HreELwS6D7;5HxGij*m zkzY-wd5;CX@sjN~oSc$QJt}|L-%)Fi3HZ~(QvwCd0(FcwerzKD03l5HJ*l^c{vh0V zdsJ3QHE)Yq9((ac*tm^Cc9uS3{=8u+XGKTBIp<{4TgRR^7@J$+AF^bHrr zUKjAbpEc&Esl(=&%O$jD48)$bMX?OUnf)|Dijw<7qB@y^S`(%b5h-a`+a7u?;3QgC-S zsIGcH_$Y6JG@S)5bxmSBsUw#b<~2VlUQT-FJuBgFjs7k1?alSgwzDnlQaTb8%14(H z{p@<4M+2I7iSN8Q7MbDC9(aDyKeDgw*52_RK&ca*-#Zh4tam8E=QtJg^f+Bh5A9s9 z=zRRMx(dItYRA?-G5-L9kcqWbyVCV(V`;%F8Z+R270I9YD6NHwzLUgPMp74x! zHP?mVT&k`W)s*b}5sA-nIDA*Lj4r#MVom#Icvr?(Ht^p;6~6FR8FrA{k4|x3r|{q6 zZ1(zA`+KiFiiS|6^sj$*Fj!#df+v#KHE0Se^j-SJRia>7&G! z%&G#O02TA^>|Nn~P3;eebvYhcZdjO$#zxVR-?e=UuG?JCcjY<~;PMT99!G_Aa8aG_ zeSY)h^QzLTic!D8r~DDRj<0Vfm>TLF?LT-A%Dz$krM0%xbz8edA#Np&h}<$Z<0FsC zzMk<)N&TPpc>Y`{-S}6>Ker^hmxpJx@pc>J-m~N!1lmSV>0Xv$2U7;&-1BnjsA6N; zv*nKyd``I1d_xtohFNBuhmZre@~>d{AL6|)UcH{eU0A%rRZtVYsI8Sc&@% zN^4W-Uyf0DQ{fJqXK^x#H5)ti;D1W|DfqqdL;H8bdToZ4t(d2ZW%KO@>;ay6=xg@p{s^}1C;Ss{ z;p@9(nRQ}M{lu;-(BVuZaG9Kv{zv)5@i_C|$YCzN(SPYP8UFwUi`6akZ`i`>_T`Sz z;*B*x2dOOJ3jAr%{B=I1adcMUlq{cfCm7?ke#C#lYpmzK{fDlhwT!%<@RsRi6n7#= zufz>^LGdq!wFwvO(6?2kLM#U39mXCD{J}X`6JbDu5@i) z+S#Pi;Bup@Ai+PCMK8f`hh8TiYS;WarWPTjc!1*`gB9g=J|@;$-Z*Dj(`i35DINZm z=$;1nrE6!VeS+rsgY;~If2DY{%dwbsdQ<#Qv00WA5R@vy=2h?PU;8~=UEFFGYjhUl zq;A<6`ewP!clHGEidc)U1?%QEE&JGC1#$Gx(!HY7$GSzzMQeR#@JFAP<8E{B(ym@> z*O$OHlM*tX2nt67zct45Jo5`KTkLHNyC=jexTW$wM)9Bg6UW7IUM=>Adb2|~B&$pS zQaw2DpVGNKPxcb{r)nAPtl}Y5mc;zy{`s%A{?T!4{z}LgziAKMKGj=Rv(^&brDly| zV0RTc;~e*|PLCF5(T=GcbvR=vb9|}T_)Am$l>Q=UqA6#yJ4oaiKl;^5FMvKUvAjty zY!%^+w+)Ys=eJ7wi{d-Wra;CvdDQgzfWWUklKusiS$xKR3ZZVN(!Ja^dzsdPZrYv> zFCogQPTHJc{hGW*b7TFjWeIhdf2rN~ex|JHf3rV~rm`bXvPrS`j_C zJSPN3xanFJ{{Rgw7Cf^OanQDFro7eWxwv4+{%DeAt}cFJ8A$)UcQ`DDv{$VW;e zjK@@7`_$3p`F%I;*yi-FhZg1Y9DZANE;z25)8UkJtZh7KU5`5e6|Z;VJu37A9i(X< zKKh^OS2X=*3v^bq-7=`<_7Df@S<0?8y%}`rU@7Re8>ajf&~8f2dO#7k%)5t5>2!Ys z_()v;0Hs<&ZC8am26}fj%Gvm~S@j1JOjTIp8@pF!VXEo>0NG;NIMv1n&AZ;b>|IR8 zKJ%TvXQ_l)b9%!603&O|zYcyFYGO$5aE!dCmY`L*{tb8;nNr>pBFwo`a5Gi3KNQ>O z`k+`MgdtDjt=PO-{jE9{yoCY|0`d7*9ZcUmrw1B!q}QuAc~(zSZlj{Um+6$5;yBP9M+%)#)}M`meFsM-!V$M{!%ZzH)ay6xN+!*h@YMXAFTgd444-5DP< zbg8GE=d@Dito<(8GRiVpyZa^gBZr6Jr-k)P?6i!7AHvwI-B0#&(jsXkwAG|XG5~?4 z;EZ}#qscT8*&B;SUBGjW)rG2SQW+vb*(^5`>4S>&sc~k1N!m4@o=oMR3}hIcXG-63 z%W5C8?D9n$+v;e=hesfOy{es`?9i7F_FJh`Fm75V4aIdD{;=bF-7bHr&`RbMn?9ty}OEg;wAbsqzNV=vk3Q1OGW#vc&g z!)n0{TVyGY26`Ire`sI$CfDp|H-l5f9|OKOO%|XfK`o^7hRE;toZ`6~KM-k}%wqCA zRrXtk5t|~o4#M)fe2$9_F2RngsJw5BsTxp(tXWBZgzS`UV810_|aP3Mwu9Hu? zXNvgK_F30FF*@4mn(R7^dk$GqK|hE!g%7};VmWr|7R6Mk#%tYfd_!*A<+m#6A|Mdi z=bB_%?vbe5B3;U&R8f`MYv|$PK5~`hlRPKkzDnD-H{N(u-vKq}A}eOyzdiA{6`vo# zuNlnkY={93fW+}$;XbEiFIyQ%PYU5rCah_%r_37H%sa8TH*;FA6El8Sq9#5XWe!(v zSM@wn!{FbHtaZzHC9@2x%P?>LwOjF*!v6pj>yrJJ*GeH*0kYXR73<4lx3ZFvNWNIk z{EXBKsK*-=iP(^E#fLQ?@e#9n&s2l{AR$P-z-OavEVl!&aGG`rE7A2)JjI<<-ra7tB(`L`K4!e zk*zNd@_JtDi~G(e#nJey#a`Ng7Eni4o zSVMZ-9^;X~Ok*`#>r2yZBHme}lLx12x;`Rjm{@Ytm*8CdG{|bl-lpHp943#W{87+6 z4&EArMv_GXZVk!CGwF)+>#v7D5wuSc{jD_fAanBueAl;Wx^#jmCZ7IzK|d;oAK_H> zcwYPEa`DOa71>9`toE9BacS?N!&}2FhI3OiVtJ`a`VInF-!F7{z?qYdrRMA|W6HcO(qr zzd<}r;6I1HC55!zOTzwFoVYFWoL2*@{=}aPr$stevSBUDW)7dFeI`TV_Ekojr(T?w zdmbKh;VwPmB~pb{*P%=H2-U5u{0HJ?yn*t6c&9(aP!G$2UP1B8PLEO6#mh#{WGr#f zMjO(-_u-eqUl9Bmv(&8g+r+!HyWtyhf`1I=xDScm4A!+fkXJ@_+zbjyTH>u_LV%&n=S)nNw*}>lfImv~tvgYOd@GXn*6(TGm1xgi zMR#XZ^4!N08R6{w)xSfRE|zP9rR?inMRa#MxV3@y)@#TLvH`N-dXOuO_{ZSOpAp$T z^pZ8eBXO5MrDJ$A<8*QA2gCj;2&%zWH*BBOSEt6-pV$q3Vixk%ml6aZ?FYZ%Uj>ZA z;)@WJqkJE-*7RGomrK-bcPcEaoyWG^U z6&Z&|R$_R-F^}-ByksosdR&Ygo2y=F^k>MwvvN(We$?7D4mUBnXD8{8>0bT+00j&2 zEv>JJZY)&rK#1-=wgLSs#=l_QJ@xO}!qOind4Xn;*m4zioOAeBW&Z#LFx6VyU%8uw z2KcJ6Z(~_w;V-Qy-)OJRztrW!K5QLFXRW_8=Z!~BS**pio(T8mxwg~R4NMuqW;|dT z+|zEPp3x+=V);N%6_KjNb}l5iAgeD*;?+BzlqBZ4S}8nNE2=S)-F5@=;B#L>{?AW! zZ{a;o@nZ!bfGkap04jgNydO#u-`=EhxCD-QuT1bqi*IyK1nSV(%tU4|0)TlqHN}aH zrKsuNjQMA=TgAFkCyFCjrQY&f@7?GRIQ;9Nk~XxSNvFvhHi9}=2d4PaO=neUnh>TJ z{Ij)+25iI%{Ryk=O86fa0NGVwvx&HF$d!>lCUrfdlFg+=kv%bc*aD+y=1()U@ zjZ^T2#mY@6xm+P1DJG}XZ(~_pEPI)d;en zX_5d(U%OUeniCWy;W-hWgFVG0_iKL{RD*@ zJV`R?^S+!{%!*ICM`2eqzb;Kt>`2HcO1M78qWc}?%C*eagu)~uDHx2f93DQkq~51I z-#xT2Ja2px;fA?nvjBrikSez)rF_j_Q2bK0wu9l`y8i%W!8%-+KK1hTaaT*6^)RWb zd!PT-{2=jd-=7t%CPVwSm}ArTtywnmh^`}$0tkM!cf^)b$12>oJ6>6c{{X*@S!i%T z3{Fo^u8H$?)A{hU~KurBkjNX+B3H$5*HTD<64-IG@AGMB4OT<|t zj08|X2Nmzo!a@<|dK?w1RGrqy`iu5n_(9;k6T`6D>Xs4g+ki;#kzI$xFAVrs!&f4G z9$=~HK9%4R{?MAIgRBHTYGXJmuZ(e=)@As;A-WjHj%TdTt$2G;lW2j$v-|d$pc$Qr5tUuYUbf|AH(@vHCKt5&9wrkrT3BX=P_rjd8JY$4dPy{{Vw* zU7p+HC}#(4&V~j*+`F$ECwIrg=lL_ciLd>UQvP51Qa?$)Dh=NY{7R$0hi}LJOG#hO z?}4pk(7ZXJmvDa1r&{Fa{+O+QM1K@N;47t?YG812*ctV*Hzc7P}^VH}YHM4IuYrFQ z7vg=3J=T}3y2Gg2=P{`x8_4OAfzQ&s=yFz~=}&Xll&aIK&jVND2ZmZ%nHt#qj7CS+ zxVWq!vzA1YE5HK1%i;~Kj-_dDu4yvvvD8P&B7XrRJ*&w4S*0Mc=?dj>>zIPFa zmL985Zim+8_^+(c<-VxqF074(-pCH{=O>zE)}^k}=|>A!q_ViNeWi?q@Cofrid%g? z7Hgrqw$|t?7gs~mj9izzWNTKkV9yHVH)@AZv+~?uy8Ozd1^SFuthSebV>2&1oRva& z#X8Q-m3-@Hlrk}{Urw99#CM%-NFbJD6>O>}PULl~-7P(+b2 zgCX0$wrgfvSS@U#cVHZjSamgo?4;3maYjAFQr(H3TaY}loMySt5XmdrEMc>rcYZb1 zMPmk}W|rA9tT`C0y>CTLKGWqu;eq*BXCBoQ!MuQn~*X1?n zMjZBv>UmhVAFEGZhtt0W^!sf*+gjbW0E}#}q`WF5wX{fr0)%8S74z4_Z7giHmgw&# zGOvyY(!JZm8r99RC56Zj9^9)j$@Z_*a>`Dg9X5V5m`j?oPrEm@y*;G1`&2=9SDtu{ zzRjqD2_C%Hrrfg6Vyzj#$3fD#4;I=)v*5N^_pa@Ek2{p5YpbLH>^MATwY2?dE^S>I zzGhMs;<(x75#B6$Jq~Kdm#Xi*2wbQ}>}krJWQ(VG$n7GxSAa_!e8ZNidcLs)3d=3f zMO7K++NZa@j^4nyY?oYDHR7EBYSvDlYueIv9qWS;O|6el4<$KW^eVTCZ3`1)dJ2aD zkKykQ};~&Av;rj{g8sXWW<_h|Zz@c_cX`b6#ZR zsVlc*(Sqii*4NAjl6|WgSoTbyhp`967TdM5t}QeX`x3RODALBJpC)E_>u7| zOOn zd=&VrE~%ygBNb4GgY-4tcsJs8yc(gDPE;!nzgqcQMEK33_*qukErD2W+!z7}dWz@v zmeOu7B}+TA8ZzZsWyWd7wK`DV=8}b4yDbmXF9P_&(_f0xIGrX1!E=sO{{Ro`UDbRA z`z8L;^IUlU0K@(on^ad#PgAzGDAMJaw3r?J>+jM#SEGTft3kAU#(7KGSBmmK5P#sg zI-^Jb00k`9bmU+*_zT0%PC8^>kNuXuIn{M&=C%>IQ-;Af>t9R%0Kt4Ui>n{_D%XhZ zP`)Of8yT_YhMg}ODEZ0Q5k%X&c57hxtz0@_u+U}>0BShkA!xfGSk1b zS~%mI2M8nfs>I>q(S&a;PcHaN@qYRrBUiJS31V!`22V`a)BYdvy_dvo9d0dMBavYq zVu94x!TuxBwQq+WAWO?+c;hSss^kJa4SKi2Z;Z0(mr-9^N~;iI=ERSTjB+tvHCXby zti+;8Y{a@m8_ssggNpI%$Bx~$?>lSM{wR3oM)8+{qSf?(vAfH*NR*6s z{{RZ|ma`W}Gd4Y|#jiOzw$Gx%wK{iCLY3t5e8n7XKXR!ob6f)r%)a2&onGJU(_$RB z2Zih@EW{Je1j-e*o(QEKQKP3BO3a^6x02oxOJsd(Mkuxb7EZlJX^~jUWBuEWxam?Y zl#z_2qdxVNlfH$*5=%``f(b+C)YQc!j}O$2YPHn2s8DV{P8>u0;kABj4135I=Z>{EycRx>Bn#~N2D@NH?L9|lUD;mv3%T9w#1*Y?1 zko>)=V!d>^2;D_TXwtwMGMQ}msL3fmoiQVtAg!b7D9@ehe(3)I!K5cY2L3zX%AR7P{{Wdkz1;i2UnV$ zSE2m}iKeAbRjGmE{TZIy?KbfL03O`eAhBw;H~W%R$M;Qo!bJ|FqBfrC^BD_<$J6{P zfbry3nmzrnYkjz48Nk6j9`*DT;)NB8gZ8KcP#bAwt*b42Ex%Xt z<<8X9hgxZ?HKFoXoO;(ksOb~i?s=0SzZtIAO8X_Y`66W@N8Mq?XWj0;j%ltTO}qX@ z3)>Zx<7s=MjHi804&zON-dT{Y(KKaQy)#^&iL^)}w3^k1;L1vx@3l*Gnr1+lUWH^8 zOI6V=-r(u4Y>ZkmLzU@Q3q{-{v~2Sq*~7%cz@HeF#!HZv+KDmV7UU0XcwC$x$X5aJ2f+8bV_fUHbWERSlgyG>h6rUKsL1xkdh<=D z{9yPuW8%>4*A~#NzNUwkH~s=!d*{ZwDAXqJV9X>h;*n( zT~gdIQJ<9Jy4^cTwu-{oi>cNTZo=dJtdqy`ubln__^(@&!?(T=5~F>FP1ctV#Jbh>Jr2#AH(nW6ZCRuVYIsR4g&&O}sCMfmPpTbfYCW_fv)W?I6 zSe~BPud~QAxKWoaO^=k!vbR-xNN#x^kKte0TjU6sy_t;cMW?$67_fBkCc z{vN~OUk>Q;JeourcbT>qm7!G;r$#-w!8os8{fvGR-T2>D)HN>>7-zAyl6f?{hh5J+ zeq(Jdp123}6^>_!r10{n-i2UcQh2o+>)iC;+2(C4PSH|*Qa4HX5$E{0>t9EBOT`!3 zxVG?bhM?bnepOUsqKstzwc`=^TFXU{Y8r*?K|GP<3}FYMucm+Cn;#GEJYV9;{wVmA z#3Hwc?D~Gy;hU=wpJKf$@_Lk>VVGTPew})FOcQlCKJxen-~{lOhir7oZ$p_Zl*kl) z*2pAuuSM6iOTV<6)B^^&--R!3UNW%!>njHs`@^2UrFw<#pXS;X^1&GNueZZO6zHf& zq51wYx9@qS_K!N$G*7o!$tx({I2f-3{iuEtPvg%5YIYxGS&Vj%wL6oMUsUP39kiw6 zoB;tMod`)d@!k-YeKMl>Md2?w~ z9g0tH&)U4t<9EhC71{hX)qI<)dsGNoU{~hP9A_2xm;4la_D9w9U)l%$6F(0|e62v@ z;v_;8@tlwcJ!|5RiQlufq2motR`A8k6K!XOzcP%ef4zanrYppj4vWVq&q(fltTC0o zry8`ifA9~HJP+aT68QCEzPGh2E}?Unq(SpBCj|X5n*9Lyeejz~_%-lO4-P?Ol3rZI zvtCUnB?$vR%+(Ls+u+8H2CZqXH-{jG%fr^ic0I7!A;vR|W2 zDRW%hJ!hCXe|7$GFypRekwHKC_`md-8=vr23rMg30AyRMNkGf^hQLP0v*qxAI{bd| zCX=W?nI53((nfw#6mmZ*{f7Slf~cf2e#(xQ3U;@N^zp}Ix$E-N#JU1r+DrBbL6vz{ zW#+uDC$H?Y&--#eoQ|FVrl*rVKGeS?cvp*qPvzPRRg7aBw&J;~Z8fd#Wr<`gLYKfj z#d@!aG-rhc&a0!e*e5#|JwFQNZZwT1ZAou!Z0EL8rC&U7dsD&VQ-!X3xXc~rc%mIE z$MMOfE}N+BC3sK=HR!$>@g9>UqOH5+>>tQio85SOLenok)drpCDdfJuaB3L5O$Ldr z53{4k8!z2<`Ds**JVwh?CGBvQ=4Yhpch;8@7r2e`zi^=G>sf7i4x+}`>a2iOKUH}kG&d{}Soj9uw@+%dSX6sYB{yflfdvo%gyv#9aEhjizF$d__T zs;7ZoSFCB!{iNO4hDXlPzo!-4LGc3G>rpm1V6HKRJXQyb{5^YraxQe+q8H4|$00Za z+PkorYP2V5bUE=D>^>%f{$mpQ%JRa@8_RsnF$OnL=xW41G`-R+MwvaxLhFDSR*tWx zo7f#(k@k@lKTT%%<*r?5Bo2kzYqfjVg}p`D{iebme%O@r^e6&ED2b zOnH@$@i%k%)>7F=9HY%Io!p%9RJ5(%Yx_0Kl7$|P)K;y`g5m>hr>mCj<{W|atv!W0 z3fA1sVwBa|v2w&(#M|d{<>IVac#BcDk*(sDMZc~&HP6j;tsS;9+r)}Gu?L#9roj_0 zm#D}RG10|DsW|H|kl~4xpSoyTYWDZ~zmqkyMu;+B9e-Nh)Ac3Oyy+pkEd;{|bJL&x zwQ^dXxupFkh z^&q!@Xd*Ml>fN!^>{4_|^)_R-_eiZ?MW*<=_ey9q+pShvATR<%Ql7Z$UG!J_N=Xgw zl`FKW0r}f354o==6@Zl&0qY#%$aK3B(1Yf?WQS!))}r`$rU_`&0;uRZaOvwvhIw`*odbCHs9Q}sDi zpS*Qd#nY!vv`Ht9HCQHVI4)En`^1ids$XjQ<@y;ToU*Su0=P{}#Cv5e9^wi4%Js!s z(Y$x8X>8XK%Fk`LnB-&ptD#_AA2L@w<(Na0Ssm|%yjgpv+DRXox<}xKHKlE=Y8r$m z&QPNVy?Imkw&wD4u3N+t&2PB8V;p}<)w1zUzo=UiWAd;W&QD6_tAd)H)K2<2r8f>) zNbc;l4NAgdu}Xv!(Tzo{>um(TDgdX>SAE^!;~!E`*I#+hK+l4?>% zs2L@Qu_#Luk4nmTdc3VQvAl7Sz0*A?YDC#7zPuSxm8tYg>zP5ix1TuRQ#=Q#_y}3Y zW#U~h%F=KpkwGJmQ(H@>XjYQ4=vIy8Zrr%WJ$nlBYE|}cmf9Z83(6eK)YCjq3>My0 zk&WbF@k#dWCE28Sut?^)-C*58s3rdZ%#r^95-$R^G@H#L?nDxG-IfQ^vsC5F%@bSl z$!J`$u(G?j6Kr7~dBtw5_K;f(VGa&3tZF+OYioJ@?J)$=W0Yg^gNmHJq^Qx!XvSmr zH$MaPtQ_TiZYlCPy+|#z=p=tSVsgFecY{j3g`vH*a=7^!1!vettIw=`g(d}ahDIcS zYoLP8?@N@x!ewk_$#0a3e$}LcFq2Nq!PGQuJ500RWF_aIHA7C(jM5o32=WJUTKC$` z$C)H4x=;^BIRc`K?H3q~yGNgVR*J7G)sxE^B*>BME#qixBKdcG+3TEBBC@fO#m()T z6kb~cwQC;@M`s$`HOetO)#ZcEv@xJvrFj_ZOY0Jh?-9xz(c~~2!AIA#e(EyokGcl}l&4Z^?m-V{7i$y~ zPfM9r>0t~H7(J^}-$RD!!ZZp$8k0-#`E~hbaVhH@hvfs>x(iPU+Qn`qxVY!kR&c3S zlhRrhSyScF96jfTW4XQ|?Ic)`{M&0cSMYoiHvJMg8Or2|6m_pvw$o*f*{5>S9CWuh z826-u?70mYM=E|`SC#g!N|tXbQM6Iz)xg58{{V^Ocm5L^j*IpUSHbbkA>cS14EHt9 zjT6Ba)-T|#TIICa*#7AjKa`%_;=YfK^yuYyzQ}`gXOM#exUUm5UW z1`msW1$AHSjYm(K>rT0W(aeW-3C0ieudQIy^-C-vnF-sDoK-Da>25UH>^wtZZ9HIt zyQ2kx9Wk2w+!u&AxKaAPA_}B!yWM&pBZ%N^ElU2{jGQF9Y-ee{FuginiJ{)#AG1jk zkrQ+sYR``JZ4W}UI;3R`?eiljLU zKLDp+2!76b;@{gCr8fw;{{X>MI0aJ;Hy&J>r`<5IMwmDIY(Xt-lidHPiIp0Z*an zw+3~Y_qgkvbI;{pPn2n7Kd#0NO4_61j}YB|YW!}}tx>lLCye#> zCceM@wKXM=!n$3Jz$41i0NOw6oqW4z@b^-&{h)MM@2w<`BYw^1vXO<__!;!+Ui0qaeMV~Cy`xWgB$m;~x;D=2bJDzua(u2V z^=i;bDMY)VTHD#`?e?slxg1xe_-fcM!<)Gzu_*+N#DrIz*a&3ldZ zt)^Q160y+bwIW$8k)%9jn`-iXO=VhY5mz{4309Sn;)h9FO-@;@8+J|xE3okUMRBH! z9YO-6=M`cND@nPziEN=r-60Iiw0y>-)U2QqNyS=S42w%$ zM&bvAU8F@MIZf)Xez2t^WWD=Uq~rG`TC~I{K&L zuA}DsE!EOQ!?u$kE!^-cugo!7~`NdTS53kt4%kV_F~?IDXyEso*1@o6GalPM*NdsN_ZE-dR!JMG}g*M zL6Nq-jHd-k?cCyXsa9OA-Jd`BU*Yz-p+n}wZbO5Sg*E9v4-bh_NY?niFccNzA6oUc z@Sc+-eqvjuJ*z)Z)3hxj)!a%I%VZ4nuWnGI2>aU{Zv`%9`L2wA68LZZ220ZJ%o8qm zjAFST0bW^nv&S&_O5Qg+Wl+HX0K6)P#jlL^dKIjeF<#}BdI#&rO6-5&o*oFB;vbJ> z@ty6l5${r`9Y*T$b1Ypb)3r0J4}^^C#>->we*$=F%R%rpwT+Z}B0360F>+b2IDd$wBf* zq43A;@B2xE_Eh?K^C z4Se&4GMxI?eoaaH4tr|Zj&WLC%AY`ub09hET7DSQ{{Xc$h^?7;+&LpX#bvU_b`Z#N25XD(XNP04(d`<-Uop0fq3vC+jXm`75X0x`UyI`_&b}g3-8O#5gTuy+ z2u3}PIm-(fBep?5Ku{bM;=e~f;MYoqB5 zq^K4!_WuB;YhBzxuUe0!zxLC^;QM`B5q|Ofq;)?Kw0pf4=G#(^AiipV&JRlHJPn{| z-Ye5@JUOcEo+%(K7r1X)$?@Ecec}HAw>8T+jnP7$ocE{rR_ad-7_~?r9&}`3yVscQ z2&dH^<(%bX3&0*ay!e0Od43H1N4&*^@)psx9k`A>oCG=KkSnF}$A*8_89-T?e{~=S zYYur8=3f_lId}0!)@?ITa}=>$Ji2wve|crez~h?H_z(L*>#1{V`#bz?($#fXS8kc9 zpO?)9fO1c!SG{e^3|$LbQPWtAEp<^nI+2Sj=*G)|mscN_Y2!t+x`O6Q-UugccU~~? zU-meaoS!vM%66PE_VundOL^`YtluCWymhac#nAQ=YEI1h>@GH*Di=dO!*BK|&T>Lv z5$RdiR&beznsB`F>sv7hOn+zEe|O*3v+kN5LVc(<`Fvpau0^Y7txH0Qvyl^Gecbh? zU0XfGCDbkv`8cf~vE9bMH{3qeVQc}lUnG33gC%QDQj5AXlzF}568<^iw_BTb01g=A zttx8}-I$@daxgJlnx31r78_K`5&G6GuZP}w-I^>FJPK~|cQbR2#Ckro+Fqq9#UA)b z;0n@6qtwyon0$wwz3V#v07a6@SGR!Y-n4YxUdGzb?9wmJ6O&Hq>MQ9_*}p`-T^=hNAP<_VtXrPd;CfVlZn=a;ycs&<>t9iR$tsXd9klFDWB_Dz z;=Nq1KHC=v%Uw?wJetGcWhg#})jkE%?4Z*vqn^+bu&4^Xjw`FPw`+OCtbSgAX0U9d zg1{rJrzHm&_N^#xeAf*u_)vHR*Xyu^Ax$XkejztG)RdogcD4p&wpNht80lQ6imk(4 zeWZ_s+PkAkc@}=#!E^)MDy=l&oQG=GoPLFkSZEkJv z!N~({E&JZv!RG{0MfM;x#F!Fu)Mt{Yz0|>`46(2S3Blmjo}KYvPck&p$>ePctfX^aKm0AzA=j<$^*gJd zDhqsV4H@S?oO{=-_*nQ=^r&E+zF3oY-sDrBUrV0WIxx8WLZz;#`gis+@!q|u{?;E0 zbgNk8`y`$@(zi>vX9D0754C>n9;fSH&o}%N+vA3dKkdWt&%;s1zFfXO(=8z6a_unY zzoDbl{cA-+6eG(M=W}}R5n3{RkHA0pI04?@{tCn5E4FYWi{ZC~%bau>)hpxw05VsM z0Wc(WIO|_v{{X>$uVaV)3eVzO3z$PebMU{yt1&&W+MX-qI4r)(9(Lptkzb|db$N2? zdURir^L1m*QKi&{WVuxmFI8%(d8@U=xSJy#s^s@1M0~I_SmrdAVr4k}E3%wi^brMsZ(bNv6jxW8O&juR8d* z@S@*avS}a#%-BG{{A(;m8Wp12T7s!G?Id}(!=D;l>v}|%_g5I$cdB}vo-0?+xsr0s z+gCk3Ymo3?hUf6limr5f#%;%h%HzFsLgs1i+CB!u+}FqBC^}VdBz+zRcB5kCw{R`d z45x6$MK9SSxtvTRIQBI{OPh0WWsrR5uRhgo(^hXWQZO-|ewD{3?&x#CE%) z4T1bb^r8H{hhSnr>zaz{>OxjHNA8IEsy5UGvoheEjtxbsIITw+!YG;&@ClYgPusDNgh^-;CwTZ9eZF@$)yXjWSo7{HoDEvm>gi9ayn6~L|zU^ z61|!I1%JV;+MT8M>}%qy zYdHWPLEyf8#kTP@Xwr5pQbI@Iaz6_F5BU5_{0Q-@4x_?4m;RE;UzXqSXwAg&{08{j zq8V7*+4%cSHps_2Jk2pAU_PwG{Y`$Qe0~1_k`v=!`xLb={UwsT{9GM1HRS&Q6?4a{ zQB=&-mw5gbU!nLzZzN4)Z!GPG`#gU(KiL#m&uMoR%xS^T%bxvd_vq(Jw-Lq1*^~bO zK^0!c+D8$~Zx{Npml*3`hG1$t#V*MHlEp#}n~|?+7ujr8!2@b#n2*x9uZwMCe%6~5 ze9!YAPAjgB{@G_Jxsz#UAyj%-3FCOL5>+upx;Tj>kzZXy;IZk50fY=ThLd1phfm=J{YF~vY^IE>s{%4r_rX@mq@#Y7vVC1tre{uj-yf#hjjU?UW!K4x=KgM-9|x zb;1bkyrJ_ga?LlVLwXW!I+gWXaSX2#IRV!>9jWr%X>n+9>Kc2Oc!MOPJ@cH^gtcqS zW2ie3dOUtjyQmnlu1ivee!FQ;8tgI_r2t2YAiKd*xiW9aj>rgG+sb$ti0QU znX`pqSM?YpgUYkAhvr8OzN+hGuPlNn`PB1@YNTuya#lu7{hhVdgxYQ0reVDKr3a~O zV>QeCBiFy-R(R_3!dmj~(lw=Zc18JCLyf0I!B&Q^U+j9gVp9@DBlFTvh1u<-lZ zS;;lrxPE1p7UXf)ft=S#;(rWio+Q8i(MxC^S%y5acDB+n+}9)G9Wz+dr&zC&LbfEv zhBRN8jsZWFQqcTuu6TdMv0r$q=Gj4I@*_WZjQa}Rw@yi|j}21x-wW5U-x~ZC(|*Sq z$D+os=A4GfAal)m&xn2rY00SFLp6xr$h#UfJ5*rv`2PS(`gY3gJbh&@y##EDFYeG4 z$2@<9X84mqk4Ln7Ni7yx8UNwCj~#fV zk4L+*(Ic|A(^>9sTWTV%aC7Zh)X#AkmF%9^vFK3A@R(?ON2A=QAA|4yCd&_ow5t`C zJLz)HjsB-RmHaF2-`S(#RDTUTF{OA*M7I&cZWWwoB)I?*e*i1XKV^T}wtoR=aci2Q z$k#VA`Ob_!;bD?LGhbMIE%5Xf=k0}9xg#OF3ih(x1e9FmrO%wsvGAPzoF#ORS@>h8 z3p=vTpch^^>C(ME2t?N9cJ}1g4dD+ZBv)j?#}(SyUW|}>W3_z+C?y*o9gTCTW>UAc zhCN3w9)B9qy0JpJR9;1AYBypT{KTydI_zIY17nfvThX_)89Bh_zc#!%@Vi&>ZilSJtibK6T;7>vG5n!GBd9%i9jo@o;_rmv z@o$H3beUmR9$D(Sug*V@eh|0#P2vl^Rw3kxA&^PN0qg!X^PEK~(ZkB5H%HrW)ml~K z?3+Ga_?N0){3!Tubp@R8?~tTVggr1aKDia<9x3rXjn9JY#Ic2vSc3)6Q-NBa5%iCU zAGK$SuDluWr<;8>jF+5w1&3^d*0Kpn9TFd?k$>0lSIV(1;{{W{eiuCwhFYPI7_8;dH z#_<9vWEAcH06!!D0Hn;-{{VuctZ(}+Vo*pPDbtPzbGhsDi^X0YpHI|b5P`B~1duv) zuhL)mDVz;+`!ysB9_`*K)4}%npPKk{#eNzu;AGc!<>PE{J6Day&8*>D`6Kzrz5%GI zWLNJu`9$%3XTomgNh1WY>z{{Y#MD(UhEj5o}La(S;4_@$^z zVRTk4F66Sg`L~R9KGpP>h^=IwNm+FE8|%dWC|l}QP)Tt1VnRCnp7r!uWoJ2Eo(^S2 zNj_G24abiz;kyyZ42YzRZ{Wfc)pH7XDfd2rNhc%|!%Ic{90B+Ql zCtU4SQ}|bvUUbJ8< z;_aD&pOQ$_fse+s?(X6HKxyT|=azF-;MXq})%H3_6oJC^s=B756qcqvM@nf!4zZk8 zFAm$X=uGiZmXa^Ub!8@^*HDId(LVaH12tDo@l29VO}0muAsn-dM1nD8PrHZtQ*NaZ-Ajdv_g9-p5LV#pqUDNnn$)VV~txwQU1X(Ue1~ zNr;>MQx11kJsB<$Me@piWA{g2%C>d;oms3!rN$?Xf8*WHQT}+P9z}cVX$q66s#flE z^67JxI|$1z3EEG~>Gh}RW_YcovNriZKXyu<54~vVH<3rS&h|}?-+L=vWrv5fgKe?z z7x$wbE0(=?OO$5(o^iye-4xb7-ZkTHr&7Z@64dQ{2drLSY2rJJl1iMArytI= zd{d`ux_Tz1XyB_1@-vF(d^aA=OZUXY4?9#>XQ*rD-o(uKdw7Q`Pc`CVq02i(T^{Ze z--~iM%ME_*<#Bmxut+oW4)veo3GVK6X=Pwak)6k%*w0Iyl? ztm*LSGU>D0zOw=i$DX(Y{tL-o+Wr$LC*|64T8*LG*~-^8_HF~hpmSXHplc04%<6C(IU^O`TWU~hI$Umw zIT+y9RWBPTogDLTR%6*uty#n))3!Iv3r8S4jMqnfuWD_i#VlSPw~>JPik^R+XWQxS zl8M-zxxpPe){T_5wz8Pixr_|BEm$gE^SZfiz2jnzo#SmN-lc}8r?s`fISz-9>s@>| zk!rv+5lJ$l<&>@tKDEdAmrBzwXOC32w%Fx2M#Oa$cf!6Y)~uj0ypg`%FpK{H)}@G{ zN^wpX(5e`TP1(B%b8v?4RD(#C76j#;fd;eNSGLt}O`|~Zl1D>-6T61j|2+gPAS==wJ1g#n`;l4i*4n)E;GH{5n38`iy--D1xKfP$9omL zwTjklMV3}g!w0raYiO?qmk^3+i-kO#)ikYiW}c|rw6W8p(^%XCo~N3#;miFTArRii zuiiIOO;wuz07RP1E4vNDv8u3W8Z^J@Hj*7yRVw1UJmV<+Qt@}7&3WtJ1n{A zhXDR{r}iBgG4|VkER)Qj@K3#5PYqezUHNg!R(`zkRqY|Qyw#gYzb9&SByqd7IXgz; zP0BiuE|k;WvD1}gR8r2p_R}r2i%614?(I2low51Vq|!*eV`ZtBFA)CiJ2>nOXX?;+ zu?rjUBpvoHD%D2Pv2lv!W_{j(uR2F=i0Gu{RP$J#F8F&UqahY{tfh`g-CYlcukK^i zk`L_m#_yc&_pbX;u+gQnN$tx*%s^&tgEhS@W+s!jG0RewD{Z0kpNakpz5A>#()p4m z#dxwd`<^Q=P55KtZ3REGb{BZbRFLQ91HU!&LGY_xNb%|ViZid>EGbWhE@zC}>-M4< zhY?L5JBMS%b$@D3)|$DGu%_2!eA%y^dqkEv^?ed;g#5Cv0AsCh_)7fGJZ4zgt;+On zweC8P!;5>nF=?%`+(znrv$K``E0Rx#ma)q%wS+-ZGH`36IfW+<%*oMx-MDaI|ZD(y8SB0>VZY-_$S3Gt1ub?gTtBno_idZa} zAAh|jt#5IAVj3T(VZ?edT0Q{rYzEu6WuKv@mTtPIu-|dfuJQWrCbYi&Dq-UDk z%@lEE7%$%TuMGIr@FU=N$A+Fi5NLB~x~mc`?iG&KW5#iudUmhWa!(ZTF1_*e+gm2O zF6ZZ&r-nFXhifmLo$qFTefSqlisQk|BaMT{%O6^^pjyFg=ZO`A$&6(C;=RM-C+wHv z&w{p%YXpY&&u$~QNVC0vy}+*@ywPO)Q<&I^B9LxUJ?r#rt2M#qblplaO+UQ+*FDN{ z*t2OR%B`Y!lq{*I6P@txgK=%H$8h&x2Ll0tKP_~g zAQ8Og2;2avmE*l)Y2R(Jw`necXN~X)>@X@_XIGUiqP%Fp+;B}}XE|%o*V;7KGSQe? z-KkwIwwA9)WCCUAcmC2MM9 zHsqR$H7<21Ce!1(w;L6R1P;0Ntoyshf=!-aIsP&;MvB(9+IY0QIS1}@-mBb8Z5_#m za7yQ>?M{`o*5l=^!YH}g1$zsO71RaUK&YLvH#Hg9@ljHiJ=py`9$+${{FRQ*jP<9 z>Y||^H#OK+ptl@aA%++do-=WleH1KTK zFa|07Be%7BEw_Q%^G+6$F9NyG0{GiW(2zxRiZ}x#dska;@oPd+<;IDhsjn*$M->@c zlINipf%a-mk?5WPgGJO8;!bi7MSAyz(`KHVW#vb1xbI$Rr}*bgxsQ77B)P?CU3^2c z(XATlJKd#6-3`w*^%+EK(WNI+Hdbe!iS{a@l{c-=NY?d@J4&-NTS`;``@!p87xAm( zb)}@wXQy5C%7Kx<#d2Q~em~19OQ+ahF)iD1uN~I)xwUKGwcbogW0R9uV{I1^ztTroOG@i z#M*P(=z6X2yz)$T57gs5YqgVeMH;NGLw)R;;Xi5H%S{8|uC;v5V{frISu=y$yh^Rp zr_J9(v)V~5cRwC~YAtE?FN)f`iFbo?!I&Prn)5o}_>Xqiek{D0%FZ5Q!HMM70Ohe? zLxzHd2(Kf~uU4ezc^CXYrA@7Bkm*p{hIyO-e;WF`;itmJ@N8C*Tw5WP;oLUoI5@8j z{hhorx{rwtq2+J#4n|j}O8Tq9kl*XRAh){6{nUF(sN-)QwetK?jpd>2qV+zbhOkPc zy{lTDh2ae`hMK=;Zz@1L*GZy7du~aT^O~t+Z8S4R-cp1E<{(!@w(WJd$@|^$Uz|;* z^gl_BImIh<1-~9lkX_5SBjhH(ML*!&7_MU<8J>A}D_v-wS00!XOrM`#pU@gFrjM^^mZpp?+_fE#0Uduz9Z-@%RNfk7J+1S1bfK8(gS5$v$aov+84#&im+(OLxyS-65X`BW&70uMFamQ(ha)Vdtl{7mTK zB_qO~FBA=}Bk_k4EtXi-nOf!sO$xPQB{`Rq(8uQ_uFxg_&kQ=(|pK*WbSt ze`mi3d~5K*@ZXCpS=Jb4S89kM!_uO*uxc_YWpc?YFCIOxGuft#xs62d$7e6jbg zemG^my^*coYP5K4`sTXPrb%?`Y4%)&?TVf+2bm}HQXegGwSXMwt!+-1B(BFQ&~EI# z@Y}<8GTd9o8}BHYBi6o~_$%S)w1I7R4bo?LE}OZp7WiXnVc~nhs%p0ih^dS}r9t7( z75L}En*9DKytjn7PDr{P|Y_V_QL+iNa< zTsUvMAKTlleuEmE$tTeK)xlG{T~4dQqHnSZ<-T&Dde(=EwBZ(qJ=iL1(DWe3tu@N7MS%$_hwu zsiuXK;tvo{t-}QIF+fImt3E2#mK(M%en}NUUqgy?*K=Ckqj^g4$hk)yaag*nvFcJu z9uj+(xga~_py@k=1Yjt5awDBY&I9Wj)t zNhE#?c%@rJyPC&WCOP9_1;i5!0(y7%6>muKmYbj`y=b!V43`a9;2xXd z%e`{LUGSUgPpLFg+3E`#<`MH`1A~GxE9Q?Bd>gyA)MmTU^-n&@?pb4wU<;tY=OB#! zRo_A|sZZWvQxfG-D`<6ESM8N+qv^l!jtv;Y6F7Ek4_fLppO1FB&70h4^AYy3nFd90 zo)Z0-Exc3VyX)J{ZUmMUCN+=@o-^xN-V5-XcrFGf}m@c@W6)RABO%MW+$r*-|Yw6 zmamAVxsKuyiEvMD_4)=~7Vs3U{HyXumr=r-(dx>#SB$dl!Nox^6E05`KiN#~6*n*6r8}OM3R2ZA%!t;?@;$$0$sfm6EzZqJZR6t^G~GDO1o&wiY3=D( zqP31qNJxO-Mo!>9mEkp^>d};%N?V#rvlyi3w|ZFvOahR4aY~V>g_)#0`cg?dth*qH zkg5xl=~*<+iOIdoI(3Y2F_rP!q*q988HwQWnuRXX<^duS6QDk|EU;KfZG@fo9=wn6 ztnFZH-BufMB0t?fEkvR+3aJ$|RxtTqT3r0z?yq57zR((_xYF*D61(v3K2o&nJ4dy)@@52%xixx5=+l-@RQ^ZqNBkN$wEP(O z>R0}U%^VZzf3hRZeyDtPPcz_;jAO=kcvn#$`7I@WSN{OPqiPr6x5q#Zf8xE;KlITR zeysdlgY91md{1rv06jcAdcT|wE6v7g<>~q!3)2~Y?|!G@KD%K(m9@N6u~k@81Mn1@ zJZ0m3LOH%|-d^7OdH(4$bl~<8Izh9`(uFHLbPcOcQF(n^8#M z^ya@8SXD;4ve5p9qm6i+7W$d;+uMoW(%1N3mL8Rp;!P$kM2KxZTo2wgbQ9U!TQq^K z$I5thJx9GZTN``3RS+-^+&BZ8_i0X1w0a&@d0$hF&@@YDo_xwY*vAIC4FVX=q+e_s z-9Xt<+-P6$jn3B-s}nCH6?01p_MTj8f(I?>U1{H~&nB{!Nw$kO_r%`_r?xSlFXfZh z+KY`Y;vF6>*C2@`$Q5clY8Fp#ax(EQ^=|!fRW72t7O+h$spUl3AN_i5q-jPm<+CUo z?NZL#I4$iVQ#HmU5%tF&)dl2Mw(A-J&RceUcN)K{=~CM2O?e6^EZeq^x;~YItZIO(1T0SP2*c7~vtqZLD1B=47-YH5*T_U)H@#;je}y@j$q;v4QOa z9rI;>)@S_wwd`LBJ{=@-CbpK)BoQ~2<%o_XVsVz|+*hZ9!ToC2N6TXIPYXA$rz+kA zu<+l7uYT~SZ5>J1BE~d8LR8t(NU^anQkIJe}qKsCZNWf-LicPzpIQS>wrmwEu%(n_! zIZyn2N~dzys(dZ@AYj%bj`h6IoWyWz*0c|VGrq(X+GG7)&4Sl2TD?tE;2c~a=_^k0eg-)SKm4H??Jj+N-X z8u1G1mcnl|9Q)VFkFI!!{t_7e*QC9jw%_%wo=tlP!@Y9uI|$$HUx8}!=;Er%wWN<; zwhEWk)RyP7JPRWJ_m({7r_+(G$KS$^yw*04uBFTfvPguUr!}BvLxQex>}%Ign^bsJ zAtbp;wNkRAmaY72UkHD}PW&eZi}0Vs+RT=}zK2ehOKXgM!_vO$xl?+G*)^w%vAjFGb=Il=nZ7Ak|qLDQ1Z?#i(rRmDZK^Q-;|zo$2W zz7gDbit^lCP2wB5p8D(6GrMX0P#XD@_J;8^o;&@ZwX3}<4YrnS(MEf4@BaYSuTTA) zzB%0ZH}+4E>JT-lhF4fiG1^$15$o&uSC05P!tYhQpHQ#l_I6 zB|nk+t^rD}B}o$aXGf83-s!}WM+6Vpfk0EB<`MeweUMhp#lF^~Fsmc0%Wa+YN& zKjeR$PZPwML1fgRzny>SGARE5!9}G=zh{{I!^8NV!B4(R*Tp)Q_J)t3i#wPLSg?BsdG>4J>u;UF+kUe~5Zz?}C#}xRy7{6%5Cbn)CR^H_iV5 z?a2OeIk+LtIS(@1o?nzsPC2I2?BqIYY%`u$&{Yo**e;=@wVm5#nj@22Kj>4+vq6#% z7(FXa8!HEaqmncX%0}lI#e6*9C0!5Dsmau)j~3QE8*Qgan?t~ilaAeM!hdL+Jzqo8 z{QWBEvB)zj_fwufm3?R8Jtp4I&6-0QS5l{rg1&zE(Qjn%6q4I?K>gr3Jf5}hW%!1t z6OR{1nlGX9CcCF5y&GR?yW#%wsQbpb+nr}u@Xg7M;sg{UEncskIVvG7gptT&Q0B)Z{)E=V=^_)Kzx)gKv+%UqjoRy>o$TDX$lC5qV? z%rgGe-4@DeQb}$jSmN7&gW9ybPvPATJFS*Co0N>?5s~!bv7>vq-R(lY%mA;ugI`}l ztv3bkZhQ_domzDypF^+kv%i+M)=}?54peambsvp&`uB#kJyy(K*lUK}*O3N#{1bP4n$$1r>fr{^z|`j9+RoeV+A9pm03F&{b5kz0$5lycbSmcKM-xRQp#gS#w2P z?WOyzi*jCSwigj37aPNM#cta8ef2rT>e~MBoV$9O$h4Fh6$%a6=jG{LM~9|+Sx=jA z`$^zty!!1%H;(72hL5y<@jZ!a3xtbdGIaV==Cr*@CWijlx0}kgD&57>3}OYrZ)vU1xow_CLDy(;t+^MqFNw$;?yW+^n_s(lK zP`;ByP4V9O>P`r5Yt5@pql>$_-%hO#i(=Nebk7mUY`23i0HHk&DY|u*sp0HcE~mC4 zG9n$1t!3OfytqrvH8#tTT;TIvO@-#4scGwLsoFGB{{YW&r=M@eu#%}5B>oeom$RuZ zX0Q^>8Skvc!oljoX z%2`|wHaM;Z+>8l8e)P1V<8_O;0KFN2R7_yKi*EFnc>Y7 z?WEhh=_kp!FzKI4)wa`rvh9mWAC~zzDT=;zC#a<K;ULTU!ykuJKm1lQos! zmwRi*;q$2Cs;7=M7+`2yOcy&&22}q5O5f2nzwKQXIj%I+jkjS_Y2%uSsM|zZx{c(G z+be0_1i7_7V&r_Ca6M~hPrRBb0{JE~JL0l5fhOY>vzX*wLgN6BO6l~;u4S}ysai+o zFWvHoU*}j!#YeH6rB&;3`jV894dg0zjQy!cO3%5IN|r*9t2@Zq^BcW%Ru}PI+PqQe z7*|{slci%^ctYUbCEHv)aiBjd5y8)IYR*3NE=(V~Q@OTl3xPDYwROcUF765QEfJu%`aFSd^sAr@XD<+~pALAA8-h$$n92E%t#X$C0AsY2 zJXT{e{{XDOtxXU^_JTgp(1L%6j%mp&SsA!rq23#-8;Ij$Wz##(D;vb$58CXmtD(#l ziOT`$PZH`D(IA39IT-TQ(CD`z6;fEp1#-cUO3K#XQ<{29bA;5bPM-=r%mlFNanh7Vl87ww`bwq2J%f~>ax#sbN!14%mDq_9<@J{6{eRRyqnM!+E{Ue?N#*IuQ zaA}u2lY2i;deGKlxVpCj;6yyO$RKimTIR3rXAS$2)1t3>O?#f{yymCY?`G(;;N*Y zN27<7IUlM2(fmWwp;xrrA8GqU+4ueHR;(+$Dx_H;@JD*h(yk#}0Fp*{o@|_d(^jn* zUKiW;cjuhf)6&ExMk~woIOh*~#;RDwG)cNFHbzBh>Dn%yWZxrzM{2{gnpov;GDDC! z=AgUrd+C9}}O;O>(=DdxCw8J%22TvD<(RA8D+n&$FZrZ=}8NzM&t zT--G7S$~M+4@z5!i=3D2{S^J4@1mQ* zw(4ORXxFH(FEgd=u@;f+;A8&DCXZp$8Yv)Vk$NANytDTFws?PLT~h5qjhY7o-+^AS zqCo`i?`@Kq<08Dr{t6isv3|=snYx#574`r-Z58BVWBY|}?8&Weej#{?-c3?gk&g$0 zd({+1GHnBpKsBMS`A>f?$%#H;;}ss9o9@*eKKzO+=v0(xFGG@4-0Ix=L-tVcTif_S z_1lz0Goc{5^VYrd;nt9Cq+5oSHaH`4j{JL9oO~a!I)8?&^hJwmi10`r`LAy9)KOd8 z#bs##W4Eq5*X6nOcUu)NnfiuBhK(#7S=HKEDS=}Ha3iScT9#Hu);TW&{DE*A@TfFh zKJUs1%r~Fmt5+sVT{W$x>(5&Go3`{mp6L>gg%V%q6Vewfl zl|e6}m&g0Jb@`G(iZatgNZc@eEA-d?4dw^P__p{U8FX9&{_Wj*nKPgEgjM_gO!?j> zQU1rN-`)QJ(vkWX@kbx5?OoQ6mpX>%@Azh5RXs-)$>^FqkifIa3o%9DZSP%2fn&JU z=a4)u8Ar>-c&WD&=qr1=qiWj16L}g*I8cmx^sjFHnXU|au(otAh`^~G`L8F@ycwyr z(UWw9Fa+_^y`SI@geA7tVxP`#+rj$RsgqN$9A`DOXO)^%rtobxeK%wg?L5H{h!yJH z>o3Lr3h>8`v>S~BU(>Cw;=72%+mmsO5sZxVtt}+F<~adm095m`$^NE1eJk`?Hy37p zdeY{)J`MQ6`#AhZ_zQD&cc(!vk~5E%15yF=6KVs*}I!Tu!v&Atus2KlYvYa5mMl2}v>AHx;n0O`0KiT)jUOU&L);w8m_O?u+mH>LMC%traaaC<$i;O!(s3=6*w(V&hgm#xa!>V`5!LD;yqvX zbMdUYo||p1_;z^T%*U9qI6pQk#(pSxLqPGIGikTl zSIfI<$cvMc->1EKpM?A!@l*Cz*3XGNEv(#MX!}}Qi*iZWr>FoL%2eE2ea&f9RcZ3Q z54FammOdtFH-$pma(ZVyE31a%?J>g~w?k_;?;OT)UYwai5>0TQo4pGu2JR(5elhlz}y7S!g}ZSB6%V8kqtAkKLE--Zw8S*>Sj zd1BYrcMB{GzGB9wlq2|QF8pUCSD#^t06eKz84SJ6QPnit-5x0QT^ciSW4uW;VDh9M ze+t$O^)OMI*tWXask1fth z5y-6lW5t>c-Kts4#aXx+9V(^o?o=hp&A}a&pNFKD;&hrzp15judI?s`e?IP~x_U1B{cI6J%To4J(bzcKNW}B#P1+KN>6oz}N!d&bhB5pq3;=7{hrliq? ztIkqAOa2Kf`vR!_r~VfFW0%Hq&26Ii-op7U<1MmD5>J=YrsMj0SM*SLC+lC9Kd`@r z^gj!DU#zG`QnvlxMf5YxO+!KU(3&)m3WfwmGpawwpf$zu@4AO`rS} zWDy^g4+@`Vg;(HbSG!bSh6IXQh3E{{ROK>nmgb00mp|6}8m3iM|AueZx3DwtY!dRcx)6=>6PO`~$y#>aF=gTXnayNF1j8AWB= zOdyCKJ10K0>Gc7D`_I<9d8G+G4E@5|Te~@Rem!cl4=!!2cqgq@yw=(l{o8T%sLrDu z(ryxAmz-9L*1H!nQLwtODKJu)=cRc+#aZHAZdNQX!wc`puSV5%va%yeK2|u*d8fp< zV3zT2pE&jWYv;3neV(QQ`lR(a+iQ1_mnnt7!L1E2G;CR;&PD;KSIJu?xP|y5ikDBD z?GWs7h0j{?uh8_N7&mtHHLSG75t*68AMWOnEoY8rD!CF29y$tvaEtOt)iJwZpf9It z#WvAtDLqDiXUS%%YR56CCwLUt?F@`w1}>TErkizjFO*9uJ+n==5hAP-NXxs^wOkd` z@&%-6Rr+`PSqeJbsDht7VnIyq&uR--nqL|GsERv zzY4h?!l2VUPY}H}kx$CDI@gB^?#I>D!M55uIbK`aSDJVXv8mc0u%xy&X=b*&)hA*w zoy}zGI@D5yOqGnD0{1lgNZQtT?(T_Q!zd4G^stpZq#I|Iio-fGvNmn6O}4itpDnQ< z+Wg1U<`qlCAtBQwh)jJ{W~rH^lf(w;=h_T-89jNYT-;n<>g#DX1X%mktyx9IG|p&J z>tdWb{mtHu<MFp5`JVdlqB;`rE%UMRz^1D$(!x$m#`S z>e{0Hq*>X=oUSrzVOlfL=#(vHif~Kzo3AOBLm28az^gJ^p@EINMgVXI2Q>W>)J6+k zo=b9n8olC8HZ3L?p4qn<2s?eM<2d&x*;(G_C8=6ZJ;+Oi!0b(X7wp@o$7!xxY3Txa zDCGHyylx|QKPvM5W;D9e=DUxFh<{4-{{Y#OT#nA+W0l0RZx84Jpb^ zCwm{G9{{`zx}S!k)htnSJ7J`8*a5)&>)iY;;1Q3 zp8yzVk7GkQ02=nGwAPX^VPk}Giv2ev!_rtaQr!>7^L#}`ij-UGW%#2?T~gL4l|Y68 zhhEQEU)WK_I<3XN+jI9+3cs!2&u`}=$Usti)Ls$QthFn{4aA#~RBig#qf&+0!$%eD z;bv`F=@!BoHDN$2;P(ALr1x|uqpOpUHMeSH*2Bw?af-{>Q;w%g#ME)KJzqoC%SN(6bH?tQy4O#oc(g?{cK1i`9AKYn@cUnh zntp{aTZtEL?g47+bgvk}tJoXpE%0{b93BNl)TPRpG``x<^{Z`q8QXYFC$4Lr`0L>N zPm13Md`IAtA&}}CRm3U^>R5v{34cX%m35N%I!y zL8^@8mF3V=r5Es@&i??7dx+A9e1Mr!AH5IP0dl>V!V7o6O zzgPbNVi=ka*`vepyY4=1;V0{LC4PB$e@k0?HMNa!Qv{0r4*iLP581E7;rjWPpZZfJ zba16tJkD#UEBxYoZyic?GT6<(w!bMJQ~v-3l)P&%*w@ZMlm7q-rjiB^tgI{ZAHlvn z)U};rEf&&Nlq(JDhd8g&Z}=_@S9AWujRlG`ODB$WRRMb7%-OGkJ^=g-yztM4#=kt_ z?xh8UZborWimB6h{&_#Lzd0S84?p-qtZaBk>==li%*r!{SCc3RkUt0pfiXqr8UqPMu{ya&y|a--trPScqLp zLb7qa2XR_ps{0x0Z&`awUFv+1<0%tBwz}}Qiy;UI#kqCs)F0_y6XR=bM@hJl#cZ-$ zzCsLi1a%ek-^QD%<9Wf;W4eeb%&fepHS>p$wYxj0np-)x1pD#hw{c&4leIbXa^L2B zwsjX8cW+b9ZtwKfv||G?ky`*{=QUE?%YQ0FvbwN2Wz7w&KC^E3)>i2fTg+Eu3>K)Y zG|8jBZBX5iWH~X8)%p${HBBFyV<#_Y*5_QoCx#`O0LCtab*s8U@sUZPCA29W+ z5e+Ctk8uo&Bp5OJd(&mNk~^^RNDMAFW8SswC%j7rwo5c-KYWqVYmTi%_B$}}dm^Rm z(Y>Ix)1#6!&)ywBooe`E0WbP2Sq+{+tc$HKXvdvsPU3k7rD|D4s99O1;*HEY;<2dz z0CsOd$;(u97aAn?E+%V$7iL%<)Unvv+=RUf8wcfn)WNR9s9D7nv9N66{*|MrcxHS1 zg%;NVZQG|c=FdppqdjOw^0ZdwRC;CQz42*dMb9gh>-tvRovY8YaOaKK?OHMDH%S{A zfIE0rr|9};m2u?A-##+K-n{DbQir>#>0#(pi@Qd$_&~riy$@3j87HdyixcXWSNcneShqlrI|4wy2?6^)x%9) zxomaKq|$noB9m1}l&n5tN&Hypit98BJBhT#lTHZD(ShuL8ciEcXND(&HE2&f_QiBs zeV&mke`*;#r$LV(ai4l~sO7m(r|jo;W#3rOYSB%oMfZEf(zbjhsX%W7-rBaz{I4f^ zzZQ}uL8w}hE>HTi+O62>x`NzEsA@VkZ46)yqoEniaMZ+7dPyCyp;bO)Zclk_50?J` zWAasx$oU-pRU`eMP?;pT$O*_K)AS?Y>$@Rskx)Lu4Uqhs=)q|#6ocA@&UtMXpsx+Ni&8^$K z^YkB%jiR)bMjjG;wcl{cD5oUqL1dGlOQ5bm>4p^3l2v$kfs6;tRnRv1x&v0-lwAOPvg_nQ3$~N8H6} z6&Di^X=z=Vx?hKGbnBU~bjzkOaTlO%=F$5g2 zJ*%^cjH&b64m;wg-|13D&GseRD}qLN;8U{PsLib|aj&G!VJoe|2XVUxPGQoKauI5mT+_%?lC&dZr5g*QY;QCeC|!qwuR&uJ9@00neV z!=~L!=EacD!wSlsPiDw)aBT9eFtyg}8^R-*3G*dyZq>&4-sbMg{Jx!Wl85FC-o3-b ze-15fFH%SWhk4}m&3NC%Zwy`dOT;f?`D=cwDL;Fs_}8IIP_ORw6k3f-v!Ar_9;0x~ zvN=;UTw|y{m1|J4w$^m_(sdKTw#AH?Y}t3yV~@dyG1({Hl9&reEsUR~nj2BW&A`lhUi)!6nQIKJJa2 zR<7vehTdkj=N~!8y?g1Ux$|u)O6UfwVRsaZG{3z>B~!VsXIQ&qJa^U{82_IA?CEK@GlBb-*Wblm2u+t*?X{buh|ypQb0D6j^2_WD(a zxxUlElIG(G%gZR^uX?3nWgYd{c)>!49M;UU+(uT}?bXw7JXOhZY)guilV>yWf;)S^ zhWGby&eCnupK%9(cf0Eu?jK*P_Q8UFyTuU4Q7ZW`pA06yug?LOe8kxX=+Y=7U@to={KGDKS5KuGVl zx@Cxd$woVrsomX|wVxA7Y8puAIKkjoC8=u{TAaCNW1hJ+A-ZeXn{Z%*oYH{?7g;Sd=?GyoCo~Hm_RqYPam%S3`zM zNgson`l?^Zm;s9%jC3@<2edlJiEV8h0t};YJ?c*%TRpdkuQzb<&Q-C~KGkF4oGg4_ zr?s%bX_t0u*>YH4MEB^Qp0q=_SuLoP3dDZ4$l}|iZn|N-* zXfB~_j3EWUKEl0Y!qWNiG4lRdF|eOn{FgD8HX=;@S1P}=Nv#po+I^l`KGAEHa6VQY zseD9!%2_6T=WiM9Qt6UGZ)>!u+@E`nwI7I52@6irgYxGCt$7oRZ64IxQMXd1v`%c% zr;HVooDtT)N59~M-Y@W9gY5n%9~1bqO45bHX$BFgYB5{+fkoDK`HVMXBRJ`r{O-{m zbqi>q^70d*t(p8MYpdElx7e-ZZze@Xxrtd+vXP!a!1b=IE+OEnD$$MFzMEe|gD}Cv zGQrCaIdZ3MZGD=4Uzz<5URmkd=9evnS4gG|fB1Q21BIVtZ>CAa*)b z0ShE*rw7jIkzM4!@KJw*5!?Nxd?VtwTm$l0L7)8_UvGRKG6}S#y)J%I@Y${fxUOhl zv$g*K6QG0k0sV_@xP$g-@Tzf-md*bF_`07?{e{0{{UywrXYB9cm>Ig5ty~lT0FT!> zWB&jJ4){BKMm!0E(-+Z|vvrGf0!p(*76ea5wj(M<4q& zRJHx1{0(;p+7nFG0LMmw0sjEd>S#Y`9|hbDrk|+(FsJ_j6})NX(>Ov z+mGRI!`PQ(UJ|qe*f;pozu{T<3;u;$U&+75v0LNcgV(N~sQ&=pl>Y$YsFUO8gJMnX zWvFq~c`N?_#a5h{huq4{)w%dd@Y?quYw&HHnCZATANciDli)|e=~%p<4>aatFiBSb z0OG46Z`y0XFfQlQbxu0s1pffy>Z5+vKMmEk{{Uuc=jm@B{{R z#@W6YX>2_TK0ov-&DOtUU)h($c>dk^C7|7H`@VI%9Dn20Kz`Ss4UC>x(e=Fg{hB;~ z=ygB*HFv|ZGBkZ>{{R8w{{Z6Z`7u`NMv0l%AFv^?Ca(b4tfBr6+5BwAF z_9(YGgZ5_dqWvtJ{{V}vME$FN9!bLJ+RjJ)J4gQj#nYhusy-jjFOs@8vJb0UN00pu zquE#N%{iysW}o{O{>nO5KeN68_;wt0^6gk-{{SAXn@`!t;9Z73$MCyKj{^f@ILH1y zSoa^bZ^P?pgbkwWOiy*Tk01ISPcQ8|;7Be0()u>9w2a_e$DjQUui4_4aeK_xxc#4g z3tf%3KMnNP1F={C0OG2>&+OCi67hE3d@ItNV;E$A{w}$D&)Zw!b&(=%1H=b!UAB>b z@ydliZeN8GC}i;Oh{(<}_Bg-zr|16wAE_Vq zQT?1Gf$gODHKH_bFzC>^1b!`9x^L}s@Z(d+x6w7RfuEk*G5-Lw)}^2A8{n(e-4>Us zDE|Oz1pffy>N#uqf{dQVUXS|`e#u@R)a2H5Pk=riw7QgHN#KG-W*_(6Zo~UG{0g>| zO$WmLEgW=UvVZYZhvVOalw{ayC+QM@@l`xu8oV<-vs}fdY9uf4`6-kC0FP7TujWgu{W#ocuuY)=(vYdu!-;ex$zHz$$0D|m( z$CfgPFMJ#E5-0xacyc`clT&T~0N}e{vK{74ufy+&QURWx8jGK%XQ3+3xqibFy_xi+ z-?FFd=Wz+QpWyzE$a<+q$Nq&_y07dX`yFeH8(00Ed^d7I4EG9k_U1te$uDaOw?RWkOSN{M6;_*c7b>dIhyTCW{vh8Uu^nWmw^vQ0O znPdL|1bP1ef?8>}BJ)rEf&LjmDvSwY@Zzfv#2Weif8gfd_$RB}3uW*}#d5@FBf-(* zA74J0Kj7z2_$P^!!~XyXPsMzWr~VMs$NrD6L;E)~OZalsuZ+0xBZv=B$BW@q+2Hh=ihyNmmmBb z{{a601nQFItw(}>Ee28dkAhSw{Cw~zaXFZDavZJ>x|S?(oNL>{G>fIk}i$o~L{w1422d$xbVFY#|b_9Z9(03WPBiC^$^ zPy7>y;@^R_4~G5`@ZZE#)OCRst+bl&gcd6`Eh=Qn{%dVqXPh2!Sp8EIe|jvJQZHG( z4~swGQz3Z1{OE#D9i1QQhg+&6jeT`;>6RbPHDcodKLcG?}CR5>}o@C^5K^!-nrc;QMf2o z=2E?wXBn&R*YXU~Tae-pWjJA@{{d znq|V|ggbNJ*177gO%gOtJhwCTmTQEGV`8COEsEp*AwbV=t>)wb$i;Uznr*~?d~n=k zcCJUp`b^M8nb6}Kn_JIq9k$S+Gn1aY)`h%#PGF3Acx_t4jjqp2(|fxn2DA$Q2xtS<0bgj5o}7XSH00TWMrrGh;o#sBUcR@e4p0 z>~<`B9M*cFwb*nF5bu^efyXta_Dff>`$SNvIO;1oyxAmZ)jmcUAB9}BxiMLhBDM!% zOp%KKHFH;1#9PzyN6pp#GxdL& z{G$H=!8$){EpPT-_{pQ`TD9E2@Q!$wOtOmV)n+C=%?wMpjPP<4@s5@HxA7mrR(>Y% zt)Gmv5`xWe>_=q$(FFX&W&u*vfNu=4y1dMPLh*0*g(l7WX2km)z z@xS5SpTYkC9qOqS&W8ZEfgdByPZA|4Y<~vG`4iwkszQw|?k@{5}YO6!cmrT)v+uOr@#m}9UEIJQLkHq)$%W|iF zc~b7r`##mo%WXcXBM_{ljo&{N-$s`z(VP{3c{{S4w>MX}v6VPj z&g0KfS1vWQx4Tvr{$K-@$)=qr`z#5rW*c#q!8s#6s>Pn2r(9~NwvwH@Hx_<_>svVT zZZxSRX6qVc(lFFxW9GM2HJ9S4O|`Y9i^H->esyIXImLD|hnC7oFIqL4FiF~Y{#DOv zw(Y4|iB7{H&z8;DdVY1uQNnK4Iw?uUH|TLqV|Lo)St;?eAl{ck7IOU zE)MvY_^Yt^DWcr?cf=6h*xkMuE@SoNR|Tpy?wxiZdz2G5-Nts;s{9d-?(+W518!xO zM*H!PlwjoNJoc`bDJVB{8K||dL-kMg71V#UJPmf!@giIupmki=(3*X`QCOjM$#6+0 z*Vevv{f(_+(0m^?0q^1zbIavtKh1iIb07-}t|OHQTP89$O9bu;?oy8(?%) z5X8bXAH5`X`d78k@zb+9oi6L`_TuhmBumO?wKrPVpwRT^l1Vvk^#+k752nV@$O0np zr>$jp(@&RDfnbGLhZzSwD~7x|;il)IhpA0V-$iW;#Ity_U4ur`XJDia%nt&m@n?=~ zyeF;?4NDA?&fq9?8P7fI&Hn)5Du0EVlV7WyzG2?LXB^hmua3Muu1tJL@x9EFrqp#% zer~*E*NZB$g!xvi_I;dX4s>)HyG5y3PkH^Ju*L!MgN%-q%-wjaP8xdcUn!$p`HFcw zSIyrL{xpBWxcHNMrFe(Nr_9tG8*6B{9iWkb*#Hm1y6+i$Sbp9@hcj&ph& z;$vza%_Gw-^)KwJbzRD>=47Zf*}sXORnqM*)Bw>k#0s8sk&@DvJ% zA-gxyx_^V7IzB2=P1=P-4yt+v!T$gOT@~?kr7l`;R%JsCQLcF_SkM0ef|LHtP5VLo zEwJ&fjEHqz3TUNi88YPpup>Y4AzveSa>glaqPS~BwDMpOta0{~3>W(5zfZg^bEo*P z!M8RtI=N&ia-enR9DbGXuf(r~wtfTn$EoOc{{S>Ybnq)T$}-K?ye=W9d@O4Q3|`#tDkI(e6W`cU=7!Y=ugw70nb063U&%v#FfclOu*lOv7)0D_ZvcGJZl zv#c6r^`M$1@n)SXDH+DrbDyZMi!Zz>1V$}u!_6(UR^t*z2MzdsHTN&={FBM>3vH{F zf9?A`5&r-KuZqR}+*YQ--d*d~WpT}C#oFeU{_6bMuLRmTJ^ui`FUc7q8;kczEmUNm zn;oi-w({9t3t3~xVV01d^|fpd+GM!X@B`owPad3BABYggsS6E8;14xPQ}5coBD-+8 z_p~LmGm_@+DIU`Wk^bl=xi1iFiJ(OKMzpY4^5P?|l@?U~!F&$vE`<>&AS0qFdfV&*nTV%5caA4;A_bQ(BE1 zSow_K3sST_*!<50)I24suB2|ROL-*ZD)b)pub}C#8!D<95V0)B>st3ZYuVhThM{#S zN9fqd6=5_h3tvC%I>`OpL77f@$UQi(&@i>-I!OHI8;6xTw`8|EMViE>)-$&sGpha5 zOZ|ruK@66kG^M_L^)&Ae+G}iWIpDTu{iBpms&2NHkubyhBRWJh6jvQO)k}D zoo%dF79t?fHxvrOqZ%C8%bv@1x!?#-h0ETa5m8V&4r{T-vBn~;Q%SxKU{>a=*U>SD; zjtAYQUiec>)F3v?Flh++gluwy-m2+(9p;fI+4QTYlHEZPZ|R?EB#Gs_|S(&N?rpQ1PvtaFo8i24vbY3iPd(@YT$Ahgr0c2T_u)d({D`Ey^1k)D0_h z<({YMT(nc^ggV%#t^MPq{ zmXTToIXj21Ps;rGNz zwHRd5bvOW=p*~sZgV5Hlikd#5r#+^xY^9s-n0X(rD=%8`-kYY}rQL&w&j87j(zt5M zomt%;ohdqM#*URWoyEgh-r5Aq5;pX%iW^uWfm$1as=TZ~b*?{5)or4kqYBb5b_1H} zw0&OM?qm-Hh{NueQIF?bbtzQb+uG@|wQ7vzZdts&>yW;c)A(1!7rH!5Z8!$0!QuUbyi=Gn@G^2We==QL8Jxiv7UJD2rsMomIDm`S`hMI)tf+U{VA zOBmSsYzWwTS47s@zL|b5jAQKh`-J?*@ab5-I5Enlt;&P#n$nz9TQW~mgn()~Jd#^P zKu2xcbN+oP!^9T^uvAdsZO9yonXeMxFL0h`@cW9TVJve?JYqCQBjhdXTks^kKZqQl%pg36%JA+pxrRi-YlDC_N zjzNIO_<8>T>sMc)=`w1OfgFT@Fh+1|o}6x*W;Lhm)y;^mUhdP(3zcVyan>gqlbf?HY6%^s>zUu8 z9kHL{K9tF9?DYuM^!uMKLHDButzW@qt1{i0e}5X~M`A~JdX*fSo?B~Q5LzYfoi^D9K1Sn?^vxeulT-6xOJ-(-W<;5I z{3_m*mUk2R%B3VAs7U%zeQd=!a8bYTAjSjE!ChggO^7<{uNF3FP`aKyvkN2JA$_d8wK~||X!j_( zsoNCAgG7foI5^MaitYRxcFgd-f`UTgKs$=33Bp|QcG${wFAWj#AH@wd^cTL;H7gcI ziI)qvg>G|?(z5jZa@Nw~7%pUV-~?<}(x0}!!PnGuJD(D0cH1YID@&i@Zv89ew9@a| z0M}tzqd59vz4aK>ij-fdQmYT4Z9Ea>L}`s1&yVNphxy$#L1>BrqUokPPC zcy8sQ)NGB#y~a?2zbb=YB>vMs2G4>uc{S}iIDW@*HsxgPl6dQ1p>x{kdj6hcy%LWy zBb}-+I{Vjze$l@Pqt^Z%UFg=f-ebZVOKBO7atR|f_Iw|nRKR97lhOUh$Z=*#4AU-_ zp7d6S!crI`kVer2Q_y;v(9^G@TUWEah%*nnp4ASqr%IaB>6ehQJT7_cG3!qE>w zmn`FGAaun+6|zeY*<%=GIb+z0+VJ+11o92-j11t>DQGtav@!lEX|rjc34BE|$@0sj z%aQn3$hGzV0LKf5Pla0Q1{*)Kq)-zSd{AXS9$0ekK0^ z(T!CW+UDxvWSUF@eJV@)87}UoD&S)3QZxShQ)ZajrUanmVxjYHjU#rfMW`xTxm1&= zsxieq#G4_AjmAh(&1=D+!sJUBIj-f0ye^4Srx{zRn-kl- z;uc&TwSK{34HP*0r;5Jb8!>H|b@df@Ptn$ACDV|(HFWu*9fDV9cl$ADEv)#`BKROq z2P529=x2a*qiLcfFs=edqP`UUmDO#$d2Jd;oG|J2ufDt=aWJνdgK;AU{OM9%y) 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 pDvuR0c~Z)CG{$O>U~P2V;SLtQvV0tDj{F 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

    8m^u+ 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�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%0Dx%Y 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 literal 0 HcmV?d00001 diff --git a/object_detection/g3doc/img/oxford_pet.png b/object_detection/g3doc/img/oxford_pet.png new file mode 100644 index 0000000000000000000000000000000000000000..ddac415f5ef079f8d6fde8dd4c9838735fd96325 GIT binary patch literal 276715 zcmV)qK$^daP)00B)10ssI2d2tG000961Nkl?Q8?HbBAq09rcUM*B8}IN9 z5#fH0nR0RbyYHbtNqKVf4l%eNw?R!+jc}kEh^drUr}@=wc~v>L_0Y0Zi>?Fny7oHR z^8W43yZdkd$)E4}WFP}dB_zyq+^wVE{`uXS2PrZjWZ7OI;hr2XaH)h3ZE!1P} zZHsi!SY$*PYr$bIH-~a}?xa?!!c;xuO| z^x3A_=Q=ONC#Pp3MG*n@-Y?paLXT)Yvl~E35=nP=FC<}<)H72tmq~QNWKIW3y16@K zG&x~r5+kDbE!DiEwRX9zkB^U^KR=!y&ySxU9v&XApRbpvOKc*Yq!mupr>P!J$9kNW z*T>`Q!*YK(-OPu>T)j|HYn@9iG*2*Pf;p!KkwG@){zHhVc}~kPrSWpPeE#{TkKcd) z`NNOul;oh1gp&>2!mIYOhrhNlH<<+K$e-Y|AA5+GQ7M307(EzA`*a% zBw^zwLqu+p=iUACPyYDTzxcb~{qZ;N?r#qPeeSuXny?b9bjjFSM1mw`2CG(`W=KiO zKq)|M?YeDSUwgas=a1)4A3l8g>!%MtKW$IBT{i*lp!xmXoA-bF=I+(4)$;i8{PT}L ze*XBWK!tUc7T$t0?yU(Vb0(tD%HB$7lTB1$Qe zOh^hwB_u&Ar2vo;08<%}JrT(cMe60O0!RpebHASBijg!kk}Q#g8LT)9>1IY4o%`Rh z;w0f9DW;kAEfAnQSLGT`UzVlbOv|$PTsr%PRoJMe=$Dr2ik7)anFe=vl36UF*`uY10AOHH(`KQaD{>k6`;m?0sUfpzS)r)$tLs*T>K!gO7SEyvB zQIU4OuFubx%hU7I)6?_wx~=WFE%R}%$K|@V$A@#@T3?^~b`C_Hr|3FAKQHBYeDn3; ztKZ@7-fcb{4uviWRBM;(^QX35`{zsV*KF6`o{_6tPIH~6sVt%TgzNz!D#5Kwzc<75^G7O6%naO$daW=yzkF=cSgbA}`rn4+Tj#B2x)O z3s{TnAYzn5-gOS*um86gu=tF`{#f1&%XZV8|?tz&#p zx8i0-hnbNuhZ)UbU@6nIOg&L|ETiv`)rU ziW%1DmQtO3JQHqp&+9EH^zq?yPwh-)j7Lxs4K!J1LKdwN_7c-0B#1`aR=_Zw>hk*L z{`KAQFdLA)uPs+?^E^*p(sg=!@>ernm7o6KfBoOT|0MsJ`uY9)chg;J>NeX_sswj8 zcc}Da3QU}_tu5N7b<^4ujd6{3ZV!E}-T9<#&8;IMwsrh!myll3>l9HD?i?yrPt1WkbI?;Qj0AH?Nobn?v1} z8rCjP@)Fk1;d4P8?!WrOA8)?;&F!1t%=6vl>AAItXf&(yyd0UCt>Q=EB~P))EMYLp%?wGAks`!UH(6J=v{8R26`7Hd z%0#AmX9goJG{G0IsA8taZQdsHl5AUyC%QKUdLrSNPPMdxm;uNE2LVx;8IdJ~E<{2~ zQ;1-J5nxvaDPxiKh=xpLME0JUIcl_ID!GTsmncxDNtuV`_TBCKcen3u%E1FY6l?E2 z(}_dzX(_T2=EiE2q_P+6qu?T$kmdNC=slGrlY5{Up)7x0SGxPJyf*ajYYO)4B_UPC5QWnfqiW1ETfi?nUX#X3*Y>=2TmP8)0l#c z+^<0HUlc-)_%4kUWmF(V8A5=Ppa3I6UoPOw%iuq`q>_|5@}&Wpv=q$b9B-EtfMkc8 z8OY2_AD03E-O5swK*zd8Td$XOt5%ogxXj0?E(SWSvv1KEo_Wp8YX&JMV9c=5lVz7O zB|%OBDUvj*3os;zWC|gZ**E~HRGLAG7LZ_u>`)3&aU5?B$JaNf`69 z+Iq=o1{k_+Yi#S$3k_1Ck|0<1tD4%!{QmNM-L}46u9<0OWuBfsfB63UACI>;Z{NOt z`|keD+uPGksnh9nIJWH?iHMK_oREEV)o_OdW2tl5)Q`9Ro8Nu?cYpZi>)X>@C#-S# z^jwNPpC6+X%b0HNPH*2GPxr^eonyK_KV6=lE{_kbU+d%+by=*1Y0I@=AJz*@buQ8p zLaf)xy-t%+?2SEo55Xcrqc&+U^2ouu!|92v~{*X%8*EjlmX#P!EHPy2@(hqmLO>fQj{PE0^xv0_Z&Sl zqzv`U%v@VU8(E+LEiD zKRqGCI2=yLX_?)n^}3xep3U4_@O-_Vx2tGI z2Q*J5DOl8lN7vK&WBY4NpZGtKa_ao7eAdr`hQvL?J~3X_Es+Q)Xl`!%)jNUrlendH?7C z?C<{R?|yp#kvu;>efO7-pFe&+ObDK)a`}%xpa1z=bX)q?ub)1&b-SL=+vQ9J-KRPy zgeX8H zI8Hv4$PwOLP=v&6LIX3UNKm<@By$)Emr(>s^wmO4RT@bn44}vi0f{D5m?TdGz(g2G z1QH+tX2Q(WGY9mnQ1*Z_T8mz@vp18>%nO{=DML-yw#^YODLb=okx?qy*4DOuecG18 zx*nEGT|tk~AQLNpoRZbZp$y>07dYu9zVuG_Y4TgDa=ytsQ2y1SQBoW(G%mQ8iOyL)wce2T3F zqPW4Vxc45_%dyTkryH@Z>xxmIWcCOc-HNA8rK$%CNKf_5P!dcVwK$lgc^R>LROT_N z-DHlyKC+`?U@U}zzr^uhmF-`CCjmmruF(*fEOx{L02u@Q2S5{mGzXF{gr> z26s1eiLqrHY3Au8Nlw!|O;eqxV_8l&M*!P)Z5h|?+Ix?P@n74UX>m+e2#)SFv90id z#X&N_P;0en_it~eskm1d%`6iJ&eH)irnyX0U6$!|d%U^7sdG(2t5b@e&yVMS{XhTZ z`grZv{P5w^_uv0xxvl^C|LtGieSP=z^ziWGr+9AL!{yvBpFez74~G}4uIgZ+bD}%v zdA8zfb|4pUM$Xf*&eNrB#r>-{@7;addNl&L_F$;isUESOFV7D@T`#kjN2m4T$!x8) z)+Cys9^)~IXg#*j2x~|~DUQkNr&l*tSNU;oP2ENvTjmbO@v~#S|j-GPncK+NcHL?lg+fz(@D#Gcywmh(MBzz4jkijWYM) z>aWZ8fx^OsmzE(DJvCB&lh#mL>58sMknT<(GBf)YNLdC#G`eei4v>}>p}6*bS-0!D zUEB4%uIHW!GnC@B6v@lvK2?cr6HE7!F=auW>Qc%CZM}EMNTY1>$ypS*>T&CvAi6=N zCzM9>xwuPNO@c!`6ig9hp+?%10m=RAD!Qamz?cU7q>=B&?@QoI2A5#=OCtYIe#UHM z^!OQoKuchc@4s|lCX&&{uncKtw9)M$5cEuiVpL-ZNns?GSrJ=%=H;2Zxjxu%SrijcIJ)W=Y`66UQMDGB)fJ7qT@JcV{GpA)b9On6Wn2wXXn;FbXDJC0? zhpEh7i_gE zo^B57wq4e9OMU+Q`SIa-edy=Mr{_;k*UM$QZrkO$Ubl?!n7MUd0fQ3eoQ#&a)Z=#D zdL%NUUt4CZO{FwNPXtFMCsPip^^E5p?b@ULy4w|CfWwN};?uMoPs{D?X<3%}_INxk zDww9SEQ`CVYncwu*N@K+mrp-_{`tp`pMQS7K3@O&!w(-me7b-8?$zt#+c&qz+v1DE z$Ba!|Fd{@oE#+^&{_UG@zIt_gS6jcFpWEZp`tW&yo}V6ayP|tmpUf`n`E)p4o*ytz z%VbupPKU|M+}RstI!@VH&t^82jT@;t`>9NC{B8;6`O*Rj$gPy(ib%;Aq}*95i^I*G z<}YpO=-S6WqOH-1z$oM;4{3dRK(u`tqUxq)~`^4Nu41X1IJNXPh6` zJSUQV?LE+D?m*H3JO^1Q}zjOcG-#^#!U9DQL1W zKBY;*fKWhYays1J+`qlOuWS3^AO5%h?SJ|I{^iH-n{!*%EmoNX7dZF~M0VK=J12kD z6KLcf8c1+2xRnVEv}KefJI-P-dD@6iqu#WAhAK1L?>lz%Y_CblWOk}4n{X8o*u)&L zQNG|?E!x(z<#t_}Vn#8rS`Hw+m?G%KB(W_sBkQHW8#a04ib(D1L>s>xD(LXG%c8DBx3~?T^C9tkTQ;g<=qd4*(>g z6v*)L$Emadka6%>L=Piy(dwkrtXTHAwsl?ik@d1H%d*VJ!*V*ldG&Ta-Smu~K0bc@ z@h9~_SkshrPgUKb*alzm&0^8%!k9_G%scHF`*gZu~;>hI#00TFeLMHeE7>x z&p&;7`uO<$haa}*_2cK~$9C=B@8911U;l^y_CNbqf4;e$A3r}_o}T*_fTvWnyuLeJ z>hs;J+xAVY*S>Cfy>92{>vrAx^T)nk`}G_VN>V*q6zBuvyl$X}czyf?z#Y~Hz-0nk zYvv@0Vm-HLX%(enGL)&7xgL+l)9JK)b)4q;a5&Uuo~Bw#ad(>Xsa>yE3X(1l=R&Kc zeEj_AbGf~}y}!ME|Mj~cKYaY@yPuyvJoHQZ?z`_keOewq-M)T%e|mMCZ)=&1ME0(f z6`Q@3yPG$6%d3iVdH(eD>EqMG<9dC*IV}D00~WrTkF@^V+ml;-`|hCaA&>7>a9nP! zPNk;OKmYuM3;Oxm)-@YYoj%c2YdK64I^w$Re_D)2APXqdcNE-mcdcahVT`JF5aP_X@5p73x-t6nhB5*9{(0JV{(OEcpUUm+ZE?R{jwVf6bdCbf({exG)M*lPnx--@ zrIb3CX)4oHYAJK^sa6Bjlr36o*&3uC5p8>T{waE{>w39d*4D4<`tbbpbbjtV37uY_ z{`}AX$)EnopT7O(t6J;l%kzi7JX$Hyvo!#*Jzt{t^>V%Dv~62kTV|#h5p1em?zNWG zJSbcq(b!s1Jml7kikHJQ-T2XFhclF3v0gT^AP1`_>O3Vdoesx&nWi$$WjQU&GS7=2 z4u?ZIjx5=g+6mg3$#XV=N}!=~kL1Dzf_$#xX;l(|(Gh299sB=Z1@Pev9o7#%$*iAv+>g0E^po zJyb(FO=gSJXnOQ!tulkFp+)v>?a|lPZ=-Ff=L~8%gD@JN^zLL<#@cAUftlPtoY%FAO7L#uh;vQ);peQWR{u$GcqF6 zGA!$+m`W`y=&5l*va9tFKxg5&+K?g!ZidW+DF?R^q5!juHezUI42b|~nHdz4B;8(+ z8Hr>qQ(7$M6_!>vtCDw!^OU)zGJE!(?V9U(y-~N^N|Wy{j2BRg|Q9hkFG8 zS!Ao`QuMu$809SyDaZ_fWr#aTW&E9lV66M_dGGhW@L6BZEt6t)I7#zXb}m`AQC5vP zHPki$NPc}F6^(}JS^4fBHe!O0W_wHB{dDxAzLS=hI2>GkGjsmr_^=lOKB zWeR6NYS)Ci(Dj9%29mziV$EpwXkLRby^=Vxn*M7b`yq<1u4&_i*Y^9Xa z`__B6T9$I-wdD2s@rR$BhT_HnOT)d+^Sms_2yO50F(oyK_&!)Nj-yrI6iicRs$pjPG(svlToa8c=1xpG(FF&4&EPoTq3vZTa?T| zeXiD<<|Z`eS!JH;yp(EBh-AVDM|HT5zA)7t{=#g5ap1H`5rgzcuw{t8%+uZ7tJ~x0 z{&b9|>*t4`H};&E={B1B!Hdt#F#U*g>orkJvR~`@siV3|0 z`XrfSa;hC}9>7^<<`)tqU@wr50JPp%QetulHOS?Yz>$z=@ zt@o`s4~Ka^)UuVebt`3tU9a2udA&TZ=co1ZcwNuULa;BaO(8QfdkIo=dW)R1Fj&ewuUibwlPvFzjqj{O;<@Rv==9{ma+r!U4v4&iePnmt4x;uP- zi^v|P&8$QR+~|Yrh60;PJxp^RZJy`lwA|d@-rc>rzrQ`*AC_gNVqGuS^Yim#II7a4 zbK5%N9HFh}*0Qx&TU@W(<#OrImvvngNq`wZM(+^;ijz&tR3{^;-jwFW4lSmsOowGT z&h@a^GGR86J(m$5dPaLuT>-GFsUDW;I2SJtbAU`?P&fvnmPpV!gc;d(>4*b_?HwC8 zZ6*^7$)4F-RQKck%^wc);cl64rpFJ@Pd{CL`t-xv*Sm)s{H`9-r_)VFmdtF?=fi2L zb)FBMk014;rRJMsh9UwI6egINm+5$Vd-wXCaao@(yf$yGt(Wuqv@8=mg6@e*ueK~9zxz0#8}{D=T|a<9=$o{-E%0}S#VNFiCkG?j$F zYNn1DE7%AwX{S)aD;p>0oVdup-8^W%9_TQ1vS%f4n@ zqs-k)XYpDq3Sh>f?&cHVgvsQjRFWBS&1|U{asZ8jf-gYFT#tuxyqgc$wx2)z_2bje zS6esFXz;-`2D+f!XVO;O4XG70&@u)HpT(jbDx7^x}KxgigLTRXQ_m5=bIs+3d}RSXKE#6jBBe(CZh)B^UaI7>4_f+k5u z$nXNKSm8^8WG1sQ26rD3+B<+jnZqbU++LXV{7>Wii0zUFOmB$PV3X~%8bCluGBUp8 z!=bktFvfUPX>|8mrfDMWvaNsl{<}Z^@t@t^yqj-NKQ5Klm)rStf5+qD-Rt8r9|t+4 z7?DzBwGsw>5h;uomNc`4WkA*A8zGgK_4)C+W6KyR%mwdVZHs79#@3^4x%J$d*0o(P z*Y&zuZ`n4cQjrlF^ku_znh&RCI!tvkQ$oc-Me^LO_;i?-<2>CQ%VEM)WX!S?oO-sc z_nsjsVRQ9b4z65d@g!uzdTc z-z;wqKYsUPIX-^+>%+t6kJsm`q|>Y8yKmpYR15-OUYF(AFYEcb#dUMY-GPXJ5r|Y| zf?mrs-Svj(Pr6>BJzX9@u1`-(T{cH(D;e0O{KirK8pATG!YMa*_hLZWmr5ggGR@UJ z$|$2KFvT3hC4+-56Ub5CK@M^pD|g%V>j@+%$PG-lLW)LOn#l}a&73fCr)HSRq+J|g zbTg$uSY{GApigGfQ&7PqB+&s%1g&LnO1c3V%0QaiKx0OSKc18E@N_0gpQ@>JFYXbO zO0~J#>9AaH+tca!^5E-XyMAixdCfI@_TKw?S+2*!ayYPDODRB>sah>o+?`$v94VC& zV7Nh2Tc56%OYeQKV8^o?+0Fd89PdvzC(hUBr=LH6{_*GUp7MFKwe@f-=w^o0F0C0? zX%E3Y%$SNXK6W_WOvsF10`$)70C$KAN^)qZ2E$sWod!jk4I;;2&gdo3N9->+#6=*5 zh=|L!ozGXdi`h184?soRw#(Kpy>D@OzCJxYUmmZLN;P(9>vgI95-dd6^|G#)tzBDN zduuIPWCROd+LZhiO;cc_+q^8kOkO652$N`xNcG-Zw{%8iN4lqT(D6qG>!rf2wrkT+ zPeohX8hy;@Fk}nTPTS0U!B!;gRK+ht2@Z<^1IZFFxtrRWOe5YE0xwU9$mfHZSSqr(S~J%@-m)ZGVhge0e_8W<6u zo*&=*=C{}D;dJu^dp?}J7JG9zEl)A^Zp}5}qFzdIuv^YHMZ}^=Ls=Jl%W(%V(hGBP3%Ny9YzJkN)_oB23T zOQ{om_;n<*M=#yn%UtH0Wx6{~hs9=x5B+sy_MWvXdqxNe6?3y$A^(hw@pG ziM92DNK+6(gfvgi0A`>fTBf2MziP`baS&*(1t!)LkGUow3!_-%HUB% z4_1LykDdbHMj%5fV6qegBz72*k~2*{hN?y<&21O*n?c6pPS}HrnbI@66vo8plm?SP zB?W=Bo|%I$LvmMB8;1yC$b~SZD7KFY%|@&=_mb8XC{CZ}9(r2NtSA#}bu(N__RlT0 zrY+iey*^&&9nrG4jEI*h z#4v04h2QZ7MbNJz&X<3_2R53N5~JmI*`7XMuobhCLx&p?+1lFs+ShBlp0Ah7C0i$% zS+>kWOqShazLp8()r` z0PNRopf^5(JDkK9YSReFPP+fv(g(yZId#9{Fi9}wo^X%>+oeb`ik6`dndcd1JB_ld zUAOE~l13;E?&daXUNbj){`}$g=9P20yM6old9}z_-SKa}doZ(UKHc9huWmz`+%gpr z#it$H%$;A*`yh!z=x|u!aI+a--#arigfEAx_pP5@mn|v2_ynZ|vbA1t~XUsy&^bKYo7v`1$$yd0j6)`CQ$XNzpTtTkFry*N2A(;r{em zY0r<(*B)h=Ejy*jmXdS7;JN3fKI-vZ^kd{A1yKmII@RSgV@2=Tw(QxPw#sT2bc^VF zfx(gP=oBc?pp+pcmu$#4nIn!+FMd{*EaCv=l@#DZ;{v$SRRo&Boh+2);N=)`?pCbq znmUC94z?lSgfq}d8~!yQp;UpLJND3TnhQc>WBT5IcF zlWanrWCRqnyk7izxwJq>T8X}Pk?y{q=}{S(%xGguYSm1onz@VD#1@PcXrfeEWyr|K z=cn_-Q%z69#)y(#V5)$KG%_jC!6eN@2RaZ?Y+b}Y+lb8U>VuPNF9W(?2BAuYlo8Rp zH0YS(C`jBS@dc~>f>G{`!pn=`mtTN_dR(5bAKK54{%r7s>PxkaKx?gSy|vbR`rtff z^tSV$B=te8&R}7goB%>dnb5EC#SqP)01PyDYKA<+5Q9o9LgofmLkD_9S|87(B*`(j zRJB$Zd+#wU)C3kYBU?y{7Ng}F$xH-{DmHY;$lxyM8rjZY&>DHIfXO`DMskd-DP;_0 zDS=%AW-Mklx+IPwy=7}XGR=O0*T1E4`HI&T6e* zN`l$5t#!L>eQOpTDfTR7xSsS?0xh$K%{DW5T~DiMA@X>?5vcL^4(9X*w*+ z>3BRYr<>z^oTp=*4%1XDdu-=R?|r%o;~mZ1-QBCx2wEDMFvB2MD>Hx?JU`kv_^eVRfBNXXJ+?-l zlRVy>u2-Jd>%1%nbG7XfVf_Nc2w`wv`)0+h7R|uHYMYkja5x>Bx6-0g;R%y0`;LG% z`oM}nmKWM{7M27-Do1aK0KnZkj$>YAEVA@8GtpB5Xy|4z+tnHRnCPA5`bn`Dn!6cY^VK5Ii+|GU*B~Nj)T0Dyi zr`cj;8_y*?TOqnmL;$x@2BvoxEV&w{&=gXtzAz?Pc} zD5WXg-Q0QxKy#NQb-HVC9x@rxk-==Tf-GgCm)^1~-7DbP1HMaY_J)1@A~kY=!G$j+ zdH!C%)NKb0hA{*vs4Z+@r&3sM`vMC z=TeT<7I){YlCAf(Tie|h2YBHm9km!qhZ)>dD%{LPMTFFi^ucZxX(**kUc%C8rZL;+ zrU+#Kl?6sQqNHdM9T=~jdnU6X>NN&qC_8rEiH&P9V5;h2W`c#ByAuy`Fky`n$l=gI zbGf@OGkA#zH*0JWkzEmkNb`UA;Y-o}g)}sRkQLjF9$$#7qLT8tl;g6zn(A`!GHE=$ zTdwEJdfDQ!XQpMW*S==k^`*pmS$+C>IxO3#pO^DvKh@{rwDNdN6(W2tk8-%O?Bp~ z#Z8A9EX>AqYTWOzK{}GTITS>ifyD$W6SB0hT6#;kd6^=@t9|p$R}Nvub?cX>R(|^F z!{gK0$m_$?^AA6rA3t2T^Xc{RaJW6)-QL_B+w!;hBqPSe2t{1ZcZIUvSYQ-%P4YDpWmbv&mSuKvKZmm(pqZgMWn93}+qxMNSNBK+u z3W!uPGc1RiG-b3LB^8Xj*xgXgEK??zf>vxGoko&rq#+=K26`Amh6+Nmo-1PK{K6p< z&C&!LFm!ZDvIN}ga77rddx2mMy-ASe1%u9HQUR!x#f(q^cEc-@KoWhLCz@79&z$49 zCQUKD)nb!3BOT^eJTpsiJ+*)`#7=ee7+w;nCa~{@$y3a?F*~alL^JFpYnsW`attB7 zx3y$-TAHI2WM_1EWpI${dhevhLGx*k;zJA;3V_2QIrm8^a!ixPcrp{ie}DuKzr^gF z%v-LBC^Wv#WAg6fCcoBP*y$Gdy5 z%1kIq->w_;*|C;9Z|j=tI?h8x^lUxjbh`cR`)~Whzmt z2oex2*0t4b%LGf7$(Q5oQ|TG#jcztoOhGrB3m1=~ZYeW2n7W7%8wTJ$xO3VmU(}f6 zarnVWEnsH;Vs*7!+!+!%{wD9H}CaCN!uR_pel11GA=7yv; zeC(ggU;pF3sVGRS*I3tf-p7!?R6*T#iP9wPeaEfdNaGBsZ^ao< ztd^i5Vh`d3GXW)3Y)=wHJWLH&hx|pHxwj*NQf4O0GHJQV!^vu`3T|tk&%Rx|R_p6d z*CEps>QZ3}HOwrvxD#`!OPvqyMH6+b6$cYNo~}$4O(V%WjBr;O@JLTl*jnpb zYptnEx#d#gz($sY7bXu@r`z(nXYaACJ=PYKO*YMk!W`8tgI~ahoKOiS0RWPnl%&iRH?^WgKn3_6-1$PK!t{lKbng-mUB9^7L#&=9VmF z>FwgfibsHCj@^?07$XrG!rhkRvP|xrmn!9TT_-z~I{SP`Se>VNn&+F7`%z`FvRG>C z7c8=V>+P^PHyO>dJ(8e2`*wTu|LULpGf@Bf@Bi}n{M7q3l+(1VXwPf@(z+q>VmQGU z7SOMUF$1bIv;Bna#T@vO7*k`+r5E;-+-9#RHS1iePe>^?N!QIIf^@oNQOprjY<`8^ zps4lhXl;d-8SY+dbSOCjO9u=wh+s%$WcG~A9Cf;~xGtlhF_@!5N~&A0nDN3^c4wvB{_5?k_itW*_4<8YHx-=J`WDaU=i=OYyQ{@Zq0h@r1-&eXX*t;1Ej5`Ry?k!n zBscWlR|^0}$*q-8?U^XsWzMB*YTnlxsA3H|GV-Nldy#xv4tjuE-;rI}4(rM!1#)*+ zCppsK2qWGkC1T)QgZ|M3#L5h&jgDZj8*GG~42&781SW?CDXnKv8H9*LNQ^|%NX9^D zlt9XIxHDn*s|+xN7^>CG(S{FvdRNuEtoMy>Neo20&UNlfNU4X1I+R75w)6IsE2JLC&Ox2YggU$qzi34!t53(ne4NdlWIdL{aUwdS7c3A-| zm&pyZJM#f(Enu0gjW6BXzKg^l3t01gLhPjinfdtq=+2h0t}SJ5o|&z+%!CoN>eF22 z4cAbDXswO@&dR>#Vi3_Kn3+5TBolN&p>8y6t;|c7vPNh1xz@gQAA0K!I5T;;xq18b zH(mJj-RIMC?0o|g^K`v#zRa~&Td#W|{0kZb2?5mO>2!a8c=Ot(3Zqy1`0(*`ds}=e zWukfPPW=uZn$N|inQjv}Ez!v9x}bd~eeu{dg~b}p`c&~R|L$+=>F^)^!{3j^Ceu{r zsch@jy{2A{rR^n@UckMV@V9H0$06=x;{x2O&&GWfsU4L_^OJSkxc{l!_RDkeGoN ztDx}>;)RpSKp*|<$VoOVssQZb7?5TH@#1tyl5`Lp&|`-zK^i^z;&ieTJye66ZW}X%4qwOfrES*3DJ@S&9ld%i@UQAU9T8hzjf|!D|!9}BqvP5Ub;Jg4ZgprAkoghZg zwNv~cGzjB^3`a!U!+c9vj7c9gnk)dwmdHWw7O`)KQa8LH3K7Y)sm=tavP!k}7e)!n z=)0M7!@90ztqLJBS|TtY+K5Do5pLbFIzq9()s)PV{1P^(oVo%1-sj z&RqkReMsodAw@<{T81*zUq0esy$|QLfK7FF+tWbrkCJK~7S$%Hwbr+Yh)_2xwJg=A zsZ3_2=axuzbfk+)!pDUhMlTYPFV@o73t3RcrD1 z`C&dD=Hk6?WnN01>)~de57+B;eLA=8D#dm^djSSmESuka_0{iwbMy9XqpsUJ9}f>t zkIU`*!|~?09HV9Ly~^t3hca!~bBSWLu(~p|$u|Q_pNn6N=wv2PN@?5WkH7o&t2b}| z{_)ym*L59={O!7yQUNMnT={m{N&X6#`{ie^4kTvbJ^_wBHRT{lk4-xmbX!4XDs`## zrW{Vwbee23mKT1FscvOtlu&|Rms*a6i)+rtma;0N5(b~LwXusw@=hzTE}Bz5h_o+F z173)&IqW=#{h-8A;+vdEi!nMM0Yy8RPcdj9!$fZE%K}YwTE3Ko$oQg2!^@tQ5!^*k zl#LB-F-^>n%nXIOUG0aVDZ{~);z=nh+ z5E_q`4}DM#$;Yyd)6uU11tLO)IbI9V;T zv??bIXOl6jRnN@Y;1gqM8c<8X0Kz+L*Lp-ZZCX=XB1auPyt;PM-VjKe83(_LMoZFG z^o)Sa+=dKtl+Jq@7(Q}G$qYbBl4)hc|`W*h*988b6_5@7ME)~U>y8EadSwbt%M zDQ(u2xkfuj@B4PZ(fOJ%pxziv!j};vVWiyHdmrR)Gi#Z;HY?_Z(^Q64y%Yxjf|-xW z0Mw8gguHRgESWu#U z_3yv^-CuuvT7CNIr=MDD%UrJKtGk(5YwhJ*@3qU9B0v&CA|&L!*Ewd4TT(V3OvZNy>g-M&Trc>BtXKE_& zkZ)^jQ%1%B+yOCDV|Ut+Jq@H~#0Y!&;qF3FWvOmsQr-srftfkHJ6?9B?y4TgP(bj- zc~U85@ zSbBIp__Uzx?pYX*XOZVB}u;cq0ym4B2Cg4Ciq|sW~P~qy=anJZ*n;DMo}aE?8ClpM^l`Z1+QrhuN|b*kxx=m2Qy z>yvwaV`?h}bn{P5>UAO1U z`iDQh``sV@=FRJ`pD*XbaXHL8e|)~ApB^55etP^oA4){8hZ<5-$85VuDsvQ1 zd?5>tJvI*lN4S!Jn00`tyXt8jxOJo!$sP1v!=3lQppVULCpeC>@Wb&(M*Jt%w!C7<)tHu zQ6w>j&~4OHkanXUz{b{=K#~Rp#TQ!wNXi(?ULz4;f)+3SATcD)nNR>SYgQQApi*YH z4T;Lb@$l~3w_knp?#<230>56bXv(#%=cl$_#hjK}?{5md8gpxFJ{KZDH6H4*6wl0I zy>wZxExIOeJr0vH6T`9^DQ6$qzgUaFCCan=WM0IYv?kfWPdOF}=a@DyV*AqjCb8 zMA&e2GUoo)FG#v)Cc;g%7qe(_UXzSW$6y2x8BQo&U}Vc2{~Tp-GuRfzz)>Yd^cR-RZvH+j!+ybPuX*SisMx0mWA*N4lis}g`i*`+%E;sKcwNt*KYyMN_Vy32PdBgLef9O)1h;SBzQ10tH;0?K zPM<%2zMi*t_g_crfB(P#yYus<&Sl%K&H|Yt``QOBzF(fMAHUzOpMhr63Dw`edacZM zd47yO9}a!1KRo>7tKZ&AEya0ST5AE7Qg-vKp(c$`4n`?TIhp}O(_Y;QS=_5>0t-je zIB3!ak?gHP>J924<2=_o7n^)O&eLHz9+%}Xxf@fIS6|9ZG2Mx>?-&+fwY-QYGPqBf z0-01g4NQZWU9zE9OewpWjS=JSel-{cW9uVwOz4f^e6ottgGDsT=hH;i9 zj^2N456x7j6fy!KFfxP6QE!cw8M17-Kb~1MFP=Is)0_9V-+uG<-K+b_JT9%R8(XyV zwZ%n6(`kBbQL z2^v710j7Q1}Q z8rx97}|*ZDYG^8}Y>-D_$wfC*%E=Y-teUPgR#bzyr zCCRdGmbT@Ht^6|%qm*!xN5De3;LCr zS*zDlSV`zG-~Hy>+qZA-y)Mf!jb%A(AFg(DQWjHI-YKCZ|}R=n>TMhKRzRLnwFn``YBp`_3r)J)_?nN|Mj+>k2iyt}!-JzqXOJ$?f6t2e)c>UTf={oNl9UZ`Pk7uGLPWSR7NP$hrt1Mtq>h7W`!y!rC`dj;VwDWvP%j>CYB-NAE&OAeTlix)8TMf zmZek&5-Eq1z7PCMq3@(>7~Fl0u8594{53N}VnFnf`p1YugW)Ts*mCgyUr1;%CVC1f zU0;O9R;1BvyIBtTTI_JW^TkSUAQl-Zqm0SEyECOmnU&k9jyAJU1_X}%q=e+eIz)o3 zGM4%E8^39d!PVet;j)#MrMGFeiw$?8`(a6OQb-wxhbm{&1;U{6f)WgzsbF)l` zI!9VOKgGJP&rj#)2Su-LEqk3_$je^b+_H6x4mEV6Ew__-CK4N_Qd?n*&O^uE0)V5{ zh*3SigdFWrM<6gH$T0{Sd`Xfi(njTO``ymb{QDleAVnGy92*;HE{&v#y{PIKOE34} zl}hv>y&03Opng$5kA$B+%1jQ6L+z4TVQ5Ug$a98a)OaJc=}3XA z0!;OuPuI5f3&Z0Y>t(y-rL7%-%%M%mVQVx*Wux0br+YOEn7M{l*hnT#*)uz-p^hj< zh(qx_I%q@2xQP%*cR$R_;dGqqBwLfK6|?Fhij{epBLT^6R+qBT5$dh4jNJ_lW*pG! z=nnS1FXM{sgi&*c4Y=8jDn5;gYABu6>vVhfYRk`$&+FmU&2n@5{PAgXZtf=551&7A7R~?q*Y7Xa>$kuAZAbj|-~Hw8?d|RF-ZR<{KYq9M%isLn?@505 z-OsgVoq1+l3+mmg_wRrI{+mDAjXnMJea~jj-+cS~@~Qs#!w+}g91mZWZM%wauSzp` zfuJrf1D2tnNVV>Q0vs$NC*9zHg$k`OYFcp^wKwjf8Yml}r3k>`X1fNf&eJ>}N-2TH zluW5HjO|9-%P_h*lSs1w18iLw#5ZEB28P+#q%3H7B@v#=$+|?vm@bv!n-bC}_c6Tp zFv<_9O($Qc{^v5?@Rg!XR~IxO>XV zyEotTxZK^$@890Oy;)9^v$d9~=X39^L)Gf5*I$=ny|rW2`NPBY=MSsb!#BUxVL1ll z{Dke(c78rT{QU9bPav1$;i9uEZI@J33WiR{Y?ZQ0iF})rFm;tH`)a)CkXUA1tfWyA zdjxtoVP-8uOaugX78^SJV&=&(78@Q4qxOMSqvM#_Qrl3gcJu_~R=9gs?9=v?I~)P0 z%ZK!@Qz->1tU{|}H!u^2g?w_Xvc>pCGR@6VoNkChvb@-254#$7N1~uO2J?7}eOEB8 z_?Z1;RN2%}IIHz|Vb?&kWY;D|(=5@37363i5u^$yGC}VdP1#}?$vR;Yk){BOVRtId zjG+eYsoWw)SuF&}0L@CxVg{2cIkJX|XBY*~=+Unk0t-ACVD6eQA9A2wLPJJoL{2aX>(*OaFPXy?In}La@9P-DLDW&Iu5UH>+Jo~ zGgqv&ui@vq}O$C*)pb+qso{%aFEC4?Z^Be z-rOw5({eDgwYR71`KQm@?Ymd?a3J~i?$x_juWt{>o8$4iU4H!e!*zRJ)t*0JPRIJ& z-~0)4`R=Fh|LNcT)9ZTv%Rl_%U%&t1c>C&K{>%Tu9N+)=-TSZKo!6&N&!5Zm?r;C_ zpMUesH#awUc>3Wl-~F#z%jy31rnO`EUp_otZ|m>=!0u7!e>a+3lD zjMlSt0+R_=ycUvlz==v$l1hgP_9jmdryMh_0J{)2!0~D{vvM%92gGEike2GqHpo2I zZGi0Asj-}m5W<}qh)vOAxPCUPdVv&aQyP##Q-=u&&tckz03tbrS4bflL+ZTutVI$P z`v%}0L?+O)8_a1#PYw+;1I($+RCYuFMpWiTbaak^VIq+VWe!erCaK6Zk!89$9_{e@ zX1YBshsm{Vm&fPp_58FwP1AI|xmk{f!@K~zlK#PkNyzp;;Y;&08mP?OI7{8(I|3gmH#c*- zTFV{7YOyI>M(d$WtPv1Ne+ zR5H1f{d;B#V@rL{ywh>6DoasdvIz ziY2_%h;#JbH<6gUY+Fko^az{pPusRhny0E&q2`A2`pw<_Th#gZP5I5&U$y58=K1jL zw}12B{g?0lv%m3aemzgGUcI@$zhBSSSNYY~_pht@RO_HE+4vsM4 z@bT;Ecb|U7yx#m*|LXtp>dkAnM6Q4N`+xKE_dm|3o9Xr``$O{m^Z(MVT>j?Ii%(mY zyHchM^w=mApPj?o0EGo%$aFcP4@JwEz;LvW)#*r&PI;wF@&T|lC=v)^@q;qis9g^O zn#{&ysGWTK!s}J0)AwF&j6nGEF!|0$+)oTKn2QJ}hU1ryLYWw=X*%|j|~yScw>PdWR1G8j9Q*_=JG=$P+7*aW5KZoQ}_$hkvodQByDgN&?rO( zl-bP?AhIJYh3rbv%J3qjfw{Y04BtYy`wNG9=ltYe+V5Lc2Mb^19ua84l$j0h5i7;XY>KV--n$Y_J7bOT!a8x9yiCp+b*vm| zL0<);55JvpzjvjKgtfJh+U4p;9P4bw=XoA^G-IEivAEAuTQ8S&D?U%twD@$nT*$t) zNQ1dkG%q#u@bJi)r|Ae-FPN%L({!AtYqXfEPxW*OsJ zuIyp50;(58jwuLaGL}-jnmig^*oIAB?nF3JmP!W+Itneb?6~R<-4ce zshJ3+y2$|3&`SqGy=Na2O*th`A|rMLHV3#f3N9&k_;{S{Hg-j2W($Jdl~kDO2-Xr@ zV>cOlq9a557UF%Dxc`O>URWr$FiTiZw4vr<&>9AH4S3NUG8G!Q;}ENKAWJ3sbvs|T z)Hd}?Y!^iyPRDt^nWj=qmWp50+t9!xk-o9R+ z&QIs(`{jtVmXC{h8;(`V!NoVyMj6w0{IO4vI-8uX^m;8g7%Hy1W~O_=aN0G}#vsqk z;IwK^S|O`>DHU$C^0E~O$uNpG<}Ikj#>OUxI|zk?gW0A41g-nL7bfFAH6aMhMi<>jO#zkznpfON(TO3e7p>g)(EnhGXC$G45l7eI{Uq#m91w zVTqlIo{@)7Q&d3bDQ+tP7%dQ7$ z(TkbHbTA^_#*wwk`81goBy*F8Wth60##+4EG#7*SwXYJ9o;#XZobFaC%4Bu1QVi8f zF*nF1l!%Pv(6;Q{QOaaVl_7iYX`K}|+cbNrwa#+|vXtrh`LVZB3d#(162%LjpC7la zDT&>`6K>v{%Aw9}&R`ozV<{=wbg;v6Dn1#?&1CFbqi{GJ4u`|>cyv;kb>GVZ0P{Q_ zjz?u4O1*AtTQ5`f!&DP``})ppYJDAyovrut^)fGbwVZzY{*Tx8c!$DW@b z9)I+w>+$%TGt2f5|9Jemy!yM}`gA;UVl6Em+Io%-r%%UfVqd9B4XR%Y2HhPB3fVRw zWggZ?o4S{;+Jkyx>^m{2Na~QMEQhjvoNSO(Z;|RTrav~0ZYnM00J^e_ikiyQ5KIn~ zFBRD{V;4Xx32Jxb7{aBU?*U}yV7|z}F(^(>Bf=tOYE&7;U!+F)(n3KczAz?7;h(#* zMGAz&dvvDQB6~OqS@@9r=>^W^&WVUjUjyDwgwas;&>r$`l!RD*3IV$AP4j2$V z+UZbYZ245WIFm@W@NJsB!X;N)aJUe7cMxvwFjF;FH#3gT&dpy&e?G#wgEq}1glM@M zGSNv1HhZODIY^DeexO4#wW2_#A}ApvGfK(~DjCBKeK4@y;AXkwC}a52t+wUJR48Vu+?(TI*fZVMOX&NkQ zW~6#V*y!FMM<(29V@oGrR{ZQndM|445cX!4yO@=kBqNG4)L&vb0A^QL5HS1ombYC! z2#&Erjs@6mRsckHzR&;-yCIvPe9@PaL#PZ$MJfbfQIOSQ7TwBXb#^Q+APe% zV-4h(ijjk%+&dLcS~UPpPNmf9rNSqa*}U2?Yc50f- zyj|YC@lwv0r`I=khdR~yZh8G`zCC^W_D}QOO_?W2NxQ$l|I=@N^V>J?pSO#oYQ9YK zRBLNZQk|wFYgb;^RnoF7wN?P76hchx?Isb>#6mYjMy3JFG#58B>k;m*E#|s#Vn7jb znin(kT4q$sQv4{Xrxdnc*PGYh9+&r9Isg6t^uM>~^>_cppMUqKf5EKiPv`Z+c0R+W z!(pMYwF?tvUXt!cQ=~OCk!Bgu)=@T)JDw5hNhL+**!!|;4aHFs4Z~z4hFU^<<&ufv z0@)))I#dS#uxpEC$%*P-%M1Le9C2YdfH7f%GLyr8IS~m<)j^+zy5N}`GBT55VBZjs z1kuNgo^lfvp^$?Ed}$d;O9o&}Ic=1$gO=Hq4i{2k{Y4&YC}9-Ew~{HwUj}E{A!(o60g}Kn|8^n&Pr4{n4j-n7p25D=fJc z#5~tBPsigid!EbHyp9te%-+_XIfjaZ!CX=U!!EJ}0|+F>S_mj?wI|w&3hGdzAkl)! zLL0*L>ebxcrxE8t7|U?5Hit1m$&4Tdua9OBS`0K8VKg5OtCFKYA){i*;lQ5kp#&of zAW^x=4Ry$#oIcPsvpwR{;23R{#LNmWFhhyNXf-&zTz1J%Aff1k)L|^c*?F);U$*L0 zGjp2z^(Ru4WsDCmsq{Kqm04+kagG&!x)F!5`@a&&~q$j7D4V3 z%yAW7NRdN0V5xMu6)#rJyjnT3&ecmOuw7fJZq3k`%(kk4#jztxCOCcEZA3M%#pYsl zGOO^(y&74iFtZ*lslp5#Mi#e*60C_pk1^_3T@l z@N#{O)>v3dL7;8DGwlCQ*T3{ywq=V4~|;eOrY=@CU{RYX;$kP1r5 zlqj-6HL6O81~h0Ofy95{$IwF1p&FEsKt%%(37Jw>CQ3wPkU&;MMka*2$9dLts3G6cj}p0W$Od-rHV zT|02j?$K0sXebD0w1^%iCBL(U2Rg(Y9nPYrkdln}U_vztA){xMjG!QtYNE<27&Z(D zhd@SHqq95V^iW0lFMjze6WpC<*PtD(>Iq_IG?P;6vee@vJo@YP^4yBe%i%B?=ljFq zaM(KLWjdcuWnQK-g<_eHW_6yHj;(oHFl~4aLZS##N*YRti%7`GeUZ`mS|=&R%vG-n zvnJscFhw!4Nr5TFvUQdURSH$AN|j8W9YDrzb#8!lDk+NQ@O1P~hyV^jt|KUbyGo~Y zl_pG0dUOEc9R=d5RcloNx+;kxhT!1iQ;1N|GV_z6+HZkOx!Z{x{W3CxP9Gd_+Sujf zM`fa>W>&3KKtL7L2)K7;b{{gRbKyanM2HkqLdnd~-ocvUdxSVPwK2 z34uzaGj;?-yNW^}szJb1W|@x5bezgu>RhE#WAn`|xt&)s*i`|YfFdGE%T&3UvYBaB zoy-<1Ce%zlQHx>`V-+m^Yiofhxgz7{Abg$+}+*%mwxXL3Z-A$Z=aU?^WC9Lb*frU<-mw2rL=JL zzATFbQ`TQfIqr*r)>@8YE-Y4<2cAlH{T^MbMnoog0Vr1Uzbd78QmtiJpGPnv=H)D_ zKi(fG(fhjj)_XMTF}FYc&;RrD;hX>VfA7C<)3JSc{>kz2dw=z|UAMLMi+kVZ4$z82 z)|cxqxAx+Zra;BnP^DNEr$>_j#i4NY5C+u62{A+#;vh5-gi#|+bJH?cZP+5k2-1rk zxft9kH&qocHELi|8j6Ytj1mm#^wxEaocX(;7%<3P0VNR8-Cfu-5iAFS8_^y&se3>} zGKsJEE*>H>l0$@qW+p@^l<+h_OfRPNu@H4{Isb_%sUfB!DkdU@;R0nJjzmmP@*rF& z<%jEano7GusveKa*>SzB*VpxQ{|+%1J(V)G%k|~*v0Yv@qSku%#lv!%>Qua|o$u=5 zpmkZ6<#Ji;JT1p_i?+pDa5>ogH}O#@j|k~$)d$nRXDtM%o2DeQ#zvnIzr$D0@M7I;mQpX5v^&YwcvzK>A(i| zh~C_tbR?7=9bpkrYT`<8LC`&Pg!e#!A{6e5B<$R9LADEkhO7H-1sdWWpvTa_W_&~D z+)4K^EX4T!>XA#($?xUm|;lThg<~gQc ziKz}ogAMB&Am?upWtnU_FUPy%d{_o|Vtrb9fg3vg+B;o?Miik&zyWv7#6+lrMcf2c zv}6LWa|rzDdHBHt9)&_R3I(QWp%FdC<~L6(L{U|tknAKS8rl_?57+z0hx_|4PxogR z<)+rI*84QgDiy5gTL|XEDY)4*-#xr5U*3K3^>2Uilb=0bFJJ%s_x|XQ{+*>9pRUjI zRQkHf`|rQ`!{2|re>m0o-TD5_d3ot5_ixYhbXV2kjy~z6zR?PZs#Yg6O5fU{PSUXx z*#cbpbaAdDq^h@do6ird_1YTN=k4Xg>+1`vEpNYgeEW8ut2>pLPm2^0lTEc8j(Xje zNn={v+eOxmYl(T4jS>|_yU7L#7im~a!W9aT-o;sfDqgRSgI+>f(N&{5N}D7$xL6HA zQ87S#a}BGDpi>HU3Q-URVP+0DDIE;-&Mu5er-*2q*D^7pHJ8?7i)cN%1CZ@Xl3hF$ ztOa7`h*;@4->D+hpvd~!uc12fcr=KJQuj=7q9eDg!<;L9;<`rDAXWN^{c!Nwyi`>| z@0@f(6sIEK)cPVs{AU1T@P7BdudDKLRS$LnP zJo2+wO(|8<%Rv>bS}3`4PN2`wEfrQ&=tTN;ZGfBBPI{mqBeOe1r2wj3A`lj3%9(@B~R<_ox7AY3tswN^V62$~vThJq3wbi&=j%#m>;_N~YG53Oa1F4vDAoUF^k)B1e( z)x-JcfAG_v{Orx+3p0? zZrir@dE@1~55NAaAAfoAet-BBxf6*Fa%eiiWA-J&^s z?VI;ai3s#im0D0qccBhyi^edI2q!%W;S72pT!gL?L>cZ!VdzNHC0C$3Tc`^KAvRb5 znjr^H1UrJ^-Fsv+pHL*zwj}*0v@0PYbWs#{!lkzcr&CCJ&kl8D^udCGfF)-kIvJ)a z>Pcc_*zf?JP!`TbLc~HD5pAr{inLj|h6fbkD697X*yFmzdiAZgt#W(4^wXtZo{ZsJ zyZrdW(}y2D+UfqTe);ar&k{8WicFL1{{KP{Ndj9uduZXX}#877y>9 z#yY-vu_f;n4S{dyN)L}lF$F`QU?u=20+eVn5EP`ID7OOzgGyygOtVi{g@_G{frQQn$y@{?mJiWwO#YXM_qUR;+|~ zt)&XqSYbFk9J)g7#TXcG~wr%2Uub1cdKVGk|<#4?F@~h*Ucc@W$JSbHTkTagXbWM!PokT45tj^CNYiGiudT!8@8=^r?=>r-mY!E^ma`P zQ`pvWSAyXp$(@t0TD7uhFkID*UBkqz2vT`#xz@gj$EIC+u^xii(0c_jWdq%XNmT8ONr0kv;W4lIM9U;|< z=Zq%%L!1ovw6pmXWSS-c0@(N{l8{VEwzmKx1?+&R7C_>jD23<;W!bV|-xT-1`(3&9uX+;cj>)Z9pjebFU_5Kq5(xtnFs!0_M zgS&&AREru#446tSR+2|=Y4s2g!BVH~<&tW0L%NWnSncUTQ_2L}def@&-ANC2I?l(t z$Mf5tJU+bD@NV0Be!b4i`Qh>Lo1Y)ve)aIx*I~AzG)xU+>%y*E3p}ahO%m$8R_iepCRoQepHw42?bgCABcU*a$rDl6FNbb!1 z3Q!6)nAU|60!cqEGgB?4=!kPeM7T(GcvZOtATSVcg#=QHCXDWkfqtakRYy0JfC_+2 zama$^6hup5llM(|?7h4DSh*={twl?a=_ScwRw7V>k`^6F!8HJ_+PG>VIAog5{I-ursJM#Ox&pB^7iUw(Bw-A&5g*5p+AdTo7kHx<}) zw&?(%)@m>`@hbpPSGv20IW$s6$#uqgWd)2h zcO^_=y^~W2RS6J972=`pCelGzA;Y(ZH4x$(w$-Dv@Yo@9*pc7nGO$vb2 zP^8+^%xuEuZYEk$CY54Tq2@wBS@Lo=QAY%H$uZ6|Bh!Q-t@oaJ5GfSqfVw+9!kKfK zcn?)obE*c3E{n?xOAR?2MX`0L|y6aTu zdG;8jc(XlFR1{^}$=G>*Qj`*8P)ryhLbDpcaLN@)Ba~aQ77^~s0s{MjF_%B%+hkWY zr+42zBGhg5S{ElWu%ofbWY={)Ec*2N1D@B{k01WQKlnfYtN+_SyFZj){Ne9^@#VX1 zTT3Yh5tHZVk0$zj`6#gC@ko_vT^&@#gKq*`>lj*=TlW&+K=y_ zzrXkgF{^sNc7sYW*}1nag<{GrTwF5YOOYZD!b5}+!($tStM)Hwje zlz)m!(FEpA1e6GPro?5X(u}77gq|~fiV{Svfaxoat+$tTdp+t!Gr^8}qV21}t>Cvv2>$OK?JudUzB-0!brpnga^MOg?ys znUTuq5D^%_bm1V?BXSPOwtH`pMsRi(`N)2!>%KHnNQ|@<$su)z?_`hckR(hBJEf`h z8Xdd{FJ)U_+uEZOK&fgNN?^015I00bwIXIhiWv|}m|-f#1Z>U`Uf9=dD-lEFtldgM zwYEksu&ma@>D;$XV0XvEe0O)cfA{9yH!4LAN7-J_(_uQFzx?UX?(ZJo+`qlQf4p9= zZ|?4w!zm(;kn7g#Llsk!<{Q$%;L1+5KdW=Q}|RMpJrf!#bjJ6{J$7L#u7;dRAX>-BXzo{tV5 zPWY35@PGUV|C|5SBoEgwe)sYBzxv~!{d<4)-G{&VZ~oD8I$o}8v9hi!M3>_dt$+FU z>m-sp<*=Mdx_j^4h&9Sm4i9HE->&_`c758mtutak3hub4x^Sb|u9TS(jCyNYY5UWhA8`1YtrEIy|kEObm#CXP9s9)`v%9S_A@MKnG1I z2AOi_zZp4l;eE(9ve$|1djRS!=l~)P4~bxx(OL;591WBZD?(cOXLmzY(~SNwP}GL$ zQK6db#NmB74kARU$@Pi|b}xu=(5>tG@zp+_%KO!)rz@piURx#ERzc?^OD06L9?^Ph zY+Jit+T}G`uq?IOJkQW+i`Q6Nd%3L7*H}^YP9)ONhe;cW)=;YPrg?#o>Yl_A@1m80 zrXG5gYKS6F8ZXwTn3muaQ56S~`--AXsUfZ)LKKQ!2oQR?tn0ePN?yHP`*va5B3}Bo zZR>UGt+mYu^sKUN+pVUkYTZ2!Kw351LW`B^WeqJ36)nA#B33iG%QEg5QYH~G6vgUp z;gT-oj5P2XZg3-LMW9L&-Br~X5LKh_O!IaqB(iXbKq8z_Nx27y zS`SKu2nGU}ci*@TN$%e06tfhkCPo8~*f9iE69G(Ac3U4OH}V=1#*1JB*T(K5Qc5ui z(TH#a($1lj(tSfxq=jiOq?Z2m65a!;n<%ZVz4soR=R;96A|Pf(OL*{bx~q40vaRdo zT90Ku9S?7Ra`(kI%i~)>Yn9s9O1%B*r(Zn2S!{ZH|3y{v`9S5`TRj}(y44ewWdV@Z zlGTccNQb*8JB4Jebs&h0^wiIKA!4F}V9|jDlgv2laW)TPoD5Tvp+LYGfOf)CuKOaw zQedHZY8wCt-FuOnKg9U{)KYKl^?bZrpRZzet!PW(>(j?S|JVQUKmA|+<3Ifezw`J0 zgTMXv{=+}~$>Vyx{Ad5`zyELl?%(+O>z}mCHq{!fPjd}{nbmpejb`Qyi%7-}3uO@@ zjR98TB><4(3w%&L^!&{W!-*wx(MRsa(Vjkd9KrJYVaccv~ImFG93@|QQ6{pjcsd} zS6?rXGF4e7HO9JL`^(Gq>BH-X5AC|Ds$vm3sm+_GX#**d9d)rW`(-P}9EpjuK4 z77CQ0YKLku@#}WowlY^eE@H(mMgz)3Cfbyhg`m+P?fUY%t(V3P{nGlSw`=t7zO~+a zKGCf;_g#TwS#*%bTTRuh!n9Ddm{!kPuhzAeWI>nWqQ0-Ww5SPCmMBF_ge!qJw2B>%vzAcEbQaI&W5&^oP=jOG$Z_!(j^olDkAgI?;GPqEi@m$yva)4XIy;fxrXG7UC@z#3?`>f5 zNzvR-AR;}PN6}$M5sp_ulA?+|Xgib+w(x5xfvxN2(otz{f(p?t5K|QuRnTB&vDx1K zBloEJNx66LTSSyvDY#y*V#9tdDS$0pK${PJ>xVaA-2ddKtM~QwqK4_bJpAN$zWnCr z_xEo|o=&H2TWg)Z{Nn5LGOL#J@hm~nndA|e5650s0MlF)k~(U5s3@v7pqfQ=5lw)+ zl#&^Y9C}eP6;**6q=$P9yV>-;P8jayY?nvp*tqS4m~p1=Ej%H69*YWwRzB$A#rUb~ z2T6sfNUx7CuWnJ+*u1YV-~PY;(f{H<{nP*b|KV4Q{fGa<|Iy$1_y6nL>$kuD^S}7t z{wM$QAHVzfTmOy!*0;a@tL5&r$g<3{Gu$+~dkd(gkDl3%4W`VHB4Sga2z&Uo9}m-= z6vh>sZ<}9RYuD@R<@)rxetd1uVzrc_hS$sc_aAz45xODHh+BpBMic0Q>-G6C z`NQ3EIGt9M56YfGm&Qoxe%h(WqU zi>8HI6hf%CvWb|oiKt>zq&knDCZQ40+53hJ-U^!8=53kp8auc}bjn3aE0tC(Z)8E< z8pzQ*886wykRw&f$Pg4U2O%1%yh6ToVYqusb1qRKJ>v*&nEP#Eo6JhTk*PU`2N~^A z%hx?l)saagCS?q}fDDQ$nX%wdA)}G4CkaUz>Jmf|i$sqkAW)ctp4V3urdmuy#5C~@ z5n*_S15*Z@dzz-+qxar?^X_G;^WnHU6r~>T-v0C(J03rN|KscR<;_>`9>4zG-}$ZI zDXOHO&-ZQJ4rO_FdTX;-Emmh$jflQo-`qWVi#SaQ@ue{cBv7D=ZR^uC<&`nZgaspM zngNFcOED3UW@xwaJr--9Gz6a86@}*ViJl%GB*`uun^IGV7O13LGwl>p6)9E*vvwrg z=h;tpU$6zxiv)4~;rXBb!+-MLIQ_eS^Y8xr-~L;F{QH0Q&;IFlef?Me>`y+v^xytF ze`o&fdytpsXC?b(J>H##0LxV7E>2P+fIY`jGz1f7l$jEat7$pg(Y=LV^jKS*Ty)(o zJiNlbytM7w9THD3AAflIp?RGS=jniYZ9jf|QdKjB7!aP48bXATM*Bh)s1^~pkK~|W zy0nXE8ghn625NGkrtA*M;IUNmX3fg%0|E{=j#N}txgDb844{BwQYdE1UW9IQwRK(T zE(RI9ZJ3EONLMXt`79sQ>+)%|zl=)xc^ZD$P`#6^85O(5fGfeLkpK zY_VPB>Ve`?hqyi>GOi=~wQmg84?1#Ucb*|05#gOm8I87M9_mu6BMO_4ka{= zkhEweJrJq%%ku9bVH#ADy31BOLa2~J$z+Y}t0i2B5U9aSYao5B35B#=7YRoK*g##% z*bE_Bvox_$aqpP`S&9Yd-2s$of2KdXzI%LkcQ}>=(B`GsR76ucucG3e4guz}^y@kX5i^fyaa$mph^UBr79K=+ z&KL?tyUNJqR1pcNGhVlC))G!YtrQ^0&=DsfT^JIOOenI1xugvLLCU@<1%m9oiI|B( ziE!DW`=V1?Zr)OJ0o_~K(uKyW#r9wRdw=gmq`;s-A|`eo$9MUm&oEYG7A&T)N~&2mvNy4Nx3CWJPM+8 z7=-0Wpx8FCZQNGiZk}s-^YKM~{J6bbl{g+MHkq1VsZY-5=2z(vohElvQHAvGfNL=k zMR*vW{z9mT5kal-6DFp zI|(hKF^RdTBFbmI>rgl42KU2;V#u7*XU8?R4!*SZ^ZSSUW$DMIS}bLfc|oZpTU%Xp z>)yOi(=@fEHyXOQt=rbGm-TX4Uth01R~sC?@<3*g9-b7s>gwGfrD{l2fk`=;b%<}# zpW9~sdhH)Cu?59Ix!jgTk`m+Adu!}u9|D_fM$*rxc?-vg#1JOHlJ*l>kN0La2eh#a zB$K4z0*iz>`qf3vjMm^Q#7yBTFjO z5IfU?NB~}U-}oDxA{Y#T1nqYQ1_#0gq+1S2KvJ68yQB%kG%bvtMW=gU&W{TkY3(n>(J>9$=j z%W}4P0(~jwBFA@M93Q_}?%zHfPN(zz`R%*=$2X^k$9Jlw>YImq)pB<{mCXN~rXIAw z;c!qJg?lU>#-21KV!UmM<%Wh)k)W^cI0+Dt429kq(@004A{x=NwfqF{?w`U)2FM@6 z$ludH!&p8?W(E?RjqUz(M5MJ9z;2BapY5Lr!szI|);cez@P3}lV!BLnI3NC>|Kd;I zy!+x|S-w2o`43MFe|UJPwLZQ7@bTr@!2^%BOc^C?1M8RMqz?DoPj6)ORw5F~Y&O*x zbWKuJRX1JgooJD*uCtu)MNiY~`hH!%=jGW}nY|nO6}rK@;4q6MO@;0hHZf|^y2UE# zF?(Yf1O*!1iOks`(?@J@jW9N<=vfW2uYxH(eC(nEiK7OJ)U@U+Vl+`^%xVd)R2OPT zl!%0NL{-OKtAgR$Cj)#JG3_d3z}=uuw$?Q_(!ND>!M@Rr(5XTT1cd%Zao;5p5rLkh zDnN+7p-Xdld2MX#_VwMps@|QKWjWY#gxQEtu7^-)DTm|nbzA-N68&0amfoUm(Jp;m z*X!1tq_lk`$uj$Skupd?uIyH@oEB(D6%XkbZ>?RxkC*l75{;mQ$>3~J2F)oA0Ua?& ze}UbyF#Urdf-==y$cTtRB04Rj1H#=I8Ky}FWyi`fpt-yS1woiYLg4P3Xd$FaC{;xI zc%pnC=uj~nm@#w_GIoh2{cto{iLN>l=~QmTVXp`|(J~@I#qq*Adqlv8;KB}~iwrs- zN7_1=p1~LgO!8o$2=4Y^!<*Mc(#1G^FiEP2M^-D@JAmLW+z=5Gy>H~eU5seqyA$zG5`Le|g83M`X`uxm3^6NG#+p%8begM*29s>10fRNR1nQ?Ux9y6sZEed`Ly+E+ zQ<<|poN|6qS!R3r@FRs%CUfO%nCIwd*A*FTB{3n~8fA{$K z_RaZxuGP%!bX)*krnxYE#!F`u%3SOCjO25WN&vS&NWapFL`2d7D~U-vgJ{rTY>i0SPP_2B-A$iSEX`d9jwsA%Hvh~E9PMK?Yj2Bbrz zJ2ZMhzg%7gdOF?3_IgBIUS9j<oFL*55MsoykJRWPUm*Ay??4k&F_TH4?aF}{9 z=-FDBm|GF45RsfAZcpic&MQD8hred#5_MdzPOKfPmU`|F6H2q%RoT0Svm^$FdW~2~N^g~I0yR9^wr$=6CfUi37*a`_d=d_nxkZ4j9_*xtgK^QtlMWID4`?kQ7lLwrrM>5}zJL-ETt4 zFp5wWiYK{GAl}nqYG;P*u#*7&R_5gMOAXzacQb%t2$O!gLnSm4UXTz6sYN1gnRcmT z2}fuuEktUq5t;;%fNF4uoU*+e1ld3zs5mtwk}L*bDutwkI6f^~hgMaUBh$`Jj4A|7 zn8_;Hzvr78WSsqjkXx5J{N3mgAss@=D+rcSdy5EKEpx5G?%vFbm|3l*v-jBAHkVlh zMT+;0go!y#rG{UiGWPSpT#ckzG8BrHY9$9}6J5|lw3O3X)~UsZ>3qK|3+sIU#aC~> z{Q28&e&=-mSW3COJ3l-;+@J5JxugZfJkNPfn~0e~RYPjAqUIbQypPWj7#lfPDI!Gy z6NwOsv<=Axa}t8kGo4&4B5nl@`ACBSm`a2*woRS0J|}Lpo~^6_a8^F>5*q27?Yu(VMU~_xD;vvz6bvT{gulw!UsH zT9{4e$K?-SFYnj&eQzJk9_sYk`r2BnF->!T&9~;QX9-AZ+PJ{!2jxHr1Kb>zJrOg! zVV5?hV^LP{5D_RK8u_`Kqz3^2k*=&rq5uL$6%jL$5^!2i>M?b~u8IO^V@(nquC@gj zn}I>g5dpY*rzaCFyE#zC+N!9kS=FLi1JX>(Z8W=8qEB?6pvpMR8Q415qF=W2<#K;+z=BU*GP(b)kAbfh-i)E30m8$zZk z_jRtZ-ObBXtayzc+je=my#BCVFC7(=p!aTj= z5hm?gppyJ+t)ey`CtiB8a|SisOjsgNY-;FbnvnrV8B&vROjFfTrB*Ey)=kS)Mbs8$ zM76SSJZlMil^1bUif zz65NrfH=Y%CE0T!nWmB!6=W`=fnl0IW*X!ql=Yu^6B+=fH^YE+vSH7gS9Wl0;A4Xe zpEsw82)uo2bTCeCAgQuh&~pr6I!5@gS%yft-H;?{wFo~ve{|>Re0K?Xetv#_da5CR z@+bfJpa1i}nC^f6U;VrPwWAuGrf3nW+OF&7-D)Z6R^5BGDLF~BoNSDPX@83@$iEnk zR62~@+J%Ia3A(#N4rRtcq?F}&dh_N@i?(k4b$x!me0;h7__}@Q?PyJuDx^tO`PO7CD>`Cq0xhS()N8Mdx~b z?bnxWTeV(QOBFk4%)&|K748FS14f|R=uSmO{Fa&=j??ilEy}4%RarYqDc4f2imuRN z8bG!|*|Ep%d{QvPZ)iY{V&i>fX;6g~v65^s*G%3qQ;|BNVLpY=iImZ*m7#1B5%K77 zm?g(cU;z>L@TL@cl;j0?s(Vmw2xop8`#*N3*W18mWuNPFh5(QipCQ>VIQwnZfob~I z=P{{C7O?T3NFuAT^R4m^M(!t6RMYiF1et$I0O6s+0WM{0xC`vm-9=GLA=#S`9*Y>+ zL(*5U3LV~h?(#$6=et^qcQkBrJk1Y}$3+(9-Tk}i z@OXNB_vXv5e(QIB`|k0<-H(dH;jk==5XGiaikXq(vF623b_7$*PUaD|w^693Gm}ly?U~5CZ8sz3 zPQx*b#Lgsan2oJK2APf5>5A>zyVL3nO_K=!^{;>R|Nf`{*MI%zf2yZ19`C<7zI$}D zAj3D1>$(Ccsth)Cg-p|g0oy^Lk%315*o%zdX|m@_%AOtj_A9?4GS&{FlSqZcvCgOI zOu@EYw{7DruZQdVetG@4UY`qPwxWpL22>ffMzWAaSnEV|b`24T zgbN%JX2^^fKwJd7{RsdVRGpXBdqj6i7p&cEB1!0OBLOG`R(%jbinAmgaEwHI3m`J= zg>Lo~NadosxO{wGRo|18b$t~*&GX|j&8SnU1l!s!udmzd)AjPYZQD$-x>TEI;bO9= z6vf){sZrg9@Z%2kfZ23d4u^S~%UskuH*Zg!s7E;%TA#!aDjiK*4JDOiaKP+fy2Jx{ zKIO?p08qNZBG+ zkwu+!h==qDc6UK^+44#{)b=Vrc5B2S2__9aALmbxu8(JYFB_Tsi`y}fk9C-+DoPUM z_o@*T*2oW&;bV{pjB2tk6jUO@MDp=TN<1J%(BTpc509RU*xL(pXGANu45&avp~Wf; z(e^?%cE}1cG;Q2`da^q|eeuoR+b?PTI@q?iX$+uB zu2u3M6$p54kPt$8_T37k92#Jzb?-Bv$QZ73BLpCFb9F20`FPia_-#&AH20oPRnuQ= zKX?SU%t)Y+tNVPqKOHADM&Hcj{_)}R`(LBHe*Yr(U%#8rOYRd?rAw$tDMdssZA;e& zZ(U6s6i8P2yg5X2Q8u6^41p2&8N`RN7c!Bes)iXV0k-HVv+$|HRCTIrxrfcC`OWn3 zdjIu@%a1=ke}DS;^XvL_d41nspV!x?w!Stxi%9Ro({Rue!oonfgBC0iMon#V2*t(@ zD>D)@aR`wYZ%h~jd$@P59#^P~hpK4E7E+U~1%XmKxHE~J-J^%Ha1dED8-WaG|Y7Lq5&(vBm3uGqn@s#s+w+rRL?nI>jtmSaU=&l)K>DZTFo2tF*dT3)5CNe=)Zw=i z0?du5?%#w$HZ+ngdhUGE2^Iu`ZQU?UTFnZ&qdOI-byn5JE0AtC$`Fc|Ktz;*!l`O$ zz6Ri(Vlqv2+pfr<7;tl$w2FvOGa}Av5e|mTx?O?>u^!jsa)=hS))16BM}bXM5aoQX z%VD{Hy!+xOWjVfldtRM)cc;VC4=nSWZ+=(nNe%Py0l@LNeDkxP-rb$1qO(n>X=$wi z(2^!YV1$CTmaRK81f02a7z$W2*PEXnCsP%X&6%N6w`#61746YP2Db{S2o)0oX}Uh5 zAI9OGbgA3L5BI!^5q-y{bIvMh_65~li8;E48&SvIZ_0z)_?TVYt&7j6GH)KEFUxZE zc3oS^2@L)5@&5YpboteE*URty?6($?s$~;#>t=Og&0O`vGAD?Xx8r%fdtBZ;pI@Jz zzx(dHfA#)#>&_PI7B8DWA9z5BMy@lVj&Lu8_r#?G$a*VaT4^|jHBuyIDcr^YwrbK%fIe&0TFPWntW>GhOhOPH#Tk_h(T<#*OFl8)8O=Ba-4PUuu(r8HgnN*} zP`X4zIC7aO6y48N6$Qd+fC&Bbo_#!uvBT}QN7ren1+=J{O;If@6dquAImlp4cgdU- z6hpZ(9Io9}p+waw1TZZ!M>-RV0O)iV?Ny71YQ7-!aPPzAjkX4Dmb;?b3M0bdWinrj zMzq$X&ZQnsm`Gl?4N47>?pnB&l@i{)MqMe#^Os6>Zyk-|QN77#mkZ{(9uMuhYBBG< zOxE4Bw~gM`jmydAiSl|_PVQbxO$enNPUZM;c=*Zv+n-6DUb?d<|N={bqD)(Kg{&D$BG?ngpaOk8{M90(AjR&q^fu{_F*=(pE%;ajwUIwFv871?fvTB zym$BJj4EZ}q;l6!;c>oy_)4Uf<@EA=h?nA5bu*P<-zbE{7CuvZ@9q+4G(eE&t!Suj z9+uJDk){I>Q-CqcEJ!(mYN)zxeQVn_wn`7fYF>l2i#QM#AY9#Ti*V_V-ZkA4yo$n1 zMU5JWsPLKE-6u+fIN=JEOw);2L!lB5AfkIym}J6Kf?67pJf1w^KWh&M8j~2oUQAxA8b9U_6QfD7PG15-UWncjqV}A?x}awv2Eu*c;?&7emK2g z0jk+P_fbWx)>=wcRnbyJiD+%jrl%B@shEirovRcRiRj`j+yj}f#c-h{>#-DrfJMU< z-cvb*Vi8^4;h@7CyLSYk5}EEOkX>0$iA;Lh7p0$lFy!#@8$C|4K}3r{i&mB*QUvKm z5MmrZQ<5wXAn0(5$bNs*AW=jOMX72vK?4&R(4f+SQge@~bcYYODG><^&>Fg+7RhiQ zaPzs`xw6zITepB6iinhg9!g5g?EQLL?pwcf+t#)&ScH99&b{k09h|IQkn8nYr=@FIOheY*d4G4hf8gPCtVb?o6LI@|WG9>Ncq2sg zX1>UzyQYf$7D=dNN@oEuybbp*5m8ft#wQcO(Stc7Zwxi7IdlRXmYn%0E8#Z^EQZQ= zyxdwW_PG$iu)h*Z>g^36vK^WcNxuR^-bo4(S=aW%6aU74U>>^^JiJaal2NPjeP~1HnB50&CMAV1) zG%P#Rq-&?82!IG9GWyswL^}X+39<}4V7L6{E=EW0FFxC{YGk^gGT_8Yw7}H-`MSP; z`gqju;=_mj^7`S&ziRzDAExPiw8NrxA|0_goDx(@k>o%CNOpBPhv$Q$S`jQokISU0 zm+SRq?cGs#5Xwxc4=~F!kUMlODhSVs&;qN?vxy|f}(;1IBwN} zx01n6B?BVEpf3M$XiHQQ+$>eC7KbNcI(K6EJ))J@U9BRKj7<+Gv+;0+ibserU{=MZ zu@_AIJ;vj#Y7Ifq5F%1aK^e9TEQHgyCmz(eT@$SE%%a(GmJx$K+|b64 z;|`*kmQvh1poqBbP61FPdLMYq&TGs8*%{cQJn}Xh=hxs)W{AM(hXkgIqSf6sf?`uy zo<7QV-+lLR{^GZO{}2E2$JYa94V_AD=ylk;Fyga!fw70kX?f@3<^{14(j9_gBt4DQ z8AX#C2zO`fho)xI`u-#g?@|;P3V>o7uCA(#wOR`kzxvvHZ@q^$>b9b9L{#Hykg)2~ zTUZWNs?yL!O^S$_n2K4bLqZalADEY>&_Azuj~KBYgDO>vpbHTpp%LQTJmKd;LvfGY zCS&Men0`)xJDljkq7>m8t)jvyJ(Q!!$sWa^`HV8csZ^-bv{=eeMS|s4K|XaK__W^P zO)7)i+0T)(pbVe`FYERD_urpfo0+$Z_`AhwyQ)LPxyT5GLw&yjKW)PJT&enOvC46v+cdE77VEe*(|1yK+{in)Y{sIamv%3+9+>}nY|41Y#6k0MC z6cCD~Q@TKlnIL?)Lc6;=o0Fsz%@q>&A zawWAQ)PkJns;agueAR8cUN28CFHd*(wVE7G$M+vUKHlF8Lu#4pA{(N+S)Hcoe1HE} z-+p^KosRc!%5tiQ)7HGshdR&67ku}#pOvZ3^WpyS?Ze~SWm%SG+UA9%LCkELrnH(_ zrYR3Xj5UA)VC?kMv>_fGnAlf*3jFm)gS@dlZ=6H!3^4m_d>jjW?4|Ga^hJ`?H3{EB z(p%>ZP*24YM#C;Uc98@@AjS_!#WDmO{#?5Mw4|r(gGTI+N%G+}=RY9d?)Z??Y%4;p z5s?H(?}+a1WttCo`S{C!{o{Afr=R{FqLGV`o9DaJK_$ZO{3_r8m2>MmhM_^=$=5V2 zjiPhB17k8tk@I$yL|>B15yC&#(_+i`WoN!d0|Lgo>nV zS!-SjH1jNi(OX9+`Y;;-f*uYfx`dbnqQgnB%N!z31ouNFef+{xusm#ZVx(GbEjtGU zi|P%Q`Rl;@K)S|!daK2r!g>``69Q}7UM{cC_7tVe3{fu?9iohIqIZw2x9!^cN_r^* zEl_PYN*_f=;5QOK(lFyx^>C8_Pyv+C zj6CNk@jbUvgp^XOh=Hma-qJJE`^N6Att3k+TPCdlYEnv4T4RA6YF{oAx z#wAb7`^<(4kNlSI5#dg6-dn(jzk4J%f1Io)60i`+5SSLIrticUtt{ol#`NqBfti|# z7D`Er_F-3`!}B(cbEs+bN}at>gQg8zNQj6=*w!}jh{5%OilP>1uya%gr{^Q;@NohN zJOXC!G{!pX(aKb{y2pCGh5!+25G{0Kszvos=hpjtIuUW6rst0z%raXQ&HHpb9S(=r z^>v;rN-1SJ9ZvoEbzY7hay;D~ud5Kp`-l1A3(U)LnoF6^$Gc+H+x5$zd|67F=flIp zo8$2~7h4Wf(;g8+u2&^5iv`dbSGJ~rf{r#*J0Schc01&Eqeo=|34Hq9KurfN+rp- z8SB%E$c>*GObSN?BRY1J=2OohVpbwT5UNGt0BW7@?jGW4{rJO=?Z+2 zKAvkN)VTK+43|;HZ?mZjX;$O*TbVILDkAirPVB&7>5q9Lw)niq5gQH_H*XA;sgwk) z7GJL2tsIVrhYv3wpD$m&Tt9q#`u6GNhwBf|eQn$N8mns8*u*zwGSZD^;{ePTZ>qfo zFt(jzic$rcb`GFLWAtp@)Vl_aG?S5-)EOG4BZ75sgR_a?ertGl2};p46dTrhovVj- z1St@MDV%~3A9P0Lh&&QNrLViNCH5GhzgE`wEBzbp6XP>e(L~S`s@i~S_eNZ!Pa-wR z0}Oa?tw-?c;piK=3Ea#@MT~z{>!ngx4EoLSNcz2IgwsF&UPZOoq z8WMCiE5(F0x{tqJufE5=2BZ%!){ZInS|F*BhD{#f;==-==bzns_lPhgm0lzxf)d*m zO(n95P(lpIWs8v1#D#Y(SV#IGGt(?=ikquycAp{%zwXwiVxp!})XXJ%@0t%eV(Y8T z#gg5ng{0>^;K{{_V6<3+xqMTKG>wrrOBV(Mv8@(Z=5sq%7mIE4w!Olaa$HPaI+tlS z30)~|2x^>F593XP{_UrZf zdbodk_tn>Sx+`_Qzq@-l-%Awsm-%=oMUV4wS&ql!u@(SPmnHkJQc6U4v{H%|mE2YW zI&3M2^>F557=Rs?88guStc=!sF98ffJ_Sh0Z>HACr^jlq$oo&*Pl|C=r2JS#)GU+D z1H{d7aL1h!rc>3?;im3PsbcxcvI+qnFsn=+PY% z-A6@6l!|#SwMg&n`TDZPE2fYNhljCRsa`89dpLU$Nu0oh6)YlErI<=W6zR|=QkX^^ zShsGb(#Oz8_TE9des`?6Dxr?=<3w_Ef1 zcqjAW?(M0)T;}tgN1P8@dYd1zBYBOb zdCz^RB3+53sCJQ^ggOkrxHHrD8ui)pAfxTc>-!m5^)nV^*0cPX^2rh_(R>avKz#OO z-NejeeX7HKmk|M#Qc9|p#__!8Y35q{b~cPNX7}nOsT>~Dv9sc{OCD%szEpvz)C}vu z2>;9vmmy^?2tX7R(mlGWe)G-Gui>3P>JLvvrILqgt-CN21$a9fJ})V%S#|>` zC-l>FlQ2bus%FI>_giOVR9U(M+&Xsws5a;b0U1xThygZD#ldns9FFy6JuI(NHMyHl z?_b|;t6ZK|ZXNb&z3TQHx>-4^b!b35g9Sn&a1{mzoot-!=QYuUc4XUtI z6A3X2-O&OP>5`R`TB#L>QmQ^bKf8Z;@v9V_icof6t|E1@SxWRKQ8edfdZ8Auh|Gqn zC^k8S5Emhe?hR7tT)IaM;odxOAn^c9_x6FzX+1}<1S0of+z7xZOdHDdw;StO_{bzm zJJ#$t0PzrWFp&&I5Y+VODli>JC6rJzRirscF2jd@N+_hjR1_2wIHNIGOGX1&U```xLWv&UTev$UEE{#= zJJ8p4jc^u?fSF$6)h;$4?(KY9V(!;Qw>{rV?AU*6p-7BV|x8HXn6?ka=vEqmf<)_m;HCB0`QPaJ!2R8%IZ zx9|lrIgw0&KGO$ve}HcG0|0K=U2-rYLX*VhXyIAiFkDtFi@TZTt|cP4n;h&+84ALX z-n04o_Zm3#am*|<5lMu~Z_8B=kpXz<@P#LtPK-t(T9cGNh)mOTs`Yifo*s`seEX}f z-rT)bxRygHDM0)Dy!*t|P%`2c3ML}9PeqAb=SPscv4kdeeN&D#BW@=Dh3rOkgC{}C zu3sd3Qs7jvKWSo+<0OS|?x&MK_+MSuH+Nrr_07+}`}QxN{_5N9!~6dFZ0bVyxO!Xz zSEmmqGoeYh_Y``k`BY-wJshg3_Z9a5i&ae-7j}b)gm-G;wavF@2o}!dylDHRj)MBX$ou6VX?G@%Bb2w%h(fT2(-AV zmI#k_m9~}+jm63avsJ`HqF6bbgm#BqL|{&Hm`6bzOlK_?A}SUEwNU9~)zrMuqO&Mz z*-9tOk`I+>1+=xUu(bLd3k-mSM@IuSxml8H%LLTTq>~PpJa*w@1O~~#NY&d-hcikw zL!eEi3Y6Vo6_#y?h!w*~ad92MeRl288#8an{b+~+5%dxwU0g#YCIiG8-C!bJE$5hY z%nSgn-72q!7hB_>QHKT(F$New8vc_i`7NDunMx^BMQ|yLdSfk+P%D6RcDAeJR$7G~Hv(=C zDnvqpT|{DE`J@T~6s7O7Qc00y%0JU!>t=|&J9G&J5fEZQ+z zrlp8{=4Y+q3{@iSF|!aboihTVYrl>|O8`evE7R=S>o$G(fBef$-?tBc`Snl#_{*RD z?DEFva&Sf%0VoWPYK0@!MNBP#K{Ch+!~qvGDI-!s2Y?AcJ#!kkvl8juODQ^R^#Fva zL`2K#MH>&FifT~$=ASSkrB)M2F_SM;j(B%gdz{{Wczk;P@MC-W_4?tvczybK2^W}X z^;bsgvQe{>8QDt?5)>FnBLe8rm8&_`3z@EkmEIFSRhCRh5Z^?Vre!KrVR#WrK?|1% zN81;yq7b4%7-gav#e>j)+iI6NE5~RIsRsDNF)hqiBs5+uF*wN0XAVQ}H6Lq4qW; zWN;QmBs6LK%)vkfv*|3RWt!)CViiccw%Vh&wITYnL|HaSYvFYEFtVsph7_kJe3Ua8 zNJI{sP>R~53`A^3BP9D|lFkq+hU1zKWF%k+fbOtycG80m(QINU zb+9o_KozK{)%+z82z4O9w?|-esDb+EJ^TxcAqW_vbftnx`%9 z8Z18k^65Dq-keYK3GGUaAKrg|KD{+7CR*pCV?xp6@y*+JKYO_U^36neoDau0hkJHE zpYKa4=ZDARayT9ihr^+$WqjQ{&x%+*)btafB55X|=x6Xn()|;}*zb?A_7vGuWc&sh z@55`}rrg<$pJ2=}+G6zkw{0pCxA}~LmM{oOPcr*(2awxhHrO$YDAf1*tEwg=bc|$T z15EeQK|q1%*tJq9>6nBuw-1<6kZEoMvcAFMyIh^5v*89S0T4vnmStJjwUm+s{G_`6 z)w=$ZfAshN^dJ839%_H{vtR4K_b|UZ+_iVYtD5!JO^bV1O-*mEI`WEXzz3>;?8L`H zG56O4l2RFCa)Mb%BfhbgNO*G;3c%g9C7wdkQcd-UGdZH%nK*gG{sIV00xa0AavD zP@H-0F?u*Dpbnc%0d%6cLgAo^m{G|KU)tJKD_#pY^px{I=~fw6jT5;UsN25T#zA)p%b zJx4|j=G~>LQ<-Tg`}L9S>2Bmy7;3AUsYvspI=PmJ0<%yhq6dR40zqaPCG_lMEu~rk zZXmb-Fg=iT4^`>mq(}5Z^)Bv%m}N#YB#=%OfrinY5}jin$dCuE=Vq~ha{~H!}~OMaid)!Zn0jU)h8XheD@GV7*t>mDn}yeep>?j z3(J%kAHEZ0aLeh~ox6EiTCJ8CQC6L;1;lNFnbyT9CM;56Q>f^{bpl%>8#J!#i$2-B zJRXjBR?lbM*2{Leyol+Ww_h|5GwbZh+Yc!=EiSqoAMPLCy?y&dsaDjKc=zyFM6WNe zWuBJ9VVf=LX{B_kXJ6pKggyZ&^f4=+Q)(-q5Y*pu=DN^FRN;|H(i4^0!Yv{pRbt z$FJu{hClJc$?xY{i=c=|P<7a>+UILp6LI%`ci zOADtX68QoM;oxh^?T{W=R>X)z5Ccgc_<3j4+xne;<6Nb%2qW20gK1t9P?wOT!XynM z6;s_kIwM>K($hppvdoF7>WusmHvRuos7ks>nojG#H6U$5_9FRFFc z;%=|3mnc^n=dvsh9YtF(-1|yIfZDU|bqx=93&il_5&;p?-4p%>M5rK@o;O0!jkKT^ zn58ka6om#Apht9%4tJWGkN*}_L8Q%Xx}6S^r>bf(LB@|mtV4@>J_1I{hD{AM3Q4G> zgv!iVDoOX!o4Zr2ODU0Aa-iw3-sAqXsKEEOx)cFMYi7mO6dDkrWR0*C9) zGIe$U-CJMLagti+i6PhP#?5P;r}-FF#9H&MIk>GaPp^G5n~u}n-GlyoJ$!82%eq|v zIiB7)SSB$;Efu1N`MyI=4|k{2>F#_wEk^*=%4uF^EvMyhZQJ2+SW2C1nMzSr=Z&jA zL^2~5aqqZA#NCs0`&rq&mCb$W1bnXE#2A$9vsLN-eC(BRe{Awf|MjX3z(jkd(CkOW z?a!pt<&tDDX?JRGNG`!}Lqs)GlKj)*F%XxW6?T@3=-~KpWU6@DH9MFSrV{+jB{&bU zygpPhv)z+!yx~%`ZL3*z_ulgMl3)JvSAX_@`KSNUzyEjV`uykr;?Iv;mmhw(NYMq0 zy{l$rnp*E>MuuvkN?*6z;lbQg?n4Ox?1pq0&grb(1XQLQN3w+ppnt}H7b13NX^w#V zooFf;=NvuFgy};@l|?NgicROkIgkQTOhr^7hf8~X>d!xJ&oA4yHD0$^dvr`5U?4^q z?4TEjz_1K~5Y>|A24itBcI2w5l~ELa7KmCRL!rW8P!SXf zK+`lq65|VrT!sw06$(NuE$&1{C!U2qFR~;krr2k@Jd^-2M#Bgqkbqu;Piy<`!!x6v z4ks&hE2pjc)utWf<$2W~D~%jALCGNPsuj#y$O_#RgmEQ%iDdOCUqjKLT_T%yoCkIH|O7DFW3NdTsdl zxjB|Lr$|Ni(L_Kzg$A2# zKGa3P6`|rqkm=--A~J<2LM@-$JoD)R9qUIOO>{9UVyaR?)H}mN4b8*DDXEfkM)%%T zcXz#1(HAw7BohZPnRV3+p)jGaIZf@>kL8;tvjY!9=4k7{w(tcDaN~goe5)L&D<+iP5Iv!7Npa$c*Zcnf8zc~K< zyu5YvI?c;+KA!K-clUSa^Zn`J{&2rcR;-rgSk0!vW^_CZdB5ms}|T@O|5FW+F)jGXQ28 zZRq=r1d-ge;dVI9^Zfku`WJuk7w6;Y5C6@d^fDXD2Ab{UOFK+Rwl!gQ;_Q0I|>o@q95oB{8M_nJ^ zt9gXT&;`PsW-?8a)jG$r`LWh2EZg0?9}Xu~4X{7;_VWDtyl(yy2e>f|A|@F$6aw=#T)Pl&O*L0rkz;sJ!WityoQkx@Om6}bp<^W5v;HKZ^`z=K z5tL9+hbG#CsZD{D9gG~{OiGimcjmYIX@7QN40n53QIQBb;ay)FXV(RFnwN68)AeeK zmF}^TVL5SNXaqxLn0XZ2#S~G%^umCuCBE4^6*QehE6Nh3XpSRlGXZJ(Bk=g z0RRqDMMwy0F_qFb_tu?m0F}q-ET#C;d#ve|YLhtSc>ZeLE{|`EYPnoia6Z3%cX#)A zm=5=kkLUCGd|b}+=`hX5rIvYOcQc#JI{dgyoso7{3WPxSvodRdewx0=4eX%aRPV~& z-z1UkQEGPnz&`KaEO|JdkmgDhwyuYvWPWS^6%>p4Q@jI~R~bjr)1F;>#KleqQlvNtyvaGWRFb&d~Eu@&4 z)k%wi!f@@~NJz0FQnUyn+)z9#WVjA;wsa#kWf;;ek2HpRFtY8A?rIRYq0~~bxwcduX}M?_#FKjNKcwM3K^ZclPj3cG0nJO?A<4%)b&LnO{hFq;q|s3rdC=0&vY3 z6~$PlIo$=_#Im?U1tDq*$utxxP)JFfEQG2fPgNVt=mF`UP*>$`Di@B>k5NcWOw!qL z>?9PD@k|-0rPfyDJ@Q8?t31pCKb)Xx$z$U9w2V_k za%3kn!xFa@1NXd+bcKW(P(+cIxeVIyP8cmtUSYEDjGzImkLd%o)&UJg1U7|KwJ03xdLt;h4a0W0@5Oilq#xo2>L59?t zCQ@ReT{E^#)b>gu;T|n|XBRE18qTeEVN)>y+`}Y~Rv@6rflh{L4~b7g0Y(oAMmDmV z!EqSg(-I|H$ns(qH4Sso>SS--xA2R!HVGVpt%o}ar<;U| zK*F-Hm#NlCr9dJ&PzVO5c{VfAfQLqvD0Ft39!lzL*+LWrqLqY1Augl`7?SB&-si5} z1U8GNcbe5QAI`_ay_99}sPY~P?>)TtwRIAVj@F}H>0J?`U}yxAv6>G>^t87W5aBIt z_J(A-nB|bmVztO*VVQ5+H5idVpfN@gm5NFUcN)Z`P}BudOR1BD%mRgU_Q+67x85f; zA5JywNN)sM-bVq@dkqz`v74xdRA?YXT+PycLKA#7Qv%}2#5I==DkwFoS9arMs`!=UV4lmU{R?%1lo4e2(_~@#VW(YxM)-G94dunq;b2U&~?X zftRQ61=ss`zo_-A?a~i-r#Eljy}iFXFY^RBPfM{38xT@V1S;J(Ri!f`H2l^AC!X%f zEp+FI=S>FPDB_{cpeBBWfMzr|*!LDoWVDY=o0vQ}A~97Age+YlvYrAEv2yEt<%Y{s zvm*o`X;qCqE-~R6&|PyI91-5Ts@m8Ik8P)@IHEfN)S^8RHj@!wm}6Fi4O=789HvZx z*tjVd-8T&B8zZJ_dj>lI0o86RT;nkA>R@W-yo(2mYU5QV`~KsPfAN>U`u$)0jh}q; zb#KqFr@sCAsb1vm{g>z8{>p!>?Js}$@%tZu<@xE?&+osUm-FFp^0pKp4a_?@Xmadul)jZZ;6_ z*XvHH@?_=J*o_&cxg5@?;}>6DddIe%HSGD@AJ-q*FQ%>c^~xkSLIkSSU>=gc#)!}o zg@S9gT%c4fCMl>2)pU&pO#*=)=0vA!xCF_eAc`JBs6?-fE(2&m(YzECkxuW?g=#d5 zjI-*bZKYyc(|MVp}8fE)4ni$LZBgkQS-~ilxyS%Dmju}92y?N`=1EHfO z%`_&U7)$~|b`dZkZ7H)yQi@P>d#_r;h%Ug!eA=L-=5{mp{s@tPjx!Tt zef%QTjOtNSCN2{@+$_%F-RHNCUzF}tHPf2jGSN3uq7#}i8-*ikMXgjR2DCbt%jE)~ zxAky19_P6So!z9E390S6nq5oLS?gA9Ip0mo-SdZI&{dbahkG9VaJ>8C-J3V(`=T;W z(=<)PUEOSKck?hNNzc6?cJuMk`HGV;P3+_U2kd_9zQ;y2f+GVxcOyftkZVx-*qjZ+ z&K*eke5*PxT4Dxy%M8b#PaFh{4iCVd6rQHQhR!aLyiL8e|2N7_zA5EsX30$Z%O&Oi zhrNI8wQWt#!?1d)>K$Xu%ew5_xpepG)7@l?q9`e1BnU|y1W4?_h+zGIe9OP_5AYvI z0Dp0UAch4vf#C#>Es>!vlt`LVv&m+Y-RFMWXYaMvoMVjlt*U&ecg(d5!906K-utbf`Gp8;J$F){NlqS z!i+2>^wla!KR8>fEhmLYY=wp3j+B^WO&AaR=kl}^p2~nA2*u}qKhLzx;nkZ3XVMvh zJS-JQX9tnR-zt$isAkhr?>^ZsK(_+0jggwIB#>UA4m*ba*F#+5p^EAcT zfFyl8c{OKh&P3r#3@uDNC|!WS$J^k~sqg40!sTx26by@S_LSqASF7u?3OErJZnbWt zS|?$OOlzSKBIG6OIb+n!+N?!@RDud-E{L02Yd1Vv%T_QEovT@E|vXqYRXWZ@mYK z03`v^0Hmy@ml+@#I{=qJXuk=E!D(|Dfmxb7NF-M45K)1Mr>2RS1dxC`d$6#Eba+;r zio4skyDu1602mJjB@goe0?v?*p*snfwV1T>grUc=zXSS&0ZB&7!k)q1(TL$oej~ClmGkDp>N?NM|>9+7@TM z#<_d0ZWBt9Adn+%O4%5i@h|`KFCoJust-oH-R?9EYs4+aFFyR~Pk-mPnS5x4AAEiNq0R5c z;VLltYJr)yq^JCGzU?}aFx0km`|ZEkFT%5pNY)uE$S-`S%jO)Eol{p?M$)_7tyOQU z6VOmta@x-|v4uxlnK>=O*)#Y=hyj+vI5BRS6;j6O$iui;p|3`qzFh3J#l6uIZX6ac z3CaQuBV=541XEZbEd3HXLx|Xb5=n7vDPDDIo+j?tfb~|5$-(TI=jRb&Oh^N2iUe#T zEb=s=1gZKah~Z4EL4$|to%im-F4-V-Y9 z7ot^(iHLUNw5|=bKn7JUhokR^cT=;)tU{&iug1d_OL_VFRWljK-CN)O&ax~Pe%v2! zMvdV8FixeEj4989Mlv6MW2|rPjEpeO!zrf_VyBHq^k~<03DC&RRs=aUN}_jXsfNeK zo6bGGXJqE3Qe51b9v2#yvc3tR5&?RH#q^Ccp!>sa*bW=cBxvVkJoX?j>sb{eSUafAYtFxYu^Fvz%Xj`+IL451{&B zZ516$DW1U3HuwON9eAXAmI7T-ijAn#yGQ95QhvaL#kQSO9_4tWZ22V?qJy3ckXa*( z)mnO~vM49Pge*Y{q-i}`z?pa?HP&f7XqgVv@yYS#`ug_x`vnMzZ#L)*La!bE4kKEOz_lt`&8ig5|qc!CI%Pc&|$%q2s&B%>{i2Db6 zx&RBnNUgb1c(le5f^Hk3S&AaSFtDl=!j#3mUyg6qcU;^|bCVX1K@_wKYlf&_P`inQuUNSMw(Q1=twA)M>nw7bpiughsa&Kt#ZWBO+RO zIDy==M|Ku4k&rNN&>;tcNs3Bg5x}(XRD~Rp;-{_nkpP@`&f{C0-8XwcNK(JY5JzLM zbiy$xSfqf|$fZkQI|9f7bFWrQDG?~8ShG>X%nA{gVc1Qo>|xft?2gClXTx}yc6Ynu z?KG6z<9>YhWNlGIj#@ly8izr+h$`ng)OtK|biqbe%l!2vHlM?OOZAoMR$1|Qrfn6u zkDSr3zFstY>9$^kK}>)2Lu-jHNDc<;m{%PH(F)s*?2(pwU zqFqkeq^0IelB7G%&&_pl^3!r{oXbzjs|X0rz!r*VHCO~_7Y1Q&iaNHQvUlp_to%?0 z?4@yhID%Sh5m8v~@9%&9ryr`&4}bZ4OyMnbC?sKiUY7IguU~%h;m^fZfSx_Odi~k` zVxX`=Wj_vSDoucSjWA)oyjFR7wa7Kp#djeR8C$E-!IWtq0*d?Z9B3OB#!Hzjr=zAWyS(ES0I{4HIRc`!qfH1D5@Ko z!3mU@b1?V)4HE>KUJD5W7S^B)Lqr5alz;$&7S(}=ahRqpZhRXew<|LDLYK}RTlEiT zK(+yQqfQ1#t4&DE+v;Iniw~Kzj(~?bD8$n(vU#I$2Sq3wDrI(EMg*1OgMgix!Ogw3 zmOgVXVk<*oZB5(iprlPj5Wy>X3ny!U6z(@be|5sV#p(`f5h1L# zxjU?-abu*PN8w@tB9f93pbTNoy3;*NI3heaf1f~?c2f60?{}tA1lYsPsWq}@=>zTV z1B1vQO5&RJpFPFe!OdWW3*fvJTS~HDf@ff15oTeD7R*Wrl32Gp7sAv7kwHWrEsTTH zC;oDZqXRS~!Av0{%p9$)VPR&>1G6ygkZw;wKxFBmfaP5XDN-0%*E#q;Ap4sqOa&o| zqJvE1)qZzV^zi)s7yJG3=CGfHt_~xYy@nL`=B<=OqMTxT887B9mu5MLF0obptt1NS zNu(E{CVJJsoCjH1GIQ<`x`}Y=E`}VJ6s2C}`x%(X{u|%qa)Uiq_ITqNzR_zkvnLc2 z8Fa9rBEevz=%_m0a5lVHPxo&gee*;_3?-1IiFmdWU4M`2o6OB4hwFU8B3&O8TNBr% zZ4__(B3W&Xnc3Wlhhd0jcXxLmeDFa8{qTo>NfqwKlD5y4$R8fgA;az4-x?+E?_PfS z*@y0~JPgO&$pmx!`pU4B!)P!kHfQ|^_RHnMb}Zy-9} zxgj%1Y8WSk4-bo0t(jR7aAuos?|D84+irla@{5QT07!KR8A5{K0!;K&jyv$QyZ!3r zu&(QPe>&{Wc8ulx^*X=y2=^9nOEiW&b>i6E0vkEN+V|;13^9)u1d9lAPd+b`lN+_} zUDPL&6q*5vG^EdEA3>DNEGsEkSqdP4L`;klC={%{Gup1pH*W^uvAAzVfLRl*q@W0- zo0T#3&;Z^pSTHAErpyj+Xlv9pmWtM>Qg~}*=+9WeLI_824RaOoU^p`BD98X<+9p{u zYlLWIXIsu~1$7j~B667MHya|tESkW<;h^S*;9x@}&Qr~*q|(GatVXM0MqBb3pm7a#o(VBY;+UCKPn24B6NK7cKIWa5g z#+7t-b|Z5$BO!M0V%r?8JD3X*1tO%7Sa{|vQr;E8#EdPBzRT0|P>*I?dy#MibGnI< zMHHjjdEXw;!eJWfW}L4tB&EXuRLZnB&^ zIH(kqY1og$AnQ6?6;P2hNxYzCbn%V{0CGZi7uovk$8FJuFWM$zic1Bdbkp2?G~?1A z!Y`b~t&{EM(5!E6p?cS!0LbMVT)sIaONC;)6SrHEJfqlhS(A4VNC+Wdi_6VLCTp!$q)OT91g?v)ANc_ zgP~kBBD`9*XxQ|+l(Dhw@V@Y1%21y66u7`-Y*M8G_{&6qNf{HFwYIEY1L z7?QJMHAS48JiD6~_tPJE90~KZcVCT*M|yb*h>8U3)YxNxvs}Lo#~|ZjJl@}Za`)=< z^XZ|*T$hE=xted?CnHzOBfPm*I0sON1tm>CWxW+DbD zWY8XLV$4k9p(um!*vYJh+C1FXb=5aPtL+Njs{D5CCVHO-W)Fz=tWMoiKZu0Yn>BY< zg(JC!s_M}kZd8}3m71Xj8UPQEFwRvnpoDOD%akW|XhdX!5qhU>>@B=DYwoQYD#8eL zT`$Kz0G3D2k9%_%Ni=t|O3cEex7F)vZ4GY~cb?!IZX)cs` z0C+S!*#5QufgjgYow^4iHgA;QZ5yf(#3H^pbzA@L^#GzY(#VnwK~hCpTbs3PMF|3E zct02c@|O5KQE&V?%*_n3Qgq&O9Pa zOZd7L^CUZuG_F$8oqKd%C45X|BGl)r;_xhZCKa|~W_u|D{U*Em+`6ss@e^lDC zuBW9s!pPhRAym@PA}q|(Cy^+~S|GqIRB}>_7GABH2N8Svp=?>~LYLiG-_&%7(L;iS zP>79)Au=dqWZ4HwW<@xK*EJk!p}pgN!*&PY$atM>i?&+=0T$*K#7-haa}>WT<7qDBOUq}NAo9iy`}C=YweByEoFgAM^RIB1S=_iE7~5=6Du zwXS(Za1cwHwqRRHK?;+!YG*eh5_qe&Ms1PoGo~PJwbokKTFuSEomjMpq*s?SquEJi z59BDqiMa-h({9v*2-7r4(d^=_EgYeYOH%Ux2p&Fhn7NEhO6^qZgQE&U&09S=B--Fq zh%&s1&h^#e$A|MtcUOn&E1XV3I+b!g=umVAQt1sT*V)NTQ2L0F1u3sLrbxrnt|X(> znnqST39>AI1Y@KnPk@Srr%9B65#BruA;A#waF52qx$-I^?g5ZFF(*~E?=v~|JB03Y z07UP-IidlOgfq=Wii&0&4>*7=*@N<`ZfV$@u6>l?SVUnY&&860*$1f>43QE*b?0yq zVX&K%kOonNd2O4EmWW2T6vB(;)lHf9Z!e*T*G71}{cJ)2W#fonC$N^_M@#*I$m;Pm0)2f9JP9{{0_4d*|u( zFMaoL^S$x=`{9N@?eN>a7FFG#r~Id6=!fN=Ec$jnK^=~`l71I+T_kGp|yK2oTe*tc(= zz?8n!8!r&VX$IOu!R!JMWeG!AI^uTb*yFg6iEFu(dt^gWL~BjEeJN%5b$&x3cF_I( z{h$2F&$RG&zxM+by}y55WOAj(G`HovoOX4dKmF6!Kl>MK59{f8|MknSKKjYG-@ke1 z{dW(yZ(U!%IFIth*DrY(udZ%-*l+4UJM)DvH$KsZS6yBvB8Ep=YWucm^+%`ce1)iE z#Ps;9UEC!*nh%le?#Je5q;>9_PtvRk2R3_&aksyH z@}l~(T9aiN_jn*}>uSqtW?s!}(-_pi9p6CuN+}OHvZU97Q#f&6$;rr2fS40f^GHQ_ zB@to%_{28J!linxn?0*rASQKn%S$}l+pMa3O2{w5K{=l|p{_?-Wa9{Ho+{OP~&kw51X zJThxkM9Q$kv@3CFb@BRoJ+G?8gUMW{LC3?mzjhYX29oMl#^Kr1=U2P^)#2*q>Z;^b zp6P7@?cSNm7lq<#>vH?d$^msaG^Ku!2W~-Kog?q+>`VyPAnwTHdu9UiBh6B9XgP(F4cJxw0W~qY|T@0Y>+S$ z5roLYIrHg_sX+(miv!L%XHjL;~tRKoQpU)83^D`UhMWznICeo*d(H4TN4pjI!ED+ zF)r}9fW06y-m}}gJx}BY8!XIDa)~|2i-!nqhzROp=BN~K9=}Hj7?!GP+_cLN*)@8n5;(qmo%te$9X6Z-=29m5FKs3);E21?gcTeo7A4`Ou zgcRuzg`&j(g7A1K`>S!>b16i_d7cHzB!vo5G&c!Cpo$7hE(A4Fx%|c@2m~`4I!mCh z@B0<9IdCSg6CG@X5U1oKh$;0=85PkyEWB~}KnO5W4MbqT^3Ci1lid*8g2N0BhjY@5 z`a45FB-JT*5lp!o_d=}TLR2Coz|O&J2;mY0BNC;lZ~=$8x6r~O;?2ETE>)PBAYDM= z9^oVq!oUE9rgjX%g@9K-e~MXLqnnZ3()=z^9L5mAzo zGVCxB*@$4WxS)&ag-=AOegr@y92xSMHz0Se2|SmdnZ0m%N@>sO(JoK(5qZzmVBSoW zzfl^`z2TXazgM=L+#7L8ZaqMLiCXIg+`@Vh=ML<21{dWsC9{poPN^CJGY4FyBQU-x z0E|*T&8;<#Zgi95@2i)ue*V#i`~Ch0zw}GPFw}J|3^TGsr#-}cJ)K{D{^2^`z5o8V z-ucci@y&j5@H8#=FIk@5y!HJh?DW;khldj%-YWaO6kXRj+rhpS-WtioM?z=Km}CbB?%!M!YITwgo6sO zP&5jE!_ug~Ywl*$31Ut?JT6FbqZZ6Sbtm)G2jwqMkV2(6Gh8VY90YQXF+c=n0T(W# z3~XT*szfk~tlnhi#3-fI)q4MbE|+rfXN&3O%wDkBVEk?RdiOsygmJ|M1ICe*Q0h=bwD_YW~&##b2Ke#~RK< zVWC30SVPLq(&?N!Nl!X%;oHkd{mMwc9SK098>(P?Pr*3_Q1<^RO>wU08j$%v4rtv0c7SEF_fx;)FPSL4)YQ?>Q0Z(%Kk=V2xcu~l~*_A zcpC2S%Kdp++}&G53vcFTL=XaN@x&mR%eZ6e9A^Q7VeSH@ToO8~3Ll>h$D8T;20BF} zYt^h#U?0Tr&;T+k@}UJyV{6I=&E0cX==Mwb~HEg zkgyUoGDbki85D#@WB`*$_(F`}HGGk5^Al?6%94KO1od=@$ZZM%2&)c|!^0KK5GIgQ zVG(Br7m$LbM=r9p%Bn2NGNfXbTv*FEj=8dLs}T{aP-ZsT+QG+!P07i;d5BlW2ymEF zHRpgBqAIbuMKmXJVpSc7Qgj$pB!EDX$Kw8G3H}A>gWf3pf9}8j%lhj-`-flnL4WS! z&7bube1zVp>_k~;$jrpuqwe;5;v&K<#YwK8>=Ioak1;nhD*JM{diu`w_4V!T(KH4*#&5GB=(#jw{+wDKHY924NW{PcEFlZC4D$>cMlhmW|k~y28;q^45(_Y zOG2tLjLWio^wCG3fByN~Z@>NCd+(KDSk^NUF+o*ZYf~vi1dmdr76ymPuvT(EPlx0F z`gnSE|K%sIUw-}i?&a67KKt;C&tE-x`}PlvhtdHLbxD+p~)UyTR!Dq zc!@aI!4X78U7SG?AtVg$Mkt_k_eJt$sQ>y#7xt$N!$3V+HjfYFu-+&{nWM+>NH9lu zaHExolD}*%!dn6xMOCCgrPT!)aSj#e6CT?2+e@vjI^73>w1`g3@et;V*CndeYHL%L zNO4qp%S%rfptMo3bZ!ecf)M5q77T~O)3U$fa+yO05Dh6hxUTisr*$!o^pr(L`7BW<=?Ql=}lf5D0UJ z@rX{Me1n|(3nlnp_V@+d=Fctbf5Dg2Us%?E;dB0F14O?Vw$8o(D@G}8ZA*aJu_jPQo&to_(@jd}N{uF=RTNWhY4ctj=NLZUmD?6J6$=hh7ISbw>H zPL^8kfbaGt+t>DN6C~$-E7upLB{(it1W7!5vuCHv({`r~4tC!#B%;e-`U_kK>&N{mEFv$TY@bj;umM*Z3G8_(x;wLw6xaF^ZdaFAAR-JSMR_7{cnH!T{CO7 zUYxufq~qYtP}F^GcE0=i(=R^$`Bi~0ePRmh{*RWIn)GVrwa-7v3*QbID*JI1bqSGL4knB7Q>XBK4sppGl51y z#?QNN4DU-jkdYOZ6^zuv-B(Jl7D@^^2~j*sY6$_izT;tN5h4)^)=`~wkW$L%<&&!C zFFsrEzT|Z|j8bc@>jT5VLK-2GSGEJn!t4>AzJ|g~M%LVcknq}^BAhA2PU|?BOcWlq zCPhkuwP_$C+7fH6=lk_^w(5Y8h!h>iQOc-hsWQcO@^{V*g3#?%2-~QL&Js%*bP^oR z9YI#gfM|ul2#&~;P`3FqdHL#~{9VC-Gsw4)u)O|>hVaU0SDgD<+IAuxW9h#?E322I9&1mN?Dl9m(%?E_38DiySvx!-rN9W)TvPj zWuT2qV2v71N(Z5EKZ^)sj94=*frQfWPNWdYa-IPZQQDf3|5hGX zGVYGitSx79Bd0K*hH+ijaU93pboc6hoQ_gdvg2axErpISHc*E zQZ(%zGzEE?I+^c0fas=-#tdaJ6F^2D7P)pzI|UE#w!Ck|%Oy`DXK^qn-4VmVVBx$V zZNov*0>e|5n@nqlBoKox{)*h?Qm2qp_aMk>n}0?(e&H(_c^u1I-b)b$BrKt^*2WRlK z`?{`w{KtQCe}DhOAO7&2ci!n+;){Hj`s?a&iw3LheE<6MkJ|mq>G{q1?yIS&&*!f{ z|G13T?|$!luTNi9ocHDU>e<5*$NlxqR0eoVMVG|Cnx&TnZReZ>MR;qmp|HZtDKw%~ z-cvsDd^i!{Nb6i953;UI$cmYU{+li$xU*e1*Z4QInn6S~h*k~&X`iAgg>?!xYms}3 zE$AbOIqYGPVMC&=H7AfNc5-;2DazG%_xt1ZC!hW4-G@a#qqf?-F3p*Al%1IwMBJUz zjD}sfumpvqp#-B?0~xtTUDoAv4x?}pbFxP3%2Fm3B3WCVZSk)@;~+B&Z%z(o-Hjny zhC>+!9j6fOlD5lzk}qg%{ffkPGccJTlnR_uyX4Bu9^^)UqlQ4&_ zZlMT{uJ{jXZr$(_AP|H|DBm~lc{-G10TCK`0-J6TP6{o75{2OanuQB_2(y+_f(X0| zxPZ#A-|cR-+=O1mbU2Pz!dgo4VU;@Ana;9AoXIITEZoe(Jd=5urMV40c!P75f%u;2;C7J#?U0hoD$j>KIE|j5G)H(wn0I`JRAe4C{%q7>X z$v1^JHv@25VzT!7^Ur?wpZ}P&{H?$J>sPnev(F_{p&ln@M>xSv=@^6Q^uQ%Z19W%y z>bmad@%8?2m>F0sy0*l(TaQ`O{X8IMl|>_hgRNC;G}eVL z?;8TJEeikw?u-v`P$d`QqRKoFIk=)#_=;Ing{Ii^^e#Qub@}+!`SW|Pr;qNwe7P=_ zhY4kef~c#8>0Zek#!fkYZ(tHQ*eQ`;;c?RAe%P8{jp4JXZYpR%jBbo5D^gEyDt+mJHQDL1{KQA zi-jdvc(d3OaF8eQF`b!^+kODPv05WUiGl+c7rnf^*_$&s)!Q7b#HvL&<$6v8hDs?a zgNPDQ_3q=iO$M+uOt7$ML5^05oH#6eO}?o6x~{bvJhg44f?*VqK)42}gCPPUgtstn z(JWHbO=$DXt-!d2FQuk1?9bNO|K`ZapZ)(o_xt>-l5qYTKBYH5*p_w}Z(I27%{%M5 zQsXk-LUr6927b7C`b@^jh>zE|$K&Hd!8DAcmLjUkqFJyL1L54PRR(*fu@i0CAR=d5i z*$z*?1EE71_FujH;;tTUo-(UPP!J?}5nGPX8*L=eSt01okf2=hk+7ujIPzavwT~>3 zJSlpNYUxb>_P8qnC79KsaW?N{%(=S_56kx6tXX(CZBBownPlGbyds%|X&Whsp6Q(v zf@}s)#6XA)!!Y@@zq;Pt-i)V*<>6J?UpdcCvNl`HqE%t?HBZcF5*kcQX(>;jaE7&L zPR+coRvU=sc`m~|P5Y5efO&anr+Z(|SG%dxe!^Kx84DLRcM2nOK?`ed1}Z*2vQx-c zFuCR379pW->bOldCCo%{kwJ+yf;+zjo-hefsw>h%!l*fUAUH@NL4B3ad@=o$z5uzT zDQ0>(3KPLtLX?<;Dae8(4QiM~2h~wTSciyUdkB_f9b6LMNpv+Qb6+FsysqG3#_SNiC)1*e#8&=)AOg-&)&X%`gDK$Ej)dT%W%6J z4RT^x&+F^+tLb<Y>R|v#1^2`n`KW;Z>J39qMXyIAkus|&vi~lXVTt{ zx|K4z=~rZ>>vb$?-rZ4>i6RIQJ#;qT&F!)I_FPU`a*+)VCW>SqMd$Fjd4$qtLn2%v z7Blm(2q&TG>TvVcJ8MuBnYGxLU$5tx+pM*kIgz^sQy2^p>}fwt00vQu8f*2nh8cx{ zoI=-iwYs96?cCOfdOF=#YiT?`mO)foP!Q)Ck4y%NhG<^Tj|CT5FvpvIjTotatT#c78wQ>0W-EZ3?_JBT~|Auyo|a#Xgf4k3bVSj<+PmcAC~#t z<{EykZEbCC=0spAEX)A2tg9+{P$)Gf0y3bN(1^t&)fmhoK}rOxixE5>Z~o2W|8E}L zxdM5o5_KBbBLkT7Kwu>P*U@m=)=l}ZZ;ZE>#=L)r?SpvZgG5Ox<6eO_MrOKo_K9-i z?EnBFRinPiFKsJfDvH!i6EUlZq*s}xNG64Pz5Pt`d~D!qHq5lH>)qYmtJC~9|LuS0 z?H_#S^=YPuWi%^@bqJ|l$wHK?h)qD{O_`RuoX+PZ=*6>lUVQu8SI?f0^eseOiaUVdezd#wy*Gemsr?k$j5B-v28lj;)nx&urzfSRy-^)ilqur4?ovQK+bsK(v|FAc8szHgvE>_VnQvT_*zlTf zUdm3y0S5_ZWhYO2y^UOE5gw=%9#Mm8L^KaF1UbbL-V}}DBpF$e5;7oXgpG+ntZ$eX zQowvsR)wl55h9pXhG7CQ3`5DdZ3dBp-L{By53jW~deGfi+idlrDtoQ5oa%g9m(zJY zpXd2BpHD4XYnGNYS|YvLK}-@L5_V;Snz@j1dL#e_s;xX}I5)_(KmPx}j1oBnJOznJ;;52g5u z60~)9P~U-4Sl4H}N5ZDrKt&KplnW#a3WcT1AyE&!7b#f;lg%yN74{MD=y*<=eS0L3 z1r8Az$K5ag&A)NHnnoD-#)p$a`(?Z z`0)7bTkrhk7sKuec{Q)EUVizz|LC7ZZGYq6{&$a8SLbCG4hF-+*HuJqbJvgPZoSEI z63H7~a<=w#&L=Pj04?gxN>Pg_8FQN85_*OS_0u*IEv0_SvZE*Hm>HZnz|2jY!57Dc zM9j$gVs?9L;5APjAtaLvs)Jy-oG9*YUh{HNWFyZ71Cwcl?u492-iX$tjGT5F{chstLG}iA7NmP=noMF(dP+tFyC+DuY3)4&`aTtgShu?1u5kWj_vw zGERyCKDu&*KNk1Pm6}&SGg22E7WAl1O`;%EFnR7=LIj0N5LrYzeytu%-kesCnpA%C zJbN3wk_X5OIXa<5y31-HSRsHY24NLu^38))7!Gz(9%UFL6f%}l7^5f)MG|8v%xnck znV5)MtLbxdp1(g>x67^>#&cxmBG1A0^#hC+a;ZY-~ z0pPKafI?O?s$OUnDelh8%FGmh7EJgzkN-X&0KnnyxdM8FhS3XmBJbEn`}hUA`}T0M ziyLJ`-ydw#e*da%9JmYwdHF}O#6Vunq;tbI6k;RoC3S)*QfNsGrtTg?oxi+gFp!}W zjvnJ=lP8lWR`R}cPs>0lg_(zOwDqh_Mo2XeKzMSLREmL!$UChC{TFV_%X zt55Fc_59@a_N^cM)nz$<{_#IQfBxCWAAj&ifAo|4rTwq|?%&aAclYo*fy#cAWq1s|r}>FWB{Y^iJ1 zN=BEv+CqUI8;;VpzN(RUuL%k514C0O77&*Gjo&~Eajlnp%QapE|Uxg z9S))sBh|-+4AqxQ$I!3-Hy5h;*lb2Of^eD>6M-YPNDFd!i;!SM>s&_mh#Jw%n`KO3 zA3{xOVLDtqK z+ik|WRP$&awOL(WonPO-X5Haw1f)BWl0w4BVFV?aK!5Rax1CKo3uV333> zi8@-EG?S|lg;a}HsT<_9aQ8YdLc#ux;{I!Y{eRPnMi^@WF;F5RLl1O1q@u^&MPwKT z)j>ocMMMVer>{5jwzk&V>C|-$M5s0Fytl*+1GNIJaGVDYLhCiHM z4Q0|{Wa2XHmxj0Ad2cAkhu3%O@-P-|_2F)**H50ycpQe~cfRwzaNQqoRE3%qmh{0G zrk#KX(d*qf3rIcKcD`$fA{bFoqzA| ze&@aSRfqreuN)q)WaggXezDO7z#xwv%*QEGg5?uPDTBv>n23_G&-$ofVrJHEzq+OH^btNXu##vqT*{jjlv8wi zmJkD^3Q`Jhtu-?fCsAJA%P^?wI?ra6%HVE@?qfiYIs#TP4?my2zWegimmhsHsyzSh zci;Q9zecI^>TOgg?y@+(gL^JL;yYG z&xwdUV`d}5t>s1+UVxP#)T4g#04}K~5H3Wed8G(P1yxY&7>#M~%c&iX$KyKgD114c z*87+4t+kfw9}j6Bu-4YOSo1KlhTKnPI442MuC4Z9=K$@G2O3A#$uTgMVVH*9k%x(R z=%R0cY-#KL!>g}efAz&*uu%2w)Gnyorg2)LW#8H@>iKDjY z#=$9jB^Duub0&uX-Lv0P=BsTaVnr!j1}2RrVTExrC<~RsTqrVH+EfQo5}~P-VN`GR z>?=q^a6X@xWtrC{*odvxd7kenhVytgX(=JKF3oLjt4Gb0B#=;qTLm&|C=j6p4Ht(7 zn3Wb%nU8j4B%SZ#~*&+txCA?o9_U5XLnuHM(Efn zS;h`Wuaw=ak?9xLQFz1Bgtny9H%iIlKlQqpiZlaBEHx!t8mR1l&EW*CX;YwkzPttdRyYnG&q7%l)K-MT+Hv zdi0>ADdF;P@ALh-oZ2!!+&#Se!(aPr|JJ{&&))j@r+@mz&wl=J_k}zeuWxrx_RkN` z-`nx?AQ^YNwpLLMYC*Kx%8}N!z!um|L=i?o40U&lq{uOC7IB_sgPf#$6v9JA*2mfF zvA#!m`uDwYBlSX1>1>$kj&}<(A5_q83>RX zQ;`xSBD{bnrggsyqxSmv0DRq6u#LOYW@kz>Ssk?i6lCtMslfxoTUdly5h)@|Gg4_r zr*mz*sAKh!Wu69Q5RJ?!*^QnWxZJv`utFnG zw8-33Y;2PnF8hkLjWj+#v#TC9h65AwI~q- zt4JYo4xBPAhK6tgnd(loC=Jt4SlpX2$soJA-LJKt&r9hPBINQA3I~YCN zT0tz@oD@`>fdDinD&cGZ@dTF^g#wJ#d?g|-Vmy<>$QNe=0I$u%s2Wm1`Aw=!L_}@o zF_VXADPVDp5aN)GGk14D3l|N>;_FE2K{P0VZ8a$a(sG%1P7iH~QpRO%0Os}na2%)_ zG1xs8LsZwW7=513?MsUG3Ds-d3v~ha{Yun*4CaL_7t?+?SLHrl$pb6DkBp@h=dzlL>e*#Dmzeuit?uKCzNTV zDACxwiZ&29NSIw?S(cPYOl6vYKmO^DKl|j9|Kh*+&wuate($qSKbfZK>9gB`pL$yn zq_Ssfs^$bO!(x7(*X!Hs+G2T_75rwD5>cjMWHMdKp@TA)7@b7exrhWJQbx79-42vl zTa*S2ytUhY>3*_6XIXmeikK0O=H0HHk*4siMF6CrOv%8;3hPLM40$(FCk28@3Uh7E zH#A~9X(SzyE&5QA=uNM8zA(T=ljoOEQVRc>SOKS2%II~S&u79t6%3)|v2vuU%WW}` zEf0gxA^{nmJ$vuFf9roXefK+G|NQfpAN_tPzx$nEGjO@RZlc7;GPG7_WVx)7JZFWl zL5gOl?$lSK8KuY_WpM<^DAH_#2ppR+V{UjUHMatZ6z-bag?YnrIcj6mL-vEv9YCY> z)OBXbw6##q#9DK?@$o<@m1@$02nDA{tsS zbX6Kbn9`g;CDsT~wYIi7o0zq&?gp?o6EH~3Yj{Y}45M)p^c*%&3x|OqtdbrW?#_FmB$~T>FuAw1>|~}#YV`Ivt#!><0a0>qZc5@sRG0t@XQonwnX0YPf|)~$ zNC~ielwlGnPO-LD>)hO&5Mnnv0Eu_%?a4UuH>X$Lm;@BT<1IjV1ZE#G-n`Z!A z9)@v$`}Eo2`dN|TaJaeJU5gYmb8pAn+j%)Rk73#!rsI?Ao2%(qYaQH=ha+9a3V_i) zss>ROFgVO38o4Jaf--P{GYP^eec~9+;B8eFZ)eeT*T;EYMiq7r_h}eE`{?7}{)fN$ z+yC&le)KPX|La$;cGL9y`SZeJZLMYlbGQ|W+S)h{W_GUDT9cvpX`RpK=4KRaUWoRW zw}1%&@A2q4h9^8E)HiIw`OLE&#v`k}e+2*$VI*(8$8u()Eh1+1!duVpkYo+%cqWhy zwdb}_qUU1MBUqm=>=px;l|9jje1e8 zOuKV^SWXWh8E>xgO#+cOTfC+@*VQ=Mx?W#jmvNBeK}-4c=O2FjM?Y)Wed|}gyO>EC z4mY=-fBwpO@Nvo%;|?PAt`S63t#L5*l|r}oA&QGFdpE_2AQu3hRBYcElmIa&&%`M$ zb-F38yJrUFr8-4tPZANS7T=EHK43=xsI}6zUfY7a2@tc@-JK%L!x0h2On{02)>>m$ zW`?jTi}a`|pr&&E;;>sHAacFm2|rhnqV)MdFL%XWf4!bp^2cXL-rR$u1u2m=Yvx3v zAxPa>5HScrW@f&?SVrMuOvJ%;HBe>dQV3a-}3onVYo)Q6%@mUl&%v!YO8EnrUJFmZS^_hdeSo!pGzQiK%*cw}yu1C)$1 zh6{5620*F_Qi~c5nvTU0VYNnwn!4#6NHQDIJi;$5Y$BplH5Uj}Am|>X3|161ifk^3 z5%9HDcNbyNVH~s(fs0NDmf{q#wx|um2*)}<))3DyC z_t8fm{MK*&#y|Y6-~8;OpSx8dx;h?3#A`F{8Wa&Fm1b)xN*?RF*5<0ZdYIXZXV0eN z;bEQ2Hy5I8(U49?iI-#)OQ)QZ2azMb;TF(irz7700RR9=L_t(}8@LG9!H7V%5xATU zMC4?$W!=BAZ0UWFKXNXyu_fVeH2zt>wi*|guYR)}kK@={Yqh3ENd@LnYkjljx)6Kl zGKc`3F=-ti)99cC+~fGI3BpX*+Sn%`&i)9FKgVJX%&?;zB>oD>g{_ik6~>Lvf0wnVG0C@~HISlnAiD zu^q{9S8}ul0LmqAUS_*2fg_>_dxVNi({y|D1RmgU9olCf+~3{J_jjk)uZgILh?7+h zAj~|nBWB-bgS9O|L|j;vLM%)|qzr9cmU9s>e5cB);?1nB=lSKbEa&t2;XLbhlcr&s zIE*h^fV^%g*L{5s;Xv1hU2w+k^<&!+%giF2xwcIS5gn!w$w)FG1#}>Dv-Ad3CYBH( zE~HA3AO$o?l8HzJP$?sb-OnwR53s@}Y5$$aPqL&)gq3(876Joq&06Z&iGm4`Qpz+< z5Lh_e)yLVrl2HW{grgu7t{@E_RREpMl33Q<`mvv&DzkD%jWZZ)Tm8>aEessYU;#@m zFd#y#!9wm}5L3RZEs}y3i?<>GXc|XXm0=2Vq7w6il-+V(G0LR7d8zwx0IQch_9aLL zd27EP0=(=D4eXVVN4i?!L7E|vWa%* zWjPZIgvW8%jZ=BUa+!$2Dl&#}b$GHY{8E1-JUv}6ciG0;V1S(rU_i5B0unvIx~>3NK`!o|F=X2!{ZT=GIU9Im zeQXwqNJHRirp%TAju9h-sLuE8wH|M8SrBf_>||{{uXlHKeHA;re)Vb^k8gkX`zX8p zPF{TX1%`c_@AnfG(l0*08_NEz?|l0lrknSDUJRE(WW&DtYSg2p2W!!bPx2#A>GE=c zvf8nGy;zfOqhWsm`<^Nz8qK%qGJDSSWly?Mz5EETEh50ho!mWCKw@TqaEhTQGIBRh zCxBY(BCpE}o4Ej@xuw2G_2(a7jpWMEvb3GGudUapEsNuFO@NhS4)upX< zg_#O^W>7c>Wuyq16B)6XaoTX~S?Z~s+e~#gs?5~F*IJkLe81FIYhBk>m0Dj^{PNle2u zO?#Drh*-;!xCNc;yl}0v`BFJrN??U(VlE5{GS$JjHA92|DkaNYVc{;sKm?gJVzF;l z1}VcD0Sb5+Vh|Zri?V==rmv)L+6%LA3J6E@I*fZR$|5ZsiiqH8@K_PUeE&e~)6ICA z>+|dDTFO1k+uwPgch}SLAQO+%u^PYg?Co(J_hsj1{Dh`y8isKi#*x*$4a3mFWz=b! z&gb(G!OXcb6_LWCst6*>rX~ofQ1dX~zy=L+v(9p1W*{z9Vh9Jp%#|Z$hqbMND8ugl z{xs=i^YXzT{o#M|AOBze*+2eA>pGWG4*TISO}4HH(~Af^oT6DRpdy^_rg?2`ou+-W zAh&s4ni+uizW=>vPoC6yY2_EIbMA1{DKeQe2~MZs@TC(Z3ZP^q>YVFrb3jDFB*_)r zu!Rf#F8`Yv8IBF>4z??69r0VsM=>r4=d`AZ3e+ zj-L9xeNW`)T!!fkhcYuMf;3|RAjG8{_tV|o-FkZP;~}3_vY5Qh;w#wB56e2&tK-wM zzYdT+d^fy8T-JR0&s*fz* z`gzIS=`*_OECYUs0E}a~pe_l>w&djyWtCxUm8zo+PKmB;*Ju8dfEj5ez#?Ll8^(On z*^mah^SP%6Dk6Ho3xRulVK>HzNPcHR9m>qk14xSyIaR6xh?JsZJ3JeyfK^rRUVc$e z_qDF7?9B#*6eQ=}-CKGonTI!b^KdtXvn{sNwayQ!TE=M}#&ka^!Vz^@?DF!efWWYD zWL52#p;N}vaOXz?nX1s)q1hy4mm+DM&zjOWhXt!J7a0l{)nRScn!7i6SZbb#3u{_+ zf+D0cnh?_XkD3{5F*ha>_Ou70h`L&2&<4S+)kdw3dsRi~M17&Ov{oZx5M4_dT*C!2 zl?eARGgVcd7(Nm zSkBE5SI?f@-`~#>&%XVw(^8*I`~A~zkI%oY$KB1-C%5}&`!XG;iJI@H-EN$gbtdrc zuph@UjJQ>&!lM)*T|35U5@xbSuAO;JV1j^~JAu+_aqDm`?^lo3r{OSnYDAE#GL%xP z8QhcmmhLTyjJo@LTE;Tk`ShRsU;m^3*MIu=KK}V1KY8(F+Lef44)c0GEqN#ke?iMyrQ z26s{;xw~=?0n0l!S;Ilm+%E+pB6oY??vhLd0tcq7;3HSx;O=^NC)>}UL&Fdg8(3%9iIMZo55E4vL(xq;Fe~v?g$$O zou=vGetvlU>Trc&n9R)T46Eb7w$d`st+n0Zc({7MFup$B;qL1XKKSTQKm6#e z_rCk=-M49wsI7G8$~@SJh??g>(^DA)eeIkhK1C#q!+i08ipxu}y>U6iI67~;pOfyM z9y?~%!WFJe+3@>iv7C41#z{{_J$lX$2j@FT0#$ZzsuTBk6Vc-|uGZFSt+h+fPI#pB=F?6uFmf_# z0^!b#+|p;fZ+jdGk_0g^GgQ)cw|99Q1p*PMP9ujSf>mXZ!Mu8_Acr-J>d}O~2?I(Z zmffI7urMgnrGSFmYQ4iuF|=SYvs#xhcc)e>+?b$Th&>`Ke9*GfJ%uO_qwc0*KMVu2 zgrl{}nMmJiz)A!MF}d>)xx_)=4`*v9Dmn}zs^9`pSPsh~vaVR$H>i-TBBVKR2|}5a zLdcjxS_E3Owvrnm$N;n;ug#SxIL!?rqONCeix*#QF4G9+@BZN3k3RmyWOwu8Ey4BS z$-B>9eCO)+`sQ$a=gHfX7H`$e4%03>o#XWtt5|JXM2kAbYE88a;I>vB)oTlZC`+bQ zg*yd@vp^iFgEViH(y-RdDj=;jCMTj=>rze2bT}NUl;$QnY}czbZ|k~3h)HgChd=r8 zkN(g9;s5cs|M727k-_`?qHG|N4cevS#b3`CrS^UukB_aUHtxUI+^q?%D z92Oxw0O;~}>X~}wry`S*?w$dEzOm^^fMs2XsFczbSROgm zk_Qpz{47!cWS$3?-T+dX%uoX$z`9mJkhkU_ui|bLWg2l>POt8!@qqnqT^hATtcuXG zHZmWF>E`LP+uQ5#`q7{K=?6dmlOO%#-~RnS{K@-&`LF-l-~L~Ul)AKmg#(q7V6mYw zTdmJ4(32?>#?+fbkxSHHXTkKAmF0~RA4DK;LEM^e??^6_iCC0{%#&UdUCGtYiBeSh zGF-Q3mD9t8Sk$f7qVr%kq{w*wXkMFASSnFRyl%0ye;QB!Yav7 z>*CP_YG&3dg$acdh&hN9T1qK(9EaU7?3r0ulm*}xJ`x*oA+7=nW+Sqeku@<4$V-6W z2nbU|nZ{|i(=rs6ftktOeW`W1pX1_#u{|Jyme?GnGoD)|6b^(}=4fQissU087}d?) z$1*bOI8Mx}jQiJL0nLh6uR;Sv?jPnC?|#=^pS=Cf?&vH4g=uTDgT2PO`c)lCIn8-YqG?jF^0UrJ%&M$MzPR_A&? zt@HVGI?eO!ZFTqac{wk2cR1cYc^W7>l;idF{&=bl9KP>m_{%|!M_qA4OCT^?MX_~^k4R*M> zemLL%^bh{<$N%iNfA*6>+|=1uhl03j3+Ow1nW zW|zbSi?+#vgc9HDp!VQlP6M3xI)BFd<#=yW%Y453`io#L)9!FQ))_%-BN7S~DWXKo1`rXqJWiVuLv(813{xPTS{qHqh{<7$}J|#ATkcqH0e~RM1+VI)^PXcwzQ}XJ`d4a zYjs&YPzC#H5se@wG?Ud}W*wyn4x{e(`ze!8qzvvZ^NHn-hV%03)ti+?NWD0VP`I)R z%X*$`th=!otrl(ZcAC#BQ%D;RhvV)jdQh1FVHtymHQIRde4bCxINTf|RQCI&1r5jN z&)*$~!(aOT_ilH`r#kEo(>M-1C^_v_8-fWGDZnJKHBfV72N8K|wD}mCo4Z5Mng@lj z_)-b(;RN^SxL32Y)wwPYU%&ivKHcAc{qp|)_37?@ez-rK=2v$Q`{PwOf@Itu-uuBX zKY8|iDAVC^IL{9+Uw#?q_K*MZZ~fEX{>`HC{M~1#`8*Cg8G>sqMRvQZ)*6qTLXui5 znCDve)6T*LG8R3}5316ZmB-y|T+6un0*a?kZoc*6?Z)x#x8It^ef=gMHOxGsutL;Q zq!do8F-rtlcsL~2Wkh)tQ3CSZDe75A$dkj)Mv?L8!Ue44=KwV`H`2laxSLzc9upK` z;y{FZqVT~;0vU6f6y>Cx&eW8JCSgo?aPCrJW&&nLwI;#OJ%%X(_L6Yei zOqIM7#ALq<_fnkA!n|_RJ)}O=*XK_^`}pZQ&uhI=CR=CKn0Cj-PUkOArIdPl{lU*Z z_{s18!MrZt|Mg#=D^U!G{S_&n>ReW+s<%AV68kcD=WX^WMLY%Ic#H;G-4(*<@l1e< zbS+YkB?5E*29ow76`NZQz{sU#L$*l0W}PM-3g53Yw^n4} zT@1QIHFe@}h7$+X7S*}Y5>ZWvScbCG0uc!x$DIg7B*1QwA?{!;ML|-Cinw0w4<$@? zqYl$>_xk?sG_OmAyIo{WB%qucgOjJ*tx2Ox5P~R%N%I#7#Gq1&D43iiCJ7RFr>ypcql9?%H)vZ#GaFHNGZc0y4xMc zvFJDq!%)fylDV!SJnnZb{AkBFE6dHzlXabu47Pa<2M?potAQi5?DlnCl!tkqcUr=Q zOsAV?`{T_p3?S_H`|;{HyK5QGZ9U!`PxHg^cqGQOKT_!P=P#}g$7eTBo*s_^CdQy; z&;k!wOB)j~7#&xRH>e-%XGE#pTY|LIW1$-A~@NlZDfC?)Q5daI#5A!gNb6wy4_IJPggI|98t#7%}`Mg~Esmrekuo1{| zJJ(Gg`)ZC-lboxM;5R%Ksf&rS{YRi>?dQ#Z+`O9CYAcZZItoV;0kEONl<8;@-kgYs zLBjKRy?A?M5-iyp)W#f!u|+m_?B2<}F9yv_GH!`1oG(7|+|#A1TUSEm{?{o6i`uG6 zv090ooycrG&0l`;@%{asSpX^&A+)RyjP~l&m(fh7;mem_%-+8DYrlH9es;gKKl#y* zD)H@$7Xr8wimql;sCR6|iAb;oD^Yl@0bnD#sA_+Gdg<<9OW!DL z{`<*W@3hV?!52#61Sld}_v8^|uHE)Pk8dI%GtYr;IAM9{dR{6UYG+A)?@z-ozeQhe z(<2WxcPn5}utGUSA(XycUZa*#wd{s*H|}?|zIH>wI1L5tN(gF=y}HdrjXb!*QA!ao z>u90?lwvG8Oj9YPxiu$Z_K;S6ZHvkvQp!|xzZ*yJK4>bV1jX8#tq~N0K^P2G45O;D zs^*K5;)s@4V-kNMk_JZcI5ixH84bt*i>(k7XQb=XleAt(!gNtlRgb72`(0f-a@XVA8YlrpLetu^vS@MflE z9Huu53k}o$=K9I~-K)0N!{KU~Pu~1+bGz2L3 zbeL|QU+)k5<26BsQKxAzt2ejT-h3Q(`~7iHK90NHI4amVSQt#gJfdtg?RaEyQN&}( zc&yr&eGxO0IRO!*EX{Lw8vQ;IVdmXoZ(eQMJ$){BcXvy*uV3FmSR5*G*bN_l{#ki= zoupu8Srxvj zotr!PxvkINdi$^a`rmr-?stJQ&&xC^^EZmSMQG1qL~tNI8na+$8U_&3;dlbvGa?L^ z%5EK3C5ww~Kn^`=)+i}-$X74bhKazcDiOD45hkL{un1y_2#OS`8UXb4f|5^)a;*hK zGUbCJh*_F9VrW;=_k)%qQpIhZ#t%@sURng4TZ9mp$kt_M=5<|LxH8Ob1ipIt^#?!u z(YSxIJ}h1vb8Gc9M7z5`&F5FgXU}xl9r$p3@*bDzv@SpS{eS-T>xUQbe{b9$!eU@0 zVPObkUDCe4%Z@H!QQyfCb+eab(lbMZBJ~Cw`DU;H$iUbv#OAbJ!(<7*WM%IVRYXKi zVA~PAv1O=_Z;nxGREyR}lUN%yW98&4vXoK^ zi$Zu5E?_}`V=QIT;(=zq79V7Yh=4G&s$eP_qJ_oQ)z-z~i&YU3VG&HbVK)xrG~^W1 z)_OXfPUkh2S|i*%BZtgPHgQ3%b@4@*s!IuC6=D{^t-00pvC}ogYr;hr4koGL5aa-C^I_%FM^Zbqgb9;xg`@5hmIV$7gR{KYvTd{oL%Wci-7f z!|m-gGrRls&6N;JDaylGCWdkeCTQ~RC23CmCd(SO@voCK+Ha89{&#VoLRe5R*x<~7 zaq7MIIg=jxq#`6RfTu{K-#2eqI-_F4fkxDfnzwN@lH@hjq|+ z4G_UCm{%hJ$-)FMj=Qxk)HygoL~EmUvG?ZrX1oh?MgsH=72vNUjn zd4SDV&nORbARQE%g$KmJU|*M$`f_^s^0)udZ@m8Mi+87MUrwlN@a**j*m6Hlx4YxB zC(z^G2X~y>{KaPu(q02JAvsef3c-0k#$+ z@PgxhYJS9kQ(FnnOsaa=?+%B>myp6^ao_Dur`OAR2KzXcR#)a>w;!3A3z0<$y1u@>UsfLu z!`0LM)$^Nop1pYI-Tl?|)%C5r9}l~y*N39gsEjPS8FaiqJxGQ`1IxO$aI(&IQ;{%dK!w+3A<+a<&DuDQ=8Zaa59Y<(rVwYEKU zsUN_(GjvZ3RJNR;zNqi;XykaY0S1Xxdov4nmXSOVLM$oRigsS-yVe>@k)q@_pq*ZR z`pJM0P2zG=w#KSNsr`+|DjAXIQbZ6FM<+_U`^T8nGkN(MT)4nM?KF5fJi~$&~`pcYM6qCg57>x#i?>cBX|)V z$FduT-EQ2Edo$ztJV3bL@*6D4TXSULWE4~p?tzAdL&2qtnocsUMzvVih-hZ3*}T-n zyawqoc#;HKtZiAXjqV&_Xyh~m7^-C~8@RHkaC!!VAclNHsdSgXDFy?faeaO5?(;m;Hy>ukL5ZmcS@Q2WC|$-gIG`H`rVPBlblz>ZM?7&cNnFvBGjv%k=>Gi``A4vH1&69encdx&G{pF`W`RBj?lRx|y z`=Zk@GPU#lS2n*079oHnM4N?L2#v&ifA{j|AAUF;cJIIcewF=L$~24?R!Wh8s9^Nx;3A2f{2mB1M-_!0jCM%XJyvU%SiJq9x$*6JG^TF%xZ0m)nVniwRT?Cd0p0K z)fxwpB#9|5QwvBUT8q?0Wj0b~a$3?GO8LI83|M+A!@1G882ugAr-g z+(A>HQX|6Cv2>HPY=t{fnG0!n^B~-|s@Nz2MEPkE5iBrsfYNZ5$$~?n6d6)O?yTdq z-ydtO!!R5Uhq^3>!=bfy`}{?;`c{#bFJIo@zaCWptkzmxp#1vv*VAq=v*lb%8Ct8u zFwDzhW`k-D5Vft5YnkS$-yx4-PU;R;!hw#lfEKFEfPZ8A&fDYs3H05|iCvb7%Bg!PV1g|fg!ag@_fAEXzLw_SZ!Wl%MG?mNmZdOrG z_2J9Y%a4YbKlu0$zxwLyFFyV3%TGT#-QDkpah#@cH;$8z16>`)VHoP-)vVTa|MbQK z;k-D$zF(f+JbUu|$=WJgdHVEL3cEEaV(tQghm_6r;bKq~=I-hJV#!pYZCij0s3IbA zID)q@t3=NzJbcSP>d_Lpj7Y{`uybE?xV4DxPzMiyJi^0Sc~emJi7{;MVxk$t^o3SU=lqovUai>f7`n3*FW3@s=urlSq5?k8K$ zXGjY@?vC7?TN8z|cmz)aKiu&-Dm4yLkOC!0%s4!RYimR#Wz=B=u@RJrmUC-$t~0so z`E;t*wACnO^_W|m+frM#wVD|t+-j@UYnz6#*81@J;r@JD*R`jI60&(o!F}?;>R>U=|_E%fZ&5%!_ZeHmaPCs#)CghIjX%!%&0?t%6BdMKwrU^JvE7 zFfkAiB91^bbDJoF%vy7A79N3sFi_W8tEIQWEh0K7w@0^UbTm=!awTh%^Pg5C~S;UL(>bwlYaC7}+ zzu({8-2vfWeDSl7J{%1_O&_r=MJtP4*))3XxvLZ~jn*W}K$G(>d_!#aqwJA_BF3BQK@3%9QAf2S|g1!dioXf&mT~ za=VvJ9C!NTBch#mGj($_L4 zHE?ykk1~!|H;3a@T~E_AWLyl#eqQIN&u@5lQ({;oMzIC?ZoWoztB+Uy6>K;<7(Q@6tgT^$}Et;nIiX z|C^J0+o_mm5GxXP2UNMEakNDhHt$r$V94goz1;_%<{#!Q3mF$su)Ag6Ttt{hOYi0F ztw{!bDp;7gQS)Z*Ng5)8fjK(oUkFY%52Aw%*h7@b);J$y|6~OYx-Y~2^!nA(sz>Zc zDm=A#wbXF;QFzBJ>|zy04~wfRM4MZfkJGfDb`Tln@^E)|US@Brel7Qc@rFX-%tVrsvErm>)W4Mn~!oEr}8dkN^mxPzx%na*6d_-2GlNn-6yV%}kz- z4`gMbG9q4I?3z9HEcJLC=NcgiL1PsIdvT9+D`&RIkvFDd+(WoHvamx$di2UX*>0*_ zRFr%6QWKb*VDLJtOEn&KM7ofEk=1cEcC%*>#)q;9j; z9xP6sQF5YSbJpUu*m17&XjK`WnF+$4vKtf>`cikcT9s^MmxPo&cUh9Su2fAXm{V1+ z^9l7-H&zE2-P;Tx0ERN?UCQ0G@9#f)PUNcxPgjqgojrV%Q~&(w2YtU< zZ$H@Wu33jT3bD3u%fBO6h2?$ooF{Ss@Hq@O+mzaCg#kSKZJvpasP{%RG<7BrIm8=FEu*3|6Jz>zc%YdK;bWQIz$_YnTcYrt!=b2Z`+72 zSoUqnK?W8Uc4tRid~;V~gE;|0Fw)b?5O6S@n59qX+^U!g(*8Ie@2<~=^_xQvPJQld zb5_;!aJIR4aCUyNzq-*QU7X!Vx2D{y1Jrf$SjV~0bQq7B3~vABFaOgwuiyOXKl|r- zJPp;tWEN(L`vj$mM&EC%~9K4pkCX^vwT?rDsZj2-C)$+xFuoVCT!bCG%v6{F8fI$dheMcu`py3z` zy`A2nx|<`5sC&+teVEG>N&;HL-M{_%+h-qsaNO@MU%$CfV@?5rmr~3urKE7JDjpp0 zWJa~l&CtrFnb{Hnn6wC_OUcacbfk{ALvK@Eb?Zlu9zA*T6eKD44 zy@$CsJ~DI4LPX&**4VwwaE}P4(`5}t1atE$ZpZ)=)f)aO_O8deyN5Rbzh#MV^O#*2 zmCI$c1w`?MghND5!tGKtUXy!lbSe0})oM^Ynu2wqnluo3VLb^;6tG05>eba z81Jq>`^De>^uPYq_08^I|6l*zCm((HP|JxPg5W&>4&^52u^{j*^M{^vVQ-H~4+VhL zy3h)l+^d-dttiqwC~{Snzo^!-WSMyhvyHeM0uTn;2m)BBa~IlJO8{xFPP=rA4Ln4I z5(!9aO+?k9Rjb#Cc+Dx*1zI7Z=0IsNRGn%b0%dv^VO#h@?RO9ns+CB{&EO)k-kyob zuwGsN_KUB-9(Q;9j?i_ha~Zlc9Pe(JMxXadCpqphnuR%!i0Dy^DzCr|Bybd0*Qv09 zO+__PmyFre=XpMkb{wZtTmjCkY6LKQty*h?1zScqbEt3|1z0t6R)p5cW5SHqLsgj> z94@9}>cz|)DfP|V633uwDOAmf%wW|>A>?9ptc4j!ETqM?#KF#Jk&$z?YNeVgn-c;i z05gms%xsa|0)tL-ncT&yn$DAsbE&h=Q)JBN#M<}NNrIEsNCIeMVr`52Qj0P`&R$t! zujZ-u$2w^Y!(m$Ogv?i+ zs~50RPq*x$B1ha5zw; z9554$l`@u@IfddsB_U_66=ck;UJ*-$s*=;JN+6#nWl&C!o;_p9M6w<>*WX^Aot2A= zGp}CFo&3F9plwmv#El4Mk%i}DaQHo1=6F@C0^(i`DY`gY{>tehpQ?<8Ck-!!ESSE2 z%4=;UrX}CFpO}B~F9VQi0hY}TSjZ^~HW4|D$*qZS^zGke=lEV~C->vb41!u!KqQfB z8TT|Ea;Z1B2QT*E$p@?R2ipfvId@j|<=0<+_Orj*U0pu?_y;wo+V%aq>-%1qk5{i> ze))I5`sFYF?&m*y{mlz?`qRJoSD*aOkKT-jw75DuloywORb31ngK~S?C z3yZCanT4eij9QUrpXVYXi!3XO&f5)yS@7G{-JOZUx>Z^%UL-EGeQbTt0p?VB+qG{8 zDcs{MbZ$xI1VBIrS5*rL(=}0(7*u9%^eeZp80SL*Wph$3bMbj9rKlM=$*JWC3wPix8tco&j>9i-F>ZeQOp!&TZXk!vW@Z(Z za_*Se<0HBjA~ll8nCf~c!6!F$CRa0>i_usdaA6qSA?`)%WH!Up90sc>aH{S_tGXGJ z6L~@=N?o6G22-)3THH#h=2ntaRMkoAG^=VUMeA&4tIR@{JUgrlt~g6|2nzvl)p@o$ zJ5x&BCt(A_32tuUfR?Fft#;5PDd~I5JeVG7>OgsWwyB&(b?!F(`NQ0;*5?-wpFUe% zoS)r)(Dk_=R_2&X&3#AVlId=x618{30?4#hdHKZU5LPo@7H&~o%&ZlTFm)uyqPz4k zj6v&HgOj&7o<>!%b`Ss{vi2sMhx%^m-zDc$GEu)8LWjvB4<9}}91iR48G$d~ya{%2 z&Kc$+5<){Ws8%y4r=rR%%n>)!9)G~9Su*arOyCMs*LCiDPBh#7vy0wj0TB|BCm+d%q1R5kh;T+g*Yvr+T00R{T&ml#xr*qJa&KX z4p79SBjVg(aE?zA)u&~)EexD6m%(Yx7x6F4+7MvzW<>+IL&cl@IWsTs-HBnX6wg7D z6P9geloLa&%w>Ny-d%F7sXyP`|IV}TUaT%IJau>bm0A0K_2AKyizm;2_>(`*7Z1!lRcp&*Yn;)5 zCaNmX4A*$E4+FpyJL!ecMq!lVI+u`81Wdo2ZQdp)Z@aX1)tl1D(P+;^M36A6XeYM@ zku{2U`&)jh_3%zCh~cmRRC%2}lV{31UPPaZOl^3M>M! zYbAnN4Imy&6?LjfL~=HF&*2CQW+G6^(rq`Lh_K{46HMS@L&`v%kGFHVGs`VvKGMsW zGTg{AtKx`~eP&?AoHrL2rb4CXu}gI_iZ`l+c8g4m$Y_~`43TlVFy)rB^YmoLj_bXv zxojH%LS`k*uCO939HWtA(#jx6%BdTe)TEfR@zM4!rp$$_U{WJ3RLlrmFq@9h8Cu~~ z;RLIk4XWe@a??2cjUy3FWYG|-d3B#mrA~0Ss=>U`+8hVPT#Atwr3l`3ha0HW%CQKO$omazl=!eaA zvpw4k{dT)yqE*fW_Nh3Li7<0?64eVO@#aGdQVb@Jw*`fl~)Arv3ekyxp$u z-J>og=^osFz_7#hEhV9QJfyY1GcU(it(BA{we^1c{h6y#x4Odh*f74X z?k9POe5wHi=eWE;lQPi>jcHo6wsLroaq+D~OA5@={)E6rE}mFS!&H9N49BaiDU%aLdfht09Q(Vzp_PM$ zRvE@-sKw`T9N?k7agpmw?hQ3A-8NZ-I=+o&F&-65y_ zGOBwpy;~t5(uD#Kz{wn_Mkt&`k|#6MTB}YXIZH}iPGED88rd`#ad(4Nt`oU9vV!ZZ zNY2V$a@A}o>INO%Mla0ltR#TTY)UQ~9zJfQuxcSyovqGVP>ZYSXwz6!%?u36Ok@Og zcdg7bkm8*tG6-B5Cd?#~Qg#s~o`~Uq)mku4b(&oxgfka&TCd)V0pisb)o!QpaCh_c z@pJ3?haY~2`yut+{UHmd&E{;iT5bEmVGN$9%2W)fmJ&+{YMqF+oN^-x53{pL4<0=vXHI;3dsphMJ1q{2G?$W67q1h@n8cxGWTuj(mt1SD zDb-P@Lt#tw-a@sO82~ez6Eh2^rcHf+ zRwtsCE2#_o5{F_8r3*@H)|#6I*ySLw2YI-a5;9dRsf3KTUIjS)MLw}ngUZX?FkCZ- zs#o%s`x4@V@Vx=>wk?*{uEN`TFsBTlQitj8c=ht;#b>G9X7<@^Se^a)>KEU<{N|fi z-#~51Cfl|21EtJt-PthP;p=Zcci;ccAN~I3$+KZtkK_3Bzxu1MU%pfG^u>luz@xiR`IB4`wz;=Kk=J*ydNBq{+8adVG&tOcrw z+Z6zgyNgJcG%ee&)(Eu|Lm|oCiz!#)rQR~7Zo64As<1ojG?#Lm>okrBHsADX>9Zeq zbv%j|)`~iNtpq?#yrx;1fiauVW?sl)q+V?*Wu8rplUf${L|KG#QmwTDWaZ^jyGv0g@ZS7&vru&L^C8cQj)R;#YF*4p-l9O)A*wR(}% z1A|hZBte8F^*zDZBg~js#GFt)j62FS&+}Z0se+RvO6JtBw?^+3cgve$Jurj1?&{`E zclKbsx_a>F>G^hb`QpVimBccvS2^dii;K->)AuX(gXE6fjn(EFhKwdC+^`xVlCGDO z+7<*xBujh3oxI#A<=O6#zhXAYl-F{!xFD?jkwQ=siiTi0P3Lo-&Y}9$412TdV z1mV`_?jb-oCL(ylrJC8+QVR&?k(YZ)`()-;^%48dmNOjAnzC#~;nA8iS}8R0Wgv+M zV=00}MAqmZL6n$w?i(KZm+Ix-dt&! zAZzOLvkyP0ZilNEzJtSgq zvzS4#B;FAMkcGQ@sdhSAh&jB$mMhjeeJo|mDL#a~ol{9w)tj5!V!)|TqwrKWb$2}- zHvoCdQ;cRJXz%7oDMb?&I^($IW(I{>C1Fq5;HjBKxXwNw>h5ZP_3i6=cZtL8e0SxC z8zQ1Im1#fSUE1L;YZ0vpn#01>kr`}>>XD}#t7BE)f;_Kt8WO}(l9X~t=(X0WAOk1{ zK;~FEdGK6L>Ns~e%+XTzg0ZxiZ8kouxqDc(1@s$(OLePO1q>Fis!2Vkl#@YFTj^1P zBpAZGzjodlQ+D(w)x4S)vu4=lN(PP?doproVDEQHib!_%xt8N`KOIL?ofVOG8CEGV z5V~V>C4-p*nFIn7CwGEKmTqAg!_3%C&5O>p)-u;R7k7tAjgYCy-JskS3ySR8*X}uYcP1fCo z9%EIiGhtbrF(UX=_}(xTdg2Q9{3e8Fa2$jG4*(-AwA$-+k3h-GGGv&fx!yOIvru~v1r#1QU9 z*d;qkEtR--*$HRt(nNPk;o&5eI_i7xdrhQDnE(kN$^^}f>|tC;0cV;any+Fu)wvGH z*~MlnHjeXjoN6%`L4q&~p_T`$fC605F~BX8Vp4TSPHEK-d9!s%W=3kMOg2x`c$}tE zO3~ShniGSNi8{`;N57H6?_jS*oy}mGxPm0a_!`_AW;RuiFlr@(N!#d}yMx&rZj@O{sfi+SL4jno zO-n@1iH7xR9w(a*UX{p8Rm#J#xxe1nJRZOP=F8P;Jsn3$JRXlNvDZ-jK#~Zll)}t8 zv%A8mfZa4_sfP9W*=oJLz1|P2P1MKKcPZzoI(q9?!`1a0y3n(9R!8sF@13t|L~L+k za-?iTGkX#zB~@h?iG_u$h@|9m%AhVXKZEGUkvtirkh#IxBhCrxWOZbT=w@s&EH{)GV}S(L5S;m1S@vw<2z(Oq7!{ z^6|L4yJhBndogUb0CLxbCDx?X?rw&Dw|XWwH#axCyN>hY@BD}_w)N)j=GCkF&!2FQ z{neXst{;5zduQkURArp{2itARD3w%Mw6*|6cMZ$>5D~?U3$SVUHLAQV|E?A0EFtWo zF7;Le2BYPZGG*Z;5sFDfS%iqfmz0Qzp-#M!utxjU zOCy@w=_3g+&p9&_C*caL&(oOpdMjAEUXNHMl=D3eS}w^L2Rs0^6-dn8)nuBLo_o}A|w$g%@txc5KqO3qrnvBgsKb( zK}^}9fX9l^qcPX{IO<%OlU1GPTC0bN1S5Fipi|CNuxXR4qqsq<6T4&vWfCQZ04zb% zzHrXj9jMjZ$~;!pxt3D2)EX(j3=$CtlH@+GH{17OfYRA{mh~C)VYmCuXTSd2pZ&La zoCqL57$K-j6wVmUg~Q#6q^iF9=IdAAd^>E`fAjHQKYjZ2;iLP{KKSVIvk&_9>iq29 zy$27a?}?8&fjo8Kj^%K->$==Ap-ZNo!8#Y}GpagEwd%@Nl_3j&dr}6%jbin$)^r#Y z6It{*!fMrsMJSxJ)aog|fLWZ0RIAQ?HxT&h_NH46_e65ewM<`p_8Vs2ZnvwxpT}{W zj>CF5PSZRyOFkUusI7?QClQHjora-Xuh%)}uIohP@#9BofTXVLSQ3EEX029kuPzfk z*pc*oe|~>m-gkg7tGw_T$SRSL0z?W4(`pL(4A_}nq{Swl+mpx^j9;Mb8(VSLM!{5R_)J{_9|3~aWQZ^zQ-bsZ zJ8Bv0tV6d>=m=`?oO9m~?tXW7JL$el{dRqJ^ZIaodGqGwt7lK1KYjX?AfWu@`A6#q z_YClypMCUQb<=V8_2*xoJ^mzZdWI2*OXGu$(WXPs~f^Wtla`@fL0($6Ymu8MFZI zx;L7Qvbv;omsedj*pY?9IvR1xguKORA-*|UY(Ox;v8;n+vCQ*@1>7P+Vi&~Z7J{v1 zjqgN)Mi(Xv%`#$1Xn+wb35b;4xXwBgJh8@N$*Q}nHO&GMWnza@>{a3fGAEE4T$Msr z5OP(CFcrAtL?Db`o5;a1P_1sJk%1?Dm00kF*)xGi9OB^FY^q*rok~>)GbiKh{Oiwu zb?^T9<0sFaKL7C1lc&$0eYkt_czf~q?Eb?slgwh0Sn{Nkmuxw^3aUdAGanE8^?EbT zQ%dPLj@gOLL}JG!Se|v^++GNycD_5R8>3+YYVq(3fU25vp0%iInBi5e0CLXkHkNr9 z*26H|-QDzBh{y;Ze)!?d_0@iNE2NwClil569LJP|*b@uH+l>Z=F;+_4_xnet*Fgv!i@1a?{9fNc3D^eNm)|Y4S=}n(5+{!W4XP2 z`RcP@{Nn3xUNG}|y}o{PnYFHl^Rvx)S`SsV&qJB&Z+`K&|LH&c|7_U)(;xj~?mAV? z+(}MK74LY9h7We~h3oAKcOkLp=RGXJJ!*0Ig&3+=gAqvzu^51WMjXxzK-^uMbv_FP z1C`mZm=++M*x7+3?#7LRwAkFk%;ZFJKNVtc))9+avRZ9Q*M`x1G>PO*!bV7x)KBhA z>P8MXYI_F{A$N=xjrHHn^Hi%c3wwADhS7iXWT}osiD*tp&OVkg$nr^~&q*a&&T8sJ z(q+~>mvT4FuHGf-bAOntfhc*GywA>RT~#I`S4M?RrbxmTP7D#b8}Gmpk=o9=R@!}W zXp4g?KIW2w1#@0vR>Ho@ny5h$`sN4(NH-_vDe|-9DYI3H9ec|LHUyNI*1PC zBmhy-T8BYRmJLJeHQWQw zTdigx&TM8R$tJDi!TP?Mr>;Bh_ss07>{c~c*eUo;DJ3URlf*2%sU_~m-2<#A5@rK( z!zd4hdREwoYt$ei;IiZY6bXIN|*ZI{`Sk% zbtd%S;p2C2!K_sv+1=DVAp?pGI0qTVDHdM|IdXdq9&_R{+$@xiAcO`~8U9lMkvEe* zqGjffk`$$_y-4ryz87Pxw^;eoI@N;HwlF%|MtKC_0fWFR8drpjprvqCmqaA!*65LDF3{isUC!JMe7 ztAa}I{G-M`!`KmjSSX^C!=Kdv|^P>!5lz#`5Ef{KYZVc#b1hXwR;$%C zP44>O;+`-a_IKOO3J2Y+`c1z7@|zdq;W*E8Vm{0x5!D(RZ&t6&oU%wthr>RlGz>#k zo##0W1E%>1v(>OB=#8LPJ2t3px6PS(!Dj6A88$loo+&{HIY=9B^*5=*Yjaa2F@0g^CJuKh>?}G$0~t zBY!KMi)#!KcZ=JMNA#Y9$%!SVD`ALhy}iD={`NPA%dbq*bieBJ%=pZ)14 zKl$;n-S)XVXiYg~r{h%K3S7uwr;TycU2+#P^#+A8a|vv;?GuPZ*ulbL?sF+b#L=o7 z9LlwN5^(Z>3R_oZ3meki-MqR&-Gel`Fc%|)fXu9v@{X2mx#ICWkc0#)zE$&|F$;{? znw1&FMD6bC5w8ed^s9z;3)IZLI@|kfgOvYSI zyJ5(EXUxVj&NC#f&L33D``O4RBolLSO^G+%APGV$nJh~cDl8VI(8=AZTgt@ba0@!8 z8C>CHCV`o>UOlb{BJLu1Gm8eDot=nN3os-RF|vqScXtXFJPZ~PT&V5{1Tzai76VxZ zgA#M%Jl9d@W}+FkmS#pIoWh)%V6Kix_e0}lYItzO4N!n1vgMs!rANgIIfcgC1ZIeE z&S^+#mAXzkDehIt9P?zRYA`Z&g=7tTNp+IUNxR&F>o}#Hv*ctfcCcjrz9}T0eD8PZ z?(%>C-~XdKSkN-rVYJA{1w-n%O)p^L~3kOVR!9-EOx#j$^6Q ztJg2S|GghQ{NTHJa{;rnduL3x$~;Xa9v~R0Dwz5{vw4v4PF(mHM}u_}4kl3%iNxKA z#C?$z#lVF{>JT>Q=0YCYA2Nq|O5(22y2{;p7{2=A*O{#A^Koyhq1)dc$8j{Xl+vp= zH$j$-ZLWwEwUknpROX3Uw%fA+NlPi?IF?ejn~mx`jiaP)HJrVkrsMJO=+UE6%Bt%e zxV*f4+`SiuWeO#BFjKg7D^U&aCJ4n$;$cqWP}2Zi!jaJ5rAxa9{10 zq^t`OUMT*D*#6l9!@N^x-u*3|7c(JmCqPt8Un)CC$UR&G6fI>+#6;o>(mDt}c>c6| zvZ+ceaFV%}{xE8-$Ng^4ww?NvFKVsRz7Fg2AN|2kzW?c`hyC5>fAe3CvwOF>dUN^W zi(lJ#`0fW!W%kXJ2M@mU;hW>Q?f0vTb7Pz4noP`>7ZazAOPh0;ArLcjW>-?vK=w`@ zHMxUDawg%15-byph!DKAL6*4rXxLJtMdHt{wW^{;oRWDlU8Dw14H#*b_YGx4Q}djD z+jkrJ_$<{8YO1Q$c+oBpojO{OFNlnLfI_Yw9}Of6C!A>U^l#9I8wJV_kBY}jinq31 zjEhJtb%yn3b#{(%?{&nSUF-T>cvx+Y$Nhe<(NbLB__=xl8Bg0*vJK5&8=2q zN~lJt)^-75-W~gUqcBu(pgB0jiAT+iWEskM@i+Em@1#()>uB8E7)fEaC^+Debfqmy z$t%1h77`azuWD8es;;IXHe(`1WV%O5GH2$<4{C5QOHgBttT>T-C3uaWcA`GEvPFy& zfF=>IN=c;aQ-qWlo_1Z=6}R(?EzQ;A`39H)P8l#X$!2Lx zHcH7UVw}A)v%3MLrU9_VGr?)_GOHFM_ecTKk|kBI%vcRWo#)-{<+R^DzJD=I$DNhw za4bc^!YS`>?u<#@4~G!|Vw2_&M%B4iy*NLQcfRUOL}u7-HnDCU$8qR+;(gA2o%V0O zt?TuASE=g;)#OF zn8aMWBs#T0$QOEG<7+Sd=Ko1W_ZV>zO%z2zH!~wna7U|atuVDUh>?;BsFUPN^qlLM zGe3Cv)XT+goKohLQfKJTr}40Jva#&@%{FgV*H=60^znDT|Ma8pOTW7Q`s-5Z(fNbn z{*!LKDf`3bd^1n=)fX>hJp3R3@jsq@@aZ4?$+P2O-=D1@Lgsl%M>;*iw&?pKp0=eE z{lkd627{0oyAwDpf+^w-_|?KG&!X%535ZXR$MP;W{ih%ci0_{^E`tjb$!Qu$3C#+oB?AuXE&e% zZdy#|YIE^YGp~_IGa~j1=Y3{kfU_H^a?tfbc2stZ~otA^JIBWdHzWT6riay0@PLmiMN zR@AE1_IF<&rn~*C*I$DSg`R)>LuFfcXVdQb=6LMZn-8BnE2iCceHi!KVTGzhZgoy6 zrIg&)!ReoEL3;_R)t!lB;FVMNy6_LkOw~Coc}+_#Q-Ie9eA693V%*;Ca^h*+AMS3a z>6nLnb#kc9gBnnDd*gExz^futD^IEb8cp) zWzwQ$zIRU&VOF2#dA(jw@1L*cO#=yWNYFOPnWU&V3!K@(MoC(dy<0N^Z;TEXktE_) z$y?ve2u($-C=fdwM=bb)LMmZ$KyRC)rA8B)gN?O6#-Pv?;qCoWcY>*VWUV&FS2QNV zFm_g{WFTmr>bN7-zTX}#yQ$7zr#jEGYu(@8jk`nVeD?71-EOxpx0~(#Pk#7&dDvdx z9bdkD3C{N(JT2XDoUJnPFkD@~d~>(EyLoZk9j&f!Ke@bjex8hr`2={K+kr#f_}0YgO|}XSH!Es+yc?DK^jZtkoO}H}x)wIS7rcc!E0s5-bk)4mT%e zhgUchYSr07Kpd9KLhK9zvwMQDt9rzpKv0cLoy?UDI$Fum!l=6z^HQ`{cZa&VR|3P7 zjT1Yi#7q{q-wQiCEHmevLU^J{XB|tinOvNz6DrJ|3}t!1B6>Ixgh&{J_=>En)yyh; z5mz&DBv73ur6Q`?U2+c-C+2GCq+0cIZ?I3Ng@CUEYbb~V5g!_gPkTufkqDtgI>{XCb~A_8~tXG5lo zd-rZ%U+<2GM^7FezS#=rPd@qN#b<{sX}Gw*-EQl2Ty54VcOulUSIkp-2Hg>hKM%nvu^0){RD8gzug~q zoAvz%5AO|065f*Hq=Uiy7gBDWK{H-<{Ha4UklFB;wU%PUEOTb+d2Mdn&u zDH9)y3R&`M)07Q;KZsD@o7muxQncT!ODT|UvpwJKUS40ldG*;Vr}g)L@<;3Y+i|)p zxUP-N`2o~g_Ws$?~`d88&9 zU~ni?Vx3C>CE|!O_9il;-n&xktaFy6s0c9Zkh>vnkCRMGP&b1ODx&%(5DzUp|5OL7wQAw=%i!9 z#o220$>H{PyuR68Upc3a2fw>Z^SryenvVybr@l+N(TqxhQtRw49Le)uyrpz8DAl*4r$qr#(|2L%ec5&0{rmUVXWO&$d*n~Gmi2n0N-#BeN*&DA zR9knm;Lb+o3Jt|(<8stms}?Jh>*VfE!dVXE;cz%~!pAyyDerbSuU>tt<2;v&B-8P@ z-|r*v!2wQ^a}EWQs%n{Y&Z;VzB_{+^m}neFB3i8nE%k6Xq*$@SiKy$kEPQi&H6HKI z&(9w{e(>%ssHT~+14+cptkk~Gs5XblMh)gsjM$wl+J?oR(L(3taVg%u1%N}{ zJ#bZUxVnY=Mgvl$4Z&}r_Gt9m#?GzLESVjPRF@lxK3)e^<#viRt2-~_?q*l#y_MNi zJK@zZ06FKJhBfs{#w&=Li5Ja$^K$pamoL8j?5o?+{?VWP$@jni!ybl=m$$FJ{LN>N zA3Xj6yN>(ItCxElbIKc})#JzY?)CZkId~AFzVG{Pg>a^$rlo=*WTCOiggQAIMUA=1 zPs~l#Ef8%dcSxnM7;8pj#vJ(>XyFixvWZ0!Nftwl!#K8)S9Vlbpl5Z`_F?341{SEQ5326 z*B4L+tvoHv04EZ-k&}~!&>ZgS;qn#V!5rXD#KX^l!qA}-0H~V5s4`2ij-|^vuX5_U zoP{OS9MDRsM=exl=xCKa;)|T3)%6xQBu=a#=o~mJ8kgAJ#lavXSq>`J>S6B;=jwB8 z!C@|v$op9;Y?QOJTI)C+-mNUx-+m2u=Gg43h=7Tm10^6ciM(SA&+(|)%mBFoLeXTl zSN*MsCU#Kn&LH&=OE=f@aU7?4o@)^}CpjD^X5R0Po9(JgX&U#NRWCxf4<1}xTs(O6 z%;sr(?_s&PPbuZx$0^RuS0>Cih*Ov+BJ_|UH3W&f)>>U_9rsp?yK~BSyJM--y36A@ z&f_?b%I%nXsamKoTM(>xcr=bY7Ty&g&_lq|&1Kc;-CN*ROPzA+krJ8{hC|IWEz7_F1-RKk26HCL zg@z8D+&QFMz7W!#^n_LgiV~xF6fA(q^eq+<)W!HC3&)x6?ha=If>PhK4sXu^bAS@u z)r7gI!978zad*6Vh3Rq{$K!5KL?QJe$$j6i&hA64lroNoalHNd#b>|%(93r zpZw!L{~tg7!yo4|_cqT~#^dq0-=_~grkwpaZSP&2J%9T0^KaLKru)O~@#f-e`|!#4 zw%e^xN+i4EVOVWUA&t`+vph!yh!>wcFWA(AK|3N^y^*fXS}+UsjwpHU<3VX7hBcbrxan2N8JJ7xz-ico?TJ#RD<T@ksHFAKNd3mn* zv;=YwS#dLdYjp*!LlHnvWKs}^Y0K#fAnX|8W+3?d4%dbJXSz%SC!;yenX|WqZe?h) z6fawlaLg=>`Gew?Qp%Eh5q4wF3au`^q+&8zVdF$Rkxg3Fy+$Gw6FU-dKznL*49OiK zgop}Hptw5>cp;!bIG7A-r<^wDB%K>GGhB{|a!2nM_n-aee-B5>>`q113GBkcOrqBE z3EgWojVGy;g4A9PZr9OC!aw#cfGcMDcF_&ymL*2P*oa4s%nHpmrIbp!y4&4!Ded;R zWWE}@{mu3K>+cVZh3m+gT!#!KAA2>Yh1C>^ihma;;9_!Cl!6uJd@bTEe6l zjx18<@vy%urPMkT^M1c4BV&2={OPN!+noF3p>P)I5+&)GVWTi2qU1zFjOq{wDFI2y z)K;t2YQ4=la}wrMYju*sMF% z^5{E1`qRJoH{bt9e^l#SYn48cR;X=O+k4MHM&8^Vt{yymKEm^Q`tYM4?8n9pYWPbae)&M!AU- zf|lTvh-4x_*=q?pr7fu`!LkW`U@JNF;Y@IbiH$%Hs+^n8D(w zZ5%}qjwgvYayN1!2~ZB^ZsnA_o>Fpzw`}nB-XiYq?nKdr)LKLO>h7_jFTi0OS&(yv zq?A(Eb#pDX&WGL2;pV1}$1d^NW}66y-Cda{l$yxBlu}D6rCR%K9mzagVmM9J&1^QR zpd~`Hm2JHL6(2a-M{6#3@V7KwLre7u6Vw=84W3TlFut1lt3}L&YU^ZC4KOE`oN^9N zS|Sm0&I#F~)R$W896viE5elq2)U1M1`#wZQa88_E2w*YcgX+B(Ezjy8QaJr;sP^W6`?yFO2 zKiSQUNr+(X5>0@so_5(p#7tFbiH&C=26-%t&`_pRuN2vyaMlp>sb@;Z!#MI-P3Qf% z8gdyYhvl5jiAAPqI)D6F7?Qf=7FaeJIkD@+RvjHR?oO>*PD~=c>B&B(t zhheD2n7OzT88hz>hiQ_o>xT8JT1k}qzE9oIbsZ-$LrxEqP@Tui%a`xofwDVv!+=4} zBy%QVGe;&bZf0y%L4s_kM2TG&WkjI4B$C3xyw#L<$Ya2=6mOzP(0g<58Y-#g#-n+JKVtBc3y$Gh8`%j5oTo(?)s zyW7jJzxnF3U;kQnBNa844}S2w|LA}Cw?F!$Kdd00t7*L}B{5!o^^LK7{|7&Gr}Ojk z-Rn2M`sHU2zVpfU*)zMs`TYl<{o)sMl^;L-xF0exghHHL8mxiSx#g-#%Ws%fTV3MNNY(yH@*-rw!7UzfujTNNlr8YA)KG#$oO<^&>$ zlUQKRB-LTw^#5sA%ovPqKIt$;{AVB4jDn?dmTpRt5;*05li?v#}!9 zestp8wDX~{RDpNWK!QyMb|V9^bi$kyB{PGGvKBZ~wgr+X=2Da~ySuWAgDBkFV2umx zL?n`X^5jWi@dU&HtJSTTSvB*b?gVbsHU&vFOR@Mi1alEflwFrHYKdvR&t2Dbxf6BE z!zOJn{(-m5-E=%1kZlMwbJgOmXwf;*^_(6!YL+GpG6N{8J~K%)8jZ3PZ67UR>+$WG z=!D+rV&|!~YON_T+;?|JRiElavZEy3u%et5K21|v^$hE~6^2bp2{1?^BGqu`F^OPn z)E4RLb2Mybky*z^m}l)*xz@Vd?U*^G?l@j|{p$Aa`q@VxZO+er^&kJk?d|R#|C2v? z@#0&Mdr_U{?|%2=7cYJbV7=LtQZ}27dMFx@Qc9U~7Dljpx^bFv&Z?!>@bb+=*RM8f zrwkzR`NPNWDO7*^+b_Q%o2CNtP;h^!U|`r zL{5^!B5FB*z3uOq2`6t<0ANndeP$t_1=}d(Z*f3yV4N}1ySO*IQ*wZeg@Vt;iB(J0 ziPc9Cp{2ucymzuU@|4sZvT9R-1=UKmEyn|GiH?twQr| z?5dV}oa^}J%dh_GKmX@PkM4P$cdvIp{pst1yg-h*fs-%O|6t z(vZWcxo_5@R+}f9$Jx#8jjpAd*#c}!mPF*?99QFxGV45hQ*qJq6HQg?GU4XB(sRb1D%)|T{mpj+tpU`P{=1nzuBzszqif1+3(^mGKg8? zSP8q*f`#16Ld>?rE=Te>F#zGPB#1M%nPzw*F_5a7J2A3L^_F%M>Me3HJ#DZZ1W75t zjrLSIu5OMgrJLK`_Us^>SF6?G_BsJH7y*?aT`%52I8u|~@yk%e&YM{+N0CgNRK3e< z*zC)-dh_N@Ddpb1dtKKZ4oBwn!SnAvc=GJO{g=Od@zskz{K-H1*)M;2c7DO*nC9`~ z!NbqL{Nm!`{xEdke*5iewPNNv`{{wIwH^+KK6k6ts#eQ=A|N(4U6zgsR@odqbzRqW zUEjY)SQ^WGJRZBD@6BAzxgsSpFo!J01tdWnXaED;5u2V+%Ew&A6dj)u(z~E#Jiru0 zw}s?RZQDk+m?Q()!${; zv-9ECKmY0HFJEZUdygJ|{0INt`Ge=zH?Lpc>?NQ7-XDD0_q{U>!;mC{ye>4JIDx#m zFc^T)f4=G9460ZYv~hn+zrNhlGMh!M}G27@!8wbl}dYQvo@b`Nu{23H~w z3A3+9=3{^LLX>g#Vd+wId2pTGL{rOoM^-@f?4C*PNxzIyT12jBUq)>_7rvlEeO zna3&E8symRcFd_OX&%SbYDJ-9JEOku>pbNlN#?Ha^RRmN7Cd?SlsR=>ms!YMKv3h9 z#G2VRvcxOOZb@Nq)54CFV_XWlrV!d>XQ(joWrZ5lGFoU73$hYR>1MIltk^&xQfO>_ z6wrmJMoZ0)$DoNNYc;cuI`EY#k#b-bJ%m4E0U;TXl z=5}@d;Ni2U-}~|Jefo$0Zu6p@05i-rn!_<$J&Lli&N``SJGk?d|PqybezgR?alp>;0i0Bkc&0TUebq`QM z7R!yUb>wN}Q4`COJg) zoycq=lEe;C)mqF#DgkqlNDfzuNDKBlQ_7(@a^fUit>xzCN)LP2ay%Z7$77wRF6Ta< z=bSlFfQd$8oZ+NYODV^3o@+^0#nretsW~sJVM2<7E}W4T56DnbJ5kdtm^+)3RSrLD zLclMLl@D@bBMVAc)t1iALbI1Hp%j&-uLEEZa%eS>QTD_{AXO-FCa^k_88c@QHltb1 zjUmMkK|_U72cT%9*K}!9^9Igk;Rj=2U`IE5sr!BJDMFR5sO! zHxnWvA?h>tIXm^kV213La&m|g*8Pyq9^Jq9={QYCPBPAu zs;<}T<6J)c-gkfTi@$&N!ACb&uWoK`{`8;x*_*4Y2WJ;ImoNY6pZ|;9-QmTH7eD&Z zPhMTVL7gt{pO5>)IL_)=oo#BZUQ4YrDMz|(KMehlSujrfZb&lGYI89FJrP`8-!tYs zfBYb^3|-%KNxI%6OWm!Qr<9~E1Cuj3OBJ{(2@|uCI1y7g7jms8%&2B?PAn|MlqeM| z9v-j}SIg|d>>`a!AZLPx2~yxTDPZL+!sNA9S0g6(Vn~GSrt{71c=x7GyK)?<>!5bF zK9ln^?Rj#`#WS)H6XlsW3BI}f`mg@_FMsjNUyVCF{{4UQJHP*vPd@$KryqWAbN+yc zV8%-0IFeD%3>seE97*ekA3S^f$&Xc5(=@$&b(uH!pFMa~rrXVfbyq(q>jC|0v=@__ zi_Y^*aK}tY;aE*6C+e2yQ6q)BOHLv2H8WQuVn`BVvuX};mXsriJW{Ky&DKj;pE0ax-ce}6-4N{1FvclI)586?vYp>ww;8W zbMq57QC;kZOr6;bqA)R2F$$zkjmXUn;iqP9A(a9%i^4e;v7B@6`ka_lOSC;r)(Hcw zGFRgS&0Xrc)TN#{Ir-q3nNnMkQ-t-12yr5!wL#rNtJZbhcDoJ2*^pD)w+P?f?Dq3f zRLgDMbloQR^?mI-y<SX={xN}zCNuC>ndOhh^SOxkB@ts*&_nZgpN zLX992ZhMg$A9e*z_lRI|H4XqR4JVv%oW`jZ&Ycp*aSVo1tu>|8_v*D2f;-h(GmDmK z7zQoML{^HI32H_&bE-O*Qmhs&*jw`W2s05K4^yH!cb4)y5lR4n^?FT?B#DxU#7SsomZW>YxqNwj`Rdi1H@D-I?tlCTfAPCd9zXlu4?n*D z;K{JwIFhPYt?uf=&ZR5GsTdvK{N|Tem*4*2$Dc}?)V3J#wp?N-)fD+8~@$3U)bp>>P}Z`#!*K zj^$kvmJDH=imIxW;8_o=&1$vDeSf}PGqal*@%n6@OFhohI8U9`X?H!php9rCt2Z~f z1dhlDiAQ!>%@9#oKe`!+W~(8-U=9<&1{!v$Q?$IECK`_8*o|S$^NX=;GC&Ad^u}58 zWusdb+S52q<2bS8+zsPYhP9jeJkPGy=VZ07jsSnW)*7LdVH)MId3IEoYq9VrGOKQO zcXPSFxjwr%ujX3o?d{$D`}alUZnxX6hr{9U<3IXiH@dyO{r%tny~A+^r{^DjcyoRC z?Tc?#>+LijSJ-^`;fLGR`o-5@ynOj`e>_}V+?Ou*-AX`rcX#tVC##!Pf3{iY#N%=9 z`qg^9?T2+Y41M2odM_Y5ac<=%s&*mB6Vu$yYvek_*0kL* zI4#7@I0YP$a~_O>L=@o?O_XFFp-f8e96temnyT<%Htu$p)6JXgw(XNh*L8BhfsXUx zZvXXfJ|~jHetiAr`gS*d@x>Pk{_qFC^XX6i_#gktAKZKN*cc+z=dRYO&FX_v;u=VS zD%)fzUw`qN-~Q%{&BG`6KKdc$?FxEzd8}0Pt#;(DRo1n#iP0Gr_=@CDENII z$0_BMIpsceL$~Vs^_o-4-C)XqbgO#+=6N3HiS(FYyZ!Fn$`U-I(^VCQa5J1sS|io9 znTvQBprGrzKtrf8GL%_TN)#%AWrS)`p`lJ_d3kMSiilwfJ>lt@UQ~Gih*^Yk;;!q? z&o8#yZR&DLUA$>lOPTXwf7eM$DHUR~TFjVv)G6m)Rkf5-rfBo$d5#~UbGf;_vRZDh zZ~A^U%{7Glx3||_*ZVv^efId~P*$5&%KZAx)d%1GxO#f?;!7d^-X|X)4hJypcDM61 zefiDTkm1um`0t)QdzN!P9wsx}@Au<49>=|FB`ulFZC($^P-M3$U`L}=d^Y8x7AAbKw zzjJo~84};T`RZ?e{;Ti&@OPwJ9i|CDP6;qc*@&s@(y#`JMgfBhF;VEx9q<V1tLA2BMzG(0coI=<|H71;xpQ^&lBE30$xDW?08S^6T zd&>#N+dlp69}Q~s_Kk2Y08AY@o4bhQoMM15GqW)GAxBf#+*RfrK9tNUrQ9c;Y|?Ss-ObZfr%~r4>qI_t zWoE#Hol-;uMP z#w9Fr?V$b!gES#Q;cj&fYldaELh~3m0arC*4IK6q<_mCCH80?a9;ZeZpl=$JCQ zxRZ+r#-v@>QT3p}hwqjdiW@TvnPe97ULZ-B#U&9%5c^`|M_?wGP7);+W~8p~H`{)5 zmb*c^jTw+dBp1_h9G$9rnUB-u-R*vN`)*-j$?kP|)Ipp`+Q>jDnVaMq1)>W;wl%ie_hXa6Gt5qGx@o+fo_j}a3+N{0m-Qn18 zwzoIel5(k9%QP9gd70+hyW8i_p5&bOcgN??pO;eZZgwQ;W_NS{!Nb*R_2cy)4i}G2 zwUl!8`pvN%#_>>VrJT<;k71gu>U5l^JtcC4eJr}feNHL8SKRx)7in*JN+O(G>o|@g zIi6KB0|iRpZbp)b8*v5^RX_x|MSn*W`|4(l)si2=&N>`On-Hm8@CpFxZ&~m;rN$7) zVg(8iQO9m1+k5xf!CJ?|oy`R{r_|5oaCbcHuiyOYr@#8;&%gZqo9pL4{Jnqo|N4Lb z*tliivALd7S2X)ZNYXZ-4cl{=fg9kG}iq$G`LYtNRbN z+TqR1pZ?9Sy4B`8pM1(`D3NhUR3@FuT-ya)jt3J--rb$6j)xErawmCFayruCv6)wmT_<`e9R zm?z@R5g~GJF2PIbXIOKew&^gPHlhGhoJQPPVuw;SV{?P4sakdAN@hl~NE65%AVQ1p zfCj3=(pd(ZhUp76Nj|xIF||51F93j3$6a5$f%*TIQ&)}%q&k3^AWciP~UfW;K#(Um(bKE*B0vBeF--P1Cb8U+KaNxG{Qc&wO2 z62aLUJHgc)<^rlMgcI)Ys?O}pCT<{Rs#M|3Nr{LlA=A#)wlx-F~c%j-}S~qL1^{r?#xM8QX(V-Zp}#~c|u7xvl3Kt>NsVV zl(}a~spOnjno?-_-mNU*0Ua00BKt=KX0A)VfxA1ZYEXm0Y_*EWs_%q299IlJ&y_iW zTI?*05-}}x=X5WY_bQ;91}`jzssyIgWh!J=`>tEBhhZ3q2;lAZJm=oR5U)&L+;d`` ztc=Km)!O>G_wH=04?z%2@-B{de z$?P^CS6wErFqK5>E+-@})fN+^_?o7v@3V;T($q0CmF&mw){qzWK|4&(rPQZvCsjordkj;c$5M#b@d1kN)JJ{)>ALpB}0*b53Gr zBy48Guqu1tZwzuD zf98DyNtD0 z-$-^=E_x`YUZl=^NZus?D6JtR79MiqN{6bm8$nWZf9;dUrql_ipA!UsBQ*(YBo9%oJt|EKDqWd6h_e5+=!A|OkN-u)X7)? zC?U5|niXE)>^`X;i;i=dikeYXwaDysCuaz%`CKit&+LpO#LD7iyjX7%5x5X%SdEE? zIh;hR)@eLiE*)WBrE?IMYA%C;AOm@_dSzAXT;410rRFRl`e9H+=r{lpkUFueTeZ`; zL++|beIk-l=6Rk;5>m>2@V#m=bu(&~n;dj4SHLU0HI(sx6-q=g`}1NfAtHTFwbtCp z_WbPLy?giW-(PLdR%csM)LOO9HqS zch_o2-H=jIx5IAl?&sT0yv(c3#vRIb|M4@^a{2Px{XE$`uZC>ZM=u@ap%Y2(1*V7y znOmunGfPBv8ey5A$Vqrf3nu0zJ(7f(iBlV4;n0S#xI3(ZJBpTv!#(9a-J`opieA1M zo3pXAy0KazY6%`rr*B9#8>r7hiUy_=vY|C{pB}be)h!| zUu@1F|Hc3G$M>E*9jg+NX)SYM5#}`H4U0;|Rd|)4qBBYOb874lqJ1HzWd*bl3gdz@ zC$l|3PC*~2#F6$z4(4_`I@~$VQgFdZweF7U?hfOYv(yy6<`wT)EkOc}X)DcBB{>!P z6FBDKXru~lkqW2Q9^uzks(Y|*C^0ch_{NZto4W=h&<&;)?qUAipnMP+;I#r3phl7l z5X(Jy-$ERQ!2wDWah8slIB}mPNoOfLVK&RuO)|{j!(kf7Qg*W!69{#^asteS>R2Z` zkPsy)MFKZ-;$)^Fg`{XBmg7zv41|Ws05CH53X=s|q1$X`_fGQy;#s54P@F8~-p=e|V6 zv{tp{EHbOEjZ7$%#BCZa!2}vsYJHcR&>t3#?N!5xG3Tz!c_0>$5a&c4IYasO_O4%3 zzd0vyVYvCt?&j|D^6Kj9=JM*@$^wEgS2K4QLET9;7H`T3?N`*4KmlB>f;#tUEM=Yy zo))pkIL~I)+=N*u1=pAWNdlM=Ck@7K^ei={95o6JYV>%r&1yIFeVI^Y!aj<2;r5e4KPT zj_fcYq?B?l)9lq}cP3{?4suAb)r!k;hqNNc?d=ViA*o-layRUDJ8?WXzep(^kH>i~ z>&;m;%(ZMb+v@)M)ukB?-Ns3}KIa6r*>gtM@%u?kk=Y%_HA2BUWg&vKJ!eWOryR0) z5sC7IMY3p(N#?<`VdfK&TL=n(Bs7?zaAOuCaj(FF*hKhkx=v{Ih@azdicsdm>`g6?{8IANP-w{rX9)s?Kyw?M>y8~XFq-TXhZ82rfI*v`2Iin7f(L;s2GFcW{Cwv zrPkC**A1w}v_!ToAnZ<@N-04OrNsg~+CX6@N0V23h@;_dx$o*+YPD*Wi1LySU}nte zWPxdB1QvEC32lGWlAxu9KE`83k`Yzp4roVB{R&*@+;@-oPZ0K*|E62Q7kHi-c6P+z$oFKqLvt)#qv! z!)k5QDuYrYb!SFSS?AK)B?NW`cMnMeRr6*WVQUahF2M3)6}3vW>ZI>{w|gb)Fp zx10oWSdAZQDAk26C98E#DKU#`BSR(5OwzB`oDx!|)U#wPTL%^!crcfu3908!rpxQ& z<=xfgw>P(MD!peWo>Q(2&t#$5C3G>DInSeSL0e2lS z^E{PeI?pAU-`(vKQ(yUbJTh#q>V)f?n|`|;R-2pK>$~e4A==;G?T&{-tpM+L({B8> z%R}UQZMJ8J+Z*X}$~{*~`D~gdPKmtqnaBOk1FFc<<$QO$>zq@uSFe7%xww}_nEC4F zS{)bn9vsF=MD9I$Tm1^`TzW{|8M`#dk-JgT8ETf zR%VsV6eF0_5dNyQ-nlA_jM!bx)vdY{vvljtlka`-a(pvfY$>xpI6Qsu$qs=*oTByMh~1Z8q3n6HGq`fQb$Q%V54&T49bX~#kA zh(vUEmrO$9aE%(Vp#41wz;P&1%sm7VvFa`V(&pLJK?PJjQ$2=>3A3V%F1s z8WeG=L3T~eA~l>(+!qH4Up&V`zY`>LcPC2j6sQbNICbn|jM*5lz{nW+gqCt^6WY?y z-JF^$Xq0_FeSdd=B-9ONW~8d~JX7Dhnz}WQ$=v6hg_HN$IU#p0(&$%-t5uqgRg4)G zK%LBNe|@>X{&u(9P1AJ3mrj?CHFjr2MC!(HVJ=G+OykC?N=jyy3B%CUxrE{|tZsL^ zT^NK7!wLcQNMdI5n!B#V4|Ntu(C`8pHKcPV+b&%YK)bPY%b!dRR&7j{7?j=~5y}$9eXc4LV6fN16#H)UrL_-hx>9=r+@x${-^)te_dZZn5WX^bUg0Y z>uq$LZMz$_oYQ|Y(`psnZ^a5YDtt*N|B;O zL}pq_Az=@zBX>2MRcoYt=osEpX0tiD6G6ZfFO-`TTtLR`>>3+As8)??1wi0AXG-FP zqLvhG&Q2m-(n;o{&ec;th_H=&HtLXl=o6c%P8CAl@rqN&{YG-;q2tsg!@He(5JB7X zx?nQ&?nVCxUk-J1=0>goU>*+0@fZDMk4;OkEK?*0Q&9hD(PU!52gSHeOYiSP$jG@y zd(KfBDU9Tl5t-ko%6^iWg_1U2HYUVl7Tb9vD}Xz(!K<36C1T;!^+V2CMBL!yI+xUm zmP+c;Na(tUAnf zu0<8J7|gQJFbqN*(dm7^V&?1XYmr1G;f=$f3?BLnV6N`&i85!YwUTh3^Hd9qY`5EP zy;`l-?p}+z`#8>B%2sur$48GINuteWlTxa+c3GKx9FJx;PsdUU3muQgc^tdMS#>wx z6|J>eN@=dU>+9=St`gCHzds(QZaq(>?)UpX_hxr+77@kV5383iUv{hYdb3%d)9tOU zR;yZT*L9twzRy-_O8ETmf3-e4hwJ6**W2@R)ncQ*esje{-~Z%?uV1~4Xtb$n?mB=q z#Td?#!~1KVt9qG5B=z|pc*`V8;an7xx3hqqSW@Qj>{eC7lmT#JP|IfXfLWNCgV<=x zsyy11(|bW7>cCP>&BDyBHcFWz$iQZZ)l_@4#2t~;0XV=yoGOin!_C!ePUXdypZ)D$ z|KjT}U;g-?{p)}Ezx;3KkDgDZFws06hhb1HIQgJ2zltx>f4=-XH(+td0(`YTyY5zH zoDSby?Ja-soex)M=Rpja=UGL=JwK%s^NMOQgOLk~vkMWTZUS)-DqI$KVuqW|-cBq& zxt~+S5W%XdQ53-wBfc3iM#Wipfl*;>$*yA}3l5tHiL zy}4BBU&d zx|}FG53)L^oFua=r^V!mT7)z$PpW(Py)4geyG%@BS*fS=NO!L>#SlfPXk0`I8dyczj*2J*S%+_-Eh{(*U+O@CU zedb&^ni&uS3KR#T2nwKHL_px91n?&d(1QemAcs1ffoA5M?%v&d*Cj6z;l7q#^kB=Q zvUDt*-AGZnJ8ns;n{5b#aS2;&gmgj z0n~Xar5umP<8k@;hd+h6cjdVn!o!Ybp@8XDxpKhWUtV5Br1w4_CXu>r+gzu^G&7>L zR;GedFYR1Ks*Ek-!|Cqf-P>BLGdY5Y4~Np5old89UFW5~dHZlU&H(0luFE`^%0Rok z1W75hp3mAh>s?jXb!~v^_VRL`mZLlBJh>Xcs;#M+*>>%%+jhQO*R3^GLla1{LWXM!f@H5tbl3Q7b~jC9+FkmQ1D9 z^bmCdlSde#3?5wMfSR_ZUmA}6r^3lWlog3V#F8asB+-23NR)AiRs{_^ks@$;wi|LOnw-~Frq_W%CPtVWi!|9mld~40jvcDnWP}@zt z5ERS><{s)8DL(mLJh2Qn;q+w9cNqKL!oz%T()URkk+fq&6zs!I@=e`E(j9Y$1BPe- zma8ib@^IThc*d>@rW`>;l#37#3uWFnDC0w7&bjXPf#%-{B6pbec)b-*t8|8k=X&qP z2IN39H@lH!A}>RDsNuHk9cC>QOeIBCTsX5qM$K|3W|m-Pkt#$@ZQHiX^=W;%_U)`U zX*82jxwcjNwV<X?A@<6;DV7F49q@|m^@_t8>g91Jj9Ki<9 zjmj|l&Fjo0sbC0FK*rf}(l+!jMAqHfZeOL{oo z9=X`>W#N)G`T0xs*j#u<#e;}AsHi6oM?hR5o4Kw%TnR!EL}s|m^US@aLNy|+b)wL# z_iiFxj(2X}yIo$+t6Hs63cG)KzW^e@BAfQ5lv3EtxsW&>fBB$WbF*~VAfm!F&-1#j zHmwYLsn>usLr`};Q^KKxS3G);3Zzv?`p)NAi$Sq{g8s}`2KySq|KYwh{@dA?g} znYqa6bei8UW|nEM%d&{n>98CQhonasJg>s}>FTa++uG%#s@JV;*US0(QtNzveqk<0 z;`8|optZg=6XtHV^}b%TTR5U!+Hp#|7WTDW)~kjubb5Szp4ay7-Ma=nKjUz)(;d%L z`?_h{q)ylCwM>hkJiK{xJzwA5-@E%i{)hi?I-QD4L{gW75qbFC@s#EOz&Oam928gA zN;KRH@W($s|C4|5U;UT= z-GBehH{bT&OE%Be=UO*&gp))(RjUBOJja{5-Q*Gzw56x&w&Bk)bQX z%m4@T{-Ss5^snbsu>n$ri$8-MkyY^!l{J zGBp{NLnZsn{OG>KsOC@Z-lvQV7}ghwlLXZP0?>epLEfVsDFRmpm{t zRbnA`4-F?cIU9$jyf=p*jsSB`y~MYPlx}ook1tIP0FT>wGTc_ez)DU*5~N+7r3x{W z5fRjS2M2r;Y*t$8fR5cCaT5GMl-TbSsRof)IloCPHRPXNY zB;3}PH*OMDK`etC8xe}&No3pF`Evc{{kz}&?stFq!#@(y>3FwHhfs5C=Ab~Gjt}qO zc-N`Q>2PT4x^3I>c>4Y~zftXn)9G|NEr&xhU6!TyzAOuS5cB>0JrVV`nQEv7qixu> z&28)3n)!Jaao$=}Jf0tk)XYuo0!T-SC!Up5;;pi)X)dn!U(>ms$c z*1J7EpWh38{`~oPJkp)S65G1Y)1=KxDeLt@5fc9H&BLF5`P1=u>}Nf1?RY$f>f_UM zFa>xyp60`eG6qLPO5s|9yoHm+d|bktx^4RP+JhWv{xxnzCuhKuddDOvL(}W-yHh05 zjX(wrA!#ax0lc#0zLglxxV*8B(tMGF3K>e_JiMD&gouT}-5sVx8^v(nPE5z1in+oSkKt(7-NU-maE9nt{eX{ZVtgI`x3zNJb*jE z$@TXPToj(T_+I;Cc$)_zIB1?Hk}}m=CK)g_Gr(N9SzFgNwiZrO=PCq1JkX1qxglk= zX0~0gkl41(eACNI@!J6M<`2Nho0ktU^0*V`OY5!(3vsj19uOK{JdOzR2$)(3Y`lKnDLu)^+#kshBJyzZF&}%D z|F8&nerBYZfF0XZD!~P$s=b?;)u3e->wRKkW_5T_nP(xgwi1^zvuQNFM1*#SxnMtR z_OFa4XCVOAH)dAVb-kLIQ-(W{sip1F<#M4&`+@RuIk&AJkH>Gn{r2hU>2NsQpYE6| zq4LZx=g(Vj4{yHt={LW7e0hHJ<{sqd=O=Y~``r)6<6W7icgN#A&-uho5iD4iBYCh; zx@)cLbz3j)?!8UZM8eZFy*!`IY{>AXbn}2DBjEgUaRk86dIbRDZo2grlxlu!s=b>R zL5c0$Jf(=gQn82iza+sH8nYZo23@TjeSD1T-unE^4 zpbH0iV5*d88b@&g9KVNmmci(^a)7^6D+m)x;-AgGX6beIFTSan0 z&N2mnJ;iEYCWd%z*c}l<_4@pB|Mtyqe)reQ-P^9Z_BKt$9Z3zmRqCwokkN0|S|h?O z6J>*$5fWiQP~;{tLy(b~sFePR%m_1h#)gB5ix3!0k(=LmMcGKa!$(c0p#!kng-nmN zOaV$x`K|ir^|$aqkg%Cy7mj>6gK0252gUH(!bwmBz?u2uurMl|(0M9y zBZ6;`Q?3-4ct~4c=?n6X1mItO*R8##5U_}VSzFqrA|t-x9&X{`W|?6`3E?A0ui^7S zx$w!yZj2Ct0!npo>Q)1kyjE9_&@g49)>=3PVT-tKwy-k-ZRS$V(OLsj^hV@em6@5udbfyan$~qKrJytR{ebw$wDtu;Lys+nD{*XQ$PT8;p? z(3Fk^`odA05NxtSN~G0ikBI(eo0Ct&q+}6nAKoo@Hp&ViEmK+kH z*+VJuY*W>auu53o{`EW`-v9LD;jp-EOZD#EQIcon!NP13BZ$j9Z*U)Nln+VhzNU&8 zPI9;R^7Qob^zqYAfBjc?-~VKtDvMUhi8#la*Jl;&sFb^lP)_SiR7y=F3gVromdF)? z1ge@pZFJ>u)t)?12UvtiBFrojeBXbS5MK3(5npQZer^&$cQ#V-RMk^ZC}A>2=Bw;T zi@7ln97#jCm6e3@q%vLwsNQN_#(Qx9OfYL(rld)PcimK#8ARd+Clan{?N=$oQQZx~ z#8k~2vis(Av=}xrV>KWw5KTKnDqKhk4NNHZk+gem7$F1T0|+o#ga_4(pSBJUW}c)lJi^P&P=OT=L>Q@d0St(e zi`SegvTJ81b7Fz-s2nq=@xKt|LzPB9?jD42Cl3hs-We2Tg^IyO*ejrs66dMThqd{i zKmJ3l^XBKP*_$`_$K}*lw}3;MZKn1YN1|)6NUgP*s`jM2Sn#R_&d6*d5h-Hs!ljmp z$uE}|627~?W1++0Fiq1>Km6uM_2b76jdg47&BNVt|JK3A`0&fGrN}gudUtnv_jWm) zj?;XqmFgsnww`Tm$+@ml%sR2Fn!5^fu7j3&?pqV$BGkF8tsjd^{ef zWqx^iX&T^ZYsPh|;O?xTgrpqWyGs#w6JZDSv;(YDxOMAy%ZY;%j9f1li#Qw(m-V{T zQcC&bAAeq!MYmQ;QK#PQ^yclmckfR3cYSNEHRhlqRAj2hS}Q!(^GI1@8!p1Yo2k41tdl??Dg7{FOJihFaH zpb*Aj+Bpf8C_BlM>#jI;qEJK)@|<1_C~&4~a7j%Q!5oCb!j;S{;VB9TOTgg_780(3 zDkV8Cs>;LO-2s?Ix`p*!3DtWS5#gGxt$PGBcIY-wri^k)N3t+?6sP@|3=V{o6(YiH zsbWNm+AIR*rjThOcyOm_o(dHfT4Z@-uS<<{ZQwAqc7f`)Ubg1-^^AZ10S#lrox#i$VU)F8o7^92nSCkZTx-Y>?P%o zhx>c}Ku!QXji4huEY~J^S&68Wl9^eBlfuBn66zu3-93#gcr3&%;F_zS!RH~4;g$`M zd!hUghB%8IBw}3)(O7Tn79tLiaD+8?A{Hr^byd|`7c=wdAhr{>){n=NRd+{!*@%$& zAzz9+GPyybNG`+nyj_;^3u1}bQ9ufs%FLlvthT; zd#@}ZqRpmh0{OOWs#?m_+jhBJMCvp}t*g3exFLul=?1C|5ZmbjJK8&3FkhpY?w&)s znKh!gT)=YOw!`U2M9VZ=#4k^Os%3drL##w;v~71LnIeMq|}r^$1bD1t7@?;vINkiVQO4)RvL&BILBfR&8*}{01<=hVe;$0VBdwceBQbLAl8k z>AYgOQ|uNqg@Z_#r0yR56vQ-1=^%3t>f{>69_$Fwom^i57!g?qk-e(G`A$`J+pg@? zuI+NUXuF212)mmQMCw%MUfOkCdt1Fab2xSDipr%hr7ae}+FINS{SH>;$Ci{w01)Qk z0TdQIvVeoB4kcbbIk^x>{BB?p1DPPC`x_-zKMdJMczI9=Bs?^{46YIOEq@3BMF0;N zD-%IIHEqZ|=zMr?<6MM;A~^RZ3W(&&GLcGi2h7aWnR%Y3k>GX19rLXdD^R6?#WaXy zo{oi?DCT)C(=si`xz=T>P1+kGsr2;uF}@ZtF|#Aoty_{Y!vjtL7okim_fkew4Fy{_ zcYpWpovYQkygWUA`|bDn87+5<`xHVEF`pimyKgWZ>+x-Udwf`yW0~N=ROZ7J99z5e z^I6-*PzzJn3?+^LOR?T7SD0^G_mMUXA%-~zCMj!iC)VgM*GmvzwsYR)BH~TeBLj32 zBr@|`=`jyxa*$cvY_o>aZv=v)_s>+z8?$+Cw)95Wwobggyu5t#-TUQu0MXXh_1BF{ zndjNv*Yz?@OJ6q+@^+r4sT6Tn-IUyGt;#d9nnqSdbIK9td!vj6m8!2MwW;fNb}I&6$A1^Y0iiICmfkCL(e3;dCgbl?%9eW>4MC)*2CdYrkX#; zB5CZeIiDkBjQU5pd5jODIHAk1Zam4uw-7oh>LlEYyuq_VFL?nB4}?=~Lv zEa>iT2n}T}+ErCSG<4u*sI}>qmh_plY^J6j?%kUQkrb&GG8-e?AJvr{WNskMmt&rq z1_nEgtt0>ufe0eGS0R$Vq{C7B{QUIt{3(28a)&Vms7!OAIBuAW@k~WS3DV)$^;#ze zaRv*HtglhlcaqGjx;~0KzTod)cC9=Vmjom^^p!;eV~nwsly+v$2P2#SVxiczPDI4R zWad^4v(?{!X&+8|NlcC)rW!=V2Is+_jdU6=G`939c7THjM3&zG0}z?=w}v^Bq?$h> z%y%j-qZA=QFa?1bQpL<{^D@PtYedYbwFa!TayT5mwh|xvUNac6zv&shZal`EHGt(SS)*2_@@Zn|}MuXSFQh0}J}?R2^$qUY!5*B`;Hc#S!l zIn8DRBQ_y>6nI#eyE_GlK`3MBroFb_h*_z226?g>vsDVat(2T{Sh)#kvTC{^TmeD^ z)1JfBG$XRmXY?6HAsfUFqQGU8!qjR#G%?aQ!EjYr&$vJt=zM~w-mjsWD&e$gm9c{2H zqGIna;pV&TKnBAQu_qS=>5vm(awgV1>YRz$qq&CH3ur7JOyFN~EFQms2ZXwx(iQ6cI5+B+WJy>;?#?Ql2%@#g;F(}xeQubaEa?U9gl z87z(-1c5;?O>+)nKd zk3T*=J(X!%mgV{J@&3(&Ys5LGWezrqsT@l0eO~6iUbs}Lby^nYVio``(@cDF*9e~u zf6=+O!8R=v!SL+lGE*s+fysm1SCWIvOKF6icQy^m_qZuN)rDbMSc&#*EMfu=?GO=> z1S|Q7X9>VPMA$q-dGb;1ZeW%Ogmu>zwUS2;4EWMoWw$Ov*toL>OIyqxtA8bwU%yQ1Q3JkOcCS*O#(S(qI5n2 zl0h*EnYTiSb0OLHA!ORoYWm$r%d1!0AZXl&n!rh3Lsdz}&R9%Ip zzkFeoRgH4fRR-L)Ph_-rChlL3-@Sdvi=GSaQMG92d1i_M%TQ|*$6nk67mFT?8yLg8>?hP&D-kZV^rTTaK;wm0wI9uCK2oxgee_S1(Ce}TF0 zmk{c~chj^`}=z-3xMaB z3*c0En&*da-W~7msq##uPmiBJeC*dX*c_&$v%*d1TFrXz8s?XmCPG{)5tUN5Z3~NS z+v+5$>K10h?83G+n~QZ_d++Mz0K2Oig8(Xp!?Eojnco;1dq-)BJKPwtVGa&rH7`|K zYfvSkKF?CiyEkut{?i|pI=TD#>E-_I!{y~%N;}@22wqDmrMPx3vc9yb2r~*ZnZtWT zDB!`WXCg`%=d1Ho%~>PL=wrs#XSi|Z=hJ%^iBhBpw*i(!@|9mPfU|}EqN2#rHh84i z-wN?)>ph65vP6>J^YF_mW@f4s?%GYa#I0PpwQ8PdxT^B zc;o_fUw34~nIH~X*Nj6-YB`v)lp{@Pd`EtUS=3oE+C zy8x;R58?=9Zwe9d+%o3V8k~dmfYgbI3Bs*A?*jPYiOd z#Mrlh1Q{ME7?^9{8WE+J)Hqkv0XNfZv(zkqY7Df8k_t1=b7yJm7KCYK_j%+_SxOZV9byKZY8AL^nhr_#XeizNxTK(})e*Ua| z^>8ji(?czbO($8JyEo{jz4xt5o3-BEO?$Zw)M>$zM}E+&mUh2eZ{)u5P0wxkev9YO zNI{I<$SoHKw@;f0PCHCLW~X#oOFsi5A~Kl6M=$k?pqcksAhTbRLGOUG_&At>@ZoYp zL=nmf8d8!=LBqVWn(OXxtfiuo5Hm~W0B~-%%#8)iV}Kx{Fl^T*B%spPYo$_HLe;Gs zniIH|V0nK0{QKYk{^PG-b?(eakq8j2DRAw*AbMeP4*|TXstU0%A>4a!=D+^=m%sVD zzy0B--;l`t{hNpTw|94UYum1ClR8az%i-=#JshYA7lG>e@spij%r})rL=$1@+}Rje zKD02E>9DSs*4i}YbYZvax~ev4%{}VaJ@}x(g}HS#v({S>iwyH10JYY>wW-L~2c!ce z85usHSfrnIwmI2xnOPx5P#QnC)SAm>gM0^v*~f$ffI2uhu&kIhLw1Zv%*3s-=I+45DH?<){6aVh0=I&E(r+GWvg zCO_0dB|^$1$2V3)!~1o;T+X__AUZ6#n|Bj64l1{1{g?d+*m!-P9sYgU% zG4ixm{?aS#{Ebmtc`t|_-Fuyv)|!Pk?H29`xE+tndOd?=ss|G8sxmXS&WOw9JWUfO z2SRGmzBN}@r9hHIO}kV+9uH{_E?lyH-M&&@?>9Ecmm8fd9KNrJIP+vYqV11BdgccM z<{mag09jQ>{xu{9T=A85iKGHyrrH|2F|%;8EX-j<1UK`pt($iNMOd9`G){_35(Ge= zVriC4V9C7Z(XA(Zam)FNEQ1~yX_TINOeJ-iS?dq;(zllGF-dZIeLW&;c`_hmkEz-l zsBzwhGm*O{+LMXEs?9+J7ZFCmz6q1j8FChtgaq8c!b5PM6-I8gBr)E6AxMjfOc_8L z@tZjjgv3bQO{7xkvE`460WGKd$ynW`wuza6GH6{C<~=)@Qi_GUk8+>qyd_U)$E%5` z)~af)H6p6Z%&o8MwIH1z2~t=XLHUxGT20){$Xv|H-9S2cPY_1te7ixU?7uTrO6f7_ zBMg>WUAP>l(9^UCubU{9GI7O=UixL#mvejhyk0+Fw-@j2G6~?d%2Z0BaVFjr%*a5< zOiFYN#~>#5G|_E&jRB;7j8bvuupCP%VsK>O2m^Q=lte^SXpjvvgDlyL28u8a2!nXa zRijXpB71^~WxVOocH2-@2P^}uz|_gXfuIrubLNtAVwO zODQ8vafdIMWm6)q%^dKOGebUaMvJr5i5UoZByar$IoWIZe1#gh&qU5 zYtGYws$E~MK?b4j1Iz2KaM$3w!(rRDrrnz%4dKS#9T+590C0l{fl*libU0Os?r=u1 z8!Y(o`7#x$m0L7H9FNC>(x?{%1*}^qC1T)ez2}@aFN>7gQ#Hm_n}w-}a8du7utZ^@ zW~Gxbyd$KPLNda`(!JXe;RBQ6q_rZasp1loLY6obfJCpa@q}hYAZ(xWkM9E2U&54dNg&S0}(S+B`@QYNiTP zFe{Z>tAM0Vs@|G{MdkyCg#~#c&EcL&gajcW0aK6~AuOP|xbRvkFoDEcBZiqK^91B9 zPV5G=)cp!^FoKQBw)OsWK0v!Q3Z8MWwZ-~E_HwtxBoB{LK5kEl3tJ&E=T>e#$gSPZ zW$myf;Jxie)<5T0Q{g@#j`3~s$q)tQx@H8#f zH$Ki$B^-x(cv$Xco=Tm$$eY6JdU5B|!-E`Vf}MZ;^Y;AY(Yl+6)Rf3OWU6JVnc%SR z3@JsD*42v0T(`?b1Kah=(`@T`b;ae`-PN7BXzzV%0G4H0+qHLd_i^(BLR5uYTRmfp z0;<|AzA~_SC85YRDAC3+-;<&TF1&45A~1{@d4;!aeYt2U)y$?+sosX39Fs9kle^(^ zj)-ZRD13FJU}i2c{l)NAvEj(68 zJAI2O2yQm$Z4n*{XUm-5(A$&Zu$wGvq+?RgAQ~mhi(qnVdWtL($M06x^4#(v5y1Uh&yr2Sjd|}1zbt;9d z`09H(!5IY&0jV=_cqF?Za_O1@lDVHHq@TNRj#bRbM0Ai_L==ty1mu)!-{D9c1PYRn znMpjzg}78M%P(}vPc>2%;kq?p&MzBbXtpIZ9rnCtxg}D$UHCbMA+7l5WushD@ z^}ywDw=8#W?~X^^)^>gQ@ZtC8=NJ7tAY16GLi6R0%r!#xj2YHJEF$W3|Nh-~zx~~t z_ut<=yeTrPM`7x{`{O2W@29tKr!wPwJ^%8v>LsMjs3i!1^!!(TBM>10U}orlgmwH!INyFt z;3jSwPR$XfN&tbS5KxGy_x^moM0lC%aXH956Op^G>l)!n^AM5dZao*|ZrZ%nQVO_~ zQl=Ov{O!wHmuauQ=DWc)1@?&eVIgBo0v@di1!8BEC;SbF!vzB#Vcz{sK{R%TIh9%x zPe}^b!f^LQVhBkc377D`*I#X0*R8j5I6mZMry@#5o?atCQq(N54rB^ug2KAP8vD*O ziv^9|XA}!}Pe0-v2^olVjPSp(;n9=`ftXpYaG8UNJj}g)>F1mv5U>TOGx>nDiewo` z+l(Qvia{0$A!c#}$Jk|M@8^abAHv)KPP)sDD2L%qPh+MaAb;tO2<&pL7@-jYSo(@* zJC@hS-CJuJ|2qYAyQJO%0MH9fdD*v?2V3l2E)kIpySPF72|}1TSco-~h1@8dNl0oT zP^Ru7L%>G>RX*>9>b}B3MzbazGu)j$s<0@g6cRy5rsZ(x>rxk;XZP#nViN^*1G;b4 zwUsg*?!K+Zx6z&~T7Q0ivL`Pcp*wkCZ`mUP=>eSE=>4H3`a-ISeaHCf+2L&bU!4TQ z(ZD0wP|jeyB3AnFRTvjPoFYJweT>gx8R;JbMpDbjBWY6rW56^_Y*dLS=ZU~vN)F3G zovn{zWbR5(c$IWOAf)p{3{r5iIg@%{YZWO~rbO&j$wQHjWZ|k24ywmeN~Hs$8i^e4 z=fj6vUp^g|VP5|@k|5Ms&Zw>K(c))~Wz?0UVX-Q#-c z+E!O%3-9{!^5Te6>b4u>Bv-1nrm4!HVFij;mbUKBl`TPJcVraL|3+mWpSy<$?d@dR zt)wHMPxBHiFV~CqFbfb>)$O{b6>Y6ko$EAB6M_VpUSBHLTFJxAwhTF$7ouavX?<1P z5wedF7LGJO;gQ$muEb2KoC7h7b;lkV83g7+BDY!|895&0$cB`5xpj;*j#uxO0%^Gs z`7iP!GT(wdr)i=%6s@Ijoo7l{Tl0XWt+0~|r!O}N?rODs;<`ME`C^NJ1#+kc!3g>``sP=?Y%q+ra#I^^? zXsmeWp(h_4xt#Z#e`bV1uqW*7nGboh1)Qkt7uIh#s>3L$K<>U9Cgi^&WDFTZDzj@q z9Md$xbY0hGnq78sLq#N*+*C$tp43`N*p-HEHPTGhiKx=JR!S`_sRu@$uJ?iT+QJYY zaGAhrz2De?d#Oyg;mcyfU>d%AQp1EGrO?b?8W8gVmAH`lv-+j??m^w1q?E(mH}t-A zA^rVXHF|qZh4dR$k%-)q4?{#0;8)zN{5yFYy(YBeJ(b_T@y2$$y8WIY$~3NV1~5_D zo@J(dVS)CfyA!d7IypEPl%oyt*me)(I#!{PpZRpnBTcT+jtGvevb zKSLYanM+hTsCFf{#GeY_Xx0lAo<#z4oy=@q*WL|A%%yiTidF69>6wVOzD1SKkDo8w zI+rbp{KRBlYq&4UP&h>b2ZOobxN6QfSmvsaY+YQRGjH~=*G9VJ( zW0A7_jSxNxf(6K-K9;x?p&sUvI2xm*RAg``Nd|nzPLXXxL_~;15I8)e6`aV)fJooF zY~honXCQ)Q`(7r2M`HX5GZGOCaUgnUSUOn<3ztc1Wh%WYwW%e)3nTmRjoBvLP57XS;>b$`8 z?(RXPZoO~4=SHpNmv(v;51#{m{-?bH(_jDLmGviXZ*#Brdk3(m(By0M;J&GuiPS1H zNFuzcllQ!1vb$`pY}=OM66q=dJq}b40Hw~Q)YmUyUDpMWgAmm`PV&pT?*@^h zE?c-N3BGiWB+OheL`>riMuZcmHFywF`0z5q$aLm_nUm~TPy~oA`WGu0FqegMkw_Ax z7MkO7ro#i>gIp}Vw35)1=azdq`jIDvE^TL|WDvF^*9xSnUArSlIAGhh-O>k>{GJz~ zlwzS4nkZ3zGk=0v4=0E1@MsjyX@#>_P2cSNR{%t6zS&fhxI5q}%A?>+umzJxa+xE{ zc#x(EfFar1x~nLE>7D?USxzbFr(4c7n1q?aFboBm*?lk?$-^wndVc0$GE3o< zLn+g5vfcu@y34RoFOb_1v-1 zD1Z;c1IiUV-G;bV4=UX{@qD^pJhzsSMhhTO}UM)E=oLP-_ zQn0`!xaJZS=0OEivJ`s_Nzx>U5M&CEG(yVwt1vStMF$kF;6mD6-FcWq5mN!8aKbf0 z;a~=t=j0e|yf-H&XUiCABPMSf%#}-hfB5Fx@9s}0xZS`1q1gJYUt#XKf{VXsYx4p+ z62Xp$%k`?NMDgaEcYpOaf6G#S{`JHA@4o%vr=Q-ud84WyfBDFjxoI(Tb)M?h8waEH zY&TquJ;I{0ZW@9oh5a=dim(hK-r1Bf`DjFq83d z00;_(hY$1vhk2n5eV8p&mMC_n`1y@zMGwtE=E+}S;-U}OilYq-5i+S?CEB6EOC zo#6%1Z^?Wki`oJJ*FysC7Ot5?!b~1!IvzKvObBoUS$I0fF*8#k*@IneEHE)}o1S~| zF-E5y&OY?qp4dp9JCU=))ZodviZBXub5OV$0Zf@)HK0z9wm0%*o+gEy9QFNt3lBvI zNy291V@xK46YWW-Gzts=*8~KU#*~l?6FE~vvM7xZ%n%9^>b)~F3*T@m0jZj1ltP|E z0ywPe&J}=%-43~km`hbPfFOd%GfqLwQizC&Jsc!p0@B?!=Je7@0w9as_QXtpz)}wu zH(+p}Rp!yvTenh01ca$B$GiI;UHOgLX?wcr+Mm~FY*&50G`m!ex%lcJKAoo7{iV3S zsc&OBlzjDw8-OJ#n=rF|VEqbvad&2UJ!s#Mufcp9Y?o05h=})DLq=$&h>)7gIGiKD z6XX`UiV>0L$`TwLBye&{!_I85g{csia0;lIs(UWShO!P|hlRlu=}JH#L}8Nfu>uJ~ zVF{x2?c_-$d2J#kgMFK6@419e-6mQzHpA83$rN>(?jG(>r==7&J)fVK`_pAz+ta5H zfB5-zWf9_6u_ga;m8bA1)0DKFyNA2)zyJOp{@s80?x!Dr^EZF<-9PzlnWpFSd3}00 z&5K=LnyFL)s`a*T*|uw)#f%b&<*7!pAb_$Q`r0E<%e>5zHZ_k=pUgI}7=mW0bGi10 zX(rH8Ce!x(_-JM+T<%sApxY=% zXk8`1Wuk<13(w2a*Cs{C$Q`K&7iMO()@vWn);#iU9Mew}#fw3kAz(N44H{{)yyVe2h!Bs&J zu1Pq+NWzPmsuK+11j|*Vb`Er~NDU%&b03*G*~9kU+=Gis%q%raV;g5%kZ?E%P7tXX z2YBPFW{J;cKNY6{*wxbIYBb2%SbIo>CcqQ!TtJva5Een1yef&g4j3f{B#=N8g(C#gBV&(`oG|qqSfe2M4vS4;_u!z*)U~~0u(A}`m0dSF8 z$ShPfucd}(QeT#P4pLPLLU@?NJk+#1iaqN2+P2o3U9a3$Z)YPsw>Dj`9859`#mtjQ zQFv=BEpeKUhw{$&O&PY-04P^Oxw3YOG9sb|jDVDoVYot#ry!YBA`&j(aB_w-D9jx! za~0yu)Gn2aOhn8f#YM=PDiLu-6jOE$wQv%cbqn_bPz?$QL?Svo?cW5oQiAVAqHR7L5-?L`*SUV!WUN?wli}2@@$y-NL+tG(@dLl}NqEu&YN<1FSdF;9zxg zhlos4_#i!C8X~gw_2t8QcmDt`>-zEK(}!RF{8e#pedkMMr;)eot-^aqfmFEo;cl)( zfBbj<{-6A_e}4b%!^3ZX*n;@l+vmqBXr|0G$>i3Gcla7_rUi%P3A}7VuXN5_E3Y;Rz0GZr6ka%PZ0yRgFq);vc z21=1Bjdw@A2YHxzH$XhI$vDD5MpOX~qaY&T2z58#E+U1eY7U{~9Yqi^L^YUEMG{ho zG?NQY|JYKdg5vJ#3Rpr(79KIcCcxY^U?jm7rj|T^;|RdD6EuR1S%Pej_M6J2t&;`M z1IiS4JQi{MifRyHY7xt zHj`q?-MU#X#IBYbOe$5Wp=%k|@Z|GEU{2y@7!jF=s#5wiIEcB@tg3Fn?%>#a4+fD4 zSLWjBc`FshokH@1c4G|@@jx?W5&%L7A|rA)Bz=T9N&$<9Dg~QOZVus4kF{Su+sDWL z_~xr_$ltA1`d>!biC;a#-$UH#hi#ekKt~ursV)VP44BA^T9d zBZ;a1)j&qY)FOq&gPckPcM>YZaMcLnS~(ijdjT;79X(*BNRlqB<&+{j_``4mg>IWC z5#E_5nyQRV02s_cP)B$On84-%4Us&0J=ifphy@%b*44WkEH@d%Rh-onhq{Lj{F<96PztG5mFM5qU0xF?4?T+Pf?-F-PM*KPAq zX4Xx&-inEZ_qF%7!i|~dDkSyOr%z8Wk9T)>kI%n;etwe5wVXU~y?lDP{K9r3)YjHg zic`N{KQkAN&=KZ~T>w8ks)$lBNbaLkiwNR$pnMJGy}BG8h7Jr0u5$&bPE*@9X4ddB zPeQz|4ZvZUv=6TRSi50J7}DV)nUO?Tugy2B$Eomfnx?<_2zIhVbW>_Az``IZyuZv{ zj2?xE(y*HbZ#r@CVP28Gj4?c3hyZp9kCdnTV3xQ$%z+Wrntf+Fm5lp<3F-Vb2+`SP zLM@0mZM)5yg%cMV8X_CaT^}HWo2zzpb9dE7!F3J>EZm_iHD9Xln;skTRS*RisgZu} z>0K4KC;bNdQf`Fui^?t;DpYT7r{{+=I#Z|A_;(K>lvkI$d(wyLAz&Mz~B)& zwwDoMk*;hUkv>KUCeVJq+=7Y%d3unr(6CQP5_%F(fJ70&On@*0B(65B*oB>--NJJc z(LgGVBU7Txdg2d6?7pK|MCRrg``dh^%T%FVReN-%LPc1rh)^NhHez97%d49j)>6{c zVQ@nufIZYOeCOIyQu5V|)Cg3F(*UP++uC;79&J0X*ZEZEGA)~s!&Ys5?OTa&%0116 zms8>0TZX_lwoHtZ^$U49_Aer$!4ufMVRCK~E=gfY#5`>|tJF$VCh~9+lv?UxUQWrE zl2w|WEv|}APy=_k!8!#6I77DcRAY~DVJ0JTP!ReMG+FB%= z>1QcRL~PxJ%kj-O4-apT(^uO(W)vC5+$4Kl;K5f&@k6ckIL!~=d~-EFy#MCE{lEP` z{`C8QB)3h%=a1t|dvu;-Bg#zn(Js$71N|E^V>xc96 z%jc(O2b1upPoFO9b)G9TZSC^(`Lb@$ZF^D0<$4W9zUD zM?|Wg8KYyUAG_9ZE2VtT0b)o!NSX4U)^(egqroUbgss~&O$iXs^BgW_CL+uwc`RTl zECLRXwyvG%ZkeZPqJXMy*K@b8*B(W{!-9NN4Ip&SsNZYH@UXrO%)~+Buin7$gd7jN z2QXX`nL~pRw2NOn1N?415I{HLH?oo=z`0E&Vum<{?CY7lHHu6SxOF!V3FaWnpi~7k zoXoqYYra{8ijliJ$jJ(j=KVMGud(*@8|FSd;CC|5Uw-BDp6{5Mb6M&Z!-mS!55!aD z8i62mGoHrX;bxMAx-P^P0F^*`jgQ{iDFRZQ(8Kq44f5efm9j}dv3t=DGR!MB=WB8y zFq-<|_-&SU<6*Y7W?h-t&DP#3*9dke*9e%AA-J+khdVQK?=jdxNLZ%BgAzc-KnRiU z?8UuBxcL?vfwxj7E;0{d6%mnVwT1&w^8_Pw=XB9d52ZAF6C_$~hzeAesn%Mn%*VRC zIn*Dfx^%XBUM3|XYT_Nn)F+OG%j`2udT;K&7xyFv?1YCK@^T}S?ssGe7f=Sul$@f{ zmn%VK2OvQd%v4LMMI6?g>T){0{o(lLhr{ung4=cNPapcT_jYl1mN3(<8bLV?no^;? z4ulh9!qnZ!xo{CJ$OF_!h}Ap_rc#-NJ;=F;P$)$pBP)@MG|oKnC{f139VDDreuhMl z8zN~tKJeFhUQWk1_ix_L(@{hw;bY-S)Yh|^U)DCU@ZofN|80GE|K{Nv4qRT&fB47W z|3z`fzB9?*jdte{Q#EaeM7)^)*Z<9b^DqDR|JlF&*Z)&to|yi8{d_o14{z@& zjGJ26E-CO$w@@P@Gpy@c=7QaGZr!$R+oolCeEhtvmp}jEAKJFoGL>o8Zmn%1v@Az$ z{qgbFrF8lsbdw2n%wV7qdQ1|o1 z_a01t5!P|*MpMR;hJ3L%?6-JTPOufomMY?AddK!|A+Wf+sz;VucP=ulnwgk}36BkHNsS2dF()bDYLYIl31Tt0)G&4T-n&rc zq$(s6IwCxN!t&ztiCE>ADDD7t+Jk>;{Vy@vxQ-0x=gR zb7(g!RBiy;zS=eNa43VuHZwA@M5SahUoaQdrkR%+5zHZ!F9Se&zYLEC8v74-Gwaq} zTLYKlR1p*;X*{fZ%2ireGLhhmjlR1FLOh&PQ9{{Tlt@Xu9PaMp{cmKdCq1?Hv7Ih+ zpMBNd`{LWB*=k0>)QMy^i%)I9!c5r1)}&<2$;0SE7<^*4Ee_5i+KPZr+24S#^X8XSkN2$W&`RmZdj` zaS{^9EP^S-4dDh93KF2WAj*Vt`ym>xP81|e3}>kf2Gqi+5Z6RZsbco@j#aKyDwshC zo2G~3-TnRD!`<xq{D0k@=HM#Fx88doR`K33n^$iiJttdemqJVINjPS^>#hzNL_itLUo~fr za0?r*=ff#CeOm@uNJcJPK77dasRn2dw6X}>!{Vq&!Q|s8C6U7HTkB@VVBUf8ybfwA zQ9%xe854;VqC91o6L2dfn|0pfha>&41{CuOk7Z(j&6>NH@mzDrNKDLw(20qL(LXZ> zjHpCJDx^wf`m&ZxTc{ifh#)p7^d3wSVH6x75po+%C-%fzcQwy+z3d~9pG=NK8UGmA zag>69b^{MlUMZzVtn2FTBBY@L3IdA+0xcrkskF8d5pxl#5t`h8cSqUvo=DKVBp2Uv zO7p=l%EhGXdfB#N*IH^MO204G-K-Czoypnw2M*e*(u({g`29*=SegEepK z*0BFD?^l@D;a(H`sQ_O7ZN|*KWy#4%-kAM0{Tfd3{ zl3nwX+^1x`g2K#t-=^ubl=9bq_jmv7U;d}T^yfeR!(adO+cmDI!`wAMdd9-k4X z@BiA$eH^~|;rs9Z&HwoSc0Vkq(|7NFJik1C`1J3%gsK;w^PcqxLI#r$WSIsil*Bx8 zk93@(aU*@HgM%zf1OoHKR0pn^nlGMaEQBb+sxQ|2R7$Cch`YPH2yd-1^Ky5H@bz+) zT7)JIfOzT{iar1cS&U+#;Wjox@}Z-w!;56PWf& zwLeo45gK;;Tzt3CVQAA{I0;my;lZTl)+{6(ll6+}h`&lk~3WFSuUqg5JRL!tD}|;!=VTC^z$<+f|Mj((h=|IX-pe#c9AP|$oi%69^ z*8`&f1nSA8^=_^~MJg{y4;c6|&vRWOg1CeiE(mfDcMv#T~%A(PdPk4KW$qRkafLYE^4;6?U$EdTU+OO{_ylv>nuhr`26`}>+$R5 za(MgAr|0XtpMLtyKmX^tZQuX)w}1XU7UnlP2nCx31hpJ#o)62sUC)fTUd|Le&$G6xNcra7o7T3iU6*C5 zb$0dZ^(rFWv}=#RT^H>{CE?n4SC;Xn7#y4&e%}v?K~mshUI5$-H3$@GLFx#G4I_+zxvUU)1{ zBQPAevLbn8=AAHGH!c!n*k4_q6FFovGu(rRaJ;ZY2~|a;-*sl!Wzqi{C5SI&7XS&f zGkb(95MgfWETsq;#`-i#knWDmCyuy50weDN9Ksw?Xt!QVi=kXI4D}+XO{edIiP#Ol zke=Qc0VXj-34t^%@Z`Qn@~OT=j(pKh6A2fp5uuxFHx3AupolMeW8aUY@xF;gf(Eh0 z-P}}FOt{qXPnZNXCeEz$;nY#!%%pDC$vq7T0XBDL!7HYLyQdevBXas9buaGjY;NN2 zPC`?i=Xsu&G8eI^>a0_OLtw@m+&}$eyk+mD!g9Z&Ok4fg%0FA zhVitvlf=Wbha%l#Io5y&^HfUI(pCzl<8tD1AORO<#xhq*D_E1UK@~-B}py%_;x~|M@hR4sJFPF=*EYHu+ttm7A@sEFGE^0Ja zT%KN9+diMWEO+Nm+a&h;fB*Lnzxi=K+^<_4mWN+|{`>3s=R$B>gpN{3W2@7Y2%|7m zo4<;Ya@X5ZEB_!Y1f@YK`HC}>B77ksE*23?m}Z_Io?ku^Lc+lU#Co|dGrxWN5Nbkn znC7+V)8|K3Baha%2Cq_z6aedXr54AjPV-c2A>o7w@bwoF$-YrZhw)3MUVz?7kV53p1;lt7c^Zz@BCgBLjqpGA+D! z%^#SZ$Blxrd%j!3)65J+Vd&b-jZDDA%u-8q&%}@2stay5*O?MF;!HQ*ciO^W0Y|8Y zGlfM2RRSTIJJ=T_1crs1N4WKlk`Y>jA)ntD-DVMC?o)D(-HFJold!slC5$cIy1P%) z#G3XaqHf{Yu)6jJ6q#x%#dp{wh@i+sA0OjJxT|J9Te5p|Gsp}e$#v#Nw}e~k;RQlP zM0Xn>5-Ft+k+$Aio0b_&p-$$Vtz-)yo_kDpsCrmPOf^o$?z&&2idV1SOpotYWY zs)x9fI;@0yovRR7UELipP%)rna7p%MBrFl34rc}2t$|6}MXo}z5FTc>ZcTeIVqP2u zrLB)cZv|AZ*LM9COjJZ_DHA3sY|*EgO=8@hQj>rMNW z3rMOmq%>J36y~Y+fG~UMAZys*VcnF_DF7F;xnf0&VU=0T3LwHv?%^A0xOG|B*o_b{ zM0A#OQ;w!}CX#M`@z&saj4Q2TwOojszpNkYye!M47EN{fTEjD!c{4rK>A=~x6BQQW zNkpf^Prv)CpMLYZyZeWC@7^uTqSo7a#Du4ZF5B7 z`uzNyn5njYxm?zDjllEEi@I^4&z~Q=VmWZv>$RVmilTIC=-p^;(=wgz|NY=+{P4=`fXTHJY4P4G8ww>+$?&cu~kc;}0cEKGazWH7=r7 z%gi*>x>_?wDH0sbZQHI-4|mINe)IjjOwYf3yj<5y-+J$bg_$>NC{mowJgkFds-;de zO+{*vX&Me+{1p|lmO{x}fHI<{YPyK{OiZ4Z8-yTo7!yUzZp6XK3SkarrnLHo69^GjV&_Wp6v((vA<$H&i0IxUVuWwA3^I9y2$?BS#2_*Z&%&YF zCo)7(AS}s^B?gFKXpI=u2kr4mtxAHu6)i>tU)uEx%jyK6XQ zXf2aVgsas$uf1j8O_GZlsA_=_bD5_Y!cH3916x-G`MSN-G6U=e2eFMaAR?SlAd!T6 z;s|q$K$Akn%&fC=5W+%(h%331n;DP6ge`eWo~Tq>C5R(E3L=U@2|h~+md!LwNv$&z z`Q%}f1b5+T7TWqvFjtCLR{@BM!oH1ZX_^>h%}iAYW~R+1 z7U4;w`6ONasjV051o2=ip$D285&PEH>F&5~y(|X^muV4)we9r+1He?I9_ctu1zecH zEVUeu$EnnB|H#gVZCivbhlBQwi0)1Y@7h{BpU>O2 z1tIOg+PV!kq*+)uv))zB9Q675X}z{m=I#0Huz8-DC4)RzIr;V*# zZGHI^lXqY)4`n&D*a~QE&3a$vgXuai)4Y_+^@=+6S5o!8((gX zZX*MLzu;K;{}2$4pq-XBu#oKXZ#1OAHAZKoYkZ-|09yr&5bhX;ooT?JpdgY`r<^** zgg^vRAa)ASK>tMqKJ-506=XWeSE$-f)Om%onFWGUycQH`|EIl&nghf&Ur+G>kPgX1 z##NXCX5rp7yug6m>b>IE1HJcjd%tm1B4VCrGc_}GBN{gPJ{%5M7`;1@wAPr5nE|1@ z0h#T*orp+yR5gh(eA_XUsWQman)@_OB-LTAK;F0GgCq{|NX2#pu?>e8kT3_UqYo?p z$opYLKCwj@F`9Wkm>PKQJBPJUe5@!GD>&ySb$X9}Gk z7LMb#T07gUFjF<_y_ZsPoN8g-dfS@1x(5tVO383zWb9nYF_38g>{HmGeIu!|OfuaC zb=rie!!24*XA+JDMjSgogei#J%{3U|=1AH?ZsdxWSSY-5t#uYEY>2La6^FTX4-&*g zCYF!pgE1G9DpI8~NfZ{M^0dB`>5ivzJl;?9j26C5IGtYCl(%p0X#ocg^O1;1 z%5prtfB*icpMIK-_tQL!)NQ?r$my_{ckP?0)|z>hv2GV_tuU-<@2i_GQ*qU%s?B<9 zq2};)UDtKpny$TdwQjDePnWHh$$M8-4Ri3_!$T?Me5Li;+U07+sLb_rlFr-tZ0)(; zf791brQq-|Yv*FimD=g_5dBc&40x>)VGqaq=kr+sF@`0bGsgyFl6OgHhNOyZX z-Lb=Z_pgn^GIIwY-wow$M51vdQjQ2D?ZlXhlVNwA&4(>9@wC<5F{m^kNdNRy>hH=` z7{E4CeItkK?1+acFy(*)3ndYNa#}aqb>V}YnPyGnX*aV0`pXFRa!2k6++D4?8ncx$ zu^UW?!Z8@>F+hX3ts}-}BKu+z=0Q-o!Q4C;lj&2IFg<~w?t~P3HADou2nEa>>;W4A zZdpkg#6%h>RDz`?cZAsqXi0MMo7rkEm2dun?yjEFtH3aOfe-7=yRq$i9-?U?&csGgBt*gk!YBoatTRrt2g61!qI>rrHBDrJ3`G>Rm01^m=BWt z%Vd(VAp(ygPT=l6bTei%GA0s)F%m}s5i=Lp9%1>ikPnV^X6{x6!z3#V`M=C1Lm7#q z&gFQV2qNa?4bc^b<)=1S~AXLJoq2gfNj1i$wq|jIb2EMMBE*FSAmuJ%-|JUW>pD2Sbk0kQcSL3LUVr?*mSSKWF$91b8_PRFHug$W$zxg3{eSqOYQ z9v|L)^XBb0cMoqPVm>VHemoxMdG39?u4n6dIL!CQ`=_TTxDvxR4eM=dFVBzes#~Mr zm#4?;*0yfsv98p|Szl4??lM1-pb@zypm_vdRp&X5|NUS3{= z;_&TH*UK++5jcf~ZjW5=-hKO5dRbk-?d-jk@@|?Y-CU1)y=*%b?8c7-VPG;l1^5g6 zO&mGlmZ?tDG}m%GEyv|RLU#}MrIhpe{P^iHf2PBkWos>oK-cwJYb{c5h`+-r0AU}! zb9uhJ9Qkyd3cm^u0FZvFM3lBfkdjuk?(Sg}N@Gdv*>78KJo02B3++I8DesfL&5khZ z6fn?D&6q_q!XgM+T_bL-->?N12En3diDs7k8)hR%W3+DWo>51`Hz3rF1G5O!d~3O- zk-;G$kh$f-RtM=k$*&Pk z=}ly2ZkoFj-!|qFVd>27!F0=B!!30#!nKq_!eoTtV2TK);slDKy@weiN=b-mjC$^a z^^~W+fM6a0McTOU);ihgJ0-`Urb^4rJ)#Fw0g6WK#0rUYstHGUDZv~};Bcl0Q(_4w zX9{3#ZC$s%Dcf|a?-n^Sp$M^9mA+W?1BaI0t#&C>nN8CXb1&J!LK^Bxc}&;lSCe%* z|4e%V?uPgC`f!o=j)>!r6%q%d<^eG-9)l<;uokRq{Eft12RTtEVj?u*o^2$fo7 znxrzsxLe!Yvo|lTX-%rNfBWXX3eWXU zx7EKc?!Wu%-zC%Rcz-v|hh;fVhs6<7`A3mt+;dodt zm%d$1+vR+QIRm}5&^Vv3O?y+nUe~8jpSwk>oYRTH0tzN6EZlormO0mO0M5_Pb1e(_ zO7KeMbXxA;o7q;@!+qTvP4#{?1F@dl;qKe|=I!INF7?~FUfQ-D5BGQXKUA?TUanf) zuc{XD#bcJ4Su%0>mdNpHoOh6T5Ucybd^(-(PKV5qXlwg;`8c|Aln`%3?XrTx-I(cg zI!450y)w&ivmwgnJ%Y!6XewNC4WO?-f(&mO_5J2&8y=bfdVKMIF%8kwxvEXP$OUA{*FldcIT$iXDd!M?iHl6&@L+YdmT*5n5#2pk$1WvZ@m;kgGb!K5 z#KqWxBBWrdrA!oz?#uB3T>5rl@mje~q1d|eQV|iocQYxf<4hta1k@eyTF4O$a`s{6 zp_Zm7X_m>%YL=;g9_reZYMCY?&c$wP#>GpC({dmRst|^vn^}G?ZeHtDr^8fckrI|h zU3@5u1i5PoY8KbNX}k8;>U5}%5I!94=fhkpyRH_!1o_6{tY$`_Vzf9WljAI0N-0D_ z!O^XoJKE$Tpepq+-yIG|refV!U(ajL&dimdTzv`)jHjB2g(Od>!PI10OzHBxmTCU( zyKk3Fls|GUlMvh2BBDie`lIo9B~KRhJVz=Ua`}@hZ`lp&v&USf_s`q2+|AS5A70K)ruzQPH{1F_$-e#WZ_(lggwJmX0pN0AO@0Ip2H`Ado42VMEE%fggOv+T8;IU|M5wNi)L6wNh(LFbu*6|vFex1f)reQHySuvtGit59 zckLaaN&olM?s=$L9*g5LlH@eQkwPwJ0+`9vkZ=~93NcMYG^kN&b{!6KR}hJ?i0lgo z0T>1ijaUI9Hz)70wWj7&WSQsFN*C43wf)MZP9?eMh|7cRj}C?tR;3^>nK zN&@Q>JfO~%D^D=*YUbTEJOJal+#jauiLt9m)3gkEOOm?_9?=J zgV{`Dq&R|NR70*HD)Z8;36l`8t}f^Lo(|vqP)N=nJ{Ozmu=?}m`)_~rE-(m@`DfFZr#aR0d5a`tBqBW!tmENuQo}@7_Ez4pW+Kq_jrA~D?lFy~iT&7Yh%j-^X z6r;Tex_hWmEfK*fsX#ViIdSiE#vZkru5UZD4WR~dBFUhP+yvsw2Ga3rC7Zj-FE4{6 zwa-4RAI)8RM??_U-1vIz76d?0sCz^bFY~za2yU&pWT0FhN<(rY3eNsigkj#%DQKFe zN(6#LWwpl4qlPE0J&JPNcqyst8KLTdqzMIO^A0j*BEU80u;F|h=GLu~2TLJOd3id# zI8{Dq})VM%c;f)lmz2$)5fYipO)xaN5nMKU7UJw4MCSK?9%6BmgLHet@C zsZgn0in#1CEZ*8GA>dL=Wt2)&Qe_NAD2OVuRA#3hZoXMZ2!lcDhTut-h_J2^Sf=C9 z8Yyk6t#{^P+64ke52bJs@sQp;++ECwVB{v@G)XCw2nPWSbKBaArhdL&&X>zovA$fu zahQ*=Rz&Jlh>QUiH%>2pqTnzhCE0K)cLnsPKmR-*?f{%lr@Om5VVn+2*jJdq^Z6o# z>2O%??(ZMoKHR-MET=NfK~ihXbJgB*(2Iy^S}a@`%yf4=U0$BIZ42}B`8?0FwSLuW z+GlU7%)A^Az4t;zgt>^BwblZjI(gyh-p6_zsAbYn6JnQTTKZzMRh2Shj6*_fojl z(HYfAGE!Ln|7`tBk0nXArHQTm5K%qnn7PLz^JZ4vTU}s32zK?TLG+YAfD-}@j`{OA zApZnOfHRH@AP5xL)m^vh=8f=hx5rUck^SIc>#+zg&<2T&@NlzZ`ViT&_gdeQhZ_#S zse<=PC$}+uTI)i5X-qtNL^ugO37AzFCU-BivXX1LeC|NT!rExiT|MKbuK z1BidGMoE~cSYhe&_rLCwh!`9oskpoFhi;UKD3NRt;UG%o3<@_2qUqHc1(3+*;xv zF>jTI6$wzljGB5hmXQtv2d4-sT3gB8n<_JNqO81@5n+d+QOnF})>^L{R~fJ+40E?k zw5Al4ZXh;uB8oH?nse4$ZfUA24<~JWaNPLZ#Oo|Ijw3v~XvVm2p~TB_k#(JOT6KqJ zO+1*m6}z%^rCTCm+Mbp6!Y=Ukpx7&?Qp9KzKV0h2T)qL|UzA}my~1H#PQq!W?P zIk(|Hh1odVo$O5T2%Cq^X-0x$jAU^ie7`egm>pwVN-Z8Cx_~tRGALjyF{gyf!>9sE zX3ymC91**P%CN-!{dl`?$37#CAn)6~`V6eB%1>(&No1IZ$W&6*uDy9o-_4GxBxVB| zvZ@iazGg;i-}T&o{`t=#x~|vqnSp2@8`%b+<};v}a}de8UL&ki`h76s%Wwbm{{DV{ zJ+}Avj4_Y<>$mZG-@d-Q?Q@7~ChgN%FHK=)wvWtYa2fwgJx#0xQYHq$Do}}x?(cIl zHJ)GN>t@?G`TVagpZ+TT>p%a;{{+kD&;R1{Pe0r7%isRr|Id8+=Vg`8Km57(mQ18K z*1ldpU4H(Pef??{L>U41U=@Ht!erXmVpQ8j%H*_A#6w!f6Qy zLEPXmQyAa~SC(KNV`hdl1t>JaI07n{86^M*b@m}GU4h{#(wIOZkj{~%n&@rBXe<=E zdIrcvq|Q$O3rn7!tWyQ)50?#t+%2RO&}%V=Gn3N8BoR_VOFwrqGee+(1z>JY;lz<> zb=e!o^dK;c+d;)O_Hvusi9RSp9D!Nwup|}{CpxwrOd`!ZTL+=rBih(=CSk(KwSVCqtA*+UgS@UE&(s8_VvhWJ>;WqBCy?5=cOE~6EQ#-Kb*INH+v+||Ph)}MeSd8|xEZUH!)bA}ZejlmgpNCHcUDiMmkRQBa8Z*9NdJMs&#d0R1)biaky*sJr{78*%+Br?TFH)1~UBw?4eXg zX_+~uS6pA2JP9G?bLO^)JD;g0meNOhR1YR!8J9kC|X?iQ_oN`*zHE zZs`;w0&c;~{j#hV6%l7Roaj9GCQ&jF^K$9DTs)&+um9q&e{Y`at0nGKRR?Ul%)U=5v2lH2F*F= z^ywyNUgjwXiHJEo93h^WKIfU~Ba%cBi6|GP1hQBQ%Brds&W~^6K4%7{@4JS|t!V(m zzVnfhrz8n+11m8lisXymd*Z*FS~F9b&!oxoMNNigoJ5ye=D-YLFe0*A{B+V|=;Qm; z_pjWi8eg3tDoPK}Adv>Zix&yEEOE=j?)&&A@drL@{U0-TvyZ6&5hchI4Z@;94hT6s zFx-+oDK=8iF0eMepvwpvuAD0>GklIr7Cf1X6W}?N<_FYvu5QQ z%}hsfCbMQz2(lRKc7k3+D+ndu-nVrLB2t38ANOtF_tq9wJ%`m+V{S<6?tUCc{Y8)I z8Pb;oPM(ET_H{t14W4uQoS8$KM#Ol#6A^?n!_0;qRWBs5yQdpdt)!?}FVB|g z4U{S>+K3sF(Y9$qXslGg?wlTJvsEm?dzi7J5;%8>q~2mz^7_c zBoX_*BdIUVf+LerVdoP7FsuV2%1&I4|}_k9i#VQqZIE(bGL z%3BS)JR>YFx+DQq3`8P}f?zBn86G3Hc`S_<)r*oyUVo4``%i(t`M&>ny?p-p-;A8p z^Zotx?akdYN%nD6Z++Vi(LT-0Q*jDKGTn^UwcBaKeoR?0;l?1M{8*CWOdO8LqODS4 z{md*L(!KMGQ0ToA3P_Y0VNMy5oE=2M$fdX5duv_4N9sSIK3YEbk4bO!ZBLTS$y3V8 z%l%lLky%}4M5ag0-D`obcCph*QcdKUFh^${bQVLIXWGu=ngk)t?7Scp?iNmzcnn=L zlSJ`|p~yHh-cJ@IQK@B^&g7P$Rx&@&KCo;60RR9=L_t(XGCva5GG@#;;6V$RM9+oI zl88P++dmpaAOHKYk<58yb|f)tvIe}sI1xlA)*Z}jZUEd}nH=Es)6#i*VPT#qglCnJ z9uQFx{DEn6-YKHYWES_T8$^U+MqSB>qRoIHOm)r(%V=QF09a)WMfgF1Ab=^{OIuS} z%`6Qh2?vZK!>1#dxwQojpQC^ZXI^A5+(4`1WK zjA&3K2N^QMDJ`MgK&2`@V~)5mr#N>?jsQhc!7kCLH0T8(m_BzKn~(S7{cYaw?6U^< z+8RJ}V!D`fh5}RPsDUxMp40Oa+s7V+*0e9ns-j3iCW4Vjo*A=FDM67LFcCL1^FiT3 z#H=5^txWbP#D!<36Z{-*F*!xJCJ2=g%_Ps9-;W8tpio}t`4Eg~$T63l`~A5&NpK)N{y zku%-HK-Y_A=IiV0&wu%=yZ7E_`1R>>z1@^4ziYWew37%)?K3Tj4x2<^)rc(lpb(ES z2+^8QYI3^!m@{S^`}XUXf6nmZm=@kI>#eVE+r}bejQ26p=Z7{V{*YrJBKL}<$&YMH zI5LEG9Lil&;N&D=MF2Kf882_!=Igr7IorB;Y+ru;kN@Ex|NYC$ zOMd94`ScSqvnq}wJd;U9SfoW99#C+`;0I^)kFRt%0#U^{1?-_J#;JKbwez)txY-PE z%1i5OUzg5Zct2@B<8m~1mBp;EqBfaDK=@%jjvh5iN+ zMS4Ust(o!AV{)lOBf`oW3zTx4NGyN9R%s{pJBx&)WTC`Jo9+(xO2lAhSn;cg2&(7Z zKkVSp8O}|0=`=9*BZWd)9?Dv?V&6xEr{@fsT$&Ry0#1wU#{uR1O+^a1bE5Y=;qED1 z3_d2wsKTMf4DRlB*w~Ku<37fl_=HD58$GA?o!0CNlP{N7FIfWo+4h&C4fnV zGa@L;Ev7^d_I{Eo5s3vYs=Wyn@tMdo!a;&zM`kuzjeO3*(pFjh(=j6&u`r9s(sfyu{jj%TN<79r zIizDBgSmHI#&Hm{jl=By+t*)T-`|N@sK+Cx{=>GOnbS?APP-M;bAB*OuBgeZV;QI# zMHwYYCbN?LFi4OmT!sFSY)~u5IfTndH@IqRm+SiU^z_kQ;P0b_q%EwSL_rk9@-ZRI zn#MgeEoX*3AjNNoRi!LINJJP&&ky&=(qm=!9>1R)|k%*P}gL0XthlmncTl)|T*X%}Wz zRWFyi`{PRHA0C_UghuY39{GXI%FM7h?FHdxoDo@@x&o)TyFQbB~KJ$ zASkGOw(?>6`+avjBPh;Ee%V&^v44=Zwp^FX(>$JSJKU0tgAdQ?0O3F$zrjR{w5QgT z+J)iaTh2p!Z{;Xlns$|NPo6}QL=e7Suh*xWB#v!A%rX-eKGQ6X!V=SGT2jJd{&CL_ zNdhCM!3f|YAqrYkZe_*`Dz}CNO8rboCVgadJ`RtJBqm?#7XeYi-1E$_l!}-tr(c9+FcArJq(?@QX@ghTvr~kiFs~FIIp<~$V;GWCYeX(0 zpPp{!`OBAIuTP(Dx7+*s`=>uW-99~og&_EDVL@a%LbVJ1-VHck6JX!(*Y)z_51)=> zzQ26CZ+8Y*biK(x{V)Hx@An!=5K(Kg&#A5dczycy*I)Pddl!A#?!?kW9~HEGyr`L~ zkBZ(HfYdRHruSN52oQ3?gke~Wjh5x>oRbnwdj8%B z2A|sV%xZSlYHrTVqMBzR`FmCPaigmzn@%U`4;8P*BEqNo`}alXEV1E2)Xw%_nHI_Y ze01{<4fOg_;>MyZO3X=QL45k)QN8_4%B;vPqJm@;Fs!HoY!xs^h-4OK5?DR8PC+wH zX952KN>hrrJYS29a3YQft8yD>(gVPp)gg!z0nA*XtQ3~#DIt(0)5wQdKF&p%B@3@G zryZM!NO;T;5LC@>v^I~hSs_$;HX(?pGIfv~3?&wLXlqUxv-d~!5eWd-kiwN_X zg;d{@&*Ffqgy&-OQO)WP0xH%u-RGQSLt@HBKh{=vpL5n35wz5i7s7-fIa_4GsUl)& z;RmKEX`mS#CG(9+j(ezqF??Xg$MX#iAw?wpPSg7?6(B5`lTILT<#7=xXE-V+-JOC| z*QGzr%jm*8^R~a;r@d(1iQ3_740 z8K49)z~P>DvS<+b@H`O_D2GpG@j&S(Gc$rXk_0M1p(DZ~A}B>gn1vGL77?&O!T}-= z;_NZu-iX9(3?HJ9&YY&2DhviI*4`A%9xKv0Es<#>1`J^tkxkR$1Z)szL?kB(O_&H# zM>Xosjmjfa3gYZW6+bNt@rCbm^T5;d5C8D@|1ic}Wc|Cp{0mR6*xdHLLv^`5wVXtp z9v=-~cbjuE^QTXri0JKoJB}lX*Xxrq_A!3@?N^ZXrLF7A;C&z3GO73PQT1~NlMdo~a`S zm}!QWJ^EZ(Xo_|q+$(ebCnC`dOOMmr^H|pNT+)8u$UkZ^qOc5e6V`YDT^~g~ zpHTzMM8uH-nr=3aIcICtA4g#tYgb^RgiwHp0gE}twy7%j3s~vQ*$8((Y;>aDWqOP; z+_Dn*M8wSJoab@*`(7DNHgL?$eweD4wA83bYO1-mEC43AMS zwTDh2B9#nxPqTv}8jH~&B0vEaX$*M!oCZG@(K8w=pkVhIem*nt(Ch=rq9Q7A_oM&; z5=jBl3{EPHLsjie`G7{FhCJbM&d~ud2;7(mNU*0xq(ug?w6o6l$T|EN=`(zjrK1VQ zjN=~ryCw&9WmOQB^~&9o+>^UKw%>%+zCpoK?wvE<`jX7|6`o$o=kTH zV!DYCB_-xC;?`I!kBlHrUP$XlrIv&vC}J=dBL+ZvrgJ2rU8n}9REjX;u#6eoy)x~RgDJoaPz3vW zTh`_F>Gsph+w1M}{ICAizdjDTe!e~3)}|h~eKU7v+P3#O?d9bSNk9Gc!~JdFUf5j~| zu?wgtalodz2dch)`t0G7xuj3eacr5{M9C6OLW0F$(~i2EL$P|oEvFqqbXnK;;|TLd z|2I4wkLsz+I+;lXRsASm)^# zeP)WB-RgpRu`pNCTvn9ed0GWdmiyT;mk*?Yn+*U0$|Nh&7AVbn?=?6jqD()I!+ioM zd}*!hnQ3Mov*}fpAk4|`er&s{w&li`Hq9E7^hR-7c_pJBzd2`SwpKmT_}F*x2U*T} z7e0hnbi#qpf3`YJM72*pqOUSydXl)%?>(03`2m@k5v7+4mL>=f-?ofl8#N{lXMQw z%8wXZ6`)H~aPBi&B*nx)X4;$+nINVKpXG2SZq&KdOO3!;4s#CxE$tBI1_}3Y3re@) z+PX+d=ed_pD!U}qCe&<7rm{JQFrPw|V;?&af11;QkR?)rF5PEZm~qO&UDtLX#u4T_ zJBtQ_kVR0Na8j8*iItQ{K|(mE$IQ%;1|cDql$kMS%m8)DL5zh}k`dy|tv&s4CE9e! z_xWwn=TCqB`9E!M|Ki6#-EL1x5oY~+n7Dgtzo0;X=@i+BJk#A(x^fcJzHi4d3Bs^E zb{wxXj^BR$_3h=Ah(7)Nw7=^ZBUxlweb~0|`y9uZ$FzL}R3y<@lE^crd%A~5iTsax zfQSj1;Ypz5XuUZStSUhQA&qIc=wZVSvv*6l600gR=KV;nj9W)*`;23Rus}F-xI2*~ zrF(J)ljfvIEZ1fFpk&qVO5BC_G#?*IQ6Wtv69|-%VCK`uUwsu1R&7hIWgw@_MX2}I z8$VsI*Xwm@+L|QAbmOK$>?FZTqJO*vAQ4?b4dwQFV22ku1C;YLqWW;pB+b+0nMu;P zc4(5NF&$&$6w72{CWOzXO9COi`hMg_s3wL88N) zGa&@w{bVYiC6&i3qi2+xF}A9@<1(fsuPxB0(YoBGh|7 zj>CK;oJqSbtTKI^O|8sodSpSd(uFuuGa{;^Zqb7_I1x3LK-v0(NFrQ0IRiGSH>`DX}k~-IYnlq0k+#TTl%gk3}tH52N8iRVLoAQ8BMjfwlJ%Ej<6uFZWDWaEV2oJ(}>eu6Y1m$ zWHvC4LDC7%%;1*hN(LmR?}Wx3PKId;P;jCL*Y3_l)gws?0welyG3NA6UvyDN2I$8l9`f%ltMV{ zAQ9z8M4&hkVPF;WI%6S3l7$da6_JOB8&JsyXWJBHNU?mInS4w6&%zoRXQ!!sUKO0OV(LE2wE^ z;{-!W&#ubJIVUaB5kir~PRf0eM#60+PRCrH`>!v*{pG*-^N&CO1f*s3rtyb^Er9E? zjw0%0AiQYSEUc!)Bs#N8C#7k0~J%c*-%ZIi8L;{q7B_f2lcXiM{cbGA<_wMdy=F?RhpKg2? zY0P{ahfmYilSs8QTM00V!kRfnghh_A5s}b>@TGMRoB3#+lY+yexauKIGO`fI!c2sd zMCMsY%hrXV#Bu3uUE1Zc_S$vaJVQg1qzkbmBZXM`yO}ml*>x|9096(d^(=fN3ff)@ zNY3!0uhwV;pw=3wZk>C8n5q;P5CD%^s>kZ{27#iO<-plWDh+si=+@7b6rK+bObx&( zPSYHM0|{tCXAt?Qn+WdIL|B6pMD82L1@t1%erXp3-u6=A<~4Q4s*+Y(Jg+@KQh} zoQ4@uWD{1&jF{$QB&aUVa3dlnZ9gaL6Xr)a*gX4L-rjZ+^=Ko35NEW10-!> zmeF)!(lKCWN{on@G0m(d2dxQ|gANA;1rvx9gcMDp64i+$GkG{H;Y>(0&+zHy;Z3QD zaAzPJ>8OBUVo`QX&oDFU!6X?ON#USCRIz+`x-%(+14&4v3j?IVJYhnm{SBQwr^6)- zRi7s35k6rD=PCqBWhUjC8D@sLhnjE!d<+PtGM>QA8cU{yZ8u_XtyRD{+daej;M)iNkZh<(H;6PN|CaGZC`6Dd&!!Rp8pz)8VGj--^D?BeVi zm4xuz#?N*DAz3IBFaU@%qU!uW0?dl(sM*7b@nq)YG`BgY)O0E+MG9{Y?ktN`V>xKf z86KjoRNesGod|lm!zj$gen8(Ei*Or$0;!Ub>6G#ji&!QjGZRT>_ViPgQZ3=gDAjy@ zTd#FA(wzyMr;r{bqyqn7Gkh;{r9fB&SQjF)M1~X7$yyVkpp&PYfgo0a$S`xW#|X9t zGcKi$X2nT+@57DG@Ir=Qq+__dx7HXU#2FSuH4O^qgtXRLTf*m@C8FmPf26o_28;Yb zIOH>kmu0ClckN;EnAIE7kc?RqW8#rcZY)B?fNISE@{tywBy=uRL?1etQ(7fOqe;r} za|Tnp3)nL&{S$yT>AhW+w!R!kNb(|!u5b=NdNa`GA~RxU%;~ysNAU!x=^ryf3OY7IS z*Y_W8Hxf~m{|rB1pbSbl01*)a&dM)Z>&MvVF?wI`+uQyAdbun|4rBE>e*N1&L-_La zY~eUU`zj)zm&@Dz{_^%lMA!9V9@~9O4iIIKyO%AiD7Iwtqvg%ad}h$m+uIunmA2QX zEz9zBUEO`($Mh`%(|n)9Jqx2s5GJkOaZOEYllV~JJ{s8{7|0CFbzQR9=OiO?T3oN! z`Y^3EZCp4odPtlDWjf3XVFy5*Dy^$_Wf8ipy)|V)m{nc}sW2(4NNds>Q;>*g*Y+KL zpu`Lb&(yl|s+uR7vFt`?+D4W20K)yFXBU|@ON!VgJF@NoA6vSd=}*nJv27*Y+>EEiN{B!me}l4R~McZ!TixS4J5uNJ;6 zOS`BLp=eixx}NEoOzwtK6ldVv$!eFX63)XjO7L7);7mnF0?Cxa063Bv#GK#+)n-&I zQgVnq2Gt>9x$qxW2r#Ct{kJ-7_)+St8n^ zT(Bs>mT)`NxUTjX2QuhmYyeMWczDdS{K^u!r--Pk8%4yNGwK&*Rz4NUsCLsiKH)!d zj3UA;0v#yZ!X!FBZS@`>+vll1Vt|OF&eX{tGxoGf>q7ftG$B9qF zjEufWNm|oAVReS8!!{?+5r6*qPf6`K#;2#xPwUMon&^K94m-1=0TPzFS!O5@l1~8F z>vccw>GA2)v+wT*Kt!+a_fJ3mG{)GD-8|RpMYSWww{PFReEC`xin}w>)Af=B3!ihk zlT=`koUlO%{ba}n!W7k2GUdz0n6#l81cTN6I{k?{ak(z&>MlwU@aSre3Ap3)$y$!eu` zATpY^nisPmB4(m7F{?+3CJG*w9uYpll#U>xIFsihft17Vws63*aF&_Xj0#!*)oMY^nZu_&gC6nEqBUsDbLlKb{)sD|>XS1H@ z!~zr5VwaCqrV>KVi7OGGC#HyqG(1cjg2GlxLQb}Q*~~*AL}n(^0>LEw$Z4t!kz`^O z%CeXuiA2;r&O|)`Ov0^3@gJh5Ak+c1oL^y1S##gm_r3Rs05#>D$3A;sK@onu9xsbF zH)9f;?l~WOIioBHC5Vh*)l9~jxRTRmoe`^{7ZFHO7NhRi+?~8J6HAeQRf!?YnaA`9 zBRCUT#K$P;<2j5>DR2!@rbT?Z9wAfV%!J3gmqAE?@ za~^|v(Qae6aFALGxu;}i%y3E5>R+aNBojAE7Exg)O9Xf_Ce0bqj3qq`6d*+KqN{Q) zydKB284OwbvNCIg?I9@?85z+&LI#o(E30RKQ}k>uB|CsHo-y6054Si%00B|DPu1Rt z#)xzZB92TP=@HJi^|JJ*3|iOg=g&WCYregWd1QQVQ6*6Yh$GAo4|jf?H@D*uqHn)_ zQ6Xmh`sINe7C)hfWh8`g`A z_&c1WR&8Mj3lT^7oPND6-1;=n%;~n@@1n#+^O#^-mZcO#@0-`lqg-Y^lx}KS+uD=` zO+OM5m@Nh7RSE(~IoQTZK&nycG5%q)Bg%VPsb zcOPTVIp9_0%5wxwNvB}ao9HKLT^k9r6HOl@V3oGh>S$TCYo-dIITsRbEYy~@ ztv;s{(ezPl`h_rO&ci)23(G|Vic(XRS5*Q*7}99Qe-1;Hb=T7LLtJ4$U)2;@XRox5Qi{PB;||{7ZK+Mjx+!nE(7J` zO<9)f^jI#J^|n~d_ifH`bkVi<@5)APIR_CU;O@v2m5A8JmPsCVS^L}D%hzwe`Isv0 z_UW4NIcFgIx=f|@AqxnUS8hDVZAS}U%%~h3^#zKAdo_mTnfX? ztYn{jIJ~vV9A>tE^jJW2d%lg*fkBpKDeOTb8ADTACP7g-FE#?Sf773(hFp@|JL6Gu1f+*6?gHCNcwZ6|IZT>!r7lC`EMa*!| zicd>?nA#T+ zVq&vmu2WK00#p3gV`h3`uh{^AoT#+@~U?6Eio_hkyHY zG~h8W5+zTL2&vFspqycd!sM57-!n36Mhq~k$g0)_aH=2f>VQHz?oZ;5o_0F&SRRTFn~FYGC3nmCBi4>baPr#gdh+oI!j-4 z95$y9%kU&t>?C24Gl(-X5j^q0cBG3l4fV-oXph@oRJ>EWoZ;m z%k1*B9mnk^*Xt$V!v|7D_4@S0{+)w{f%koX^xRoNX7j;QlrY=wcN^onbbh*W^85S! zR1Gs7$1&%;zut+c_rB9VXC{&c(AeviPu-$g`4B61!(SXyh+7W{z+K*XwgUDxY%jT~kZEUgPOvoz@q#Ek`@ zVcv*aXKm+duF_jy`f_QP@7OY)QLAatX&-iXCax~W4EGsfOf<*6aZ9G)ES}8t z^fAE1ts{L-pB8FX2M!XXM>0etPUS+1$efe*&E{;G8DVCN2c%V7!_NqA@^c2@PA<}l zq-1V!y2dDx!zQbq9SIQzxeZS#txIYBy$tF>sx1hdCm@p^(}^H3Qw8`W5J);*ra{EW zqQaS;vz+tXPDE~*lm$r%WXx$A1aVKI6R?_@!8|<~?sHUGt=a(?K^xv*w>fEDFRIJt z8^s(G3ojJsn1q?#!(niy0tIl93^F5FFh*gPdCckVaaxcvd=i>Sf?QOP7q{>jO0CRM ztO{@%W*z`oh*Tci60tT0Pq&;&`2pi0hI=}|(+)Z34#-HKF?QeH4x{Lo&N0HIx6fua zk9}(COK{HA&sG80cxHC2_m%dTjYi45}Um!T`!j(&12ez@anCb zO`#U=G!MTlOc$RL;FJdD(OP3=M5wY{!0b&j9Jz}fC5lL&k;*cMG$90MbfHBTPLN8n zY%=e2OP`%pn9_%M$WSv2GiI5Q?=cRVBzd`gCIwb-Gbj|QXiI1(e~W$ij5_fUgi;l< zCPU0Bqitl+9Ejli{os~hc=EmvCKV-he!6_V>JO05pMLuLTYvWVDHlQGPwNvL-{WTS z<=dBQTg)d@G9c2@yoomO^fBha%(v&y+p#13nEP?;BJ-1i*~NZQ6hLxD{09aZZBTsdmX9V?jd3t)fJ^jF4FUs4OS3)GA zN%iYT)gWMs2;c8>&dh*H;k=cZl-jy1{RhhGT86u6>j`qp!05dfSz15>p@eGpCR(FQ z{|-MOA`xzubB@+popd9LG@MDq2qPm*rPbk$8xS7G5!Fj1BB9K17LK%ILPVHqI-i<| zK!AiBN5rvHMgyEBBZZV$9Z98goFfHZ9id01PLW}=>q;PZB#S4GeQHoT8DhFMU9`o% z@8PqFQo32VhlwrAB8B~?wa97pU&2~9F{jDuJ@Fw$K(JbEfc5Zv6C;b_8If@%P8gES$cGZqXeTEIL8 z0B&Mt=~>Xu^Bgms;o-r|qB+L_m}*b-jSi|7`w-Qbpn5jKdB95H`!h{XkMz>5X(yBk9Tuv1X0~4vw~RJH)$?v?e0vBRwp` z_CzI6Ie>J_F~Ua@5phbRIcHcp;`;P_TP{8q*Y*#8{fEE%`Oh!+ZCx(Re0%!jW@C)Y z@?BddGChN(2?sMDb8741a|{a$(T?P42NBK333uBkLEA-yv?{)R-!H)Hx0i&A&~Sj+ zlb)YH@B5uukK@?KoWlYM@bsM1wYO96VSda}kUxv?iKnMUM8+}K)*U(LT$cryMC9g~ z*+i;}u2=5%{2Wo}T?zB0bv#Ii6^!Q|B65tq+UR49FJHctn|$B*aOVe1kxrE%;*qT? zBJrSNnb`sKb?wWtENu)&1`A7DM8qPQA+kzS03yoL`m$(Wq%~%eQp@!32CA9ykVsRV z%BpJioUJti%*@Ry$9`Ef%yD*`g45HJ7(pdBJ-fq{AgZ(l%z0#ie$TL_hsvlN=nq4D zovboxj!_8Qj~=U+gq~+4Wu`JyIsp~_$Djfa2sah-=}uEXr^+RAj3JC7yq7xkL%9@x zpeN&@sgI=6+mwY$wAvSbvVr1!icZb0+i=rUCd7nCh)Cdx1U$n>tok`q zJxN0R9{1NhklI^1=5$K#y(8Ji5t$l{q+pxXh*#wfqCksd>>~|Ad}+emy)7vUy-Nj62mdrr>`t&cSxfV;-uqbaqFV<2dwmtq-~HuZm3ha;cz=H% zW9<7r?a&6|{^iSCdd!)}aqRovTHD7x&h;b5uw!}>3u~s&IcvCAa=mhOJ^gqyvu)c{ z*gVqx^V72<(=+UB*VGz4J=)U8zVG`kqE;Hj*2lJM6II=|O|_-jx~iF7E|)RJw{Ne^ zQ0*cjO}(Tb1qe$#Tq2ns6<=4Qnp%2Tq?~JWdd4>AaU2_QaT7#DMB9S~b9z8j`?4&u zEF#=mBW=Xr?XM3(2o*WbtPmn#L2_n<7uymAO*c^EM~}5IHJJ%e5QNSal7|aXoK2qE zFdn}JehdZ`WHnF( zK$Zam^3@1y=1I^tt zP$$Vq3kL6^VC+y4IkMnn`j?uS4!iMdf(p{*o$jTsb;f!vrD)+R!Z z-89Ag#JWL}lLbwjRg=7u(lImKr}?ySM?3ZmFUK~Qkv|;Mr8VK!l*7X$oz%F^YzU!D z_@K=6W4Iv%dyHhANqL3|u$P4PE_NC*lv0VPae%`?omGOnE|Rp$8dJk$eg5IvpTGR_ zby=^=?bG#o-QMpIyys!^3_w6{g zi1_sU`F2@!2IKC6hRa__zJOw5c~`?{Qs z6gtfv^@||RVzA0yi=c2-N&5d&xeqtaTH;SGl@y?uJ-q~WTn%5Qa@ji|!VL+W1F$3n zf=G+w5L~r=b&wI6k)Fi-q1OgL5Ksnju~C!5ouk6kYL1#^58{fecKDogj)C+9%tr}P z#=bK%MT$#AWP}qaah?Xs{+d>#7gerpm9h%O$1630cM_&EEMhp*i2%#j5%javoQ&2$8ZEqgEAVXgli-<>^OCdZRM5fRt z+7N!mNfDTk5k6-Si{MOn2@fQNQ=SHj1PPE~;bV+#+JvOUxYciB_-!Ht6Y&xvLoz3 z6V^t2WE??F%Q=RG1T&Kp!m{!$+~X}QgE&PIK~Zs8W@E6M8AwPUA+k-Q$w_0&Wag!{ zR(8fE%d4(~@(8nV!|*)Fqy>#BBHY;pW-|{LYjB1cv8pstQB_2a#w>YW8xo)XejnG% zvOGVp%d)-iPq*cw{dQU0rUc!dZjCXH$Ud|M9lHaufLjw*-!?0vw8S91bOV+efZPu6DZza-&&W5*!FiKN?7lzdUMaJ z-z_O3lo+Kt7iDJ8m~)K#>`h9jtg7Mj?d@o7QDJvJw!4T#m}Uo1Sz;F6x?V50Y7&Ph z0dr0d3qSQXb50StENdCNs{|?7ET5-U#c7C-bCEq=GnpCbfOsw6SvPc9mSth)Os{eD z>6U^rC!?^JTtUQV_qy5CH+IxhIM ziqoddc^qc8EXyfVIYo`5_LuYus)R~k*IQdJM3otGs@DNT+K1kQS(&L4$PgmDl%OLk z21JL0b^VxDdZt^)@#DB%$RN%#~9kSvOzVwyHE zSPM-?XhbL~NZkq)InD+VdBChT-z=sWPfo;;JaQUihHr20$IG{-*Kxf2wD2r1r@y&- z5FgS`p0ggia}%HjXB1H>i$<&@j0jXJNKnkk5l5J;og0IbSU5Std>Shw)7(5ll!YWb zY+4$)vxerx+`@P?LZeI-A(@oZ=X4*iJrAD&QXy`P2@Faf)68bLESVm}snlANwR}=_ zK#IuB)>~UH#Yb{XjpQJh&ts%{OGUzoDU<^N7}CW6Na_libT9+#fG{^^BwM2-$Q((; zZjnruMxD90!}b+@S^97j;{7=O{L`P5yJPF6pr`z*XwmH zy~=vI?b|yEGj!bdIgi(um+iiZmQZI(T-(HE1kn1ZvivY*(Eq#FH~W5j`W~@ zx{v?@*x`Dr)+7L;M5$&gZrEWlN(pj04M|v`c`?OZsR<`q&vxJrg!&1f`z1}3mJUw zdD9H({K)q>vk9w5pU%uR*x*NK7ZJtD<75)HQ5N8XPCO-i8UT2V

    W7L~(Hm~D3- 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

    bk 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 JPDHLkV1kCHd-MPR literal 0 HcmV?d00001 diff --git a/object_detection/g3doc/img/tensorboard.png b/object_detection/g3doc/img/tensorboard.png new file mode 100644 index 0000000000000000000000000000000000000000..fbcdbeb38cf5594681c0e206a08b6d06bd1e86a9 GIT binary patch literal 79342 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�I8@s}9>daiyll14Y2V{)X&=-x}Na*I$+ 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

    FHkl*~F{93XPVQI@|roj(})x%XB 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

    )l(Sue7I{Q+8P_B!bMn|_{;W_)pN^lqb z8oX?|tJ}e78OS&KtQ5eI^M+7P!Q||NmJ^7s&MH#6_(Mjw36toljwZg^J2Ykl52KAm zJdbQv8LEc?{<`efYiHJPucjw?b)twv|FR`(E*UyV-XvWFEvMwndxTKRowi#=3*3Y$ zV&TWL?gxLDk-{vb93lkb7kgRprZIy(;%T$|fQbjNcQa8%3ERO(1!{(3C0Kprb;|$k~6EVO^9hj`EKh^++m-sMym;j@`l9TF!!Pz-QU%UYw)t{ zO*9z=xV-_VOWj111^e2b`Cb7fubJ4sK#wv|)Ujt{vM?#ipLDD!qt4!G_bfT{RYhK0 zcjF3376aX4qNW){d(N?KyiFBXjIWV(R_?fwAektMC?Hs zPH@o+()~BD-eae`@aHKt;jJ(jk_CugjbtIm<&mL{PQ?DU&8#hh zyOFl#gmFgC?yy(Kt3D=TUmRVKg&mV)9ANmR!H5@ItLB2#(_n;rJ35oVmwjLJnR{bd z`Oxy2Yzbf{!(H5%xJZ*~-WvUX@%5HbZLZO_@EhEVdnxV`v{;L~1_kfNgX9FZjg@<%%1S=cE3?JERQB1L&ts60IX&6k>#)ns-TGoBXjea-_T=k< zLo87zN;Q39>lGT2_p4M?D4Btyt?lOVozCa!KOsJ}u!;iYr5_TgT(pK9j@;P=d-l3q zdsU8ptCvB25>(-hH?||!xzj$omp zyg%lR`>j_tB~8|u4~k(*O&=1FkBBD|na7s}6q$f_~XZp3?Gx84G?3Ql91fSdt z@13`1IV|=4EIY{^^c@Ao`qG&+u?{nY;>S$xJt_F7dPu^JJNVmd?wG>pQ*et4y__iU z%q!?xvt3Lnt0}3J@mRg0`7kdWJNb6^&w~9#k#j5FJ#T)AuHwYRAMZYGks9 z(e9_nRyl;+a7YKgx~e@v8x|9oRF_pzTl!`BtE&9nl5H=nwewUR>go8R5*sIA)m!C( zi>M)aUW;G7;KggOU4iCH3T0Bp-CHhO|REtOkRj}`Jair=xzJg%DPpVsp-T=V{Jw4iWeW^1p@4x zT+! zo9SW5(@^LmgAfijG!|4jG-Pi&3+W*^GJT^eI2%b897#!R`NdSi_9iEP$D&9oiAn

    rH8kC$~hUlrt!WJFD-qV;Oy0PSIXu@z( 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 z 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

    CHL3}D(gNVe0gJOTk>?s(;?qvU7O_lxgS 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ȟAW?yM-S}DgPA|A7?7EGlt{;7~wbv_XM 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%0DBb^V6EGh*&=5My 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 Wis0iHI-1AWU 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%0Avn3xwPiCFi1(yplP#6mDe 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 z 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%0AIIcTm)F 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

    `7yJJhk&3dvIE|)(mJUMW8%Yxmh1;^ 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
    CZiS% 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
    Oq=J`x1PuNQ}4 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ⅆ-t1V-#S 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�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

    }a-ep0aZS>#CQ(51gESRd}7kkta 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

    )Ze(8uf2#M%^-Q^m1fAw>p5x9fW= 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 z z5Azc-D8233@7a`ni=oj_Wo)7fGuNzT?GE<%n_A*Lo8n|gnKjhXl+9Isc&}rT0GAQLZSf(3aCvI! z>c^ij!m;b2E}jV0Y*M)TiN0|zMBMeO@=8PNb)>Skdz38c*M!n;OfwDBe>2m$ctwNF zM#EycF+yKLvOW>CJxtA>y1Wo#Pu@4~?26G=Id>b#Fnhk)#MY!!f*nBe3} zVQM|_=>#*~R+`oE$xZzs$Z_~$79iU%sY>vvJEKIjR=6?mm|AC@1S)MFag^JtAy5gY z$cjHEM`EoZeEnRKARDmxNUp z-t$2j5Wt{Oa{`X=(k)Wqk110W5X=*vS23M`-O=$6Lf!o6mF=GxPvS}2NudD5t5#Wk zj+8hv&o6P?^g~}Jv++>S;WvLQmA0TH?z(kNzTLWg2S}e$^u{*2W4w+ojre7!Cp!Mk z%npVw6-{J$hAmt~Ue@OZqzDR+6alZjEn*AFCz?-}-0UH5cx@NcGX=l^Wi8v^1Dv5A zcB^t%j)k|*r)H_9IkP8cKg7QeWXi;dPlRupJB4~4>4-y}#{}|KRyr>U!zl;nf}il0s|KFhg!fzPAR?!PCUmumb`X4L5^pLyrovc`jq(ElNJ4?JMV2d&5>h!eVRpO8!TnuXuE${M#A5Q_LW_I9^EX$jEUNmBkJIX-!i=-8T{yNI zYGS^(cY!y{^!Q!48ioU*uI-~4U=`%M0iFQ+KhdKGfj1iiokYDZ)#2G(vL*_{tVh0? zbvgT*!lheedX|0~|FM^qC!@p%Cd**XhT_Ts`qkmuR_nellJm-6zqa+`pdd@LWQawP zVJ9&)w};k84mOH#{EFx@1psao~P)mJUjL;HCgAjmzTbHIkY zJN@d#ve9F}U~GB9-J<`?pJyUkxA;rt*bOb_a#C+!(#22~4qe55Yy>aV_CfGc zzjkkI?yu35<6B@QVSc|Jml*x7J-19ah&S8rR2Q`-AgxE%cQYG?lZ|$MweDyd-50K_ zQ&f(+I6@~$o9&xI(rsQURhs7CgHBrBm-L^D%BY7U2s@#B@)G_7*UU<60>rN9-9z)& z1*|RWs3fCmLtaT~N`#)oj?%=E_E>i(&ZDPOT_MHb(LCNQ{r8CP3?Ah~y_o*}#kS9H zq!Vs5^%yR!8G&CHzH_p+-H^Qg%tHZlg-t#JGt z8D-)Pt(NZ6#{5a}J)dF6Q3()3YLcb|pGRMN+axvH!h<;o&eIykM#SN7TJcwogZ@Mt zfmF5^h!lRB!5YSm!tqG#NJXv=oTPE?>a7s=T!nD8CWmLb(vB{30kN?^11g zzk>8Ow}=&q%`Y~$=$&6Xz&u&NahD^*H^9WYuV`2|=+@dT*S9ql zX)Kz@Ut~}8*F91P5{ykrwP1&a72xmMOF}*?bTh&Tr`7OFu|X3Q(+t;vD$W*eZ zTMt5|39Rw4516Mx*T!U{6c)6(G4XTX8U?*ylf?F;}^Q~GD#+fVrHPy@$gb@Eg zTi1+L{)`+qU19t@LbFfNdPsV$02lMS_2U!emORn4?8jG3*Q1u_)cDAuiDU7urh`eo z1x^a_ir0%gN=^qvXXyyDrXoV!&mjc#(_o;)^p`*2iar3^I}B>zJl|6ZsLWT$pc?ph zI?TaKMvj#RPdUb3!Irb%#bKuS94mX&n<r`?^#%0wh7OrV=67b5}Kfb2l3G0l}~i@do*K@yfJ*0G6T z|DjeFG5zd={SPz=eLFgGvPJfzoqn&fEf&1TZanoZTABLt_$EZ_{u-?;k>L{*0SSYb zCK<&qnr`W3-72~g!rF`H$$pi+U(sw8d{v;W1E|np+~hyrO41^5%p);@B^uEijl32p zN3h640sEc|y}v+``RC^9EsSqnyJbu+32_^l2Q)P7yp9x^1*;JwXyD-4OoC-!;g-zCyPsm?RJLf){?sL+yea^4%0 zOq6$|<$h)`uN$_^J6#0vp)~$tUqX^!n@(X&dJ2CUA#jA@HKD6Vml<}zD`NIGcMQ=3 zk2*CYi20)H?es5{Df5Pp#im)Z=vW2X_;Q^?%ooG6w7!Kqk}Ov#9dV^wiA`mB%8s%a zdl8AQyne$*xjCQxc|Rh^>#*=ESDB;|e|LJOaL&SWzr>|XR5oEd8t>X9KYi_;QB;J` zVn+}9-qLt;o?HI($y!20zyn*JW5@5W6noC;s3xs_{!#xSf1?Xwt7p__RoyZwEz9Fy zu||=^%-?ieoq;MXaz|U`LuJ4Qp~*fflF4OAy%O;!S!O9FVM+(dAXCBynKoSbbrtkz z?PS`7Q*8)y?ZfGr=B>+Geq+m^$=83mGstTh((=l4EnaZEYL+l=sDblzt-VD9)X3RDm&2J}_%e}K^B%wY^_RB2ap33GR0;V|YuuO*5 z_JA~>HY@i!TI&zE%HbD;b}nE!W)Ozfa+*eKtXd8!vkE^mKrq~4Laon`W~?QtVa_5q z+}T`MRDw2@x5KoU6Dup_``mJ1e*eHSBPrt&zRqpOKKevXa=uE!SiQZd^V?qW0OwV# zZFj|UwD@qY`=F{yZ4gC2sLp5c8WS_AO~4wqhN^V6-@5O0M?Y+L;~Zqr7DH4SU`w$B zBW8qyJ-lowP0ef>@xN_R5{84{wX7uNjeQ_89eTMT;S{8j(B9}|!Z?f_@HR-1ZNzuI z2y>I_MOlC@hng*9xPn4<9wU!2_UcDaM)2Mn!*r0!8xh8jrA>R zVWmavbZV!hzx8w964viTyX+xCQn;y;M}`kDRg z(PhA}2nQ64YmOgU0KUJQ8Rqx7rBU9vPr#sB)bcPgvdU^d4ys*OqE)w^C4W`S1S0+_)8Nny6QnS()L=98gT>XiK^CB|VwrJNSmdwHI_aU{ov zI3?Qf0GIGG5(d_~J%H%E}0WK&R>rc?wrx7Wok=PydR!LulQ$ zVN2Bh0hFCNt{(PfIZrTeWqZtYk`|>$1rRwDyLQx?3`H{kyAQnoAXDdevJe?Pk$^Tv zDI4crn>*5*iN#SUEUdFOoxdDy;;J`L~2IO%ymH!O&~qOY@b}zQ0}V zQKE9(f0(`DO?=#6N7Roh?qU)tzT5LRLa5s^quBGgC&sM{G za#O(-<(phZs@xJPljshjE~w9GX2PE#hwSv=`l5V_HYEIsrR|68`w=GO61;|2OKDiI zBUtJw*HSe5CSh4c?rbRRL0Q!1$s zQg#i|vem<8hnm)ZT>XMn3YcG3>T9giHLqHq^p$YtLcF(ih^fhKl=k=X$w@l>c@erLz!#5x)AJy7oDk1nEjiNO?7bcmoBO-O$_!bbIqunKL?%ml z?c87eGIoH8EuRTcPVA6~khGXL9iC5qS;XzmI|d?im|BOJWdGPg$xSEI=J(b!rmqQk z5m3pv-g+vvtpXn9w=gKGe3L_A*L9 z91tWT5=irZo=nFJZ}4H(<}d_g<4W~V-i7pb556F8v((H=@lF%tiY~-4%@9&+=QK(E z@ZD+iy@S&)2vvSH{aLbh+w^eZH>EK*3->2se&=VT<=e7~8i#$f==t)`;-#6t!}DR% z3YGBkKJzNcgzjpEByVF=CQU;wf*eaYbbGDMgas)Yx=vvWSB5@3!X#HUcFO5xhPiBn zykat0bW)nnlWA(q!f-3ixF|^VSF$QD9FYNi73Cat;cyLxhA}qG*EsB>*Pgw6xsd+! z@YmWToNmTzL;XN0QnsZjgZl+{ntTpP6L*6kn36eEqdPZ`r$(mttQ7vdskoX2R^8mY zUokZ?*u)qXKWg0~|HElQK>Ze#GL1NB6w#)YN~YAvQ7=9K{f@*L`+>tZ_as?z0r)ys(lf8=bi!72hq9z7#1W` zX|qaYhg})ClQ=yORXJ~dR&GP4AXwZ1_@ah3bxHq{#Bc!vtcUiNEH1hlyfMnuLpX@> zG>0hXZUwnacyo*Js@@9o*VBZ$i=r5A>!cv}Ps-QoHR&aj$a0X~lMul(tBftBF0WJ( z%RF%(w=7RTrQq$ZecJ~5{nw?WD@u9%B?dbco$w)yMbIh?^A{F$AaaX;x1zjJ*GQdq zoGK#{l@EpmqxMHN)Pt>PUGLU+feFe|mr*A6A$h#$rvnf$%m;d0Uc&=vs1@P3pmzmi z$+LDYxByKig$%`CCEW%~HpE#{Dz54k7S8q%h;FO239hOzR=5V#|BBFBy zv{|?p-A>Ap8cZtbcU&2j2YUaF4Ege_2X-a>N%gZdL)ZK1h4&wp5cNZ#{BMM<9W7*4 zGqbqFgBNT0L&Nlx#;d8vO@96+5CA) ztq8Ki_E^dC<+V7GrfjvsYte*SoFq-~#&|J*sU3h)F>AcYqGSOS|NQ`>}8tv3b1QrjU-t{&L z3gX_c-K;mH{2p%Vdt^+xkan7lygM$SN#uQc!~VQ<$=)TjZH~OIBqqVJIAFllh%9&h zT@`tuyyrS;nY_)6f0ZH#%{i*Qe%9a2E#L@k^d;k=D)wpdWsefki6Nimzhz*-{;HY6 zgU%`A?L7hT{cW+2c85HOUabVSb~$_%ug8vzl{muAnWN%)`v&Y@O^6+*Mh&M~?(fW; zcLdbTL}P}r#H1t*)ZfRJYKFQfvGX{<@Vm-}+Aa(^one&$vYmhwig(SR0G%t~Mcgot zjfK6&gct6>*Z$h1z0lyDxX~A1(Jh&H8|a|4_t9&gE836v7($amOhLSc-$GQj_h_A+ zgKETTMazDqvpzzhDy+0|x=a!T>ceeBQD>`=_&>n?n6J1YpWV_wu+6{gqB~6TxRtt} z{XN@8i|!nVKaDi&Dg$ahOF7rFoPgtb>$M}b{J{S4h05vGc{NWniEYcbVB)c&Z-`m= zYSp35_H5fWp~B=rhaIZXJa;%Q;{b7hhbmz)sUOplHWm?|FE_?j@Ndni6hm`*~irukZX>IzF!#P+^`eqbENHS{ey7EZ(Ostj{iW z$yP2J>GSnm4V|Pqu2>-z?WxOESS&WfN6R}j=0sS2`G7xNX@UBNpeWy zGcNJLO&8YBt@TnI0|Y=mnp(z+HLK=wscvMr;gVu7mbf~TfhxuJ7v-JCX8Yzjq|)Uw zu?4zyb4xFc;s6@5YvCF=^TX|fZF(r#Ky2#j=QP|UkB?0?U*FMeI(1?M#6uL}mh#6L zNoclu)5N*VFmFfZP!z?8g2Ru{{Gtf92Z`V=1o)_8o46ZptEuugYE*Q%$>iZYS-60~ zlvYBHdRSzNQ_S??F}Ba};+C^!uG>jkj;_vM;VW&kLSX?qDF$>$Pe88A5N^L7(xXqY znMcQ!(>^GKYl=XoptmYo9FH{1WaHUQ0Iw7@8L#x3(nK6u##CsOUg|5ac!>a44(6~} zgNF2~N$J@2;ImZO_%AMK&{BkNv_iu#rbUShZ3aG;OtwOoMZ1lYgz{CnzSI@YdI#8V zg9hOG6X9ho@apE18(F1)=w?6NNFNMG_Ie_uJmCiq(X&79BmSo-SOv|TOcc0bfAKQE z^q6n|WHjf^q?GW%B1VC+0u3W4!oFuUNZV(MAgzPbAF}-lH%|_mHSEC-TOaN1kZ==G z`HzRSHA43%KK%+UY9Cg7-y?<2&0o21srS*_)KCm@J;DCjC$ABS`vUHJ&ZyZXRgECQ zqN|Qx{*N1-fU3edj!v2+)6Tjk5q(eNTuwr8b8XcJ*+k}g;cV!@Y)i`R>&&s&L?-IoC-~jPpV6UWzihR@EYfw0j0Lepf8!Ac|Rlu zux_lf0CXRIN`e+Nb4q=8-|>$FZTUxz5>ZMBwC%D_EoF(pcT3ULWzOL!^JiU(hueM_2Qv>&YaLmvR#D4C@yuO&;aL+{SYz0+Sx7KG zXsOnBF<&MVeHk=rVthb^83_=h(6A8KwVw(ZU$yYolT%(Pc*#=X*7pbN*5ucVnvccU zbi+AISH4RX6ZNQ8!=*<{HOF)Un{1y0GZVQp7#7IDWqZv6qWFhw+^1l7Z(inCP^%9; zeKuS9W9`h&Jf7{gH@POjenT|nX$eELn6TZM3#CuIil;$KQY1aNE6TXmhlYn2%Zw*b zMVmc*$C}<>3|8u@4GNW0Cp*dJpBxg){iJ32<+lspE&55$dh?KRXAb5o%(~^`uOmo7 z2>ftOQXsYUddFy{9XuEPDv`c7X^4i^9V#7y#o~Wv0qj>)Vm!yEmbC4~Ew*}|k$LnY z5t2e9cfb8$?|%1fMgs8MQc9<)D=Ta3sKGo;;s<(I=Uw6a|I zo_b#4^ys?i{Ng6-8tBVmB=az;{ID?OfI;3E1at3iQYtOUz+w$opM&rgRJkC_r2>xeCifG&Z`563FS(y**z023fAv>SZIEj{6wGf?uy%ry^i4`Pvrh|wpm&$uBlI_wQ*4;e zL_j!<53?~3wVd*x>JsO_`V~xGVCNf_Y1MOgq<8yosmDO5GlI3Blz93 zq%)&pT_mmeu-}(>Uq21!$qa~83mN*8)hf(^dPTfP6FCQ(njqT260l=O`H=^s50=o9 zNAn^*>WJv91P8=PX_LY&(qrz5=I7SOlMVUHCspq?!dmd*m37*ye=9>ijIKCn0%(uy zrk*k&yVMz)hSoDlL$yKsZhSQU#JULRm~DXfEC`!E9ff^-bE`|E(7~V|c>YHsYx4>t zW4?dapLcx$7stm8CzRAh(y%2t;g9UfRvUB6E(#x}hfnyg9fY(@R84pl)hhc*c-vfI4VW*%Qo5n8ByDC16C%EJLBZrefJbOANyShZ%GjW8A3 z$_9aIoR>H%FMDe^LVv$Sys+zoqzF4#<+raN56zTxE$VrHJ=NiOeC`f201`V)tZT_6 zFw@6>qTgJD@Z>rN*3gJ{C;wPbB%j_f=o-8@PqSOr0+{8dx}Y|0DE#t%I_p!`-sjcM zHWz5IJ&hDl<2Wf_t{z*Ah1S$AdcySQ7y;hcDx$8r_g0-f9)}XbPBkAj+I*>ff#?%d zkN_Df28tM}4Un;O1()nfc(^HbwJBZ6QOo?c_9Py8ZJ|ToPo1|LV4`1^d zC2K@XOfA;-0GINbMsK948dN67jp*L92iTwaq4O?Vn!L@y_0lW>^JW_%l~ZP1xB!lz zhOD5Wu+dS6+kY(6C9f6-6G%izRP|pOWo+%@e!yIsk07kU%L**~`q1h>_njx#Q_fFq z++!ECOC}bECU(W$bLjkM*!B&);j!SVNBHp zL^xV|m7;?)VLk*$&01BaNif@bx+Q@}ZGnWz`)@npZlcXXCLvw;wc+(gVtu3}>Es`` z9z0L8(<=@#Dhu-3#XCI=X^E17@&!5HvS4^d&{lwpV9_af-Uo7$X(QC zQccCsKmSgze`HmI&*i%UwqLjuKOmMQ%rZ+E;tgZM$Z_n-1iH6+-)B_5L_#!uXu=8= zM0CKc;vs_h323dy)m@*x_Gun*?j_4O1CH%B!>=E#({E!PE=jM~c)ZC%$Mi_o0R<4! z8zNuNa|=l8GlS+9C^emI+Rt|ij@$LGZd%u>PPp|~DwF{%OU%#!thd+9qU=*tPP$bv zHXUg4a2@=IX=r%#ZmWUS{GseP2h`o^Am>FBGw4tIHyQ5udQE z=z@AzE|~|>)r41LjP1^C@1f^N5lmeO?Qhj`Fyik~bF`$qw|r2TAl=*7@$`87gC5OJ zs=39Z_xoUz^Q_5PV3^ws7gEaFs%P%zhuW8Y*8f3vElvll7u?q!pLqUB9 z&LPZbTlkaw@C2)29x&98*@&pZObh|LA*1`MHf(Bf{293@Z1PRQ(E7x@j2xwE6`Eyj z`Db=ZqUfCoaZBz!1}z16Gb*I|O~d0|S)Z_itS({7`-}1NSwN>pU2y)%UCKRER!O?$ zz-nuW8u=>MS}mK>GR4p?It)^4QF|y}Tk+dv4$hPknyu0-a{sY}z%Vl=ry9H}g-!FFu5VDu@=bgn+#p;L5$Wobk9nSa>QBoH4 zSuj;UP*$3kxN`G>k<^NF;DO3$8k9ln>OFC^0eql~ z2(<8sl$XfN?{cCH5 zo`gC+oFdai8-qnj*AVte$&lG7Op%1j=SEVlJ-X)>>2^rf7zB-$d;Epqz+-Z68aVOn zsr;v*Y0BN(N~XQNlP^TMEv3_9cO0%q7W?e^?zc_!%tCd=qRhu+Tt-pmR13y_gXB;s zoKKgQ9byOCT0!LvEklBWLo}b}eQV45_`eB|1aJ4fZ5!Cx^vB)j5VWFrdw0^U$lLWN zE6?G3Nlhp`t{WRvdR)1zgc?vEZa>ofF+RKRf#z*ST7zX1K}Q44@w)3xS=nS>a_7%4 z5uJ~gHZMHMQmGdYO8KA5o4PQ;vxwP>tQybwGNXom4*S35-%0r*G(4g?68~(q1WDAx z0`mT`JfsNMO8Eaj5Lxf@cYn06G*x@G9mgqbrFY6Js%KR9-$GdNOH%>6cJ5Y#|FQAw zN%~&<^dI|zi{Fi0OFoNavA`(;X=!qFzL^%qd}NKV*_Cki9?o15R~N0O(6H-tGymp9 z2br3Rs$cc6sDLT~J8?$(eeZ$$j`}|L6UW0mdv0qvniKd&3S2r7G$%V)u7= zV(i)~-m5AC&NQH_vT>PV4*S>eBVH>J2T_J7Uu#rR0Lgc~#l9r36J8~hqVpTYV;UqJj71FEyAi9Uy6VMi4PO}fKyiC%x z14abFZY(1um4UDB8W_0dduAx3iGUui5X4TWu=8tHbH{5Oe_)xOgYipC;0Lr5_gF=JAfYJd5}csxSccH(a%Z@gQKYzRljg!oYXiN*l!134+6 z$A;_nll@O8AFq9faz}!T{tTOL;gF*B#mfl7qxUi!82VE2h}9wg!osR?vja2wbEyEE zs2NnBIHU53sML4wY0HN#&kL@hz{^+GaS=``J)6IBMb7I~%d2VBsAborRO+PI{H)B$ zo9b|f5UaO6U3XD>p{Si5a z&PHk4q_QeK6s6Hx?aJdODzGtLo~i1*--X7z)k*2&U5edYU-gA6>y)5Ze$F@Rv(I>P zIPYlKyp+{{bJ`gTsz^)$PcP~Ktm0IG@NRH$@i0RR8Owp$WL~KkYm0#D{_sX5Bw99)RpyQ*G{Kt9^h=k`2X$Ehk%b z$h@L#r*GfPXysvTOj85iv|CpQV0jM9Jcj3i`naU0*>FQ)VjvZ+ZJ01OAqS%TuCd+<-@9U{%^~hqvQT< zlyjcV{io&)actjK}HLFB88Up+853Qeu2d|O;&RK7$s}~W* z_68p%{tcwgSEK~|c7V?7$N1YQW{uiqIkLZRXw{uFS~R@ZfG||m+}NH}eZgmqT=_+j z>Z~(?JEa?Sc7sNPjp){RIJ|#pL8;O_`M%MUBeJ~Nl-+U!6c$fkACkn!1%)pDV|g~r zv3(|avyVxS8{Jy4x^S;pWtm?g&+}73wCViEvsTy#%A&btyrsJYx|4pHXYGJpw(}Nd zS=Jf^fVc;bqJl?hfh4@~T2JF<3+>tex+1{y1x}(sIh@#r%8ahBp|%}T)XqQovXa&t zT+`o%OFF8_jZRNzdy!Lp+fzPx)6}ib3ZR{dT?P2!0tPb7V2*JlQka7XdnYgO#|fRe z6k^~Zl-WLGE1c1MJ?HVH>71K+J=k3{3~_#H0wD^m#Tq@bbJVt@V{=~~65tRqu4$%0 zNuL>#qc%b#RiiFHsej&L#`U^S=$ro&mKK5?^tJrqdrL7%TVEUH;OPhOii$~6c<@1b zIc*s-CK5Dkq?MMrWwGqImDJ)7paUZ!bLVG>o4y zR4{z~>(KEr{_dxTmw12OqmdTp_$A0@$0$JW( zTEtwi{4hxygSeL+P@AKx>dx;M8g$Ve$r^RYN!%B_HMP+Twu8R4VIPrzn%pz&!KWIT{OM;Gs8jOZUxRe2 zrI#)-wk-9*nj#$c6zd}LwZ4BVmMXdb*nNT75NL~1hwOcii}W-cBpOsvV4;YtBPqR? zBG7hSXF2!dHTcvrJ$#bwCS8~q`V*i{Kii+Bv&r8@0^y^TU*CU!ckAu@@u=7RJ~2QS z@d&6uIAU&54->JlL+z8jQ<=FwHaVNb6%?3gIbWL~Uo1?!|H8Tj&twAt;CTKGW zCXA7yU!+&Q;LE2WTr^;FX3x46~?hx%# zAc+a{pr?|?^yk-@dcp=xT$TDmCKy(%kk%$_^KUz|eyE+=@eKIu$C+H@s$ z{tAiT;}Wm8Qnm#Du(SG|Fx%n6F&R&B*)KRSwhFVM|2vB;VQtQSJV2nR38yF>@-nnFHs3?*q#IsU~Q!0a%* zj)GpSI)OmnvroR>?A%s}%s9Ut{1M$F6YWxZ;^ut!Zw(6+hIP}f+g@c1&IcpfYasNV zZb^WbSXD~_TL;JG4hr3)u`b-_k&7_E9M$|o_M z2vq@L6#3%7gv@fm7jxJ>wjmi$SQ)2p!XQgY7kY32wunpii^f_+yW)ANi`m~YyaT6I z*MK9+nSq1&%4A3C3Jgk2wLi_b1GY-5BnA!5f2lKy;2YAbla71=y@H|kxbX5wOyxfG zn%rtT?ML+P8Yfpv6?+mqXZ54tN9sk)aeyN>2YUdepqO$^HdX#3wgdvTX=Ls9*e2PD zu(9^3R#+}iz{yO;$xFGmf{jq;Ewou>QBQz(Fc%6Ii$A) zC3gRubNylc@?Sv>)~4$E7+W=NC1zbRj0;>ZA1WBLBCp~JXcN#Jjpb#IUBqvr=yxPC zG(P^%ZRW{@vqN}ml*PyVD0DYQrLRu}Qw5hhT=75lT5``HyHJ;X!xt_-H9%X5w1%^3 zx!q@5Gd1cIA+`om^=?})jHgWxYi>s5+Vu9m<0mHOe>l0|S_zpZbxD!e@@9{5m51h(ine5cOf-_Y?N4Q#iJpFUPb&gEKdk3e*JrP7D@KEw+r zQi8l(F7;bA#VuONPa;#Te8I&C2g)6X^0L4{kH7t;vF3GFLEA&7PtXj{b)p4&GqNp0 z_v@kcZa?LA{pi*t1Uks@a1a>PvMoCvMq^4*`T^*LjsMBY!bcW!r_JrE zavXV0R3!EBdXE_ZV6jF@o^)#Myj`4cBQT+e#mPf;Un^BCb(#k%%>%X^O!}mY2W1g! zc{aP&xDU9dTY#C1`b|T^BR9t9P%rEa9#c0~2NeI)YR8Yl;qoXE4#fv=u7CH^7vS1) zmmEr}PF;wHu<7w#ME@OtGFFyVgt7R-Zr=q4dD58%lS{CoNHPIQkxHWCB;ah0kGxV%1Fc7Bs*>!$D65zLqua zxMnqG#96cns+=79VDG)0_+LS%Qh2*5`b5OMA!}a$0e|wvE-er*Nl&hFa|Ak%T{|L4 z6S0*N(?iw>=1gGN(j@q=5q{^1bmjO@sc(bRv79p>mFna+w=czZs5Vl^Cca%%`_^-< zS7@NS0hNQVHJOSBZKp=8hn(^rk(;aJW(IIaBrC8MU1>)pmZc&@MJd#xnr$;;8a>&QW z0;Son@D~(>ux3n-MF8Ec_$tcOG-@o;{0*Td=dl5A$zUprBY)Irhx)X6-$@bd!jPf%U}~lp@_LYQ ziO3fwMD&aCZ!3VbKe_zd_jaKRuS<2DSI<>{E39tWn=1RCL{C3+3U2=nSt zsK`wEYdvJTj(GXy01j3CP2F7&gKRft*3Boq!0<1EzJB|5I{joA>Dqa-{YT|IQa$9T zLR5MCSd7gS_nvc@ihBTiGvjbfS=WS16&Uw#hT>S%E32I5XsZcdso;0SJ~VGq?v zSrgbz4IcLIWnxor?JrUCs4fi(ijf*k7da`rd86p8YLCybcUrh}T?~@jyDT@N2ODbTMjUCa`4;~e9-!v@Z zLr5)?1#SiDmMPr&)gN<7{{|4v&27vsPMjB)x>@W25D(UIwOx|)D4fI=CigP;Mb=xh zEROVpSK8uF<9vdROgFRHC&{&ZNS{R;H#p*BkmW}%w+KCFV6S4`4W2rHqv`L6(xgsW zyBlW>PsX(O0T-QYMaKZT(-aMW^;(?{?!tSHo+Y{KyZ5(E9Vq)34kl@mvpgkRiI9E~WJWfyy#=a2!l641X)NcK)`oP|&mWErXnQ zb3Zz$#ub>%V6I1U=YRf!iNyLMa3W#NKxgW>wI? zqT!HU#?VHW7egC!-cEp@?e2MtPDqPZ4BFfFh}`4`xDpzPaSKzvK{7R+_dyX+c*`Zr zI7iK2qkqGe;JZ>ewnKsObx`b!93+!DNZ6T_*H5Qdts4&&XZ0fKx6o5pWgUGI?*2L_ znNH7obH1~i%yCQ8N#ho1R)XShHRfonOvFrW4o%S$8l080OuRC%jIE`rz`xLO;k;q8 z=HujA;ghW%N$T*-^lfWO#}$dUeP=_&70iAQYjS-;w3 zaS;o5gg}xKLhskV12ky(l;LQFw~zp4V59$_{G*tvSh$RRB@5Gtd|5l8-8a%ZAE)r~ z+oqB9y)#bTl}tt981q!)@0LtucZ&=t0^y2S*MnpCRRnnn()UXf%%W#6u;s_T_c8kC zL;Ng?&)~u|3-|DJ)HKJ%PjQNhYdv}k=hbc$-?$C1Q-GJWdvg-+n^h{#Co-tm$)**9 za&U;6;DZJssX!DBzB;u&U%7YSh(RjfCA_beDXspwZWl{$Cgk)6=E)F+3uM^dKzhAl zL0YC2E|d~5<`wU@UNy0sxTS9u2!$Pon_`aDzB7sZ;k6B7*IrWt|KqSa7iaiB3ch&T z?Lz4bY>%r$#jGI!7SxS>)wF*(m4PJ&4H|(Sp=%N+cL+<`Sxkrr%XRtphrf~kI0%Wk z*q?d(5_edG&e--4*RY_?%5}@6sf$fw0DqRB;l?hpX!-u5%F*otC^(Vy!HRd3iz!g3 z_GvTHHGQwf~q#Ng9U0~bpr>9SOz+{>@ zzVp$s1p_;$j}x4M4<-R4?S{UFX8!j_erA^*X{01ed3!3>(Djh+Xx0qm*N1ikV0WW+pxj~d2dTY?>nyo7%Qk*)UVRGF` zfv1cYeXqPIk9bJ-36866wJrzld+xv_Nbn_9<)aTTKDPQJHAEn; zpUyBuVhX$@e>A7wjWbC5d0>Y7*8k)2ip_7#dv6N6UA-`o4-=%CG$GeEIpZy1%lLhBs259o}jF zdoPhi8*EV&K7|7@gs$C3>O&tHle?7AmhY!$6P*=0TKz>$ zn^6^!Ht_O$M^8}gt;qZL?5^Tut{MJ>(}mDazRK|dIP6CmI|&7#t$K-mtcU&;hjk-K zNh6eAxp+hl!P*df>?j}C(R1A-ZPp>VCiZd{puQMtX_qo*F-M-Ox>YguBpLIM<7b{D zZuZqz<_1VU)8)NytcOqLjLzA%ek1h|Id0|Y?lq(F8Kv+_s^(3j(_GWg z0p2LI^hJHO=(3r`m;z1}D__j&+@FW9%Psg@hdLjabvo5faM?;J#j2o)xntf+6^Idk*7=G8;>bPnme))JA;XOj2Km$#wZ*GM zSh)B;`lXol7HbR!cauQ~2^ZgXBb!MAqAk7Zv=w|fh5)s*~)sBQfo@cGCBSh|& zg-FzhqwTZiC&(ZryUVuSiCV_;6{0YS?f~J4Sb}1pJoN5JM(CmWdH^%h-;~38@ zp#RdhDHs|2!hKqZ4Y&@B3w!Sz6xZ#M-9f`P*5w>a-3P8=R~CrN3R==a>%glO19+yT<-MkTEZ)JHsx&DCL{} zG`$$?>O%Zp`Gqejm--(y9YV}qvq#fB#~NckMqDW7g+Y&x+CBO`C7N0C3+U zeuF7v9S?vQW!^1O8}@9!XJ-8hWWUL5#GiSEZuc-~4ywxlcgFsC+(@^qwM|Mr#)irE zUb3=%@xJ**_zxce;A1In8}eeJ0?!58-c1$7iN*^4*6Af{Y)rdhS?xRR3n)){HzKsY z&zCu6inROW;k|oPkS(oOc@`+7X@Y;IEn{_CdJ13Gk)v|nfqvA5Ri2XBc$XNE$HDX* zV=JQbvnQGb5~hUpH1P8yh0T@1O*M6Bo9nKPO8lJJB3qAy(X%~2Mv6?^K))XJ2Qw=z zOC|cN#%h$8jg2EYHO%1#QwwH%Hm=I#RN89$0lx1BRd-7Pz!E{QPbp?8{!3F_w3Gq+S(S$gUIK4}NFfts*&; z__SnqF~?{qYZA|^2@Qx0YFF`!kGwVd_vex$7hEgH&|CsHs3N&oYxHz*Htw9z+sC=1 zQ&2l8(Ju$U4)$<|v1jb?#P81xXgLDH8nvORg zt!2BcZ0m{B2lV_?Jq}j8gDV7G`9iEmyqBc>p|oI|BY?Irgt-tOXl&~^L-~!X!#CXs zXJ=njWTpCl*)AP=<@mko`uxY3rULsA4f6>LDPCrCMc?OFQ;B)=3UA!K-EX_-Jj-wi z&b3Z4&sU$`ytOoFoi^dKU;MuK0ZiN&LW?jn@;9k@*yiGXmm{5gcz%2F)Q^W@ zCnDt__XoW0o)2=D^iEtD`&J*x8C$UtT+yN)*VGBSUlonfS-W2#h9-d(LdOe3ahqC3 zdM)aiHsPQPq5Aimr*puP|2TxpdBswfB189pfoTQIeLwqs>7S)Cc>bpIx*rU7m5c&Z zGb)1_?Nq^`a5B%0me#gLm;boynaLVISrKf(9mp*4mum<@?@d&!RFM+byrDuGI|6eM z!)K^QtOY#~%Q|v6u57fM?r_?!@R#x(L&=3^TI5{P;ldC`{rLy|6EWM3^GmpY9Q3Mb z#${lO>AC%X9FBu3M(Qm0Ar*)=XE@vL^75$`iHB93v?IfJw-#SOSMGjNtG{s_PudtC zyV~4y)#ADXh3BXys(HvfejLW#i>ex24bZYyV~i{Cx9|V|H1xU`P%Kv2{$*fH+HMw3nYYOt8?H!pMqhOgZ0Tsn(bc)ANm6qMC zcpp3Kw|g0nSb6|mSH%t5V@0>e+Wv8Tpx)U}F8#m?7o5I&joBV?hCS#=uUO_fK+7ij z-A0vl9_M+9c!$VKx9x>ZyVOf18M5yw!R!~3R;g|C^)l?H5%$4oj#PIVHZlxZF#opJ zWF-&uC7v8YeJ9sUmeqBC-}b`8Vm{58+gLe^l6~MVX-_5B8n449W?eR{W69PfMl+W~ z8O@GFV+P0?8$sz7iY+li5WWV?WhK09Tww}Z5Jq7DUx$BK1qAH+SjHJm0L|&bLfdt-F(w461 z$SqHICs#rk_Lzm?PNs22CNKd2G#fP&(RbQBtue zU^j1=ja(Y&>YgNwYA_-S`rh}A@?}qyn3RD<0gn*YP|dJ0Dv?rHRYigs)iv#N&#Ht} z{Ep<_zX*9M(7ub`jG$%`pTEO#&SAlG*c`4NjR#am2|w$CB#gC*jh0B^tl*A!s*(MRJYzcBFX?zD+^JCR zjat0!G}qflBvUE4`=d>{jL0Ag5HKY{G(G8QE%oa;gqZ9}@=|)1A}ns$qhzS!UEA(d z_ET0u?L)DYe5TnR%XnMT5Z*x_8dP&*`Q@Ua@7fv%TcuLVx&CqZfum`F0hFN`juuNC zDI(T-{HENQC3ZxfY%lbqw6%z9_v1fr<*T?lpwe z|JYIso0$@7opCk2o=)p9!&>>|Tj4O8E!RUwpuR)fwMbv6+@eZ&o=jUAO5uhVC%?w< zQpc^V?54$uhU~n^P|3o|8Jfp<^)ZI3yn;0Fip1>e$;sWY#FL2|(X}wuL!9R(7+m~= zIv|Qme|C~FwamqG<^GgvO66N>*)&n(=KaUf!gc4eB%!oBk9`f-W)}JUCl4cUj_<)= z`dNQdY`kS}Gu9^2jM4DA-e*V~>6gXyqgH8SWlb$UnZ+!%%^<_ZOj%Qq(V0n6d@mPY zPRph1QWK12vwH{fVZ3up1C>fPw(3d?rrt-uIk|Z(@w^%4T)$$%T$xIj+=tQ*0Y*0q z&VUH2KTDP=zv9u{@33M~%3nweZ6C+w?84Txn#}gGGpulSB-8`8`)PP?D|wfYH@#Bw z`D534HS~bYmBBR7Q|BDvlngMy>ZJM&>pZPjN?c`+K-GWn4PTfezq-v6_R?VCCPuHq zBKkXv1#bVB!bC6Nx3^#xd1I&fdN$V@|F`g)i8V}wm~cpE!u`~<#FWT~uC!#Bd{TQMcymZ;T9vv{B zW9nm2XQWp!ItFH7XKOz$^?$h0>HS5?%TMG!bPx=XZFwsEsUZHVEjh;E2#}<&cu!Bn zv%@w+ z5P8j0Z3_T%iY#V8zEye!nAYNmK6-NSQ4jk$NLsyFi**XhM(OSKgVVlNY_8zFE9#+X zladBgr92pOIqO}!hJwLKgR&tX8Hd~K`F@T~cVR<=lL>1mez04|hvYdKsz0hzgg-9) z1Mwg7Tk5HxaA`m+l1$Cr77-po*>0ElNq3;KQGXo|B z^ys;Z#K9MJy#Y1`SJQ`*ZdWwuMt;C;{D#uvP=H1sQk5j8$0TbPYB)fbvuG}89lnF> z81`+qiiZPz=Ka!&i?woA*^;Pq)pE%C9e^%jtaae(KFqO(G9nnjwr6+TqOik=8G6(vt9i{P8a{V z^-7k#M*Z`6x;*dv((YcW-F|3mGd-gCL+){6&70W~ueI5_)F-xUa-xv1Fp*s5s+s)y zI3TG>J2^32J1O6RvwFXxNO{>%-EpGx+NQFU1?quu=VcRz4l6Wy%d&#Do88x2#@6zf1vbY9h)W?8oO@^HJ`aZ=BJ|`0t1_MP#{AAZl}#UKYqR%|CjO-smeV{y zge2gR0uY7wA=SC(?2~4>lkhPchgGc*Susmkd3^ctf6G(S(A6D5eWO14ej?HmIjZ@T z7q@HM6uji+rRA9bzP0~XXqGCrdth1H&)ZJJSYfS`I?dXhreiVsptS0tva*pF>vI++ zD)&mdN`2KE3^E?SY!d02))-z)%oXYbCUDw-Cq9U~HRLHbASx?%NR4)k#cs(NfO1Fw^?k-CX5;35eSI+g z+#=M4_(E>(gJ?s2z4XQ2ErZ2}A)NIW=EV~1-RFS(@^#Bnn>yzKLv1bo<$XFqJo)Vk&Q{mjwtSl-&@A~hzG+qS?@ z*OU-|BvHW2`3CnBmT`qWO<@tG@GWEDMko1exbP;H&UWt5+1d$UnS3#94 zwHF@18gUzA3_aLbDxfNw--Ras7Ph(6)7rf+$vAcu; zKz5zW<}>Meom8j5l)2FdH0UMTY%HoYs$3Tq+h$R=G`ed#-e=qa^B+d6V{*F!2yyne zpfB=yE!taA?09y!+59U{Yy9YueGzi0???Q__$46)l|SFffX6llNj7mSR$nWZ7}Im ziKv1P0bu?tN)~LYCIDkv$pAygsP{4tM+Z4>lgX6N1JdAo`zf~>tiDQ|S6 zR$s2Mn81eNBARR7-X1+5qY2r<)0=on683zAD_C5my+e{5r}TsV z%#w8F7jrWt_0nSY!QX_jOU*NR@`)3|wdel^bPD(RKA)pv$9tf-xk)%c}vu z&AO|N^F1{n?*+WvxDGQ58=%71v2|`B(_Frc-y`-c*lqg|uL9_lTY-Eu?cztOr>WQUL8VQA*pHsm z@y`}}p6Mb>uCUe{pH8V9l-X!uoYCUT3W-lGQGj7f*L69ga6?s61bynTDvnY0K5s=aw#Kqe6j5YVN94A5-7c!EWWSnvkO`tM(LmZd5ec1P8Ot zoL$_$+-9oX!e1o7sJ{2$B!Zq}T{GBbm6_Sxdgbr1P!VZmQ)J|jEv$^ur(Vd)(XAjq zHk~QefyqIju3(vePLE9T-EpR(cb|}6 zKkyYp(YKyg-K)I|a_#4q5-~{hTR3s&MBJF3mYiJ0i^6nemn;)gYS-4Tkv|b&w;1DT zmfS8uEjPAC_pPOmH=(eyu|ok8x1qwdL@}HHT^h2Vz&!<-WUdr z_F_RwjSCOv3aPh{RMP4^A$@Dzt$@a`*E=O*Vriifv@O7vnS9ufOT^a{EbEt)bbYaq#CWEE{b<_{U%F^X^FXbv3-LQZ@ z-f=deeq^Z$!4!AzH5s(Kh2;M+{rX9Vf&Ie^8KA4EdmYbw4FuK`+Fr>18g|b-8_TcshAPOf<2`#Lb5@T=uEe~u=}7p@ zs12g*M1Tq@UIimMuu=op51(gpJc09}FH`L`NK9j$e;k3ksH_OxHo3>xKjxw>En7Tj zNJ_1aYumcW^|chkPXp;};>_!-Dcs`d1%YM1w!vssvqWvV+_FBo^vD|rzvg?aZ^mY0 z^%LmUqhJ`@bqb%#h2mwiAR(V~X8@o}&?BR;=TOoO?Zg;5Xsz-c6Zj zw$8Tqu5_>8eyC}2DL=?1U%9BF;MPv#UYNsCW!a&}EUbt&l;&%mECc#&1n15jGb#>% z!CrX`h>lc;+`FmYV0)$QNb$zZy{h@B8|IC`KCMq7YI&)WTVv0(e-sYE1Hs|=S5<34 zwZ0O?{8YxcZhA3h@(^MzbKoUA?cD@LPiFk7pS$s+U1#^YlE{l-rr)WEl6z7KYS+3u z1FXckSBHg7*3n(kSdn!sgV8-^J<9Y<^XxX(kPBi4@ynIN1K6coPOJ=n-nGZiY5H_* zE8v!~H~dR%3v`b7?nK_VBr&w!hc{4i;e_Dm6JL;C$d1xE3m^(`)Fs({-vqcvAY@L; z5?3ND)GQ+k%q`O_2*2pj51)U&psT!Q$93a% zO6=t~Vti=|8z521VT4kkHF2?=f?Fzvf$bxKQGkvp@+trc|Jo%*x6okKgF!BZ2({ zL7R5NoGU@ED6d$$zp=X`wZj@6AGcmGta_6#!T2d#*GQ2g->Rv5woF7z7sZ92Oe-Um z2RnOX!!%U##T#+>gIz+oQ58jE3s_)F8g8{TS)ZO~0Vy@*Y*i4ocQ`Ymwuf@e;thBv%?yP+9}MuVsN`{E|$o6)IsWcrYpViktD(Jk1)I1sVwxuY;0QLqQC=KYUUqStj9DY4KxL|z## z=4ztmq$8H-OZ1*~TusvL;P{R-QCZwvofm<0@JAYZ$OR97{MncvQ&iyEAPP(pi-X=# zmu@`2tDYG2-A*wfegD9RlMTm=IXDX7_nyWeX(y*8l3aM z00Ad+0;2Vohu)|&^XiL#clR4}mM!l+5~Z-%KH2aT`}zhMnAGsEtIs|WV0|2Qc4s`BN- z-!&hJw3Z?E1Bw{OjXoVGk)I)`_Ttj521&29?HXQ2%_pvu7KsY9P$j~ny9-el!EBF&=J{4H3W9!>a zmo2CEFOkHnNO$akXGhBQeywJP!5axwXn2J!7v0a0|47JXn@i{v8<*By+t_jo_a$6y zZ6qU}TjoDj_jICvcaHYWqCCag#=4;O121WYyH^-Gpj`PtiVEsmtBfaFva4YMBxM%0 zhZiYS{Ix`DDe8+>%!diyKLygypPoNwelmbVLDRf_ur_*WOM>v3Ho0T}^7rJn!#Ye3 z)-pgh%CugfZv)u;Xnsh<`dmU#W1YrF!qMF~^e4HBaxdgSyf&u0ww2E5^QQ>w;%oX{ zA|}0M4G(F>*5lZlCg!7(wq70$l8*kVCdXWzzO8-RAnP>YVESYbE)+(^!tm^O+O<(w z#tp@EtEE#l8c&@YV=kWQIitEFB7ds*&GoyN7L#!0eSpG-NlbZNZR3;pu>+O{Uvs+QR&t07u?W85z7;;kDQ&iW~G@G*a1cu4r5kasPC79=B;kuCW| zhC^mI!QYochjo$Gd&(oKwkh*w1)QdC*Z?RdAi{!Xva&$fs()FZXa<;%YA@t>_Lub8 z&Yx_wI)AS&{alCMLo!EyH;k_u$<|W-MRbX>lYd@CM=z@i1T}o%+d3|(_KxgjM^nTr zr~^n|9zIAv8BADhz307}*kBk7;AHKcL5jORSL^UTVYYCeF<1^L8Ct;MiBG5Y<|kEm zU@+*IC7jP^WE|J1X>aRAViuJi+HSiTmyW})Lp;C2gTB5FZ(HP$_+K*LTI!;ixyX<9 zk87>=;Nd0Dg;aa_^^o$y9j~HvN6)>7z!+N{4`5cjbtCu{tTGcmc`4rUd+MIAz0I70 zX27Y;30xMrPT5qc>jbK%9%q1jxHWFb8jZ@g8Y-2u7Rl54os<(4ifJNI2u%tD7N^aP zGA-v0kVfedx*PlmTvJ~}#StqEfEteueVNRQLi1F8Zhr193gGX#Sy%RFaP|gZk0_v` zFZ;b50cbBN%1+%2Avsp&hVIlp>(9}8lyuJ4!c{Q8C`n1J%3PHJ!cN*y@^9_dHT^~ zLo$s#3Nj&hlnE`)>CIsE?%$!SMi&+}ILi#P@6NgdWhvsEy}O~}L$wnX`idASBvu{h z9j%?22(8!OqU6l->bSbm>Y9SLD!qP0j_b_MB&B|a?7OXewVLAFsC*Yz3iV?;F!bDm z%D~V~dSzbOk{O{OK>Lr{&u*`Ek;iv3_&*UB{a(FG8Lr&uj;;=XR%qo&v1vl!tvFM9 z`$}(7PDAF}PTj)33{9C*SSGa=0MmuK$_3nphuBp#z+tw3`3=@cMl0e`O3ual3v8;; zhsn{4pS~oC={U12Ph5)84~RZD(-OW2#f-a(h~Bq^D<*Q=|~&(in@u%`zY15urrBbML<(zE$9V*)Ns0y@gWjK~@=cIDS70_-04k^Jx zsAb1h)ZAr~>|`)%hONylAP|uMYj=xdKOL$qYX3Lol<=B&PVr8CmYJr;>g{_9Cg#yz z(hcfNqnDLyILHeBfnCWo-fqn=E)bV97Dlp#5mJumFK3E2Pmf|bS$Oi;g;Gx!^x@Md z>gW2y%k_~e_y~QQj3UuMm2Rmkut3^qQZ^Ugt86oa>mU1qw-Q@) zC0$VD#nk8Ulu|kSdZD@2;;EKl?|M63a_P33m7gI1!kpY8=4P?H-uGLSntKkv_>4=) zim1rJhl1BemvgfT>pM#P2DbvE^e&aql)t6$^q)`RIAESkkUyy|vUs7!=97-i5vNxS zj6dtF>BXxtUYRE4#$EkgIxeg_3eA4c%YUTO&*AQ{oIgXHQhQ^->par7 z*D2jMyrQ2GufMy3J0WCTgSzyv1J>PXRUY$YWpDwd5@g=4@~UiQ+6TfkLd+1gWn}q+ z&8V<@`k%vq$itVIsKc4SbGI!O zxVC1uoX_p?xHSAPNXEft``#Z?f;#&pyY(RAQYSyyvU;S<5b2KvBQTXer%n9)F|Jj* z!ihVzhReb&Jy!v3Qw`5Zs0Wl1K>GzD289#S^T;mz)q9A8a$N#^-6Wam0CO< zZ|pVzsiQV=F&2lFk4hFl(6}bPy=aWPmmjZeb)=9kv9qwTc*8YUWUxHVjn|^hmFK0^ zP2Nj(MFo8OnJS_{0TSU?f3|y5{XqzV?v(RfdSgMXaD%7kh17I$Z27qMFD;hfi~ttebJ0gg${YOP${3AKr?*$)@vvqU`rCrmIk6PAbL5asbmhdm|p z1ZS=W^SRKV)w3HXo6R~)m(hDuia%V4O&4eQ`*gVU{RHahzc!5p0{uiT?|Ro52M8;< z)M=2h+dp*A`Bvmz=FVLBv^t}Z8Ze>-vJ-*n!H(#++znA*@UiI&K>*xB&_9mKkd6n9 z{*vFDMKBVjPCpq#Mxbn!g+)-c@u@p#)#ulAv^t{A+vLFQFe%=9ywjcrlB#mS_$K2V}9 zpuRgvW5;uP+62h9wb+ZF4lHmNW^`|pyJH@hzQ3PmixKTJy8gDX^5zu_tl$3N&wm_ZTbD(GvrL?X8XET_W{l)-DkC9}Wsvl)Ti(qPH|5h(jN};a z=RyoS7pUJ>XkT5)_Q)ft`jWBsLGW;Sx?6RJV~Sr`my}-}zOuB>p_|n+IT}JW_+)~) z6LeJq({%}P^9P{exWpj0N*|y4?u+395H_VfsJs+#*}XaPnhcI4e9AhVK2i%_8jXUr z#ZC$N1i{axO+0H+FNl?N2s|2OO|7iWQ}0<8NcCKQ(_aSsLdzSK&_}UbviN7l=K$L; zS*nNnJ=0qX<6>OcDSpoAG`bfuX95{V&iR6?o$5N6%u3y-9U1-Y;@06CF~{3$41uc~ zY8%)e%rf|g^5f)A?%Q_OkAg)N$Gbm>L^eV>Kl{t>?IRSLt^GM)UJlm4m5vr@zzs8t zwVgxsjavdo+RH_DUhPq~<=-vLvZmv{(6T?znj2;xl%M%FU zsSP^q-1tOg+nGnD4rFPkvd6hu3vaDVbhfOIAh7d6-{m=8;1!yMp}c7`)zmh=7$S) zaw9xI-|eLWhh*!Kkian)pTSb|aKll!=b>ZR%fDU&Mj5ha&iqW3NsPWwr0QmJ;kh(? zYeUib^rK5}V$ULKi!TEytja7=I&E?iS`6DshqT!;0MJcohshjTlL=`FJ9OKX5H6AN zKXof6-@U6Dv(EvSK$Ed;;Sv^ax20A4ZS|CC2}lx!7PM4A%?a3J zWEO%Rh&0s<2t2j`Lw%!nYj{=bIFht}4EZCf#h3>Ni$>Yh0gm4`xO_eTC|^)*QsDZ;OOFHx+QvLwT5T})**SQkRx|Aiq= zaDF&p5h@{b+5OS|w`pK#yIR1QF}#e#IPhQI(~ufTP1br*IhxdMYEb5bvno5>hDNXo zOOh`@x3gI_M#x4~Qn^*_JdzwmQTnB(zt6Int0i$F8wz$BCpT(Qsmhs1;Ltcw?SuarS(a-<-b3pr9?PLF??UG==TvaFWqV( z6J7RzsEPNWOD-)262X}HApk$9x4b(u!KIQ2)`l#x{m3EyLvG)kH{3F7<5AlgEV=hEeCJx9Wq{}`4bg63n%zadfT)scH)6rCokCs z-i<2wYg;o9ff}|HZv9Y^s!b4?OpR;I!3~yB8h69|q(60_) z7}1nNIP5uIr>^f?HI6h}CB8Kc$ma}qI>XwVi+ZR_%Dg27k&cn+YyZ$E6f>WoNbcmy zG6b3zj;Mq^*lIi!Z3OrqY2=ipg;;XwAblb)+0AzU9rYS^L<(Q*Ydx| zEE;Za{Hkeqq{eZZzHD*f&9;zhYr?1R@H3Q4V!D^#$4tM*8iM_#s*8=6e}hfzgH{lk zVU_-6w8F97Xvh*GglUp)R9xJ@r^3QqO0WQg-9>ghd~5k0&XFqO*#v!ZnJkl3@rEdH z=|2WZ=>0n-hkga~rM1b3eg)fmOD6;4CmTSjZDv)Vg@n&VSN5m$TW+j9tR3= zd2jMyB@mib@vT&AH7?6`Fkex+efzXiUEqY0M&;99%n8wR;1W>uZ5osW|I(DF@@F-# zU&HTt_1aBJ)!8pwOM6e@*JYe+c_iVu^Fao9c|F$j{;W^l+`I8>1UA%YTg2>fw_&m%`L$fstpsi}#g%-~R}e7L+Hy1qU>GJjU|BctNw3zFqm4)zby z55&GaB{}TrJ9^>~9=xLN{itnP_@;g3@+??f$xd88R3R*T3?Ayk+9)-lQ1#?Z{kP<^ z7rbbWI`O|#`U)?XAl`R0a&&Nc71f|k{29Qd-P-iwLxA?)xPFTW%jH$PQo`~py0uJ= zW)O7do%&6zn0;C$$@fIgGy0x=95$=%5@OamJvS!uWzNmdeKwA>-m|L@vZij2K@im= z&Dtrc@amK1*p+RHy>Joe{@%+wW zwD_^cMD?D^($r$ZVZ8}q*yA{8e7)(L&<58?D_8Ypk(arI(vHCED4E|<>>sS3DSSX$ zCe6#YiLTu+rsRM7V-2wzwO!pAhx-OqC>bFDkl?`afjz|D9|U=?u|T8h(6Wu=^5+*{ zGZ}nD{{Gw97yq(Z@oDqwns>5s3>f6|YglS&h0Et3Y9B7`QmK+b^xdCV#}nnMGHLj3 z0CulsV#QgnYC?M@xja*>nD5o|y1faZ`In;4MIU!R@sha;j^C_|@%D_nSI@AfL!j!C zf7v`(pNqjFgYM_5UFfyt<#AhN z6^4@6&PS@amvAYK=VZ}*YdsjDYg?fgL^)HMNK7n#o)EOUYlvGh%CT)s&gySS`ldGv zczCr}LSME!bIZw2@%noyUeOC&k*`2HilFh#43Kp<>qf`cKHN8O+`{$SQN11jH#m;K z6xoHx)W;~B!WAK31fca=rdQ_zzJp`;{%O`0X@O2umj&h9d$Qa7iE}LE>AYLH5$6=@ zTT7}BBl;1+E`}bTj5mZmEr5_)~q}w-gq|>T5>ks{G!gl3yE_g$~^<(;u71Fj!o%* z*}8pYVp3CKN7bup)g$=4H4nvGqc2w{Oqs&r14N@Nj!4JWL}|9@TVKznO+@OWbjwjK z952Ay+$OncqN)&`(u89=N`H06zZCW2oJE=35G@r{(mXQWL$}yJMkl0d4;cYVHSc=d zmtCVq8J+ngVesFN4oB^AzRj6(u5Dgz?mV?uf=G)c9c|LkEK_KhuviAdjp*}$1ez`UA>f!WKvbxC871aT`iN$Sp2Lh zW&W!=r3UnGp|F1zwjgIttZOynoc8KMS2k0-+iMbqJJ`kHAV)uy1uvjNQw~LT5HJ=d zsI~v*UpDA~Rdmz?4!7@dg#s3)|4AsUTb3IsI3gw^Pvg9ZxKx9l8+sykBVc&O(t-On zv-~*4*g$5oxw3z)xt@k1hOFI>_f0$lLWVeOAck|VX6wWuMy&#hTgB^fp01y5-|olC z_ay;CYBh1~H9c7-gWxR%Rf$fg5O4>t^Sy|NX%1zvee2S3Z!1g_6f)}r!fcWKF_v*J zQ}K#emI%V~q*)Qy8fC6U3*#QN z$EEn`9qd~V7X3Px>e6#L-Rm(!cI)nHXxWKXZN&xG*$jDu!MHSp0D0tCWoL4v&YgY>eOUj;hFHCGS6P;LKQ0Q zSD5bj#RF^d8o8@bJE~G<z?Y zzTkZ$1#-y*+H6(Uj{?Erc1x=Ybu7f9Lgp*z*iu#fDvo@Jr0)7HtT|{|BG}Vke{@Vt zVw>34Y|sQo{cA}7&=Hfm`98NHAEsxIJ3y0TW1eB)aXGU!75iwM;CXaygS(KU&5%Wb zi-8_JtuDnB*=xBl?4U7tzoNfvpvKJ%y*vMPov|5^NE#RKeV~{818{ud)A!fSji35w ze(p@*xgvIX7KYL3u$TuerAxkJ=e$M-g$ z1ViS%W0b3H9R5kIPEE3()}vc8T@N%D$u)n<4dQ`5X{1PPp+qgC=6E{#7~FbHsAbA$N`c$g50YmPDhJn^dmp{*-p+XrBn+) z>S7#$+Zx?(-i*nKx+&-D^h8C;#V!xX^MCiyn;Lyqwee0!^GL4Ad?QUqs9SI+`=|8x zN^IS$*di1SEBp8Tjsm!rhPtb9048F)U7CKX-A;Q?#CwGp(BODLd8ML}zKI zASy)J#pj@jAe85!-Y0Qo!uqAcUaIP&*0Zwj$)_H&-|6Xd)U~Wg2Csz)s-3^ae#p>{ zgXt+UAA+NuYQhtu|DbF!X~Q71vcvmL**^4q1|2c{OH1E% zHby%|S3u~|i*Jd2Qjap7eB933jN-+#?-|88@H{8SWy+^#;79dgw2_wZu`SmrQPZ5t zB?GTYCVUBN3?V9{(pYRKxAmv|N6*&n5-Y52V0MowN3!LUdTJTuJZ(7C>06X5GM=@g z(GPTJeE2${NBbqg=hMpmgk95JEjPJ&K@MLPb9*^z#DvY)d~)m@>CiO~3${?NlHlX_ zaN!W!V0HMz`jvKwCiYjzEwaGLzFoB!oz1*MzPlr(H|N&0|AU& zBIgLf%XxRqNR3c*k!=q-GqtDTqrx}sP01h5`$7nz4J$wIi$-PV^DdJ;BQ5cPTXI7s z>+vZL%-uEBKa(*M9};$k z-`Y%QQU9F5RdYcD@lJWSB`<4ZV{b3=Xx$)xQn$2H97uGiw#niQ`v8UI_Mw$Bg}GB= z=SFEU`p9jVz3kF$!YZcf>F1{BWfN@<_Yba=rg~IpAwdG)T7fi>uOCI!dOX^cLt~tU%@-)%>h&EW);0xKK4re>Nc4#FYedG&fj+F78=LpX|1XX28QLl z|NeWk^4g^j6_;yMy1MMoNL1!~5ZrMFh@umr}>0td*=YW4+HpHdF zqaIvItkLC(Ha!JTseh9-6scp#vD}*0EdF+%#iIVp_7QKmdT^_}HM2{e`*=-a1eczl zCqd6&d(j8!w9eMf7_mq#;rlG*%ij++tjIx?&wmzhR7QGFR zn36!j`cYiJntgb&eeX>W!tdX%JE!KTvHsa$(E=65Bdxyi&#hsRsnY@3+oWbyzB?hK z?&0Ny;~!m9Ua(6eW>V>b?ykTk;2oMEZ-wBk9UN{8PbAw;EF64| z;WuE<$<&VB+oIBKCNBMV)gk?J%7vsPAi-R$~Hz8f6SWg{ycXRV!5)ZQSrQ_1#8mzAW zJ_8_f>VYr6DP7TT z@M0*X4s26l8mWwzu-a1XAlfPpu7wg&e#6oJ4uXp@N?_A~*Rpa_u)Vzso&;O6p^IFG zKi`Omi-Vk3J6bV=*Y=UKP(%3?Kui;5VVIevEGX%_Z>5laNMAOG+RaWaZ4&awD0^71 z4<_kUa`)yTi8dLm&JYU%M;B$>R`Fr+V>YV%ibRAns25^*erAySGH>dB1)3-%?U(fU zooYI_fCjdVC@&6vs)H$*e#ovJIhLSjkTv|NHp6hEM48AymMzPs*X$4)ZZ=ws9~(&o zZG_UWqnsVLEZ)gQG$r?!N;O!1;4{;CB1CW#D3a#zxKj4Tte%`c%=30Z(_H!CWrVj* z%8eL9T!B1oXG9SKVBY!I+BqOe9k5oIlusqC5Imf#4;2RRS{w5TKILL3gVPB2KbGfg zRR$KcD$yTamh2lr;K}r{=dL&1pw4j>k2gggb2>RhCDl9-*k)r_=d`7{({T3#7!e%aH= zK+k%2uFhhl-wkt2sr=a5lO55B1YS4B=`~UTmZ&9Kw*K@oddEfInGR6!66}iJA%3aa zF&5kPnq2rNWi^09z=*&2+yW#iHffTg6lPhrIJ z6H$Ji1amK{@8gfHQ!^%2e5q-vA(>!;cM6Pde8>#+65&RL3S@f1RQeQ_J+FiRd0Ssk zz4`NojX}HaO*f~p88c@uOYZ3Bc0ljW(Kxu8mbK6HzS-~NZ1`c1#BCyR*j8ME3{;uE zduwW~OtGpMO*Mf#0Z4YE77)9YgoDoM4Xc)VkH^QoTI_8V-K=7|rS3z^@u0fe_jcm% z3pYoh%2hS7QtecqEwz`&*}&{wGM1CPZm^pQ>|dq$}!`5Gfh5EXL4Lh z;CQTG0fIfZqPPbYNU7~5N9c_Gc7$taA%soOV7=vf{JgGD_nMWB5 zAgu%|jrURz;+$iD+2l#1`d7ZdP;VKZDJfEOnPR^Fq%{%AbmrtsPX+oDk9zg`^ims} z2OKT13`@lHjc*}s5z&@?B@%OS9j7q^m8~q~Vw4E$I9wWTmWJ$^*&3|hUYtj!sFp>g zz5Y6n#ysirZH}*aDpZx}p7FS4L@?`NU5azc5}b0qw(i`{X(|NB1-f&bJ`=H(m%as|J)Vk-I&ZT7!Y9YCTw}i45UswKycA7Di%{n&^fdK)E zJezp9p`M69ffd}zm1JOOG$`CspVHa687}uiK!(cx@UDMnXy@v(W#02s(*e)oUy{Ge zd`oxO_Xl5fiimo8#h^e@zM|4QKFBDxf}OtOfExC#bHAdHJ}MciX69OL)w=F}V6VLg z+y#SL<_u_(UihO&nHjyX%sbr4ETV zX@A&VKJ~uiMOG!u8=f)IAFMGhSG1pL>z0KRbHC$z7dDCpUcp_p79si=!qJL#L4m=` zyNR6E%y>0rWQmZ~qvR9-QhT2(E`#!AN)}Shz-McnKMQztW$Ym~N>RQLG#Ds<*+*bs zd9k6|_jYe>MBu(fGQ*hoCx;Y2Z+~fE_^vH5Mq*WF@4kQkJ*c>SKiYP6KS)&5jj`b)(}#ZXP()b z&sjCY)9K^EyoIPsr^5O-Q2=P;7??|?&IBc>=vdH{%W6=Y*s3qf!->J8Za&9ShQd^N zb>r;I4uyC{>x zGs5=-3Qb6_6jVsoT$NQiL-hMs^o&wy20n|O-tH$%28w>5Ec(R_f0K*%zhdC^J=;nH zAnP^0=?^8z*^vC@+(NvlW=8cDrTtfCs|DY{@Vo0a?ghPyx&wSb$vbbl9j;J!xZ)MH${Su~rQh6n&TKIc(eOyi!2=ew?)vJ!ENTML}{m zmdx~D>^_nR(xXxl?(pUHUdlGhiK*xBjt47@9z>S7vSxbhl?OTxlOG2A1_yUInzSn2 z?|bD4aKwc=ZZ!fBYDTx!y|Ye?v#?2E7D<7Nj1ziOkXC1 zDr_2}AW6^@rF9(bDBBArZMl>Jd=tg~gMMGbiZ|V2Lif%!S?3#tJUQNP2K_B8?H+!2 zSZWY^^=?Oq3CtJq>M23HTYjI!HIBkv$;ov;w$#)m;i8w3@*4wrFOPG%$-y-H(%3<` zM5{h38r-Lt)Xi8?)Yf;k1axfjullF--DOTPcv^LD)Fm~y#rk=t&tik+(50Dkf7!^v zOT#6BTu`MliM%@q^=#cpU`40)nXmTPMkfBDdvQWdcj(j~H~ZB^jO!VY5A*kyz! zR58x{ozm{T_FA_~(P-0P6mHRnnkksOGH#f@CejO1H?wZ>rTD{vRY7qSXP)Ab<`<3R z8BiAIjl8(tbluXVj0vv2a%bJ)`id**);kaT?>x22P905I60vuWM%ndd(9rPf6+lFV z+1CkVcwpxqUeBsWf!f`oI!TYr7&J`vmW+Pr{I3I7t{?N=0*Ys1Ocluy?)RH{HzHs- zt>&BvX4;%c8X5(}!gdL`-JtPMiPeEP^-F`XsCX5drws=-U}u z^>{_)$Tj|UvCjK+>2Um2o`f)S(9%~a_Qt*0vWt@5SzS(kIjNLsuVLz%>(1zl#sQMq zex*?Y+Ka3Q4XsL{R3ECF43*u(Ik@=Brz8B$9qxZ& z@CD5h>Rc{Y7O0jf`d1aL3|U(M4}>w3gQV^NznQ?L6xiaJ9WD6yBvZyi&9tK1=M6T~ z!m8+3t)A1A6(hEHZ0_9c+n3p&OS`xvD`vheo_%NBP&wo2%T>G!0d)Z65$@GF*6=;s z8=qR=kYN~rhZ)1Xq(Hwb{dm-3c>;?EJ7wEy&w$16dYtX!B#+RcUr;a0H33$oz)~aN z#(Oi>I)3a17nicgVNuYPOJ*yYG*7qezQAkI&*Nx_G?{8s4 zk#At(I=^d*ipDy1U$s~r9`ac_81I)%!7IuU+Wl83)~-dD9B21GnGW;5iB6M!GIsX* zH&srhiHf9$yqA&SK1}uy=1C}oU9oRvJP~UgL&Hg!VHlExkxY<^aiW}{g@WyDaiHqT z$T59<+`f)%1?wq#(o%yoskt(G`-%9e?i(r5^F0o&?w>_5KZ?%=mZ%5as#;P8CS=Br z(t>>wW%bRriK{D2&U^fmlc-xc1jeu=XacwFJ6Dxld+^>~!fLx$-?la|-_4>H)Z}*Q zX4U&fzW3}=37TMUckT0}Yt?2ko=6^wF_@5W;NKE%uT}U{MW(5km*x+WTsB9qjx)>T zeMVMCWW*!halh^N_PjJXSDV+Wbnn@RyA>Q$jxT+tvi6Hpe4-4|VP{(RN5--eq=^m}Zn=qK?NO*CoHMEC0;mSPGMVT91GDwJ=*r z_om*}y%OB098q|yH0I)^%a~oQXDi`NQ)$@b9~z#FR9w{e#?RFqc1SDR9*vz$0-8}| zLS#8pt1TF9Vq%QGB1#M~Hh6E+4iDVj9K^bh{~@$4xU498-AE?rIwm~E)zph*pR=41 zMb-)Ne&j}c(RNQ8Ce1PcV~PN|7Im@oY9flhv6-FbqJ*}i5f)Z3{?!EE<<}=;gL6Jx zVs>;FSC)5F?-s!QU%|)83K@K9b>&}_{GHoYSIex^1z`k?5MggjBR0In0l$QM1CqDU z*u~6K&`sN^%Y&)q6iE#&ibrv{RLM1jgjaIKmH;2kAtD9HHlAP~#QESAm5)@BU#&Q&XTlt9ss3D=U&5>+qg0JK*xYJwf1-m!4d9#W_CLH9_C7`W86gklMv>jFWUOp?3@4 zF8vv-Ks%5$qxDS{yDE%6rQWvAUVAurGW;M#mQQl6I4PR;9H% zsx%$ozyY;^V2IC_(cRb48JxD&hKht@oF5-4Sd+kIGL6U18Smi+sW#mw67=jyoBpjQ9R|xYbw_gb2a|S(a!!WpgjSz@U%>JwFa0m8 z5pYp}TY%9Ez{#>$ubAWMptw?nb)83Putqi8pn<(6^Ql|S9jiMK65c`w!Lex+U#zJ% z9=0i+k*#pNjJ8kpa4d2Gt+$R9OO=+DUl0!Qg!KIIY`7*S<|Xy`0_V-l^D^4!r+cif z=v)$xtIo;j$#$O@5ApEzVS3nJjj?L||3pJ1Zh6~{EY}kxP+G|JFF#K1TItB$3jsfU zkf8O?Du?I(`=|u3DszF&In_4Z+Ne_{N%fk5vJ}m#G+~okhQ)|ME^;)>szpCc(u58b z%83x6(jIcBf^GBb@pdEi^Lp^#0Ht58`xd42M5CN#37Bw z=!)C7MR23^K~-_Zj9pmw(>6K{N7-h?KjxGzr&gY6XI`UgKO6Wnma;OKbo%ARsb0j+ z!%xK~cN1VCy*iSC$l2A5Xm!e_5^UP@2JA+Y`~n^yEY8X+J;EaO!oR)psI{z)V-x!- z7ChZ)>yW1MZNJ&i>W0GEpJr{60AyqYl4m4t!oI1v?D41$>!jaWJc!n#D2kimF3L_r zkLr{zhgwka%QrBgMUd-{6>32KoT-d%&JX{^A(GBNv1!+Jep~A+svz0JLL63Ti45ue ze0dOPuj7kxIuU-OnIVDHaOw92peZBAm~ocvX7BMwqyL<)B9PxgbRk|<Rt?N=w zNlLGe+^+GatxQ>I?!362*{Wz7F_)ghz!9i4pJgbdzT8}@p{tj-2l$tbKDbx6XI&da zI;4(1vbCUV1DrT%JPmNI*q&^e^10g?itoAJY&|i?&%cu;#>}8Uw>FB9XLF4Aii)t? zL6{DEe^UIyZ@35sD%=aF8@dyo4hGR|E9`Ika~^dWHvVNZi+klZ|D@yP=V$l?c^9Rp z0NKci!hqiUmhqMy!9x4CtN8?`Kj|nPdx$jsB!K=|f<-`kGWwZ=B$74QK<%VhaNp@A zWyi(o_2YymFD)EGA{E5Y4>$w{cuux@9HO6}i`N!H>zSmQ9WV4|X{dtq@82ek}h-xOp2XDQK8|ym%mrZ};)nQnN_(}f2vXHTO?9XqXq?H>WNvP|H zjz`Zf=YA+Dw?AFtt%v(T6DZlw_6(tM?~QE}frqGSyEqMD$vAt}G7XpFgvw=tMg1}) zaM6nzpb77`3m(ro6e&AZ>Xc1|m?wkT)v=Lr>l192TI{HC0&wN=Wj(N3QK={yWko5c zh7IZ+8o0LjqP&**08kes6RuxtmHXzJ6EIISX`z4!-wqYqX(syB$$ecfEi8>LG2~x1 zw(eA`LHI*G)V!n~L?Mz_-@ZGen<6F*-M`AXG91xU(-7E8b(Wt+``$^4nTtOxL)oEO z30cB9Q})=DzibZ0cPelJ-Af}mVG2z-GQ4EEX^Z#pqiMc~>k`h)z}K$KzPP)m=4YQ)nXUZMN7dcc5II>BoRW zR`A=f9}5YI0)m=waO%rdH6oPBbb0CQd!g@|M`m21>qlKh?XE|fy^T7V)6T_;aph@t zX^mXkN7<>Qro9zxO_!L7=gD2umJYm5ogQ-(uTK|cO)l)`Sx0?>_R>ySkKtZ^oyLkU z9%Lva*nU+QHTtK-*{hg+^sZWI(R2!mOVBTkpV)vx*B@@T5PNDwWrBzr@zu>xfbG5q zy^kRgqF!PvCQXOD5b=XP^}L@BShu#|eEB`8{&KOD38GU$wstjoDzwT5Rx+C@KN3GS z?hA))5BJtb7uPGr*(y~G=EQv1GsPA!tgamVWs@Lm`6w%{pKEOS0MTC_q7?U%axR8b z6t$PQN{KmVdW607Iknqdx7g0{dRnQ&26CHgEb`?2l|*4hjA8BmZYwNlt(!joV!+8n z#Jr5lM74X-FY_e^PVWwM-$~9Z50NHEv_jfsqPF-I0sG}yIyZzf1dVTY4~)bQBa>>& z-_14DD2KYqYhCtDMT)pm7Y6|X8B}$@Lq*qaXYVo%*t-^0O3oBMdtsY`EAZl1onjrM zAS5{XDtNQXGW_e)o%2c(H2VbxUuCeWa^19BefN}FD=*%^W{Z_+0Ew7a^f8yPEy2~* z#}2jibGR+h&6KnX@*nd?lloXpxGHNo(n4akTshJXIadJ<`V{5!LykziD2)4PW2-=3 zGm09RwnP$c(3k2iImhT{2IJzHCD#iS_)Ix;i%3n?3AfK#=N#bf)F#TA2foNIaS%UV zR9&skD@adOvq9YcY>u=r$;g~FVSG#>Wr_uon0ELbkCo$@^)>yLAz#&NHv&+*?}v7B zmKT}KHSC1~ASIis3SYyLsub#z-}2&Q65oz;@-SjwiJy^mB!))uPH#wCCRTS!r3IQ_ zHk$%@eP2V4=Vqj&29;nz_wPr?OIRmG-7(J8Q`Xz2!B%i%R`#~E#2Snm1gXJF*J`^; zt@S)+$LW)OXs$|WRlW9PU8`Zu3h2vca$4V5M2x&4@mrXpy3w8ZovqPOjnQRHc?{3*|)ZA#m7*pA^I!(WvMenrgq>o7LWVy=+_%; zLjz~8zqjg}*9wDTH6<1f9x}i|l^=1njy$I}AEeTe=x^iRIOmvhk4ax{ZYKBp9AgF>$>vtG35nQ_4e&z6;u zjc*>J)4Z}AznheVuT*_YNmn#5OgD=!p}-6lX zM&z>Qut}R{7Y=oBWhr7)v{xUcpH`l{D^`6+T6U@=*)hi}s)URij9y z#bBDPdx0oB+8HBLa1PhP`1QJRmKr;$-le7?z10O9tF`BS} z=N$%bnBhkJn3t8tfSe?$$LF#d%+o%p7rd?cl&}aXSr|5yI&$I4(0o1Mrk$! ztGl6c%aoj#!8~Gjm#j2F6+d-~$ktZc1LHJ>LrA8b{dMf0!}4VdmZk%-@NMD1qRe}j zT{U)p9?LnwIqp^l`7)D0Uz&SD4K~wjDiADAP)KIP`VaKR6vwv?nY0b$UB;>B3L9f9 z)xiK0Mt#iE5*58Yx64Q-7s{`oFz}1s#3%i%R(ZBx2Elf0(iQ>rHwsZ&FNkMNB1`l~ zgK~k7aXuqhkgPEtLR5wFUp?G^luZLp_Ona-CLzr1g(VW^zM;O4X#{_08|`2J^6I4& zj$8JEHc4QywSK8CY5>*JArFxB+njS{Bp_n{eG4;gwWuOh`oJy90KheaJqC4`8O%aW zF27pW_=H@b=()hpefAoNF8h9^Rx$OI5u#no!8}9$>SBEH+s+0yFy^0bDJ2eZosY#) zt+w5VYVsG2OD7yq#!;D2pGz;ZXnKd%+Uv z$Ys=NEwo)%umh~?XEbu_;cWqdr1$(qB4@aQeSMN1vUPGjhiz3*$`at2x0MFsY&^JJ zxJE%aplV1(*ae?T6$!&JsL;W977|Hs&Owk)Mw9-uvUtBQA%~Dq;GR*cK6!q)loT~^ zY4s7-za4DatMHKfgVoDdAwPwb-WV%L`5m2vGS>}oJi{0((>4M*%~h*e&z_!^TQx(0 z2*QC(y{_SZRm@q+bO(dWC8X?#k`z+)kQ=7oDKrqmM9*x}6W6zlHm)3Z#rWEaGMZ#Y z3GpSPKP&8X0zKz7$&)r{WXTQH@!mk(j71{^k=)DG?X|um;160D=E|_F@YRWmCE#z&zq_nuEke8nS z6C6yG3#zW0K?}E?+*==QOFX^LeWHaU+Jamj|9XD?#>p=4T!RHE)A;aJ)!wMd5B>hb z#>X}f?5hTp%GlEwf9|4$JLuRnb+jwaaIB2a3+QKH&ZXnWC=McyJ=<6x^Ajc6ID2MG z!c9E#Z=j&c@C{87B#+l3k8tVILEgljf=Dr+bGVHGlqUvy@DcK8{HF-f@^gw1_*4&i zf~?2=HhR$MrK6OLT;cMf%qxpKN&a;L9{3=Rh)v~sMLV30NhHvH14=5StPzJ=G_;Lz4N12W|n5N`kxF&alUIC zt%$0KM9MSGUn$%(2FjF7m}`&LTBF*oCT?jMOvEzH!8iHKAl z3m`Wnr>Y&NX;nBi!`ATHvUs!u#i8j>WEC76lnDnI-OGZ#l_MM);>~Z5XtJJU203QXfk6McTqJ(h^$ki z!P4rU_PMc;;?I#B=WkxIkSh~D{39CUX|2dZT{3|}GFX4m`<8!-cMaD4Be0Tx^m4lX zvfW_1t<2*yjYCb_sKU{1Q}s@Li+*zBJUak{!oyn_&mjBj&m0?MGA}X@4DMgCzw~@K z10nh5e4e%WIu_ASGst}-uxDE}UOU(|{pNE3;alnY7bk1I$zMZhf)>SNN-h3blgJ$l z2>!nK#?^nOV*AA@iw)dB^ zo8Hu#RZB1(ic*Pl)h_Ymfz|n^%;v<|+^b;_2giI7szTX+1Q_r0VAb*>7KL1^4V#@N zahdHo1QR&iw%a!$vEY3Z8K=s_4!eVBIzI}+wh&VI;jC`fe9Ef1>8|j%2BPdE-H|!n zCTxI-o@2PE6n~J2vr-qZyfsR3B={wR4t8?!5mQ%y!w!}KmH`JrmDK%;zZ*HkLBUjRJ_ zupL_tYXVT<;IYAi&{0qoE$5%5M%$zWkG-Ijcr@bXK&N(4nH!;-ifs zM3R(@#?D#!tw6D`ZZivgb4T>nAHVB42#6nIIx?O=QMGB!c(TY#l zCG(TJgw~n7oFyJ9QazTEX*vH2)>w~gjSL*D9ZT9bjY0$gQ0Kg+mV0~HkdeS4H(o=Asa z9m1~rc;L{d1tex}-lQyT_b=N3sX6%iF=>xkhwjn#Qgoi4AxX0Ih zdw(i7FK}Axd>-R{XRTzg`#{#?p{19fn<^>;tp;*N8#mi>Hxbl>w5h(63};4@DA|m< z>ocB#PYr^?#j#^6$9|&sufbAOD2CqrFZ`_oGv%`>Koa9Irp;Xy|HCTM@Ecg z2+TjNWCqO$O&xC@O@#a4&=02_3?wAdg?)F6kn@i8@t_sLO-zK@j~3TYrUYlr)h&3c z(|<*Yv`F0wFn zbbN(mku(0*_rH|~0TDs+IRdY@e7)5$_cKpwN}&EJ3BS7iMGfrZR?GCSGTg%~rrg9CHHt;MdT1OYU_X9pq-@7#Xs{;@ae3IJsG7`plUrmDos z*WVxJ=TsvCq^~+%Nz6fhZzwMp5f~p+m9HHP-Su1ZFC-~Z((|mhydcWz*u5%dDGJgd z39)Pv7}4f;EBf%#51jHud|)>b_Lq%UFZRvwXA6; z{u8~{R9XcE>K%fG3BybZ$gK*$Lx5bjxdNNn`#f0li}=y{MPo5fFHo&B!R-`oU<&W5 z^*ye3sIpr`&ua@Luy$(*T_4ZVIZ9OiVC>2juh-MY=Malq`*x!VCnYzn0wN!YwHYQ` z*_m;E=Xh4R*`=^3_eD)CP^|CjZ9cfDbeZZ_>|jP@YAcFiO*?PS$LoUo zr)R$!$dw}HWc1Nn;#x>vCHs!{jToCGue6-l=8SrRx_E8SQFY#@b9y}zAlRYp0tVHh zzq4;|*-^*-W2o-sDcxv*{8CeNV-(-@(s^TiVB#KZ@Sj|1u#Gh=B!#d_sn0<{vV2L6 z#q;n9fQ2!mjzAc9aTzzTPfeZFo%q}(yD8!J*xx+#YoC2jw_iXXGEH@J>2fVs@9C*D z3yP^3QxECy-|yneM0l`{idbYtCWV_(t^6;+I&SVkz5wv4!)vgn~Y(f?!rCm}E z*neBMLU1Ggr9yTnrMEo+#rce9wfC&ZT_lYbwawFWO+1#%$2CfXQr6F-lCpdcVM1D~ z9WBka2Wi~osjOjmK}*SgV2JOq2ab7PrfHWz`mv>*F8Ycs;8m>OYs;JLkIDn}Vjn-a zUhwqCS;s5gX`V_kn|n}dXu(S?#D8#7`!8F%RUNtPVvh)E_%EAcOE~Ch`;Kv;D!l$J zrJuZ_A9w!W_=n~e|B4N^)~fbM)wXX$Xln`N+tz2ukZxZbD!a{UxNDDYEfFMWtGj%y z#wdT$YBh9Y2}WoXv4UHm9bt_T1Ek6}KCIt=Hm@MF`U-@XF`VNO#F63Wd3iEG(bXTu z_s)whIRhoN{E-C6Dh&nOwjM7wA?IIBqo;a)q?+idhwJ-!;4oF;e}|6;k^)h;ygCp9 zuAR@w=i-f9xg=}F%N;*4PAkhkyuwNqCnnp?%wXD}Zm;h&8g^~4e6cKb7=d05hnf0k zr;sqmtnbZU^D*xW)5iMx*8U2u`_hpySK_|+T!1=Q>_=J7Nhw4+Db3b`ugX-p&4^AU z*4F9y+v384?NP?7_Du!E3#)j)ZEJ`Yi9%(Dk5~SfPe3}ZcJFp13E06Z(jTR#rzI*X zo3xeAKIDzL>mrK25ign?7nqj&b{ONF16lTAj_l(7gL}aSwXBOb0UD-v{N+h_bsooZ zPRi+v!$&DW zg09Q=T)P7$4g|+Yl7nMG#7TNvzRyMSr6$Uq{ntO@+V}-T3nVjf`o-@fh?P1?9e|%* z<{$UKa@p{e#S+}I)96aCtt$UF&kPJLVL+6rF*>%~0$D?5+5F@tulC|^sD1vn-Fsi8 z*xXt5!W*9bG32a3xa1j{U=dW2l^{V~!nHiq$CZ(wt*0(%Ize^vC%B&EyG59-!h?9m zp9q?An9yv$x286IYzj2~%7GF&>psi<8eFZ09X=64m#8yGmSLafqAqVGmJhqcT_4^i}e z*pQ%zCUA)#JEksKi|+skp&KWACG5;cL4Vl<=Sdjxl`QKys`5<0Y-39QtmV)IwYJ0i z4yq#ljcN(oU0GeCl|T16v(v+AD3zx~s20i^{0Be0%vz0l5QuY8=?W9ai{g1}?zE@U&eoR+UFf!RXB+%=#xHX>7HsYNyeWGe5VEH za1uO^1i9k&_sjTL?p+qx^4)NtZBEJ@i8>sl?nCe!kwm5p_bPw}yKpa2-+p|$`rPR) z9e&CG6gT66)|Z(Ha|gD~bb!>Zs8=bIA2O;BDZ*xxsllU=F)U+HMXodl!3;%XLTQH! z2T*uAG9`ew)qV4O=px`L8{vJ($|ZK4_Z)97a%eK`_ygC+hGDWM!&HCrLFp=b4!=kr zZ?)>mNV{uo65HepuTDhLPC{3z8u8nh&tZ@Li$k5-di3O8u%79_o5v!SZH%tWyD@0o zjN1p)W}zm0ZPWgGjgqB7+@j76J>1G-|0aB#DHx149aLS0>$G>RcUpbvXnan|@!$Ox zR0as9agFLA_=hucNyDGIbeqakE&f|`m@t^fr6^x)_4;mq0j?ZMmOlOL)|7d6^V+HD z1Raa3`LgV>Z59<4lSCx*VNWq~mQ@0wofwQ@!&G^v>ZJ|LrM@h2B2kel$&8##sGf+o zP)4@K1kGRu^anS^ua_qp1 z&FB5jSgECvln(tdGZ4yc_K+qYDhCN}k&m9b`k?U1_t%1-J$@h5nZ~w&*OesVtt}=^ zrxU5YyVSuJ7sERX*4kN9KmQnp=+6MePfxzW){Q)(lzvkf-IKMF-|1^epfD9>#f-Wi zYzdCxJnLIHXR2k|7!!KgufP2PDp7~Ln(A&^VCsQ#6=mknl}&aj>DJMNv_;J{WH+x_ zdEv#a`{b}|@2c)!)y}-G)+UYt%k(+vTr%ZItpJjgp7-vhJDdY`$X`3Rbss3y`s{(lsmhhLHn+lH&Bp0b_gwyfOX z$h~c9qKO+79GMdl&As)h)N&^ZxXpnJksRPaG}A0+InYGK9B58*Vb1sC`xk&8_jRAw zc^!uznpTv}_LNrtP6sg{FAV|yQ8EjM3aIB0*xSyLt`{@0Xc--KHU8o9ik>8QD`~%B z-?u=Np;coqu^Q;+tSr->KK?j`o9xyy%>oRfrwh!h{1ZLbK@t3E`J3S zgog^ee|kW37hdaD>BCF)3l9BN~Wv@y|=)b%fr-y z-AJw9OM834z2V>B)$LChH_doelAaYv&CiS)jI8>;PvFuT6B;;Gm*MoCLK_#~J(jPg zl510+II+6Nm-mS|^igVk+guG)>vbV^QG{yqDJ`w1{Ipo&yVvXQnSS=4&OiFd@7YyF z<7Gu^`H=UOiET{WM5?_;)Mn3e@BYwqdhklIUBl{P05=|Zmw7GgTe>r-LlHf$>@0Be zM#SIKKowUF$(#4HbiCM{5BM#z9&9wT{k*(UrMh7W5hafZTp^5CreC%-m9lv^pT5xa z;#u*V40`VR<+~b>ZmdG*%L^g$_CY%h1$x9?s%9oYp>$1R9tR|-SdZ)W2XHtrJMo8= zob$R6U7l+6fq8_;Us9l3+S2{&x))MUsuEOwH2fD*Fq6`TUnXlJtJxvzD!e}qohp|v z%>&zhdMyO&8$|maxDT=fXWq>*MEndDx|l6`Z$FD4bPxCai5?Ff(BwT92dL)~#oI@a zP!)w6qC!1*UB1M~WMMntY-J^n=ku92CG$tQc(alk6{G;-kN?t@_-1U9{bVX94DMf} z7J2f9=@(TRQ~Ug3pLtb{q}O#}!JE(Cz}*yIi(do1#Klc|IZi9B4%yiK>-_37EnxP~ zc22gjp^EH!qk1E~1lA(#x=sqBp|A_K`I2`Bbc-`xhb(M_c`sZ!pTSgdu!+|j?zF)7 zui5@}pe0V2lz!E|s>karyp{O?5Z%jv6T{hO0#Jd;1SN+jeK8}c2k2}aa42M41K$mtxb4V77_-l8h& zw~%S43lrr2VxE8ZPbaNw7C!K{wz4VD8k`s>%9v;Sy(ts2Kx92*M@rf^HD`m07StLI zTrV9=Vbkwn#%Wd$QuIPOn$Bn4HQXV+IzPTd1`2KxVRQ|DKmDulr9b5>)oH=wT4D`U zqMV+fKh8Yn<;Twh{%C`NT%BCEdDg&oBtZ_}uXg>1vXtx&d`BF8*zHvqXN5WY9^Hsi zxRyV8YC*_f#^;>-X6YnLIN>%oA)KcYVq-JXq0gnpa%997?YZqi2=*lEaGfVe*D>w0 zD}UdFZXXlXlWYgkdsANLc`ZEakJJ{sJ!QxZ@IR@=yF#>FTpFHp}WQN*pN}G}(Zb z-ZGMxi&p=#d7!*j387XqF!33#HqW*MZi)0&j8tU3sBo%_5WAsEG*7BcWmnd_vBZV- z0Qvofml!G%ABa_{zY~uy1^;c3Y-iEc*yN)mA_=5YLTkMl;yBCqUmD@VlZ}7zoOo6< zs;)}n^m9+{5VbFi3#3-}bEQZ{C*%zXn*#iEpD*v?#Opt{uW?2P*0FUcd^;I_ICg@SfGhWe?*4%hAGceitKD>8LYB-h6^2jFaWcaNpbd)tKJ>_Zf!wvQQ zIyKaN-4*Tpk304{Swr#pVeetD*vc@t#Tne+{utyyZxEXrGVB>d298+(IdFcn4>+rC zf5Yf2Vve7R#c$;1&@|Ndl-|=?lx8Myt#SRXoW#2*=it$bakhJFnpo1)Oy={xn%n<7 z0WBX?Hf0*{cZ6E`tzMCR`bhUHy&@d+umQLOXARoh`ykU7cBE!~U(U0WA(qTrdgcmd zwZY>^$#5W0D}bV|bJvJ+qH8-#;qaq1N5<~hlQ1iqpcQ-^)|YnuZ!@yC3zhjAl0Lcw(T+}nBN1NmT{?@g01mt>w_`&Py31` z!kc(5LXb%z7j)GBxV=2(#C;5^&Z6g?SCq7U`s!f)ksr?{psn)fU_{n-JOcfBNNUVa%+ z&{76Sk(HJ0L$A`Ba+>->d^)BtZ)2!b>Yv^vHMOhy4Gp{(+~o=*g*!{gnwUyg#1doh z4jH~+^vc=qAYbpy)1198c0{Sz3q8rtG?~5&=s>xEy{0MK^_`ZSA^B>8Mx=xU+1VPS zEKxeU{W?|b`Ec`mN`#I($6a(2H?iQizi0GCn^!#2Eou4bmA7c=27pBd;d~0L5do)+>jQcw z4;i&7oGa#ymkS-V;wx}7%hyaik4RWHOj;|vIS{U+GktBt{Xgd+FvY+4F@A}xd82{* zyKgzdSQY!yWh)z)G3;H~FrwQp9cTUOV%^6lDaI*r;vS*AcSCX?;|!yEVxbv0%{ox8 zHC%tm(5x0|2pyMnd2=PtvSKG14)D*vJ8%zY{28YeVTjW!(`|;!^-LIbR!52at+;J( zVm2tAcI~h$d$hx}kt1ygmE&gG`kmK);9`G9`a%{upVvD2Lr*Ox}F36C{N8P+Cc1H$s&U@isydUo?!GcK(`yWbo)c(kuspBqegw zlW;r&lBG3a_v7X37G6mAUK>C!YWi9fh$iLOUuUL+R3?h1pkG1A7p{;iJii*xZ;Ycx zWkUfxeNOWUv%m`wG%ZUT)ibarinOm$c`D>teLZ<(YEHwCVNz5}EkF(}^#`N~4tDA* zSVm^QJ#1MBgCmOxQ|v)FWrXV0@Q9&-8(yCoj2VP6JT;RUsW)=A1o@GxapEoaCYykQ zpi_M(;J0M1J1_w?G18V8fuTa_1C1Br<~L?1qSlIrk&oRg+wA-D_zji3YcY2ROp66& zySf80m__@^ynWFz;<3ozVBowZ#w%m$@c7l&2g>M8~Jp(4>G$M3a{l&F3d->s-d2-Ph_+#g!OV|Fm*RSQuFTrDc#Z zSS@gp|70%~%DGr~0>`RpDs|9^d?mc1mlvZ3XhG59qhrn3ipOU(aMGV^6T>%+TuXdH z2yHXNoFw>fhxw*xbE{1NUh;j1PuM6{#3iFb3a_^RWpeAuti0;X&97N+(U!;_Tecik zeI(`9jY6RKL4&z8a7v5RJRHcW)xoCd)wU2GOQ~)MQeQj1i<`6R-X#R`c)Lahkav>} z4vn=pKZ}ieRf}%rei6pdLo)-lo~1c7rQGh|(oTy@&JN98t2P!US)smSHxmOqW z9-Vf}0IC-{#vNZqw>cS`k+n#*JC#d>djxK_#JTl@k*}5oZpH!|z~}Cl;g=0wrH)Oo~MMcHG3y zQt3QY0utFn9l?T;yRALFgaPPyq@Hp27y1NeDq*!UAX~J%ZR}+9s^7Q$ysx>5XR&9f zrB0Cd6a%@;)s}?NkBO&Xz)tgXCO zYJ&P?n}hJgKs`jX7LM9HX>^XHflKj_<6C_WjLRdU_I|2g;GUQQ-S8087q==&Gdbgk z4ccNl@@bARW0?uoNrdUuLat!nR?2OnOq>oc;4I(6M`D;tcrybkbkX;exS zA2u(h;(Va+LT&@|m}MM9ff9Bit4#;+%J-n%5?Eotfg7pi;K|&Yn;qASmSm@l#}|Fy zpJF{y>pFP)-437;2gF{|4}cFRwS}%-M^8;vtrZzP4c~Kwgk8tS`1_#15Qj|&X%|L; zm1ghR7H!`)3cw6|1_hBJLH-V)0Hj+8a;D*5{XYGD0T;wul-5HZFX1Bald9jiv5`1I z+*!||U8fuKL%R-`W^n%$eegGlQtPe)dZb->F9WC6dv13Ozf{zER7L#rOELkudEV}e zSV9DjM5;+)uTHff71v*Ri)E`5p3AHbKd>zIMa7I>?{ZplsPdH7bro?J>M05u^aCPS zWci-@Lo=T}+k=+wsrjZ4%BqIpSqM<;1YKILLbZ_F?y+1W%*)%sC=GcvK|{DAnP+!a zN$3jppL~#Bb=C`N$@QbjcAEGPYc3!6+1h5Z6n$bY5-d2I&C9Ybq|`5btXJ=PcQVCddqeN zYHZtm50APKKAml1Fk5`y&)-l1I)y{DsWcC}4U>*(1*)WZUxzAR*|@Ay>PO=Sg=Mu} z>jvT>G8n1T(SC!{QFB$s)ipxdjrVDpIZi+ytV~VDDxu6Hy4M-YoO@bZN@y;L1M-n} z@CQ5WHa3}W!dF}N+P?fKz8}Z>lB>4s3+*lO)D6Vtbyg11yM2-VLJq4b^sDUQ&z6!| zvY!;aJ&G^q;0;5#0KT1I;d;E9pt(0kt0Yl}z-&UA{}u=3p&$!)`InU<>PMPrxkGO_ zl{jRBdtDF=xjrRrUz_lK0JE$v>+Ykw@aQ8n)XYVA@4`Zo{(H*V#|I?S9I=5BK%_%| zk5UeZq3}0Uw${k1ctUTQLA!Ztum>=?GIbn|WV-CE^%Dcb1^3n7q>$WY=mjat#HmmNN4D~eu1@Sz*rm>0VT-+Y<{KAt9kGlX+pmW?YoGHZ-> z9c9eh12+xJ-RyIUtGM8V0DH6H{`yOjEVUB3?KWySvdGLW+h;0rA@AG7w?7B@cV1UT0wH2@O|4V#G& zB1_UDdq;XFU?_pf%x*?f##fL@9?n}bJuQ{9vPM@x>Yroan-$yUc@UNvXGL|UzcMvD zriOw5`K)vEmpr;96mFBz^|4M2;^XVWAs4F zi%4#mXo;w{Y5F*X;j|rVs~|wB9U+(G9XL`Z@Vd*_KE%u^QHjNXYy3rbiuJ8;IWb`8 z8$BwXqLOq_=t+6FUO@I+>u?ET5e?~4-du1AnwFHS@oVNvV6V5Wkv@hnUcN$nxPM45 zB}PlKT~~w1yi#dPz5eA4&){sU@@$Q4;8XM+GWwXd)phsvz?7hTV`qu?M#ctrrzt9r zC$@}db;&ZZLfzKUm^@Iua6i&$L*a@63g*^|d~_ zUrKh9v=}?FR=bjgtR&Evg5FcRjb2^N1+i_!OjK<$Uui(zAycGEw(K{{JD(BTzJ00E z0a%yC7EOZ?(8q;!w03+(JL4Fybpo-WQ0kdS&e|8Zml-?{!F)A%Xv41?N_> z($bFV(|fvT`!JBaVctQrx~T?8$Upp%>)f|59$d*PUGiz_pZ=TpP~mn%+n=M9q7^`6 zbHY6I+c8wJu4Hh$9(M6fZ;H)qmLdANfEB*B`u<&lZ6t)u$rC?AGTiz89crLf!ltXo z#%=SHrDmn8K~;Jl1WRXSW)FKfO9JJ{M4G+mVXqa%a;W}76)py&dSvbiB_H(bcTeJM z=B1hp*uAP^9=U5hbw1$0LI5hylgCLHWWhE=&?UArZsjiD_i<^%MtNBIs0lnO(Xh<6 z;=@!h_J3#Zl9s|J>r`mdvdz8cq1nNkHTgMk?Tpi@h45}=#Dm8s)AC;ae+?Y&a ztgx&hS_f}gW}ZkJEoNhyK})@rVbcy{O*AvTL2PY>R2Qh);5PxevtYgF#ldJ%?d#1Z z4Qf~^U2JhJ*SZ#t?xbMP0}>*N;{S}k9mD922LnJx zkTJ#nH`cLkYe(E{N*(mY)+ozfC7fg0h-gL*mSEx%;k9B9Nx(!r{H{An;L8>^``OhB z0WylAYLoHmYMLtfsLC=ftSX3SKmklJ@I!d?rjZN0kv^521tiY32k^cu#5`85Ur1}X z)}BQz_#QagHBf->x$_D~b(BqWHYgME$X1z_2eu`Jl4 znVEIO1az&JGPES^I9TuZ{Sp`QN~FVNGlB*r6g;8$5R&iK3H$^_d)#*US1Qd7q{zzr z`E6)*3ftRg`Om!TWTjrvV2rbdI!JkzMTxTC9b1EsWsgeS%iwF`3(8};mf_Z?kwIfq z2exb4$ai-)cX=6GmnGjt$YMIt}>jh@?9R-#Ru%p=ST@gqBZWf1cXgh0}`5){0j} z6yK-SeWmQCwPYS`dz}mH(63y68!AnzC1Bf2imf)wey>GFh8>I@tOr?ki4BoRzv~_M zRO$4Uz)B{m>tV6|gXd@qD+zsrp2*gL+c(aB+svCinaP|ulOrO{WRGZz1NG(njp3Ch zPer3(tPs4*&NIT7vK9_!e z6-)U3j@IyRHVC5@GIz>E-Xm`=iEM%?y1N-sCWS+UH3do%2twXD@Ld}8IJZshWj??+ znvx1I+oK;lK!SHM?3BaNqWbzxx)#hsTdgYwFNSQAbTQ=b zgywNJa^DB&=*q|4Ar|!eqQeUySFIU4@~gwkrpclQweL1Ro#N-Qf|E?Ju(G=0$_eVW z5zME_FegTWDZxT~HtcqE_eO-W$Ya#EQ*Rs>pC4|;7q=!ZZ2z97_hZ3BhxOc_h74E4 zDw7DZA6e9~4n~H?&t=Rs8OKHHR34$#Si3lwmndnNGNTaP)N|l$e zm!{Xlmi{g?i7H%j+ei+Wh5NDA)zgVrUym@KQ6Y#TylB#%1*qw!+CQ7$a1q}TkCI&Q zlIs+|&6RMEp@rZ&mg^`B@~jMElvkQ7Y|<_I_4Ej9z%cN+U~Xx=+R?c2wo$`P0;suD5KMH*^0#%^_~ z+)*sRdSCY>^XKx1I6$*#fT$f+yz=^tP|u3J7kVn&9&C<_+r)RJ-~ zz1TzN4p-b1fg;e1UgPZP;-~&S@FCOWXZlppm&E(VgRq%Q!qoFM(FQp;dgWV@$c2irtk()e6#N0T^;apqn7HrC224kHZ$ zf3i4!=!en+hCxP?XeMXsCz{lgezOy7R&J?t+M&ZsX73h3qNwgOMNhc;YQDSdCcOW8 zXR<=GlO>5=L<)U#)Q>~te7z=xRd=r-i1MxMJDZ2*S07*8M;7{- zL+b1Ka%QH`p~CSIZ>`N;e6R22)S=JxcDiTk%iUNu!}VdQ9IPGE2e}=NA7{|(o%mis zM{(vQ`864(#l6u?<9+&u56s@%Z?9`y;7<%Su=@IQ^*sQQZN!|rv~QBZ*@Kfl2NGovZ*%iTI^YegJrl|74t%M~6GZ&Hkg zYl}%++9ZEAzGgod3)lWR`cnJ5Ox$fReyaeV%>BAN6L+p<<~M?X;n=aOYw)yV4s`&X z7*m7=ma-S^qgye}`_lt#nXjdufAnvYY0ojj)odGaIh@~Bip<2jDZ#qKKHDsEUS-cz zpyHls+4r^Q^f0Lols#|dW-BECRR05RgJ-twRx%DPUGg(e+mvNGr`CYte8d*d;WE62Z0P1VYi}yP!8{* zduq8r5i?~)a3^6}^{XwBKJGw*wPI~sF-<3oexwXRca&0epXea6feHuI4rUG14Rx-=cwW`$rO*2v#iX8J!=5~+IauD>1n$uNJ7uj>e}Yme0|2t zLW^tChG6dWmYn&LF{AATpKdkpY{#i8dOlj}(&a*&=%bd#CUS!U_g}l!e1*>nlOldj z=M-P};qUq8I$Nm@w-9{-H+!ae@d2zNqoxwb-THZIo@I45RRgxcdB-7k+xnZuu{-0x zQHM+Kh+Pun6XvIVyYtg7t;Ugg_L6YYqiTUzSBIBOzlAroH0h44JzLPqSYYJ7s^`+k zLckONg|2^X+-6B$W>LqcgznjOq+KG>a$`?r7Bx1X%rwqAY5D$RwoSsB&AEv36Q~FM z{zF8te0GjGnpBsJYOP#{*0*wi9_G`GE_GWCL%-4)_v+Ma)nRXY73!{iN%aq!W>WgM z7}y7)UN6gxRE+q)Vpc9rfZ8=#IsJdF7kj*Gw1*){9MmRbZ=1BgvgIsW{5S$r(Eae};N zI}Y$mGjTd*9Sj3p~(5YW#!8OwY3i~FZtv%2k znrx)(=#i{HqebBl(Qd;6YT{f$pBi5lblpxq!=N4|VKg&?!i}t38)_tG+4_LtnQA02 zzu0RoH+PxV^;2=OdivM=&EHV0nvk-qBQIFRQ~vU;KlTpwwXWQt;#Bnf>zM$%A>ZX8 zq`jlPUD&vmYGmpk?5~}?JZc^>zD1{arTc=;F|lH0H#>Kn<}J>f$oF=xG3&{GU|ilm z4}Asfv-6mJN~`fs;;nDazQXLxX%ZOb{^eyX&!VCtmMdwkwR*H^2GS|6i7J$SBujMr zfNG0EI?7H==jhc2*K9M1Z?*YKOS)a7%YSnO*Z+J)x@$g$3)KN`yq2%aoPU*h?wPrK z4r#g*(s$>N;MADAD{hDT7h=7;b>ANLkYW?&9di8!aR#QPPtIQ@A z19BTCB)Q6S&l_=86=i)WtGy&CA^Gd>XIWjH|E=T{g2U)0_D~b+Hn2ApO_g0QWooJ4 z55+5E;seb7`rt6cUL!RiL~}aM{k0MMg%!_uKCCdv`*q`~=PL@_uLiCC6*uyd{Gs6h zxJEaqymWo9Kxu1qKgg(*75W(Gu<<&>YTE?;PzqO>+KI(Gh7D=6@f*?Y^o#GI-vg01 zzf811?%5gpcy86c%J^^K)4k-EE><$_2D1O@Im6kuM(qkSPnV7UpjOYeilIE| zjlYp5a)fsj!;&U*zj<@GJj)i(o*r7%ED$`rt~}PjYYY#Q6&9M^Ro(oIQd0g`9De@m z_4>hgy})jfyn61Yy@4blh7}6ox$c6AeD77 z9G}Zh-SR%n==VO|A~m((%9k|)VZ9I`l3&w(;=oRS#(e7nzs-gBE24)r9DXwL++|Bk zBaL31x_;o@=63&f<&q&|IXBXQv@GezknLp+3$&r9IXQW=fv=aHV`e_cA!()$Q*^j{ zyBtI|xW&li4Ft7wOJGzQjy}g;xHYZ%_M($cEk@C|a}cZof5e63s_@sN-dsm# zQ-+rJ%WfhT{+3Q@t4Kcb-mYBgmms*IYK=>_!)%xLh+<*=)9)fwaL}mr3qh~_xk*KL zosoYOO77tHL{s$Iw=U*D~2EE}#chM3yr7qC%g-C`-LX8Kf3Soc{aKuvD}-aX+%euZs6QXbbl))ei|kc`Lem zyt9)yb>}_)qTp7AX;O|UuHPj4M@(V=nMfH@P&lw#_n2LV^c;n>6p9fwzKoAxFy71E zj@EFsq2+!fvZ^LH)>Fb@%_#$e(tkZ57Q_fv>$x6~y5*X|(=A~33ApHPXEImf_H6wQoT=B1}QkQTxphUS*X?> z3cf~@MUKH#55kESLpZ37#v{wgMJY+~KGr#~^#Xh)eCfszWoVb~0Gc(4#aJ_L54Y6? zCU(sHeyq}(Bitpcc+>n+3f9j^%v}6aWAxO&&F{W?1osMZE>Alb>_iN!<&w2kC45fy zO#Z=`6`I#0dsP`ZYmuTC<#0PrKS9S+*Vgi7I)!6L`Qq8+Ft|LGA|Wz< zi!xf@xI-KJ|uBE_2 zpX%QqFZjIs?^lPm4KO8qcEyE?vJZlPzz7A?WQI;*1fQf)sX6r6yQ9p1Cckcp`B(Pd z9V>qOXZRe`M4{6yb5h^u+z1DGXBZ%5SUIY0r(}4esI|i=8?dnm|F#%mn{1lTs~)EO z2W)6ocn*qZX8W+ zm))~wm88qXyC3RC<8dKfK2u--%QBBR0h6^0)PA_3z_G7-G|&0e--{2v5K?8%0GVnu zH*|a&(k?W8#`oQ0-s(;7mWYo-aOeBaPE8-t(j(=SUm8bZ0&45?*xoE-xv|Zx=e60$ zV|LDNPK>#^pP%Qx!`SQOy*$joZmoVq{{56;kiA+b-(yIkBYC8Lf-1Ls6MgV8RUMFb-DHqh zn8f8U`qO6o{f!um0WKFtq$|2$PoE!B@$2J}q7lzq{wm}X(m(I5WQ^ng!syph<*@Yu zF}{rnsANf7HE9h%3RuqC?t6CsZ`$ha9@M(UWafTs8;`QVp^h56?9KC!k(lAXAla&C z?+4Nz%91uh92k$4eKX*kwn_%2I2 z8mrWxNKPhChVp28TgCfZme0~`{YT;?^L^X4aJ+eQkt(G6(bYZt?ETfTSuGLXrOlAD zb39+)wg{)ea0e@9Q-87cs3%RXXd;yRtM0KD}*9Jp%aX44&$D-SJVPzgvi^={5Q-xT%cOxAP!^IQduXAa`_pOrbA z#ALdoAXdqIfdK;Ib@nm>qHZ24Z=3J!}%ls*v8XqNj8<2WF2LS4P|w7rdI2HV7IPsuoq{N^Qs!A zFR#gK-z2~V^VX{I#dh#xvHW4{kEy-3@}h4oP&V~*WscpVu- z@_T^!wXF!-d!T}H;IZCLThwI8rEFpYk#}d7&JCwd&G+(NN*%^u}6d48k-Lbr@lDUU!zY!JT z8Iijfc1Y0G{4^M=W})7`CEXp)HH-&)n|rJJ2h8!fGoTYj6phH6V4i*2;^Tjy3dsd( zni;O+`f4=t_8^S4z;3(fnQfA|wsT`h`{9T0p*%?zOVi%z7O+SYGgE>@fimXft5VIz z08&#U_k;Eis34Ev+DM3Pfq5Ug+_Ga<@=QN}7xD}BSY^Ee5w2DTD^1P(;<~fBWPwZk z`~q&C!2}b)23w|y*bQzl%NPcsk3p3#k8X9+36nwer*8sv9PRjCbJbH7*A|*c7*PH- zp4A}VNxd7~p`OVR6RV=ZvCKw;g28P11unxlo|)lX*-xE6V*=qTcsg z@vb(S!3Auzihe-T(rBJUu{eURW5)(dJFxKXNP=GZZ*H+Dg+Z0*b9P^m{(c<36wdzB zpH6M+Xc#6dGSpS9asZan+UG%(K7(HknVTuiV;`9gzaiUfyj^#{z42 z_v(#3^RZY5$&)!wGh9zEJ-)`pSWjM5-~2sGBlhKI;|S~@OUi37gT@?_;d;zS;GT-K zb6p$~Ku6%++%XR|)Vj$6E>Du_4o`l~|EGNxn) z4!-XBgH$qUF$`Y#89@}#^us$`>nQ)yJyfv1f|tD?>>fz8TCA>m`_+fzn>^!Dv!XVZ ze~VO4i=7KGG4WXEU~Rtz{5`!}W&>i3*Z(eVu*a&dNBmwFJmdPw*HI*Eqty9z#_|B> z>?LfE6^7fflf6?RJz%*au~ZH=OZ_~V<6~c-)_|UG@>XbUoL#moU3z*QxantDZ=a{e za*+N#-{gfM>uT2=l^;4q`o`C%UZ$U`cIPabhJL|%PfR`_S+%&ksxJ1SFIFx*>#DxG z(8k{$DOu_4YhL*Fo8oWNWVm@q)|{lKiu7YzAOUXfI>ple@$$W51r zGHzNJZj7ASHItn6MG=EA^$0=CKjQ5#e04{&^Cf)qp52#Wm+inC!MG>efo1BBLF>q; z9aSWB{!QFzRM^x;_2Zu(qV{SYa&9v%JB&h}x+ zvzodz=3}wDaF;33sEtnJ+u{!QtN~-#zZ3Z{DuydJ4{J-@0YQaE7gkF$@+%cXvPYw! zFrw~4W9#W}0H>0V>0N^r=h~tIkE&eQEpr}ALRtYF+`qR2BMPvBrMnE-NW?moi#8g; z*7iyD*H0ew+7{JedQFq`q&MeLQgYSSO!TbR)dKwU#^aEL8_QqMzKw-jnCJ82yd9lY zNyQKZ<8AJ*vJHu*R{q0ShbysK6Fiz2EHJ~VH!?UzbrZxn_#Bje{?~s_GIceOpwuya zElOy@_HmUa$aU7_?JibxA;;&^=NoF-`RUOYt@{xdncR_&!TEpbXW?=L*;z=KjR-f7 z=k~|Ywu=j)L88}eEy^tZd6(jZ8F{NxVYO&^FC?~~YJxBbbA)VyL(-lMf3m@b`+2odspF0{gt{ZzmuX7<(9B-U8>!# z?^VAfihmTyf^CPH*-9ZF? z98uaCe9tAr{*8wJ!h>2#jGc(=lmEQlr*J*~Pr!(%;efvHmYo4(XcIgss@X;``WU)p zz2pT>4ng1!TF;rHk4{954muofK}PTWMs+EE0!dq@sCN{|oZA)2QM~qka?#5pCe9gr zHQC4rGX-6nZ`mj8P}%i%VvAZ@)z*mJu*R~j;owD|z#gr{!1!@7myU}(HZy4@kZ+dB zS#cKp7f;OJ@xH3soqM-HXRLo}U)H*qqfQA~#|6zHNzK8jzlpmnT-AW*`VrFT9m&Yx zu#JJt=PdIzUfMhscEzPgU%rwtU>OUbuyy<)__x7So6>DKHHg#zVTEh?;09}r^{Q>P z@V2rlOUs9cX=g@pPC6C(qucWlCq-cn4o^z8i=Ni1nE!Qig{1pmS8{QgvF0}~TrDjY z2Yx9TZjL0BrH|j&`k7)lXjfBgVGqgH38YSt@^cV0>cvdC7PXLKuyN>oni%>VY6WuH z?G8H0SU#i5$yA$cdqKbDO;5sRMWZ=UoJ&PkZoZ^G%wy_EmP*U3&Z@15H)52oqQaEf z%bd&S=yuY;fGn_&{O(~i;46Q}URn-H$Y;G2WfU(!H}4aG=s%x(eeWUBLAvRorZ;>$ ztdYYFXN;oO+_3u2fR+l1*qUqbSTwW2y=W>D57od5Uiu5VsEJp2Zf{Wc$XxM3AYD#0 z#bhp2Q#k3h-)D5hsbuDPls9+{2#Yiy9?LP&`!O2l%n!22ziF8`NROyEzI?#)u_UMd zZ}cek&qy0L-Q+50cLj!-27rLYN7=*K{5V0v z{XlAHbi{=g2#o6GX%+vg zzpKRbLl$lOVz|Sk-tlttB5M_|Q~Xa%h-vtlMobL76}uw^G~UAZS|L1X^($NS4JvbR zC(E`YOQkeBVw9p>Poa)Ib(>RyqqxtQBPfOSE4S>1pD;at`((;5ejs4Idt!lR zQ%gTky6J>UMcS-T=y?5C!1 z@fjtP;{_J;=00`#rWK5Q!`*~Y0oP4lAr2g=Oh>U5sAP(D5V8$Jwhw?fhN2bq%fWv| zh0_2|e2CY=t6gg~<>qF>i}EO=e~Frv6>{EsJS_ET5@}G*QLb5cp}yn;1M5F*6_CC* z7L8g$r~%fOgi*4MK~h|4EuTzl&T#2#Jw9a^#XM;)klM2t4(1IMm9yz?U;!zla6~bQ zjToj53k3(Yrs@-r*Nr1s#nHM+@4LR7+|0XhM%V7uy=N{w7w*Z%N4WuSJ)S>ouSLm` z0FXqKUlh9;?js)bzY}K{SW&hCW_eARcrE3%+Cf?6X=L77V^!MTrl#J(m(J(^)S%H; zr!F;dHC|f9z3js!zBU}Y84N&+O36MS8V0AVk;bg6o7bpwb>%~b%WAZ#X#%Z2r7ajh zCO%QUt8lf0NA5&V3`5_BVz^QH-(ug(LG+b$N*0%$=Yb$vl#8O=ytm_cerehvmu%Og zP0N^|1TWQ^unvgJQw7Q?X=Rv$xlW=a8gTNc@8Xl0pYst#DqWA_1ds#^*yZ%py9ucv z=#cN|_u$~^Ck4eV!XEY%BlpV9F}=#Vwt%(PIQy|21X8C{b;$0!Fn9MQLhHBj`H*(K zD*+pyhFdEC(Ne7A6|1adRU2*t0alVXre(Z~vT0@0uxj?S5zM#JFJ?|i^1Jx01~O59 zmDFZUV~q0N8`2XxH%VEWzVI$9_1lFSt&(TNKJaXx0pmdr>B-9-Icf=k$J^6y)C{co zrL}f!4>`CwkvT3+Le_^b5$DDRrc_+j>Ol{t1>d@yFTo1UJWo;U>r9J#&LbqEa58SH zs{vE8-)ZYYaNHgTuH2?Y)Z6;=T53ci6MYr|zaf=Xaa5Vd3thi$sd#`QX-KFyrC zK+P2M;zt>{0tH^+{7LVe7I-TOCa9v9W=Wy5vuo~1)jgm?Z>0GT8ofZqa@Y{$(Xa_V zh{$WGLlz}2RatfDFMZT&Pb-movG=m?vVDn+YCSIK+YKIZ+n=2Smdub|)Su)vWKllW6r6kX&%Fi%Jp?4*@Z59eh%%MVQ3 zP!LGD1F_sqCI%x^(Ja{)gaBfR6=ghg7(wrXjA}OYv2^V@Dw}XGBDEg56~VRP&er;K zHmM~f>dxP+2uETFnv|)+FWI=!_Rcub7$I2mdim7%bEyh=sFA?q#Xqtl~roClF zhL~w~Jj(tV;UEqng2}Kq6%GNOoBd@u;EavHg{ca?0~;`Z$Z(lRQ0|SWqI_KvG^M^K zaLMHSQcs?`6{^ewuP?{@*CM#K-kuIposW7Gar@71r@hhVXu9BORY7|~?t8kON`4Jn zT5x8pw;}ZQHCD4Yw<^Y@rpEQB;G^Zrwn1&4xLJN?fUkg_o<0v^z^ZU31q=suGjMzX zJHMMf=+y81e{=42JLsAYR2W@{UBzieZ0nR*I@Hcr@SKfYtIpAaF4ArZ)ooz%JfClX z35ZAqE~b&s3B|81>s-n)kFe)mF1EArTU)4N&*MQ1Z9tcJZh(L}M)LBPf3F2`-48}xxM55=#eC%aJ>{Xq zPjcIt0u6IZZa>6W67Ny=))L;>MDKqmULiMi-Oz&XgS|I`8&syUBX@kF@~g+}2#7#9 zmye9p6aA2P04rDFMu26{NmD%P9>2!(uG%nTc%;n#PW%C~gBmKQ3H>7)j+MjUBaS86 zC}N^b#rM=bOKblY=>eLR*K6xbn+hTT^3b(nlU&!Q{XdEtNG&;S)9!==kI}KeG?t4!evx+46^JgqpC0{@+E zgj=u5!!%FwDLb~T?;81RwWj>nkAPX#zIWjjYnwS%~;c-|L?>CX}YwRoce_HxHwMl)T;v z1Paw`kK?U9Q}|b@^w9JuE&@46c{@xXeIM`II_7gr4lKWS_5f%!>|K9F7`|~kdsLZ9 zJG@!tiP`t;8NgQ@q4eUcPXZY;7Z=(t`Bv-Y3V(IpuWjjB$;!pd1?AtfH5vCtaBqo_ zJi)(}fvy|{C^h4Hq4Y?BsQCxnWm|?~hG6{i?lsRaD{5{!>|-_%=HUnq*CV5*m<5q7 z*h2QRvel%at}$Vq?sK;O{6uK0tM!%MxLDs+QrzVFo5O>prNs|NeX)I`pBLp-Kf8z8pXbkA0S5l5`vf8E*y+OG_TR4fKZ?%%k;(r5 zaGBIi?g3YkpC95#pB*{nG;%;Bz3ITl9DA%_{8k;5#9QI46@ZH#P8 zj^%tFLicyy|KR%JI=x@7=kxJ!_u={os(dI2yq@4^Q)I+bHLKw8#@Z&qA?|VemT_D{ zo7}$~e?30x{`zJAUpCQtqwSsnxBCh35EtkqlzB+*i5h->au0VHwK2zbDCKow9!722 zjxN0|o&Ev~eIYk(U37%&X6U38z1B&)YBZGQV|Gc&04qTETm1&^GR4@vaENk$Ol zvbLW-waW1Md19C`2cNBf_UNd9E6k|!iu7Y!$D}U1ZsTg1hKk-ijle9Acx=DCUDm?( z<#*th`SYird*>ZBds=sD);!v4GlZX2Eut?qCheklEB~OH?moH)-@REgbjA(+J0^$J zg_E;1>CNr_v`kryiG_oAYWLJSdbu*#A)!k|d6GZOW^@XLA|F(>Hix|Z$&!7cZ_+p_ znGP-7*a$d$57~UoKR!F|Dj8QGnCZ9BMeEnUS?qYxlQm_XKYPh0KDHJFS_-jCasEa4 z*>>>5t2hKFl3|lSE!1(JJ1f7zuDuhvkg@-5&L9pS@#=AX3+k4Q@kqnb-%V~IH*rrX zsW-jnpoWGmG|bnXa}^wytW8j||4DTjBO?V|v$a}pmHlH_?84ST_ietJ zLsYMr*fy=Vq>7A$WL^9FiM9y5Taw{8CEnWP5C-pm{jS_`y*!2a#2WG!2gvm5YoL?O z>lzD@?XhjgXgbUm=XzP#Dr4{=rm)l+Pnn1{hxRqb_y;8SZH2-P*kDG{OP^fA03v)&aQvGeDrZ!x(6pfn(Mz<+|-_J2!yGGSzr<^S}OKh z6lal@7NN4&4eQKXPQ`3*Jan2Y=e^L`uxxheO^4g}RRdr^{9=KLux-}LIN@UR+XRt} zkfr2>z&4^Ga4M5GZAj8UMMWs-JyO`ZAFo?c<2mMDg6mVrv_J<)(V~J$9)p_Bt{hv# zb&Wx&%~pC>@;dLamO}srNZ(^BGGhQ8CswD$Y~{C(Gmo;bq#|P~Uvqk|M$Vs}rYX(^ zP0Cz;6ZpQl$v?*C8do%p&F?lSXiCU2d~9jVZStT!YG*NHd|7hE!@$nOKt@zF3q%m9 z>|7N>%Ao`;RlW4yWvzr9J4CmCDQt{u(nmy0XPl`8?TSRVmM0-edJOu=8C z9r#~NO%X!r&#<5ilg|!JSKc1gz6v{3$XCtx3S9IO@7lG03a^m2vmn(uadZ8I`{yUkVA2#i z`yYf#==5U_`vnq#1Z8%;n|ff#Ixx9AX?5Oi>8>)O?2hV&f#KWH-oF)lSJf-crqde> z#30Gj_6hP-(4s|XmIjpUu~R5JFl$;$v{Ff*h@765?+9g#j}k3Mk;v6y!9fZ=cl5;L zFyQ;NzHuFG&p}g_w=uoyw5*rVkDp>{4zPFn^nK@!9q5vY7Fo#bjpenupj*ue+3Inq z!nn8Jw+;=sMM76RW?yy7H-|~bdXHkvf6|#jnSORID?u!$NP&RVE@12GSlVso2;0vZ zcTqoDD@QrA9s(I2sb89T-KMZbaDJ`2cdBUC+IVch&U4OpxjDKp8r#cVC9Zk7<#Akt zf6k!NvJW1ZykuZ6X4B3+>-~_ei%i)^ZOOt`-lWskHn!+QyTUHqkLi(gFNa&9!+#9bnC3Y$W%>^Xz?du zp-*xh+DAD`CH0+lopZ%p{I)A5woA4vgDo1I)T|VVHL+_?ri?ARBm*o>POBY)$5;<+n;K|Ny zRrs+`va@wjeC^-~eU{QFL^^8A4166X_g__nf&07W`C-(iF27 zM259Qx3P-`*IDNd6=#cXI{4)!6eVtGU{LqGkblx3~1Lp zT;DUV94c+UN7UBi*K$e8^7rc8lSd@SG(XHv%_BFKL+;u&VDamms$ixZ>-u6u4^(T ztn1eHebB4nE*ELlGQaz_&qo!M1wl0Px@Y^3^BewsBasMlnY8TG@y7UGXXv;oCDA0b zU#29Ap~l_Hqu@;z$yCVnL~AZBs>dKm{DkCy+~d{5CJoF>%4NH{y-ciHVyPfmT1 zc_na)=mOMJJOK`>dkK z5*F0ybMnO<|IixorTna;Xfn*jR7QfwTF6w!{Asy!R((>OL$*k{07dp1$u7d=0^jM( z3j)5Wf&6Kqy5{0^X?fx~O?NyhLcy{eOFhcYA19H2p8h~=G=RQ{)Fj7FD^rFO1C$LC z-+48+OlW*q=jBe5XfQsLp(}MzE((9e4T^J4i#a_NuH5)Fn z_TG6hbJBSla*jDV+zmV^DJ?q|pz$x(c8;}PJX}9KTpQi~e8b^Y%gGT~71w@C*texRM8JsU za5wSZla@f)=EnN1@meI-c`mLG`(o0;` zstm5Un}hlCcIIwDEb>k@14ka$i0X?*Z2X-*nCi~k@zBfWGMC1fkBh;OR{pHer`@ou zC}v$fiYVgeU|fQ)Y_bWZVDrW8^X`G-ya8&N$(Es3BZYG3d?B)`NCT$+)mhMBh!lNWfneEDQxD|3X{$gzI9#4CYp{b24Z~z3Zyo{& zV)J_UTsnfYV(^nYlgrpmve_i7SBX5)v7{?QLpov$m5J@NS)1;KB^CNej zquJ;0l6v5S4r?ZWAhhRC3v^#cP>Z=CcyDBx+51|Dn858sQj#03W+B z;v$`?zH6GIUy_}DEE}n68HxzSb+UyJM}eETvnGc@o-_t24G@=}OX%V8ev~k2Qw|z$ zRc6$msfg-_9-Kfm+3(>Y7S}-LVOevfgKf##$C@#rP+fRG(W7({;{4#WwgzrIl3~^q zqebgM5#0EEB~U0uD=BB{Zx<;yy=NxQ+fC0@J>@|zefBk4g*#Z_f!}z&tJs^+9JrsG z4ZpRq)iu+Uum(x_%^1TaRx*azIDRaVRw@Op@v=1BBod`yN||<)L>8)oWs=CUXn_(( zx_D;(jBqbHa6Ie9Bbl_jjb7~u{?#gJcfF`vt8!>a>!7mVvo){2vA(^f6=qGe)FYi1 zICjgMa_Z06iHsV~KQf+iEcBK$oO_`3V)O}=0KUw%ytJ|rx@&Fc`0jh7gia~rL&N9` zM@PwN2@d#hy;j|6e8~#eTG|eU%xJ=yamkUhrSZ0-XmjJb_ItRE2M5(yB0^GNpt*g9>PyXk@bbF%K}-UK&~^M=Pc{%hoKiC1_3JL8iolDW8)X;+qS-&L}BDWrwuI~}r@F_AMwd;`e$ z(&HtqM_4f|_1hzRb2Tz3%ti7kWx##qzcW`!^6meUlm&*qKMrr$L}G`}=IhFr54bhu z-f8Z0{^)FqZF{=&>S=$!t5m0ZB~@pNPuH8_G9E7;xj)WL7Q~hv+1NuTZ+#pn6#+Oz z(RFFSk@zO!EDE)Mm3aPqAk_f0fX!FIWib4 z&jy%s9A;_V9rXu2#eQqh%|j>g5=pqKfxV;3-Fy@WgQF0TRjb{TCN2Ut^-ICO^}YV) zh-toEMbn_J(Q@1$NK>8(EF*_9P*}L$M2;eF3{-S4UmDysa_Gmd*V7=%M$!i}(|O|u zPsSvVP2&5{s-Ha||75dM?r6 zi|w4d#2cdYvOlQB*NM}w55;j=mzAv=*{@AE39x8T6k&v-jG9_5wpdjtqtq1(Rfk=0+AnHKK=*vXMoDDPje2isQqUEm&N^*qf?^*|TAyw{-`I zd_fFRX2Pdz*zM3!r|fJE-b^KvOy4(ocSD-&@G0BiMeWxw4x8N878dfIF~P|2O!Qtm zhBTxLyn0fpY~St`Rz6PQ2ufTS5PhpsSJ%$FzlSiu)W6gyO^r#987!Uoq=@-K*-gt*AZ^P#6E>wz7wCCN zXe8yv8{8pp*GHDi)Zn}-uhp#$$zy3+MA$s(uUl*a^C#!L_(^>CGPOqvSbAP7jst?V zo)HG7%9hxcl*$yI4DO<)^UTA6tj(}z7&P3xV;``J3R3q>|Xg8Gt7|)k%_w4PL zXP}`%-bOC=aFxkFAuTXBAL%|&85D9qyswS2>CIV_GPF7p?-gR60u3hGnkjmUGaHm+ zMS<^nw(1wZC-ulq(fAX;o9Wf7ZcIP;^M68KjL)ZwJwMLJyL@u{=?44MZIi+&p!#Eq zpsNV_@8QA;o#`u&nD|ZVCLwi`IJ48E3A*6P6C|^-} zW<;~I_w+KzO8q@rD<;#SviFoX`;1S3l%l%=c@2F*aGKGNC#@B-DI27^+HYI$eFT2! zP56yCHP%mtIBU`Atd_8-$7BDUc_Q}$YIgR7^lrXw_tyB+i#&MKGqz_Fzud9A@;`ll zW#`-WM4d{&-Bc37DsPp>fZDSI+HKPudNOQ)Wl*NWB#f~>v(UOXA}F4r{Cqd&Ris3& zjG0M#K_UEXL!G#t<;uc6tw$|`}WP|1bt?8O!L~9wmjpWiml3yZOc>-|Agqvj}iH^X}wB{jXaf#@(no_igGi=hW6MZ zeR+#&UQjvHKGSZwdT3~Mk&UQhbbCL?rb-@zOP)+!f?UCy3kM&kGrU;&XKvj1$9rh$ zybW4grj%<7U0Am`>Kx``QOeq6*&PIBK22%{We+KuX2P?f;S*(~$|#wRZG2*F zuLFHQ$pNYTTNYI#%~*${X0S6_RmowZ3aNf0uT#tJD3JRTUC0c-gc=G z2!GB;xXaVqO>Wr_8l?{pvRFISnW^)Drzs8fQG~G7Rl@Fsjn*NgcXvgECZ%qgA*q|w z|JOIghOQJT^JE!_Ie0-fwW5q%0LecnF#Uk8T9m?!53M2zq?zCU^Vaiw3V4d$AZi^H z{PJ&iH3X+YmaMo3>Mgx0S_6b1nF`byd-y!vu$x=p|6=_-A@u9*R=bO$i8t5)P|8uM zJZdBpld*>kCtE82EJ%Al-aQH^?R+@7!3;-}Tyttw#upT@<(l&3hZH$x#dS!Cd_$uc>HwvHm-tQ?BSmC1rhhmVl}Oaknv zaeZMVFPU#=`jV1F(rvXH6Kd0MOCEeYdlU4i_HDT0Tl{Nn^BbW3qAxPDp2w50uw4#O zJdDBCgBD0-DQJ)Po1ow$4K#~L7>QLumL_F&Bd{aT>-|gJLE`m};zC}J%PeB59Wp=u zdHG^WR-DEs+0FeE}eP*-a;F*^eLe|yy8P`n0qWJ zoLyxQi4Q>t5V3jtF|j|_iJQKwBD~T!Is$UHVP5?}?M@%!a zyrct)-~lRmjzBNHv>U}Xd5Wxn+4>2WA;zx$UJQcI^)$;0%>JsIs_sw?=JhZN3mItBq~se?OsHi`6>@~1OxX0~^2W?GV_h}E(MZB3sKCK@ zow^tag_hA!uc2A% zfuNvNL3_qSDBxx-%%4G73;;6;!5fJW1}}vG&3|Tao?91KOEm*Z_|}iwZ_J4uFR9n=UasevI{U>|*56_3Hz`8j?IXKgL#Bi|;XOaJ!9b9(MI}C( zPDy-N{&fXyI)DqH49C(&o)z6&o&GB+;57}#G~Z(7hCZx7l6+p=`$*2;GOZt|K5~aYoLBYjag5M4_j!X$b@k1B7N9&d(30qs1wKq8$zHG= z;=_5)3iTFluHopo-!KXvGSLiheF^QS+#R4<0{HI?PbqQ2v~O9mrKQ~QP+SDe_lzYTNp-EgqRk@n!uYmN)JqP-2@nQ#JLq!9fKyQ ze-q{B(p?T_-x$EbE>?PN1gxOBdT{C?K<{BRmLL{TYNKGflaQY?MNxQV|r^|M{2>fhzqxTA|TWZpy_p-omx=lBJiFi6WueYUEiF8_&rh7zh(ab7QMh_()Ca8O>zwn zpSGwut2_evr1EW5)%yVD_IB6%&=+nX0P#tTmo7>3qTPY3O0|ksTjMyb?KnW}=QaXh z{geP4o2#0V?+8yy6>Lsr!Jv!30naI;d(~mcPk;23jrXIN9(N7G-g*inX z+c}1nMaQ7y5hqhIMG3-1xIS>$P>NQonGqiPdKLZ0^eS^Px8x#?nvU>`H!|zHsXtQ6 z>Pp(fJ@53|J8k{An}MNei0kc8{yXzydj4y54N_5h(-)&#Y1cECRkp41cR~IXBjNaf zbduQ)^_#D~it}$|XD}nIAde@%M*K(|7r)^no;$Mn?knqjk=*%)=e;=@{3AqpW0eAt zBv%+vD=6JQ)+;vI48E4xuBxJtKW#Gj=4DDKfM=;?nXHUy;rvcHP!RYk-VFhf1`yix ze7Evvp@%!?L}FUbvGdi{Yu;aSulacz950cNw$}`wavLB`sz$EYJBuBV2AnV1o3o0V z_dx~Nh4u1AQo*lNLN&a7E7#X9EyJ0bJ@Bu3eDjUZ{?1wHY7j?b$-iv(otcHw{pYL? zvF}Z*it2+>lzl{Uk~t42dL5g0=*c2hl#S(#ZS!YU{+sGJiItc0B`;&ijX9g+Jy%!; zw7E0R#H{R<54ocDBwvF{d;j6LYTz$_ZNF^dRQavL!J|EHm3x6;26N44h6B7Z_Z@#QWB)-*ddo>KPM_CI~!U?`M*W3?9 z*FX_9{V}Rq4aW!Vl(C<#HY*ftzcAXh3G)Ui4HQOYQ zH9+B4y^iR*T_axXOS+fQSnPd`6I5MH0OWEI!U)R!v+s)|=H%!A_909Ox^5vz5< zmdZ*6l=W#MO4KGgeEVh|jb*ak-}TgK@aDy>UL=f3?=)Ba zgMt6)@h#VMeVfDws{Y)!Y@Ki-GuO6OgE~V2<~IgJ<*|z5p+jq=%}CMy!I#bpE_~t8v+_v`XI>iqGTj zHq_=Z^+KOfJ?>A=c-xZ;J~Jh&`WMIN4Sr{Qv`TAIXF*vqGAde;u5|c z1~R1tf*f4^7{!!Foqo;IRsVj0m1>Tu2IowS`lTvdEaW3MMXoP!Hsnr$!gL*4x97fl z1$E~~V2)Dhwr#0M2JR~vF`NNV?nU>TTN(*wUk!11f(lcB3E)Stz`g&#wm!EL#!#DYAeDmu;*SoB-kpQE8zfc&SS|%Qj@yDdD6HjM? zUvb+e#|KRk3Y@(zR$wposrYt}BU?Fm4`YLAY5wgH4s2`P>waRxecs6vokP_?mh84> z;(M=cvu+~tH_sPrTV~)w0WUE9>F%f%Ih|^uEU&UQ2AF|2`xFYn7i$e3cIe7nQ|tb| z)zlV{?>;Ls_H%w#XI4B3d$D5l%e(@>+8Ut?h_`iLTa2Ir%xrg>snyqRBo9=gZYoq1 zUQhw>)Le2Vzp0p)Yh;lZ4&%btFj;{{ys^_Wq*tBJS5>dMtTKj^LcdfV{qia&8)Zq4 z5aSP2WiVKm>SP&0h=*(*VtqaA@^RwmpFK3^43-?hpKBYXhsznm=6a3&(6hl{6VRHk z2&TN*`RJ)1H%)Y~Ratjt>qGwCsUNyYz4z={kOTP2UdX-yWg;CDOZ@JH7O3oui`)}+w>jF4_$0mpkQY^ z<3H|iUoWrS`{`Ce1%T1oc2)FsV{>N(Ae5>S>|BG_pE-=W&^-urz2^JNsv|~^?Qyd| zXx31&i_=ys)m-4D1F_#I)kPXP2ZGOBEc7QAo-2Fbz@CIf$2 z&}^3{I6fc6@=+`>MKsf@tZMoop&Iu3`;?YIX`!TA=sXBAr(hHMy?QUm9y!9Y3vT2@ zPk*Jqx(~n2xa~X^Dq3?pS{5^_@@CG`J>WuTC0v^<5FXA*dHNDwJseW?=(Yt^_V(m8 zM2N2py;?Sj-9^L$_Oo5PKMjluTB_Xc{r+O$D2j^DD(t{7dpPV^LNYQ+m0fi#ICtzAexKHRkbWXoMBMg zild@6k>zIIA?KZ83;j-qFQwgO>3Vy*9lzxnIVpGmDX+i~^r& zAI;6~%r^4WTzPnqGC3gMq%FyF7Lo3X)hK!@>>58q|!>yyGx$!^Y4*vcU&6&rBA2ZH_-ah_%D<$F8#^ghSn2u+6keLA1 z+Bn~~=)TgXXhplKSqXCs=$y{&8{C+cZ)0Cj&B{|=M^-oP)fTs z2Xtj(aQ5<-Qn+BS`{)CN+jVo9lmqo9sKsV|D-r~i=;O;CP+4$W`YUx;@y*@HayN`u zZny-0t*+DZ?DjSX>k;2Ih6Oa-rDvdLLFHl;u95O1DAumP(nBr;6&4)n^|ZrESRpVX z56~b%%7T$%eJB$}0pZj^(Q()7(YmuxWslB!zjsqB%^?sMI7B7AdEG#V)VQ*iB~xnX4obIws$F)w;ZC(Hx2f$dgq}_m=zyf0k@`e?-^v z!^Tn-O)Zag2Y2xAXuAkh4ONj7l?}KVGtq1N(k`jTS!?eQ_4L{;`dH${%_;*-%y@d7 ze;vL-+dkwWe=B=N!~a+oY@+I-?6i`vsPL4Xe9cY;L{OBr%GOTtxs7yeQN*^;m31k| zEJkLfyiFKvrTA71cBXAsIrK%lCcU7b#uIxjr8NXej5f$#>4+o-DAQ2gFC7vx7Bi;B zJk=h;IC+f69y4DsZf|c^+V!h=H+6asbv8S2}OD+PoOUs#S>oA|X+TtSr> zJ+Z)k!4At5spKc^tjH+e2a_Lf`m2tgjjWf@Y`oF(J0xRvTX^syJxd`5!as(!D;rHy z$Xe#?QOOyhi~PcUKaZe6Fq~@npn_YVPV@VyF!`|>=rusWH7`+%%Pv@N+#m|KE?*No zMGOhR4&i#5#aam5f_AhMoATv0;X-rKyc#B7(F@Ocb`WBA#rm7$LwIDckJE#X&zJIFzeRY|?}>zF3lzZS z6+<*(wiT<#3PG3yC$~!|bLC8XWbkB7qO10+)MtQ{kK0Ktes=?x3bKjhLB0$tm!g=m z!mHzZ!>`43{yP&N3ndzlT3%lXNAmnc@-SVy)qn3W*zBb_DXp!c*8}1y!m`=VOlLX6 z!_VFpI>XCew%bBU8h)mA=x0)=Xonps`ssM722Nf$Vk^Z;G>QWn{DP08#_)DoQtLOLR8qwlSBJL!Xp=t| z($5JG`3$cAG=CbBik~zYO(LH(x8F;w48Sj=oDOB#|D2}I7B0=$92(Bv=PEA~|0 z%p@4nGZ?N9!|Nd2=k$x#;RmF#{wGB5-AjA|c%uu9&7Z@r3Np zPq0g#Xb-Ebxi;ZJIcnMkh0-_0nGY`k_|>e5nrm4SH0-5vybADfcR99SVB#Lgryc#{ zFmA1D#qPy`vtO;PEfVAhsLBkFW-#n0`l>zt4QOb;9ART_`M zqUq~=O^DL^;Dg}K(-8jG5h;)84rHmRjCO?pv^1V9WYO z$DCe-f44Tn+cHmih+OG(j^)Ha5608+pTe*P|u z)?zC(r=i<0WgDyPzqT2&)5z>bYVAT?9j5vanxD|$Gp4#SP3wD=DW4xph9=(HZmnGC zN|BtGdbb;Q?fZOczaq*>MWR}as40D^ahb%9 zoyCaU@7dCCk4z)Jp2ajr{>)KVDpty>&@PY1ahJcCkAkdo+DQH* zKOCMI!es5T{D3*lIOPC-Pc(*(Y>rXJ9)<12L@imVoVY&5dhbf@REw_PFje7QBLDSN zDWfP8mUCaJ&hw-=ykF#c}^Thh56ziX`c6O$`JSjQf&5T}!!5IW@7Bty%ZvJg05 zcj)5Ouxyj^VXY*~xtVmjSa5>0|ImC(s5DNRd{oq3ZmDKTSlW%DnI_6xfm-M0faxkxl$;*wSL&FwcQyP z;!#=EANY-j`5;WAGJw};t?#nz`xUrcHk5u!$4S01>-J%adr0Qt+NiE6w3XtGF1 zy3vTG%O%HN40M@yHl!*?Gg|?Sck{JRS_;gMEY_*$^;u&in@jsh%ZA}8!+2E<4EIAb zI7NX?y=A(h1hp9}_*fq9#p{3w*H-_rw4pu7;~cj~EdgdX5ECHH~RMiNF7H#mTv z$BY}np$wc1oz_sb>4Vs7G5d@f>DIL?9_E}G8?Xe(PQ(8As-=h@dnNzVpq|wmy_@RX zHUV$eugA`+(t0f>1g!K$i$EWFe>Qh1;12BHK^^-f{(e$x-n9C;)7#0)*yb1O&$N42 z@tO+z@G3DG`LHw*I@mW(+21dkVPZoMMt>9z{h^TMxital%m#bJ9Dxuzh63vi1vNe% zWtM=I4Gl_;sn&w&&^VbFxb7#J<5!d8H^}!K{or0zbup5sGa;bzi#HE}U*0-&vqmbX zvwD!YzeW%MJ=EURXuXbYq3X2<(Wg(h$(I{+u(x z<9zLh>}#q|#-*Dc-4T8nQ^CZ)`SXXVxx`bgMZs`*UyaG2d@xRSQ5v_8h~2<>7E70T z3>ECzIqNYo)y;_NgNBfKdMMLFv76xfb#T#?-5B0@*X5xSRjcg9%8vmPMy2{!uDW<# zRnkKz*V=L-q)b8EAKyq4qiz4(A4I9OWIXw-RDu-FGZvhfGqjrN#nn^cEJO_eJ<*lit7CG5=_v@GR`xT zQ#n^Tit=5g<@5pz*BMtW?-wj0iYAZutrrl{7Lo9%I+sI?b>3afIR>{(fExJUyIdtP zO%KSSc&78!&&DrZzy=#6U8-iOj;z$YwWVC}^l`e`rs2tvGmSLFI1~Zq!#Yy=Wj9@{ zT=1)wnn-d>+~>6msY;ur;-q!~UL{%T8(^+?o%kOfcN?xty^D9^f9GL`=+f_ftN2qK z!#OAjb83~z&Y?8cO3U=YjsYN-(~W2a%W2m1mQeQLE=4&OsP_LRPKP7DD>s<=XW(z| zt98DU$bNS{N9A~NHtSkuNZbuM#Y0G%n2rCk%ml@Uj$B19v1*9v>lH&Ii}aR#h(4zR zrO*$qsm`e_2s;}#B?5bHqgrTWrOMT}gV_1Z^?`fEUU-N;q*x@FfN(9qQX6+hE!+M( zQ~!hA?DpE0_7Zroyy@B;o8H$}{Yz4UJ$`YzF9Yc?kCYy(|5Hk343zqj=W*QkJJq&D zc_sKn0ZdByiXwr7sbWCA4zR1J-s%E-d3jCh<_S@m)4?03vS$!AA(&4I+NQB1cg(|u zB`-*sOT`2}IQr**QFl${&hafK1omFre-k3#2luZG9Y4Lbi7RP2Gz0mE@usfT#MHpo zDebrP_a*OkwkiP9Q+S?Ozvvg5N|+qIBY=3>eLO9zYx>mZ0Fm~*{7iK)53y=JE1Jee93!&C;osBsVB6qmpe^Y5?(YsFFF&Pbe2E3 z;5NY~I5~yr<2!?K{x!KrccrX*Jl0l(f)Scg&>9OiaD1;>E;-^xXRnB2?_p91iqal~6X$%Xi$bcC8fNsh$Y<#&m(eTU1 zQJtOF3-a8Hb?%xyS3rqi>7)wL#?z^!*&0V`a7rZ8ym4*atGSJgtQ!ekWtU|ASTD?z zMYu57_3asScv<#twRlQh;*B2d@7ckN#)z+yflAwj{I>Xc<+oMz0&T8J!Y7VV8@OVD z!_t-~%x;rr~G^_P%>&)w>v4-F8FidSLjlf>W#CWug%bC$9PW! zqWoTMu|68e_nLJF1C{=kVG_k{nh6vP3QV1GRw~$!r6}*#j?D|+QiaD7T(KsZ08Dms zxlRP_{iss*&H=Zj4SR+D9&Zc?p0@)4x%Clc4HK53brXH-#II7%&kcB*a(O+8#>RTR zHwD|7DhPeNid;|4;(2Ub(o)%GK@Dy}!c3B(qEClcmzRveTHKD2}0LLfe2drN+#E~jjC8)i> zp~n7b$^Q89f%R*s)X+SYDbY1cZ(AvQJjj`3jhUT$J8->`&QXVI-@p8MSJxsN6vqn~ zH&^V(SV+;BCBG4K3V3SBf!C;2=NyzVPCLC%8Jh0*;n6@`(jnVAUe|S0f3WLi;?XoA z4qdu&>)Y;)tQL0bA6HCN96%ZP4;c;qB@?bzo4SSq$@W4?{$+WYQLDF(8m6ULjD4n0 zX>!APx`%C!)tF3%Nuh~4EIifkvb?0{Dng_7c@x>RKC(_P_^vQFDpJ5L-;*w1Ww714 z_XAgtl{e{uTgc(UF{}6)EVeJjLH4+{cZDwXAuly9+=&SDrXi%^WoY#1(r6B|)v!9+ zSXvxo+0x!1EfHCx9E~Ybl7GsAd&+gz6$%bbb!AHv_NPkUL?m;>LGHoJ;P`y-lSdVR z0#t}23)CFAcMLqi_kQ-#jpHWig1SdbwUKK-$Cj`ttuc3^bd4R+f5NN^a3E%thggCq z*s4A|uh5Ntlp!Cy%H(zfVaF%O0kojl}v*mSMdgHgClHiI!TUZA7mXc9~XSjv(W>H;v28c_P1 zD1wH|@n7J-q^2OU;RE^r^Me$rTX7 z`DV0+Vi!rH1ehPG4!OuUSQS;1gNJ8X^7M%+oM#*-)%_W%y*T&iBKCp$;*9ilm4_j= zRr8@F=(OQxsr~q{)ge4YSV7q?8oy0HS{-dK@~KyfH#1JXi5EK$GP05K34V=D7Hur$ zB{6j`UJy?jqoG*GMmcK}2+ir=T8|C0vdJU&^;HiQOO9Q|SJ`el-j^vJPO0TO?2pGU zVP2mV0+YV~Ikj{5FRo?za-twgf0_`+B+^){jbAgtwnr1nd0Ea`Q8rl~0Z8f=b(aD7!NlH~=xY^NzdSJBj?&?aWy zQ)Spg3+D77&G-|EiQOU1{O+?h8GB15<8|>l_mWdHEe(&Sg&X|*# znhP)2wfjV6CX3HJntdI&0?7TrH&Ri+8SyG}ZGqC96M|0rvliun_X<>ed&=-GpjQ76 zO?NhBRfhw0qK#BFD((5>E8kn(&Yf4g(d8rbL`>aw_3zP?twR8v=U1ahirX+yEnPhe zA51Ml@UNdh=Z;@$PM@w#(45S@Q1@8FK|z}R%+CWEmUrIwDJuQ-=e_z1?%TyT{;*x` z-)(iTT1?4TaK{4fVycgtz)b}>e(z0qcekvHkHFk$nr`*IYbhnai5Ax^7adcTsC*y4 z0c>GoU3lw~pL60?X#0}o3Tkv~wapJ}e|epQk%*o-eVx?R$*J0RoMGXuoV9^kGev{1 zZ-Uy!y4(db`u&rC=qUk(J8S;6ACMp7TUAnICk2skz7|vFLjsl+@WD+J$4TOv(!{}$ zmgL=qz(M@Hxb;}V%Dj8d$YilXJ|;aB?+#V zq-sj1|5EWBuAv4VR?Vw^jcL4mc|Qq{jp)4~P%C6F)l@l7*p_{rqwqA#xtuQV$O`_= zNkV*oX=k}D3O*bbz*L%7_-Wm~Y;+r9>7%({(EiG^&PZW9zKCICRBQlXw7#IP&%QVt zw7~eFUS+PHb5gVXXuIjvwZLavD~E69cSXW%@ja>F{b%i}XxL!iuAHo$*pR;g8j8=+ zuFFxK2}~lQM~65ZRHGi9d`9U4QP{v~>EnC3v9L<=u+>4M)9BN9%JPF2;N9Pazi&qy zj#p`lqErtp{PzwK0TNA_Kuf?Mjd@Fq>E20I#9&vd06pu$P2Ka*s#pDwJ|ze2h*_o% zR8IUNKC!G&jkGDSWui5!TvD;bn&C}m%o`sRKFXfBZj+&if(h{PE-6eXcIEog-&MO~sYwUR~w_lM=-NqPayx za}Tt-a^y~2<-Alx#E}CL6g+o|BXOmO=0J0=-06M!{ssK-l&*$LrS9(jjQ6 zMi!=PzUXhp=7^iTJ<6Qicf~#TuuLNe1JBe5ZmMY9M^uL5lj^2_3vFJ_R%(xgob_B+ z?9Zuqtk(H<_J1A=hD2XHu*88i-R;iov|e)~9ZU5nC*J+u_$wIw^lY}tHxJwHnZ-UC zr|O&DQ13>5z%yj4GB-}!E-A^X!%I@;dMJqblq!ao%jf%{xjC00%0S}^St(e28ob}X zq=*No(S0l7Rk*fjXV&I)OSb*kwXV4ej3XUcGF<)>b?e&Xj|pei(x38u)12MIg=v_WSmks-~J?U15!)^5Cw#S zBg9n1{lkIe!5*}a1`>c;S|fO&!NdiehtShV8?_S5q5XERn6CK~cNgKoPu7aLrNc?* z@D{K^yhUe_%egf0g;UUy+2XX|kI$h>o#NNb9cRKlwd=_^y?8HSb5dui&rjwbf)j~- z#cX?|y7gE{T8Gcj(l(3X;*&ZdsUUA)bQocNu}HT6^R?4TFsM8b;3nAeY>^5|Mzydd)VTKinS z<=;3g#|q?}#c#-JM|&fl`iy+li(mUL zeb|w-tBsyZb`uSHq85J8>*^GpVO%Pu1u4e?E8T~@Hq&Uu>uVm2z`3EAR%ojw;R;~_ zVi8z28ec-#D2yOSbWg9Lf=*>rtNynn98N`Fn>MROxr%)RvtphX?E(?q^;(Ut^^(BNiDu!-0aU0uPQ$n19@&pP-D*0wgRokzRWsG1Wn~XPln6P zZcj{22h~}41EtLhvu{@Yn1 z)~fzkioN%)kwbsogd1Nc0Wag5KFm_Af75;4TUy2$C1<_U)RicD+cHS7WJw8){F76% zwwKOcCrx{BFygBRX8Eeswqt-aoL4fxpv2p3Fw=rte*zCKESCqW-@0Bk7aGK?vy!L3 zajeKPL(Qigt6Sh_@Ok%8p?9(F4bN*Fo{0G<_L>egmIEEiLu2aZt5rQ9X?xGO`173N zwz=vkRehtPsy9Wv5@Akw_AHTLT)qs-Nl3RZ_?L;`UoB`8ImxsChkg)SKG}V#qANko zJy}rhm__rGAB10$ck3JW6HHVoJWOsiV4ZQDkn8JM{B}ell!Kk#?ZJU4UPN75FlmFd z+c>DlSAE)_9)y2WoIYFS0bZ9|vvaiZserM(`nYzf&{u5}Q=j}WRiTUy2Z0)^Y&enq zuq8xz_tTs45YYY=`MvGx>;=E8_hi4C9^d=;;9|zocYkO?LI-5vVeKI$?p?lA2ha$Ng;xsbI19DO6L_J zlt$mnYE{b!tv9Xh_8kj3^(4{_(qKOGTxe04t-*SMhX!Efwr4v>)0aqk9QVTZ3lBf||7;BRq zruN9C1Wn5vTf>ecVWF?&sHtxR3}0s@@XbomOkZhzx2Z*O|1Zh+eJvJ zm%fNX8I|KAyK z{sJme2N2U~#w|*HJ`(n9wf7rhwd#r}HNlH6kgs1X+5AdtySO4qP}~D0 zJ+{^8A&if!g5VYGjm59}Y;pVuq?7S}WebFN9i6JN;Row_M`|Q156mDgw~qCHJIabG zI_<}pxt*`(jZ}g(49g>eyGY~ay=1{h#qUfLFE}}B5oCsoq8O(YV3mlB&8=v!_xp4} zVA<&s+cTX~#AW`Ai0xj zH`=v{jf`{@dXlkl!-2bVP)we%T%eBga|wgN>npGA)m}iWk}Fmt6RV-3@E{j$E6+xD zghLnGr@J}4FYpHrKSF5b0pZT8Pwe9yWZ|w97=#*WopM+?*_b3gfEe~Jj2{5g{A6O} zc-kg_e)`2uc2dz5YYHJ1%O>JW@o-ap=RZHz0giv3;71B^Ab+Bs3$SIUr?@DDzcw8^ zW!(s*sp&4~ekMY0v_JB{$Er&Yv$2JyfnYcvw~*0&6Nmg8TRvF?RcGuDVpyDOk7kyTs}kAt9X1BcET5XA`29EYh{SpyBuMXV0xN2^!}L zCwyO~(x&h^k5xWtLZetDC1kNQu&0n-$zw$`gEKNuL$WHD=>j87%IH#*c0F$M<@)M} zxdad<$K1&0@uVT%GE#*-cB3ir>${|yFJcknzX3nkkS!J3XE-+3-hFzV9R#fHH#278&)&+K>6*103>^S5XN{N2_TtI|og zU9TJQWH3_AgPvQHM51?jdp@_P07lbu-H;|X;vm`BHGVXL`xU>063%qIuIn~KQ&a%l zBppZk%jG%zcLv>w3oGd)YvXELsj1XAI+2ixg-Lk9Jvqh}-RNO+qzf{zf znC!|t6JadYYNnsogKe_HCX$fLl;Dtt71slAgo$-A;l!>rS1;G8PZ4CU*Zy$!BoWA_Rj#1|#Pco+ zB62R?sc{Qe9UYnkxH>Y_eJ&DYI$2nU?>*ncPKRm@zq0xCimUuB9cr}iBQys!O5bZ- z8eXf43q;CVSIL5u_np&o(@RZ@GbUQd>Y@FYLk_`@PhR@doGRm9ZqR<}lRQrcJj5@nIlKDF{SA2%Gy zr-$+DrGHUEmBSUDZj-fR6a`0@G4>q^7e$_JRpyRI$DpbOH;Tn6o1@ zX2{B>gvkB+7j@QEpj8zU@y!ej0tw`40a4@rq`txtO_ohaKndNN z_;@;(U{H8GyYH~Fy!SNwm^mD=rf6-dGHr>pm%XEcZ#v z$zu($JWJH-N&59Y8=J9@t~(-lAstr4Z&E(ykNq`tH|$M5CqvJ0TVy0;qdlgux~z- zpcE`?mDR;Zim0DpO^m@mzz|7ogo2kKi?kv>k~zU)eQ@y0XeG+#k0P+D z8%$*=TV#-)Ka|?m-#`7EaENBamOX!7YfEB7-Ec(^AgX4o?zjT6DWbg^e_tMIiMq_nqgJ3Xx zNzRHwAc?x3`_JH%7|bjk@fbWL+#x24$_iMT@{cjzMz@dFcIjes*PsT~XM?Yo=wsTp zp+PrZ6we6?axbewPzo^L?4`!#%F2qN4~tcOk7SRv(;&ePo%(~*IOdp<53!b>OM(3u z{7#2Io;4h9TAHnQ9r~GboS^CRTmn4&rMXiOSrP$Hh-SrJXWssP)P7 zeKnj*#Y{8uD)|ZlD7Km?%Hwfg5U+OIqOUjY!6hz0t2+-WXX#})K97V4mui= zBg$0ura;wu+-F=((QO1La;i%=M?5evH$IY4*UyU7hkIW~R+2#%@9>`SXcaKh={_#+ ze;hd+#1v|7T6^0*oGv-6mzzdS&T6VlX{;nNrfTllp>G0#&UgRW%~J83hCeI_t?;fY zO8=-xuKX~}8h32@tYxY8IU5KxcpQ~*)o_boEhp*Y4(Pf(pBr9;t?bgV5%=-B}34i^1J!vxXpQnO3=@$pT z{N3(W?CLAuMS2r>p@H94cWmE32?oA`q)hP;mq#`*b$hkP>tUpt`R^G*Aj^;@mom6# zewG(|cg5Y&QE&-V9#Ob?vpHfkYO<4SDCc|szuO{valgOcqW7^JsQBbh)`huZSnGWe z^mO1UU;Y}hW~DmxNll#-l3=t>88bt9>}>z?RrG*t{bqO!O}8nW2y>7OQXe8-?``8E zlYK9qz5e19YOJ1#Zn!Yz@bE^QJqc+<1qZ*S*Y70dn2`=?{FUKWFSgLk^fiVEO_=r4 zQHSJS71=b0Yb_3_tg!T=dAVB*AFtZ7dy9MU4FVNgx=+T zABCTp!U-k|#&jqvy8d;WsDDIiD^U`nzpup=vlBjcUFqX#^~x~o-%7|ov9xL4tuJ4S zxt;`uJtzW0aTOX$LG)1{M$Ug{ehZ)e_uYV?Rc;4h9J4!%!Ix!0q_fvE zf~?c>v%uwhi_&4{pwT8V+M3uLuCaKKHV2Yp&G#_Y;S+l|^U~h>Cu&X+uN&cJzO;&4C%JL0oTE#`)t}S_ z+^#?_y+}(-DY*NHT-W6IZbV;?1ow-Zb%ZPd0}H9 zFyke8r6O7JX;Q)EpWRMFYD!l_%isj@;JW(Z2nB+XUy!Ja(uI})yup&t3tPcM&{f38 zbEuAXzyHqg%*Z`!oomSXUrdLy^y&{MKeb`GQG!g*%l-@@gBy89H+#U!BgPuRz@ovT zHmyV~!Gu@nndA1BO>tqd>k9v^%>hru?I8W0qO6NZjx>k?vQzkRIhlA?)+|PW3N7!8KZ|FpMknMdlaIRUWA!D(V2S`0bjLO$1BauM=uQ+6CFaMn{w6&>0_lZf9>BUtZaM;Pe{GmVDC2&obdj#PO6i5W1H%iAKb=`LUug z)HHjZkO=i&F&e;?><)pm)y97n>=<8qY`EoenBZGxxbBL)lf29ie_XG7UevIBt2L1! zr~A_POG2a2IB=%=*RCjzr}e3kTIkWZT}kuWnvMf=`BPG>fqI^np8TQ4G={dz97U{u zt#=R~?Y%wih^*f~_oRO>IL80qo4*?FqGs2ApHucVL4RrcEx58ZpQmI_vWmEW!y;s< z91epwyESqs?jL0JpOy_1&+-^)4W!xB*5^c?Y!qtP804$x8|Mps4|~k31(CXVT&psx z;fOVD{eB;vgzfD#V%2@F&2XJWi?C_Uyg;wUu+<5R8651|LQQSBbYndmkx@6Y^D25` z8|UW|>3i`_mk}9j->uf<{8T9djg|1kI-PG7Ys@z`ShW?HWQA2*<^3sZkcby7^P_Lz znkp8Vdt_R@m{XaRMY}Zte7S#f%x0dwUO}s?ERTAbt~_+vrv!*<{_GEwLxwJ6AkJOI{AVD(@$$Hjc~S<+>dvynY7PXjF+-@=YW= zy(36%%FHZv=y-0(V0xx}20~C0#28<`$D)7NAw&ePE&Lg?Z02sA#*k|O{xRKXktyyX zS+xTwd~+@Gi;0kN#I$w7+mMXK;5<_NenciQvOoc=OZ96xHY+39=+`VHWUR5=Fh9XP zQz1zW;95XhK*=||zG$SPg0H)$q)74kKR^2Nwu77!T`-bKw=YSc6+~7{S{o9o3 zx)?r+v278rh@ph-wy9!o6^JC{j64>u8tFaKj?Si-ed|ww&Y|4c2In@NuEqo`u|DJ9A#zz~o??;cT zCxx?Vj_w=27-|^Ki2qM%exmi*0Ai$Sf!ci6WRq5&);Y^-92AUR`os9q?L(nYE*DJy zhfn|SjN+Bsz&9EXb!&9#Z~Tj*@xqns5Ip}DI;4@-2siN%g3aPST1WXT%6g97qc$R| zG^R+~v^c>Q*z>ULnMsDOr?N;G>h(Vx|9j8$tKsgLvD_y?*|PKBw9N7=?kxkQdhtE8 zxgpl%roESo6&1bCwPl~nPVzL35sQ@-aI31Im*nKiy_egfbdj&+X#@FFTGsc?^aj#& z0PlbEy?<399WVng2KSc#r2`;vwCwM3wEY&ToFJ}|=~V)DjJc%Ywzz(hKfR_K-plK}%z44&LK#ahA$#kTAJ-a@I1 z6t`>oXTjFn>It85&fjc4Ov`h-IjGyk#%FK-@(CZVQ6Hw2TrSQi=Vq%pE0~S4F*giO zaar|&X177P9}lT4vyUG4lMI(y9LoM@>t1Z|r=R$Dk(t)kg7r2RKG|71@%U6O{%&&n ziW1{-^zFJwqRZdm;9Mtu{`INr7C^M;xOx@}J=s(F=QTkM$gj$!7AyOSlgzyYGF)%1 zgjwxd?q#51DoLJx5s)J$Z2smpZuBnsug;aLZAn{e|4P>W;^d;JdqGIcbSISNqYi&s z#k9|R>IhjbZstWimhh*3C=sLvK(K%Ota+JTa;(tjsU{XYdmX-ZV|LoztqucBm^<-e zp!nR3&4Bf{zu7r|%Hp3f>)Vf#*D#vtZG zu&?3KPx|^;EhPi?4QZD7EG8QX9DjJPiu;RDQRPvk@kl%;Vpg@WsWIoe&y-JLvI?um zwah5)8o$9OfO>=El^jg?102*y5iLd&`LY?Hh1;T$~c8sX0+t>A$XzZPbAYed7)IT`T8GWpOK zkG;d%ve1dA=dR=N7I;jxTK9FkuqeHp2Pi5tRONp`2Q_SjRh?p{o4l) zEw~fT&rXHzyDiQF=iNktO;l_61qOSK`G><4dY>QGtojPuE&T7|n(VMYsQ*GDho6h5 zd11{$FyV~>A&@Uy>M0cA7vT<;<`LL>vNdgEzN$|gV|mbVDX|@9H-TuBtZr9Qv#HaX zY$>fxz%3&j~5r^Lf~F+xh?&u=?JcJsUIV$gbn|3uHPNtUl$&!q3Pv6I6uwV^d#Mgl9zqwj4%PTt*q z6k-s3FkQdwab>yhH&=;ni9gmIrgs%_@~Td7mG*#%Uz<_?P<+>qD;QI-`Y9|eqb@ys zX|ZSfzcX%y!?vt`85(4kIf8-EfscOKS%g&rNG8I=ML@cb zjht8g0hU}u4jfsB=lxtz+7r>K&{DjU%~p^eEU~>SdS;B*HTC}GWy^@fM83}1zdGI= zZ78)tt>ijY@QOZAA)?;(kkRSBMjCZu0jhOJrqa_+5B)ISx|uzCG<9TGg;CLS=>Mu# zS*=?8`1ZCdGcZ!7EZ~~BTg}aG;4c!u_1IFUcw#IM^{b}R3QRthkU*V9K z7k&^bRhS4cDrx?>Jawdo_mCszol9|-I%EOW0)j{)%{2Y;We? z7jvf~uh?2N>RY0qcTJ{^0o1Z>?sHoH&lM&SS1_JJ6NeK$R|~lZ?bP|QTzItB44!Wz z+(!{co1~C|E1oOMIoijF>!Uy1N2fdMOI`kgAq0jG<9U67lV2L-Z2`_=#aC<4P!ZaX z69+Q-6j~!;uU%H*rJ!>PFz0njsC1U;OV3cA3+$d@7jg5$c-N z5u#$E>Up4Y9MmAt9aHHbDf?^ZMtl#%nyc*sey&UG|j zO|R?Cv%jP{g4Y!-lI`f>v%=wxndt8EC6wYx!i@uuH4SAm!3iZv-`ZmTu9>mqh5o-- zhOdy6YyUc>W~uakHCg!5k^DQLCdO_}xJg1vDF&Q41Ax$eHdSCSmB1hxZZF_d9E29+ z8~u<&k6)fZR;-m2Fv?UVHudTc?qqx9;h=JSzJEAab=o})lK(oZIp3ojd*|ktc-zJ& z0>o==UxPEi7!#YDt!wnShV5~L&WiY&#wN#-A6&DSTLDtas0cc)!UWo^3=<<~FubZ7 zDoeo+eT;3As?3^V-_X7Ftu8uenj~hW?sc{WFIX!kbUanWUFa@tXy}6D)y6Sob1wZL zVonqqB*>ckcehQ|*3`bj48iT#8k_*YTw8b?+_Px-AEvhpO}Ng_wtO$A3SX;UJ!Mi_ z1he@fv(dKt`sRUDFO? zYYy9QCuw#Z`@WRzDg!UG*`yk;J=LlpY39!paL=|{THC%lvrDYLmJqt=(9krJc{SBb z1%YrxEDg~=mOe)7<(=kdj_o~@8M=ct4e6OGxuKIjrQo8hww1c?2AlQp^wKdbzdQ6To`>P+Npvv6pv z0c|T|BmSMaN&8iMJ3G523)aAmM=lC=J0b*hAto&}1?l#CmulRZ)TZ6=bj?@@()N4j=Bb3ro>gSC9Cwes=620ngbLw%AhIf3fBny{7<$n;7nMb=w}y@mk^ zkNC(Au1Gi0)%md>p{vKgMYTeEVV^~Qfyp|U+U4w5n174f)I1z) z?VOzqhH3+%Cey;%ux0b$gnA-syFf0q==1_=wv%2*EtnxqNBz<$|FOM1B~qVYH5jB| zV%rcso7i3c4Y_}Ar;V|ffqlT=>nr)b=BaQxdUrc4N-FD7#<` zP@H>rjt2v}5AGW#+$>>dGv#-Yn?XqO1vM1hh+UC9{YB{2|IQ#Mi*HS=9xEs+{_J}Q zp%1Tp8$gTI$X9FhI_nxUkk9=rXA)~KSt{o%Lj9PXGhSt?P)%;Or{)9>lygS;ca1|V za=^-OpN^9>PZW8hHf?>~9N#U%ZkFXl55@`%zcJvo04Ebm8)4puR zh$n>h+@!8t(GHSf;$?8V;TP6@6qcm_U@v;=VEV=@9ZOEnA*bu4yQ;2+rgco+J7~mg zmIV*q8C3eEhABj9$0VtBuwe32cix%|&!cD$o(=iVg`%AWJhm>dQWd99|Hz|GY2de@$HvfyU zy@fW&5E?sL5HEyOogFawHd-L9kVmUnBNbBM>A0hm&5hQj&BZn|-&sBX>CW&yQh=)9 zzIJ8hzVL_B?g*H!5tenVr+{|9|JdoGg*SzAcsoqVwrZK8Z<72eD)+&83=-P>Q0fBl ziYNY;>X=G)e2v2jS#1Nun5+JfQZ>6g*6Oq}<7pY?^+?+vWR^AMWxbzHriQN-n&reE z?9Wz-O)Tv@^3ty%Ydy)-4qCB?V*~kpTT#yVT?KhzATM90IRGm! zVEe$5^Y-MG5r19DJBb~g&x!zb0IRl{$wjw^uDPN&&{lc#^;xt}(;3<-DCJHoC_-}5 z(-GE0WCuhr{YXu#`v@tF$$?SHe`i`X@Sy>YqtkZeA?cL%2RSq!=CnN-V<@l}J|C`^ znsN-Dnl+0nCLKRE91Y&M8OjVYXll^Ek=@dyD1Ad~*m7^3tcL?N70_p~`%Lt9gx*?j zm3WwkCN-4ce7_DVoM9OciPe)uhKQrRSM!@Mkm|mDSF5mQ?IAX=v?CsyG*wd?x7n;)I#zOI{NlxU9b;^avAnOH}sBzhgP(!@Ia@l1Kg} zp*?dHGE$|xbUCL`o%xQ{*YIoO?}h@Efl_x-qvU@F6uO<_lSSk~Sc~2bA8&{2BW{oq z;kBW}WrG`~-eJr$!c?_~G#ka&|GNmu`kW-BTN@b{(x13H>4|1h+@efK8kqQb20 z+ah^^8<#SjJdCdv2?1(}!s$y5-O|($5~adDqvC{Rn>`|x)Q$0 z)l*r0Q%gB7MYkNt+EdHI6?vq-%mTDnOd2Y9R)>*etx~*S%eaY@{%}5IqNC{#lk4Ou zK;w76Z;9qG(n<>`>85d>W^=qvde#H>)PHA!vCO^uolZX>8QOu%nmxYxV8F5xxxGRp z0=`lUlUaYa?%GgSVBJ)c$=Ou>BCscC7hT?){IIT|{}Np%=(juX${zT7_%$v}e;c^= z4qRU3p(d=K}zOMuo4JULtq*unM(wsE=J*ovJUUc-Bow)`5m;jf_th&M9j!hjzL(TJXu29 z1V-9}vFA}XWE-FWAyM1|x8DClw6vO(@^LTBvn5Cs=F!-~%(171l)li#E_G|cYF%MY zX-yp_tm4y<2Pt|A`SBAZy%R?dN+=lODc3mHZKZWD>7>;?A(AnR*9_@XkflcPwP;d=u7zV$}m*<7nwh zd?EVbH(ht)?BMZafpTxa46PWUouTD9)~q32o26jdXH>W7I>(C}(9`hzL2QH8P|HZ3 zQWq@hin@y@*Lr4ja6Oec*?Y|7G8rzZJKMabwK>+}pvKe}J|K1p1RRW>PF)HyC(|hw zim^p2;e;WH;ikuYrV0)yM)OZ|;i=hWIfiSzS=_G#O*c=v**iZieM=kJ2|zIJW=LZDcVsQriXX!yi-N+{Z(2$mZPhD*;9G*$(Tlk<5|En z3uxq|p1jox)j!D*34A&ZP67g6RN1f|_20KRc_q89euJ=m`yz6(dmR8B+LE6k;k46yO^N824-mB|Mlfd(UZ=IGw*= zwa{|_M3h0r!jpYc){9Ue-)zAI$ov6%#ExRT;|DqNYRKS~GB2JHyt(0;2JVyMDr*X1 z#Y{fj1@rg+6UKbcWcV;*C+CDOGU)?6!WhHkl!aHSAkt09DecdW@K*uY=ML|vQPrR=)J)fVf z#%JBNNu16^{41jFB-IY~+v1gz2n<*!GWZra-VtqJ3lxV0D+G5&m{6DPF_7x)4< zImu7!3v|m~o--#OOJywP_CF6cMPoJd?Y?{FxKFbhP)o}BoaAfmn$Fze7rd^Xy`HT$ zOfj6!Tu#S{6@jzg+LJ-n&-8DIw0tz2Q(|k5OsO3XFRZy1DZ(3d;M{k{H`4y)*k;5StO&yAO_~4fM;4UvFN|~0yE@ewHRDDGU8RrINA=4wd zLUXZ~{XDKb4|rlEnBly|Fa7vNOwmAG3<7eUE9FSEM-8@1mMofYGYQhh5gB|b(VZ_| zDos!|Q6m8h8Gsv{#APEny4g5I4#p0Axm}w@AOH2}>#awTzUFCWV?QyOPzr^Vz49ov zC8ZY=CEWB3c7I!3tB1v&Z+*;TIw7|^S*6?Jyt$L7M4bI3y`d{fk+`Jr3Ud&EjaXLk zokG1LZSim08_C;@EE|W0=k^&~cNhejS<{B+=P;cZa^2mo>-no`{Bi;mnoP~Y42Gn=R&(IDEYJI zUi}+SwjJao-0loNGR(+>1$oZV{~ou4eoBUa69v?HMO$eo>L&-tL#06=P2+4D{~8~L zLhR`1CbIfeN8@1@9ZptmXTc!Lv8jakX2YNGz*ld~bN}9SC;41`Ot7%UW0XHXdxp&e2Nvj99a@67)3bq!D5+3Vq(Yhd*Nz!x zo_6}eeiOwFr5IPT&OVk4%UIkjNI+{yalUJ&TrbWS`fOVHhXUn+LSchv#?3bglu6}n z(eDZ`tQi5I&=1gNKzij8^>Q)9m)oDHgJD_j(6m#NG-U_!fl}QH*HL)006V0}3df_jWhUkFN<8@m`@niF_2VbB9BlYb z=3^BA*!jH~e{YD)jD^+j7s#0|FS>kFZO#glWC6DC)Pg61l;~@n$3!`=rV8hDoVjtm zc!qCS02nGuO|KY~BnKlkJeGbn|M&Ieb9|d+5)+f&AgNvMhraIi+k|wc%Uj{;*NZjdJ|X#^ILf$Tp@=?8(ehSL>CHD6 z?#8rjhX~B?@yiUyCwS#n_2!WVn^kvL7~@Fy{$&i5PUJ-&`&*^C@7D z@BTsyN(vo1tIg^27{eE(-ctyk`9+pRhd&-7HI?@feRbf=+i?2rXaQetp*d z%Q08keyhXmQN$3IyLz=ljx@fa@S$@s_KA-|^>4hH%3of3b1kv{BEC9NJ_1H_oEo3K zklGrcT$sbtl5V+`4Nsezy%)6g!>nHq#>@^CB32J!GiMA6pZ#cg1}IGujKH{m>o+8Uu}>>CHC{HNwIsrxb(3m6klSy76UX6U;^A-JQ!qdDSW# ze=L}YBX#_>yfZA6HCdl?-y3luW(v%Y{GjH?bP#>TsG0pLMDr^sVjDeOl8xR8M|+6C zE2{WJTveRx>XiYh?fv`~F4uj1Q^DIB%IA!P&g50ltF7*+$qW+09@a`@9up__S@625 zkVcN2f%QJL!0f*>8sNV1utTKZhnB-Ps}f0A=fD2lNK$!SoqF9Ez+1U)AN!7k5WPql zYJxMDH4YJ8yCi1FVKGjMcLHE;4BUk`!z%(;r{eb-mZDx`dn+Yhw5l;QGGZnkY3U57 z?l{$iyls?gtf3>`Agm6YvfQOeYv=>ipM%pe2=-j(LY5VU3};lphm-ubbPHHs`&Kg|7TVezYkX2 zx-9{3+3QZ~Xixm|eWJtXINn>4&$n{78dN6}eiI?s&2YaAubp(X$ra=JJKO?@w5DZ-Y9@BUk`vD++8;jcn7*ls`S_}O zh9Z!ra~o5NUVq6&iwHtMMPZrvN1|?1$QymzZPz8w-v95+ozw+iwO*N_qS!rEPQp>M z9*AI>K?(@w_~X@zp>rEb_fkq@ZX7EEMzR!ajcxEUTDabFb{&$1RM1;l#(FFjwCY?E zzw-N%|I+5a0<-+XZq(XF_&awB^~pfcrS8cgQ|KExD&<=ApLed3cYP3<&sx53e#RN+ ziat*Zj(!?P=_52C*xe1&S&{1ArxCM>*3v@RV^!KUsC+9zC$OdSY6L-sC>7W&PPU(l z)n>^|NH&;r{Q0`>Jk9a>wF>?AFIn;Jg3H}?*7mVqk$Hz)Lx;|fBC+5aPVM?WY1i8H zP&;=sO9SBR5xS!TTbnkARy4ce#;r}?b{0Rmb4zNO$-=e6nyF|1_K8-e4>N79r^Zd#fajIHy%d3{&LL9 z-4qeymMSjTXc!d#Gn(~tdT}FyT{SdC>=B0#icmYXq&t3}9?Vp~vGGMQFehebP1afZyg)dq1p z;m2fpccUtkG(-l5p(?5lq;Ji!PI;%dQrgpg1#dOCiaB=0+HaC$nnb>%nt6Xf4#K;D z%!pg12MevM2Qhsl!n)!H>BMzOy?)>0SzUbB6ElSE(hZoSqh$0$*GP zhj#!GhTD@RxQD3S?2qaWpKq3JRh*SJFZ-7JI3!VG000)P-hodhg{xrhh3;RzvU>r^|*$@>~%27F@f* zKqCV<^~_esdos7T)7Gv^)tR77uW5W4$4bR%Xe@`8LqYs*)-)UIav6VrB5`{l)c{WF zsS75R50fcdH?_9|o!)i2ZMN=M6JuLVCIG{DYYoeEe$`1)Lg(&{Bpt6@oR~-I*?9;k z>kpQ~BuerH3x+Vm_m3{As`L_g?4)i{1=UxV%bfpcx^_ibNage^@tu#=A#yl35?AC2 zZ1HDz05x#TUl1FPTIbBt+=~IeWw&%xCoyFNIh-tb!#bO|c1!Atn-&zg!|lw!c)PW6xPZL5zS4pHF6Im;uyTxnZKL zpr+kA{tcJ!!Pe(in&WTz+gL9QdQ#v>QLU1DnxaxNkk)=EU^HLjTHIcG(JJ5f(hjwr z$q^&-AxIXW&X+ABF5JLFT-sy4d5(P~))dSvFHL2?W2=K*q76CszWwVvF1>E)KC(@Z zl_$K5{{+amzNax?QYB-&myymXL(73D`;`n&2kQmZLgiJ6@{-lIAW(^fNbLSnf8}q? zqdudk$~=JhAl(!c1Zcg=$t{8ci(h>j`*n5kdwuMt*`C;e*_IarJV$lyPzAleObNTHJclTW^c0i!wwNO|NNIA;oSPBqWEd>439CP0J^YHR$z5w6y zlxE7_J0oqFGamwaemJ=ne;x*jf;kF)NC>sT@=G zQ~vBG3}viVli^Yy4co(D+EFf4>Gu@G^oQ9Mx444ePPvI8A3&~?Qkb;ImLfWhQ$3d0y}ep6ZruxT@e9rFzhN6*2N+sN#AZ?I z%TDfXP$7fj@LQ|+Ij-mPfH~bo&Q~7PmBud%8vsR{(FN^Cl{hs5Qo^>7DzKMHB-Z5k z`T4%sWZC^6LucXF^xlSH-{Uc`K{^biM@WnYk07y4Kp1SW5z>q?y3?bSqyigCN(>ld zLtsIpQc^06kO4zlx?4Ce|H3am-?*Rqx+>Rh_0Cq#^M1b?9H%nP|9;i_jGp<|MqWSk zY(}UX>;9~<4h3C^mOO^dRH%`QoN!6|zc*P(oO6Iey(Rz8rK&aqJuV%rDyVqw5mg1G zQtySH2)EO6%e#AU;HCN4yX9IV>MX}`W|g8!mxi198h8+_`uYsqmQtqk42be`xE}*G ztqb;_tp3i`m)ky2QPXT?VSSW$`!>jFlzNvqn1h-w|AjUUD6dZ|mF(-PoKU&2GUwEk zlaW!%(;HzF?s|3jsPYfs7aC6D1yk7^B+KjfyA95Ua`uH*h@6(Qk~4|-VbW1k6d5Sw2{7e{|EjBA%?&YHJ?)VKP;8h zn&aus$~HYX3<(us>IbDhKhT;s;WDalv%5`hJU^rApMMr){ZWj->y@|{ieU>{QVrjG z$_&p$w04Gwe&DcauWEBKPaesC*i-G{=~1KE0>npmeu?HR)6e4$4w3T81KRVaWeQ^u z#DsL8dJyoA$44&JxC^+UFVhMH#|^HMBL1^86B=T3fAzlVH?vdYC=`aL)!$n^^Rpe# zl446@k~Zh2f8gzysv%8XqNRw6d_ZxBEVJdI^0x9}(%(k@G0e>_fS;C9yxJq>A%naJ=~}zP!BBX@1-~o8GhsjhX&V z>{6__OTh|SsPD`cHbrmx3yLFp&eR#nEdfd`P0o&O6^&P?u1ni8motxsEA}}aU(>c| zq+Knwwlun**YPluXYZpWpZL|XNfz}_!2@olIjmq!HofiZmSE;}gG}|ls$JrYl$m#S z0m6U7yOc}9o+FLGf6G&aAeCa~xfxwVSf2Jdj6ro+A~+?XV34>(@roWpxHy3x#H@_Ska>ikZ#}UA?BelVI*1?O8|A;WUdbUhV2rvt1@7y=zpnM>+abv}%sPZq}>NUmBQgHvf<*rc#D- zmi(%&hlq^@BwVp_Px71o(&=UI z@_UQj9qaPB^^)}Om*GmIuy;8+G2k~0i%X^|QOdMXk+TDt#63Vnp$v zSD5X1BguVY%?l)!Vz znmC*z3dY*V1@%Z}jHjEp_Q|Z`SG@Ic!r2LVqFWPI`ZXZAd3V`8(0a)cb59M)wjQ8m zu$uN;J>W%6U{aJ@mmu>^%DARo-LYr`lSy_NyN?Tacdl~I84Tlf6>Jq;Zaoh^OHXIB zGety(w3NVITA{w=5$d<2w|krg;1oxxHB2|2x9^(_#8SLZ902glqPuS9j;rIzaXy@z zC$Z=dnXgT>;hkfRWzf*vyoST@M@WESp<%(WyRIFEB8Ziux$8nsHE09x`X^KT$5>^ zXx_N_I@^E#s&c}8d?m9Dqf}$nbK9`zzCuNOb$OSRj>v24belR)#fys;Mfx=5oOL&| zsD{LaW1}q=bCGww#&aa~qTF)cSQ@8Whrl-2_LxzeL7Lk*J1@5==LCjob-WjevuvT& zJr;z8YAVZr_R`et%k0fM=($W`ud@Sk$ZU~qs%kH;2fKr_e9DI08YIBhTlvU3<24ox zeC)O6Ky)awcnnQQ#?CI(G>=gxZE$;o?Mb%x-cHemWN^dqGZX~IVS3u?@0mUDGQ4%1CSE$!=)zL<;?N`8uC35Do4J zE3d8hq1~EncIncgH*ebo*KdvJ46N_`ckZ$!J_URI)g;=MlOvdJHb542^zzdh`t!rs z^Rf#>3x*7yOEF~rvvue`Sl*ve1kO?TbN$N4mpI`j4=uN8fSyK}trv!Kr{Ab!AI8v= zy(T^LihwY8cMvHb#9Ha(T1W{lNokUA$Q6#|btz4(uRFRONh8DvrS71B4`__X*sqVn zl!Jg|(f5ToNsMfT2o$YZJ|Oxf`S?M-sbZbS+CrzlZ z!egY>bc+3D#67>&`8A2RqVVNUORtx|&w|(Qf$T5Dl3oixXa4v^Fc}-P4CNBcwoLHP zMKMyEmkLWm$m@1`USX0m;>L-U#=!Y(p=Z_j2by2r~m#(rb~mb9x%{0cxV?@()p@wz`wA}>`d^N1#NBb#v1@-km=OviTv7iG7ioa zkXo&a^4^C*O-d?Un1&<_5U{R&DaDe@bDmy@;{KU5rvEd>I!^L4pxExCE%Sz8KV+p; z>`!Gc7CXFZr%$_~zXYl^8SR;Y`X<4$J27`ir}V0&sO$bW^`KGmHzknPNU}@E z>yiPNg?jR8SXq-7%0^4>Y*wjD(@HzFj0q|Dxpe2jMoGdorZUy<*CM6m0Djv#)e$Y@m_pFBF6DFK?UjB$=&F;|3j3`07_DL({`2*y z-Tuh_Zg`@xZT&+6SZNdJT`O%9V77D8GOXQ%YB&aSnmfi7iB^h8mUXD~YaBP_$DYcJ z9za|7>d%1qwQp5NCqa``FK1+G0umdW33f93OwmDtI}-R-}mhXN9mw+f>`&qh00E)B>G^jSgj%#uX(H5m;M;9=_Vg14JMIBtd1f z9p9Ss)#B4t>IRtZ4Wteh7N)hI9g|Kd>K#~ZTX#6zn$*_;-XCFY{CAG`x3((7VZh^$ zNij+}X!-B*e#=Gm$QU1j9v?EEOcg*Q#}-Mm!GD{0iie@_C{Z30KY6H~+BNso~xr#Y9rvi<5Tid2N-sDtYYUaM3N&5v0tGS$yQ*<~7e zVE5-$H2@I`YOL#UNNwyi%QxNXwcIY+4Ih7h!b2J+nIx;5E<`M*^~>M3hFmk=oppHd zvaQ~~e84iqkD!wa68D4$W?1B8p}Kr`PzaD3gxpwlDMtW*k`6z-Q$#d@DGny;E%`ti|XY3a(76Sa_)KOqK546S*!gs z=07dzMX#nB1HiZ1JK0u#8!Ke^$QNin!Sp#D-Uo`1DB0K}2}(}wrdpq%U>+YVsq|`? z;jjgwAt7uaHm${=yMEg<);;R#ZiE~?>kq=6+<(6)N>P<)1 z&FL4&xXfmI^ySs{fXkJYat^n89=&&Z8#x=lppkdqPB(7KuHr{7_^?8sej4YxNB= zPchdbcn4z)En59La*B)4qLSYylSx{4n=8oJ?+&RWJ{faYGHf{-;Pd6_u%_YHepYr6 z8zi#iEfz?s$TWMBPRo7%V}i9f0{>)fjnpSBFndIL(0+oKo6VD@18E&*;XgS{W3e@r zVAa$L^BDbdIZtT6EzP(%vF*t>l=z1?q`I_$YYxF1L ziSu4Ou%yBCJ?@p4fOE?!_H)(BNp`_I|DR-5dU7CWCEMgsA!4;=Fix_7q>m%OMUF{ z9G9tY9=QMZ)G*~!S>#%eYJ2fm5tvP>7>|Mkk_(IRTbQxRR=>PFfty#-=F1j$C(23} z!Y1fvRkixrPK*J3`{rJ5FWLd0xQGi>Q2^a`4KE;bgNnx6b-&`d#ecqKyMYYKUL_9Z zhJ3?|g2hQ!5R~F9$7guD~&ZbClI<#-FHPLw%SpJ)kv9u2BY5{DzA6i z0rn?FM6Ff_<*I6ijAz#K*2vZeRz{pY1z%{3!d~+4SvEzX^EKA>Ib4$88kcQvB{>=( zCnQCKDFZv`pfy(iw!0b8?)|eQ;VimlsYqe=fXO6eh_XgYaM{dm^R6D7_??BvH%{?? z+iv6T*W=uK7T;F=SkS3vOg(K9b;G4bL@nlDsnZg1&XXL}v7~P;LC>PJdqTT1ctV`C zhg7QI+~_ZGhH`;PdMzLGffGk+MyoZ(C!Va?VVd8hZIAu)Jzs0&rx7zvX0b9i{g&Q7 zsbU?-=U-c#1DHCmE71FHjI*3?85i9|ntAM7JDHV{9kY@R%l28SB*GEsNVocdJ#P!C zj+#dkH+I$tcXkxZ=?C>^O-sH}(x{3m*0d8iKk=Jv{js0WW32enbKqIz z%EWPNZWq#Mb;8S!;}NeLwA(dny1(BhJ#4g-?Um29dm)!&%~@g-f2oG>sZ2FLLpZ*7 zUXvVZgW|7>hV=VlcT8|_)0!}CD?9v-;Kw0CZAX?bmO7Uy<^J6!py_Bre3&V0N;T0! zxhH#~q^ev}-BZ@Mi?H1T@v4l_{OLdn>qS2xwc zm|?WDFTGGmz0)N6UwgFwix)|xxyFJbX0!jZe}sMN{<)RmeT9-tmx01!)P0NvscNDT z$a&OQ^-h$_5&fqnB0Yy;;d8NlEyA2@q6A_|{4 zyh!@&T;~uV+RZa_PUr>J^IdmiQK)&u8gk1>c?g$7@|~cU*N>dBkOC~)^sA|eeY#Tx zsp&8`KX1ab9&AxPaz9UBxjy`_dyX+ijlyGfT0qXp5~^&h-B{5SW^`D*Zt4}1uoGkq)-uV*AxSG*~o^ijT47bm5Nf8+`( zBWH};)CFSc6F~oz*U~_KU$ET)Cvf4$&v)P9vA3wtoerr1 zdv2LEh*$?E#?Z=AQE}wQ6v5G6Ho!PNmX@iac(`;%=Ky1 zGioSoos=er|MND+WXF5SqD0#@lSA^?<=m*yO!ldNzSMcQ_GsuUmnY~-6+(}Ho+JIw=3jp2_<#A#HNvT)rkwJ^ zPqvLumIORkTQTjtDU|&R*jPS#+#w5_J=vvwwv$@_TqBj?%8Sh0 zUTc^vS`|9_4bQfCk|f=3{`!FnRG`E!?O&6$vFYontS&aVu~6*5>bzOIzF($hmTAA8 zUIb~cvN>5!#-`3C6~6Psr7{ z#BWG%*z^FG{d@MVo`*06`G6kDt6OcR?-J(l?-+ZOBL3S64R)Qem6WCt+EFC;m7lOI zO5u2Vc)4;QEjfSzVV;%nQj+M4U%qAzlPw0slB5R!A5CY!LVMp@)_&9VZpad5AIhdR zEBAF6vuP-OU>t%7;gHJK1B%~S%P)DVCz3Qdhk-R{9PTvuys+zVGXHDO|8vjZ7jDnV zy#dEM+!e{tc*D-W+FCy>!0}twJ-G|z)}ocYbY#QY??)-bUiv^7!~!)64|2Jd}~y>lWGg%OXNI9n6o@* zrLRy(T$axX5iVLstT87<#Yaeus|)1{#JKXztm;L~o#Ey)4(-cBis{Dyq>2;hj>VN? z&s#MVWV6@9+JCr2q^nY8_)NaGUT=h3NJ7nLi_;tIxM{GFB6<*g#>dQT9^AFxG!j(v z`!o{CQZN9L=T%d5-gYNv&XIta5VV4&*FqhBkH>DwZE-ALmx>@~N=W}lS{{EyC&%)b_J zUx#+{LR_w4U6>k>Olto;4^)VS zZ~*5A2D@ixAys=XRj|GGJ{>}D=`Lh4^52RP**h6{N-jMUPR`peWPXoC1yvZU=QxVS z6aUV|79Yv(vBu6`)r%er-t^Wx0vcIHfTy}IrdYSG+*+(Ni~@ojcvREftbfUJ5g6Sc z&FvJwJTvh>yzM?2H&JSg{|+QGl9tWW*Jjrm$@P2vyA$E+YxiJ{MWO%Eo9#}L#lV|@rqsV<%&_B6& zotAyvAg6J(+&klFf6?T<)IHYA2hwSZ7nz(I&hG$D8C_q_zx@14VMq58nDY?T`2@?T zp}Q8^P zqS@woLETFB@p!W$5cPT*Qe3o@M8ZJbM}S8pSJC5gk_^&+*Y-6C!?3|rk6=%c;M!NF z)hVu9ZKD0xKU;|3domDz&CC$|abhd8^<1~9e(*wGolak_E;y`bE_pi-A6^a zEe8=o{UNicnST@6PP_LQ9Qg2$tF?a>+qVQRIcU7iD7rS%_dQOq>SkW?(2j5Ey8d?~ z3%oPNa|B1<4=l}rC7U=DT;gnEMS7wuHP)G3Eh=H};E%5FNH{z~BS~f#@0**Uf@U5IQ0 zC6DQE+rrb3!xGxie^=F`MxzGk#4^!eWwN{9cI(sG4MbWWTo>>8c>YCk;fux}-5)tc zrFdVLf-|g99TfwnpvAwZde%ni#Kj3)D|nxu`dgnwGiVUTfYq);q9D32|YAJS(s&p*o-3aI%$O8N`|*XikO5CxcnnAy_$Nm z&=*#`ina<^6qJ}(!>jpji(aJVz_Q&dU8sM;R_>143cPg2$d0rrSKgC)FoAhDMB@SB zRxV@-`!yvrtUdy8$?h^vvUPk*5i#JUZ20~XBs04jBqI|se6;-hj3*9P$gIhtVrNLJ zVaJWxdku%wI&0^H#8)R9vM`M(6{C25+9%ad3F>L<3}9pnuf{@OKG$ zvl_H5i`aGeMPkbG&@ln0tA&1ywrN-K)2~^GW6JE%UK3-eX^-Kx=^>UMA@a5dADk?E z{|zUOIe+Fx>OIFT!&#-0c~#w?`Ua5v>9z4WYr^vd6#+*_AWsNEpMR`;F3F=Y`>-W` zY}tPinge)_H^w}2nd!}p2xxBhLZZWDQBDbp8c5JXWdA+UJHsrdva`(HcS4#qK7ub^ zIC50oXo&lIRd%;b-&48NsEpXrC9*#<;!HQf6m%M7!86Ef75|-UASaBEk#W^XG%*=X zdSo6`%`m9s@CeCYu=TkCi+%U;d8Z`d1^{N?3zjPPHh<3`DP+Aff zrLvN3@Ehb*eKJO@%z&w4rGwbgFHU2Gg~O@4*7XxirUauYuAM`w$E$4gV_@QZqR|C5 zyT;Y|amo+OlhDGqhAU2vuiq+(s^BeUZ!&n!5s)a}c(OjMHN(YCxnTS-xA65&lzZyKHTtN5y&MNyX6q z*`@iUqpJ!1ptwt*Z{}-&At>bUK-!UKXL3GA3_47r_Zx4TZd);+>fXBnaO-u=?NJpN zuN=D1Wn&eOA)k;`o5S{u0wzH|79swGO6NEts5~=`APHU!SXxP2+Z|%|4&~kCA6qyT zu+GEOVT~jXw-h0NW^r2(p&kLLxf_j7#i)w6xCcrU$gHS4KN#18^2Z9Am zA`$8g;>*l-u&K|eD4;=xA6~;iB3Lah(SgnP>?kdJMM#Zcb!sPMBX1?xgSxYB@Vd7he)xxJmvXy5ngumRIxsuOQ3PjVty;RL*+Ek6T zYc&#M9q;9=ZEcM86ipWr0~}p}7gs&KV%p_GNTf=5#A-o5{5l*5lzf&iGccwo!|bleKm`_Jd6G2~r?>xY%HE1|DF z>ExGAF;(GnOlB{Sxe^?T<{Wf1_N~S(ArbraHPxeMAU#U*#=|72S$J}jS1Is>KlDsX z7~qdx@gC7uZLAHTDEo}{gzEZulc5qYbnPpU!Y$Q*@1n83+Gah)HCJE(GA0&M zz8U5gm9%nZ^SX(s|IFEA;YV$CO>~6~lnv3FkM}}DnVsheiLo>vWqtm$)+Sni zI7E_8kDz=OPdM5 z*9IW4fbJY?&O*Xq#@%-RF~-X8vCN6Fa%alMZqBI-i95SehYrD9+crWwD~)`S3~w+j zR`;!v;D^Jg+Z{vCgd9ez!gCmM1-8^{CL;LPB?bKHo{Nl_3DvEhEUAIOMh?$a_llYB z7MAerNj6^N_&e6bNN}Jgl&G}>8nvp%PDU3tS%HxiK4YnrcWeH5H`bKz)hTzOULSY-1UG+QFs-tmhu`OQ}8Ti;cYMcA>~TG^dwsc5j%dOMD~#nYo-aF=1ngwE;tlEL?xq;< zaDCA4_c3L`K~{GjG;Ur`eXR)&u)>-xBjRj_Wq>7y_Ebw!M=kwkkTgasNBX4|lgrJt zBwoqVc6t6lT%UG{FTQoUIK%wouAnOzU|2@Va7YWxaLQX#uN5TY?zo#ff^*Jdpuh0l zSs()}v7te1c9^+7^uFGxG?ik<Vbo><(_GJhiPKveDKAl<-aiOdFLxa`<(n#>4vtgL`AAi(gQ;*#S*iP#47n?n z>u|~N)rkUo_{xkW&qxwJL_wvEA@3nc{8fGV7zt<+>0;W;^yoS$cz}W7%1G_M8*`WT zRQqxwrlP3I9nBruDha zF%wT(0`=$)ynQg2{~0m4K#E+VWWQT2& zu=%g9xG(EZ>(GX-=_|fJQK8XY1pO{SnFkHr41rE7Wn?nxMf=PHM6u-OT3sit;E2 ztM+=h+n2_4yT0BFkT-3sJLaqNH8o2%;D0+??VAs*Q$}>i2Eafh%9nT*emScAU#*IN=y8sHZHi zQ@ML~^{4|r1tLa{pMi=N+4{~I2hi>;71^w24>!vo*xhoks&`DtNtH5cu(O^Jzh%r zWSl$%m$gxYT7RVN8>s*)WlW@OnKa2M$GEcc?mC1i;`_9;eM(4h|J|6R7^Z@=iIf_OOKo?bCs8wNDA;(4+{i^a2XEACuV`cho zy+Q^kGU~NoZ(`impB~M=O_1|@n9y`p6ton|_1b)Dt;JO*RdYOJB_}5$S0rai-G9o= z=O`yT*6*5y)pIGe=XDYgzSorq&Hh*+x7nfq$!&l>cjR`aGU35={`GLfgOkli#+wUm zCVZ@1MVq1L=}!;rHd=N216CiwHX2jUHREPFLTC5r-t8e_uF^#N*~MX=NFhqY%dIHc z`9(*&S6?>oIOdu6*7O3^epNF=oM50#i_-WM$M_ELOM)~^_El>SdvxxS{>v}MN=u)i z0qz>ZNfHspa@VnQKYPV^Zt}u<%Gxd3M=Dj-vCIUcXLEB&gu0G?!g_gE*vI4?=ho5F zVfpaC4U{zMHA9VknJi^UV7L$K#~X^ZpPOb8&wP-io=~!IQd? z2--+x`Un4k6x%41JsP1Ewi7+52*PAv5k6)-6pUIh==Y=o%1%c^6s#ubvyB1MVm$1L zR(3a_{=RpfnpXjdojh!g=w}4u3;>j9tRx9xv+%R{u&5?_RT50}!{4m^wsm^6{unXe<$WJ#;oOP5mG z7zLol%G=55FH@;__nH);ZxzoEE4kd-cX3HaIWoahzD!kZSM{w@ehZO;E@PR`rCk@~ zT-JtG>M$~g`GA*hcX;ZnmD?-Gl`{9nzl-=p8`|IGyg9dSoz6Mw=XdXmi=eu2WgOP~ z5)j-nWuGoaEJr5t@zZxi!&14#Q~CdESOef__E>AUmXJ-YWEk{>^%Nb0lUx?HZBcroGD3~x6yu(#06SW{$;!=JY%vvqBOa|6*H`6@ z_C)=Tr`Q_h@t@QF*`y6IAJ)QLwmF(%`4{KSifpDIR^CM1Hpv46j2!!}`VqH?j{^RH zXiQb_!=%#h{1iMSE4=*nQ0>GLE7?w;YNt_^PFl@2@)BnDsmB-AVig53KH44fSI@fK zx1UTtcyF4f6D)9ohkcLMZU;7`IHG&w zP4XwXYen0d@46CzQPnnWQ&*p~2(Ja-zo;Pk*2)=WG1L1vx2kpOjsVYh7&droDEtTN zc<6x%vjdiUk_%dV|crnut4P>zcrs(MFZK z@f=3llgI(!@dd`NkeprLP-CXp*=>{}oJN7ws068N=!y)lR11WCUBf4=Uv_dnNq?od z=v^Y}P^Bt4qtNKWkLN!?BxFjLiu1oEw*>Kl{hxTmZq}Z>z;>y)L?lx@RG#~@#c&H zu)=H%I6#c)?iZ)n(rRQ>^C=G$i+i-3vzF|Pu(*#<+RZ9s)a<4Bm{Lh!(_syB2hJU+ zJk>!=+EpQKHGk^Iw`({X?S7fv^OAN|_@=d@o9RA5LXD>L0%nSPaVk&xL<)ot?g(x^ zQfRF;eb! z-M>Z9L9lC_k`6=1cGnJCke5=wjC8oZi=T2eGhec=0@ZUE%aBpWjigZ^fB_0elI@cE z4qTaLSw*oQm}x#(fFyH~WW!pTbYB}ARXy3BYDtW|yqR1qNfZc1OgSd1SKP8gr3$u{ zSqx&rdn?RtoqF^$LHxHw4QXn2i@l{u)lc^xCJ^{7q@+9_mj8Dyz*aG}{hAI{?aQia zy9TAQ5R{)gQ|&QxNqNzJ39+8_Fv)tTp|l(Z3$q%l_uZ8dH{s{xxGtrUovg6tlE(x@ z80{L}0s5X#1lEOZq6Je7GPxQ)74fm(0IYn>@bSsWB1pXp85=DXHOq0R*RkQWU>xV+ zG^N&n9_9KipP`lqDhIEfpm3vazM1?Y7bJ6TU=mVPx&22sVq( z3?V!8O2_;&Se19o6Gk*gsFmK&9Qz{okC(@Y{0L|={2(jISO}j{*YnScbyzvIK$HH}rBZ^U?qLK*Hu$Fuo z>hapdP^HW;`km>kIC?rnk2?*l4H9ixM0cF7|JQ%gX=4i#O=E?@>zo9vUK*0PhqN6X zyl{G{k)xc|$-j7<9TM%@Ewd7eX`W2h_mbnWg-V;DrpeW^DY#sNhQ_G26Mc(ylCKB} z)4D9{R9kPgkZ5N3D*gUYP?is^c^r?Y1|DBUb@ntcLLY#3w#}(f++<_{EiCAV!)!jG$D9x2E z@|QJMj7BiV;-x&zd|}A-LNq4wzjMgR3ICAdS$wh6+HQT6EG_WVCJ;e$e_PhcmVAxl zNk8^hmy-1Vq(EEmBcEGEIz2CyKAVXat((+*(9=UqdA+qf6mja4ZWo5DE+cfy$9>82 z(yt`blk;ecVW=|`*`=Q|b#Zr)oxbrlL)$b5we6YoIjBfD?zP_=A-j7VUtZ;kb7lf7 zo6>@s_iEgS7g_7U`})+?rs5rHy*tC24u_CdR%u)Fo|MHRChG}))XCXRp}$(fZpvEr z+P^v*2##aHB<@Nv*~tD5QxridZIpbv;z13yT}J|b?r>SEhn<{Yh!Kx2xa|HYlUX=_ zV*v2KwCKw(Z*NO@y{EU$%qm=R2=qwz5%&y^KFr8nNguK^s>D-U(8cw;^W_}NdtNSM zbfc9_y+vX_fTm!XTm84Y^2=C-((sfIH`Nt-6Mo5c!*oBco|F31zJ|MF`Hr9iOR~rc zRT)_b2t$=4R7d^XmddEE0mJL)ljHT05nA#%DyC}N@Gj~pANy1EyP+R1KI;-^4O5ge z0)KY*Dp%&GE+jrnZ2XWBN@^OLAR(KM&||)=``v7^t7kKJ%srcmArg(AoUbSr*YjND zrgX~8%R5E`$@z`nW<=bxGxF6gpGOOAzC^OwGx31!FjtokW8kCZdgcjoq&cUYggO=} ziULwcaQl(C<)ZNvr!>{n-tXH|FS%lOS#?{Z7J2{lX4CyIirp)Gvl~~{CM9cjz)y$# z!R{`T2Eu%a+XD9JE=kG7zLKTAHJb50QVuChd1DB(j5oV2^Y+`%{j`hPzs=4|Ub=++ zdWDlN1gh&y1GD-@QN;PB6K-Y)E6^AjhEzdMEYbd~7LC*#{+6U~b$qEzt=X1USAM`?G*ER@JighkJ3!S|$KKG}aGz#IUU|G8}BmYnUJ>lCVdAa!CV=pdW1s zk+TJl|Ca4|+x&!0#P-=ZyJdLNf)DedRq?sXg4psQ=Zm?OZW>-K3g`%~im~Ow%+YUt zuYA!~dMbBqmzz#BchrThtQ#?QRZoXpvJyZK46cA*H~;lhs-P{I?eLxU<_r8&=MIqF zaIl%0f5B;gT#;?@Y@K!C;ji5qJD0`P>OXT^d~H4!_r>|QiqkS1{&elM)UOQ;oYq@` zZGzfv-a#DrwJMG?ZI{)b)`XxqnOb#sLEx1GxsFJY zhRx2`7}192#=(`H>?brz?XZ6j68Sa?Ac1{RlRkI*Nh#V!K@y%R)j*?fuE(nl*(yvY za1uZex$cMSqfla;Pe9c+EQ67iXj)`MDc!3pE*y^A8M)$IYh83+^-GK<(BlaS5gdvY zcn02V#H#_+P!a$uFYfW2Nc2G;r{dC`%R>bX1G2)yGP?jn4;fj+Jo2_9Njb}Th}*^|4z zDW;p}{O;1L-j6k%-!H^gLgk43wc#Gl>JN$Hf?Ae30S<-ZIfM>VNZ3T#>7|a4i_W@2 z@f|w3lE22Vc>XsTh3SnsGkh8{*KE#t#-tXHW6L?g!Qg zcQu9gwbxBhIZGb)s<6Mj*+UW)zf2F32$%ew9lE|TfgyLTtOrj>VQ7O0VAM z&#H_Xs-~MgSN}oG<&&j1Uvz@PYdzXN{RMw{WwX{1XyM*$P{jClYQv2~Od9QBnh`k< z0=nz$6oyZLivbb-O%(K$qoVW^DJ8dr(C_SO*m!B^hp*PLTR+RB!@nf}c&G21N(CLj z*5u$LNU`G+rf?Cjj4StKfJ|jg;A#E+vDx@k`Ip^GI$if9n_9`gZbjde_Un+L}mONWiZrs&laS8Qtyv#cKP8m3JgI5NVjcn2Q{(kA7#Vm zYi@TqC-WNJZcvm0+v@U;P4)$i$v;<^JAdsWrY;~P+8W_gmSHp2(A=bfG#*0|Yow$= z+e)jYP2B?#Dg%4e{jgO6!K0<+_0wcd@Ml@gpHJfZn;re`{D|ub(4`bO9k+4xD*HK< zhpF`44e30~59W*^dQl0W@a#~l>U4(7+Dct``eDR6oY+K?n|gJ6rS4bM>qzyJl1SO$ z9@CGE)zf>R%?Hrx%}dc!{|cB!UHsj5C;Qh=X*oF?$_WDS=n8UCy?vKHI$&YsV7D?U zH6?6PK5{H2F>(<*fDjNs#2>lWVu%7UbMpN*{U>sllRH(1JZ7Bfd{1A^c|_%Br&wX zRaoqUn`-8XmFj6pSQI3GREf~-X`120OiMGPW)zMYB*Jjcs|1H-GpyBghBGW#`Qtmy zCbRtqDWx!C20m04Y#GF(N-SAP*P<=#@tzbEoV?S793wy*+cu2Ii*AVTFO>jt{0HCo z8hI`y?zRXlt4AgmqM~jpCEN1udI=%pntAA1t4V>)yj6^%v^9p+u$*x1NBW=t&aron z3JIPef?IjzQ151{sPg_39!mFLe|jE?k0B)Fj}ZIY4Myt+7i)hNfN?V2<(}28bP#+` z{!lw8y2l;f`|wP{88E@mweWgmyzMNG->5D%u5b=UKyKf4l~9G@S5bpz+Fl#R|5gT* z18b@&Rau)HPE<^l0#G(jodx4cO?Y9_sX9 zFFuAx3lik0T=3EDAzx2x`W#*AtoWrky)CQUQ+-W~>z%#)0o{!2;hQ|*t z?NJr*!;=ZrP-aajml^9DGzTA;o>6ySJ?&MJ2!5|oF?&u^=Od-Sgm1SoeCu_0vWIYM zhnl%N!NJ|(-dK^KwS>5-PGEp&4;Zgv=KcCKOy*xeF8I+-v&^J)+c{Odd1QY#ot2tK9c3Nvb{Z`1Ty# ztj!078(Y2dH*b}pMR|?ybf)bkc;;}_WB~h_8j$@-FV1ndj%RU-dtl$hoagxC<`8JI zs#=*SpSpMHRaSpObt?Tp{K0Ywi01m`JbKY6XL?yzHBTy6&8$iCS*50oR5hx@yow8jpm=7@rcq#ot%(Pj zci*{Mo)G(i1RQ(MsKk#L<(xE7_cKr*1v}@&N=3?gwjfSE=x1{!@5jv44X*y5qVxVsdjI=y z-*Y;h((IJEHElR@?>#zM4lq%1?;NOrxD~hORGQmF!Q3h0LL>*M;3#MA#FeO+JNL?! zpg`-URit+`|QpQjd}@7v-)-6j9hTacT5_i!HIe7H;B!e|M)G> zi7QP6n5muHu>hs5nw!S7z5=mBr!9lS;2!jvePU-?5{3@B%$}bL(tZy8+bBXM1LC_- z$hpQ{g`&_oCVnor-d3>ZXl$y8%5#z*d0o!T=Kz8C!pTzdkf9#v# z>_cChCv|EVyhbS5{!~N+Lo25v)o9g+j>?bR5R$t4eK}2I)r+zhx&9AR;!><`qm>1z`gM8zFw*}Bw{lcX^?%AHgA0VY8BpgE7CNp z(@r}N8W_x0oygsp>+3z7nKhxZ~$YV|N!cv97OkIOQb5vI6Se=@#T5?W`fPF@3NhyYJJ+k>LGSKXeHxmtb z2%OY$pDf<9!U+~#g;wT^V9DHRPmUu0Cw$ki5JJ`0|95NTrv4Y%tU!T&sVwn_PX_nC z(baL`Xt^5}DH?;-;izP&0ov)HU<7~|INX5R{Bn?vl$Nl7SehNBQ1uIy<}1g`H-zVo zvt)W5>jNGE%l(RMT7~-jB2Z&PUEd!jF*zY)UD~`X zq*f8pOjY@H3l}Ke>HlLVs0b^O6ZQ6e*}yQGBcn&7OCWW#dYI~+(D?V~4whdpL~o2& z=%>}>!vsgVffDN>k<~I*^-{~xVF0myFH#70adACD16n~023faaTK530QG&hdF>yT; z+)wJt{>AM&7U1A}M(tLD(p@PXq1Iha_h+#E2HfebHS=LR3*@Gxf$4ko?@S~WGF!ZQ z+b>@y?lSy3dU#)Txxnm{mbC;$>!Ipp^^T!^UoCdFTW!_tt+r=c&Hw8B78`F$#JGvQ zHJ;;+d!1Z=;tMK{?57@k6ZaeyKPeNhr?I^b5Zf(`_t}aZ5iEod;HtZhzr`OYS=XM5 z$On^Wzsr9LzRvj~@Ok29n|kg%d^uB_q$SGA+|NsP{0?eUf*T`*ysN5?$j!Z_WbY#@ z6`rYfIh#bF(`MyZ$^4f>`Vv(1a5Tp8p~b{`@ZRf%TiH+-!aASO+xlt#$6G;LEFHR* zHVCYab%uDY%KRHxO?+`nW3vor2zZ4~2AZqYvjibD-^j)-0Pp{9PaZk(|S-C@}2r)P~3l-3ZXsVmb8Y3fCk1Q&S@XGRJkSwX6=nsYHwpk4A7 zUP>G2m#N)QZxU(_+)o+x0hEl>M;EH}gI|>;cg(N3NKOQ=wU49KL75(x1C_F|bElXj z=Lm~{EjTpT+c%@rZW9x2_XQ$6E&RjGb$_-wU8-17%B;#y^tUg%o9W~R;}!p1LHwF!hQRg-n-#r84}{^AMDb9!>nwuII(3Gg@6$$+tjjvqm+4(rlxdd-`nsU z@1fgMGdRK^q1fTkmw&5KKYFzZC^mwkJ>5tXqw`!?;o90~ce4s+xZ%C4o4V5X1$0#o zLTUUt$_{qgBI|W}&<&JF8_v&bDh~@nm~DR*qk33x1Rk|CYh+B?Z8sc+BPkj6ds3== zk(2&YGhr1%^u;mh;3%Dm74c_skht%RDxPjgIlb6q7+OCEuH#ZuZ*-HcqTQS3Wk5mWCc#t~$$HYr86i+)DHjU+ ziRSy7IfnDVitEo5?6+2|xu53mOyr}JwXMOeldEIF#`abiM-cR8T-4L^G@Tz@(h1uW z$Q-~`23U8r_F-7^L#giO6YGjpq8$!an{P+bW_Uhk28sVbMmu>hFe!)Rb#p$8kBJ;b zdgab2nVG&h#^%oh->CW|&(?972PzX24f3YPM~X)KfYUfohc%^ctdHn&|VS{hVZ2Qcg3?bR)7rW4QwrCT^`kV{illtqxsBt{9Wv}Nd(&{#h%`-4y=fENC;r$wo?SyZNTd(n3 zQ1OMO#p_~XJ}@7Xb2FqhVoEcN6cZu|E=2=_#4AQmUT)@;4H7kzXUqYzZ(EN@DHV|h z%tIPy@)_r3tk27FZ;y~E$ih9p&w;phIr56ooi#c-*M#lJ1AelQ?V9x7711}g==^5_ z2z`Cw(bc)O!?#Xb>vKAy<8m9h45uM>qwhfAzLwWWw~L6`NLOGtfTRi(+{_)1-pR2V zuci}dD`_#iYnONP_3aVXpNJLHvs2Fkl!+&xlGKGsliI7aFj=MQu;UGTr48mtorJIGTlgZW=cA1O6w7Czp=8~7-(4*yj*o2&NtjelAZ<@w(-x64!uAt%XB zk{eAHMRpbyOmU^O-R(50nK{*h7*4jOqmrRiUNxTi``W0M%Z1&>#y6-j)U_2%gZxp7}dzAkq@-v6gZ%wJnL$s^^ z$gXfKn@AF^lJ8bmHnkzdhL-a3am`iv#L|5B6rvEx^4^QzIxS$g6pzItdfUZi{l)F< zq}dUX+k;di`H0#->poi89$jC?sIiZ)+CtcUv$-UB-b-PF3|(SVLn`9m{wxAvh1(ioWSHwl*@QIwA!SUx!d21^vG4XcLc$*#lgMI~B{4DSv?Hr% z^4ujfZf>EK5JGMc{NG6|am{~b82Lvg<5sAQm_eP57HNDXaYz!1nPOK(jc-e33 zV2>wO$8~(?D^#j>D8|zM{5lAI0zsvKkcW)Q^`ElGF4@iMDy)xA7*rT37VsL}P5Hd=SQSs$6w|P* z49;VE&a%Ey0bA~qqN<5Hw?iA|Nw}H2@bGJef5uC{#K7{$mc+r`+4{}J9ZlOQ9fDHS zAFy|@%*v6VhuFTBJVyzC(2ilR7}g0NZ5gsSKEpIgrP3!B?MC!UCXS4hI}6>K;P&`; z9Wwu2am|m!i^h%{Q+io_Gr!bgzx+1-qK+3B>P!HaS3V95c$VLF04GY*Mq8K#7i}N9 z;#am+-i^icwgpo)(#ie!lm_d7(G^wudTuN5XHT}aw|45wjmh6qoSq@HM!J)3npCWt zQ>U6tRB!h!sPh(Q41oKn3$rnhetpFbkae<1FLpR*P1DdL>JJJFk zLXtsV4mk^-T`FDjDu>EQod7*`O61JM#=uW&yXhrV%kr89k%e|KF0QLOk!L}L%P?g_ z0PW2)#THsL??vl^5q>yP=QL0-$R_kuB1a_Ic7SO27 zIEhj0TyU{c8)%|cZpk97zwMJp+l04A?bZ;^aEHA;^{hJclG;s&Qk<8Bf5wX4?7;>X zb8XaGVOu)vG17YDB(PJJ;%ydbbb{2z9pZ3{QEwe1?2jD#IXC@2S5y-6N1vhgK=yEz zPj`N=y-?QS38(<7MfS+#d+96WvshZJ5V25kqX@6uS)XBv(!j)y&qM0biZfYKZW=yA z&I==I>zH5DxWOInNS0l$cnp~#baFaB%}OO3Zr0D58#PSu?SJ+p(eWudycgXLMxi?1S-3jK22pw!_c5vU^2`>NGv@OZ2clIJK zayN5TBR#X_TgZveGCV3@1z~#-;C2*#(hqf~m%XjNymxY-cmIPR|0Jz@eaKeq;nHB4 zb9-#gBf*<@3=f2>AAbA6a6RAnqE^TH1fkXTM`Fj6x@CXa*=gxks4nvjfRt%9gtuTv zwWc3d@cA+03j#7C{l^=3bvfnsQwyo)BY4e=++f;KTKtB}liSUz!P>-xR^F2&qul;? zVkjS=kvC6=@ZQiGi$hZIgC!e>({u_|_@&N{5byhuSX&z5ZI_GWsCB-?HwR;Nptgfh zZ6>U~dUf-(35m{VrlKP$CH&Z=GOIbxEpBdnBG9U}C6oFCrCN{R?`##d#nPG;7p+?w zY9gi^!NeEx#mPeH`^f6KINr{d-PTh99>S)!?`2>?GhC`#b{=5IK z6p~~?0_h?C#-(%8hvQiqW96fzv*EWCVd5HJTU$8H5%moSTkWOPheCIGaB<^71l`R+ zDe`noov(=P@*6FM6-|irRaoyM#Tf;;=YG@g6lLG-+1wPA#Smc1qj{ME9ysN`t-eLB z8E;Ihk=0O6s86(Sl%7%n88BxtF@mI!FYT3J2IV&Efl(FGhxY2p6~c=4$}=N|Yy5vd zhrIC1Zf=|H6$JLP#4jD#6cce$K3vUB#rUVfnVzr|ec4km<5xt5{Y1CZ@D!lE#6_tA zpsyh_=sRa*@2o13WyH_^bwhNv)!Jq|sQ+iGHK0QY$@2Gm{ZcJmUTav5Cr0>%b+%P8 zRu&#h5bq1DYnFK27GcS?$XZWYZp{y(Dx;BoSQZ&tqBCa|VGcV-rUnHxAr=sBCQQY{ z2ri{Wm084=K(lgEvd2g~V|X@Q_JcJfO*ezb{Y(#Q=xUL&EH7$LR?AkS>>jU;p@3ig zTMT7v#r5_}{=3p5c{&Rh-W^*v&DTixai|=wGDk8gJfz7j6^e4j8!g+ga?SXk+We+2 zB7>b14Sym0x#@{qiL5nVl_s#D$<+3ZhM11Qz)e07xGfn-CbGvlRxsBHX1?`#& z6hVek?asv9UHI-KAtr(rP;cxJ%ATu(9yHSIPL!%47I8#fzSA-9=rP9g5T2V$y-l3!;Grs7$$Jxu_8#%HgXN$P|+Q;(tEGo3H{BLo5k#z(oocgou?H~GztEQA? z6uKc9TMTo`IkGr)o_M)bs9=^{vPlA3&7QaV26`Io&K!M|N)&AOl)c};m-9kFANW4& z*a0u+o!7(v$}vx7E*N5V)JjvxbW_UDo5|bW&aj55Y>E6BrKvB{F((r|udWP2hv^?3V-bQ!bEWjCB}^?G%sUBw zZA7}EspsqObKuZIi;wG#_j;JhWkylapQXa(x;e$oQlP4Qy#WmnwN{VOZDLve(giyd zFpdLOxy}#wjnmN!XWGZe4WH@KH<(KsE`!vgNq2VyN;52SjYp3nw>MC6SI5z^x$2t4 zTr&$CqO0V-vOLH?Rc#O)Rn=s_F}) z{X(4?u( z^mk@%p>ggaxv>jR!X8J+`it-dtWcrab2d{xmn%D7^ZIvEMk{B0BM695gEex}(692Y zU?lN-!}cAdg#2}%_uWP4!hyJLwSSXQ3+hP77eSl*oQ*)Z!8Ky7(@$%xF^<7F*qD7* zTvoTVi~w$_?DWk+Ja+IcInmLF7i0f?Nfc?KxYhe=PDuJMSJ}$Umx}qx{I7lM+XsE4 zYo1T)CBQy;bHY}YAPMnv%=EEUgT>O~5EdZ1wF9)bZ8IiMK|{}XwpV6Ok_Rm(z*3JEo(VG(qtrQ%B7~;L_Bi=>7vAN@mKE-5*H;dZEGo#Vr};C@?X4}=u@%0 zFth0l%EPlI5sR)omc@#cE;B-}0h`9iQ4n^^qRBnacc{tU7Y;{+f2*{R3uM&K5|KQB zND}ZD^awdS#ZfKWdVhv$fDlZ%@jqMX%i)L+VaKLaj@+33{r^4umpfPF^Sf6~rW6Z8 zw#D1+kBwo#zI_7k!CJHSw4@$fNM(DRAf(R`01PVGyF(Cz1aEOfN9b%p`H@@>*oHj<)T|6W_cS`6tt0!A;nTe(x@(p zjBy_HY2h7YIn`3X$SIVB1LvCaiAPL~ewrn*GUT}4yQ=m6b+^CiIwzSF(9!V+4zHsZ zT8-5rJUpK)gV|yovV^Tce|R1WW9-|6F$#N^L z1oJLnp6ic65_QrFeF3JJ#83wI`~yBzs_K_8ZLlm>8?z!2xHTl<(8pfExcd4(B-+2| zM-SW!+yPd4&olonvX{JVy7MU3gJ-5t(xp+NhZkgKQE@hR*Bq5n2#FFvPVB5yFDk@l zI%_VYn>I-ei`7Gnqcmi_S5g}XToe{^Ne^~Hsp$)9(omlkb12c>w^}dVQ zGpnzmQvkpy79?rgr@CtGs8omeL8m|&eH-sh+o#$sCCNXnN5XvmyYl2b=O}d+zwyUa zYW-(L{qk-}&7+j*!1{pSXdhB+@*?r1+mEdyWrlvKm4Buq^9pjJj^+sYg^R)v2^+6! zcRX-tlH#nyOw=0htN=&hBJcG4i@WzIQjIl~LRL}h%mZvNM1ATN_p%$0X6m?B6s#_2 ze)9onTSfs)ts7CTu86Fz2Oh~=%GKngSjM9wsY+E1MqA-wK07r=Ta<}4>DJM*9&wR> zl7MZ&_pZLowsq~O+(s#i({b_XC2Cczb&rj*W>;GC-eoPu*oQxs(JQlUiaZhnWE|)ne;^Wk?E#lx$zu7Ak9#m0-V{CLNg>|K z$F53;OEQOJEuiH?6Y<_o6{@X&(wM`qZT{Y#awh~F<^E-#dX+N1DRb=Y@cz8_Y80#Z zJGQS}ApK|5Q_I!-F_Rn{G@Jy>+>w-SnTGE>Inc3ftXgd>1S=(rCQ`GAc+7WuM`!)Q zxuSHTH^JJvtDBU9`~@DHzeU2_NwuhUaLOI|3!JYf8AXZMRB$f)if%HM55 zlyngo1b5L`;e>7J1&kaa&LX|2g=umzCZ&$k&F(k|G&wuV%!e)?ih(&dA_n#4E0^dL zfLK+I@j7j-I?cR0^6zn#b(fdR1H{MuXw}jhur;ZEn%eNJhS|?V5&o_V;~U3Qwn7De z^-JDUPdhHr$M)8XRQE{q zX$a0lGx{j(G7Ys`<2JeT4^i*HJYQXJ4a^6MIox9&QWYOtx1$$w zjwu|Yhx1nlh4BKZ*D>@#hGo#ZQ_$z{-`10LyHhv}ODge*-vfi%OU0Gebw}x6C!-4r ziA7!DR?fb!Ckxw4qt*?%M%8W5HKO#k``k(Niq{{MZ_QtpzVIztQAiMW0Us7bwT)C` zzrMb3PhZVV$u%dQWJ_NC+pr1$fbx0rFKw&sNzkQrqj}G0;0Zl1yj&%p0RL%a=SXKD zkL`WuIg(2+Q~~**NzH9O1NNJ*uoqlxt1hnp&W5}ac=w6m=Ms>{(0Ctjnwwdjv5LmD z9#ZFwe3@ND-@3=0U_Nl=(m1i!$XKbB>YdS=NB`CuBx&e^688@JvLHn(!>A12to{Ai zJx*C{MaLwYKc(jnbS4^6xy&KFgax8-V)$gSfd=}t=8>J#7I3EKt#t+?a>xW9#hpQI zX+CV3omS6HyKw?pnTse}G3%&xgl(<_n~@w0?kl(>^mw21_o|!i&uS6bfI59<<+Njz zc>#)?6g^0+YbiLE!zY)IqF@^~KS*%nU=o2iMY}vAs|2;(2=xdBie|x#bnAHR2XYnI zHnDIml&&tPR>kJ%ieLiF@aa_yEO+Xi*BxPtS8%v%ocBTRi|(^ ze5>w6TJ^VzK4a17-1GM4eetI5f%73GZrpTWpfP;Y0?Wf(K?N1MH<&b-~Hf<{P*Bm^3JDeU&a1)JxZ{oW#Y)bX*v48 zD_cK)(BL>SnQ>IV@6av3g4eih=A$!Y`EnVt-Q?+GwcUE}2zHe6b5Aro@CNjI^Ya;g z#n4c#f63hRTB!pfrpa(g#5_Ri&G!FNTqJ(N`V>mn4YH~#EM9LNHQ7%G(R}S;DQ^E% zHWBcEwv@-MJN<G3a76zH&yU*4qJ>n zz-mFG<8geq5i`j=aXcfR=?xEIY+JS z#3P2xu1pPUF`N}5+_kk@-}u(d#^pNU)&lC;wy(BtGckGLi6}@oBg|LE>f86o zs^2H|+5ck6(rm6=-}0#bMRl5NixP@Zgf=xwHk3B_(4~s7cSDv!WS z=UM~sPg*op*O14;7Nj!)B%4SMt5NyX0J(I#lg?1;shaaVv$(0%c}KGUilM4_s%5un za+8EEGXxv#AKq1RY`1?gfoy0-Ez4M}_ra>{kIYaM`|fE%%TinR{tnS~bARK`W=<5l z_B%ARligM}>y=iIG(z6J1M&>un=&Q!zn7#Rrk(SPT(dj}x<_de(1YMMY37CaCDASN zd4*Ln^q^1rM|GL|&sMxou1=0EHfc=DQaG13*%@wysl1tGb)3VtT^6h`G;PO~X^$1d z@Mfcw{?PAH*NbAPXmYZIMAHlS?q=<9k5_{5$@Q5I*gyNlM)s!>TRTCOLAzs*hWQ%P zkm&L;Q$TeM)i?-g4X^xK+1KDFZo*Ed+bL-YN~+|ar}49G#GQ(mQCwdK zY`0_h*%Ip4T;058`^>>$XthnX0aSlbdvw~MiCEvEpQ)XH12*knemdC)Xe(fV43v?+ z^NnCHWfM*qyZ-wAvzO`X3i>n!{REMrIh6%~92IN0?DbDfzHRgY&=y2%^co zluB^JHCVO58OxGIMRxyNHjw($)G^&IU&UAH#i)35Zf}FFof0xe(K}bb2V8ZtNV1X> zE3V2vjfdhz=)V{Y*wI>$qsBWNNWsb>6poGkAG&;vBWd{;0$iz48 zELYYy%KsEz1<)$j0|WIDeBX_g~*KJL>7gC}2hiQ<1RzooA%JE|5u$^Ym~ zN&cxvI+WY+iJ53CmZF?X*t1WP=K>PxiPMya`3W-|wjMUET8 zSSIoyb2%wlbWud9bZfZ(F%>#kIXBmgz>-Ghw(?;LNAJuXnlTt6dYit|O7|rkZanNR za$9u1-Cx;SwlH^_VS7TR^$j~GnNN_|EQ4D#L7_h1NiE#wK)1SvrbL=v++VdFvgzJp z*ErfQWK=T_$N>I@ZC9Ch6#frtGpXclUR1ehpCL_mpZYL&I$}WJrHNtm@0ar4Nf4 zcqskGoSuva837$Us8^_B4~A$223D8>b$()RIo?B==;!Csw1y#A1J=^>puM(#BefTE zpKP+GAf5McLuoh$oSQFubnDMpnvGAh5!?E-q9dRZZk$Ixq7f@&l&! z@=@Qu!TS|8bW&(@_pQ9$Orn640Lr1?=*$qf50bB=TOmCw;`)oM`L!%ouo4^&_c7JV z5Rk>n{Ci|;V+zboz&?u}#h%!A`~y_nzS7a59i8fi!R5Dj%@ zi1Cno|NgnLw^T07l0*JIZ?|8b8wpr(ravd;@)$e%tJGP-xw*qMG?C>Ee}!?QrQh3H z9N0XiY)}oyTQc?#%e5mmC~LH5kV?>z3iUPL6G}O3asBU#<*q0dL0rGPfq<5rOfE!x z)-KVwIV|?}$jcvQYOhR4rQ%uH1iMO%NRE8)N35;3UEz5E^uq3GbIF`k3t2(F><^Yi-#=fIt}T7Xg5@n-f>rr|S+ZJ9F&-0mAc4=UD6yU5kLI-q`q6Hd>B+USY{uB0 z45&jzlnovtKg~^VbkglYlR5L*HMMto(@l*x|T2Oo>ZPpDf5}IJqB778D(Ku^3`-gnsOsQCe;NX?dlHR z9;KapCPez|b?QjK9RbmwLz1wza1Cu7IfK2b&S6%|P6QP%s5zaGGEwRYi6HwQQLSuP z%2pVU_M9BwTUTU#O{{nwr+Vk6rP^8jUVV|oJN)nseLp-ODox807}yL6%FOj#YnD!6 z7SMNIRct*1Nk~75Gu9PoCgv75NSELs#An%KqI;8_lKsX;-Fy?+h5m2SA4a8Rl-*O? zrB(Vy#-+cHcn3{KL`1+PAu$qkhCE-%b0e{W&4IZ*kHftgYYBr{Ub-kGNLHP8Rfma|wDP62EF#?t3;Koz53Mweg*vz! zxJ~PYyiqG={vuP?EFkjocfN=wioePDa`iC+r|A~!p05+wbUCE%JicdRmw9*j&1jC_ zsNsH>Plnyk5$%Pl%qT4xH#?k$87L2O%j0xA$IQxVE(EndD;Bc6ZfE^^+ayZ~GwgZO z$1@`3?|PJB*fl+<$bPY55?iu)0-ws`H+94sd(mH8UY1OSOywIF8C%g3vM-@|F-pal z-XnXI0v$5TYnm--ndL`~&Vu?8R;?i+`rZtoRHQjPhLdjJS*Vd#A4XepRmW%srFywN ze@vEKsj}}^TJaD$9|$pu3!dcxf{*--wIAjpSE7h9E5I{6eMIitw~^9qyk7S}5QTTA@kfr>J&o0fbxt)`7`Tx*&w=;(+Pp#gzd78Nh*^0-XsB zQu&z%30k;t>hIV~Iu)A?mNsy;qn~o6iM?A6JaP+IuNnIBPzOnW@u~mGFZsZxq~L`{ z-qc|(~r@hBrDGh)pOEp9;&S055Ui|a%C3N`3= zLl9kMEyq@MP2El>M*8;5o3^LSAHpk~gJgfJzn`RpDP7>ixSKEEm2e+CFl6~>XFrrV zC3ui+!ufbI@J-p~A{dH?<6o^LA;O$VTY^rL@0tI#8Z|f>_(6mka3rEvPur5Bj2k7V zyay`D(1zC05aW_cF$;_&B`*$KJ zR>rQ5z`L)f9;nyvz8F-!okNYfM+mG=6-^ZfCcBBwM=%r;DGn3Hvr6Nb9qh4p_D#ig zzKBA|?a*7iVCbaVU^+)9CM;p#Koze>q@&V+=L^H6QqgoDW!aB8A7>1yy@CIh?_u-iInN7lQ%|M-2xK!;2Og)ku$lD zD>XDnI=TI>cR-yr$gI8bCPHGj#|2`StS+IyZJo>x2m<{){73s#=o8lVOX0d?p3IL}UYPx0>d>~~>;SA>YGF>L= z$W5Ik@J}p_cj=oa+t((7)^z5g3!vv!nbee9{dT)w0h^37?o$ELTh*LglMX<}A;hgFWC8xWMQ zzs__@;75(>CxcL@YY{y^g#L(pz9*9;fXp#T@Tq^I%x*5w4h@P8icPNRg~E2&eL}3* z4R9R?HDMwLtdf@<>Hcw1ZO7Hn%X6@B~FSMnkFF!-4_quH@C@l z>6sHS7cP>TUFE|mfQd?WxufmOOnjej6RQ0f z6ty$trf1WKW;X=-vi@xHO}vBEvXnae>y@#^#mvXXN}$Y>fi)1&59*9R(>B-Y-9_`E znSn~^d_`{qkCjhpzi|c%X*G%P$?wle+j5x?+uH1E*=oTQ-&;l(Cp*76VAsDPf%|y& z_lvg-#&dq*D<9SEnoW{s#NYZk(|M$%0Yh_Jqzo*7%qRugORjHIA-9sfmdidjc8Rgn z|5$+bK_${Fcz~I1M@{aFV)U7Y@UzMv|IseKj(OBXzL-ez4Qq@&ll^6nW&7bU%=`O5 z-(kRBMo0IL@pFg<2yEP?M_uEL8Ye|g+WS_n;dW%VG{=)(gDFrQs~O~eVa~_N z+dOeYgN%bu4nAq28gDJ$L8hOeo;E;mnt51 zRO=|D=hN_YL_c~2Fj%JP?3i`zn8S?k^8-qgXP=nnc+Opw|D9KtMdQo;fVL0-?<`W{ zVdE#r>3N$xP}#+-FOzU@w)!XKe02IWXxh*AhhjX6Q`QZR>WB)#_%+-O+;wGXP)@sCQsCSmQVn;P7VpNi`9H`|p zyV|U@5@K(8w@&FENfWclo963cGKw1 zSps3RVcG!BB7-d_dJ2hnv_w1VxZC_C8Ta<~JIjW&%&n`p!)6~+ew%)(sVgq5`P&RF zbA8M_MbI6UG%__!AVyF3PL~of+8gMAt>TC&rwgm2@XPASM$|Q#Zyj;ons@u-F9WQX zZ}nOH9!s}=XY=9o1;ip#sA*7JIAbaZv^RdZf$%A)>Q)3rws}r$&=~eRYlBz`2R4R# zZ+s$oq$S@0{}7wyPrh$+{s9seFt3p~I02Mdmq@&FoOOk&t%mXA_Q7^@s{w6=0fDL+JPEda6^*7P zc?jR8cW2pOX6!M^f4l@XXy3FZRektTPB#u*$E*)J{v=aRMFPcXuY3Sh0#)gZaWozs zPT=XsN9jLLwQ;klcD?xSJX;^C$L}J1m*22QNPPIAk_ez93S%1|791Yzaaz2P=MW)z zHoRbM7i2`WD?d5u^J&v^v?Q##Ad2hle*bw{U_2H6dH$z4x!YO6eyrdvu(S*SAm zPl4yIWIpz8^_Pn*Bz}i+4ri%C{Tw9J`|_S|7|zF;=GRm$h!zJEC1$=Ri<9^Q8QA4I z%rqdPw@Y#^D4;;->*a%!ogUWRSs$6^XD=sf*R3=Z2vsV~26SrxsZmgNw}-PR?lHR_ zW$b(fV~uWpZ?tVVxf$vyIj9y_E%dlE0{FAMJzt_ydNfZi2-2%M$T{eNt0v&>G+Uhu zhw31iE?izk;R<%j=6r zc1>Zj)kU^UC`4(&aRGF+{ures-*JsOdXx-UXbYcDz1r94xuGl<&}D@Y1!vWFFT$CI zL3yBD+`R>{C-~813)hN~<|FG-Nj_$q_!Rm-uV0We9F(Y9sA+XPosu$+F$Sfi)#F~sQ}>5PW*D|CNF&NGk*&9e}IYQ#A7h* zSNLU%@1EU7-_d#R_$9Q=w8b_5k6{rF2!4V~DNfLx?$b96KQzMEMQjpE>YtAtr8-&-V_PWN zM~1yWa1;?V zGIc{0uIal`X6bnnwFAB>A%3>x<4rcgqBTW?Pt!Mt%z0;1pML+XHR8CSdi_^*mKC{B zcH>!b4tflW(8`J;t!zwN(m3EPKDglIT{_%?vHmn-Wd?0GabT zfiXOlZfr|JFrUSJcy$3a;Kxv6V|JfhQeBFwZ=58v?gX6$*ICt5_zhft`^VCK*~%h# zttr_)^Bq9#Wo7c8jMXa=`8rWi*w=NJ_FlQjQ6yYCs5NUeOWYzcd1p`Uj6Bk9yiG*n zjwg;ojNEnuFIfm@_tx9^w2)$H(L()*ve1cp-1-@||L)*M=f|bIH+Q6eZdtJ-d>`^1 z)^>?x^ef3;!zYP-kNI2S zkF?iI{$=r4X>k&-uQ07yAVxaodtTcvXv==lWGl_BGI|4Omc4W#Jnr-bz1G_ zC~rqUzvWA>@9X15+22L}&6kpaXh9^P;5eBE(lSPH+|fXEY*pRPV8B&NZCK4IW?Fq* z!|2AFZWq5s4cV#Tjet&YkGQL3N4gpf3r0(1a*FE>)sk!<)0XhRT>iWA=iPIMy<2b3 zRL-SOZs1OQ9x<^73{hv^lhy9KYZ{|#m%KC`b{Iu_Css>-K6itad-wP)Y0JEeQjiuc zO^PxC<(lr2v+!#g0^ET!%@`rb{4KL|52pt&12!kQ<32Sj`SVn15K=nkmz_#AQo=c5 zFFzhKg}yxBzK=U2tnxT2%o5*hSR8>&3m02uJZu@m~myYckC(Y4r*(>Tk5gk7{rIaJ3dCWS$e(FKR znpD#|`>&UQ{fXdLl^b?HFG_rY0U0_A7q45TBCSuv&MYGD?H#u>rI{!F9dQ3W{`P{a zd)KhR3kYr}T1H3Vopb2?9NWe(%kfmwt@4^@NT}3}unY8&qun+Sm!v&jR(|$ZTMgVJ z40#ul-S(GMzwki7%^uFg_7&SVjuPyBwN=rwH{QQocafxg>~!(msl4N#5@v!T2s-&V ze~r{!kQ)78XOp0+TBdxqN?=0Qa+tON*I##y=&lmxM@kYDG}eUEne|mAHjyAPCO46C z!y?#9Fu0=sg3q@^(7lg~V>;*u_0c=I{iOSDd?>%aZ&d>Skz-{mZX4(xldwpvAA^m( zZTa&S9X zYQrpM{Dsq{O1ajhx~K539S_iCMSlE zM%~Up*oE9E6N3@&pC^l1#-q*h@UrXmQC!nrfNhZm5&~q?3f<-ph0Xd^+k#rc_b@V5 zd&(SR8`dd69?L&qLA&*9-T5p*hsVp;&j3>qKopEUZD@4pph`wCl?_*8o23yEFE z+m1KXhbGp731%cgn{IQo2J-lR_0VhU6J5Scez>K*6BohMz46V)Yuyw zAsLI>>2EApfx3yBGqH)qJu7dK;rspqyh&Ydm9GBTs}l;H7kRbim6_$;uhZH<=0~ZP zA|lAa8)BXiu}YN=e=w7O$jye4u%V<36}Z4BQ;gQs{<|XV=0mh66)n>oL#@AGHX%z- z`1QU9E&6%bchP=Egwz`a)O>bLND~zi+~(h7V2!Dxp<;3K)(j{{f)BC?xY1oWI6v_04QkB7L;YC-hDKu-+uykKV!d$k zY?H>?3gf4v=$^byWyXl=SdA_vlza36gW6O~_dy2D2z!CA+Cf>jdw9}gC#LBC3MHAv zu4_jBZOr$!eWrFv6>fkO5pJ)~``IufBBy`A@zYoIswR9`wCeEYUsv_fu(K8ud4tFK z-<~qKhfqrc%hpK$-@!NA_t(sVmOq^-bHsnyGOAHRN^wrz_R`#Y{V!nrKKd)AkN6Aq@Fu`b|oNry&5MrS&Bm^UGvBpk1 z2#=Ip3-8hH1TF_$moOcI(+xvVD`@{ce`FX-KW#GVkp) zdA&5*27xP&FV``jjHX?+bAP5EG|_W8*zsv%cjhug5UM})gNQwC{pyoUf{}Wj)<^K- zyzcE`sCD*yiGvzhx)ZwkphtGO{Fdhpqu{OiG%~i4>w=a2 z3A}NHD_9`37MHy|i3f`&HKZ$qz{P-4)T#B=uW}|i2>}N=mxEJm*`C{Rsp*dWsIsLP zoZ_I8-tY@XWmTV3!&OEH>YoFZ%C9Q_P9+Ctc6l*_YeXFX$N&@4UGL#V21tMb?+t%~ z@z|b+Y1Z=@FRH02q|ZcEEC-Qf5wMmK>|AU3R%wc9!JIk&U= zcQX@MT%U%H6bzJ^Z(mXuR%s3zI<~O~>WXbbDl<_<7kt0Z)A@~N+i#lzINg7U7WQ1A zY}WDe#|ykKxq6V}OY*$%%ZbE1p$Yr*mkX32WCZeI42y=%S6EA(sd6jZYCsgl z1kM+NyWm5{sK#_Ph5Vs_xIIrrsQejYe$2psf%JKg?A zEre{K5i5`Np8x@MZ&?__f*8q*{taUr)^#loWtTB#T z{8T!#r+3Ue-fTS?O8!|RLBB}SdWSiqiyZRre=kNlMTIeb{Zy}Fgs8pxS7xNZHDgp# zPBLxyk$j9=w4Hr;gpQPs?!XaokzGVUM6O)gbR6@LA2V4VU9YwaRJx$$2>H^(re9BJVeFGwJmehsF~?(B#-iYa{QPOQ7Cgy+Eh6-9u^7L$#Ad9|LD(y=uwm z_Y^d?*;b=}3`uzu6y_hDi_rV#bZrR#J~u9~IEuw#As(tsWH^%#c4kWJ5F3TkTPE5C z%Zd;|#H+RqQ}0&iDN6yU1KSDVcRa@(1Tt6dnw!WU8oQA!Hn6qG02nOdf(@N5g_RU- z@sJ)drq=9F{tJ9xljOqqU@+|JmI{(Iy6E%q{pj~Mg%(-!6D3w?l49Ip_pqo72f7^Tv{QJH+K;{+yga zwYBxy{d2}zTI>N2kJcFAMCA;$_3kh{#1w_WUy913sH#lBm^`ByYG4IKmKarm^>hr1^IxxM8oVI&}9<*>_PXQwZ? zd{q8_PpuPgq)R`(^}kK2t(zCkNi6GZ9|av=3(}k4_@|FO+U|!KFpAIcBu<#m&J3<7 z>E8%9Kz(X-f7T3V7Y!uJP|Agt|G^KbNOWV<>2xfETb-N!<@oHn9zLKL5sEF$TTBl! zw?%~NPWLX#zYx8jf+{wID;E7ujO9Jrz+t#Wm6Tp%uLREQZNY-{i+!nhJ83ntW*v3g zRv%!>Gw;_nM#87f9efqm6^A~VIPPtI&Q{P_+bT2)TPgLQ^J7dWD%IVv)oznog^n#R z-;`dz^6mwV{?4?C9%EkMz!GT7H=o~$k|$<8K~2|HCm19@cEvtPnyobsLY%I?1K@kZ zXBr&3Rb2WwWPZTEWTPqcZn7_8V?KyU8PX@0uZC%a2Lyk`;Wab{5&^z%P;y>yVvg~M zDa#sC-O$%mm78ehYGi#pdF@QYs2&IrIAU8f0xS&z>J=)JprD%hS&k$_!br*hU9V8V z{`HWkR$^*dR46tt>OVt>PEJlis0OlAXr1pKnzx#Qr7rkd`p|dF%^eScqy@4-1gy7p z{H4HNez4kNGu>1_JGa!ZOtq`kd(5sN*UJU?52gUC{Mg_;JQ_@l($)0?bm$J0#Wlh+ zTv>k@|NS<;1Voea>qhzccGyeL1gwzSbxIVwSC%Du(k=pZJlfO+R87qj6(GPddj3$# z;1H+xdz&8rx?>#rpV&Ww0*XhaC@+U2Z&WdGuy@g@pPQF+2=7hm%lX{}t(r?Qb1VtL zq>|miHMs}EC>zUPvN&NZl?cueFj)0~(J&l-gn@UR9$bBA!o(y&lT7wnj8N`hh zpB_@f>2ep;hs6z15k+6`b$DI$;hs&>4S`$vhAT=4;b#Zt%?VdZEnE6aa%273`vQhY zIV%ryk85-q7E#?z#0`FR{xz}T+2|oa?11oDNBQU6hdQjp=G3puxaMb3sx(>SiJWb0 z+>6|BT~WiRtwr}~ifBM|sZh{Ogu!MWdycJCW7nn1Qpq zViT`e5P5uj-UQ>&rUVp}Eg>b-HR#J;A+LSq5}Iuzj3DiDQNkRr0a*o2g|pVbXb_wNDxE)+kdT zVR0SeiOY0tH;h)73Gr$eOK&m@`w`A}@F2Jv#GpjDnWuCM1y*|0MBakc*q{W}X;D2r zQOsE*lx-ACpzEW$Ik(C*)?2W#Ms`Z4`;PzmNOb~F;MsBTwWBj=wD*c+eX-iBfPO#2n6 z7{!%eR7wyB{ek2m_@H#(M5#?pkX7VCN42fkd@4tIIX!%lY4YlwYH$Lua|4=q(@ef) zGFJ}dzi0{wn72APCOtP;2nYD3%zz?y!9ltIJDGrlRNu8i%43R6F}P@3Su^Zl1AyGF zMDn(#VI)gK+&<{Zjf9)vCdb>g49d@pI?X&H7|qxrAW^cCO|?lx?BHMdl`P6t$_A|B z^06g@XRZ43N$uTE&KcqCK2@Pn)ceLFS4zmh!#o=K5b~~ib{Cg^4+(Kw$u77enQd=m z)ofi*#2oD_nmXumlsD}QpZ}WXkxoys=>4iSVprp2sUhg zuW#p=6JKPVDj^TO=YK?)JyA8L(f>R3X)C7%qje?rzf+eEQ9%=3^7g%vl6$w?<)?e6 z2D9&93C=OzOqM(8BINRoekqQ~9`CHBVrO_^(J?STZMx_YPZr5d(?RKO2RnFD8C&S6 zSZ6b9bmvcs zYH+1!m7^iIu&^=pg763H`kaUiF0mEe$t$~Bt~034tK21&6eQN%&BI@Njx;@5JYhQf zwBl3yZU2ykhgp;CzLlmGDR$`&bi9PxV_IhPa6NN2u$smMI>OqG#&)?4T8uIny<2so zqJywd757^7ZXde;Dc|S7_aWbH$n}<~!m&o!bEA^??^Wf(m&j%Zrt!t?Z{8yH9jx5W zG1V55``L_ewZRT%2wU#pz6a3+W-t>R5Lmok7!ff~9SJ0ix*wnJTcM%=?F({;a`_xd zbC2wQ*2Ry3RW+~Y3=)9%%|wYL6Q5#Gd=0$)8;U$@{ek#sw$w@aJiFC`D5{D81VukJ zxJ}i+>^PmMm1=MMYm)4r0y+WdhzFjOV(p zx;s&pG;st9ShxTB{-5fqGku#aIIly=Q0qMCbCvxiY8?(1Kn?RnPh%G@gtdOUL=l|$ z0{=AKWYsnpP%_j^lwn!lyg1n?0$7I(b`5WA9AzvFr(Il7EjGy4O*85-G$k9F`XD^A zyn@{FJl2uc+&U9d`ML0WgZ^rQR z|Iav^Pz&M`=cNY(5}w)K=YaT?=)ZUGB#wF8bXms{RkC=K-*sJ;LQ8HF$cj@;C!pzf zP52xa)TQOhh}87XgkfoU3CU8I5F6*loaovRcz~Ow0DiE&o2)AA^unQ3)*RBLGh>m@ z&ZIZ#lDRqB*;AW3&EgbgrVTW*m+2Ra0gXV{qZriR9HSB9xYAIz&cN|ahrJN#ULC{F zVwLCFKuTB+p9jaU$L;MYjxll6o@>w}eETjsvk*{8180;EPF2)`jk5#f>Ws7xp6bsI z?YfL$aR|6Sx#Y=yr>?js$$+oLD!Ev;t;0M%55GH;Y~H{=TC`nYvxyr9#IPpoitM4U z_v{e89ztjB0bkA66AC}vR97SUj>*xZ+7XnII;*2-+*p@LYwsxtJp7W4X*2?a1t=#{ z(AQ4hc9_8p8||Gnb{y?GP}#2b{7vWAyd`V=*Xl#{IlPwt?_tgzbGjr_>CEihwN{&g<{E5i!HAU>#WwhIXbd;{4X;Tfh1#&NAFsznX3lnoOKP~md zHW=`8ts5;IKLlvED#{}2Psp~lo6vC}zC@tDQXm7d zeb~2zodg9{?d$M!7NCQ=`|f=?Ddwxa5fU<83m5Sem0-y1t%3 zxHMb;jr;G$+A5C}M-Zua@122hl+&l@c-Oua&#eKfz2>0C3saX*GdAXPCr4zx6B|#j zhw=m~xT?_vn^Y#W)>_&+YD%;C;u7Ui5qO6hDxpVwa7R|Z(NjPCs&8{BE{K60Ra4T2qLMRqQL1VrtfR#dj>Y5hZB zlzB(b&+DQG&nPu(Jqf6AOQerD%gZPC!vs#4pd|K&$mPcg%Y4iSxhU}$Elc|D1DK;5 z&EP_9b5cy3MP_8XzlVLT_693$Bn+5oF*YJ<(J602&nT5#>%k2cWu-Vyf^>RbMx#?G zUWjLmJ)_BQ@C}}}tHCdj#Q~RI8dB7nRa_HP6Mzs_W@nUykGT1=ADZV)J)t~Np-C+> ziwL^PFa}l5m~&Rg>4DYILwa^z=i2#)b{j~FyH)zVu=3XWgq*TOogj=!Ir_`R3s@iAGY9KDuWWRXuIqIqaiPDf& zkW9xc{RZ~iqq!+2hHEp4xhGLva;hD8i$2a?8P9W+|M8_s-7<}4|M+jLGIPEDgVez1 zHgS0RU6}G`*R)(@8bIK~!lS)p5{yp|Y~kM@xim1tQpC9Tg|4p3FRn4a`n#DX&-zdH zK^^4(J7qjRhtmFP>N$J}{Oo9WE5#ZM#?<~XtmJ*pirO9U=AUP|bs|=C&di>_uuks(Y78>MhkT9a+!PTq zoLzg~zKz8gr#OG^`T6S+Y>G3x-$arMZR)UDLa73;3*4riSZcl}zN*q!NR~y-tN8pc zL5#&YcV=2*H^bs{^p5PamfqBclpv2GZx6G<-^haOE&Ns(q3v06>P+W8td|Kf+A&%z zYYNz=zb^NBiKM*$qjpmgGlHtgp&8goR3zV7H#lgme)2i@d7BZZ6E8G_vxxN#3?+N^ z&(LYvp(iYNL}!V5K180qfUhw&!x?$a5i!?*c^#rgQpNt}zVq_HIxgP{-SPx-ArY`B zXlqBiaF$0tRQ_^)-HgHIu10q%@W{frEPXp6hOf{fKRMn*HXzW4F>Y(i-7+>a$LPLg zNJdj)@Yl^#mTxQCc6w!$h1U*`$OH;&!0n#Ic8Y~e7MU9ro2RD+1lotXAHWKYvjoUC=TK)U>2OApJ zWls&qw@ilsZIjxt zA67q%yu=fgOFKejhHXFx=}sa1e_PHh0rKy96D}GL`gKk{Od-9CO$pUt_ZEkj?t}ul zTj;LLffkS)F(8=iRD)b}&6y%@BgYtmB z@5GRyw#UG}plCtLZZUIxsNisD9=UICo7H69GJv#uSbV)7LkK{x?-vHKwWAhx;aJTx zIX!$8!57+2>sbqVWsEuBRQXtS=6SR&!SlHQvrLcZUzWF^&T8yf*bmjN;f9P@mf;CP zBJxs&fowWym5s&@L3WF}0*XZPH~&a_51iK5Z5rm<{dwx+d3}1(!#<6-0^W~g<-2<;pU_5q<9M>X)dmc4TT{H5j#V(L|#LYPFG~+*;l8EHrr;H zccjAkB#m$XtoNxZ`IA)d713e$EEfz8(B#CuMEDy)$Ce^jXbmS9vr|&%l>D+SjBJ+} z+EQVM$M&Fw-_D~Yahb(vt9GWsY{FxA``DUKe4g$tHzXk1jn)AEKes8x?RdEGfL~Ex zW<1nLW_EJ9+<{I~M;ICN6s~S`=#64Wz-~jRzJ&BTDA22=CqS35Q1jgd1ruEHQ$rNBb9QGs*&txwKeQ z4v-viFtR#26l)Kd>y&Iit)X4N7?yr*{~)NH*X?!pq-0X&^K*K>nYQvJrA`jJc!3)W z`LOCxR3md9X1Ia9)Xkf&AQN^7$inAX;fz!_Xn3@3!qk(5AjHazb1by7nTMdxpCy&= zJiRbqtS_E>i{jzGWnENgeC1p%JCzq-cfB7p!aZbDhkDY)*MdKEFz;mIPzG1uu9T0P zjP!efN&0C6;VOHn3)rHG7pc78B|9@Ys%gy8=+i|CI@PzV{ySx}WL^@H@TYRqU`3gS zMR-}Mgp4AmW23`y*^o;sw&QrWcZKbFj4iJQGg)ZdD$1I%cp{%?l?Ouw?-s^xeG`^V zzJMsJz75c{U$XslL)qm+(x)#yEK!0oN&m?+g8K5^JwT|;(ik97+l)we>@uVk zEhjDbbe<6Hf1+#RoLzk1HdskKd42!Iw-&t%sUy;D&MeA=I`0kdjBdAcAgio&En`kiFAyc) zh}oIH-aTGKPccg_sRs{y&Kn|5@&9+~=ROny42Q-$0RP1e+MIsvXfiDKUScI;zHY!^ zM2v;AApohV**-8G7da-|>YuW03QL5F~MBW*FV5`jBk)6Q&40+ zXoJNl6*c(_%`v`91ma!pfdDW!3WdML`9=7F4a+mAXVZM%P=O?XCFlf4jSAb9yOeHuun)wdLfip z{D?{mLEao?%~2){Q_8Fal;+=66rQb{dEe%AwCZNkHf}&f?(4zJJ663U@*1EP@h>6~ z4cEt(fok^S)<+KO)8+Z=;EIbXtPod(=s8S7pMpZZG~|Vmho(5eab= zWFO%*>cW*1^;k^}zi9J2WjDbX5Y%C3_hkLeg|rpW##av1R9=)CR!z*@SslHJQ1YSH zoVPaH@@{sC=$d=2;wk1Md%^m0!EItzD$HR8kJ_dEw%-h284W(le47K@q9qLOdr&;M zLiXTfttU;CGPO3p;%bA=5kJ(qyI*Dv)I)ZD#ejiW?(NdowQ`craa$5HJmEtlk@yC& zgVlaxi5gD3(5|@4*7Epl6KTmYw*AQ*7R-P=Qjo1wAD&KlnzRdJK9G_Xn{ocOAgCMy zambJx$eZa^3|Jg#gWcXHm% zJ9Rhf^?ikS0qCP%r)Xfs^|oM-pHmi>!MSHw$Sa0ThAd#<;x27JhzMBgWfzC(nMIHg zy~SSCeuvPJvBLjOom=@7qPSC8`J6?`7<;m-Oq+S~Ge8Dl{G`zD#`TMDy=xyS5Un?- z9VeJ`<9a%x^S-YXIpQM9x7qfQo5e3Md+fOU;$){K`hc3+U-pE;cUm+<~&+eby1 zK8uN+GSlgx1jjpnj(oU;<#`~Y4>3Li%BmxaP?j=_rI)930D<@=_nrWRZ?h!orZF9CA(g&+H*$@6g7ixdYllj8V0OdXGWePyLF`{v zZ|jQs1EJatLQ`Sm2TeQM=}MM4_Zv%UYx=m$LA#6o_0;NH#zunD$tb~g;hDd0c_m4{ z&4s^LIEaeTQ4@1wrJl1&GrNx{YUc0zwcQ(N9;&Uh`OCFZbzon@A-r-CNa1pH8oY={ zXq<3`{%v_XgmQ_Y?Ca^em~Q)@MsKfXhCG1(xbWn^Q)&>OANO#)}f1mTa65c>d9`ii4t3E zL9m8`d=V=&`q+3T9Z37&Ca#(IwEyR=KhB@l{6YNhRD{@+E6=p1c6j}o=BdJ`KZ%z? z&oK9^2quy>i^#EYB|{{Rjfb#gI~F^)BeJpRj&T?R(MU7DgZ4KTVKua8dWqi5Xz#i` zCpis}-v{NcttaQ{zAAkceYSSSzQQ)E8NsAp0$BgO$Nl2QIT5^4c|K-LH@?a%;mZxVn*iUcR^h4-%EYJvry`?r-)q>> zD*R~X@Cjf>x~ZAgw?nD6iRF!Zn=)up{K)-~o73Unw{X~-b9`-IQ8KNKmV4%ltOxih9QQ!eovx3(7A+$<}k{HtLiD+AojDBQG?7f2+?P_2GjbD%%a2 z=vnWV;?u}(*HuiHLl8SvE0MzPHGp*Wj%yiAJ!7&~v#PR*uxl`f54;UhG{xif2>v(Qv<__#{E>@_%oBvd@LnCiH+CgLGO8dj7#O4j?X>ODPTzGupzpEg1|Lx|@GAP`a`kp%&!0 zvwOsF>x|3$*mCzrY5f(vqQmp9)`I)hNff%j;>B3|hq9;0e#Ym)R@GMXV;v;MFqz!< zduQCB+}BCZ6f9@dHs04$Mq@7zbrD_sv|>J7^n@q$bq`u!Q#K3X4>|{o`Cg9Rq}RF0 zz4l%B3=->N7P9(zz}{ZiSc;6b{rOFRFLnP!w;8|)g^e{~%j|XL@E#+!SG_cIfl6o3 zllr@769wmGYdTj(q3E5Z!JQ%c2Bb3K_!?$4zQ)>f@Ebg%f!u^!J{96DX_RJU!>5pJ zDB3>pqV?&oOsTB}CwN@E9jY7m_U9JDxWqUKX5ycgePA=S(|AbCV_2-Dk@KM1x;Kuo z)snSqwk)&d6_HlAeATiN|<4HKw?aX&X784r+DJe`||f%vEF~*u#1th zC2V58{CDcf6>*&bqBr5}2>=usRC5yRTzJc~E1yWo*WUB%W&S!Dpn^wr)#iuw++<;CvYRr3*|fnP zL;0`~R+_VXbG0epB*d$2gsBiB9Ybt4gQ|>H_9G>*wQH9cEvl`d;l2h1X3A$Fv8u@( z{Qvv!l)J`10;6iJlF30y58{%^8=n#c-?TowZ$$*$K`Z+vbf_iI8fl*Zz!+iUe&3^Q z2OXA|al^2K@_x!C<)N!~DX7yG`5#B1BMx>Z(J|mUm*Cr1cs*Gm zPsbHAp7hM_pyiyl|3ycS+4Jl<01qC*y8|GK4dN5Uv@(HI?!Hw*+vfqWj zPa0LRT3p4iPn9K;=hH8quB7HyaPvC@J@uR14ZF^(t-p_p!CJpe?>N6X)BvJgw<I}`l%Mp@2=HEYC zQ5B_+8~z~%>mXv?HXVM7i0Jjae{rDlm32jUL-5`|Qgwi-DY<_wny2KzB(Pk6>qCG_ zt^l0H;%BqDnJ2{Bsu$M$KH??xA_8+HQNAE&qj}+w#x*uYW&iq~)12iS{~8+ z+^VOwiY9(Nf6hLRQ8+TjdI~#9kpZ8ehJ5R?Lm|OjvB#1fM z8gy;uFNbjs3f;PX^|eNTzc3Zi>Zw*@GpOSH>2yH4l6F-xwxh;>pzgY5Gc@aa=_{MV z#M8-7E!I>=eLCSOOnsZ5IY=#L<^Bt_ugH}D$kfj}8L??HAMgYb=CefkJ%^6&!@O#mIHjMgjB0TW z`SvYb`CA*5Z`VdYM*VhcU%7&Tgb;`#uNkK0^R^*hlWsI-{0U}jE9Jkm)IBF9_irD) z(3P{9ZX(oUrm~L;dFFVT$m%3{(VZQnYB#QyVs0P*>fyChh`rh_{Oz-eUXRLK9tybm%2-SX2=i;xRF}@dQL!b^5&I*RN0}|&(pvri;inFLws@U}*wkLWjgE(zy3*`$e`DO2+r^$ki&3~e`!F0a?+PR@_Z z27;5b(3#eOk*lQMEu*moj(S)%(yXG-|JxMoEp}$wFfdBnrbS=u>a{YN9N+d+Qg=YD zA7=S(UD&kJu*S<8@;TrERdXrf>;FF!v|V&-O5gJ)IF$IN76xmA)}hN`(&NU8*r8rc z1WaF^OPZJ4V`zKnSKI=iQ<8FYxgzHRAknVw0uf6i=1dS0n zFYOlHPw=UZq`z7pDdT%3KPgjQml>TSbIqi44U&_IL6Z5Dg@b@(W4B|c=p9pY2QE=e zTbnkHQbK9N27x~cFNzKv+kKoL^Niso`=C2!zupa$jkhpI-fq7IyxsV8B=Cc%RTtd& zZ%w9hp^+(3U3!v0PNg4IA*uZb$K89i`cRK*2h$GDs{d-QHnQA$rH&DCXVJX0l56Lv zJAf{bxR!0J&~o`v2kI6*_KxtrgRd~;y!7BdEQitS!F}GvrrtYc-zfrh0pVm-w;;}P z$cisJfiGz&D^~=DRZf{fo4J#zRpTqO)EO!XHeM7jseX^H5^0sb@k1lY5Ua;r6m3)f z@Oty&uMJ)v-l)fcQ2)vw&@021@%tm8%&t&d-GGCw!9$`&d;gAi>@@iL0MK;92t9nUQaudcg*+T;|LC z)97kk@KgEdq_Q&M#gd&e)^;l0{V#8VRr}rb6*pT$K$t*yHBcH zi$Z59EFj&uzy>9K8ta6FJB|$5A1&p(osz>E`NQBV+OrBGSG`9XX8wJPgh9?citN{&JX370!g;4(a2!gc4{}lDQtj|-Uq$GEh3^UvZUr*$%1GX< z<*(QmxD)ie*7XF_jOU;zrR=gX{LSXG z^d;{ygAAM7Pg!3yWBp^zE18@-Qm2*+j5>F!I%M0W^6H7bHJfFy>@vdDMjw!u(J#vQ z_t`5$u>HVB+M?TPP#-H(6!xkYU=n?~^YgdQB%jp%+N3vseNs;a*nFImt|AEBFrL5&fVr2y#O=a!ZrO_P7+uVLpKyI#asFjAJMI+t9DpAa zC){EAYU%jAZZg9hN!>!oul6mPY3m_Ur97TA^-Q7WmG^3g zA04*|X&X)?#F_-!tD{ejkyW^{MI%-3N5Jief=5H+qYuMNTRWm4FAfj>eZY#HC^WV7 zo6b?l%28e_TXqXmdeT6p4>0o`mET%YcO4cT6R|bE`?i~!_v)3~D-+Zu&ZIk6O|h=| zOD6bj)Lpc<_x%zkS!kLgNhEm%70NHNk+k0-^IdFJfVYwMrph6wChRM9caW<(n{sJZ zq3XHDpAX~e9wyZTKEC{L-t}En93}dBy#9+bC!wFXLZ1ow`ftxc%?bSXSl5$K8lGpCe(JD@=!oe_ zZK*0;^03;`Yv%()BNnB#(BCB><`2I@L2-j}k8i|(o!egBTojP>#3VWx$Ve8Z`JOzp zP^jw!HfrIU%AYc4wVz#IM%*aTuI$L1 ztvF_uzbSk}upfLoA*#w(Y`~kUFef0-%FbRenJ<(~xo^8%emhy#_5Imb2LHB5)Is(i zcs`FAy_Ys^_`ivJr}UR^$&^;6*OyfGY!WE@cFY&{mr zq13unRCsPJH7OPFjtMX9O%mbyc@qm{mnIk*Ozg)6J8(7T@4Ej4Be&#jy$%yBu4+1!00&@ zZL*a4UZLfk@8xrL;Vqh%c4ZR)K+Y=eD4-ucJv3i1c*mkROq&ZVC(>2dRwENvtSu5W z4(BABGWea=qMvksUq3AVAYt$qGeuYzRB15)&YTXVmSOxHS52tsL{cvelbqTp*|&-K zjffaRa)W{%6ipnPWwfe4vAJq*?EE0SIlR^W6Xx7EN!};&&qfyv6z^46nS^yapqH2T zb!X(qfXzm`CR||sVa+1iQNJE%h|T{43s>k8Ps$UNNd4omuPaKEGx2ZttVOP7M5YCu z(TM#uG7<)Rxf(j}2bcM_oS*$+ClZNX46bQ78i3K9#GN0`s{MFGE`xsvZ*JLr`o9&Y zsPjDWJk_2&{Qb|KnGA%NgW25O|4!+u429((3i$_cY5k!@(vV>TtucMPEGFyXAI_xa zeFGIHDSGtsUVS(JPXe)1qV1#fT>NKx<5XKl(Fc!(KwWHRb{qZXoGBm1?KH@!zQg z8=ioB;Zh*%v$h#OQ}Kmu>k|`AU18VC!F?~`HskalCRya90r)If=zYr|(=Yt=^bG7u zhSNF#7hjhu@3hcwX}ue<&>NeThMS4Yw65D{tc|z+^>c3d<=RNwt=XEsaI@m_w*dw2 zYsrW)x%wC*OYhpo(W8N9;VB_AdjO2hY z&>ykNB`3>etqSjGPz6fUsydNEY5I3v=G`s4_n4n-p@mKO8J(6Z!uiC9G{^IUrDe;V zcqOD#z(IGYpW1#y&+&ztiI;qP<;Ul_>pU$jp;tcCTi!@ozd3Q`eZ6^ZFzBSQYcWu+ zqgoK$-G!#gXI{E7w)l+ESFc;78&g*#W~|wks^!!`x1!cyLC4f@o27(zwIeb2>=yan zFxSjmT|+>feiyCzlyc3)PR_(ir5&TO_eEe=3@~;Mdj9ScX`t`m^~?N-187yW=7}Rf z=jEHu>KlTs-&x|>K<7J_GruX7-v0B>h#n!)RHyS@Uw^dci?RPT=14F5?Oj?U=0s03 z`j@V}t2ZuUNfUzLK1B#xqU)uWb~1hEEYN+(`|h6;6IYzSKZd*iGz>-_^!$PIriNd> ze5cTY==rEv)49UNFxrM_Zkf`?jw14)oW&TVnAsNg>|7K^b0ddUTly}}r!Iy!1|aNt zM#rqWCo>pJDHQ0v|DlG2|xv@@2e82 zUxDvps6SwLNB^$bS5%+G+1+clt=S25!Z53!!lC|EH4lZ3rjajceg@QE&JwV{?vtG7 z2X3CLFBWepHbMx_PZSz)tarR-@9potPecSvu_KNPY=zyJP9Nv}@Rsw&j* zW#-E~yv$cHWzF|)H`mXz(aOA5`RZ%E2c2MveQCS>Y?mIYKzK(_PmO|Y*GnNV>GYh= zQ>%6151{-}DH-8`qWyw?M2(R2GI5)|c*ASH#4jzq!6msd+ZxJH?I#{7_(47bunHTk z%MHF`{_jammxRwmrEUCJww4tc661-7eDXk}Mv@%8LLv6LDm7;EtflR~?8eRhf*vp} zbLQ1(ATm3qGIDK8Z}*E2lO^_n8S#} z5&M8M<40Ky(_P6i2{6^(q1WU!Bt-Tci74Ct!_UhdE-w6a_ zkNEsfZoXX$dP&lxzs1K3NZq3uhkeVZ`H6W&wXuM$PU~QRv;4#_Fj%ixbQ@aiHIrA6 z*Vi!f_jx~+l*acZ1Be%Qe#8n9d1_<%2+Io=g<7ctL+wP3O$RL~$f$YO#3QnC&&~RK zq-1cQ68}6$hof+pW7maJ|DbXr_PF%*Y+kF_r=QjJQGLH{FkfDvUW#8q1bH1CsLTBm zc%&!by&rQ~qlBkat~;gJ06Y(A?f^jx0UGZuNhSqkmX>$bkW12Dfg&cnNkBmtTG48% zs(G`rn5Vusp$n8Xa~<+{1vfM83rXzEzO`OZH+kL6aZM3F;l8a+mR|8I@)+9T?a6nL z>bzE^@8#vif7ejB3MZEzi!4Z!OuLvYB;z&9+kL|c)Bf|$e)s~r*~*9Xo^0xutF~F{+`EDGnbT)4NnMdqfv;WgltB zcN3pf#|HmXxt2EjE&`E!+^A6?T1M zVl=27QFuUVk@-wI;H_?sWOm77_T`O~YV#*sBxAXjpOw7XU_f=M?vm%-6Z&Uz=%%TF z)BS3~vtkyDFdLflAb671MjwiTJz-NYf7`8J6r4eAyT9&N)zvn&6|_|is^5?~xTWMZ z^OZ+;TIkv`_6c|+ZgpMo%;d&bgEFxzFp*Hi&^v~Pr3Is8A*^q{#MFc=nbxqHz(P6| zy&yWAv;i-wtIp_io)xP{+JLfatV6ZkxoPaWM$L6W!Wygj_tPC*ipTN2fg4ZsxIBBf zNtQt$?zEDEMOlIrV;~O)8M%&Mnw~V%fQ=XkEB(*8Qd~9lr$=A=V7qGoddfORi1#Q} zvuqS`AQIsW=ps1_wRQHz1G~0Wirs{NuaS6EDm2^zI)6D(!7^|Qhhc-eGmxM+m^^Ha z$tNwh*83vipnj*dps4y=0eL_4kzOiy5s>v?bW{w^Eh)$zsh<4sdyD@tvLnCleJ0Y^ zT8@yERKxPw-aJ(B7S~LfT-!pl8367O&p(tS_GtAcCx;GqTSA7?Kj8za;?F`CUaSx2 zP{OO|xUBsWYW@pT9<-NP0LRGDzFZt1);uQi9h05zw=QJVugZ|WV552sIjNy~Wf^uf z@8@%rQsi;Z{)cDm`NF81%Cdv7Z}23bv+cG1fp(YLc;Hhhv*nUKP)4XvbZ`Vpv-qgd zhMl8Oc*$N>wgVLSD>eR(4Lwcn$T{2IqU=VYRss9GthS%Nrff5C4BFiVRe}Wgww1?( zD}`<#lY_q_h2-v;p~!pV?Q}cV^N%vapDQVA@1nO@T!BbYEu40%y;3-cudkXjag z|MK%fo;_wAIovbN2=aRjWqB^kC5_thd`ifCc(|>-C(@AF@we35EH4AA%m@kM496s>*V~-cmIA*c3*A{1ClL7PJ^nd z>gvL5dshUNwi|5@To{QDiZC1Aotv@39}F5TV@N%JpT)HrwJUaY3#i87Xi4;jRn+Br z>zhjP0}i*#<@dyi@Q6Nyk#0GGKDZ3L7TIF6E?9THQ*ft-W^}zoAXhtA=i8%b5koTqYdvEr+(9rw?3%%~CiVZv(0RtQxxaC^=bTQ}L2cTe+Iw$W zt=5W)89US#5qrKr>pI3SPeDZmI&+oqP z?|of_lvWUHkcUo`8|i)Eyh~rn$#9I37?ie4!8JDr3>cSGo=&VJW_JO%dwv!bO(q98 z(w$}k_z32qin-Cj!H~d>gn`l*yNg{s=_le@e2}@fMOuj$0BK)+9Il(?q=&k%u=n(6YritfdDCtigiGeE?UQr2 zKZ1u^MwA^=>D`N`kQ23lJG|S>d~`)j_TAglu1q6|Zs){Oe10jU#++}zEk~Rt(eXS) zEG^CPeOs}i)>p)eR}&{gzRIkpEwFAZlzsT-$#fPuEQjai*A2`7`&XgRvKnE1WHv_H zu1a6tLl|XQ2DGg0tS(~yXtkLDzr8iuE&>&5&k1da8U51E|0{Yf{^}Xsrz5@1=;M@2 zz|(}*r}U(P8b_wN9{RDI>*YC{C;v2S>#di)0+%@aPpRHZx#g*dirPU$v;M%8$J!Lz z<(el`+drWSh`aAId}|`6O;U*X>GO9vUjb6%;hKkkZ`cbWy_g(~SpRm$C4g*nKv0A&5JXsrbt;8 zJ1EG8e2k9+`BUUeWkw6+E2(q~%m2;=Etklhf8S>*dNr;9vT?V#io*pNYAGPS$f^W_ zx8*Xt^OIU)dss?gM!Ptj^cpy(2bIcd&qS4mfy%ww^yZ{VCFm? zz!42OV&BuSd=|1Xt%|{Qo}PJdH>#}6EciCdkI;s?ud5kv@DKL)WxBs}X?E3uf$$W8 zh}3+IF{+L+?7h6d4r#c4n7-ma#$wvs6S0vWpU4RM0e<&-YQuxib{{Yji>1vu7s#XH zIqE`T%<)W`V&ou9Oi)Kc&dK@9MWst6Cbt0f%D-YyWYZ&1{kY_41+4f|pMnH55E;*m z|5BOUr5SUz*PSymnJeajrNFeyKxl}%=7Z}eir6~V_0CSZc)QRml%II*MmgiEINyMv z($$+p{#(2AzfAtHRRAf|>IR`U0;$IJbq<;3T2g?lK4;mmUv@Yc_}$*Ag~*pFPl(5y z#s#59P=UCR`OKk9rnAGL?Q;7e$>f`E0Xvbl1ARDNpI}gX`e4k5Jf;rK3ZnP z{L8Krb}V}=!Z!D8%b((Dmr_`Y{N|(e&UWd`i4WGTapr`g#3NkUch@eC-v{R2Q7U?g zBD-IExx2kWMR4#`qgNk}Y?d6B^5|hU(M!M`XTny+Uell)t+!2*@4;e#ZWvZ6w$(kN zTy7+w9zSayHZXFk=jSuG^=EP?AQB@smz@@Qk^PU?`19@kcP`Lnav+{3WVjxj@ih{9 z@cv0-20AZaAv4FQMfn=-71sP}isRQY~R zG+JI2(G*ibUor!s9{2$@9C|h$NO$$CP8?x<1ddL1%R2{@I06pKw6<$sw)tTK`1!sM zz@o$ThrRa(@9}F^KFRLiuF@EFm2M@gy7H-3r_?2ktJYqgS6ywthXmrCGp2w*bY!fT zjF!M7!WKkEyPEY4A6G?l)IS!eQ~#u);;73rr}G(5QgJ~~%J06Ic+paaMuA`W+!kNV z)z4S3M%YzN`sVXFCtu?3PYYj*zZ{{awK@N23XKu}Ex^+;@9|8HVB8TG=d!=6>1<8| z-`9$a$lYn+eV?Yfy2m%Nk*`tu>NEbu*>O=93WmnM4hv#HO+)$goW-kv+$ZqAvva;@_O$TKIQLnvc8 zMKnjJAF~VsxACDGAv6iTl9}rmXDiZSISx?~qu?N=Hxb!J>0sCL?9Uidp@o*fo=0#1 zrdNw1vAx0PuDP3M&-aL*@cu4EC)Tg2blrobLmle|61WZ~dMH!7=4*#>JXia5sf1mR)f*X+BbP&N_**U8;q{lQDT84nFg< z2Tva&uNsL2lG~wzIrG~s6VFj^J|3ZF>}n0`g{*^%ePr!f(v&-C?Y;}NltKT6AI33t z>Qzod{UJpw@OUQxO>~P%IId|IVQ&UoI^f2>bCOKbzUNdCTK29Zpf$6Pbf?u1wD^;Q zxA%WG_S^Nb1%>v1y4;-+=%GaU6y6sC+Ce=KaT>8z5i}k1QXPOsfRs>UR&jAY0l2D( zQCRoHye@(CBl$1V=y|Q;4OP;$!@@mNHDmD@`h8o|)?XIYkNxhZ|K^0;oYH9f7pSk* z0JRQytDL&{!kg4GzNMdkxoR*9l<_TW${3V&7O3dbsV?kTa#2^<@WB^hms_t9#tXGu zHPyMl<6&fr-7=xFsKu4XYN*J%;~s%tZU-7RvZoz|IWUt``*BUGQ)T@(KOX-px@O(r z+TACG8d*}Vq~IF?(YkqTs*Q0L+U;}wwV;FE<#~vNi+l47{Y{14iTq!e|2yaLSdM*J z>RN;>wfEPmApikwvZMLUwIFb-Q9p1etSOEin~K@rp{;u6OR1pukL+CGDPHQ|;Y&eW zR#ur~_Hck!93@++*PuCZ8S6{he7^m~C7B=W{3Uwc1@l*TnZPKH4J}=BTRU`p*?DWN z=6COkRcv9+tL~}b9A`iL!^Mf>?76pI9JKAiBtMpSYhzh&o?j}~A=)`rN=rBTHz3rE zFkL0t5a*~UYPF2iPv8@>#Vgg1VDydHrn3I_JezQq%;oN*eji0$V7Lale5xcI?L^N zM#c#nTTM<5le7E$kv!4~iUKIAyuOe43FI2}^Qk$U@LyoA#+{Y6nrRdqE`c87U`Rn%pDi7*ZNK2VjwSD2z4Ft0U+N|Q34`1GgGiC%S z+A;~l7-xV*Hz4}f;?c*rfA!RQ#BEI7=oTZlPc$pr&pwX{dbx|;SVDpRNP2lSE%(Em z?sUOhd8|ypL{%{XBfe32kU{Cgs4tcZX+@d!tFT2~vnD>3Bdg;FrqRW^P2VOgq6Bj) z%ZGiXJ)h+SviUTb-gmA923dWO8gRukA@qI8t!kIza%8z0guceq--2k^Sm-^@nPN;Y zuA<`QWvE&msFsSD^=-GIz0 zV03}%XfbGoGyys&Z}GNpNqv_qp zN#6dGl6_Ht=iZP-E9}Z;?<_B4`g>2g|K*#xH5Eq&jMOpk&XGC+HAL4__^Pg7X}h9!G|{VG8zY5R|L{VH#tEO9_gP8x0gJIB}7 zxK*Zi$<1}FspS6o3Hef8ShyCt7~*!OB+10Pu8Ccx<|N5Xoc#>sCF{a`muL@H zn#6?#NnSP8i;nO7LhKcgP7b+_(Lj(t;JCN=u%XXZsHU-CD@{Jl(Mk6Re1@|oG(0=i2)zci@`Y_Dix2(N>@$Z zk?mAlcy`M!4>vX*&p@(~032=wX*6&Pz?Za=P){TYzw6 zYxHI)H2t_^-&{7IPuumW4|pWcPycaJfBhOZPOXf1$Q=z>8yb(ceVfaXYV@di(^5wU3`uTv+j=mDvTe6LqwB761xO|ofSCi!;MryCT_ zx>`4(7-s}CI3SNs+a;s)BAX_l!AsJcy6@u}auKHeYHwSrq@v>w;?Ph=hq*bw)s7+4 z{Jeu@IFF54<6W(rO|D-pW`uok?`|=x@>;`S09h2v9Q*p7NGPR{}YhP#X z({$2st$ftK3f0R0d-ynNqCj;}OeHJx`Is_@!Q<4>)_e&V3ZQ`cvHq=i@q$AJjq)>q z4R{=O9$TFD4R1_{}xO%<-20 z6e^$(;{Csk7pp0?{Wt3&e{}c*<{J1+w(CY5ZZ33@3s7Mciaki4B3!1@-Nkirt<}hR%c1JWCSx?R{Y>+| zqpU1Ywp=V=6`)1wU=P2O5iDwtv;G9EN@tH2@J9NiM`n1I2kSPW4V5Z_6%dcu8*$R z{CCi+_aGc-i@!EhOH71B>C<(W?MxZs@8w$b@?PGt_tFR6dC(tOM+<{Dg=P_{yBTpx znxi8pr10=Mo+Tgc_2dj+7JkAcS!$0`1%w4HJtSyOILteaubJH|_ZdX!l5zqindZI6c77XT> zpp>gYNBl_a2+3*3y%Dx=h~>CZ63j z&5SSSa;uJx7J8Kl^C5-87AFI4LPcqRgHIOI=EJC_0!N{b^u*LNJ^zAS9dpZ%8jB@m9}rMDvE&07CIBtr&s&;@8__Xoo5n z6;PeT*LJ);4rz0Ewtz5WSIvA33$UAkf0n$j7{B>7384Ap-k!4+Kn6NcnOQoHHh~2- zm>K`wS6k9=Z~C_ZBr-=7#BDAq$mPhHc}kvJcwTzfbA2K` z*CA5Y!IgNz4V_~fXcgwm=-@8|q==jFOvUAcUlkJl1>Lz5H4|Ypi9`{SM)F_1q1;d{ zi30&zkP=wpp;rzG&>Qitx9J@69qA?4AMI}as(t=LR;aAO06S}(mn@>z@Y>Is%BA=% z#rVJ?LgU+V5ule)+qRtyJ~?(Yy(4jc%MWt`S&?4Y9)7+hp3hRyH}iP$N-oWtRA$k9 zDP#a9AC_1oFsyZX`xE$WhkTS4=WgId)#^qSRAy#t!Xz=PXL0a7pn7Od+Sz%qdDFP?ExD>*KhgHOmP3tyOdq%hklW0 z-?y_`o4eE6D8hz>Sorb^m(oGpFqff_@ijWw*$h=v z4Zg_upU0@Y2J=p$&Dc=XQ~1YSu(7x%;qY1W!*fzzmfXEmwb%WxR`SiWKaWOiJ2l)` zzhR0pMu(6NeKf8FzI;4hRad?ZvwZc$7*bg!CkBgI?P`Wa;9JZNE(=P}x@X&1!hCIc( z$b}uau9jBqQ}~`~c*$fRR_c2(+%1(Mjfp7U%0S)$+y! zNteRMaMy9LPKk>d#va>DC~1_xRU0W;9Gw9#5t?D4_3mS6hXKELdv;6%BcYCh3l7XZ z=Adj!LhIRACvawO z%5PS7seW+H8ei7g`)2T~Y#pC${_NHB*#p8A>5%U&smqx@ex?X3+2_kLO5kmHl6SBvfK|HQ5KUh&^r-V;a`{vL> z{g$4^48FO>x!iBo;&mVD3%JEI&CBIujU5+c{+_u_wD~yT^9QTQnSN7j&6@9RQXj5d>~X9_%4AT_=Yc>wUy?q$0BobO1h@P4{Ha zhl!y;^fHJs6)E%yH;>(N`eH#HVN2B%PB(IpPkB7^P^g&+@?jM$B60u6)=Cici{Z={ zyRpE{&7X&5W_P3?3>_SHtY7>-V!CY^uw%CL_g`sujQ;p%PoGV(6#=nd+SdNu4$2mj zF*h?+1g<{Hd*|@etfvtX0*G>{C%*z&9d3O&TlZqzn`zrYp>h$*^(abscA0C zJtr2xKK=SNj+T%GmTd0h>q}YdXmfMuEW4<*Lye$Lh?p%>6qRvEjBFo1!YAV!=1%mj z9z~0UR4Kh1w;B!-KY!W?C@_FH&iQ)61vTehXi}rJ>?#*aZqG%pCn|JkOw2081wN_M zNf0`Ot!@2x?w?vlN71;%^W>1vuO}to&;He%#1xGwdk3qkLoxk;m#=P6xRN4$zYGq@ zhSY2n6*ItK_+B$5_o#u`nm4}^0FQ#Z-+)ilM&%c?;DMomV$z8 zZcnV&vdEGn0{jJX{^II^vfV_QDONpxiHs!k4bQ z3UkVz_>gmrG^w9G-hZ#$+C%h-L;trIF;n5f(x5$#dscIw{!u-Ooc)wrlDMkLuq>RS zu)Z-5f3T7!)RU;PHlgsPwp??UIH@}5V8Z(;$$ZQE3%FJ#K?T|R;D|ZTE5fpj|G9p9 z`>FmJpU+=qyu3kIJjH0?n?-TKyO1ASqrSPg?7B#?uMHDg3%@otj%Nqrjnmio3c`*! z;RB&77q~3E4&=9cc!c4iA#iWg@2Xp>u(YTa6gq!2olNZUX5GFU$>y;k$Y{mDbx>w* z>i!ZL&uoushD0qjB|-SU5xn&*gXPr(Q>qMPG$=B7|T#b_wa43==dY;(|Y|$`T67~SEljIQ*Zr)sdVlu(@$;W zE86f1SFG+RFy4FTfI%L*(Rm^z?ru=_=Gq!78La-pOLim<0^O}%ByEwPQH&AD0XXw$ zv;Na}13ExAE61^p55Z;k?wcoDv8ftFj|yS4kNI=P4&VhnmG*6J$3GhI$-nr6zB1N! z>ni7|*`z(iGJBYl6oxv1$((BV&>sJWOYyXqj!`cPL|@qrm0X(oQ&tUqQ%w}kb3y(x z&&Rn1nSOMKe*J{HcM-n6EQc`5Jet{|+T9ig8#;_Yqp-MAZ1ecc{_)h0umWRh|J*V} zoJbGL$&&GSw={gw>W-n7%*@btJwjIpC!QDe2nh0J#Y~-GjVM&0y)#yi6|W(j0*6}0 z>9)EZ{&d?)qQj79D}l`|+0aSFah3<&ng$B;l-ne)7Y^*|D0UWK`XPKEJ zC>tfk(80B$30MJG-CFlkSuZ+i5HEddF-wz3S|4XdvzB-e00!f81kvB|HwfQK+OWI`TMr5b^gReRR=O>Vpu558oq4d6hJGF zM&bx)_aX;XnEE=><;`MNP{L8Pai_Ld3-MgtM|VYIi0Dmu_Vt>pPV9q$8tn4F-PBBk zL9GAA==cNJ6nZtjkCjN7>rtm1lx|_wejYU(&n6MFa(NWOZx+!n7G3qY{SEE+x7_7> z_AE}LG?_aW#i#8-*9B=^_KjFzsY%^I35KE(a6j04i%`^|6>lO3)C3;S(7~~8hi-{V z4F_UEi5m!#FK8vXel+LMqSIT(HZI04r4&&}q*6&BK5yAg!AN-+jpf}{J>NX&DxuW1} zx?a30T>Cw2>UQUaThiI8w}lDUEh24Fj)G-DzOA{|6Xgz7<5YkaOAZr3x-zdk7P3m@ zThf+nts1V+Ukx{7VDb?7c)II2dX5ryr565>6QgGOM+@2Xop>r&gK!#W&b{dwo75*q z=RBR(Us##n(w?^~)uU)En-eyH6O;}7NKke;M<|l*0oHxfPr?6!nv~JPJTJ@ zFbj2ESpCf_R(<74tgsX(EBWVzsbQT3O_?)yQUeeWx?8_`9U#noP#D~&iK(QFqsV(u z-Skv`U5t?t)6jM)57Dqc3M^*ym1dQvYUuf6zB$;3N<%7I-uo{PbHlYvcpJLH1%l<9 zOfOTBn|vxcs7ZkaDr#pwBAY?A+x zPw4J(F6wo+g++^Tbpa*T*am^K2%?%lAW{tS@f!UBNixZq; z1RCW$w<&g!*Bv2FQly?CCspAuF63??JrQE5ntESkTjd(%3}&Ur(&O9!*AEWkwCYxm ze&QF%c4Imfu@(VDha)EiY}FLTruB8G&;3uN?AXU#T^r1+vD(77iBvo zy|FcZEEP}O(pXKjG{2#L-}FJ$lD!H<(Y`>H0Y)6NerYJ)S?*n1osS-yduk?T?jh1N zO_0dHw@B#Z%uz!innt>BrU{r`zvjcKAJAJ%*{G1yxZ_YYVTaiGxmq`Cya|1LOMnQp zhRBiccUa)Y>5y4w_%Bs$x>KPK*?TgG9R&*bwhPM_==oo^TBumBPyEYFB zq`W*(04=~awZ2Lmk>7r(woJrg5NAK~MJqA<7E%CJ2gEXcd~@BgHJX&Z9xL7-{sX?+ zD|2D!<#_{g>0G`qv^zbf`VQnoqZ>YL5~kntLOiW)>LyzCyf-Xhw9^n{z#NzY?4-8F z2Hzfq1=qC~fMaod_fDMimh3eatby0B>$4;7bZU^<0PM(~gtx&=P-@O-FP zbregtvDLA`E8ekCQe}#f=OobIy?OOtZOq5fF56Tmqn;@uFYO1DymkzQMbUb9wBLFN z8s;e;N%^)?X_1yQX0hyvHUXDwOC0kU6XSx=T|+J3q!1@QeRKZXN%66M2LIIjW8tTs z@22NyLN^=#!Ou;U0&ozIy4KHob_L`CHY5#3(bbb^2Ci};Xcg&Q^PW4rv2#f=VYn*A zf5QM4vkp}w;N9xNAM`u_(0h7t$`V<)Mp5YUxH-812IeE@75OI*DZKO@`b*34hTy>ER_9TYdIP1ZnKbw{{P@u6| z9a6eYCe8*nLl4;4YM`&fy!$R4WUUW!uE}qDLcGkrzk>b^tGeqSAA32|{9XNcV}HYl z3737;v}usY9li!xOS)~h$qY~h#qrMU09Y&9;o_`>ELWoBNpXxF$vQg}=(_Au?9*l~ z0txZ-@c^eV2uY**qTb1ln1wnFk_?Rw4^?O@b1r1A+R%q3k-*VP`dl+>Uv zlR=7hW@6&+5ZrCc+^85zRZ1m;#<1Y;86`9DXVWrera`cO3viP#V%tfj_iNu@MHh35 zS(@+*-`=I!FN7!UpT7rAGS}z=R?BF`>LC?D@zVdD)2LD?#f&z^RgrJKfpliNxQ@Qn zJT?m2g5?ql*a)Mo|HjUaUO#;mj`u;F_qY43?*BbED{Xm)8y3iSZa?cuz@Mf6o$Cc{ zuMX!cA;L~o@ddvTvldBvC%R@v3Vg-dV&|VH9UA|ha2Xc6Oht~^oBdMpQ*=VH1H=uo;0Mee`~{pYV-*yx$CDNfB2mF ztHMZi`1hd(KL0Uu9$S_S1xb*!XnN8in|e*m=(G19hhY<0P^rtaP;7iYSUBL&LXJK` z8mW#NZJ<@`1W&m4$(=FxEu>ZTh@~I0=_+KV9CVd5@-yCLXmd?${vU)-Ca;g0>5|KUf8nyk zLcaP#K_;R3lY#QQA;_w!*1*RyM{c)oZH5>e+*C|CF}N?3ZWWd=+>jL<2~~(Z^2eN# za9+h>&2^Too-tpxZVg(CybtgAmIWLxNx6E0v>-qHEQ!+4))LPri=ViY33R6|Qhw%o z?c*{9Y}D|8yCVA<%Nq1f57V~s*sZbR*?h)he(K`>2iOVVm#PC25LeTj@gIGyVEkcz zOUB(|D~ajuSC{P5Zo{ujAu%J(N93@>E|7RN#kF0j{yuqMV-p|t9y*`C47lg@0H?`v z4AbfD5O0%E3$?UlR@@~T;Nak}MLH>Wne{*B@#04oM?0+h!Sd4&_v0fP^ZB?}{sy?m zD+e>ri6kyxH@yewFj-9KF{NYr?AuBm*}`hFRzt(EPu$JdZh-Pt`Vk}5ranZa%n&O-XRl_HmpGsmg&(bqTc6sU9Ur`% zw`N;cVgoP>D5#x_gq_*sfBfY7Mt@k@;%^TTMfOxiRcia9-=)yGD#yu}ON}O%uX)I< zEA43b1o@2M_9>e`0pNz;(yPF0?iRN<+d3>AW-SVx|B896u>7?nS?L4;}b|2B84WOF-QdQ-Vk#8-4&xshdT5c_Z+fo@z=ojF>lwm){ z4&H@WH{(lkVPwp$Dah0V6f-a~%w%emf>nreaT#Qw*UFJ2vMOQAJe((ao4ccfhIT7Z znMoAQ+vMp3TG9OmHLE%I8=5q|83)*pdGcyH{j*oZB2Nwn_z!6GomWaCK$ejScTnTVS+mrtCneaO=>h{ zhOi1d*4s)7K8g0$3am)@l!X&HdhV>8&?E6rPLtu*(K)ddYxu9;EjQVkd%Z!qO%^5* z#e2an?xVvv_{S37!*xGO1ECqPM&IZ_%v;SF`T%`7Pm~kmBsgDZ>^!8}jr$ zx6r(=K_&O~Ep!|6#C(!p&o`OZY&p=5A$2-ypjw{AykOYI&!23qcfm3}beyW8U_4cg zYl2j$C7?9gveX*3ABqXT>o3meb@ECLDL%aJ@^k3!7taW`h(*7TsXfSWadKhOg3-+* zE)Gih-%uW*HyA*5_GDK$Giro@O&p~jMvqKM#wET&p%QbPf2%|)UuTRS-e}UV_C}kD z_Ng%L7Vf?2QhzRXE~~?Xqv<8t5}I(Zh9M@B!h`Q-byI8B%-2?BqlmKO!voOBt*L#V zR>hPi6qlKZqknOfkmmU-I$SI_Rlw$v)5BgtqSh0r_8MZsoCu2-$0Qz8G}c-Z_Futs z3sfmAtPvrDLhfiqv{47%7xarNe|W3h7@5GCSsD+MB;jp{#tWoNH{l)LVh1O?J;T(T{za{o( zi+%ddFgOv)NT$ij&7xO55f>TJtIgDbrNl&s*^h6isa6t~;jzzWA>IzIX`D7H94+!7 z(?0O$1z#%=NkY9`zWvUB=L|H)H@6ZMnMF@5G#+g2tYZ@?@ll~piXuVaAL-}LT`>pL zBA&iwei3|zEX-s1{Qlbt{WzaclN;}RMp{jDj@zpI6?q5?*e8uhO<4ZAs~2Hu=cm!# zWVF<4{%4Q)vztM$L;NJ(JWF_2;WK&L>Gmh~Ho80Rg-xEV>wqa?25U4L+}dz*8#bB_ zk&8RTZ`3c5S?icqvkhiO72}4&Ks93_E!vFxoM3v&k^X0*$h~ta9L8h71jEuN)3H_G za;G4N#~7QWB1KmZ%OV9_x%(~{1|zLuQ7BI87apIs3N}{_J1kI`_P-00S8OkY@1AD0 z+*GZw1(w|JF7LvYO<3A(p0F?lPP%3&-FOqi;cQ}2T(at^P{GTjacAZ6-#i2J*BM0#H7k@bK@Iq7XKnB~D5Y;C$ALDnUT&|N~@`f!e z96N{@cX;!*sducT)lW)ZK}X-{a>A`{(NTWk2*F9shq z-gU7uU^l*srz6r|j3gL;gJA6o%dOL3%Ipc#l3jLqi$Qo&E7YS%Ne8FlN= ze14LBd6@8_#Mm_CAlEeA>5k@wrC?Rj&H8e(XAAG|=gaQ_8soCOtDP8Ggsc<*sT+wh zlNk#^ZLE!!fXobG@s{a#cUip_{RS(>lD`6@Bs0YqHX1cnXB2wco%aB%3Pol=SQ9P} z+`A0ua=)*vpUB4yAHa9qu-MZ(+cSmpmfQjfJ~z@%^>bs~4(*5?i1Eeg|IT^iGz$(1 z^lFFv!&`5j4)YM^jpe7`p#mWL9HNZo0 zEo3s`ffU^q`6yl&m(Px}v2_UOHb&T4@#_Tr?e%Vv*MAg+UVN0&>lYh+BHZJ#M*BQp zWy6Ky2*=2)&*_lzYSoWw(KP8Z?7s$68FN~l9nW`We$rzEu1a+t}QsyVaH7>@Pj< z%#s`L?v@k{tl>oh7p}Q$VEwShEEw-0EIIba&ALr|5dDZqbNAW)jSZ*&{xd=TSMoxl z;qF$i(BQZ2ADueGshB`NM{K2e=GBZdW2ZmOVEV-iU`^_ivM|A#@IUOZRbRt6`E=|s z?)(;CuG-F40l956974lTlEvc+VWUKi;#|nbfv$PZ*}E?@rRQYA;^TrQ)5v}9^XV@R zZJKEVOLY&H+B-(|^=hYkknN=&bBP)TRNtKoi-dgp{7_W(RXd{>o6AlL1X&{}SMk@h zBnY?ig+zNP{>L{i2_f#8U%Rkn-~GT*y2rmbz?Ew49zK5ZW&YFqI7Ho4XJn&l5=-|c zNVbo-qai18DZbTzq8Nvqn-Tqm-yR4zju+x;z)Ms4RE)3 zF*m(;%Z(?odOPB_CcOu!0umGMpGYRZ%%E&Z&-~8Z!(KX#9o;v7{}a7+)v_w%NqkqN zW@=(>0Ac6rP{NO;i3h{t1)?{pZi2+moQjg3u9aF@`Jq7;Z%YAIm2(?9-Yd4(oKqb& z&sL<~q=>XSP`>4oCM<3^sD0l^lr`t#OcZKFOWp9Zyv6b9;~2NK+vlMP#C$=be0})c zn;rgRubrr{-?stl-XxTO}Djuh4xWYKp3NqUSxE)T-4|qt@v@M=+|{w zL(IINE9}K9P3;6sT^&ZzexXlWwrx{k$o9AYW`PEtOlqug=!|)9h0&)ObAxzRfq0Wg z(!Kiqn(1M1`zEs*y5(H!LL942`FwNmC4ZqLO#Rnp=S#VEpD3jQ1M-JqpA5WT(2Wc8 zt;1m7EzmsH0=-K|nFDf2hwBJ^zUu-dE(P0T?8#Bs?HVkmQ$sGO52AY2=FmPwKHzy7_1ZX7m|T1paG?v_xnPja-2`(uu=?YyPC^YKu*QIMYmKXell zkEH4Szg;-GNEeOz|m!bR&?N-*eD{{TXBVe%4y&HM_SR zg4VUxI=vbaat3j&$zW%LUSR-hh*=~2nTGxrbS7!c$ z<*P?L9ab7Lmv@0JNUEPP^lj3(b{WpDW_bQd$2YwJc8n~g47eKHW*N_JR|99v1~yI) zL^Tm5F6qs06XRKRXTg|_XoKz7zs4;a`!2mm@Cm)x>_&}_6* z0=oV5u{J`@Ok?y2_|>{yGFy6uQKy)9TVd0@cX|q5c|b&WE;PL5&)D2-ct&`;QJ_gR z^MI;r<{vaZ$qC`#VB66IUT}99<<75r`lQslD}R{_N$8uXW$lEmAg_H`L|IPtXl~|> zjnlzTzTAF&AP{SEmS)lFK{4j^hkB;j& zrahGbXoe5nmREPXO)0uOm!prlrzAxpzh}zJ1&ul}lKq_v?L9smCTZ6{tGg@pu8g=8-;%NTM?r#nrPx< zZbJtTuIw0OK5HFyF&s@2cK;RK(-F|`TAQiwkcu&j{jd?qc znv}^l1!*IvQ<3p-dzY!|)3PSy+>PrAiY>a{Y^P;;U2?$s&ed3|h2rnuH*UCp(`5zqXium^^X$=Eu@$j;MmHm2)X_m zHb%i7x#4BjA|dsSo)@gzvyAt7gL>W%8DaO$L1Ge?jB2dlXWPXR8gnTc&sf4`+s?1N zF7BFG&XNFR+%>*vi7yzLQ8TBLn)njfQYxusLy}IvtlsBG ziK)tJ@HX5bed+fY8E=JpTX)-JOKMMR3e_c0i@Ivo#!e!OYkBQK;w9LA>LFz_f}y+P zh!`-T+caS1jDZoYHV}wbmCGiggovkW2L8Np6LS1)f)xps-WWS83~EqfU0~A2l#k#H z&6kOh!O7~6yt(WDb^3>Q-Z7~99+jm0ZZIUfDC!t*_OTO(Ihj(pl_^mVBAIC=M*Rvw zXLP!0L;8U*H<|E{$n6svKdJO5jdcJ%dTaFyw%uj*tD>Ia@)`99XJ(M(nAa?nohr3D zD&UVY_B#r8?lZ8_Cs>S%z+<0L)@h@b$9xU@**it1GDAi zEg67~nc@)j*me&5wkI*Zlj!hUew#JXWkOSo7_`6U-mNwrQ`J*6^q%@9F%nWUxvpDX z^!ha>IMC{$7wuyHv1Pe@XK$?EiMn9@LCiM^U7GaR|LpSb4Y|=}?0c+edsgE=6YC@0 zXpcs-jy^2KbPe{-eOkZ$e~Qk-k!#!Q6bZV^{?Ww)hs@e9GiiQe;ATe5{L`3Y^ zen)Gs3JD@;5i4RRseNkJt{^H_jZnL&Jh!@5!Z!ev7aC*=Z{FaF-Oort+%ZY38_k3u# zEM3{&(HmF0>w_F?+NbZkTZxU%O^y{pgs< z(0V)g>G#+5)!8D~`Aud5U$em4uj|m>t2{xiH!{mP)r({lA|0I9C$pB~ zv$a447V?(@KC2J(FJnKEyx{3E?^_y@hmZ@SfdxrTIn=R^E@CGQuF@W@A zFE`w$a56-6MCa+Fc;DE-1-B<+jZGsJuV~TzcyBeqE1MEgu&jVrl6$D9o!s$Ku;-Z)ike>*qMGa`_;rFJGVlo{nM&{3W?NibL3Z!su-XfIr zj+^FR0pvI13i3hnl8si`d7J%9)N4A5kMmaVi5`T@pO)H-PvKI2D7#ZguM1aW(5Bw##^JAH+0*Y;_ug#gT#FRa9g!^;iv(Hskus9YVA64}0+Id0!2*4enh0-HoC0 zX{S9|4l@A^Mxoybi6An4{GG0}UgTX}6g4O7Mw~L>|M;<_T61_H<#o<%dN#h@=dbHq?djaw90Z0=$f=m?`Zh_us6e!P%E#DlNY{1|l!V&Srk29I-Uw-MC#1PXx3X zR&s3oM2&&Z`R@E)ZHHRA3{Z#ReVR_(g%12z)N}%9quj z!QD!hP4!?JN^i3%&$7MaZAM&nMynYHR$L~NO*fo%l@LaS1alLA@(C%JAdx#K%;^*8 zdG??t-k~-HB{C%@5Hc|v(C4*GL%GIz%w;*XYC41KSi>^1AriqdzC}>YcllP+{!t>P z#>{Sg1dl9phELtTRLNYCe0g3-cF!BC#v^_YxyEE{1~bQMK+~JWKZ6t#DGgq8s2j#} zCR}!{+l~7{F0o~siWOotU<|n<8&^i7zdR=TD(7c9>9JxnQxJ?Vkp8V3QEyD{6UGRl z{UHjED26ht(}_XcEdHJHoK@(P5`Zl@2L=c?>yT3u_+W~Obk`>4JGAe`rG1=)QSxy6 zKmEf5xA;1r$rHPxd}rc#gGT$C3vhTM_T7u;J^ehxZhTsaUz5y*zuxLro80P&&zS$i zY%2IomY0~oe}@s2BI zob7ED^_5YpF`M-OF^4;6N(XvS`utXO^6pIzH=2^0Bv_yT$e(5)6SEOTLX^63-1&FR zXEQ=dNKfGX6UU5*0}b2FM9;%)j(K+XwMFh1I0&&f7qBSpuJ9*o)sV!aS-`V7dnwlU(ubBC4C>z5*lBUJ-G9GmEL99r%!KDB9#)W0&!=gW zI?&tWS#&ZAc{a?~_LMh+ZvDV$u3Hl$?v84`gJURS zRx#m3`qSi!)%4Al;m#E9^XoqvzU^vfBvi17eYi*J<~pa(?ab%<4kc zt6vywh*0GK7n)G2o0nWmSH#`$@eWp?(|hRN1Da6|3GJ-}CEudKzPu^+5q(y9U;*tQ z_&2RJaF0~1|LU7wAd)Xz=bPM4AW(Fm8t}=F-+cp2pPLhlA9!T}=!rkga~Vu=A{wWmw5P}xNw(K zY793Z^dpbSpffVa_7@3k#_|UjGWX>u)cDLNtInZvy)2sfVpO^hOJr&p z8kJd=a;O$41|s8J8aAlqNfmlQ@*E>x90WeD|h6K2-}HJ5}vzageD_N(A{nn7@o z>xh32Fzs7CF*2%oC%qQg;5yvI4l*ii5{aQ%?dmIJpVF0EU6Prip*VIb4Rx3Qf0)X= zPf37n#}BdG8q>vyn$+Y;KQ<1Kor2#r>Z)ZV-K&!S; z(Y>IV%$16~pGU`dv!6_2=QSEMI_*VxjjcYnX|c1svUa_m_m+osl+&aL;3~gZO@*c0 zBW0qCx^x%Qn>MKV*mBp(2#xr;dD3exkbyS@cBVb;;NJA54~1wyzTatK6MER%G;f>a%2Hz(f7`mAAh$Ix};VpdeuHHZ#cT~bXfszw_hSWdbC~9 z0CvtO)-Rs1PbvB{47XUb$wKerxdOD9nqr@i0ddh%3Yp*)z$Aa!>+*XnosrABjd_^M zuuJFMq6`E|)takxcS1(xSbTyZ>R=kW1w}#h_Md(0(5=u3CGC&QBSN#r)hLuu6iZgY zhBc%bOa87`%>4M`QzA}OuESBcA?drbxM75$j=4nVU$-#l5zU3?llX3eDkz1Af#xhU zR6P(#&P97Psi~4P9L0zb;$7oMOR_tX(oMcYWF!(A6u3>u$)9C`C2O{>kK8F}aaO-$ zW9*%HZs7`x(8ce(T}hLdOBJeh=kvec(cw&4^`3he7+pz#;nJLA5??ISl_733cY>yw z<0=(bGYvscZF=3a>-ea9C=dx)IGqlG1VcNF@4sxOs^6YDhzR`gviF+5zkp@jGwwkO z?=!TY=f6^7c~MtYBY+&p@5M!t?PDdJ*s97BF|y)RsS*lttZC>S)oZXgv0siJZx9Hq<-WYM97ag}jEkeo=Qz4!>cKm!jPQa1T>s-zar&h|u+W;TV6XCL8?njQ zW*ueaNRQ;#S}Nh|ckn@m0^UXy&)3Q8HOB>CNm}LjtfG4^qdpQi{Kh`eH$AT&kUBnY zH{SMiVnZxAW(i~QXz|y1ORl-VHz~#)pEZC{w|K-bDd$(FE}yRWNi+{9CEcRgJzm6Z z9IdQ>*1NS#k<ayQ%_j&~lR*Imtd}%mseg+T!kO8pwPPM$kr^$vyT1G`K+`jr$EF zruGkL| z=!!3w-o3-TD?N30oh!DCwOn;^xo}Cf68(Z&$Yup+;nc4$ zg7&fi{>?a}_o@A@>wjY+9w|J$tnd9TNh=MCIchJP=gp_!_N_)0Huk1}vb_By?-m!} z$o7z-AX)MM&Y@7m=_p|ac3$*a6;z_yZ1$lLg<${kwSTO>3z2z42 z9h4GTfZLjr2-If{Uy7-~-iejB%$`i1vHNNfiT<#z`%m2i2h}!nAnO;QX~H7S#dq(( zq5g0p6mq1m>JPwO@7X4E!b|J`K;Mb()nNCDwSE$kuk)tM9-^+y-O|?fB8BdV5pmSU zJan;X>_}X2a|Q*UW2l_GRzEd0?YO#?kX?? z?wdyFU2ln@Nd1Y8N2!1)xEEuuuaGn&jFT)ID<0#4qBghnsQOY>LU==+%8EZBbQadt zeqddq4h$rX2Uj23sTkm8h8ox#z_5j~@_O$<#EjLxhXdtTS1il8W|cAKD0)lp6np*W zUmDX2BC>qB7dm6>+5{8VHWnV3+oGiVer+M{#C(!im=u!!Qkd0DYx-RSbEtqgZV8PX zmYP#5UJQ7Rk{lT8Y>@E=P%>IgT2z-Qkr{gXzA`YMy8X8Ae<*DGM&jKMgvPg4?MjjH zr(m^USvfK|QC-WlxXwJDLLf7s;-S=*ADO;hrB|oO8W130fbMTKWWPya&h%P{g>l2N zVA-cNYto9nk9$SZuXVZmh<$Xo;d#==eP+$c{|;;TwAxLh3#Yo=Ypib6Ula;%&VrXB zCB=0@U$lZtq=80RNa5_gn$ZSS?>*{@yYY|X*od+_@U7O$k1mgXZvPeS&)?LaI>A5Y z{|!_AYa#FOWm*4_9KJe&Y-o+{cbWjY)feI$?87C6ZI-@ubeel7*EZUg7fGUm5jMLt zUn)$t_im%8=S5?$z~b7na;IRc8kA%=z+B8fRy;Yw1YD)hN~Jy*?_9}r4(PGPf7t=h zG5#jTM}=qh`$iMfc&v-279mz&{)u9+-JPkx<#6m_zQ9My$j|C z$rCD;^2O$vI6)oPcktJ`HZ9O6w`OsleQ{Qgtl3>HZ>n8%fR%{NJ71x=xP+mycI>oj zK=W;QCmc(Rwo~x79n{UKoIc6Tlj7to9%XR!&FSyYriX~Lo>p*iDJWfTJq$6!e5&1a z9}_GukM1SPA%moc-_=47a252U!s-3`la&5xSG?HTo4PoJw+eqV1vj-J@`0p56@#K6 z?;ku!OB{TEX*7n@10@NV;MGf2@^9$Z}>lSFcAA@~CgLp$|vS@br z)&Gb*7TnPM@=qGwciu~m86ERBG9pl&>k9Yk94b&ZU|Kb#X1s>d2#`%NBSt3Vtp}wi+qtiiWzj8J#*_a>ZPRVy zuC51O{+kw9WCD`(THR5Wb0%^&24X)cF>77U3`$T6-!)7NfhCb z2b=p?Lndf?EZN8{5i4X;n^0tQWzsU6#X)jsnu7+3V~R*{-Ha=mS}*b<9y!M`eWrie zj;7*NFT68FoHVUXS?70~Wz0OY&k;)RL!`%PJ( zrOd>(L8c)^5}=G;ii7KaCfP{s=)2En*M01si# z+Q=&@<8Nc%-M*qr3_|>PW2Wno+c;as^nyR>9V{;ivdPEXoANHPsIE`L{P)7ctO6B9#O5lNKpU4p_qtm_0xB$Do03cCGI)N!qRg};=bNq!KKZwMSFttkhKB4 ztFJXz5q_gbP>ZR39J%aw}Nkx3pUFf|Hi> z!Mm`Pb?4sTUX119A(D3l<-CcY;5G~EVZ)A;jqLp1&(xyFU%tk4Dj?pX6b4LygjugUa7|S@t_1@ED9#kZaYF zjnTiHtXFH1yM|c2{6C4ekvLIqGKKhA;Y-{#ObY3%*-4GSr=Hh3@{%C)VfeKc1wiRFG@64~@qw35G-ZlFZt5FkMp~f9oZXC5hDn zNU+2c70?lmna-C+Lt31p-wWHc9(AbPz~$R!=*8jRMq18_Uf8$MU7yWVH1^P2XlP7F#&D=9@>f?o~9 zwEk^T9=FTNFmIFNm7(6jEN`0=B_GQxpx}39#VCkSb*AD?DEEp5(due+1qkfHy`pb| z0Yhvl0^XEe+d<=|b?-{afEF>-7`Hg^>4=DH+c} zXvd~f9#(YG70V9q;kbR43_1(bw3}R?N?Gqkm-p%=MBe|7jiE-I}N=Mh*{@ZCD>A7NPJXb@y6kXB)d-=Qka;qtze% zcg}5?2CzNZ<|qE6dl>vSmT7)R{TG3|-2IW_m6dIPDod(!8&}GK3Y8Bv52A!?L+!50 zmVibzB`SuzsWaG$qF^!#-CLx9jj9t{z(w3~+c2&k6r+J-L|ZFqJ4B^V=K?>7RKA*# z`!b%g`u3B|AQ#VN8U*8SMU90XsZ!9JZ@#vh6#>ff`_-h`{=_L-g)61}NOiq%kvtfy zn&}j&CL0`Jbz5emC5ox~z#wg{L_MpT1yp`({WV=#;#Vmm1RxP#B*Ef0p{e1r&HYg{ z#0s4<|Nh~wV^%Ez<$ri@Q{RaVgf*b-)}jjbtfUbVAD(|uMp8c~n{5}~I*A)h73%p{ zgLnN=SL^Ke4`_x=1XjkcE^jgo>V+f*OmFJHEl+hjov6fzBpYY#JCG|@$9|(5{bobW zdwPK?Pt&Dcf}51uiB1aXf!In5hCfh{+%u{7m-K~Gu^6=F(F z?bm9jjv1qY#8UqqhtTmzFX7Sl;rRF$Oxx0am5Bn_123v}=m*={Y97a1_Jz`&TuI59%_MRyQrmaPf3(my*s4sZoIDeH!j6cp(+5Of;tRrbp%eNN3*j?R zmQeLW;DyGdfphUJ{nXnc4|HwzJ$@#zc&@B0jE<&T?yEI3!U~~)a6Xx#W&|Rv`)Gfq zfhdS5vFoY6Tc+rLnCKDVoY48@$-HQ*&Hv&b2=<#61w|#M2A8igdvY6#;6~`us2LWM zxmz{b3nWYOPM7?w{S9LHL#d5q@3t)2;!%%R^-0$ABKmLx+?ut|iLP=fI^lZN`k~4P z%X$y|`*~7J6O|U^whLLF3I6=KeW7Gss@b($L!E+6&ug(b$!alX%Ts=un5MSAs1 zq3T{QTr3LU}{OIS3U<8x_q2m1+ z8()9B0qJ`o+wVs<=6NJ0?T)6qk)c6u6~VWSeHXQTu^tOM84{aSa&W%;&44JtC4c2A zS!s;3SNip|av_lDc#qOTT51TnU~>?O+C!qmPEoF%sm(+F{ySs(9C(Tpw_P?P?$=z&jXW^2&G-cBWOQfq$SS~pu){b`T|H_dK_}(pqE-p(*a8}BD zxrE@S8hG9|eqWO8IrV_m9`|T$aIAGWuLyd59J)nkgnD2?pF;3J;om$f(&k0Vb(m2KBceEVd1?8nz?FL zKNIXXocG^3RfF2H6~kTgWFrJ=6z)_%-h5i&%D{!*>+NNi9~BS7wwzR^{silGZoQXy zp75NMOW=Op%8I+R@ntMu*PjTSL2G(c)K?F0j*;93#7Y>1)3g;?O&j|m2piMm&^r{O zV>h}Ur?c}oDlh$I-`&ELc*U;^TJdlpDTOw-=P#>HdcFdY8IE4RXJhPF3=P@rIZl|y zYX8>bV>qd4eq4(aZl0E3)DSRE4q6>WAJZO6Ze7|2-rsYGu8n;c80O0s@^(vOki}@# z^WvOwz!b~O%9X#tc7ltmi{C>YDQyoIS&pQu*Lb+e9HERK+v6KxdD#Azs^!rl{(;7+ zM>$ml%UmRM6h2(qv|*xp@8O7Ky>oaax$earucxhcnch3`K6hSGPtO+*tASTPyzt_> z?!dIcDK|D(7?w$OO4~^^t=a{zT2F+EY>ov26mO>gc-UhGO0Pje{hVb|O~(94G&_ZQ zdkv1+MDUTdM3w=A@~1jDaex1p4OF%-q)sw{V{%iM+*V@n!mM%Bkt?JpAY=~r$lJ66 ztX96bvdZ67PAF%LRr_yaPuIOPnA~G=$p|Cvl!%x?L5zz=zw2x9yUj3=d9)0? zaHc=7mReocFZcF7mjf=3FjmjnD7ufH-V~G=LlDXH2qTI8S#zn&Xtz(|mR|tUfDKf3dh`7c}UT)qc-5{)ZckkLf=ur{RDGRRdOvcuZ)(#CxzLQh6cKZD8c^&4&13z1oIiI6w+x~p^Lk4Gdk@JAdRR>&xT?pB_FH$(`A?>d3Lf)g$OYR zahDPU5mH3q{d%bOKFvrr6YZ}R6BDS#(?O|yecJ-|XVP7s-jA0UfZ&XB{NKZ0A6P^L z>_k1lHJv;!nXRnOcJ0hoN^sgBMN$$>}!2y3I+4Z^2_V8kJ^!VwvAIvr_I5MlxJaD=~vqG?&i$umcF04w~9!Myx8;Kxds*St~9W^LggR#HnZB_rg`9mo6+KL zM=^F&snsj>PX@a5-7sY=A$6I>$O>*cDlcdswRK=OG&ni)40)&V7o2WemeMcsU3(^G zJm&?k25U!@qLT4$Qnq6;@K>gz%RTGs6LQ8n03NMjq8eYnpT_3&V+vZduX1 zzg}uZz_c<)Ie!-{>vAuF6`AFM+Eg|h!vovWfIuQBqEsA@)Y(Q+W7*7OEHl(&>I@!e z-z8gOGWUx6%j~*<-c^0p;uVkGU~8sS`CsN7vYzW1a{;y=o^x%c^yPb>WV<76h23ge zEh?lOJcN}<$2OL+@0P&7FEE?pzN`tK{lc^eWK0u;n{{_#3-e`-rqH8s3UM#&Q*D$sB~o?${u(}n zH>Hm4DMXqLIaxdSrNn8jy$o&O@76PD>}xhMs`C`(<8|YcDu=@Dv32xA>%XFe zWd0GmJ98~gEK%Xyds$n6jaa0S4nGBU4CzR$_{aeLTnLNO%4 zkR1r>Nmoj4x}T|;%yAnv&G+@FNy|EO1S@5zd|>gas8@1JwwE;QvdmPM>m7GR!&gZj4u@G8UX47UmgOTVgnr zZHV!X)F1)2L+I4fjpY+8c>m|k0ocLUnyAPr%v4mU$Y zM%Pre^_ots$N>$;-ok<3vG+6!5 zi_KS_0XTWtpx;UM70wdyO=)-d`ANOYxy2a^A8aSa_0dR;bE=L~b4|ypEB`z*-F{vJ z{LGd5VZspJNx-nZvTiqkEiG0EztP`D1`5d6lEyy6@hn!hK_3MjL|PBm_8?X?WP6o< zSHiO?c5JZIm9)6sHH2B17J>><)#>Q|8AI;rx7VF4MRYXnqZKR z_I8E8OfN!Wc!rp3MIP#a3h<1|>ZwQrV^#BEbPpZ}-zKnAOC z-#?hCuUfv5izC}Xne1=2u}LiL<7nOy;=gm3kH=T5*Qj!^yiTsfnJj^tTV$@kcwQ~_ zTG*}4K0BSjaeDUgtH>O!n3)z7$^IN`LL>brjMfpSN85+pT7Gur<-ZXoZT98ItMom` zPRT^uOd}8P9$_a3xfjCwr3txee}qsi3(XUR##Da}TiRI+N9+^8h%tSM^64q)hFC@9$bsFh_c_2oKkM- z=l?KeZ&*d$wC0L1=7{nN3U>p|x@f%@pZ@_E@FKW}D$ z)ZEVj>r#qX_DfZv#$Yj`&~+H1KQniYTgq{j|4-wwI|sEb-ZEb;eN?bF9)WP$2usSi_9vRu9y z1~lHgd^g+u#_gzkEs~W*26uFftqU<;WR7tF$PhF|bO{}JdF`i>u(=}1gm>#9urw*H zxoi%D!_>qZtNEu=R~Y;76Ul>KQb18VkeBu`Ai5obkSD04>&2eki$C~%eo*MXm`UJc!`1ilXG3`)w+J`?AZf}eA3U>ae zR4Eyl$NEX0Z!u~eO5(%e;|vKF7W5jo3H!0;^7W1X&f)i;inRwjhr6=+a+?BY?8!Dg zTfv>|TDI2L3HE9*jdotSojX&Hmy?sW5$hzDa@^?o{vS%l`bveCpIrW3)mm(>J-c^p zIHrw-2W$CwK|I)HId@(5N!bZ@KRH+TeKlaf=FNL;L;owp9x18qkgX42G9JB*`xw)E zq5BUW-COU?y{l2{_9o;2*tO&eo^mPt3T>AfD*SyXF@S zsvrOSC;xkxNhU3F?2bBLz)HG2ojUV`F=0G|$5nsKVlASOilOz|`o2Nh+P|UCN0NN& zO(P`g4CF*p)XR+3GYlifotIeWI4o<-tEn|?o|(3H(SoMP{VNdkW-k$UW`Iq{r+U| zAx`<7<|T%L8N)c^0!wz@>mJNNyL(5dyyjhg@|UqT;{iN@PbZWyH%bXj9hKO0@}2dv zRGw_Rn)q?-$(=g}T$i&Fv!!?J<<(vC(EIkWtBCutVLHFcn92e3vx@~7wirFCqC&1L z`l&SAO$7fT+84ZH^@AEGMPoyT zYTVLhW#y4~o9z|{zqh<&!%ZI?0DleJy2Of)=-l~qxRr809i`Tk@Q8MGL1Fw|hnPl) z;e>;1_P?a?yqd1zPF<{xmS;fO6G6V7y7f!3`JCmC;NHcCAZdxU^i=b+oAd5%VoZq)B!ppx)s&VZ!4PDwg_c_X1fhdFos56wH~uLd{5WOXe$riV z5H+>Hg>&5}DSUEq-74xo9=Gww(6z((=BRvE_3mKaj<|x4+E;-{<%?=us zTaIvbCX(Tl3^#}~$xzIcixG$X`^S8^+@eUN;h&-yvcrYcMYZaa9r<>dit*vTN)MUq zZ8=!m=dZ!aqnZX7t2AdpjRts;;_oNu^r1fG#z77!`=a~1;Sk;{z9v!gv$o-CrUe8# ztuP1^T|>mCWW9fJ(sw0l7Hf6>`8_LXicp02#mLIi)sApWfNUno`%5=5*fob#Km2@c z<799))_BLeE3;GC3%RnE71y-uaS9s?<%$eQ;$djw)lHw9tG;f-l#KY8DaWYYpud9Qk$E=c>F#odSPMjG{gJVqHL@gvno7kh8nP zjrv6|vU1m7-@c2Czml`a*kax3^7asq#3(21m%cQr%e)k<*{>s^_=gNn<$vd(ZS35R z@BZ-5f?cpjH&AvLWUSzj)B7eaKOg==gG}G39G0`3O_J9$A1!yPPkA3TZ>Hq~UE%6~@W(>OXnQ<``*aGs!|%b}_Bvl? zq_X;7T@E9hn%e+Ki{e`crB)aOK3>BTe-FmVNDn01ns<&Avy6KzUUP;dJ=*6!WelUT zKn(9=oz9B6mgv<>llGdwum!k%-aUWyMI?00N{d(~W}|=Z^GNyZ2yPA5zArJ&px=G< zzqfujE{{TR&tmrI717!H5DrK~qnrM8Zd1R4FWzJ}9%1!%4RARLc^PMyh+(A_8FIzP z6+KOyvKfXy-jok=un16(JL3(JV6Y7oAshtps-_rX|qI9RfCphtwD^1D>Ym_y`LS>VoNf>Oy)A9e#wL`4-(s}tX&psNT z6844>sZ(x`lwJ&}j;XjEy~R4qI85os>_KfN)oM!ng?k47H#!qF>psBq27;5Ylp3}j zahtP)k61P4))EbuyPx{C@`593ng(8;ap((&j3a&56MHCAq2UMvY5>+8KWUJC{Y zUO#{MIYCX%i8Ro1Yv{{-Wep1pO(WHVl!nIvsb&dc$6a{C+muOj1JLgrc(40Ikj{rstL$rFw;mi4V#j2`bszrc?Y&*CC z(uqx7nM<|~_Jgj=e{L3B`6uN>DC~yK#v5I0Nv#2X{b;c7ra(SJ8TTMb_qw99bXfplujPhYD*`&R?{3SxCH*oR)L;1bxpxpqiu1sAQI5~ZI` zop>) zQVMP!zgHw`jhX%88%=KzG~`X`u2oBM8;eBEI=kb$wRMp0Ztvc#`VETLfpezINByhD zzP?|d!548$YkRcVi;nZGI9TTRl2Net#%=!f@34;+?Vof7q6Ms9)aidVPj50C%zDmr z;K8SPtR31lJWBNrh=ml>7yT2vA313#k;R?W&5DxVzcdTi0i_H2Ls<=Tl#D3Ri5-RBd$@j+08qcU8DsqYu;(yI04M&sPU%jX@YYpBPt&utm2nBoe%Wqw`azBEKtRzcdmIOfy9n%HmdASiu z^f1U=vWxIFUMm^ZSx&m2z$XxhGtbGu3JIfBmN)Lu4Cf2<-%r~BS`$at8(<52gtns%GV*Q&kS=cV+n+Y?_* zo0H6OzCTA2-k=4%aTDWh>&(Hof!|N`UvkTwI?v|+$s=I#3;QN6%~^e;G=;4k4zCfv zk=4F0%=d$5x_P;*w{YS>+bF&#INQxc$jx2?(VM8{oU}L~uRIT`4t-Ean$NU5B1W=70VNbEv-btd=Em~E!^S7d#wuTM0cp+# zOgU#@aGK|D&)Q`vbsjQsn3XzeL-I9-87La&{C95e)q#(*PUW-LC3cM|M#O_JyBs=t zB-qNWe0E3kRyoG^`r|dvM}@h24H?4fY}l-X;1KEktb6{A(vglj$C0`cN7*^a{&mby zanTw9LqB55ERz&G+$=QG1*KZ8-iypo+K4iqdv@jvXmsM4f;}>4|B3#VOMAbH9ac?y z3cO<~Uoth}_HF#&9c(OdDi(E+?f`d~SU=6aG-8$(u{Z#Wce<7XHW>Uhd`(eUgAUple z({@x4k(F73>e+v}9e(>@q76xg!Qkcr7bND`FiQRSM6i*rxiHUE`yub?B7;an`GU+CbxAXS{V)pC|qU2FOSKmk>8dl^=P_h zs~PbLiET;OnOVw?e9{Gtg={aR<^2vJVOR16NjvvG&Cc9E2-l_iWldc$-x>*u$~}j? zXCmMl#t|_ze<|{C_=grM&hPr8WyT9}`8P#9v=$vN+rRhdR^MN%$oN)Ix>sf}zjw0% z(*o9$&Dj_6R&E3t-R5objtasS9Eb;&X z&&rUoRufVyHh+hc{NAaA&vy1-vL_pL7Z)CeDcCV`W{*;ibrsr1_S>jK}?&yYR%LB5!_o$ zG*wg-)or*d6!V2=)+$n_8mktMyOH*B+0;U6`*r0C)FS@9I}r>&uEG0L$|)(!y4h~> z0}uU3057&P(r2UuVY6eDS!@>~-P3E4&OE%(m3!IOkJdCCCWOGlRy@D%A4Lx2BMMex zi=l&;PR90?U<#SyyC=A_(E7}e_15Ivo_p@NpP#}XcX^)5i8)8HR|yqR0#RZW9Ep}1 zUGytuQ)7_yMMy3ADpP34D*3xSK-E%vb1|ROt4R8bK1oXKkg+!;?w?JoPrh`& zwdMT5Q{&oD{k64|XT`ub*O*bnLbyNf$j1M+ezCzKM~+$&4?2Ky6)v|?&T$)?*s4y6 zn6=G|V0PZ(Z>^M?8R`D^UR3}ou~Y$H36EO0ack1bJrwPJG9h-r`aQ&i2a zlVJZ#gA{;8aJ!vQ!c#%`^yP13CNxH)`F!sEE@#2XWwieulZR4ZHe+bBCSQJ{iQt9~ zd=S$0K zNCN?5@H)+G&5#|LEwT)^^4dk0UPI3knVQr+&_>wc?Uj0S{LsVPBoEi znHoQyGY&0q7I8Ba%Z*>bKyY0#%>rx(O9QO2)sI-?6@geC*=U~&AuC4 z7*xEG*9BXTu=;J3w{y8?`VzT$jMfc>%&FCV&!lbnaDKN<{Mn&(=2aZ8@h9Pr4|^~> zRhgHYCk9>xO?*&`AOgp5v6c9tXmPWA3p1Qf_Pqj(WT^h$I%>6D0b9ASA4Lv!y`u%j zL^O%&H_omKdlD9d<03-R{9^lkp4bs$JAnjG1E5+KBs(9rYvh}7tK<@#E1y!4 zu2_fb2T2!ae;L979wj>Xe_jOG=&CHj;Svr<6mP$%#Zl^tiNp6 zY&(!he*GW%ZeP%#PtNb(-I86>&yM9D7;coV;*Z0WPb@OQbrvv2xBNJD27}?V0rbl< zu0t$DbkHRn=Jl371Qq5=&fIibU<8yT>&)JAaY^q!selG771)?K5{vs4Iq53*ahJ^E41{T^e!ugi zd`vs?_~yp=eu1046IMph$(B%wX@>0BNoA(0Kl1YL^Ycj*xN)b3J@#wv$vth54EVp7 z7vNj0FvF-ns4>d@+}>i2>~rd;IoI7O)gjkOS5LECfr~jVUv3h3LDP6KK#n0_nWTUD z+OW}Pndf#uWN0dHLYHrY+4bi))#_5SIm=+L%P9&Ws#`^y(;Cx{Ez(zi#xEQLwzp|+;C8MJ}vvhe@U4Q6~(fhu=ywyimch}`kmjqZV z17FAA8Q-r)1Ogc3ylEZZ_`o-u{xM3uC&Dd-QHhe z)w{&XA*9hxPv=#>)d0gM_CFnnJPBV`$7u=k9n-?2=(B%Zlxw|0AUWP_iQ2u&3p?NJ zs3LXJhax_>%sC4;OI~S`GSfo+q2=voV`XYR^QKXD=79}$%|2~1xX8dOM)ue_??03$ zv^#;y0UOvbE%l9!CTvJGX?)1X(xz+GWqS6sRC!Yq)Hq6XeDA=Xs)j1Y`en&3WKOR5N z*YBQpBOnpb1vVL|sDISuc0S=eMgcGso<%VcOi<@V~=oug(I2PPmF5`Sm=Cs_Pq1+B;(U>XoZ1u2x)QG)*h(9rA z_sH86v+Ao-RRlIgtP<4os^-H@$;cg5%ZDieclJpojO)zoSotyMvpOCIqQ^dO?;va- z!ghc@%8mhF=NgAUWI&r}08kbI7Lg~^LpPD3-Nx9NLO5-;h|n+lK3%G~tn90@2>Vx& zND)fK*mVDPYjIfQ%@zY0R;XiiaR-o%w(s$Qc?3Vk?KCuhpo7jQ5%1+}fB(s;nsz7Q zY4(=1W1nLdJ|0sdts+_2_0^zJLuriTq-G@TPD4L1lhLSQWFJg}>gmsZ8S=u3X>q$& z9rxTe0)yKWi)p@_C|v!Z3(v6O6HwErQ|r%|LOdpHMP}|g*OX&#iS@TY<=p%N2Rj^& zTHy>pZ}GVz&5#xU?h3lrF{4=n}_4={=(Ixm7kd(WFscECM>nJ zs>k32=aDpH4Ss^U?3Og5iZM!WcP6>dEJ?>BhA#2nZs)D!i7FrbM{UG#;C6YrF2DxL z4KX3({Kv_M!QmU_%fGq7riF14%hFsCC2H!ubxjtaJv=h-T(iwE!of|t!RBdb>cb?tfQ-S?LPKg8Spw5#6TsO!BvH~_5*E* z{mU}af$hoT<^l~1zJXZ?-$i3MIEzRG4Q@VWf3Txz6CYZ}XJy%&=t118csVf4eGVuy zHor$;Z7@7d16W8qKPO3z?{R=`l#N7^78g-idINe)6Mvc`N6buC5xQy^3WS!UWoEBx>rVW zYiniEMwL|8aXk#d2o+l*$`?lAHkx*>`j!c2W?GW>kr@s&TOxg!nUquOb^8&3yZC9Y zxmuQ9C<+zeQ`i6SiinU)uEQe=+hjDOnE#d;uW0_IlG{)!+SJmv)#Dq>LCH{u!B`+x zX^Xp$#X=ypfei|2NylL`NL%Zyw(om}S5gg_-`~A>5!KHw`;H1xkdAPg{(Mf`X?jvK zW`l?_YacLOD3eU_MOun7$dEYJ9Bp%tj3g!15@8CRKY9#FZ?7De+U!DayDL~Y` zd9(^qb7t?!guUTIxwo@{H`|~JXO|ayE_awe+KE3J(Dq4J9(!qFgCFJqhgCru#_5l* zx@0+f0Xa+f{5|g7t}Qv2le;Nige7fHZe5yfJ5PHbGk(=UT`agLAZB-0fAsAgJs#sp zA0el^x{HNR_*1D)C6ygbb{PjF&asg2BRYw`vhECDf$9AA*KE>o|TY$vthEV&qtnjf4d-OPE z_;DKCFF;WJ=F7c1^9fs;Pem=>ysX$L#R;oRSC(JfA2%s9+BHHbKpitGEox9C@=@vO z*2?_p{?f{zE*?@(wI5Fd_3uyOdu)zh7KulLD- zZ>Op5M%A48I-L<``~H-6<4*B2)Ww1u_nf)8m2iEdU=(TCz|pbpz`=HHJWk6jDmY#0 z0Eu8FsE(K+8zaAyLTv4gKvo|ffduss+Vo(dc46mf_lw(w!NQ*LQu&E5d|!K8#3Och zEX3@Mirj>!z(d61t!-VpVStTimTC!~DLT;_gk2d`P9L-=sHKN*)k%t@m%Z%Qju3mo zH^`V9ymQG^g%>f#no!Zjx6%^=Uf96Sw@T)RwtP3!Ys)hO1V_z6lxv3v)(+O)8N`MT z&I+LzKUQ^Q%`6iPPOI#5_5<;X~^p%U+36tR9+ z*&cjG=c1f{wL^PqYBqZF9-%b_e!9JYr7N`$Wxic7R41rUeJ)@c1Lz@}1>vgIGU*=# z$DGrqE>mqkEnZICldi-R>cWkJtUZvZ8`j zy)5K1@p#5ClFSSc9D|Q+3_rj0sntp;a2hKAocFRsy>7I7(0AEA7W!KrC)@qt?8NiF zLQS(wpCQ;FGH`>WE+N-+urjnfcy4a-4uxX0r4q4uv@*Uv@Hj$B0kWfHENw!LN-?+# zc%f}G)pZ#i;g+KM8g1kNh^o!Euy!?^M6_r-y~i0Irv>is2SO%{r~}88!YGXMTzSKC zWtmJfbbY0&G5x-Lp5Y6(S}4Roum607Z!JRTszzaV-x|juo$_%Z*x6(Qrg1o03b5Os znw1)40FU-j>m>oORam{@-7EY#y0Qj6MY-8CsItwUw7+cB62M_ZiQpwQ+r-cB^udZ z$|QiS0VJA~qv}L>W$Tvvb$+^rQpfYwkBVVS(;Kf7b)x^FUUPBzx6j4>$7jw2`|?$v z3)hO&rv*4u8I{($bi$%=-tLLYZlEUA4J(^UV1InNaIiVC$5{3CY_W!meS+}g9=cT>DOTJk?I@MIm)OwNoTzpirir(Uv5PtTN{ zFr>+q3~bj=hrf?}O{XNPw**?z{cm(UjFSLMvyW;7r-FzLwSX6AsM*`*vJ^G!`EPaT zkdn~Vg6(ikM!)b)v{d}&KN{Q;@{@#=JGB3@2}|r{@SRIhmE}6Y4(X|wuO8Q? zOII9*AL~p_7%01Dl2DN5apvN6yt?k6t-L1&g9b7(q4>64)PX~dTx9!;l(N5U&qmc{ zA-ZupoO;lVD+ym1ZZ*KOD;4Ili%%bI!c?Oe*{)fUx5-F#0W;>OPuXwDyhT#zZymxHw}7a?l^|bLq|HZ)m}9bjGN7;)Mw3l0b8bP^GEU zUQt5dX8&512P`bNbq|#kAf)SAu1jKije{bP+Ofeal)TpyrVBTsYT}*?9Jx(-XP77d z5|QaM;`VI}F3Mq(&i`_JKKOU}uQh}AOsIs789J;Cy3M*`Sgstb%GF+VB3$!O2lhMM z3SDJ=F=`+~vguyxBMxi30+KrgY0m@;?V$g4r?#A2S04Il`c|@37XW_xV9!GG7dZ~6 zZLL~>f%B?TDzo64B6Ra3!qta&MMN>`#NXLPGZbdvaqH$w{HY4t?n`CZyo7}UUAs)C z&SVoc6{8ZJZ#W+cL@DLTn*jDLW^;KIp%!JT{xedks1q9^F*KMk2+S2lzl$%~>d_S@ z;3q6eB)Z<@q!iFzIRNGgAzD`oO)dkT)bXq-UNF8Sjbgt1xp*<>RvFf(ZISkmzfKX< zw_Cw)aa?|^1P0~W*h+^@N|kmq)n$zEe(^rWdTt_-wtlv~e?rmIGhUj7anD1GngT*& zWh#B{x_bD#^B3Rjv(QIha!xb2t5D3KT=hQ<;db+?&MZTs4h(mh_9$XS){t;+HR}>S zVyI~}v5+jCrx1B-i8xljLYB?|TzzhO9NzaHQr+@F+UNnfA?`=pr+k<6!Mdm^?o6!= z7v1ToNW9&4ep;@h^nEBot zu?}h~x@Q6bG?3~}=TuO_V+~LyqR`PzJ3v4Rud76G&0MNoMTUlHc61jYGYto5pf*_j zc6e|a9oAASQ?#S^ZNBrqK$<%1nxi}Noy7)WZ|#fmOgh`RXI~)GAYRrDLt}inSu9QS zxeZ+T;wIc|LU-iP^wfG6=v>a21{HS9Y{*Tk$CJ0om1d2G&X<;GPyD8ENO(s8^I~~KyZRth>XRe+8odw*POu+7UM$8q?&57XWf}WA zWAd>!gC7A@Qd9$DL_Q?YCc7c2drQO2*0#&LzcbP=kX_DgvLg3|q}~VrYac9~q_QJE zj89zV`>I(zRf^|vF-_xWvSZj?y;M4g9n2i}fra}Yfa#9$hQDgS3tmJAE6Xyltn4lp zlg6~cug}=R#+QmTx|{aCX+89WPkxZ`@Z+YsyTj>k*=gtPvmD)dC#Q1CpCN1-o0Vv3 z0%wd&wl54DuAYw9ue9usMUJ;v3dcxiskTF))~@{v=ptJl&N;+E*lM z8eUfe;I!Y-?7F)nhGr>J0?+t`Y#yVdWoWoR4W__f+7 z`n>ok&LITvCqqml6pxcpq3h0>?FW8)1=czaD9;~?U;RZsw9cqVz4{TXt8emOy3+&r z0_G~~UpxI>l*esYSC=v_ujAW@C9z^3^&QdTLKy@6GH467Cnz(>tep_XV_uo@|2bQK z7*10wdjI<0N!wKEiCd3$TPcDsSa=Bc9Z|q8;SRjAsprfc)mSwx#Wb)|GTs45gO`|$j#LuY})tC z-%TEwknaj{A$~+qVr=hNSga0|)EVFmMzbXvpkhJYVPr-sq<-sUh*IoaXe8<9mzk%k zmCxJ1UW378f!u<_kJRY*+26|>JZ||S$RmMJMtM?S$BurGO?h+1Mc3S#Q`yb@yn&`X z{yE)f_`s&QrgRBQoXASA0-cPVZe?afOYWmXsOc8p3MRsxiPHxM^}C*A{(G_(!P0Q2 zzij%)FAPiB3a%ByKSMfgtRGtXCu~QPZK39(B6y>>QPT=Vy1zO8| zeumvh3wJb{cyb6v!9qpGw=KnJzl$eZzD-&6cvl1auT%-7`o77)rv1X98FMuD8+Kao zJFn}_yq(pKfQ22$FQy=vP0jv*uz{h)_z`QFVar( zTfH<%w`g%0Kjt;QwBfd~y~#I3IcWX^Q|`;FlKsoZlP*`4C^P;_jsrTPzk&kYjep&h z=NYt}&-mxt;-fEGB^2W}PQ1Lm3kQdBZcRd}Vf}XBF<@c@WV>xJ$!q!5&^!*j1a|mE z8=(vvY)sEYXC|$T{YdpZe!(G@axY%%wUaH@lq)t$S4wG(-DD$AA`xp8Nb4N-&2|Ix zu-5;k2WH$@GOvz~C_E}G9x>?kr5jpC%)AXbAy3p-k2XY-RD2VODnhr- zKWZHML`$*T<9yNy;^z5~oE)zZ;8DL=34sXNEI#r~di}zEzR3L-=*2IXy~^%-8~huf z7WY;D!&?1Wnxc@w)hYBdg~DhKZ(;U|l(*V?ol#o(m}r|7Y?zQ;D-F{uTeTY!w$W^J zaH~-D8B}Kye_6?(1H-24=p=~QKR>j0&r8fsyf7k z;c93A476T39%9woTZZEf+A1Qpg?f=uo8(W2&M_|@QN7*SAw#RYRCwT?+;uo273hFT@Rbr}uq;{d4CpY+v=m;Gt~u_g)UJ@T&L z+Vz@jy$Nv?lMXMxnB+hW&6%(W^TS|Lah3ZtL5i$Gjt2U%cvH<` z^ox!*Z=TTPY45suxd5B14m7Ws@`z^5 z)&IH!1T-;3)}v^AJ;HXKHePd##teFRx^dcBfBR+;E6Vvojd3^Oq2a=}d(0RGq-)rf zMv=TxZ$gQN-EmcfJhs`{>uVc8lU~{&5KHgzl!ts<23@L z3Z&8P61+x5^) zg-mEq0p|7TCjB6?eIVf)ce`UV0f;HuVpw&X)HlS(LzcpYhaJ-NKCL&jN{`MrRU6*w zTIVX$pc`MS3bWz){%+t+No~s2;f#lzwp!ilQKI_w=OIWw>vx^q0#*T~!!dE(s}tp! zz+M0(b9bqpjG$@9#dSDshF~NIvvU2%@A;C73H=ee6?MamN&>Zq`eX2h*GF;CAh%Vu zvEVMh`kx6{q<4YHh0uH8HSzx_lh#*TcxG|d4oiBHNq>}nrgnxq4VNjicO$Mo>)s9N z3LTF~8!03~G`)Y7mMYs`e-EbuhT{|X0%4Q1bqfX)1o2@CT-S0bY-zY?JnC}U?;Ip% z_bSH|^UW!C)q8=eW9^b4NNyrY1y3BAt#S8a!q{VJ?5#g7XI4 zsl2kwo;kh6Z)+Q3WIX|jOmeuj84wnq}Hy8ReyV^~ZvZ#5RASCBI6ft6;vpbS0VW3>4ToL>!qy`_m z&?z4J*tTWL70YOHtvr~f!tebVk^c3ByRUlnKts!P4Z^H>a~32?MIqArlP)3iQ)E7T zEK|ptJ~W^Dd7hTI?q+fLc@1>+u2y-e*!!r{idPfOGj$mA;)Qh0)&?vV-Go_rRT}zI zZ`?%{a$r)MaMyj@`9Q!3DOcbu8s5uml;5LY{&xApfxgK_b0f+p z8{pMR1E7qQ2v23h?|>7n?KZirqD0~(RJ=cC#jcqGyXVM2PZybpqX zu9fYwqWi|{UDaj-Kuv_BB4$nX8IDglxWvaXTp06(&G7JLyba8CI+o!@@Nk71;vn)N z2CH0k$IW8DnJbZh$4`6GftbTxTSvX~g*@qRpqoEP|ew(GyyZr#V9 z$UAT@rni=npz60!Wd|-^7n;PYa20lQf#cqw*OV8wF=}AICH1XZ0K;N1T5>bi{ zJ1Wd=1O?mZPdGchM7QvRUR$|xh?@J($bWlf;sJA2S@c>cdsa%hm4LB@_sa!H*)VSF zRi+P-R(W9@=&Uv=V~+r-;B61M*-*n`exLof@&67{+lzymO0i3I6ybPiulrTZWP|U? z;?&PGOD%`v2@)D1dt#o&P3Ea6#2OO{uSM$lC#~oj&J^dUz>1Ul{oyR*Z6NqH?T7S; zj@AOZ>*%t~s{^OiH)J?RID@Lnd^(*MHSL(S>JuAZN3AHPXH>FpV*JC8}(9?~dP+&8p->u`b8{rdMWgb&F}MC4N26V-8he-=bWZb)zGYUVjt+lLc<`y>^ zbk8|VkI0K=p2$EImekwxWH=2}@qkDQL7Yx+K~7fc4TRHtdhjs+WK@1v(0eToM$|QyvE<=XO}F!w+NV; ze6_Iu&y(HWVbae!2}+Tz<83wV6TV10Jj?h1ePJwWa{lFeOVhrvYj$;RMWmqKgBQd@ z!lJk9?&b@z$gLai72YgAkjZ1_Xx-xlcMRddCJCdCbzW1Hso}J$;>8}M z7|KC?o<{JB%g52<% za>KzU9{o4y-RypB_Cu0-D&3kNFUZohopTk{b7fokZ`scYforjn=8}?enea%-=7hkZ z;)=g)o7>3i4mXPD=DF3ar!9oI#a#FEn&$|GHAusp)9ul}Y~M*=LzI7^VSGdt~Y-lnOV#-F>G1LE%HEBq~@k#)MO;YNhI$@6rBlmqWd_)sa<$WL&fb z`zZv6F^igKr})H?lIxbR2WZ-pJf9?OM55ZnS0`5-Z+ndB^NIYqJykm|-stBXD9WyndRpdC*4P@>n#3uV6Qa zPgNmw$1DatluJySE9&FIk_mc^C7yE|10JZ{_OGf`~or~B8aYjZn4SyyAGSlz24*=Nrr(p`s>V*MNr>g)HB6D5oi<&m0r)Y`;}N;zXQ zTov`5l@ zK9Z4zvlmYl9U0h0W*yGhhx)g;H}FV#TYcrDE*U&XrdSwfDl|g&xC~l$yI)N5-qE!9 zAGElTXM0;^tIl$NcqZCC#~-&e2>DWYv69Z zKANl33&itpVqCX@>}5+^FnLO4m`1l42?&MZ+IR*m`Qoj_5CmA8!@#KwENnR4)Z;R+ zMlEUTy-0%hx8KkYqJUKYH*c}8x1W|8zszgPF5Bxi#c&r93K6~A#YxPOg|vE45pb`c zgeYk}@;9O()ITYE=Io!**2}g6A7~d7Uf0dbg#^F8`8bz!h5kp$LScK-Lfl3tl1>f&+KySJ-ue|!JB`bYkQiGAez-{tBJ=YBF( z2taK0t# zMMI6o!$$B1^;u)uc^-cAt+}!hw}bKtFLEQ2op{Iyw!@-uzTJB|&(7#nU+Qnm&dLgA zpSEJFzy0ryCR^FhdT1i^8riP(R-gEA4Ds&Du82`4aCNl4a*CQY%R+W9w`g{U>&4Z? z?XhsM&JOnp#G1~pLpCbe1eHu~65^8a? zW|$F)CkEX5jpQtoHniYEGk>MajJKW;hsRfI$52ffes|HqUR`1qPk&VS_1xbLyz>o` zDe{pU;@$t}%e!5Txomj>x3&2a^xVdMa1;b)-x^l*}W@qResSdjlQ@bTk~ zOputWDdqdvCb8eqU`!gmb?LbX`um4D&hnNIwS&^`x{C~7Uih5%{`?^LmPIQ!MA=J@ zF+G*Up<-)u^jL$@Sms17vQ3vVXy5}Ea*QquS9xXc8Fm{@*b(GXSmO_G{)o_m;%M_7 zBd}J5vHOCV&*~e$)U}MsC_TGks$GQ4J$W<&@6+3~{3<8<7IM0fF1yMXlG4xpnw3D4 z9L*Ch6x*0kDBg6(_yZ+iVpXMY}hUx+6N*^IW4iD>&FKur$a=#$mc2;gUNrx4jXlL0oyOw)NTw z@Lt&CQ&-V`qL}?tQX5z_8l^dwjMg_=JT(p>IW7k@S`E#SXqxyTMIwFCX~~= z9BFW;n|E1kw&X~{Y3{?pOvqx{ojplt8wyxXBx3*E+1H5dLH zNM7B!`dImQmaMlbYf_mvL`P%&v1NJyPPsfrE)& z-$VWka10dT9CDW7_X%PxIKtxcc<T$4%f0wnQyN+(Zln&eycLO zWeK$DG8f1A{Z_{BYwD^(mM!6e@#P?|4sg=ky3zHe>FKGp>SrR@d52l&dRSlKw$9)p zz74&@y$2n~YJB1b`XzX_+}?imIcIdyy4<@ql{_0^8S3YYlXG)&NogF)x#FhLJ4oxe z>6p^3IMgj)upH@!8b$UuOllK;%5{)$wQGPG@ok%iA%8^E4mxj-NDsoKY_|c)n`Qzv z^&WwaU)sk@WJ{^M03?CBE3I-NK3xgsT;7w3Gw2=I_Q$yO6?%lbw1HLzh|@x_Ju=b$pIf+#7*}>zcW$aBk>X_rZa-Kb`;stRJk8B!E*p0KnYq{Dr`C$qm3 zZ4=15=7G6q2MCnb=Bbx#AQAupE*fY|TFn8{NFi`x!6>5Uck=M+&V{p;_j4UFKXw8N z*&DkFTAGP!bW8V5brCoXNS~fae;$0ScB1Q9sce6?-SByPL*{E!{qbCd_rSLuM)K}| z*?>)Z^T}4D`(M6elnfUb?z$;ao)h@%ncx^=0`EvV!tYYL*EO*$Jh%2lvQIeg6EH|pC%3RnjrJC2Iu zXC3*gqU+kO+N|*$Fzeecx7>0GuF$>5%oQZ_$SpZe+nw?LY?oK3$NVzrbG5GOa-IF1 z9`x)Q_^bP^!gN4Run{NqCsv-DKUuF|en$T;w5((r8e7tiL%j6ynmC}n zsjq)KzS!w3oNMbQ_1-h%u4aJgzdwI@es%kuO;s?>(Qu%y{r*G_%qnx>%3AW8BKV5b z$8bMs__AMAt?U~nEzvVgxT34>$8b!`mhb}_zNT3s26hoU(-SSA`5Xa5(tj=LKM0c6 zLe@el$P?8cRn>JK5^jE1Td`#`Q}*E>x%XHl0HA;4)dyD+=)QeH@9ybfzI=L_e$m0( zuK@e{@nzGuDAOn>b@c`}{NitWLV);c)W2@r%R2R9Dg<5&AHT(_geB6kTqfp0Dy%hj zJ$z6ux%&z4zU{iLe#v_vIr-&?+?L$Ze@ZffFxV%TV-3A~^~G*8_s=6UB}o`Le@M)t)=9> zWZ7UYSx;j0eGPNWi4q9L1yR4_~l4v}S5+na*@+vm<#yPPw<TM5IEqCb1`)1?vts z#(&9x{{72#Lz{iQ{`O9n>WK(nUd&&%nt*kCn8p3@ea;!Ejq-Q~lKClDtj;#kh}%{B z<(!!GFKQIUNBw2>jjyx5()O1on03_(odB$3u;{#`LwAK6RKoYqj%dqPqR+1~XDB#E z?rJux@rc_HGNb*M{jlg3*vHGTDb!%Hl|lnuoKx+TOUl%3fvAS0cM!a~_YL5$l%?d@ z9b`-XebtOfqfMpIgkS#Codo6|KMbsRGymG}%AQKQS+=CBm4~K7R-q&MYGqA=m+}@p zL#(L2hM3@Ob+;U6{+Er`jOR^P)XL(3%3tdn7k&#e;Y3dYWC8Oej5F$y57dH!qO~9VeMJ-mdMEFv`$zSmEzvySZ9AowJBSmCO-rEtQ zGjyixXwR;Eq)W$Yce(`}Jesu7Qj&D?dNuzHQao>@R_^^?$#rw{!C`ij?4fc}TCG8@ zDzo_VO*NAaXDLXh(_LE9eGC354AOmyOY{KcXW7aGWqlrNW(NL5Mr|uSwvo}Gzt~ZUEwuXnyIb5TX1*gm zGqDrpH)U94zgB;68&YG-SdIQ{%Zsvp+Db2nP|R#{pu#I+4_)M3 zwXrA`;;;+DI>?jaK+os=?vC90kT5G*S#-B#qhiXfH!{FLs>HJw#)|bSm;KtRuFxyI zm?K1**d}rxEAh}ExqHu%uJhg;`6wldR__8@H;F4*^UDR>Duph+OSi>H`J!Py0jIjih; zJf#!GlBds}g;oQ#7ypRkTg~a|-+|aC`2X5ptpY4ohnBm>7VXl|w!NnWFztXr-B@0a zP#X`Ry9h+@pQ8S)qlpU3ss^74IS=$Ial%WM}rC`sFx%i2< z-~H~TRsrCBY{3qIiiIz1`V#{uKr;xi_$&U*M3yw=+Jco*$F}m`gmd|K0iqRH^{$oG1_@=g;_ZNPD2y4w zf{a-5av-uFHC_Umn3b0p=S}u9#Gi#W7{bJ49MTpcOLO~T4Gm*+wokaGawPII!g6e8 zg;n~6<(Mez*Gyu6GX>9|CwXZJJHd9_nem<1#plzS)?~Eh8}p@d9kUiMpy}c}Oz~*C z&;cf5Bowbd-vc!3XpRKquIQlA1b|8uI;1YIeSgfs(m)$mwTa{`I!enZ>?pZ>ySbVq zIiZs0IVQ7-5@={?@H@o=V?_h|1Yk*`(PJ--+|XnLEegiVBwr_5-z?j;3FD^T18aq; z!G~BZ(I>S%=Yd|1wt3PnThGdj4Z=*_5H}}Zo=TSj`p;$3R70r@MdBQMl-=C%~7iHa1s7i zdk5P052GDsam#q#VGI7Ga!o)uK#~W&N{lN6P z6f(X~#dDjA*QALPrR>w?>D@x$RgKoLUZ+=mWsW*!Ew=eu06sO17BsrhC~Vd72hcY` z7I)t=!U6%4IsyO0o0Rd)zbjp{02>U{dV%A96D>(KFhdP!iEJwtUT(}ionKUv#hu`T zV_7V%u20%xArHN^w)fv>nCS25-TM#VYmDW+#_$fp#iSPAvALcG$;BJqYChZys9hv2 zeWLz|d8jrJxcHmZXsij@pMV_O!>9n;9?r^n&3<)0bof&18;f8Rs$A|D?iw5rzfQm&G+dH+|GW7f=!tPRPUmS`^INUMacF zGdiJ&HduAv96td2*ark6w8j)U{G!V#S-x2dmJN#WD@0^?y5cXxMkY<{gEHY-x2^pX zEAub8QjtXIno$|V=jNIiJ(CgJo`<#{<*kkRMw;!&ZNVKLsY!SI)=IpMzPwEv8?zuC1*m#>-v?GBlta7@*T#Sr|}1Wx*=4h z(5By|^q$nlPM?r9PNl~n=hur&!KpPf2A8(=%`$+88BcVRfI!_f@f6k{LuB<{n=;?iLt7wlR-lWNlDm&>6gtZW zjAv34hZCnD@vPnCWPqF`>p%bn<5VWdu_Yp`bqAVOZ-d*qNB?klWGskEVun~P=TVIw9jd=?c3+yT3sb8?6bagJ9{5?L0uljDUG1a z#oL2|>7yTk3jAj@r?UA)k`O%o-Lo)+PMTtcCrmaPR0Uoh2^|EJZX-`(}+>S_o z^!(e~!?R|{^)QKiqUg5^%uNvgq=c-4-G+9FE%?`l5&G~ryk-H4w+VxL;-U5r9XK#jlu2u_44Jvtu zH}Z~{TXy(!0GWn>9RE+zc|Wqb{%^ePX;1gCYj)VNTD4bC2O5f6F+=SUq4wr{T2##% ziP|+HNJ5c8BD7X(R}h3mjZh;}6gAH`-@oAb;klpte(v{mU9T(8GW9;fG^Vj~6N}`F z?m(y%)7NKedNBt|4)_HzG?x^ndTrRH!j!NS;cP~x_?Mcz)`SiKzT25qt9*Et(hn6M zyn`dANV$J_3f8K2fl!K#Ms{OxJjXK&gJ&zz1&+f76O;|LCX(}$1)*4K@EUcfy-JAlReh8lu<{m9go6pr7P?c2adx0cr=u^EXx$!s1Mlf( zHGsyhTkE?`(}ss5%RN%zPBZ+Mg)e|IgzAJIWGeVJp$sS6En&pQz4qe%zp+^>R1D~|X(O6kdulaZL!F$%9V7{j6)n$S`LR%v# z#;|76lLAn-O+cyPbq)?M7-rR2WLdAy@=6Yi>os(;Pt6B+bD-0aa&-@uju=WdP#rkWV(>^bw6>K3yaK@A#y5R8TY8+4i6V@p#j<}JYcs9-rejV)Bj zc+&tBijCMab}M(_4=?O%w#D>)y=#J7N)k}G?W)uLFJQgYHM{!XOLJvr=Y(q`>;nTL zt9@zl+);>li6a&&^CKPTP=GGZHa%<`b|N%Ih3>U**58tCr&jkXAy9_wkZU~l$?boS zNG0cw*OPA+XZ2X!0aupd6vqDj*gBjJ=|L~HCdqufnyBUy$(xP=)<4+3Mh z12DL!3l@!M_?t59%!|K0T;V54B=BvYE_&JO(D&iFzI2c4%%iK7^VQwW`m>d8Y86#9 zCmK1@zaA>jRRKzu)}-}m`|r@EeRmH_@Xedrnh}-hLAGsI(0AT{TRmI;LVQ2zQI_z1 z)F(|qBfnKK5Gj50E1XK2wV_dQxIdU!I@(KJl22m|0uG&k=!%{gL+7>bxQWYt1Jpgg z-~GxxMP^$dzghCLAvBZx`00>3^z3cGp;KY5t><*Ckhi^x}G=EuT$bjVRSh3M%+njl)w-2^(CcW zShBXCcUUI{)wO! zq04lGzJA`jn*7gB+irQ>==s%!zW(8#FdKmNncl5!Q2H~DKMrUb81wy24iJN;ksLN) zZ1=jSG60-rEY1{!-Y*D?%QQa9D`t5w+4&M-(@l;(MC*43K70!;@JC8HWG6EPH7U1S z9oHy-$;k#?4Vqh{cBx;&-g)GkC%>#<-!uy29Or$kuNAATaUOK!*=%dW= z+X$FLBfZ`yWQhav&Q)}BPiL!Vy)>8V<-ns}EV%m5&@Z31u%ach*x0yK1^z6zrb z5mB_7j%i2!hJfpb#&*3IR|o!Ty~F!#@k1eCN(5D7P*n8;;Yo3T5J_|&3Y}SPJ>cu4 zoN&jZYJ)-cXHT=ku^0|U?DDHDhdSi-Z|-4z5JDTp*(+qBwdyc`f_>EZ2}!K>cg z+Wa2TRpPE7j1uJbPBlt&ynl!c1~N4-H}SRpniTQXMPD$mcp zdk|Ia6)=P9!uW0$4-K3IZp{O}eJna}U-K3@_^n1Y8b}jH`4+P)82_D;lbfWZ`-W%x z3N*H2`o>G`6geXDEV(}7GqopT0gsJN<+wdO+VTWno|bm8b>g|rsBK8J37fgnb;@Yj zM#;KH>($%F)HU7`JauH&dc@dd1H+b?7Z+;VN==1fb4X4#K6ONJ2vMj_Y<$Y|QTL`oU`p3|oD{MX8`i|9^6shGF4N2$7z2v9sO$WG~4U~PS)E2oEi-L6;p|fCKc98}kPWbcRrh{f* zUgTpV1i;^@Fr9)Uf@9+45X;dYjDmKzkb*`NCRliTddZ+O;)FVa4DbjzH&9mZGA^#b zV_(@+`9BvA*oIpHm8MY%Z0%9j54)IpzCZsO??k4MO3hf2$2r9Y(jV9XixfT*OO@?# zTs3h<(8qBj2R=B$nsA%D@!I98h)SNz^$J-o4b4^q&;AB6Qywf?!v1m3jJu|TJvGr_ znk2w?PGI<=r43oL8AlyzyisxLNFymm!V+n2^+KCg09Q*Zz$}Cs6TM% z67|elP;RVE&Mf%+>{pk?eZg$L$z)Ze%CBFLYtu}dn4ztx&?xnACI(a9@I@=f zHo3(C4%*{xwAp4HhVI}7>i<03sg}muiW6Jj_Pfc?t7cQCTnx-)%Z|r8&zw%1xg98c zgs`K-Tch&p)M*mA4zHIA3YLlC1b~k=E_#*SRv=b7{t8wOdnVJ15r#sP$o|AOvZbo) zCU~GDLPuZrE_63T>L!VQPlIZHJ$k6^_yB;0fDOZ7CKnOg(I7Adm}3T9rgebIZi&$#PKA|2<-txYGS)&f>MT%Fr9E)I8KJ>*WNA@;4{76%@7?<%4`S>jd|1 zfK=00!qxUZO=Fi7R_i0rrPGT7O)_yBxRwv+-v0nVx1FQ?Gm9icC>Tz6kNM+k=A^eO zR@pi0*ruNaI}>j3+C&6z<(zc%V^gIg1M=Ug@8+K}&k8=(DenD6v~WZQDw z`SWjJ7u8k`fvSV%KM$QKq@XEZDsgq>vm>N{6#06hOYG+8f^)knwGbJ#Q8#sHWCa2^`_Hj_cr`pz1vi$RB%J>(zCw{sds&X2}Hfc6|k zS@!I_sAknxC$6XHPU3=wl7y*bir~lh{2s9wEc{uI_?3RFoOvd|(l|>}0^_Avvs%y; zy%RkhflELo+pE>l>u{qcb(H;sH59Bx6{B{MD(rCT+LVY6W#}X(SostyF#j$?w6(dz zqAHj7@q3LrFGR+h`;(?zh9l7vT=)$u$Z{PE;<#VWB^i@UFum$R8cYonomAWI`x ztxW9Jq&E=Qyy!X-Ng7d-eRwb3!|&`r_QN$(Gw4!Z!W+|~$O!AJd>z=NbK6qh78+i# zr7xe~i}pL(#taFDR2Xs@g<1yu05!E`_6<(k?AethYFYc5$E@fhNCCg5<0VHucD>j1 zWJc%3hellHawWgyUQV8KZt5D@A5`Ri!8hfqAf}e}$-j^4lhHwRVvsLV+S`GZGm99H z#WovAzmuQ$9RHo6dwKFX5Pv*f7n~F3tc$-d`KLI5*S26}-rK)GPPqWwrHv`{Rc1tn z14NJTmP-{+DFHO|{BT0L-rcGdhpU1^+9#og>Gxi$m zF(_+nHDB*u49Aap5?b+ExqtpwIjqKh0J6lZ!=!MHkPE3#1@i0}rAsP`>vvwZlxqryfQk z{ogsS@2{10%k%k$CE9N3QlM%Rpq9vNaCjShoY3#7bYpPYsOj z{T8#bn2b)ww3!PReVpO(;p=C~8If|ZE}B7o3WtsDk*XDht#<8DEuO~3knwd&f&bpt zi2p?VVPp<)LG+r$ea9IeE?~=R`A_U@8+`*Q{uF5XZ9hCnEh|4i<4iW4!b~p%ytl!% zNH9Gh2vi37;nUz&m0G70+C}nM5onJ#Y&gpJP3Q=2Ix_f$eN@q`#vjzob+LT(Bk__% zq2Ken;lK(|OaTyK5q zePq}hV-xrq#tO;5&G@M~iSX7MPs|?e`3ZmiQ>{~UlJF)t?nLlqx?S1dPd;%5#O59H z3Ay@Ph>&Hos~KgWLjUgx?n|@YEL3~EHtMh~UBtLTjhVD;3F5T!WCz5-p^>=o5#MA5 zh2NN~EOS*zZ_%y$81&qj(z;^q)>iLj18WI}&PR8y=NIpI{Ak)TRsFP;Xw+C}5haDj zjQ?bgcTXLVSOOf@z5dKSEPL0n<&H$Ddnz#7Tl4^5vXC%h=>oJFIrAZ`);>=7R{%-cGBJaL?pm1{{ zI24GTh?|!9EeE0LWrT6=&Z9E}&$7oSb?c}G!a|one6$re&cCewZ7Wj3V{)-6*y4)? zBC?lA@psIIlHwoWd;KsWtsArVg-)hqzGf@n$6<_O)ja)n1iIdbpQ!S=GuV#>@pF^k zOn2RiXACDr+uF^0pJKXaJlSJPX^e1NK)hXQjTEgG`engvPJeZUXC71`cHg-}Uf&0( zW%7g`)%!>q>6EfY1&!m;h5-m;$v;GIwqn+jZCdcaiigYvDb5TP^EBvLdZV-2+&tVG zT={y?(jp?+q<7m%Rt`M1vP4wh_U-+B3Ab3?b07E>2I*~@YELB?>no^EmOzZX7n`4y zJKOI!Zh#+8m00s=>zx$?ca3&zEMEq63!R!44wmtqK4-}T7?^A>rkpIU7;rOv0KqAN95)ukpr9>7ORBEELS4QbJ~Z3g!CUDO3)LnTZAk*eVjYtLG5LC*cEpP^L@WBFJPX~@u03? z3z%$=O^^?V0134}gnBhjw2dr;+~+~|6~uBkg0|O`z9>M0yxo3neEc74eKDfH@o8<{ ztQTA!D3k9Y6|ZUR{!R0|Ofar<6A-d2r}{9IE>*BslHRtv<z8C?8C57iukRoR?Z* zcy@eYxCDXnn-uhvyU=C%ogL*So-NM(96tI8(DTS+TPNYE58=#do=Mr_7# zuRvDwa_o^1M|S5#bq+QpFK%Mo0leD|h7siY=hn2pc_bXao?Cp}W1Qz;$Gm-1XMd{d z-ekO@GT((O;t$mX>_5m1IgRew1ep*HTkM>TzZk8o5Wz6k#-QJGH*+JarR?}ER&XU5 zOguUc)7+lBB}->v#PR9ReMJM1sRJ2e;>u>yG^sRk&BVvp68Go)`hE`-HCpd7orZMk zw1ax580`7*@V0^}#loO;S4j|`xak3@&bxA<-Xt_&P(REbCt3~N&~L?mqJKsOBs`6t zXoYL?)xOFcn-{e<{|UF!KIdwME`&&U!&bX3lhQjum&+&H!U|g*EwW`BG{>4 z#X7M~80n!a)Ip^tPZ@u4{h^N@P5EM=%W{u2iU<5N+c**LOK){vKKmwe;fuM=Id#o$ zl%%v*Y{*1oBcwK9F}f*)dEVm2>#&jLZS}Z49QrseycN=x7c5a9>QmR91h=+^kcO27 znR7AiIN=Xtx#mm%oQJPI5P9|)+WMp@_Ghy(JpjJ3IziiA@oG2rj{GgDg#%R60J2qR z9EZ_(oj8H{ZCuqlUI#sTI_qVt^Hjn8k$mGcx%RkZuKbji?&QDUT!Vs6aSd)KKHUnJ z8`RoLlmu{8HnyxHRPi^&>UZPM;$2YuRX~VDZBLb$B`7dBt$3nTuCWbH-}C*MuDBy{ z#-SgjvB536uCT!hy(xNE`|KOmMK^hK<+KOSN=Y**jsNnE%D6IB2ZaJ(r@>QfK;**k zv?vIsoi(lI`f+aX66e3oSYz6$fWbmgXnRvPfZ9$~htc#$U$WKC$UswX7*6#PGVJ2K z?M@=qbx{0+Www8j;%q_wm4zvXI&})1G009Syl9%XF`@pkw#Niyo{~Z&MWI&0*%H*0 zgSO$y?2d^STj0;l?dd7{5*Ao~p$zJk5@_w<-!{F^bzW zDC@fmP{pEfW>}N&1ewCUx6G-|U7WI&%-yfEd zyijY^0kHPzD4L0kEIXpH%4cIm55Np&YR_ZO5PXJA-p%{1$M-0FuQuKMQq*~AbNT;B zhZ#rN_C-W?)WT$f>T0A)P$BC$I(s>+d|~iS4Tw8rC)aYkqM6?q^yBUcBd+eN`wzr zmQ&>H#G8^XiWhHmcUMQod=c^R^!|LmqPjn_@lKBK7FwuT)-3xHC*<*WCDMDE0q+|Q zU!`)uR#QkPuFgZFY?8eO(noGR&!&R4r>y$x`I$*$jPr3e@7BiEBIUB}Hy`*_WDh6Z zfQ5sRcL)jUxz-f3`}7H+DX1ez2(t``u+L=z>%-@=A=VXSTvGtV?zpHd6LkUiI47Zy zpfgt+{-<&Y!9y^6w*`9Z z$**15oVEZsGB$K}}#BTjhPgS$Ub1+BXB-vUl6woVy~=TSK@+ zA#%Ehi`DikeDBi$G=w17cQ$yqrw4awT-r3ZdemQUM`NnD*RI8AFKmO-USQDu?N`N< zMXifqm`OaGhU(9lx|!H${_;5D#uLndI`C*O^9MP*Y6OWY_I#+LHpa%{nTQk zjdR-gvVy)_;;!Ad^_+eUi1CO^0E1PDMp*S<)!v@*)s?6hl<4&=p9?tpOS#F8Qj_w}Dky97b4f%>zNy-r6}4 z8E4sBh9m7)3Ci<=o{f1{#*&c=Aijt><245vr;#~JZt&FL8@V%ERl|jhFJ2)HTj{>C zk`ge4qMacl1HYJqBiif5;A0q0(tnwczOb&I(hfE#9LH6qI+u2?)n=B>{7E%w$xtic z9ABo}SHc>nAAg@QJeBdF`;pdyopP0h(%0;cDSDjCDPZwJ|qX_s8=wd}KJBcT4M^J4xM3 z**^wzcs@;E>E+>oO|sQrW_!55bYv}}3SM#}4MKBkts9XSWK|`0!yEB?w1@L!s}*q1 z7rsVme(eQDyY}|9D(qSSY!q(LGB0}hsj`EkZ-HK^AeX*`{lHg4V%H|$`$skCE7^uJ zY09oluLtKq?>gsVo`>4HATO$$==TGSKrs`06@wnp-h`1AuU29k;$2Zq?~9;iJ6US7Gy&G97dG}y8?spiv$ji*cJL9Q_aIdt#IAel;q2ou|0OeYQ z>^Mr8(z9B1b-V^r(}%7Qjq6JbG0`JTK;>U=KcaZO>21s2vw1m}kGnjbT0O~Py5oSD zAGZHW+)cJRb!P7I`vwam*`Cl^JKuF?$U(2ft^;EeS67$AT!Gp9k63wkhu~ydzJkXW zqGt!gr;t*UD{;3*-{6ZYe(qYxn>LtuR7qmHKV8mVdmFFHE9({XO4)ho)k&g=%|>M| z6Qq-83LQi#lryNf35SEGJT5@at4v%GF`D9|I-Lg!&A(*b4YL$a|UHiC%Y6tjB!Z%j#2Q}Va2TdDzE?zc+;8qc$oPv42pnyjyF^z;4T zx?&_dFV|7%gGEXeA#t!#QmnQTcB6<8VKi35FfSf$GU{cIZ}oF3j5`@463wS?`Mokl zKjQitB#~eFm%E=N<6JvzTd;WCMoviXK?`PuiU0wPUiq7OQjZAsU>}cI6@RMlK<&MB z06XH?KJxIQs6V)bGAzqHnOJw(f4^48jh$;hVVYj)T z$jIGx{o96XL(joK?IZox#)+!{RR6h?7~*D6sk(|Yk%i{(TRpIg%`MplwG7LEs*ab4 zWR)d{6_40i=qQ6_(B%dBwz`U5Pu)uR_HOt6K%D^R&HQz>cU${@rmu~sz>OMxpcQG< zq!@Qd7M>Us8L5eCEog&Gn|flXy>@E9r}jv=M5;>a!yD5W+(7dG?!sOY{}+)^_nXF> zlQ8V7 zLv-$9Gq93GLFeDk3#%u~RUTNE7M4rgkh`l%K3neFEMc)^1FR3~ zuV&)LZebHIwNX?RXW7gsAIl>6?}d=|zqVVq+QcIA`_DV`tS`-~3XbP=bRRMX=VZ+o z2-6o%oE(i$9~*Q$8(*?|*0EL#ovmHO76m_(OjlG7}2^e|a#Mzy!auhFjf-oTNRB4kw|S_!|KNgrH{UDLBD7!d z{U-wtr_&5OwWSs>8|!{47?qCu@6>DJ(FvCUjNMN6m~JI2fEcOijlI-NGq0WVQ7q7860Pye{J6 z=~A38IDLmkG2l6!Qg*wqM`2;{MHP6sfQFSPPhoQ?G^IZtvDh7l{6Q6(IkfGVy4DWc z*(UENKobAD)X-%e)Gv~my*M`&{uMWT<&x|vXkQ-&=C=}T1uG=z3ov&}GUH1oOs4Ku zcMsFynKrTVusna3!+{5nPMc-i@BiI+!LOw15m%k_iTljg^OCj#*5B>~CDj`7GGlMS z>VFMjW2_O?CE0m>5q4JVR`iiN@*7mrbO#IVtQ_?kNl5<1gJ`tMZOmRa#< z)^^4D^~E1bGm`lJJ)5Q2)TF6wQKFaKC{R5XL}Ej=LSm1h?tO{Y@t$Z52YqK+^T>%2 z%fg_&Y4m|rRGX{>qtGfxJSpc5_gpwPu;c5t`ry_tgO$Wkjb8ds3b`0huE!nKl{)w> z!ZfToFJ8ng2)j|0TVOLN}0Z%G^sB6rIw)P`lQsJR2SwtA57i)4u;@!CWG? zY#s2G%jg;gGm!0nzf|I5<@|0+`c9CI_h{FV&|q`8(tNV2+4Y10ahSn_6r@FMPY1?Z zyY1>SK)N@c&|_chp$0Nx`LWBT;g2*jIdQ_m}ktD&$ROwr@hF1 zz zJG<#C^f0mpsP7ExF!u@dVy)k5TJA{xZpZLtXpAl535f;KK&-K`kH=y5Wr+4IEVwWE z37os?W#Yd+*UrU5<59NyQNds5mv8ocFF=~^+Di_U4N6j2;5`!OnL56KVr~|w8q*A+ z!W#Pub6<-^Qw!J(LLvvxsf(8k?RPr-5&u7xX-!3y_|q{jHgmrdzscuxMTwRA+%`O2 z5NWY0x8S#VYW=4~%k*KZI3Hhn+^zmkBfIaGG*Ho7FPLfB>4hZItH@Zktb$+hF}=ns zV&M6l5#A+_xoTsCtuZyxMdu{U)7ca`%39(7CYXQxhDy~0>&H->TrftXInSY$^c-WlsgPNp2G?>IjH`Qv_Qpib8Dphrp?pqhPC z^}LX^WVEJ!K&*4O;(0Z4UGdyyj;&Qa=`aD}PH2z)P#$Z#PXu`LZk!or{@>e%2j4AiB@75oB@j_Lh zZb013^kz)xree+fRds`mA(||8ZgITwgZ)5AZy49L!iHt{pKXY9y zQJ9NO5hQC(qVMfxu$VBR?5KJ~TASXNjRS%%ObhTTu! zO9Aq)i%OdQG_linVnWsqKo+@FVj>?<4VF>yW_m zKehc`T8rxcopK*#LHQ3(D8|AlwG=z)=8~}{NmOU5?T>d$_^g*nF{FLfEzQ#8qK7bx zP?_b3D^5~;uycu>^LLV~+q-XW2X#A}C{TJ!UH<*`-zn{$-q9Y9KWSpzA;*<#R!AX> zP;vHSu08gk`lc%RWGKVwdi>`0mlE-@obEP>UnfX(f1nEg3i?U7;K|VBf2V$~1o~9l zO?SyxG|TgimxG#f)*0@mFWUQ`q~E()iwpcPwXoJx$u^m5A;YvE(!0ML>WVAa6BPGw zWHuVvF-fxx{kOyQ*JS8TBtTW1cU9l8fO8v?({pI^Y?BPZDB2$`*39P&BSQMh2iQ^KiN1N8dm0TifXk`d|vi zc65bz(@OXBfM_6 z9JTXN%UCXX^?jPez5{hdg|ap<@5%VHHm6q=Q1RyE#($?SKz6gtB!qBqB!(W5xJ1Nf ztF)CUU0b>F^XW>S`YkU4-Z$B+}jkKR;U-C0j4kJRcjLLwAG{+ zyLl}OD$l*D^)Tf~zk2rF-FN7E6(y&Ch-3S#CYdXK$Tv9w8ncH8MYEl;|vj#Nb17X5wBJarH7 zScQyJ86RJ`X@ZHWk=tPTKe6l(cZ0UPMZEDT%-qtWK(2MiaKnv!Lc`Hwu9v- zBrO(;?JQs#>YOx&iv-s6S30NruhA*G^u-_TU6Vgq_1_9g(^}1b)Sq3EH2DmlpTbR7 zCwy$p>U5RNkb#E1A*wZV;kTJpr|1WNgTEZvq^B`6YT!S9c8|WOPqD09?RY3~sQS=D z^?aj3KQNT%l=QUt3v z%)}Y0g@A>RUEsMQ;!;KR)RbL!fQ#(v2=?Ot8x?+C{Z9MHUS%aOs|l&KSYV3A&bBx7!oWBiQMK)M%h z*za^_lP@tZr4p>vt@n51s5$@5I;=66r^i?Vp#z7Y&QI;W@H#D%v_RULo! z=a~iF2yRy|1=peHqYN@P%XGx&NEn`fnl=N{Rl%Np2NU+qZO{i_;si#$T3al4i>1ve zDx?4bxo>>H6yfBBv?Xhs?Wfl3FNU}Lh^jt6yyu&qLk&=xF28a=*~W-evSf~fc?90o z@YhRyTr-fR0q*-s!*r@_Tde_;y>q`pWUY}hMi1?}U6FiB(FN&teF>HG=xLx~XtbDf z@O-=Astr-)p#H6%nA5FmJy|~CHWPZ1n+6yONZ^wG@{I36S@sDi|N6n!r)LTJlB6pN z9`e(|1+FrnuEnW%-LnfKAn^wgjL?BuKqsZlv|ef3kcNoHdt^S+xt*OtCV$dvqT%L-7&J-&?#Rd;`3t=FwvnWtpDze zd|m-H5#o^wG2jvZ5{&8FRCH5gC2~cL2Va@yvvvuZxm#foveJWoW$R;^QdQ)v5*Wk` zaT9bhPw74V1+EuI3Hi=OSfL%kAD((Gj7=V zdh7Y}7+va)Ga*8FULX;@)Ve7}D35q^!cDg67C1=Rk3u9N<7*rob%*i*Pk2}OO0$U} zrqDaYtT&|ClfQ14CG1A@siHfPc$iCZVeo}B+ z9m=IbW1_X_AUwRl`YfxQ#oLkAdq6;XY1cCgwTI)^hoY>JQ zgMp10yJ#PmtGhWh|F8mrvjbH2k+11YTiES^tN}%TaWnag6?QJ$9&StD#KlYkmvTCL zQafE?c7P@$WrVD(CT+wN*xih%#JEowv}D;{;z9M>7fq0mlKQct)3Fv&KIQAh>oM&M z&?m;u++f^z9V!|U2yx@ZOGO(J6dqh}x_sefiLj^|^}^kjob_LyPkh4F&-I>e*B%t( zxd?hub7|bC@6xp*_uM-yy>6Kr8A@6!o{Jkpu3^4x?_|_f%1!OdPS;&eRen zJF(Z&vhOC`=E0kHht;ppG0~;tDSgrMI!INsx_aLJHLl17c$hv)bgVttqOApYx9UH9 z2Tn{xi(M(`;r7GePJN%2DtGr(3x5F&_iKu+^2`#)r9_@>i$e9|&NM}|z^|*ku?1-O z5hJ3^GZm${<3_nIP7!-G^lYG-b%j&2^sJxeC1+ImfS>`oEmBioZDlm2F%OrW#}?;< zsA1{S!NVB{^b@b$<`7{8WAn(k{;0c==Fh{L#4mWg9x`uoPjP=C=$7PM80@d_l%^p2 zQufQS{mq(|UQqd39~U_%H79Tn%<@Dp5Fu`L z$4E^mf15<1O^#v8LPvpnpiTieA)l{@JMFFWA!F0w0ian)=gQ?v@ue!6D%~jLY z@D19&UDrW82bO*jDqpV{>dd`zAf0q1%$ql|CZylpQ?FK5hO5XE4MnKJYT(=@}K;HDHo|(nuF3s6g`<11Qoev>o-e5 zc^f&YdoAHh<6lC&8j&eQa53!tn?`&UrbxnsP-j}OP9E(?Fzr~fPN73fY!_1I#uJM>RQMQ$R#kX#b_$YGianN*mz!J1WUFk9n3`QOt8oxs z%P((ie(+onI_7BCRV8s5$MVEq0SQPC**?83=z7XMWfA$_byKC-XcB2;xm5J&_pGX% z_F*64t2DW;yeQPZ(#`l@OrK*0+jd*pqiN4Sn@ao1j^08Q{T$sP^Iclu@_9h(zWi03 zq1GhAOp9!WPsR1e^OoCu*#CE?LTcp7(|_*UJ~@~CWjv)qwGW$GBfG6nc4t=AavGrP zR_V}wYdMz&kr6bL<>l{SuqrE&r&qVPGm1G8^igYH`0tb!ge=g_D);3v`R^2uLn<(| z-$#*K*5m`%WNP8 zxMKtWji_Z6$$Dr+Dq&p2WM>O5PIZ)!zSM;a)TR4w9NJo$2@UHAsO#Oqjw^W(o+`I; z>)(KLs58&|VhV-xmo0Ay1U-G*r^qi6G#9H84;^1d$?Hp7!VCUH`bY}1E^u%XBU5s- z^B`@0bwC($$M<)hE?WSV07tbe!4BxsXgB29I4QfTR%5Ux`!1I3MfVb)em-~+zV)*0 z6+s?Tn9S@l@A?*og4?r-bz)bLVo^mHnj9S(Fvyl*A|?>O$r(nl#(krExoZa3jHmi| zvR{E~AHLgwp?6m6%-j^o;qpCO8rd@au|fTnZB^dF+af;!ppHOsYtc|#xraSQ3%W&n5oS|tfOSy!KlmN%l!wK+x5{!O zN_VpdRkjfm(;*I*qqVK>TnXS?RL=`CyKpKp^A&vib!V^P<8^9{Ww28xjiDT~xeK_L zvKEHMj_gH8Pxw0qzR5+{ZFy5ttsQ4_V)^RoCQNkYEBzy)zM3?*wv={hVG^;go&?Rh zZ!675Q(@v69p0>p_9i@xRZOp2_#}tBb0r9aC(G-JB`jEA93NN6s1;bz#}otvon!aO z7Y4HV*M|x0N`5WqpV^o*^bS#Jd>+>BHG2DXAx0+pDvCpSyK9Eg6zpV#_-)KU4;s8a-!f!rbG?N z)Ny1FTxbl&g&7S_K}YrFK=ytF%i|2~L2hkGhpT|ic5i@>@Tp+ko!=IWn=UhFu<2(p zz3I74f1_)ZeBNdV7x1UekoPs76l|E~qnhdKV*$|;fZ?eGbuCt%Xzi2Aejs z4nOnAP|f`MFTAg$3d`)hg*?VToLRGey5T{cUuWrNPKi9Rb^03?%5lI=WQn4iQT=~1NS6E8;n5Ejv1FlsPFgmE6TN- zo3EFM%U1Ix?N|5V@BEm3g+B{?jV>W^A*(L)8?*9dn)CyPtaHCdT^&_8tc@4^H}$_$ zW}6`d;_+hBlW-T&1Uv{{CiXM6P}g8(oPGO-zCXVPt9)w_{>;?*y5nTuAMd#uv#a^r z!QhJYMBt;jW#D_cZ# z*mcjP3(sVpNT{V{2)+hhQ2ZK5W9ExG3>6G|&iF2nQ`@DRF=7OGW_Vl?b#0`u_hogD z%3R(T>{w1wOHnp;0!|F0o;Zk#<;;<8e#|cgbFN2PjeXm|xbNStaC%AZPkr&{nwGfZ zVeOp{GLU~#AEG>Cz@G>l`#RA*RK64x-K`onKOIBZKvWgzA{H0!U&DB;|N3BmNml?c z9>OUgatP2~Lz^b`8{;!vggH2Zhau=Z8;2`FfdVBG%@Q&}p#P)jJRI3j-!|Oy>)xgI z=(J~Ruj=VA5=F%pp|*(FJLowrTDwA`cI^?N$RH7FwxXyJDv_uWwPJ7Te7=9;ectzd z?)$o)-GCRiIFx1%^;QD?V%!{vxTIG0EL;9I2}!PJxH7ium9W;kUV~*NM0WjQf$aV~ z2|{HTPci=o`^QvCrX=6~4d4z{|F5gU-CuXR1W`q@xQ}l$#q%f;O%B}kW*C{e++e)K z%&ID_oEQ6N$#VzpD|btVqH$Ocfa3{Zh(;|$8y=z(rrfb0_TmFr%>QAQ*_}5M7iwUc8uJ&5s%Ty5{v-aJ0k;|-pTN2>)eXRA%u;qEL&S) z7{7cx_?>@UruIMAAsb&0FTc^^DvoEe2RA2XJxt%_=_5O37kPXL6b zM6H-&sVnD`Br|6lP9BY$b$nrmcqxNnY^&E& zxxES({qK~bwoT-u=H^}35G!|AB~zjBFolY`@RB$6Wa(F51D)RL{liOb{+vIm$l9&0 z4gHQKS%W#pRwfEDO@%!YISCCY%G}|>7G*ffw;5_Izn*+gjr9DnoYm@;`bm{)euW178&?`Lui^p{a>A!lcGBi{y!!BKJ1d;hS~_@PIJ=VMk8GYTVzi1m4=uAQDLcba zvkXOjmOb$A=T9EQ?BDwvEZbzOGb+ajcl=Q8MSg!aS5+JWR7(e_VWSs!LU!>B#rxXD zR=Yv$P$?#OxfOD2m3pY{#U(d;jdJ|+TpLyc>8IZ@lDWjl#1V9$x9x)kBskYkHb$CrV&s#-*k3s zUs1K%=zvbZSKWJVcZ-|I*=iYgE_?{L0sA@0eGNqEy3=Npu%OcOmYNnrqqthwAG+Ni zD@^%Qnyuc3{wfKu%^VNFZ8Z$TYjAhcjqzb(7cvF@6+5SGv@rTiV(bCO+UbHaAw+@S z`39z`e}|NIVzCi_G&Bmp^0l1!L6850y8)ZQF>~4J*3CTLHP!hi_26iNogNYjo7Jg* z64`Z!I~*23-4p*CwJx2YV=!l+JI%0R|6{*Aq_RNw&nTJH+gD8KnXHN^+kJi2GNWkb zlA_U)qV57&I|Oh*pK~wtJ&Q}1138R8JF$Qn5zvd%`uh;n(5Sji=QpL2=7a?{{C7Zk zmSuNdxRloC2y;u3^+P73_KYp# zg_DUDlBgHD?l4@v;%xZqQoG>)WSc|X&2C}{W72->N!rsIGs4}xu3`U72(9%;9uYET zIVZrTA_!R8_sd_K93gp7E-0atO9k(1Y({h+rE73kJ0i{P+_eoS%X6C zaII_N2>qF)!b^kZnCoy!o?>+cABTkv`dB^fh`ke5f!)3+`S7w;6RP$hm+|f1uunHQ z|C^hi==h?2`k7dtT)2c3bD}<6rIOZZNh(x@Iw%Bj`py!^trJ;{-IjNi@3BuG?2X(* znrs*Yn32*B>*Qg*Ia;RbaN&~i1*R_y9^unmF^wE`P6J;^5N`5_v$a)VM|$It7iqTW zhrzmjd7tC%f@b~s?nEzpaPkIW}ewk6G%H*)&N+$OJHn=_o2w=$_q_T03eJ8MsGU_Y&FV^ zUb2Z>vKsQP(QE0fYswO(pToV~9oAo#NcxNvf1ieHYs%2L;~nbw0VN`8WR+c(+f*|q z=e9yWVi-~TWP`|Lw0~iIrM$@EGrjL@RbIGCL?zTG8(KxyJe<6-XE7nkaE~u1s8ucB zH~f7!J@D_4Z_6<&ZBWrSe9;i_>;`5)@^{eG`xp;FyVuS19>+|*`e>M=T7PPVGs7&k z)i*9z20$GnnHG~Fp_7Fnlh%XV8j8w%wTLtRa%0@F%`~`Ef^prrA!vyMg?p9WOJSW_ z^2`a~JJkiVYs?n-);N@d*U(q0m|aE>hK>Pj=V#!d5lxy1i09LS`bwPTpMvHZX0CV6 zYv?wG$#LGwe)qNryrd1Y_4Uc?yx(bssRr8!+wy6;5bB2v(Cskr`}~U~O*dYs0}{VrCln^>io>Nn0<$2V`)3*~hVOBOS28PeUKM05aYs*D`kdXP= zyt8+_1vZ@P*t|l>v=-LJlwzwLeM!}WuIRwcd)ArDo-$h#72WFSjY^I;bF0Cs1><;>Z4J~i<&ref!E1&W zM_cysm**4lc*=2v5C{TYYem1x9>->b5_yzt0WzliXu%fhN{q@Ijidc5b<$RfGM!vG zW92c0lHu(%b%3~V=^8FmslkYPFkd|pd4l@Zv_LVC_LY;C8>SGl2y+fI+vy{ZT?L<{ z4urmsF5G+Gdn5mHrfZTRbzLqeU?A3cDlJp%=liJ{P0%|Wau_M=elXtSRXwJB?BO%W zvG}}Ub^^4pg%%MwB{=f2^}{dG>KeC^ znEJNEWYHD*6(2W+O)vgk-P?_1645Yn+#gapd|qxlNTqu=nz+EstLPpOy? zE9>?2<7-|A9BgiYo_|>?UkwUcsbnuzrydgF^WK$Ehl_EsNWH51gR1~9xysc{|4HIq ze^8rL9FGO^pZqpG!V9Qn`(@M=AJqrSQ+zj85P;V(%?RhZmp*ANt@#~ zOf4t$q(8%tCrFGqHI5V_H{@l8{{|?qc7{#Xmg89co~5pCMsazHr(>SUH-qGKeKSI+ zwRJ`Udw+bdN)J~onEyrrEocB$6D5EQbP<_gGMHdB*XkZdt9o`_)4anXFI3_rqMn?c z?XZ!sa9M1nHWXnukdu)KT1#cH#2aXz(CaR7Qmkz6UAutv7w6>dxcUV5*HLw}yP8M3RA|;MvCt={t=x58J1oRet&OdHizoX;T?eLcv4UjL*GL?=LHV z)q3Ugp%uAjOJ!9)6-xEnPmn1taBf%_MQ*g)Cfib~o5gz2?1O~TtwU+ORm3v15!Abc zBxAr0Xnt@v)(RH7iPQaGcoDWTWdnmn!tGo zb}5GR;BTQ#cC1ZSqkCkN5fpxt6kE7lv*&d3igP0(e8rznEc?c_ z=$+6Q?k&|fpHPONu7I{46aSdpS4FuN6P7u1P|x(qIr!TYS$^xER#;#T5GbZGnjiw&0TyBuQu*l; zcp<}Kd|jv>0$xRp7kgOy@kb+b2l2kmDDlU9jWPyetCNgGv_w0qlRoUDyQO?UYVd(rDaXhs z7f`8!<99L|JKW60%TW4@mPr%V%_YPBnW1oB_ie@@<7-vYLcg@uKo&e)6B8kkO@l^7 zl(cAcA&M4Bu0I^1+1kbW(>H^|gu-W2|8nB){_OqTt$R{E%qi>IFY;1K{>3S|icK-p zM2E#ih*^{D3s5_^oh1hIX>aFJZ z4hibEhz+mApO+Lbtsy|6OzZ*qhNHt?@_n??AE9ZMO{nSFuKYrkoi-hU`i)-Skey8d z=E!Lg+jK6UOMCQ*kEEJdRRK_E06y5t$GB@dhJgN~wt)}{nz)?aHhAnL zb$h(d`!0!tg^Tlt&_BJgDgLnFNOyLH57zHLr3(T-O1OFKBPDM+XI;?lvJzH_z2sGD zYmvc=Dxwpy`;dJ4&iMSm)BLM6hf#$G$=Y-PI7%TqLLDBhKvxY9LLaLP(`om>vJ?e@ z2_?p&Q;l1kZ6Xiq>cGfjPW$<=c);2TNgG^$LS_ zQj8upsM;FKmaXMCsz{YICyijs8!f_0>zYUn7HQqvC{B6cT%m8?_vcOK0$Km7@3L3! z9`T9Zc2jVZYSffs=>MUKWjeUB$hJ`^;H8-5%HfJCZ3nZY3Gbmz&s0M2xvkfa1!I3i`iO9>M2KxON&oh3j-p==p+G+f8CDg^=lZT&7$N4vNLK zLUwhiAFo*Wen)96tQt%aGq8wo`BrjsDf*8GavWQdO`Yf4pidt>R{{!qCyG)TjY&v8 zO>VPqnpbbxW)`1ex(x^-)M2chF@s`4=CF~`kQy7+ou{R?GcuAM(cMF=Sv9u)Lw+so zD^lk_9oqQaI^Of#^5dNk!70j;t9%kfVwR{4mq?P8G?(k-tzA&>tfu>5CoK>oU=|oj z*vlSsj3t9fiQC4?MZ6ENhq|`)vRagm!(#qptglG%s3$I$l$dT(4+Beky;Wj`c zjc)QPs%P=khrha=`{}PGkJwtPqV!t3lP}9#gNtO<2Ge6^t0gHB*+EJkjB#pJMdcly zWGxoyzH_TQJ~q#tEK+3qOcXWF8s=*_bAN9XmoLhn(+g+Iab>+8oUz(`Q1Qvy6>169 z2j#r6#{#WWnIW|vtWi#3?r&qGJ&Rd7ph>MD8?JY-JC6-4G2-ZIxFNxCBMY~3u}=G5xs`6P5TjRX z8Fk!d)f2Dqsgf!V@wO5*)p@iW$PPf@FnkyT9f`F8^w^oK5sk`&w#V@c2;mVX8R1?l$^m6Nw{a+wUc=v-;Q8uc0t%| z$?Ip5{3Y&A%g&+Fv&Df0a;`+c$@nJQS1g zTJq-i;KvEKuGK?dMOkf3J=X?E`zv&F7YP)_iitBPlK4{|57W7tg>uS1tly6oRaggV!y9A>C$tywVtw_WRitoY z0oS=aBH6A-{=ZXMABMu?Rlt@U=Oig5KC{`XS!&1GMx~>#au@;4IUB1o254stK_9Z3 ztoQkYPL8QRa^s=Y@cH3~eEN*A!*kZ`|4tDRutHPob*ogUQcb4nyL6Fg7nTOhIISyH zx)AyNWOsG?^YavGi!wu0)?0s-|JXa17XxC~B>eP8!`Ihy2{GoI*(rEOG7bhZhIaw8tTiV4s4jp}EuJ zj6RWJ8<fR0-tUg_LJ}w6vnMp@*NTpijhG!z?t{+zHDzZ%d?|F5Al_VUF6=x z)Y|Z1d9ja?v8Jz_IQX)O11B7mZLH-pYAny=wyMdbgaTl+;)+y#u3v4?z%%o86ACKY zmp@ukW0SE%b2ehh-M{)@fIc3F-?(8OB(EH_ED(5*cFaNR5 zY6E#0WX2~tpeltck6?^p%;E;Ie`e9fH;s~KX~QNdtNr0;`#RM=3pNw!0(i8G`Yo5t zWUgPHF$juqloFR)J?b?-9id zy=)S^N)2layJfS@d`+UQ`AXrt*0kUk^Tp}Y;+D=;7PBU67HA|ALB!_bVbKh5w*84T zl1Il>$0_~qIxvJ~Z~6x#`R@G3j|rO9Nfm<)uX_1tRG*i&;}`Vy4<^FRuMg-8>IvZ- zDL`Lgkw>Mo{S_Jste~!mXuRQOF{$Y!i7c6N?;@@EU3x0IB5|6L_A?>jz(sAAVrb(>M ziN>+9A4-Y|H6*sv+*gm0&Yw7EZ1H^5I1)Pn@crpU8RF4NGrbrQ|Savt+Y<6SOGXSQ{?ul~+9!vEcFFDIZoR-L&&M*TUd`r#iEZ#_z~=UpSYzGw%#I zpMh02bra>J_q9Bhe-0P0xZ@E(c8Tu~)VieVw-;}Lx*o!_1TxR4-6@W)8l}wmctrnp z^|QzU?G?x-CV{QVAacYaE1S+_rlViKKQcbq=W~r;*2H5*i78K7Mm*IejJx{%(>KfwZ3_IKzBe{D`<-M;|V64b87-Bw=l{NktIe)%<* ziFFEnJeZPCRtzSx;fPWn-xXmC<1*Y9l)SS0I(V5ACHLGTw&Z_T?5XW48+kAE4PKhV zsEa*9Xs5hlu@t~J%k`4kIc#X38sUV;m!D|s_&THZ76e*W`#Q8%iKeJ<5c>bt1})>o zHnwH_S2=n9{uKQb9bU021)f-Ql%ALtTql8UgZPn4x9>$L_7+h{+My&X^oK+%T>9Hg z#{>?2seXyHfu~C){tV?Y{O-RW{`0Gx*hcPo?)=NX?J9%lpPc5k)2dGHGX`l-rSYC! z6t>R_9Y?Zb+7pwV?^XvxH}wKTOz5gn+N@)1n(}$O5$C%cyPxrf@Bfni{;z<~OGEv{ zBJv$^)R(_)UL1ncxh@R4B{2a&)^-j)y(Q5n2QKfoF_yjC099wxBISw0jz{MP zax(J)yxh-Io(WvPzh7T}MJy*nKThKA8Fziui}&x_E}S*0UmJN5M7JRh`I(eISMVQXUd4Lu@;>yo))sAv6jAE?r|LrB+{1Ke zz(1;Pz><^9z9~~}Bt3Zxuz?x(-bWBio4f}nCY)By;p%XUl1$W43-u58YG0kz#-u4J z-USx)-X|@9JZPmaY_4-*d^hzMQ^368pFm3ojZ4`YQaxS!s**mtMC)30Hw0bP=%#I- zUaS8Wo<{MFBH_zJgNmuG@4SmYP_P|eF1f=osEyeVMX_g+$ypb`Oyx;=MQOvIJK%v498W!b+!_v`>4tZ(A3>PgVfdnDAudSR2MClTZ~tiTLl7g6jIi z{JJ-ZCUkAD!v!qGo7D?OCvVr^R?z&8ga_QDx=Giq_3w*yuP9Y99p}QnTrv*)fjv_) z!!MCD%38j~2zD&66g4YQrIlhL94&nxHE-<}6Y`Rn270)WS|@To{CRhp7z}=O{&_&@ zM?tQgvVHY^30upOR}V!0a>FEGg%Z4W(kWzyUX&sqE0YFx&1*tw4V5idmTPd-2q(1Z zD$-W{p_XP4J-DpEr>;jtsi?bKOc(J>>E1&rr;w!6jps~}51#ahIZ;F&c>r;)GnmL! z7F3e5#U5YIN${nNe@(WOwL9MQInWcUZ3Syf5|2@J!!loW99^wEiE{r6mMC2M_WOJK zy~hu-c;eE9{Z3hXli-I}L_OY@E$DCUPkBHcmde()54YL%piP-4FB6c1i+MQO9|p%K zPth0g`}%v`Gvu2S4nJ8715J5p;y6zTkof)mVAI)_v@kL$vClzkZ5pY^STnr3Va%wYHWP*jv`;wgJ+J%5C~&GLDwk zdvPR20%?|xqK)os(ifmXTlIsimwS$1cCS|VmJ{mlk>1cBJ(WcW+HLF?fdHO703OiS z%Y;tDm_WJe&|-e!sAlq6P@dsbW#3bag{P*r5=_oF0<8qA0Kb)88`iAn+0CnI-FD42 zqoAdLOsU4Qqq7%tUmHd9t9EK#;bJ=453s9&x-@Te%0+7FXG7i}on}00`)(ifLz|O2 z?;X?L0Gu8Sf_%~0NfYSi92EqqxT*C&|LR_{>nIx=JykryQmp8thS% z?@>A+cLvCqNq;|q%A^JoKZSVi{JVLRda8;sDW)1~mkr0e@7fcLjC~m7L(-(Rd-x@e z{H?s`L$8{xlW~KtD3sOv{q?x+e7|k;FPG~$_5MlCYgChMZ3Akzj!zf#;&8&T=rKoi z(&rCj3*7URJ947OZfu{!F5bsg7X&2L|6R{*_iE_IY=iF2S2F)bAgik^ir6O0m7soU z3k(42!Rr6-RIQQc&Lnu*LRx*T6kfYT!AFr8<*@VF>7=8ipMNjEzoA_93f*?UPXIEW zRMTEj9C!kE)91Zc`VCET9O~~apB$o3R8~sic~)9Zx|f3sWt7O}aXohO=8dP9zs2d@ zefRB4Ow>H&4N?bZvHxBX*(p=qr>~&ou$XG(JIBhQsDM_3ih;ag*l z3Nu1%*DKeNVgZ0Vv&DV^mTCG^4bJE&#=pUBf7vZM+ZV${CynKqj;zHN1Pq2`fObJp z-`>(**jN^{`{zQv<4##`|Pci!Yew0D9e92HbJU ztQ@eZBHk={_#|~eG}goT?k>?VkdlNl^ZVrceYZ2Y|3@V^Xto133Nm;mJ|0%pP_9j= zWh|YzI5Yu&%FD4cy^8X?n?!N}?SAMZVTA2}r;JxenIu(lZV!K3-u|$=5a+)VmG_Yf zG_g3bqsgynI5!~~?ZZLZxPo!!q#I>)Y4x+&pNZUvN2O74)+i!`v^e=hZwy`TT9afL zFb6N=&N+kJe@?z@AuZtcdqt)?hiHMJDrP~po3SwYe0q=_v}R0X$h5#RXt|l=Rtsl9 ztok6G#nac{zvjI&__`tTB6d&c`!~XZ=nAvNv;V?5t-q8BzY8 ze9kHI`nLkjk4~HP{6V*o4f;BCAYD|)Lbp<-q;k~kQ+8*&DRU>F5?JiDSd=RBj^>s% zYI(sd#VM1tQGO=(!FFL>FkDiCE!gg3j?fgRPGg&<<9|86w;Rn0i40ud?an;DOFPp6 zBComUm3nFkVYH=EO@>u%RF}3=GPQETVRqQqpm#zV{ovzQvMT=kL1B<>eqsUgP9W-Y~{2WE;o+p_Q^H zv?A@~V=-dG_|j~_8orfjsaFqTpQD(^!~ZB5v`cQ~yz)Fv0c>8O58 zV@m^rpi^rYJL>CZ^MHmEof6Wo;O+F|+a9dDvtc@kBA+!x&RrL&qsH0Bl*cC94VHpX zf|f6#>iVjZ)Zvum`IYel#M)?g~38#8QR)EJ#@|&Ev${h3e$UR#TCCO+oNqR z`Bmze;&&4KVtji+N6D(b5xppF;0{Fob?7=`=H{Q;nSpMOUAM*zK4)5b%)2im@Nc)c zbAzre8`!K8!xAI1(Z^Clz@`rpGW3A~+uOi;yTqo|-{>|NqIS+dNRg^s7#*T4#f zl`lIr`_xwnblSJ$lSjod>PTU#S*%?0eZjRE>EnH-zDB&uEkRN$dJQFQ&K^{-LPp#_ zI5niQdd^GhR(e%i@Kmx=uNAJ2Tt^y#Zv`m6#=*@-fP7N4vPQIVQkjL7Ux&gk+1F4Y zN1>fYXT8la2GIIru&pwP5k9-X{JL<#wdEOTX+tZ^Gz9> zBP%sqGRv2L74scz0wTO4l&KY3Jbx0iYl{MNd8C^{QKnujLiDa;_T0~r!BWb)cm`>9 zw?*o0`_8cAqh-Eh(S{$}N}s=Rq?5~Swh`tyVJF2MgdFy8{)OQaEce0ps8RsU1}e#a z$)BAAhCm#2efn8|8i$CwFd*it)UV_E5qlZZQ11PxCY*mdB_~yBd7^qS89HSeqrAA2 zFTaNB$4rrn@7iu*G&!8?xy89(5W!$01_IpsYXb-huroXTf~p{LG(XX3pQ+d}ZKZhH z$}Gy0N{Zf99m!OJtF0E}CrkUY8<&~S7Ht}bJD)sVE0E2&e^&HSQjmQmytc@ZCqyTF zUGWLzGH7@^2a)G%^41io>7Lm200J=*PP^KMG?JDty~lC|$;s` zpZh9+u0f*l-4{A0S_Pt{&C27Gc>au;sQk$&D;BHuurhD1DZxF4_b$2fJ{aCzduU^M zHexT{DbLVgI>@aad2;h~&P$*C7#OLrXK~C&X--zXaAd%{k}ugfdi0=$7cGdtJkJ0o z*_pV+O?mqWZA`EKcE=(7?kKc?zcs_&Y)DV#tHn51kw(fvIUViER;4PHO-}+e>Gpk_ z3owY?a+ZhS_w_W2=N~ZI_$gX@$qX+AIi#9qX35`5rXHvvbtWy>^1jB(f2t##pkSt? z-+>w%ZRUozVS=1#q*X9fRPH_*m-sFC>e;lk;-Jj$`w0&nmHG8GRgTL+sYJ_(-xopF z7Pay>U(V4N9)W-d+71r=!YajyCl`vG4l2(vJ)O+3)wLd6B8}U6`KK+=X}u6AI$OW; z3ZH{9b7lhE28EV?%-lsXy&4w&#d6uX#g{b&g+II9fPNoalyqmIu{+7jd@gAz`Zue* zS9VESFQ8U4pj8}ZR8GFw*r ziL|Xa6$$Yj)v}q!zQvl6t3I%aB*r0dgG7^Eq*c?$NUBz_N~EBV_sW#5c(Uu^wfy?# zhY}{E$v(|J+!qCI@(6Hi`#`b`YJoSCJuCI_>bKmngN}M9S8%J%C{n)h3tm&t857VP zK%+a@-gy$nm@qj!WWWY*KMTA3%lCcs9Z&zgM1yt5jxf1w4}89&kXOS`<8qUeLWIhK zyn;9(ORf5ihPay>QWGFGn0+Wx`=&8b)aYtb;j_Qu3mrtZoE*6hGlq9Z=1Jy3>~qzV z%D+Q>9+KGm6TR>KK3+6B9bcS^zBguc+BCZ)-Es3^DO(;QuabgB+;%ozBW>*|bad<% z;0E5EJz(TW!*RYhJdXv|X1S&RX|{R@wW^1;&J(7JlK>(0FFI8U)!#2|h0lsSxgvk* zHMqm6jw`jLQp>;a!k@~g(wYPu+y2KAf^$12&PC9r%DTK`uWDp9jD#zAD8kyP0(zO2-({!$xj)rvDLV`X{3&U|Lvk{GpT+7#uJbl%yz+D^epkg_ zW($g+RV{(2^Od_5FBI+7yyxiLER1|K5(H2wc(>~y*)*b9BUiM}ACGt31FSC77f->v z(_nBB=l;td&khvK;y_~Ya|U<`wB@Gf)aP*@<*Ng4I;X2feNxB#dBrumj2$zPc@?GN zqIM1lhM(Mkbu9rzel$Biyoe6k^!XI->-qb!lEN~WB!((0e(eD_N^W?u%o+lmD zfReS5y5NdIB(2*dS2TB(?6uM}`8-uC-#4{Q(dkX+pjkr##`LkVMIU5cz@xsV0n`XnRNia;Gv&?~1gqtQ-BzlU=jR zb@CgsZ^C5qH!}mgZdR>vIB;aGeIlscH#fd2HVmU8lHG{fq}tJ!*e0DC6dm zEAvENu(tcEA*tbKlo!FpcJ4-L_|$K$jU;;dZg^E-pivdH%WgFk8YY?C>iSr~C=?1W zS|q0O5aZ8ww%lC{9^w1^Rbl5tN4Rg$IumU7V54)x4!qxUllu9?jn)F|w|n_<_hhew z|4N88=rN-umQ0smPn_dT`zqMna%-=!S_?*WS1tk@5*QZL)%J951YD4Ce|79l6?Xd<*+U>c2d~8>+xM$ z0&*tjP--0U_??DY4b>x6{tM<1T>hVG@UV`{;ZvOm>)4~))3*i7(t}|oMliRsc|N;t zyqsLUEwinBV!&@yiLSCTh8&1S^K;v`OQPiA{I0F0@1{jg>b<`_pQsg{To|`CX~>rP zfS@sDHlUm1$x-j+U3GK(tK&z~dSm8O%Z&1io6cS`0S(RIH>DD#--mD(+Yt_|)z+iC zd#9&g-`+#7AKwf6I=wy7B5bJm>+|~g#9u#H#?I5*5^s#bFV-l7?n5yzn@uk-s`J?u z;YK&;7ecWJ8*g1`a_3|0oDJLH+QJA*s%dI}u?eE5U>C4sWmnoKs9lE2PVJxBwBwPI zdiA+cfHX0(KLfDHkfx88gJ1x){hDGy2{dWbVC6RdUM+CXvg9tR2CK3Z9Fjfyy~~ei z{X(fv%!u!m?f;wI!S}j^YNiuqZ@@TaFrpQ zE9bzDYSlBh3{;B0Gw=)83}3gt+qi?89mBQ3^)cXBkf?dLx*fu_kZ!|TjL7yH*pb%) zYoC1xfQrCK+E#}7ii+R%#Zx(o(%kozj-yhhBUINmze9vjQbXp|OhABn1{8rcF!0%_ zA2X0;8H^2nt1`nryLhpETU-b_gv%8n^+Hy#STaUsHqvq0lz#d}>oFaHRhou#NjzZm zmMzx6i=ZHzW$dyYOFf%K3*{p~3pZWS3Sx*?+xxnY;|r53lM2K}Z}G}44q~G9M^I;P zheFFrNiewcNb$~=lF&z9{i0m^8Cd0zv3l61ra=KKN{E5oYTB5LZ;{=LS9u0kA*E29 zO5=n2)Tm1qQTi*7G-FcPMjoEL3JP76AN-jQr&{{Ii7Z?2?yo>QHS@0A#&yeCJ-!^n zj=f}-RBBsE9R#XONfNun1yyk4_6NN z^(T2+wz)jyLTSD9!Q1`7Y0fM!=;z9q*g?!=z$UB4Z)(nE_~GlgF~Ab3?vq}y)iuSm z+K}AeQr#Dcu8alJk5$#%F|Ti_mG3c$8KfbeYwrC_sxPqfNiw+DJCB+qp zx1YCEHjUcxyb`Dk_v|Q`{O6irM(p(Q;B>BhxRdOwMQ4=3fQbEr?F{j=t`nu8T=9!# zx!k!tTSGFeo%9q(hrCMXJ4;=Ml@?Y2ZzRbW3U?jL@O3F43LX%gEK8e$X?x;DO_boc zUidS>=TYC7m6YfKUw@6xdSkAS`uAf|YrG;RmE^_G<1O9`<|PX!hIy5#vwBUEFpaXw zW+VmcF}iZRs$Cu%Y>#QN+6uvVlEu@y%%=zyk?6Ymli_rDnb2Va~qRW_4Q zDSZ$KC!?RK-g>!x{iN!TPS`~Hv%_?%1iH-n6KjFDhOgy1pLC*fDceQqLe{zsF|ucGZQrdKYMn>XFbcI&m) zyUn?xT*8#ekdk(|#mJd7dS0LmAJ<+YFNxK_^(i`0S~YnQN}Zs{j|qOe9>xw-ZE=s{ zx#aDT&4(+IdKG%&jjiZO3tI<40L*dwr4F(+OB~-pb+ctUTUb0#D(2}pDx=7CsG&sE zb?hZU_LbwCdRz2SKuP+3ZN#pI7kf2d9$2CRZ-JGM)jNP&(eDgO!cu;}7_Jo`KDu6N z&z!Im4$C*{&3pJOy&rG|eekS9yf#nFdp=QQeh_o?#pC+mKLd3sqzwa z4?3t|U7Pmma1j*Yw{dBxYk}gGDUQrjfh2GtPzfq&Y;0tjm@@%m(lC!gyuLyU_ZlHR zI;6=C+3OAVcnS%+zE|`cV00JepY>a*kdhBe{Fdg721q2E_hrq*+kTKNjQMK7dtu;V z&qIw1M%hXwbvU+`mlAvKcd4NMwsXex2!-X!AbIKRr`(Da@;@NB7n^xAuSG{knH%@n z8W82kP#5CR_Q2`Bs0yQsBdMlg?|aE5!yH_ya1O!UKiG3z(}w>GJxGzBOE>wpmW+V67DZx?D99>jCo19<>G;cc}~mZi#wnOkz65=RTlp zM|SrssF^Y;Dbihi1cq^>+G~U*Ds674eI9kV&=OCmOo#ZPME?& zIMKeoWghwFO7`!aZ3`v%qc}CnH~&pSy%4`RTrL#!>(>;opq{LBzX>QOgSBF|;L4mR zW&mR&{F82Jp}f+~qKANygA691#;T|WM=To792kXo6X3WhsOx1ZaG(E4U@p#7QNzZu zx6R5-@LE#iOAdEzjete1ITx^0=s}px5h&Powgl_3MqY?gj8ZI3)f@a&d>v|rfRy$K zZ(wr(jemAi$l{2v`m7~OkN8mfmo#rHMNE&hFYdm9?svPdBt7r1Vri-JqJj6NUZTqG z^UB~|X^J6gCJ4RVq0ylO@)CKjKFPURsv9*@MeV~%d!iTrJJpP)FZim`6#(0f3Ku`> zk7DIORF&<7xnGR^%AHT#?6fNz`U2p0iZ`;*nwGDw^#%D`x(FVAWI`A%F_clc-#D@EZ&~>p+Dz6M=pG+r9Tva;eK6F)s z1^SBiqrxA(dHTG*`{ZRWmwLg&D4zfPdn`!dB|xv01+g2P1S9H0mJqt}i^-MM_Uzlf zKz}0-ed`!>&p6(Fb-eWpJ|ffMJONDps4E1M4f!x>s+L#|)qNxsnUY3&qFtG?`iv4L z5f_e1yW5mv!NvRr{+5`-b)Ntk8EmE{CN_jFnE3csHu8XbzMk#=Jy z7&bt$yUSafq4GPW<@zk=2wSOUW3NW_Ji7 zEg&5Rx4`??!<_R?jV>p}N%xbn=T4NEdc&dh8AAymhGQ&-WlegZBOV1S^x|t+^qW_I z`J(4Ev*%acw1(VV%qLs@tl(-WM&;0!Yek_nn`iP4a)JYD&a(J?!j0B3xp_6Q(5#y~ z)-EvV&hdBbMWQ4E0P}&bz7C(6G3O68VmvJx+YLiKA%;pq4`t9G8eIMmQTpF0{=GvZ z+P3J^ju&fvF=;(w*NeSEbOnAK@!mbWqnDJ!_+^Vx6=v#E<*Zhnb_wy z7?v#{QjgNqFOGfeB>0ru%*qECkTd72=W!P9Jf-4Nig zs$>SxZgsqz$6EP%RfIllB!UD_h~s?a{Gtlf5|ST=CmQfPd^+6XmZluQa{p1+_(IyT z0PU1Ti!C+o!papW-ZSa76pfIi-~iea{WU^4fflTKBz(1oIcd_P`{$~4YrXqDsDwV~ z`%-Wa05z+jd{La3cJXXLokUNxnegvR!nGG2sxxQ?(yAWOuq_67@o1D~1Y0PDuc|n{J(gR`pz|j%Ox7~rV^E8_aYbhhv^W_Ow&ZeFjd9+ zzCQ&QVc^1Gm#m8^$~=wMzNNhZkqz%3IP{FPPp#vYjHfX!(5eiF+gXhj?I)?bklmXn zX@DIaZ%gmq)5-bw@}y^^smL80&ym?wRb+F4T=ry^EVRLEdFELDgb!$y{dpo%buZ5Y z+vdRo_jjrEd_bRYc&06JNV#u?=UadF&uc`vu?tWAw^FQ+NF^ZJIzG%)^k0mgTT{20 z^_gOjO_ljO1sRaZt${<`pempW^3QL4(t>HWPl3E49S9ls2~4cK_2R2+MU{p5PJpFo zF`QE5kJ<%jV-#SlrOa*|25iy4r{GI9baXEl8y&4)y-BK8>tzEwO&CcDDHWH1FJ&5i z)fHpIx?le8lqe57v{jc@g)?B*m^os4Vx5UbrO85-8E6a@2+&)K5+y7IZ;UK*wuKqt zB~MD8z8_mC7XN6u*`$RCL=}cyN$JC6; z;$KRA$XF~)&694{z4w-V5~<6z@$&+&_O7L0G=3}daw#GCxw6rd8!LhA(LyZicjDe4g#uR zOyPV+y;yG$C-*np^C5h|8OtY3>vJ`7f3uyTa(8&8$P@=Hwz8fe`X{^s)^sQs8*Sb6bP@75)p2zcyV^ThV_Sv98paL@vWk(igYBw+zML3!^QEcI^9#h`&X9t}6qR5- za+aOuGX=59!!N(I{<$sx^$LUEF2}-e8v-W6RI*!kp>XmNObq!*XbrBPXFIfK4GAH& z%7l_2_?9X)`-V2m()S^`Y`t0k{$og!I{N*a3#TlVKEjK*aPRyeZOpbaD?f!uei}*M zBgf>6-9x(~)wMn({iRTbagb*I=_^fcLUle9Low!PO6|%#F0|RvNV^46SG6!o*4w6c zE~nj2a}3!!|0?EOLBC(m$FfUg!mnQYBX$MjExY33P>B%zj;UBpf+%#%$EQk$jClIY@mqu_wE zou8Itl#UnLveh8<zzhmcTt{Q*IMrD7=h{y)I zCTm`MsK&L83XV(W@_kh?4`|RN2Ir*_{S#7pdylk3!q%s$BfPJ4wYACVtkQ!n;$(d8 zu=;@e-^S*;tAhJ6_V+%{yn!a<=DKv!)Sd;2J{b|cXG6<*5P$sS?|*9AJ#4aYi{qll z3aGW{z0s-`mkt@ufRwsAoE0S}Y$x)+b0LQzs#tkdNC(3HnH<~w_cqyugQ@k*=S#AG zzbser=?M9lM1uRwZ&|z(o8_}WHnRpPZNBBx8=tiJYvZwSxS_D-wHz@*DJ^ z3FCZjpBz1&^LDK-$owdMR;XmS)F0O2QyxmNEY&izK%5G$pIIkX*XPKE8pB7T&ZvWr zxHp>VDVRQ!wE6*?FT(iZC;v+^e~k=Da?O2l{FGWxxpa=NKb6a2lE+l>u>*?FvP#xC zN=P4JghPxN?~pntEhj;i8${a5MD|$w+~FvK4SB3&r$<|Xi`UkU&e6~=UU*W()1JSQ z14R7b#a?@59Ql=VOFi@_^|7I)26r|8x)|oX#U*X-Qz&PN^7R5f%#WztT!~gC{*rUh z)Hln36`33!eSJCsL?uEJ#t(g9E9t+Vo8ASe}83^;{CDSNE-XRkkrpPLpzzfK0th^^pB>hlk^jg2dx~0zJnU zvR^hxK(iDc{^-%Z7@SJsEAo?nA;$WQ4%51|I%S9#oMgiDo-z1(gWh^ebtxO|k3+;U z<_=vJMn4Cp8be?+IRwY+iIo-U}!N?dP)BZb`=?$prJ)%=u zXt19kXhPD>7gT-BTr}IZM7{)BCEt3zQzqiJ;okPQOzjQsi4HhFb^a`}s{h}rc*PF$ zApdIXclNT3T?5b*KD`9OO4cWzz6xbjB&p(2L;MQxcY|^ z?b4p0=rWrr&3k&Xg>Doi=+k*kr1w=fBqTFSNo?XGN1!3ywk1;K>)9x$1-B6@V__si zu-YOv5++$i1VryOj-Gx=8bMq_0d1vh13P2GjODANH=*@dZ;3+mfq_X}(VIR~5x18K z5iDtx&FCo+T9CI4_D4{AbqWOEt*j52?&0FR6k0(>naO=_}@9<3NXxfDpGg#(}2--hWUYO@X_Sp(I)&9j;tK5je8b<$8J2iz1^bPTT^d&Q^;aiA;Fq3vbB$`Cp7$b?vH?g*P;WvEs4Y6`f`p3 zA-eo&y~B2ZAlOhM>8Uc8t5Wg>C*nmYUzc)<%GkV#jP#(qMP##_X~nkjado+frHn}t z1N$Tis2SMDA#CUXpzcRv7%Q+Fa_k*MTf8Fmz|U%w3-zNS0DX$UH0Yc$7q%DtG_&g! z!SVsTicIb3eqnX?H+#WLMp#3*HZd>nXBa6deGxV>F>Selpf05#K7OBrS_YLR79F~K z_E;M!F-D(#VT|mrl24f=2NTwQa_AqGiI=G^oUs_H8uGW{_GQZgD|X(b4wG`1=yvCf z4KYex=gSI$)NFI^+ohysqLEq5>OO53zKt%~3waQZ~k^K!7(Wapc}(< zz?uC-YL>h`y*V-nDBZAq#;Ko!XmWcpOP!kRIiKMoq_gtQ=dE*p)i>+Wq~%6*pUSul zD0kR#b!Ae24jau?HDS4sZ|X6wqPsNkTYYVdu!5<&iSM=Ud#RI1CRpvn@A_0R)8-|4@f=hN;!HNvy@% z3K?6uX9rNPYJTN4Ic1}gB8^L?xgwkq*qVUb57mCaSlA0*qu$DzCqk*jhch=qO)R+! z>*6#BSUe%K4ZqxSH~YI?P-fkWJSx09hXQhUJBEakD(o8|2HrQuvuf=4VjOx@Y#v%4 zig}5o^quxtUFcfwbZ3{9IakVPlR9R-Mf+7Y|C)|TgTW>qb@JJ&`l-&v*{S3RJ~6y?nCPNpC}hKHYjVZ?qbkvM(?G`KM|;81(*$Dh7{iyd2<-Ymip4yxMs`dd2R zgG+@2$*Ma-?%)3B<`G!Rwqz2}q1lF3naR&{#~xFAAR=Vn5(o@s6%DC4rnuFbYPeIogV!()uo<=17#qbMK^ zedi7aK-ml3F~$C|wvM7&pyQJm<}ZBg%A}`}YlnW-XQ1dbCy#4#*Nk2du!16;Lj7r< zu8)WAfK=W|aB97+`#flvrun?GR&jeiizMv?IG-`&j~9xVDT7-9x&rOD7s1vo*dPZI zLBPp|;3>S?P`>@l~3OTm`cV|cp3;_Ydo$vD(j>}KKCsi zx)?5gh>90m(Mk8a9E%wJrc)9toVpf|FntCl?85S1S zVSY$8WR3&>5C?GJ=_+p2I<3q&sI02q&S`MCGtLBibLjakh)k z%((38jDJ7|B%rWnCH63Ujy$;*zB#$x9xy8x89tM4mENt+@hvggg~DAB?nWcJ$nnz> zQ~ia=!unmOgZ;-EioEtKPspT(DdqJL|xnB z?KhUr<|RvhdoiTo%>58u~VycaMFeC{8FZS7B&2X*6%!f1u2t! z4-{ZyDngebAP?!;B|C`rb-K7a-JY>4A^T5ikkO$;zJ+)ta!z}|{ zg~sT~s4%RG5^3LM!Tv^!TkpcfsDVt;E^Dnlx*A^lr{%0>uYD8w4w13u>ApD1Dz#Oq z%gCp{^HNpC=-B5ZZ2DTRBU;GQh(^?q{hA%Qv;A>-xUaNO(Id}RT;rRIqTwDEJdLYixx7sl(&WHbaG+m%r<4&|}=H`5t$T zthPw2S1@<<%?VlJ)}tk9y|xeJv10Mm#m;|4fVbr~o^}cD2=uyt*1Wcekhb40Mw{g1 zON^oXLMGE84$TTHFp~W^2lw4sa`W6~UlYlTw5st%e}*rsEH_^ny0=$%}w zvS|KNL+J7dtyCj+$rKQOWamBf%B^haWWG-oc z6oU1=F*AMZsqDBSELGgTSj}ILYwdjRl&re=u%r`WKA%lb<#l$}zhPB^R)Ssmk$&v& z@*2~?@#YD~htaX5k@3bMS$)2SW9w1+3UwfC^)P3}ugAznM{{m@D#^eQm(%$=p1YOo z0sWS`XkbZ*6Mc=eyiT|L;LT=jDyz@+o>Mu*Qju4@?_@g^@+Uqq&G$$hrZ)Q2%IU^d zSDgjE`3G&k4n7GRpK>ximzzBs?ifBxkL!^t1z!I|=7q`Wn?mt`m=Dt6Ss-gLnp z+Kj(c&Jy19O7xSM`FJeh^)#zC^gRR1-!hLDc9(PPGZ`u&Y+{~my83XBxpVV5z^BF` z&DfX}>p!OWU0mSkv&K<$i)*#c3$d8suhd^Q9X}t^M&7ikIi%@-&;CsM;wLS7PtH{} zsRVhF9ywBLnhT{SG))1;!Lx7_hjVNQ%>SR8i>AAm(ZlUI~3m4v<@V&^4`$eda zm@R;;EicFnXPuwCTD;9jmrAY9aCsuoH$@ut-PtQU+ni=3O&U@5L-w7Zwrdooq#Gwj z`td`c!>i1~XwoW`Xg9H%EMU3ii)Hk%PVZaP4#Q8FI^^(1{MT7Oe%&FCq=uv~pe)mV zzTD^_azDTfBa{A)CUsh>r>TaZHXlt6^#JNZkY-C}CbuiJwYyA=1gR4xMTeQW_<9CR z+J8KjQ~rLENb(5qm9PI7iJa9rfwa!3hUrM4pea}GNy#$>wod|5S=QUJ^)tVB0rgZy z&Voe~Vk6ympzPJ&G}PvgJF;{5oqWDtwDkr}js+IobZW+VUMoGIe3N#n@022j+-60k zez>JRjAp+2N!7dHa)qKN;jX;^nX9PuP~1-lM!@_@|!_2Tww>Eqek2=`Y`#$`!cV zh^8$X+!fI-F7SGC;3sbLx&?qW_$B51J6KX{JkDqe+lP3C;=xACE)57^!Y;!N>Tb@z z#z+DJ;Y}L@y>cY)N<0bF=TJ4~B=@fBA6PJ@rq#>Ura#QXCjNCqn&I`4*8 z;L@#0qx$K<3iZ_pe1JxjE~rf=HxSrUL1R4&q?qJZJw6s*2i4y$K3lENlz_q<3hdySYwLT-H z2)>r&amWOw9FHK;m-KEDDycnx+kDz|M~qGWNPYgZd#S(XusqZ3P70srZD6gMqP7IqaSS?L$b7;q zpF{BtJ%XVmn_A;{e?Csk>b8N*dV_(*tJyxVu&8F=gI&odyydHvGx^d6CO+xJq(5fy zCe-?qw5hdso*%1u4Q{+k(v)=jcZ|us_o#)szPpjy?sV{G^hJLVZ8dCB_y228)l=?{NUd#D~pa{1viQ^Xp)|E`><_^-0%UJ| z^{0`_7wo=S-Dpu=OU|zGG|1+QIOTIT0=85Sf`^|={a4vuQ_n(z>^0YD`&e3-Agd-QqT6Ec6TWU278T7 zW@~IukB6=wtt`2vA-YDe^ETobQCqYO>%d|YU&dI23ACZ~5h z>-NZ|d20J&xI_gz>qZsm|!r%*W zpEb=yesL(Zp>NYy_8y)k)j(Fi;=IhiFePPzy^BYKa*b_i6L_^r5NP`lXx9zG#AjHN zRb^5RgIgs@VUxKA9t)XsRu6SYVAMr9ts8K&s{%?QeV)Dv{Q1MLtlsIQ8Z2gfwzn6_ z4PW>UPPKDnYshPEutHz8_Rq+0=MCzdSo5H5z`|X80Qk4$Z5Aa4Q&tB-G~aW2GPG_Ank(|;5@`A zi{~m)9n&*Oik97HWLsf$rMqatJd}mRPw}tFYk=ugh z8WwoOPno*Un0uU#c`MYkL?1ssP5nLARi>c#D0Fzjq1Mj~ehisKv~ppCTaI@@%s`H* zL3uIYE4K$v-2OJ)oK1DV4-BVD7`Hoz{(5@S85qdk!{xsf@D2sAOgeN@GIR9VBTW}g zGHpeRU{>$!A%Wfwwv9nC4;@I_IGZL1iAAXq8{Z64-#O3ET?TzbY2p7t-oF${5|iV6 z_%rV^EzZ!~`_#o0QD4<^8D>Q%?59UoQ6hs3Q*@k)z~1n2<}enEsUVIqR9D1@v347M z%<<9kuA7A=6Q{)8lNm#sR`~79hdPbdltkjRLqBt$`6+hIe&w1PTP34z_*k@$gJkuz#`5Mt-1@<;;2X9kab6=8CIKIO z_LSlgY`6*w`~nwF48tn+iYO>CXm z%L|H0X@=|le@T?s*o&1rh6t?Y6~ym(SI{DSo7EKaBVUA;u+ZI`lfirA7)aIlwTgdQ z)V&u!p|Y=jyddIO`%W=cgvkBU(<&JD?Hv`oI9J_YW9cB>)5cks7{gbb1Gf9`+^5Yd z;;$E-mWZ*O79t@u6WM%1$;Zo{mN_}U6qfB)R=Rv1S|)V?x)br`jbgjj1s6=8tWdO$ zWtnyHtTsU`!#<=w7ed1|H8#`af;Ry^*5*N`xlV;9)esowwCQz!l1fn4$d%uGgo=oo z`aLg{7gcWPgLd)MdVl52x%0ILj_s=}rO|{syR}-x359alsSw#*%{Z>W#|^`ii|n4o73XxOz-mt8A?m1UKiDi&f|L# z8_7J|h z9lo;v*Jp#3`Mnx`G}^K&%T23+K;(oC(O zj)RllAv^Dz-F$@+rwb7wuCHUP&F?TgG{NVpD+HghZqg@Z z1NA65+OfBL{k{WbD5tDNe3yR!BlBE8U?n{IW%?(?e}9DN4|~1H`SOr^=JGkQr5IE-b*n zvQQ}=nQYdY7l|)75SHZA_}&R9!JCs+w``%FFl{?KvGr)>Swndd!m~Sa4<_!XsRiC0 zdO5)V`fud39yZ&s`q)UbZwANLWtZE88HT{l=`;nff^o=reM=aw1uW#qy~qGm=;rc=X!3;V-AP71mx7s zPO}O&P)=wPJ@a_nmtq$-Ju3v7#n+>ZOWPZ9dq$L>r+0MMm>4K9J5H7_ai(zfeZD5`hm8>uO}YG2*VNY~Lm(zEl|c8tzU zqfdiJM~+wEyyk_Q{H%Cjbzk%!hcjRLu3B8`h^d_GRF`;aQ-@;8e5e(zo%@@|UzQ$^ z-}gx*$EOq1z$Zs7(EzVUEsk4T+cX?sva#k@SFygov7fB`#4Z$Z{YtXX zr%R4*{=Ls~{cT_iewNm~*?;L!luX!-<-Ba$q z0JHVX2us<2MNLEaWR#WeXp$3euWYI=v}#OaD-T;PZhc^idV0^n^LccY;8(w-f#4A( z?>9zPp&x~;FmHO7P2A)}IJt?WYY6K$f1@+#>#eZ3H4oM8()nnFy)N2L*|Bdj5V(CT zHE0%8p6AESwYMLU=5=~om|v?gS}A8IKQb&Cfs_Nis%V``_eQfSU{cJ14hE z070R_Mtj;$H_lkEs+=tsymM*~9?cHs&%N2>0GnV+H#uLD4j005< zT!ZJu4!pRahn9_IoBV@}4A;kc6+wd*0dMM01|~h2?gInirr+%fK9q%5&na}lZ>ah4 z9{hLCI=$n+bL={oOP^xttt@w&S-@#okoOkbUw+O4beq1=xxUBAt2Wq5CIx0Rca?Sz ze?pS|@jKgELAgKZM>|K>qHV@1BO& z2@EZbU6#G6Wck4Iezw_rq2tf(U1?=@Z`->76ORWf+JI6OgF_(iQ1r=uN9s3MCU-~Nk)gkT?Zq)(Z*_!m8B?ul{v+H-vWjYC+X`#=Oy3by>X}FcsT8jG5k1MLwEBN zL9_a_3^@D(nucoKh;kLOCsNu8NydWzE};0Y2q= zeU5o$*p@p)ok%`4?HiJgyy2`}RSIq|0JV!Z!^cKH#^vXnjD{+niPNE47n47go;+X) zk2zj38E}C~8@wBFOyPcxSz7!F90j95=P;`9^ppc6Rk;Oii}A zQSjC+l#xxpllny9e$lGMXi3$P-9R4!5F9X4S*LfM=pEE$U#6#uZqB7-WNtSc7eIu% z+c)E5`C+qo(wGi6dgVP(4m-!c|8ZRCCEy~Z%7L_&oZVHka z*fAQL`;q~U2g<*ChPYPF;wht|SL>V7TM#&8SDcxo1N4J{a zMRFsUQ#p4gMYf)64UAott+|3<>k|UK&<|>Yw7E#_%fB~UOau3nwT(Tn0XwZ7#(BL! zGs~Ye&3;1O9}Ua|WO#LXdU9|=MfFX36-kHkyU+I~`uZJHReYs-U^l-W z|Kq<@IrBW=8lEm2wxc0a4d-KBPp)9_aYsKDj4=3V<%W8h0+ z4%0hX&NVh@O#=?2WQmLit8(<-m1AJ(aSuLBvIqU?mCm)BMJ8XibbP`hi}%ucN|wj( zINrm3-MWw>^}463d?jpjIWb-NBbM0@zN2_Qo?TjE+A-t7*cuQ#05D}w00&Dy!7V=t zEF@yga}omWsvz5}t9rF7+rdQV6U}F3GF;P}Yg7c#>@DcP-C&0R5>i$Ln7%WA`&`Oje(>(&u^II1SGC3iXgS8+Qj6 z<>&J^t~3&}BH1csEYs2hGu&nJ2JHt|XaN1yKE(F#Ma?}4yr%hgcWzJfJQs8VRuio) zb~-7JN_SjMt0Oj54NsgH{FL>TL;78Q^ft=1$kFGrF=?a;;(jM?V?Cp*jKvUv1?(ws zJPsB(y}iAt9dSS8fAMZF!DIbOMwYW?&iCI|zbhY;X5w*?y6!z0 zix%N!vYqo^xy?iFL)g&qr25bA#64MS7$8Q0v0oj2(o=q-gqNT{I_E3WeA)5bXmMPENG*-@3VQ?m}zoou)ucBQ9!D4AyGI z@+}}=>&UVX?_iT!3L~@+AKi`ul>vh)q}(87>bhNpf*LYK=Y^VXeZ8HLxb7`L`R3+} zXW`rUE;&1L`Ei>HnYY~Ku%_(OjPeHj`szs))Zwi<*?IO4Wf~x@0syspuGh=rbgZB+ z2Uvm)9vLs77OzJdXMT6WLmvB;hr8~D{dO<1@6Wv9twUsPl-6Z2-rHa|d}otKj_%x6 z%w%rLP7c#tIow-@uBN)2CIJ{`eV&r4PV5$H6`ycC8Tm3kDs#C;^45{s^gV;O7Mh7Z zS~%#^uLFxj2X)B45=Nr=cq|?f_Evc)R1mBr zo9w_7t+s{YR1pnUXBTM-l0>cp``n%4g4p&J2fT-uyk$7wl>;1^Z5XwcS50SfSM0$b z5x_^KvSClbZA# zAZ^gMKe{Skr(l@u_|6RGu-J}^b zXYg^A-y>;u`o=ZrWP&uzCBBQkX3%gaiT}0G`bpWdlyg%KWNzrPSAZ}lw^3nDzdv_P z>QWBoNihJZPoEE-9{^0w_<~27es>=dZi3s#5Z;XN(pf>v=w`e6H;8F%E7|nggLKm# zZ|^_jbq6Uz6JbrmdzKOneF@8oPB5@Qm$85M0W?uz)I*}2zF1|Czo z7Hx6>tGrVx0cuA6)ptWe_S2fujf*$x>*Fap%Ol^NSIg!Cj}WCh+LmauHsfIHri_s@ z_m&EfL50U?R^gNS3jY!clQ9V;IkEhv53uO#1+PX?`nbgBw@K8S{|{ zFBeIvZV+BW4CRKc^>%217YC@vTs{34$9x27Dj4e0;;tM?KvmkGm}LvAy*ImFA8DN% zT?M-OY^vz!#pJtcM#uIK{{p|g4+3-k2XV4$o^Rt6A~njh@>+AeK{gg2nnT1I?b7TC zwLy{Ho=)%4T2W+gN;=N!3S(6oC^t3FE$$aTG*@3w3fM9II~+9sA-Tzd{f){ap{W0j z;&t`+!g8(0bPU3xF)OK%3J25jX(Hv`Xn%1InP~Z?`wMFO>@}cWKvKxhkGz34o4IQuV2GynYgs8UihowS`bm~*> z={DULNz(%R?*p!@4Vt5_A_K-SI-v>=ZFpw`GOWC27N#8eb3YKw_g@yRj#}X6`$y9< zEaDfEIfrU}g71H5XVhS2GB7ehdD3Q?xH3~@ZlYa!S&ydv**3AIti|h;Q@*t{Jd|ql zPXc0+9))K6IDGT$R=jUzx+$0fWpj#!}q{Lo{U55JXd> zZPH%PF93DAzxJ=Z^T5)G$q-@Nmix2ADg(gaWukcJz`m zOC%%g0u#*4kyv>v0V>T&L8iRiLkW{Pn_@Z(o~6U z(bRvyej~O0YZu>C7zicLOx=`dzGK|Zm2a;ad4svWxMISQ9%h!dn6mHKJvdjJ$D{Rd zP;=4SM5#==ElCUKZsZ74i2fczhtQ=f`iNtXyiEm8oALBEp`#Xfc4LIiA2QBr8X}hJkfH^+}wwZ&@w`0wxQ)MSJlgbBy3pY zJpW$ymYIAo4P-K|3Z;p5lq=xYas@0ZqD#&s!#At^it3PuuY4DX9sa7c#)gct9Gg#{ zo}T3Wwl{v)X@<3K_I|n5gwL4&fCzK60MYZsd4BKa<<>%vH`tlMPwyhoGB**bQU(*M zGUaNC_50XsowU^qY;9y<;foW|1HMNcG=S;bPT^Zy;5c~|ta*^%JnhmVJOa4~^@vav z0=Gvlrir_S{qI#5qDne#yJq|BnpqpTug20-s*iW2Z(^*X&y)FJ z*V|URVwtvyPC@k7v9I@A**<-M`h?by_-bC~?bpL!6Br4ru!c0+Y8(%x#JW3CuRZxa zJEn7OD{{wacpgXK;RpP(54W&@M}O#nW{Kj-e;O4$c7%Z)J-!~PP3u}p+{LvGa+KbNrew#><)zh&NBhYm*zmeN z;E)1|JV@+y@WzxhrW!c_1QZqui~Zj0%r{xxYwtL^{7H2}MSBMYg2EYmezfGTY&-5i4T-d9bLu#{8#z%m?6$)rjPh10RWI`P=ylVQg^ViFj8|BL zGhQA;eYw>j0|T)qpdMR0jvTeATKDt@rW?b^F<0{}oo=d58eW_a=l}1VQ9UxMy&}AJ z$b4k|N&v~#N8SHr)>N8noOt^BszXLr{CIC=m5e(2eFZl&Q*7(N2UkX5%A@YsaS+6? zo}SZ=Rs)j>1FqID-gG~yivQi#V7Yy)?CoT^$*N!?XnOmRu?er3OnGLdO1^7b?SRU#pbsCmLC((6TOl)er~o$d>}W3dO|SMf+r_YKlWBR4Y!)#1n=D$y%74j6 zI#8%CU#E{#14>0`XHUD#=?sdmM zNCO)N@_!^?DxvE~EA&n8SH5EB>%A?pNcE@40GsD3QkG>xQ|?wqC55*WKnMq^lGEvH z9JzD2>VLZ2*|d!ERpWxnSaqfJg~(RiMnUT}Ssyj0^Ty2 ztRxmR-ZQpdoMiIE-2FXpngFp=+`d#8<~JAa(R}BzYd|P+7@&i8PhdVUN!b2X{%c(Y zYSA&ouAi?(mhLM4V!WfT{V>)G&%$!y3p?S$48A^ri%;@p%o0~R&$jEVTtljL$r*@x z91#`^uN#SBJ7GDH;}lM;W;bs}{cj9Alf@AspHzI{`b=*pjMh)kLRR0P?KZj3UO##c ze$hPW`WSF7>GrqzGmNYUTe5SKJ+^Q4kET>Hvwsq+5hOmEo^vc795~j^_;uaQzah`@ zrnKZhpA1!j;VREE0<(LC8ar_*0aclRD{nP*t0yPQw_@xoIr5+!gtuPx8vC0W<_!i6iqwe4z-#N z882ZXcx^yU`|#Z7vV6l&8K0iq`5wCV{db90ZARHs_D?04^;1+w-_4>lldZVibTEAl zF848_Uz5h?T!RodVKbhcWzKwwW{5G}Hc`9#RjKW31=r#)z#$5E!Xe;h<#s>k5}Ot3 z>4;Be?H%i{_2eW(Nv2=v(+9tXhXQIw3>V?=tp(~j7&cRPaJW*-JfA8%qsnsNF;+@1 zuu~dtobD@kJZJ}>{5jxd;)AKf8gY$ftXdWbZ39?^t-DRN)+-^Dy@1SQRxIb?-KG{? zD?VO(67&It)dr3(6|B z%rmSoe9@&V{rK&4lS%6OeqI?EeOaF%3$TmYL7=3Va2&4AhzY?sdeM{jG%1vgjSZL* zAZy_U#u4c1tvv7`*+P7QXTX1UEa-X!`1)35gZ-<`_sZ->Z_DgJ>}*>=5J&H7aM}yI zMu-O?9!#n;Ux@0_c=5J;bi^#NxOkL6s{dpMqE9$X27V{2a4u=Bsx60g{M~e+^qmZ@ zRJ?8T0#d?o?{DrG@6C|G6)%^5gPSl|l(6;u;l#-v!Ce>u;?T0IvL#eH92OQ%mz3*P zRc|Z_d8jm`s!U2(V>~RAK^MG4cv!~-eDQsU$Fm>QE+>@>*6eToK0IjhtW8JptFHF? zn!SrYC<~@b+x5_5c!mvREV=CSeadfWFZkn zo9`Bcn$i-W7p_ZrD#$oc8hqwERpCn5l6(^*b|5cR z-DK?TERowHms5~m{?&d&d;_dxJ0u^dyi+e|-Y97qrjOj=A3Aar#rwE=Z}c`Cgh z8|hl8LU7c;J7Og3#MUwX&3jVW!^-UrS zSd^LMMxBhr(>m8+_zjYLq0*h<-O$%goi28~_Sgj?+`i9CovFuo+5e2Gq?~bR<+gG~ zq%nZk2P-{T9Kl(KpMp=Qq>&(i`rY;Z=;o^?a=^As)#f#6?LTlDiKIQTQdAQctvaQ^ z2GJROMWMwL+R70au#CG(*c6Jiie|3abOK0HBjV^Dv zSjs;%eNgJbJqb~K&r>NKWEqeXh?s6;7Qt6pUJYZF=6)m_+P2zL>adZ%4OcnsqNf;y z?6fM%w#dCve^183Ms&+T&&7~lc2-H~hG&U-+Nq*%WtA#rNm<`IE~%`{mk#uT=IRde zf`JtiYS^mds6YBC3jS$J1Xc23uu}|avTs+Ce}3|`NU~|i+TeDlp!G;ZX#Cc*YqEgF zK9$4;Mb-HHS03(;I)S+*sUDyKf+dZp1x`r&u=FCqb#-p0H)qXQRfR~X*q(_! z70OuLW60P*x5V28cDKZtD7||*a%z0$UZ_y*sBDFM?mJmXx}5}va?ETBLUcTK4|y$H z$Nf17-864DF3SJl7uYX=);CFp-7bYxy>xV;CG3S#>#|PgRt(wH27v!5* zed?OCzE8M17^kY`??+2GuzZCJj2giAZw9@YNR%F!X1{ZV1!8YB_j;SNv zl#sTgd~(Y_sMSsM(0wRSe)?JAkJrG=T2iiQ`0$2^1w)QjW9jX0dk(C>;5hhRox|9? zeC1dlWhT`CQ1uMQvMRDIq;5~)N>x>@V6-DvgPe|-nM$8F@TiU!N;hpLXSiRiq@Mnv zaQq{La_CujCUNHhq^{TUsjr~PutCsCAS+d7mxOKYwb)+X%KBs#%oSk;(pPY(H?40z zRrpD15zf~hjNG?g!D4HDCZXdXFJH%3gq^N0;<=`NfkhtjnP!+DHH2KaZ^ea#9m6)m z#4oR5RrV-_7>Q~HSs81~UEI91*l4uaAFv;31x@&n&8CD{)$1t(05IW%J(h7HcWn)3 zW)jQ6id8+Cx(|&gV0CheOW&B*eXdnbsp;mkAs7kqG7tY&JRhxYam`qtC8v;{(8F4~ z`>yuxP3u8-YXjTDjnYaN_TDU#X`AI6(s~wIytSu(LV&IV)O;0r%(}344biy)ktN1p>EK;QSP>Ie$4vATUn^r-NCU^N9 z&Q@(EMsf>@#d*c!8P)Wo;AQ-)@nc<9nDfb`v@JAdfPQosy}w`ay%c&?=Yh@C_mF$a zhx^~A4vkY(Ed~u~?l-i)m~VgOS$FsYc z%bq5sO9Gko?*SYttyD?4ZwZwEq90)xaIZ}_|KYZuxxR%m>Gpj^nGP!BP`SIaUL(X@ z2V2RUxj~Azn_;N^bBY?Q#`T*se;J8yHfpEwlS{31rnG{vg5V_1HEdv0Q+z0R*BiQD z;aw5yMrUJ3sus_oBx6h?&`YDW1Y-vpQ{X5xH#+K_ePFXm)N~57=z#? z8Wy-#s3yb+t>uY|qbA9ED4G2qL+9bo=GuVao}SaClUg-8&BSQco?nL`DQc5QRMiMV zjo9NLFV##kK-& z2XjSjvmN7!Z}GwHIl<0^4ww9Z?QwSfP!B{fBb1O1F-NJE1NXnlmA-s)I#HtBWU8~D zH>p_q#Fa<22k!{b#3JLbHssO$;1GP_Xz&iX%6Hf&L&ZpSZhE{WUxhxV=L?rhC*7tt z(d4tPsc2a!7i2m$MD@81w$5Y>1t+GmoSr2v_1nC2zWd1;{QG5<<&q6UbA37oR|W%( z{#pC&b)@K#ITRf-DyeBDS6>%^toWJBZvH*qG8(5)wKiFdu=$>ekqq4-Kc0@OKYzzw zU%o8$v!uk?6GwAVuTrz_y~DzFii&3o({oAIb>RZEDH%ejviidc)R1pZh5@jlzNo%^ zJ#g5+t;u!Jw&cT5iM(&$%G8ywx9uG!PK(bb>60Gyr?}}!t=F713P=;kr*t|lAA-@# zVBTX%J-6zTF?;It*UAtr_vF$;^#@z@GFiwhDj#hRP}= z2RIv%p*Y0oYRsBDNbl8#TYymuuB@=oL;I43hhub{Q*mfg&wyMOlLz) zRe|9;=%gz_F^_PQgKlqlfxae!+@D=E5`OW(9U*35$NY8hz=G#L2vRBREh?iW5Lz+^pE4Rk=bA zy(#vfz+R=+XeET*;7x}7O`K?|v;X(A8(**89lX??di!&=K(luFA=I}L(_9Y1pVN?) zx8$!(%O&=$yW%-7Lf>^-#rxA=9%}blTMYZDg@>$-9v~cCcHYk4xOn%CyQXX0kND|d zoi{y1g=d?{^>I8#@?H4O@qoaKYVZG*j9vhrdC!g6^rv1A*5){{0owL}k{=7DOv9fo zEb>_X$#J)eD!AmUZhE3KPdZ97unAXg;^3Y~vtAykU#=k?tp)_W zAgdYq1^b0Kr8%=k=(;I-6Xx@dmZ?zb;0VbXm@kb`#h{k>=CSbx)!vQ^g-T4YQmT9J3!eiHdn^l!J+$!UX~ zb>QRS=l>Qd*)pbA)xHG>?*1iq{*m}X>e*fG1nhZRA)d{28H=1;t;Tj9K`;|)KCBkG z=*}RAJ{|zJ^CoGIDhjb1Ro2B;&=|U5F|Ku;WRtT1&`FLr8 zw1?sR*?>GN6S9nD%&ifxL462L)pT|TRgz9bs#W9ZNpMKdZp58KWwkunrqej z^W6nvk=m6z<6B5oc?pj@MmIQ8`LY8rrL{jbLn|&;^N&_*a_m2hYHU7yX}@7yf(c+X zqbDE`yVBqEe(Z(&WnywNhcvXb(ex!0DLsQ+nzp(Vu$L)_FI(O<@>y~Z5zH!7GrZ2R zfIvoMrREztop5(~%@QiV2q)E4NjN+Bwo07?XOMS%QksH?1}Bhz7&>EZYLS5$e)oqa zK1twY*=6A=_a^idbf!EVY3qIu`alkMKz(1-CweFjf{mmbb{2`cNwrOWQZB#YiSQl} z(M#oBItSxf-U8R=e@7Wp@E)7}bL5%OK8m!Ts{-PuWTAX} zR+O0xV}FxlQ^=c-c|*wliS``qd_SIUGY+@H;_@Z^i-zrH&R4X3oR-^qEXc}o<~y$u z@hzY@$aWa_k{!@5WBokt)>&~VwZPc2{AZteTU~nk@~Sb3#&VbrTy3cy&O=$}>i0XN z?vQ`4A3%>$6Ls!DQ8#&o54dSZ-k0yPE8_BZeG(tXJRg9$U3Xk-(+K|_fY^8b?KZoB zEo;<=WO?9BOqaw1Rw>D2VZ(#6CNxwp2hU*|>xm0yGuojEz(89OtB*mJkl2OTh7Lzb zAj>l-<6gAnZd2V4fA{vBoB2uz4kKNVSe2qb6OO3veS@`<59g?nn;fjFS}c}P(M^K8 zS4g}oFFRoa;q|(fK3JvPPC>&3kPr2q7s9xEnq#)v8omb*mRK$2d!!kG*)=(bj5!5HIquDfpM4>(U)|{Y- zR0$_rYLt2p*W;@R$`*cpn{J@&`{3VxR-Nvg$=x3!zkEpdlmSDRiW_ZO_}Q4&RjYbq za$6^n^LQk9{I)%D*LoD%ANjb7Sg~7wf8AdvL}MW+%&3&wmk4nIrm-f^oRqdLHrTvz zWM|8=sOyU{lHf7+c>T%AQq*=C5-dZ0QI$<5q3kwG#_=SbbMwFQx*po{pj|K~^(4#1 z^}^b*qpGUO)pCf(ILpqL<)N2yno zDkI7VqNdhDd_K?q>|g?hORLhN=SG6Vs@7(>`w5QY#h!TSq#mXSOwix10ajf{4WChP zlP_$r;rqiS>jtEVF1|Y@YnpL4*TlV#CyB$hYp8CT^M>b=n*wV39aiYo>ux<04c?(W z*>o7ppFyEAofX_2E(Iko9=MV=9O4TbJl<_Sr1tBJrJX+QZ0I$YeC5So2)WK;*Bc6> z0a>X%<`wI|8U~Ejd$(ka80Y#cpV8@*8Q>;l*e*}d)~&sS?{&I^qGt5Me0@vBg!_0) zB%Ab|8j&%dZ6p=cY&`d-H~yAKpqM%6=bf~WTn^eRP3H7HiRa2`iqa}pL7Uc^vB6cjd0MF|*x2;4z`hUpw zDH95sSig?;LXoNCc(TTdS-T0?zgot3$}hZH$6_2V!F4=_ST&EaQ4%qh#_%5&EV(gX z6V8QLt18R-Y`{R$aRbwo)qlH433x~3w~Q6h9MM-ii4v7XjJFZjQNJm>?#56%s(P?x z%ls)W>7mHOi#+O&`jeZlo&PHiy0ZFDHL4tWztKnnqfBl3IK_%FvKD2{EZm(lPgBIb zbPOvbJ6oJOH=R{&E+8C1 z``#ma64H(=Fw!m&Vfj+~8$_V-9q4gFr1G#T-;-M9a}pmMFSx$+ygQ-rth^EgN*i`vEKfDs2uNG-uG%n#q53`ik-EG>Druoi#TFf1HF)!~Q)_3`hA z*QLQ8Omk`}t+`el+CZ}2Id@8Zs=a|^^FQ~SF%!1M1Hw0ey;*h&g~^YSW?it(@i*hG zPS}LZ5;AO9BzMzXWkzzZo_y{f&P;`mARw`Z3@p? zoOzVIJzM|xJ%fw*g;*j$oA*qhtrOMmQSkiVRtGEVjc* zSmmFW7lvhRHq$|M>jBrz|8B~)TqrWJTyr>qh*$4M}3uGJ8;$B%SWY~7^$Db(3 zK9$4zn1xf|$#wpxvGF-UcuU?HKVpgT{aHUY*%=PwMWkg_1pRD$zQ73=B~P`9GN##y z-|6HTo+)OB-{1cm=dG<`<#L_{yx6)Uil-#TY(`Oqzkz5CUOZH_qE~r>%~O#cCdDsi z6uBiw?@&UWF_9h^z*z1dsD315FqK$B8)mS_DF;n<3T=z5lIdH?PlZfLWQ=pbD_Us3 zhh?0dpvu-Qm$=xQ@2U#spA9WsGTZ5N`bb*oI=mK2#G6}b$Ts;~N30WFE7N8`GYT8+ zCU~vUTyWsnMT;h+x62_DHqDe;o3J?MTfb|}y1dq=oO<#xwX2{(_=uZ?3>0LGo>X1b z_f)F-9|E2GgQaW5Bi6qTDED0rnsBg*7#`+gcgED+zu6Gh-9NkUBaJ}gGe%nS)y2(~ zoNF$6aq?>ywgv@>Z!iXmj*;%|)229`Sn*ig_ugwKYPPs2t&ewI35f$Tp$eh8FlluQ z3`)8VLk+8j!Gl*{BqU$L2VSE`3=IvMD8)w@E9Z`mPBB_=_04{5v#tox7UMyQz9#Q+ zviGbj*;#mVf?bsL+?o3x+8B7wlCSBUA_9G3_pgWMMQ6bwhh<+enrM@+D8VH8O7wpx zTx3DKSXYxmcQ#;m+h!c`he9D9y$gHdF(ap%?ONh+)8|%W(hIUjR8`fmb`7lILRX|e z|2`}|=dU}8PJ=J4EE0;;!tGmX{b_-9A#T50su|;LO_iH=PgzTqEdKWw53AqpYI@ZrD(-AgJ?GN42!Gu6hdv6pb@guWyNGj`zC zNEN@JqET+L@-lld$7_ZSR|O%1hapWf z79NOC>ba5o#RnG#h*#4;qpjpwqXQewbp-Hh;+a4C>S~f_in9W0vyvEI$MOBy8eLr` zW)mv?{Ogn{G(D7f7+$4`0(Pb9bY`82S82J&t39(OA11o$JR$Z(kYSt0ZvUZKlUP7b z_EmebMtENTWjwfY%Dcj;qC1*%LDHJVW(G^KQ2vnG)_Snccj!p@;rASECu>1)v4g}^ zn+9+>JVZc5FZNvrU^4RA$xHUd7a{{vPdnV)*LN|%b_D+tBPB%5@s;4No=P#pYT$Lz zvYREdt}g6sV_-e%2F$lY{4V}=g=Vr9KtrY*aE{l_f z`=IltZ_NO=d$A{D8JkjD;z?9Mv_)>^kU%!}nUC_MYkYxGV?B?*3l^f^!e9jihQNs` zt&9FnQ3Yxu=o*D$yqD1+sYNB=&-B#Q3>~QtyRv>4NrF4P%HIsMHJwWe-%*XP>-QU9 zkPW(^RB+Y6_e%kvEkl{{^UwC3wH0m;!yRCOoj=O}Ag6bhJ^bKkPEj(wqQA~OZPz@l zO`hVlyB=+!Dp74)R}H1_Q@Zx~J47Z_QD>A(6f z&!O_>!B*l6x)&0>uGP)7YpLX37${svBlk@#r!8QuS~|2B=2~cNje>H*HS?mf{U=sNiEUd*5MP0Qlj_2LV<2mLSWAL9%5?RADfA6d*dKho=EAer ztS5Do13b4;r>|7VrA@V;6nuQ81!+j?+%Mm-KZmJN>KL@r)ouKFW#q?2paZJ z&;3xy@*)~!PT>iVoj}836b~emZ>m~B6mEzytzpOoo^C(;<9phj{cg2{K3A8V;J$@Z zqQNSQqy1&n^p>jIBuQBWAQw~=@Y_pd!a<*-$zg{rg2j7+-J*ZM4BmR|3-V- z+USnAX{f8|uWhVX=)@|I3RqtqFcTIlxZ z&m#T}6`*d=XWCARb4X4#oS!2~E+6)vKn}h}&z(R2?%@QZ(<~0%Ky7HmOxu>6FpP4J zKk0};2aTy%^U$AZ#Uv{#;pLR=%SJ2u<--^EgLCt6lqS34<3Un=OR*D=pHKEqGIWMa zjnMo_9!0DhT!JH{UYadf+{l(A3QQekJvZQ0aDuba-(zmJI%F&bn}Z#bt>k;bmxv~Y zqs_O|(u~#nW}Mmefy*9lxoo#m4XwTv)etRw-Kauq+)-~f1HH-UqI?C{%Yl|@n?{Lh zeAowz5AP&H%AAuH^aB-Z_09PulB*H@1N*WJb+14p0E$X^b0AfO@}$~LPX{F6t<0m{ zKzjTRax{0^;yAX#10@<*+ZHOOP+vJ?|AiCeS@N$MhUn}q_Kc0qynWiR$TiOP-e#;4 zP#p6mtls)ZkM%*9^f`+py2K@eYt@CZ?)OOr`fx=(1M%*R#huJQgAv| z`S&uxSS4?W=2$wQrkut+CkPB=@;gt~>ht1eb5prLz@0CGlA8nQ&*n*bP9h0dnF<8Y zxCfrLy!+pY1RF8HFgAoMdh+J3wb#`EK{cD6}PUi z3=6ji(L&RHcE?H5A7{KPddp{6%RH@QKM8dF$Cs)4Rdd?*`%%f6*k&b}jKSyxEw7JY zrDOrpQIUCa$~<%n=vItfm)K@nw3aANbQ!gU2!a8-4v{7)0gN_VF8}dA+R?m1Q+&!wYCI~&J1%gl{1s?k~F5c z#^L@tZb?%TE+5gfOsZ3$=~G-b9Z69Nk5j+g@JaDelu2#8Bzhs$VDp1L_7jNwmQ|am z;nb$O8$w~!Te%Jgnj-9WZ>im&P2;APHzRPQ2Ii-6y@n{5?~~YmIWGs_ANNjWNq@)J zUYp~M-F%hv@5w>;+QM8JMWj%*${IaX-U&@v)=?uTXL<1!VFrk{v9 za{RF6SBY0++L6R5p*wHnp24HTx*{MNJda*)vT124w-?P5)&tA8LR2bRsN1cHW4t!T z2Gt%MJiETN`uO@|3wybmf07}-cd+xIOK(2jxb5=ArjxX~AV9 zW7p=F!^6WP9`iB z`~4da`OxnD*F?6k?AuQd`_L?P{HVNtAUz^`Wuj6Bq&9Y>d!uJAvV;m} zyEQ-cb`EVk`@;OvC#&|`F+2?t@5eP5pl=ge*i@LvuQI_5pig3&`Le<~5M~@0E{~dk zx^evTU1*3%9xfo>fxSBT52cfFCBqNIHZoFG{rQJ5sr{`MG-2gGWP88hyM)%1pofK3 zsjl|!)cb?1qIcEzUP1tcQPZ(jO_9x*Y9{WHtF@C~-|&^@z6D-;4YrcQpPt zqP+JbZb@IrXu-%Weldq5WDLeB0UpMJ*-^6f^drT(9-9YW=j45ct>_Pa(++Y#GS)Zp;3n$4i?LAc}w zPsZqbSh}dqy@fBYRGaGiYd~`|&DM@B6ZdY<^43T399C7%R+P|? zD#RT`7?E*wJ$EV@4Zg&z9~-x(hugR5wf5uEOAKb6o`%)?+&v@t(AlR!O6kGRaaxP(^|(PwHyfH+l)bhEN4kESovwm_SvZr zfyrxz&pR(T?gCRXYfpQOqJx)vFqa==lD69--@-UMVFxv4co{z(IB}Rb{?&uye*iPf zbJs?R!C)}}BZYv{r#4z&_Fq`}wiU?87!SWr&&_N4d5qX@6KI2}dKC=O7sd@jtE%-` zZ4kuZlo!xc<4}!LbYa@HuOEk{q&Bc!E|Ptl(y_fxB;8T3EUOGaS{*mg+^eVUhB7Mx zcI(O>*|1yW1OAX0J3q%5O#IP;M?y=qQ<8Yl3%C9vFVQ!#aFe+v-=@>k0-pnJ0F6B# zHF*L0;mV`P3MPxrx#ft-Q0oZS^j!B!s9oXIg4g3DXsSOatW`I6Z{R%N$gCfJEDxyq z_D^;#WPz$+=s9XPsNaQbtz}GGw+%|S6((BZ1BC?q^TwgSy^l}2GadFTo|v0$b-f#2 z_f~i=d@g|h>=}u_Qe$TVXpc^%^ICzM9nH63ZErJ?TVn?Sqi(q*=R*w{I8l&VMXBEp z@gj+>UJ^>Zs=HwRqEC~z4)@m=RB_D1OFtSlT;%FCM+kb+P;=H^cHMs`KA|H=$CB*; zzSxDTvB{=zhF*u)%8u4$o?B$>Q9wqoJI+(b+_}Jw!tF7v@fS zv3`~r>D}4W^+BHcA*k7p4dojs^}aVk_swP)vRVLdBq30Jt=??D(?!^`^=VN2$PboY zz}3YidgXH*g)>mOUFY6|4Rbh3L33gQYy|J`0S#I=M7(3VmPW%=Grrji8le^xb~o(r zyvzO|D~0MfXMMNl+@4MB^(*~x0P_K*rRVo3K z-m+|NKkN=Rp4#mBxfht?#%g%4nOP-;<69qnT?8d-CPaYJCDP3qL-OR%ipjZFp@=W} z_4RKQ?+mXgr@T$u1E0lX7>Z(h^G}#ubK>O%m@0A^fg<7%{K)-^Z$xllf zEigNyvK9%uRV`n@@<2+q+1t78Jsds#Wl+eslF3GZ6B1$UfIjQXkcaMi!%(4+x{rI1 z^1aAT5k$>w+nGI~&ugjb`JL8P$8w{Uo{q`4Sje&QjMFqY#M&)W<$S}R^yqUzP}kI` z-;SG#6;9mAwV3i%u2|RV=rCG2ADH+e%SzO&X@yO;`v=p?|Qxwl#WM7TlmYG z`0chVcW+wGS43;U?W9KEx?ey4D=HNPP7F`iLwr|bmfD_9uc&Wm<>Ts3OO-iM|h;G8KF|D7s?hoau-R&YAH@d%d=nVr}h5~>E9_gbGnm%@NECejJSk}*5s|WNUbod zNbH=uC5rM!N(*9!rCA%cn>Ff{-NlmT6eYnj;l?e1|0K=2HYy>fY8vOg#J}MF$820h zM++3rg#)CSjhg>;Yb5!V^IW#^CEB7!07>mvOk>PmZnbv+>bkl(R3q+Ud{3;j!DW!x z1b3Zn#Q_nYNB}#|7FMlnF|$-G#&StsYUxg-Jjk1aA%$GP-i*_OLA_lnNvAk%50(O8 z{Tv~IcUTTW{nMBCuyYr6r`Rf&uLP~Itmh9O6y?u$woR0bjjXFi9rTSbVdq?BTwCG? zX`_EDJ=TEk+gBvq#ojF1KG?2k9P$$QrJm*EUpR?cpMS_7oNop8F`LP(PFSx?e0vel zw)Wn$ri-4W>uyH|ke4(nw#t>V%4CTS64~i_DOOIRhz-|{Yhez~a~?HFzxgKaAqzbp z&T|no{cp-X3dg37HlN=Gb_?S&uk;y^2ptfwia+ygYkt!VIiWs1TV zuCiE6B@7{wWT57fK1ZPAI9L^CJ$Lsm^p?8=%(iQ7XhvCdUR|j_NaXxw)cnBXA2nQG z4D4}C_mP)~l*4w!i^}-EhKsDy>VaAX-OkZ6yZ)86CeCd+Z!syRJRNKokA)U<AOe>Mj4TlUOe zm%f2%rdT9Fz71r>D3P8`I>zCZGuVL5=^IpY8>kL`f89i^y^nNEh|di^v~B1=`(f6^ zsLIQYo_Oa)Uf!RyscuzWPwpR=a{jrmPJxjHy-t!-M>HeH0uLUvC`#o8C~A3~eQ_-c zSmC3+Q@^SFRQc5yTRD>%xY*c=#<8$>Z7c07+SQvq%d81+tF`q#1%+~_TcGgUKZ@tr z*eowvfS&#*vrh2+j`Er^by|wthH%iT)x%y@e7VVFzZFW+e-i1@Ci+KDGIuO>_8;un zrYc!!7249ZwSFbIPab!p&bE)$uYA9crhk1Z_3=dTRqd|{cipjIZM}qw1Uw=kNgE!5 z6{_<^aOw!b2=_me>;M*28r@I4B-W+5(d|Td&;ZF!J8z;)uQ%F~oz89TGYf|q+s^;4 zz8ciNA_|8p@)?MC{rk%13-xaso5%O`*y2C zbVV0_T!&yt0$$F-6UEg+*Zqlt6zZ5URF`P4U=f>SFaM-A{is~^9@m9La`#6S{I6y9T)+F2=e<%xu*%iOUjS4J}InXkITe9(M}M{JmAqTAc@p-9{YQ@ee*J(}@5UvQ88 z)t2JMsm2NBsk1XoTXADbr+F{f=v#}z^*cx(dlhqH;R`rEvD-V&jk_X!Et=$-ZTH5( zhgZsE%8gprfwFd5MrA<6Mj$Ed56C9%(Pguqiy&VN)kGBSZDPGkIr2B8jcGra7l+uY zLEGZCAYZ~=m>`ybMQYkp}zAeFX!wz@<8q~IYmbL_S^!;t`H?D|0>`OES`|BWR)l8q&3i za1b^x1N`3!-$j;Jvq6YrF^RI*HdkRpTza3u6*9-6fgYL9GXOWKbatkKkKc`K@ea+- z|Gn71(64YDwr9+*z@v$TU0!} z=VMLISJTa}VoI5hlG_A4aIeVK*Y^^vvxep%2|;MK@yT zS(*r(3rV#0$)9(;X8rXZb_qO`h3Byae6(@6e&?d_5E$Is1=8-HZ&~O&z`^vtes>mj z0|)HR?PE;QU*;uRLcFWum6bVH_KF~lGy<&|UcP*Fx%M$iGQ%8hyfz?r?M{r&qjlMT zZZ_r_Sc$&5XE^cK#n9l>p?TdTxP5rY$9QW(eC38RvARnnCAS4lnlA*8+YfK8I`4r$ zmhrrYB8?Cx{72XWWd?+@6X?|^vn#GRZ`wr+HT(4&S6D7qE#;K314(RB z0sWu#7I~42O0-@te9Dm0RB+9o&ANW!U}@V78oB^v)Nu}+vm^@nJ^{Y#+opoC88bCj z9PZxHeMr86((vtbk3v0S^r0*noLx*=|FD)@J#8oBeVFCAH!0gAaIE#_O3XMElHC<0 z3PD{qZfl^J-ey8RrVYfeXzj7D3s9+sRYJW{GWaO{RJPEZ-yh>J9YA`83tHozI}xhf zl3$a!FHm3<`e$-Dtlm3oMU}P7)cKQeO6&=}APO(*xbjdyjI}Oyn-da;M7z}sA5vT7 zXy*H>oK7VN#B#t^Q0spu9>&Nhd>A1r7RJv_kowWNw*|8XS&Zdz!{NS|4Qrrwzh>73 zHG?7Eh`_{-`7KR;563ONGW;2Xt7pHJ|9<;x5`cUgj|rDUShS5R$4-LvU~7!eL7%H%xz1_rbC@keDD^lp0BoFo8ycQb~Uy20ugnrs~_3Lx}h zOzj>5LS&nY3ac)?Ij&ln)j$G%S2P(wlb|L!x%5%E3Xxj$g?!_hd&b4<1;KOKQ|@pn z;paj&A!7w-g-nU3YmOdHrIUDKankKZ+XAi=zDyON3E9Lg_txw+ek-Zd-Yij7hq~u# zT5b3{SI)GSe*2JPo`XSwZJKT5>AMuH2!Bg$5hzb27dcdEdFw`mrf<8~Bz>XDh`>*; zV{vOK+VD%6L}ZU8{2`mcw2av~XlKt2O1rJGg1v6ubN@EUgm)>hklA54KUjGW@D7D5 zHcIUKxSuQ{Q1-ObBHDW=>k>u4GKViiMb@m$4B?Yz{xWB_FtDv@@QtIK)ir>h_LHD>X)`RxE|)@8 zpZP2WgXn`)>|#7H%w&7{`Nw^a&SttoS1%0%+&s{M*o~|y|D0lTVlw@l{>e<@0e!JqyYE>g9EP1McOqz8 zxy??>gsSj7Ei?0z)vPiex(Dl{LE&Ah=@Fs89xFSdwmgsJJkG2PKDwBp<7#qoW}>01 zOKRP%jIi`#;zz`(U*qwd=0_f`KaQXmUAruLeEfEdPcJTx+c&?lBNIpS5>B|;n8o;G zjJ)LJr$M->7dW%qpUNE0z58+BQ$FN>QW01 zRbQi@a=0+26kibirqNmMVOZW;OK*i_?=lBwFi=1bel0>1LihYi~u} z_FD$09CH6#MY%FA6)^}~#BXM@w5x@1{X(ybY_4heSrl#SA}x*>)bzy> zkrF0}$%LaD53=X{3hdo#26XRr?x>c?GQs4AUz0EG*AtC|9fP#LFQldqm8dD~S^sk) z;J7RiABQ*o%Amh_j&0#4&RJWFE!lxn@NG(&!B%Xjtu^^ctBr4>7$e&U(So8Rz<*N{ z!~X6*sPEEI{5*aP`1R@Ih9o91@JT~{;T%_)P-MgdNWviRsBtF`~AjPsw>Q&NKdAu^@;3jmcmeQMgUVY?;E27H&g*=` zMC}Jvf7HJ@v=;*rS z;5eyn9!1?KFg06$v>XxKu9~9G@)vs=sB(2N^2Nkn-hAh`Bu}l_X;E$_C{Un zwTxzl;a*dB)5!+kpn+@78-I8FcjDRjT7*1fd6%bV`g{N19^atF{pZl3p{vZyX9-`U zjLv^?eAAyhAZT2TU|qU2pV%4{hB$w}|K<0r%M94WxL=ZWRyuM7tNAM%Ny-HS^ky*z zgLCQxg>Ek0Cw-0|&@0a!Ni$X_Kr6?g91keMOV4G7cjdZ*VBXC=;;dl&qEl$8JJco- zh1PjKY~PR84%F0B!x;7%O$Ew#)%9!MKT-IYs5hDi>;zYC$r;q)q|D1Sx9{DpFu9-B zywY(I%v+kXJChm!$mXD&cZj+%faa#0`rv zZKL4J9g2c(Laicn=cq39A+Cu|7R{X9#5a)AA0dTDS_f4Fjh@Xo`)L*_fxN-M`_6 z6zs>=s`gtm+sM>C@k>eE!hr(*SHz^TC$83(8KiK8hLGajW<_%7@>K-ioAI;bhu|HE z_id%?53d^pIFt}g&-`0EP`g%=mE)`i$sFwSVKuYVj6L+oLB+(-zGF^?NZKI4%GPbs zVz;VsRoX;|aUN?CF~{J*@Xj8P>BP=08=^hO2#qITz=_*L?;5 zex4PF{;48Jld$E7HSyN*M$#@!s6)6$xIlZO`ObH0JRaG_C9!WBsY92^^4?R>Nk-RN zFolSooA4@Wsn34Y8rr}f_FAIC{!$s)RpUFq8VXnKZik>uw9GEFPkcyLcYRi?(igV6 zG+#4uEXs?(+r0A0!S`RY%CS19Q=^VH#eyoVkKL*)^Tsm>j}G-Nnwa5D$SMlf^TM)? zC71=jw5GPgP0YO|)A&5#h`c;G$)R)ilVnvYLBgVfIcaeE^Nzfv9MLh0RWf9LZ*fpG zxcZjl9hJGc`(lS0owU!PVK1D|Sww?}ixiAxCHqDE%--H_sg@j|HH9d@zATq3^8PBF+WsG4B z=E54!qmq%CpAXZ^+G+7=PtA-=zk0wKjMa#8h68$V9+GR5jvt!dKpNHmcOnhu@!ttx zq+&o_gLR%(k1fp^l>y}Sxc~Vhc|@*-gvsNBm0iTH)DV!xuT;$?E@ zaS#7Hq3z@NLBX=}xl?19k(`_dLcQ;bpM4b{s?Q>D*T%4^>8K(y)Q>PRLNHRNo1@&O zFmU@E){Bgs#t5heI!i73k8`k_6ir)x-lMAqs8faaD}mY}Dc$$@jtb{4^NMPT|8G*_ z>EO%FOlzf$p$)#j)e6AqU}8-BNGJD+HHRe}!N%ApsmUIy1QYpoTLphltDA~~0lY+O ztV}P!81%AoKW%2$JVSn;%0wwt^>H!pomW*;2zy@4Pahuy5$?_k`CN=GHyxlgm(NK;H>wq22D^yCP?>h< zX5$EW%w}Y*2wOUeMktx@^vck&cyJUpx4;!GipE3=?895u!a=*{I8*XZs`1ckml~)& z!XPwa*T%ep5l4TTBnpnwvVGFuECIZ2-xb^6_tq(xw4nkp$Uh%ri43j5U=2sSm?nT> zqz)q422!`{o_j%6N zQQ$PR!U%Wk7ierzGV|RASs_Dq3H;V>j@4GHACURB6K}Tk#@S5ffmD$?y|w9ZU0(;g zP^|Tk`_NIPt;T50EkZ`F{2RrFjK4EjtnFO{r4`#;1eVr~Tg&XiYG&#+6*Zc5)kNoSuig9c)*QsB$|;g>{jvk;8p z(V(>t1zKt|nU-P1s+`(Q%2ELr5pv8}gMk+3BNUcl=OQ-zV)%nylUO zA2&6;|6daMuf~`W5D(ua_^S52OqZT3!p=F&Qu>m+c?Za9c}R7b@m9HTcdw`)C04vP zq-mGO=Uc|}{rxw8RXNKaW)`|9;i~4>1KE9v(ifiSpgp81~U34TW)VRIe zY-Z2fxH8Q(w99N)I=ZJF-!fqyJ?V_HX(^|T1;R)3a;C%TdYu$gy$LHDAARdj8b2Xd z!UW#SElY`%Dcemc)E9*byaHV*=vZUoLq}qLsqcwr=6+IlE zxIavYMUMc6EOiD^{OQ%?9XbR_q!@V&*7Ij2?(B5iU7rvi`0s=Vg=)l*!AP;H^3K6o zZlmz1PBY!vhK$hL2jo@D(najK2lYWvX#1)cS|HMD+`wb&GA|Tsx$iV6QQ=Y<0(Qb_;P}SG6FwiLy+3USA*2m&8i%E4^1&$~Xb+eVdSo2mE)U zC9#@q)!gX7rS-{Mb7jNz1#gOC%S?VjFgrAD>M?HEJ->HZUJLGIlpfhh?LVoGGvaq! zggD8qecJE$XePIpek*m&e(KE%Cx^!DQNw{{tGf_VwgxptdH zfA%7=m^iSu9fIIs<>eB6ERxh^!viX_Lb}soEXd&I#Gm?bRqUJLZC6UFivF(wYai1^ zHo0N+Y;fUZpR+CPDgg`PWHMB}wAgE` z>sIl2GgolouLrb>)}x075R2?{f@9}Vr|lH}Rj*H8_WILhOb^c*bipCd z%Ed?e#|!MTYRKxq=cCvB%m~r;!)7U(yY(&e{8eKW#tx6MN9!-s@Ycl{%0w~rQJ}n((<=@;-^*{gH$ioX}yP{B~aN&d=#}ej%3;Lle??AhgcyScJ5l}O{)o& zn>0X^H2eqf;@{+k?bw3l$9Wc#tR`@vrn^7yG+2q?2uE~JVumyj*&@@(^{aJPezGj_i++e@BE5ZZ)l z)Ak`3>N=F}8Ck2^&xSt{^XL%9h9RWA)+Lw}&=O{6YVCGzXvid8Irp9(*t7aOU(4mV3WM6Ub?6vWcljcHh!cs?nBsvuskvf%^$8q! zxy0Fb3PKH`f;6EgsAdz-ay?~FOefix5#Vcy_P1>7=xareKPDRMmez-}pvA;Oa$Oe= zSi;JB=ZmtjN`yKE`GKI0Bm0h{pBA?ABtkYA;(2Y|lS;QG34wJ|H`gfTpY6pu)YQy@ zRiWlr|N3#yF(2ZpBKrdI>t5!u%9#=dA-Z+=%S)mEP6Wo0w8Ay5(iw>adS406a-h_2 zq-eHH@A}0jM2kjqbyXoYRwwOU4zTZ=nL{>d6Am}&VO5!Pt*g=?!)3A*q~8~cQbqW~ z9i`~a8nB?L`Y$g?Ku8PzR#Of+#i8J>Wlr`chB2P7j2U?kA=fW4a*<-O!hfHW0HZT$>zq) zo3s#_l$TVS`%alNyxrwk9uh=~#UqFh_n}^-*f`%dP*YG5`fQhUHPI^OAyTZnKbz?n zw%}GyZh4s7*f+LV>LjetFF>SAz>Ow$S)&y(vNd3M0oA8pk0Y?&GBn-aF+kap8 z1F6k5VXQ>~=B6a)rz;d&Q%6qacnzb53ksF~05PG1gm`5q&&K7$cZF{O@snQS-*r#8 znyW@_{jYbV!uDi3)oQfS>RpSbBAgnDr1$lejxPw02KT9GUUi~~cMP^JSa@YM_XP}1 zRG2FT=tv@^`q{Ga$}18aQQ0xj_fhCd{B?UvV6s4heAcT{i3Zx#GvtUL2ilOo45N03xJR7!z~npraGK6i&IzV{1+XH-@kI2pFoctuWJf*K0;onu2IBM1gZ(L7Hn_UF{hq zeGT^RCF!ak?BqEEAGIetK~zJmnEsCW_=_|5rcVq%04|Rb%`lU_`&mT{n&W69IHHjm zl%dW_^5iN&lFP`FOK%RtdcGAf!7HmTO(t2#CW^Up+J)}!DX{M8v4P{wGxsm`|1g{) zy%rPN4|dgZeq=$ml(!(Eio4lD~kkF9GPm5}25pqS*LOXwV%O(u%UMFx1Zvl_NL>97%{Tov`W+0l?> z{drkUN}~~4GkNq@4ngH&zKohq5yH-@%O)Y48BK;i?q67K7+7seZ>qhnfPrYr$t|zs z)>bvMU}2%~aP$X0lbX>ErFOr3PLZbx;qS~pf~j@23QprW*uwZBbUDEr-sTC~wjk-|7wyK?}v(Ht4~dwJu*yh6EVB6Tm&G%{^F>?Gkm z-0Gk#Ab8FeOgH1Q4;Km?Yd-!6cqn3g!KCC1{Pqc#WY&p_Q_^LAs?@{#H5%=4kLxz2 z`Qxc)ZiOr~Ml7nh{W9!3Ox`f;2CgsS5pXYhmcdGsN$!nvxYzmN_B6z(0p8SXTogu- z0ZX>0mfqL{IgP1%9X1cL)_r21d}80go*99?22ayXBn*DpRFY%*B3medUd5)i5)kzU z_C^qmi+~rHTYGT?Eqv{yXTuR*J^J2sNFeXnDck=RK!%R|=@gO(#+`S)fGWRe`H>_p z6D;RZr*>)Jz1^+yOlXvk4CC#CQwlzG&YSXd>rmI$_zoWK=M)|7xq+tpwr% zdcG6rD$Ci*CX>lg?(|?Xg&RPT^-_Ps;jdM^Y8VREpUky>-n+E<$DJO@*}xR%W|2p` zvFx5QdFg2>YbaFS@%r_#5Tp*Dko&t$NqvZ{omngI2aJ3N}kc=}QP_XF&L9Ni_-Jx6$oI zzDo~m!P1m`S^`B{c24)uZ>=3vWC*#%#7c@E4Zl&@wQ9EtdoTFRm2>^e9l4qGevDj0 zd1=x3^2|y>GgL+Y!z{|Og$<1M6eZMWG^S@gY$8@TPHCtXIw<~qHg0RmPP+D+I_X@H z`02kI>g-ZKKk`w#f3@t@q5YW&L+f9I??`5emdMek`?=|;U>nvj5}4noHKm-*l7ff# zRRj^zk5`*QqoTr8*P$G9X%BuuM>g+EbR3X;XsQN++NBTnj5vrsUValQsqB8K_jeft z3pZ%Wmkd}fb$)x~kb?6~DwtJN_aK!njjXz1V6~%LP$g@UBQfD+(y76fdB69+Z=V;y z@MHG<+AF_FIlgOlw3oTycP7|69JKbX+9y-D$9oldU|%|}zXRpWI|9>(lS^Hu#SM;> zHb!{+@!ihHmy)_EJ|bNb0)Vl9++25Js$YlzgH#JG41`h&(TvR7wgOw2KTGNuoi7*a z{U7VaD?9u3QCjY^4qycvbEr{NbXQ!`nbmhi>DuRsydXnUdENye8~Ajb>CFl zJ^$7xl7~s#JXZd&;;fmHIu9zsrej-8N|n*P05&y6$Zrg26!CP1;q!($$(mqPsZDfr z+5D;WrmZT|D06PnCVmH&s&-1CdnYJasr=_1p6aG5q@a9N&Vi-X^TVMZA`BA1+dNMa zcF;}MNdd8Ta6q~7k;L+-J`ty8y_bN)NwZwanVUkL9m8s87*?ATU*McC@A ztsMgUap*Do&l`-XPQZRl%FuoJ!CU*dGmj(MFB@f_S2cEdDf)%%tkwe`EH$ypQ849X zr_DyIAPg@k^_anUTk)Rw2M)4V0iDmS0wb}J)};P|L=1Db27)eT9Ri>S6P(`nQx$RL zdQH13S6p{^_3%pXpjf-1$B|XK?*O5}C*>GL-I8%GxI1@r9#1`U47*TKMHIpA=QW)N z1it&AhKw+w3i)M`~wEMP90N zWhGp4@?3Jdos)%vZr9rnf?V9mkNc-jhf&tFB;Y03raT>sW5qlj-nxYI{S78D*v)(0 z?h_+=Q8{2}Rd(EF9_=*z3pqTCM?zz+H3a4g!;?nQ$NEajM`mKLWXtAYB(I zIHqVhdPChL=NEa#n*Qs1afuNd6y(hcs=@h`C#y3eDZ4gkZ$FaQP!3++fTpt9vQQBT z1(;9CG33a{Ur|5ac@n!wqk>OPh?UuX9BBkx{EU3Lp(W_qwjyJL8Dk3+A%{bP z&TRyDd`^N`W2_(SoAe!$mg|+X%czSxL6qq{$%TeOMIO9^=`L2^ZQE!jrK>q(jKUwU7^lSZM|_*jes-cSLw;Sk?3{;DFl8aYDFS@~fF z5wwITKy1OCmQ_w+_B>Feb3g0W(2)om+Zb&AfIjZ_rhVknflh<5)NN@d8}PrlnLeL8 zM(^kD-?FhSm!Di>B{?jBx{08GkeI=iq{|rxl#yobxgbc( zn+qj{j!j#H>)aLgVQ`u^YY2dYv|C`DtCJ=p>#>X$o8RM93gvhu+T>?qG}?Y@nBpHe z)7w|{Z;FyTT;`JM)GyqEsSUcVo z6cF3#s41YO{i(mOdAeE=0T>vl43Rlv%`#zRt5rgofl@Ti8J!_2rG)!aLTeg)l6&cz zAIg&HkZEIWuIzE{?^w}KBdxEHo?w?3cts5j0^EM1H6dX7`f|?7wEIePBKC09NT)*7 zl30^(ezAap+c_9nD&edw6=j9@baXKKT8%3gsL7uo?0LvQawFR0WT?3i12zVu(NDf& zLykQsu_h`|m#B*PMukZN=#9`Fr)$YH6eZXcF%keBdt(;_Q3wahb%$e!9uraS;<`Ik z%;0dl{^pz--LJ@1%i}S%bl7O5`Y0PDqUrt#l-mDgMzQmW%z%;S6O9GUg!y)>f5UCX z4YLsz7TLeEl?PEx+Yf{79XUE9j)($m=a1rg=1dv}Hw%$O4?Dv{ItUF-lwi=XabfNq z2JlZM_B?b*Ol;tv+GzGSfcAy;eX0Pg4WM?lUbu6JU+9T{f>kNcdlJg1@#cpeHm_<_ zwkS*l9g$ZS19{Lg>uj!>diIcfxf{EBkJ6&?fo_i0=Cx5r3hOEFA2nUMDKHM!uLwLf zcl+Oa!MF!m1L7hg>24_yuc(mGUc&mg+bK` z&)1EwpRs^QE37Yv#v(e2F*)8r81H_GI;Tj9hRC6eir^+ZMPYzwCIp8Ht!)h{KDbM) zB1mpnfGffThLqSeTgH0+o5}aOJ~vGJaNVbJgYZ-5Gq34{`Wf$R{JW9!cLb#?LrUD{ z*<`{#)~{^aj5svt`nZn-vPI=Zt;4(~a9J$Xo{%I*1IU+0e(XNeTJIGo zdEc-pr3YMoeGWZkSMKo_e~(M}|D40_H~#(7kiPgM;_*SzEgcZ1ep26g%FpThYljL> z&~WBN`B)W`&so%rVEi~w2TsuLpgT4+UEdjT94ETrlOOPKMD9kh{hWT?_aT$UD>i&^ zWf7(LD<9#@?)AUNr0>t2&V1=r9vU|czStty(QyBDvbf8OTo$(ysL^ zlM|RmRJC59+NF8^DNcN`zuZSOT+UudbMfB}R%H zI@SG9pr;p;zHuXJ@3se^Ua33XNX&}TnlRrn8^uv=H&ns-VlY4sXbDCRTJ?EQ@R*>G zL6Z5(B2PqdK=gjirApYL#Nu7U!^gbph+)04ODKgO(Cf*$e%5| z#Ll>U4oSF*kr55`S|M}R9c?+>lAx9*el?5IZZTQ3iOG`3#8WC046PcN2%#=BFxlpc z$rqoGiMcmrCU@ke+r>i)KGLt{W8z&;(jV$7@7jj#zW!lnn7vOb)wS2inHi=BGw5I@ zwW+bvmt>Re;Cj8DT-`9PqSCQS4 zP4+2UwM-|3t6at%xJ;X#_a-2s^lX}KrzO_=f-0CwPD50&R(?lG(M!+GDfmY_>YLf5 znM`2!>88Qe%?1d8=2^02f`&Y%vE~LFvL0Jh@$P=3*R1ejfcEh%r4YY^uG?$fo4_EhNBZ(X(a_**>GO>@Lu+dMz}?Z~bFQk16WMv8zDcm~#6~?ul8a z5{h>vFQoH)!XAJ`RnnrukOWSpU)!d&-yDJ&`EX4(t(nEs%7^kaKP|F!knIy}fiewf zHb~%Z!W2w?_HU}mj=YRos-lrskio^}pMnYh4xa|Q)7PC2UR5y6`o@hSwyetC!C`HvI|=e6dm;+qM-(aJy8gf)e(JYMawACI z4GvsDY;XCPWu!9pjjJOz>Qf9nk{{(M-Ns(#yFKqEQwZThXVtDsVncqF{J;fXE&h$O znzdyuO|n^OK?|i^4;_?R7QA$!JTGOK!fW$#{Y4>+WuM6ez!J>y(VwpXei7eOliUiE zr39a8IM3T$_(e*}X#pnp%qGkcbjLfvAaJ?8zkV%f#}Ptq;@L?+IcwjjZOsY*RO2_R z@!0g6)zAMjU){Zh#uy&oskL2i1>o4qx4LlT5I=N^D7;oZ#Re&C`SB9(tewVxM1pWS~ zFBfrl-F-e@|5nHnXk7>zl|lS6(r=s{mX7ebcx_e{ptwe<8ikF}#j$j=Px(Y8e+QuJaon7%j zeK(XS|GQo}r-DoOwltA2x+0f$s`u~$I*y+7bur1}ug9=HagK0g{V@5$(UALH6Wxja z6%H95>6jPnpP=@9E>L9#`pY`6#*YJ}bZde8p`ZTf z8rc=7#I~nK&)$oAdNP=(2iwt1#LkrwWEyIRhrAkA7b=tObqmwc!8T1pxCw}71p*PN z9$&B!R=ZXOGaPL@t5L0gMr{9YixJS*6Dn6Gv43|izjszk!z@8{zHkZmd$_d)^F+1FaR+Y9g3xh9Rh_=35wq}+e3o&^d6OB|uZ_tDdREw-L z#+sYtJJYPvC#ehWr0k7yI!r=G*q~twld?jIT19gfkbwXIIG}jNgqo|J zb0_nxG@x3rIVoKo_b>qo7S>_LNyo-B?IeBybohod%-Q zO}&n`)D`tE?`}N0^X=j%)#k5CD>nto${BK48SKv#2Oi-AqN(~~MEb($C5$cPmi%;N zwyQGA*os0>+HbyFvKk3N!|1t1YEX`#xBOyZLE|>9xgj7NPFgy?wiZyU56zSgf9aZi z@9^s`~rK>ETP!#SA|`R3D%#eK-p2SPGBI8qIpzUrdncpHNEg z8Qx?%+KkH57Oii7nn@Bsqi??WIZgiqBQ93ysnBUwGHNH+$-Is*L=95`$L>+MU*qYu zU>YFk4V)-tKS~e49sr9P9yYU4e|?N?2=6myxYbTX=tZvKuGzO!_=KWS$ z$tE^gMEM2UkP1L0hqkbY@o!@#_#u@Trsjc~8>=)Li*1)GoN>XitxnqiUoYRGPeo&I zVY7}&JTy-=q^>{PGAS=$+NYoHR5B1QJ>1&g&9vMtr@+FFMn?kvJE18Jd3ALbv~KLW z&sTLKC0Nkz;@A59RM9^l;KNP6*jr+zn0{$UB`zSMzGB2{2La=#c7r=OCidLxumN(G zqfN!SaZc{zbILu@Wse+Ay}sOW^IGKab30nC&Pke<m8Dxh z&I!KY{;M?HEi#y85!@2~s)8rMa__FHnBE6I>d*y44*&-bSGI=`MT^5Ar_8k$SoQb7 zimg1!kc=--$kAWp;K?61o8a@d3aQ>luD#5xX@7%PUN(8qPHIu1IKT`31r&y$31$=aaqhv#|=6Nv~LIBtC01 z6+2~=y{VB;>_d0wyU|A$XYi!Glc2TXBntAbscBY|xpBs`W>z-wG=U)#E*V#@a?wXL z)1%}`)X#{2YzK!4X74HOFV@o919I>4Sqe|hB6$$A>uEt8b~v?$N1eD9B}mY*k6!vI zZ(AzIOj@XWKH<#i!YShz8##oXhGL`HK2zg$*-Dc0g3|@&5u%ji1Z*6Zxlg=Cad`kr| zNxN^v^0_Ti*U=~g)~R-|to~Wd&R}}+XTDI;bbJg{D^advL(VFxd>kyQHb>P2vIdxg z%~FUPm!%fL&gQ8VtI9~A$(HrH2f^jia;k9()HCI24KsJnGbpzwI9)eg(0EthVqCO3 zHrG8m2X0GBNW|bR7!`@7_ML*s7%P(epA{1_l1WN5w<=6`w-iIqprSP9J-mEY!0hNzxjN>CZ?c@Q4y$}U?ib3 zGNLRZ>oy_n<%p)quY&_EEL&%V0d-m>Ri()ULpGa-@hO}aE2R`#f)1AG(RI#evdAgD zz6!!=7Rh>wzZazUeJncVb$|UH9#OJbtc)58E7ZunMICIJ6kx0bq#>j=rEXS==tSis z0~j4T3v;cA;X#jD<3jd8Sxn}esE=2E<#s3@$3V2Sp11YipP_&-T-j}q+K}}?WEP>C zQjeec7ERXEC2sdZ^qvw=E9%WGT4dX*3ma2bNL~9FNQK8ZrZIS`tK;`she6zg;U3ZD zAD9E|DfPf6xnQaLGAU%OaNS!(UuFO(X{&FuO+|!zDJRZJYg$}cQ0ej5rz&~##BJdFQD*uzo)y~s{5*8pVW>;TU|*29$IT*CE5+>3`7i=WrrDB160Dmj#}LY zfws)7Zd4%u)rE%goYD~UPMr_!@nj{-UCYn}Q_Lz4) zb?H|nLz^F58`i`py(J~5yBeXh=TAeg>~1%Zq)fX84a~!1a+pq!hkvuiA>Gt|ZzKCA;^|_vfgM)YBaazdQl^QRuT) zxpE3UM`8QsXIfqTzxA-Mca4JW&K#{)xZdk>iHwo>)Qh^ILX7*c@_E2|5n#qkDR8PH z@xK$7SY4pC@=}sRgkdsJg?n8h^4@~}jjUE?=f4kfk_LVdIt$ZAGn0qNU+Zen;?gNA zS}nl}5uN_HwI+p-SNeB_O+}s5#})?Ltnm^bo0+|t{_$I`@v zx#l{?DrJ>lmUzwMfbXHexVqXo*UJ*@R#-Es|gw@ELooYkzC{x7}vOFZ;NTm314YPL8vrEIE9oPV8@>cD@ z=EQ})a2U0zc_@D`9}nN*^MnVU8cq*O^VUc^m$PCMt}EwKtC%*4Z;0tJy_k9`-VGM( z+`FcCH`B%4Rv6l{rK<&GgNDr^M|G<$!SBngWT&Nq@4&Y5P)=48)q?Ul`|@ZfL= zx4=^Qeu8$m-qTx;!A4%PV;IY@qSXKn=?Ax2A%tcUtLwb?q|qpJ%Ea01{UK_peK;Wy zR$XcSW6K}d$gqQ%2T1&tDs-szse@ws!kIXQ+h`qZ{l=d_O8VU)W?f-}pDVR-7S?$U zKh&x}>^7ul8Iv8%Z^VAyw9Fg+?AOW&EYZ_@y)~K)jJzI}T7{`s#Ev$V?+0hZAR1mnqk{?* zDL3ics8?@N3fF9`YA<^RF|!Ae)w5=jN2^!x9_Rs|=A9}a??`gBsiVw-FGM-fI6vEn z$Cjx1$N+1_KQof4k~qByLjMnSq2)J$Ve%w%ANqv}TI<|&? zUwW6Dy=VnJEC&OFnTXNSaMUVMed^d`R)7+lbwPDp8rc%zBbPS&6fMUkB&R<8NA#uD zA7g*U4OogHv*9u=%88j|DETmKd3R~MpDVwCgmz9+)RbP=eHkHctNz9o8Of#HF*@lCcPi-ho8 zx5eS0!ZO@M`2kN!i>w_!WJOT9TNRh>9n_vaO85G)Z&92qnR@%{DQV+g;+&1xUlXb} zPZgSHUQdCJz;n-FGsYf+~v8j9vDxh`P)!9I}(I5WTI9ju85p05 z1<1ZkkoYS-#$S?jw$EN#AZi)URgk75`6T~0)LHD3J%$&rCl;cEtD5sfvxco3*bAn( z?a;KjU8|bV9JDu^tW@Di%#F|76=?uBH1+DZd^8n;H)y%O>rXyuoN9XOeXqc2SFuQr zwONO?KB&e9mQ2sU2_9zGWC*5717FLF_^-64(vnI@&54P`> zf_unJn%^#Zd;(k%NX)Gb3a>ui!z3x!Z&DZGid*F!4;fd16cIw)2;Xk(kCO_=O3< z2+y+Sn16ba@s(NT`A&5d89N1^4&>i)Q9vDJC%#|5`nI>S z?(5Gg-okr0nuN0bcD~u!x2WFQ3dQbr=IkB)cOq``#-2I7xMVM#3`I=r=umpd9*eT& z%%EXzWCecv>YDPD_)1wo;u0Omd)JHP@q#zo2H2o1=>{}IZ*cwU<=$dNWT>BH@c!*H8e8YXx`)K#GWlcHEoP8dNA(!TOfBxS0Z+Cc?9!}?^kzYL~ ziBf^!TYSz24c`4bUIjRwCERjpY-}&~uwAFAefON;r~$)r+7PB|C0Lah=g~N*th{Zv z6IV`rJ9i3g4ZmYth~$Ro*c+ay*;MoqXr-Y8NRtcZBmDwS8$?S?i8ZElY2BdQi{ge$ z9fe)Wn^R|rq&P}gyl>HAQp^$3+gNSGY$3STh@3uZ1%~fv);fZ(QQI+VMahUsN482# zh4OqVrRkO96Ls!IgkrVv@VzhaYw49bjt1}`vP#}<5Pns&*H4FvXTP7IX72J;xX32YpV)AOt69T}1b|sW6<%C8&7dl^L9pc)v?2{>ZYn{=jxuDvS73B;pMXbvG+M}=>9Ul_)mp!hcoWz zS#?!GY>7gEkPIjoF~W+TKG*pg{^qh(GG*8}XJazyf&KT6*}ZIs&wZOcmhF8No-~X! zv9MsPCcSE)1ID3E=Eds^uFJ$RDS4OJAr^OQjmz{bBkuS_7Cb72igVw>Mk%>?)Ua6Z zb682CkB9};;r-uH@6Ox4UpEV;uYJ!V_UTYQ2Sj2)IlMMfXRwbUL3g#(^UQtu4i|-z zK>$ys_HQgUqb1vdDZG|%-9!+(D$G&IeKJaV-7KplB)e_WTh3V;FI~5cA^QsXg|Ep$A~KG5e+l19JIRXx9T45c>6kHCwzT4 zDF6ocp6JLGU6)M@8RjeU*2zd{GbXpt3ZVp>#A>)^6jU&W@ehy+t$zo3i)hBR;BN^jU;6SJGhJLr)LIQo1mn7 zH9{}hv;^-0l2(%hF3~51;7?a~j=mv62L3=s=;1!E>tlnc5~zyFGt5A^OUi;2f6am@ zUn$$!8~S9)2465xBaZNt`@7e#4BjDw}QezzEuvNM<+<) z^4@5$rV6P1{#`7;PeP8ki)Y!|9@gf={bXDsMVkOpT9*zM z6~!TpN<-e+4IZf=6E$kbgAuQfZ#AYttM8G#*k1Yc6E9O1?Tif$8ZYUqces`?dp}oZ zVddrLo?~tW%aA3qyTirR84;zKN~nsJeeC9P>i42GjQ4XQxWfG%9t6Q_JCFMY;IB(> z&*fl(5kX;|Z=3l;JS|*EjuD`>Gqk|M_vw+McI$e$qNG~*$_g_d&Zz@H5Z1 zT=UhxPIbDvjjw#49BVC`wj{edQG{kMN&(c0KPP~dp`Q`}pwxF@W}^*t$YIlWzh>Us zg1&x*05LQp>qzy`OvVY8P}ymG^Jp+8{JfrMVAN-)5d2OKgZ!%T0UivTh>g0o_pn2R z4b#g&8@YxT8EBAp;68t#`9ik_JjHT3K#y3KCft3oW47^0BKlxn)Lrb^DLI|M&vtfZ zR_K`_szg;1fsghi16wNZ#QpR{AR2$!f&UUH5HKYw3sCLg{b*4!l99n_(_CBc@S-gg zL+|9&UwePDkwcv1iik(+H{xdPVtT(#R$PR_!?5koKN01L(&MzAI~#0R=4IXH zUh{dZ|G7WODvSS;8(B;H(15Ly89waCm{0m;5fZA-8kozn{jT0*DejgqVY_}R&8wC@ zv?&>W-O84!9x2aLmjZs>vPb>s2nIOl6<3w{ofn_o`phgSw&-xZH@J>cPQI{x4Sx;B z$VPDV=Y);Y3oK@veEe}>pj27pGjo>H?H>+ckYx==Dv4yBBYfFA0jS^bAY5vz!;1%vi za(AXt;zmNv+EA%$vCHW1egJ>wl*`*vVdf7Vl4aTQD8fKk?$>O{{NG)Z_u-oF@Nzxx z*Yo3vW)S)QC9k^f5nZS`GN+s%*w!YcEUNS7t;}$RBKa=$m=*4&_oS4wpaVqL&}SSL zmi(3CH#E{y72O(hu(PQV3r~}w404{LndBwKt}Fo=g_W|>P6`NMZ-s)?6M|}Ma&e_7 zCzrU^!NR4-*c)*1jc>cw|IAD8hnAuJeaHPt(uZF7LAwaOy@4sPh%VuK1Z=8L_Ho*e z+lfm{woe6>a>jF#IfZxA1zy=HJVDm9w#wCZ+5;HJV^f`lrH!kij~>bOBmr0inMnL{ zy{8fo#bAb4$5JBDS?}x`WpvULa+nW&jwA5XA~p0CJy36Zhl*Xb#&DK60&R4_y= zgH6**e8YgUTSKUNS}`PFY%|h_-!jIKF@x3F#MI)vH4`?*cbj6V3Bd6O+==HyKWnI_ z*bVXuZ~03jB#Hrjqii?78NDlyg(K$-oUYqxjs4cTe79?s)Av$Us$VUGGD~!F$XW`R zt!Zd#X#hK1zp#!4sS~@2>P^uOz&&e1H?696cw{%jgd8&6?xZ|;o8<#M?}-+QWshfB zG_Wi=twVppesnz;nruLejZ_U(+V`(*>gIGd>2;~)hM#uM1zvkOdG_r24TFGP$m(i1 z+i@F#8<@wxYkRRUl<>Zq+Q@!i4R4xZ3^*c~bPs||b7`P@uIc%eXUw>qh;K@RRcR7N zjmHbGi=&g9p&LDV|I%k#(S$=>iKX9wqa(cvYOh~1)|^}sK(t{cU?OLhSLsAHOJgXw zx!IsQ;F8Cpwtzb9y8u=E7dYbArC~)^kGc}0$bZc(EVT2PNAH%Z?p&2?k#@0!2klL*6jP*R7 zCvI;vFO1*+_2E>6@}Bz?y_=$P*=g@8=sRa+MeN1E7KFJC_^rb)_XVjJhOHjA2YW+} z=W(4OQuDgw*i>{g`uzp>_1Sd(OYey9Xf4$irSe@k-a#C$PIfO18+Te?ThB~)IoFfp zHRTx>?J5qU*jnBzW|1FXBg2n|neOBiYRAKKjj9E|n4_>?4=<~a|2YxScfFrBReL(q z0}v{G#oLCpK7mXvLYo@F>MwKqq@VDyHm;o!OlleNF21V_dQBE<)tqZA=hEq+y56jY zBDA)bhZt`A4p&ude<-4KjPS_O(Mg^f1y)Rh*#}0np5*1Fy`T21j^RL+4D5f;8(cUid zNIuvp+sxpL-QK`#=gWIbBb(uR_uD%`=5DhnTWPYWs5EY5LEJPpM_D1p9we3kS?GAU z8&41^W7mXI7NoamoW(_b#PsU<2x*g`9!Yu?@*Yjc^!oH4|`Q-7D= zdiOk)eB8hIxnqv^Fk71YT6>sXyR$K$F&CmE(C&K26ph^INsjUqriXNwD%&tXeG!%> z>|OQaVf;3(=GNc6*IeC7d(^G(4)o+?WjVgeN`q3a8>6I~S;|{`9eFWi+ecJtl^V}6 zu42e)Y&MLAvg?yZzLc5@B{3ficA6yRzIuH&iQ+(ZbeT0vlIy{Wm3$`Lh$247CfEa< zpt{SWK9fV&wZiPegqHds6}%8C$8XS&#EVPc#O|wy!@gN*wnyDs55)Fy>WLOql((p8@j_~|rjnun6zcTVwTbWIbHiNoPwR!11`gli zHXUrFEN00Hz>I3L6f^){N5<<~+NRV-ZIkh}aq_d7W+Imx6)cDv6Ea$sG4{@@ODcWM;Hm_V>U`Y0k;ncY(Y? zfKmnPX=N`1(iegdrH_Tb>-9lRqb4;8Znz%k$P16^3UI|Jc zpa||F)z_Mzrd6B!>wWV*XKQzqs#w{AJ+Gu{`oBu#nCoLPO%F-FXhH;#w-9BfS8X{? z{|R-}4cH}&z+CkV>;@Yrl?`*4Z~zP;Lu*c_aS5XtNY6=tq@>9TvQhUsg`~i@u(Jb= zejb`@*{w9gcJFAr+1q0`rbZXL=;kt0%UyC0k(;{J+q73YfgSYz4K;%zSegvhkk-Ck zwtHA@jKfn5zUHn4Gm>q=Sf~j1$Q^6=F@ znT_+klDZ*U6)UJ+(ee)C-NT0~AjrsDCa?S^D&=UcEDv$X<0C1z%>T^la7rVgoRKR% zsN-eVK9pxb8P~_OdQ4Gy>){{CciL=^FRtaqH9nf=OMKwm8a8u~P7tm^1_#v!*{I!% z`2)eDwk2lKOp9tYKkBr-VpJJjkCb)1rVl&!LUrqovt(QzrL8{^bs;-9!GA$6l7L4T zP-Z8mT%GS>)-jd9wY><1S@L5RCOc{nRpZwHGS5^Bx1&wpIE8PZ5$!ea-T8P_X5y*1|MT6p^9I@o+~t8|Of16~+Hm|k*D zR6Dw?k`6D?jtb3N0Z&6%uj)0UG93mpu*gH1KII)<} zF{fVgz2cLeBgp3mxJ_8%c!;^@R#aABm>!QDlFoi?zF31-_!w||8}1@w?nM32;yJK* zR9-$k%s#rlewm6qos0F&U)V5qqMJvWcI5I=H&<_|(f!_WH8(n>zmFQt{Vsw`Ne65? zD^T)wF1u^XKX09a_kTk_T;A5shx&HotPMrsufL9lN;kHZHakPAl(Vt(E zIW7h^z7A>KdkHykP&2>KzNqGBTpJ@B&oINvaz$2{bGFc?Z=NdsyeVoy9D#m{P0P2Z zgjEC%JY2ygBKW5&`M#kNS z+%WvoRl;1N&<* zTKnXiD{U?P5(dalID7m1XQ6wVrF%#ucePnILMq9DW-3FCRjxn@YESPIVL6uHOw(C> zaWhGk$?j*2E)2vX$@X3E?J7o4n&{nEetL z294kn@k!`=>8@7%eG=lr(3KuW_M{}GMD_phH-o?>Kjhdp$NZzvfW9; z&2FAL=n=d7n7OQM(AFVCSRGl(U7S4XG_vc%KfNueiWbW}9kz!$+d3&f<@ssdvbXZS zZT_}b*QNK-_%at6Q2-e`OB=Qsbe*#9oVQCz30M=lXN|2du76yeho80CkI3@wvYnKR zy1XaJdqK*9O|?W%>OctFgB-E7;g~?aAB0R-%#UZeH|tEBU{G3YXs-A_m`9iM>!)Jn z(ASeku`}xO-!56*$Z=bLZbh;>{|!{Q_<&TrX}yC^K$hBXf>yE6u8dD_yjcV*ese|g zxtB8MO3GVQ04PSl5MJy{sb+arJ>ZbzKHZYste9w5Hy9GOQ)IwUaeP0$#uAg?+qkeP&1gN70%wQ4Io;Ju3kN01Mg~Ixh~&`nnjefngcow9T$$Z*G4_nenx3n- zseqMaBY2_>81kO~L%!*ahmvs$T!wz~d>dhn)!8CW(u+r6xuVw+)G0%zy7uL?yH4|$J^UVb7xlWZJK-aG#p@JqKJYc zbE4tis6CY*VF6-p3HREZ!6YVV6Wu&Vyd4zZVy7MY-YD{3%njhP;{=Od>*t&?k z-=3m|k6Tgwcb90?9i&bLus;AFliDj{?>*)}h-j1Qc+S@P(#@R|4oTKC*S`cXlU|0^ z`kPUW(~l;1;}?z9_8`BO3&8SN=65V9Rf7P`ZfPP>7T8|5++*~Nf4z$B4e|N;BdGhN z&bh2xH=Z%p2(w1dowov4Y?TyNOCHmbNnL`RGa(J2^1$qe3O9iE*F|-|fpYV`?W_W8 ztR9v!Tcb+EcDP@4pAXJ#8V?pY`O!?l5U83X?$PHkbxjRFHo*3QARV$4)iWDu?(MMR zQRp7FMl)`0r@WthOwdy~n`F6o;~>n$^*e{vQi~XtKsZ6TtAkA=lf}?mi_X5J8bLh# z-1=1#Udtkd|9;`T6aknEyqunMW3bOZ;Wyh$G}aL_T^;DmXYpdDCAc)wsaK}>c(*KS z2lt2J7j9esDi~?7XE(?+E!S6m>UH#6>^_~T>iKNEHN_!a(eZg}|# z#XL#2TAcBn-1cp;i-G%BJ)d0(ag`oa0q?yWoKRbK2lHk|W|c!)Z{*Mp6SH`N2V^8y zJ4ftTlZWbb#Nr`7smb&r=h<6+*#k*BLTPPJTsHsjL^OzQHW?T%Rf_mzjC3ud|Ni?l zQR{s#QQi0C*3FmZBYyX+O23Cnl=@raV@npwGD7x>vG@H$YS|&E37a{5Y^!B@`9&SH zq*CxA&@#uB-NS;*qbR|_jbCj;ecB%xBK5aFJ2%8D^zE-MD;Ib)4W-D=G$weg-5MBt zD8T&4$NQnt-EiH@wSfsO1rzRjZ) zVtwyaT~Zd8!J!b)p{wXIO867G0jtTscc%0E(=MHVQbpc(Hrt+kc2?BN!`5Q7M73iO zFF0h8NVFPiJe6v+n3oa$XKrY$a8vtmqm*;$UD=yizNer1%(-3A-eJY4EE7Uowihj*8rxj?s`!y}~m$Phw!4Oy;sZoC$lrR`YDwGQ98MZuE7>)-uNvyhQpZA*gM7HXgbi|@m4}^^ZpqU~ z%ai@ZxPL`ftxS-^KJ|T?5f&{Az331tAQX=duWlbM=M!~9XJ>|wwT=StA!~*#EA(JW zZ5iHBN3C#qi1nx$-{E9uHN;(T^NX(W$10>i%d23` zD8Ld0)P8-DqA&{PrzK3vzjJ({-}L*rm4fTTI>3bH>rG;Nfs2BzAe!&n#t(osL^>Xw zw>y}x76@dHm3z5W4a=h@fe+W&lZ?u_`Ke~c(qIytG))2YTHGe_+$r*M@lc%dqnyr}h|J@aSJ z)M#mZ$|H1R22h%762sd>>LL?^H>msX1pkWF16E)>Z}jteO%n-#QR@{;vDRSiV5>U0 zZ~sfV9Ot@O6S}I3J6h(_yRy(ZGLIF{LNOIJR$j?dS>dx2Ork{1?D4P>l~cvvPe zlX89?FRe_-cL&wg?Hno9ZY0m9OZYzyeZQJPXE`gj{;%SHVFB0Bn&%_DlUcJTBWiO- z?e%7O*%vmgd|Rk8o#2E>K4yB|9)*EhTkWYP=@;(hvI%(!eI!2NOWig?EN`|fmDR^UK6_L3afI%;jT+#Wu= zLOA#;q9&>RMFY+7G1uQ_GIzERKN=&;n;4I;$ReE;>RuuFvCw9_X|bT5x^`{r%^v_^ z7SWhr^V|2I228?-R^nXAOmXh`i`79>Tgjq<<|?#TIdJa#|3GT)U^%cwsjK=;oNF;0 zKyZxQu7zKvXpZvV^TgIc%S-IO23*_y_eOJmh-7iAEYR`GorH^bAY)ooiy znD}`9-8c3Cd-Kf}1mpA$?}Iecl*+K0%lWj-aXUsHhc6G+#pv+nlxZrtWdl43^DaB) zy)W}h?~7Xu=)qmRb`w#F+Q>Ae^JzHNgy)TK^;gOgzIO*Jz5H+n6~-<39-#7=>dEKx zfX`ejAWi|j(E2^}Rk$^|ZUV3n6yhGZg6dAsji3--gwhUvtF z_}x{d%2~?CwGg|^psZT~W+^wL6+Zs(Hfe}m8ty-swqYJ`g2R+4FxD+>CK%h5BeiYq^TT|LqSYV z1t)v>9eEtNdk2|KWr0&D^(FO=!GEDIt1}iiu9t>zA|*XDCY6_c@>B;)>r+BA0b&(r zv$QoTN}fc1bbT~s7?&a|4I%*3f}wBiBd~uA4!13)gFJLHY!ad-vxx~{&Xob06{)v^ zZ*{C2(X+f@duIfftNDI}(e196_C@&To6$t1pFej?sF16&SUc;xhApt6>8St_A~7V; zSk{yooXn)6Iq;VWk3i##Z!>uj7JQO~En zNYzZ^fw~w;P=B`t7z?XnxEhT5*p?-H^CDuI%mD2DTEDqu3Veb*UUjRg@x%~Q~9F)}OVdIO9moM10TlQHws`)T1>87` z*62Fh*3PnTDv{61YD^I$1c7oItpsSG;@k4g>&=Pw^-<;sVL2^e3y){H9lcnho@KLu zJG7m$hYTfJgl0hM8jkdc2&~GtC0+~r$C4F9A`cg3sv=hC>YOV}u6=&5jQYjW-bzLN zmIj`$H)NHTj`F8z)@K9vnA1Cg;Kb64z$J$j&0(Xx!DGe}AuEOHmFDEA6#iDDnZ-CnTFs_F>17 zH4_yu(N;OR2wYrIA1!GbjQ#G7+w2J*0CK{$oxx=QVw{lF^tWS{t~z0oTHajckn-P&D~;6t%lnqF z;4FPbrxr6}WW8AuD=}UhJnC1#KL?GUnzMOf#5HGP^nG}K&i34SzH`4WI_n)@WVd)%q#a{4147^*U!5ftrd$M2F5ydY)%IeyEeJjT{t&b zB8P+ZGrRXZVVw^y1NX)j@IfW3d#UmH@|^F!+=JkFewcnQSpbM$PcviSftRP#6X1k3Y4_1ygJg$J;@(UZ z7-4H`i)&sgTJwf()q+#z+rts>^*dv(xE36lGkXe<*{BHOlTU`aZwk`zxn`|*kpp3d zy?&#e&)12`4_q%XawLXl3mZwqISODk9Yy2KFf%p)mMNL?h4$ijNo?Bg)|M~cmV;&25IXDZNDnFn$Q5o#C zVK=&0O09&W3~Xs?W;7)N)tr!_QoJ5>{3Vop88Aok_``XwU_8W?pAPc zN-90c{jcW?A-3lioXIG}8TpSCmUk;H$^s_)x~PcA$R(KWBZ4qQ*#J%hlZUH(6n8ic zv0Y!|@a+0=akzbFydSElO<~n_sKo0dDOJ_#8M5SqU}fQ@IrAFQz_rYGIauyy$k4rB zda(#zAWRa`AkQ3KqV8w37&Z61NuM`W$<+hh7FhNJCn5UK!te2y(xRbq>DH<0gZ`8` z(BfLw!>Xw<7VK+oCK5{>7 z29g1Q&qHs$x#JAt%jtUclvE}SJ&_&W7|^6vQa)V{upm5C4#pV^4rdRebh{^!@>Ij- zoXUj{H$vOmVCd@n$B~+l7LT`O$0QhVQ0`|3S8m&Fsp(&48-@~c*+cj|)<`ys~D*pC^_2RaQ>+&%F_qdMnQFFmSo50_QMl z(=g1f7;$XWxqTw}*6d;A|H#b8{G5jkzmC$$pQea08D?KUj=_#0 z70t&QQ_c}~>7Q|e^pUAapP~>J6s22b>gnbw99iL|nI918=ANZY`d`1!7v5(y1Fsy9!$`3}>tdW9JQ=)a1y zb)Xek-{=bN=xcP|)T6q_2F%Q+W+vC%jC^oou0-0@%XkmH4)c2_*{vPCro7($@fPy^ z?Q|{A1k(6H(DARtx;mx>0#z4zGYV>43IumRQWbpUBb3T&Ygt&cBgeHt37w#@k8{H^d}t;?o4VXj(RHLVVUa$Asb*l(#$YWW1r# z0tXUVs?U$N^Yh};G%DeFmhpBlhlalo(`a<{45?e8PbBY0X!?qOREp4cc8{F>^mlUo zFV2;~bV&Pmunv7RNA}O2Ju+;HnH{U8EA`yA`B@-C&y&6k%MJI>tlp;xzUxe!9~{1O z5Xp93r7~CGrlL<&nug0izpw`e&akhjhg+Af*fT6JSwbOhYgR`ch*fx1Nx>h=_FIp#Jb?%ZG&3 zog;gjr%xLtbux>tqFP+aN|hore9(lz=JHq8)(s32!aFtB09QJ7@l8AqQ9{J>G~D*C z7aO_YoECeNTdkm5vL+0)Gc${pQ&RVYV8kHBGo?n`5GjIywtQROVy}A1D!T$ChjVln zoGyBZUnL_-wnH)o0nCv>7L9@4W>ngKm#obE{#C?6;i1Fe_AV&|t119%O5o$1qpU?@>R(E*;FPKk(OB+KHX`N3|vCfx#uW3L2+gx z7%Xs&x={0N`c?S-jC9oM_F63r-Vdc!KK$%Kv09M8S{Ro1^~LYnd}?5PH|oo1+UyZe zUVYOOTzi;hZ_I26vYB5r2`@`Y z3rv62k%`pFojAzw3fXhSdG#6sVk_%Ns!O^l zUG~}^P>_6q6OR-7oPIWPWaIQQq`~%S&bQ}XuI{sC>l=72l&n?i{VamEWbcY(qy|W3 z4Al}ow2iPT{M`$zQr0mYGt@Y!qLEQ!&E@t4XSv<4&uj=;doWU66Z`{@)~@Mig}SL0 zB&IL@gJOwM|Eh*wSw-U`$b4nR73WyUJgoRY&N3ah?2?^q?DdLO*$=%n(hc%BEJk?b zPJqfM>+N>{+jZGf6xl9>nyS46?Eb^>^$yKkvF^e9+vNNMK6ctVcm3cs#(49cueh~;2+t3tG9lpIj40JxZ=vg%<+8?E5jCc8wG8CTs$Qf5 zV=w4n{O`)_?C@g6#EQ>fQ@A5OYi<;iJAzLWQT2J=4*ca|1-7kzc+wzD^M%%!J*4Qw zpWkKyR_$F?QJX1PnByOdVb114pG|TSSo7JWVXZim>tDg_x1Rym0pblY?rs)3MqS82 zIz2&C!C#k1)Nt%MdxO@q z=wB{XD9zyAdgdbf+(JXIw%W?=u+7fnK%)15CnlQ9z0};hN}t5MI9~gBUCCYd4d2PP zI@g1p08q}Y`|3jDZm%5a^HIVA+}05#-BP|2KDH7%aY?Ufa?RASa0g0l9sy2-IIP!> zU+8`r@0=ehw*`+e?AGAbOGJN(+@IL<_3!Y?B5P~QB@8A8(+)z0*BAu!TCGojXJ&>) zu)p6h$Ms^chbF&LJYMo|M=04^H3c7NCI4YHi1&bwp2U27qX2i%U}`qLI3ZCt)C7@h z?!3S22QZYM2nd8yHY1hii$Ho?CArc)Y$5xG(!iGflG3IBPIy<5KZg0fc;KG0I-b^) zWT)_JuJ31BDYEGOBc+gPp_8WPrt#n=m_;0Qm=fzLf z;b1wVHGbZjwW&6h-KNV&y32!PT(S{?j#Xn&O#AHvp$-u=O?Vos>%N!}a+Z`QP8Q;} zk=0Jpe!cmy^;A`(jS%m)0!bT;hyP%_1^st|kHOv<+zW0_)DX!jW)D{*clG~?}WiZgtQ+*g8$UbZ@Z=e9#w3~&IOK8CD8Tprx;eA zYEOI@R6z*3TCG;w3W7vy4wU61{kSnXri0$rTje@+ELU8#ZF6ml| z-lJ{Jcbc-6Ak`c&jql%=o8yx(@93*$R9iFeh+=JH$g_kpVnfhNXaQ6ZcOInntBg3e zNbOj1pZ77%8Z0iYL^Y7=&2$%PL8_7ZldPpzAvum`L*29`(g%WmT+WwCyydc3a{}4- z_xNkwV?pV$#jNs6|yPI zUinq4dh_TB#rN?FuYXxfJ^rH`pSJn4;6z@uJlDIVi(Hw*TAK}2O;?m|bMDT-YTeW% zQ#`>sH(#ENg%G{}(EmG8nQf~g?DhrP$5(ho0DGtGleKgD=Zg>|tN6~Upv{IYb*rwb zd$gZ^Y+!1iyPQZwZsOgE%~Sqx7NqQt&HujlEZsCJ(Tto;>s-Ig^(pq0v5-A0%ISM9 z3tM?3anC9Y>UOkRMMqB+{m$FfaW<1NAXHK3dl~g*bAs_#-o6`1`7z5iw?|9HE1Dqr?rCO33msqY!zx3F@d1;Gxot2=RR(q1w084xc<4I!@~IbBn8 zKdA4}Tb5*jxq`E>xPf5JJ^9J!6nk*tY{KltDDcy&))Fg@#>c;85DZr@L(l}8>grAY zknZ7a`z!}=sPY4S(XNO}4g~B1az@JPa_;Lym8?e{_pIV3()JpzybApl(AA_GJup}* ztBs7bLZX!NJ*T`(nJhbOUdkWnI=W@!kZga zlS*x#pIxt(=4^KN#utxc#XwMN^X|Sw`&S7QnlH&P@IXh0^a3il1?xdmnpokJGChy^ zma<4xjZXTw7e){(Gx$NrzRIPXywm;WA=B$gnUTSNCnzIL_04(qShIYi75V|ORQ5rP zZV-96scMyF2F05e2Smx|Lu&@-vY%$t(~6#p6qXFz|CsXs-h1k#l*QFfjPW($ckgR8 z)AD9E4y+v$JDl$JTjvQAY_`ak>9XW*se`Z<%tZ%t!`_ObRTryjv{y?dW48tj%n{d> zmVH^63HPTmAY|6C_*_+73n{<7|73*aK!jn*l13aTc9BaQEI@w~$biks>`3Re^@q83 zl$!><ebwD4{cpN_3eIAY~7_gewB*#A30{1GTo}KJe8q*uqs1U?T;k8 z5RlFTMSB9E20l*^ht-S7+iNL(g(eri?xM|Rye&7ydFe1F7Sb~<_Wsg*UD zFV=*jd9(d9p5=f6O5Vm5kYUnZ^Mid>VdznZ(J$~wAKo1gaJxnS>NBIyV|&so9+}@R ztl!;_K=^4Vn7A|;akC;i_4r->yiF{hZx}ZcQ=5PHzvpt6oFx72#;j=Cf%y#(vg1Ll zQkM+JS3k9$NsORW$TF<__;QVY9v?yqdR?&9?O;Gd-EJmUBLN`ZqoCf;WB9yTbiFd) zW#bFJ#_RLS)pDe-bb<}P>T?feA0FGH2hQ#NyS6sH68DXsBIdyTHB->TJmF-~uz_Sr z2f3ik^q9;~MToxLK_J`!5n$UG=^!rOUG_pcwMRv-w)DZovmib}34G6@#z6spUOG_2 zXTo|vwrzdW{AxGe(?DbR4mHs%UUHE1A;JKQ0l1a{Nh@s1r z1+)Xe<&I0BL{Mhc`B-)YF*uc`*;AjAP46qH82j>b#h|+_@AQTeZu;v-GT{NNWvEiw z*_1<*t~Z@9n7NFYW~i~W@6};6HsH@qc{}hIR?|Mr*}n9x{^C^KP?o++Dr2<2P{NCv z@mACCL2=zUt@@i^mdgCaV$YFkg65NY*0(omtfqCI#i(T@SM{q)EJ8A@OVv4{!wJT$ z-YTnY50ljrx2pa@eXNO(LlaI@dv8$WQ#R%)nYkE(JonKjLF_`7P8`AdM0j%HzK zQWXQgW*f;7L3UOTi0dE5=hX1l^`zvddUq{9mV(eGx`zJhX9`)#_2}bOR_m);Y^yzZ zOwTZE<9w&6He)B*5Tj`P#o$~+oMqk76T@E2lRfb#UWLt;o*J+S(>wkRu|lR*_U4xt ztOLvD#w~Ud_&(u)Hdk&*9mo-o)@0x=(Uc?oWl$*q5%q@dcfMq{QLJNhE3`n!kkRpC z0_e|1XT+(3=zCb2KG}O$MLqxRRBcqQk0L($uh{<9KO0wsKMrP?53eaIIn{8=WF`Ve zgtu{x_@up+`0vEV%4vn?MNbOkJF2GpuR8sz0qRa1gKiS((8c?b*!YAsDgAfe@j^ z9={)xuO8vx1LuaNi1GOj{RXMuz0m0iKJ1rZ&ZQ4bbh4;pw{Jh5&Sqz?1bC&v5xPH} z&%lOp9_a6!c6uA;jL~R+fu`*-U&sj>M|8Pa!5NWxsv!CVPyKzYtZ+=;LryP=N zyxPWO=1k~V1ZTd;ful#&D#nh)0t=$8CWX3CTK&0$_zPM%yh8R~nI^4raCqtoQgAgw z-7PVoU}}l7op{>Vz36v+H!(8LzOk;^L`mt~X`e0i%O4664n{6kSC`hF6YV0^m*>s7 z!ycM-8Pyi!yb*@l9cGl2mEC|^?-7IWN}cF(!^dL-II~I*V6isgmSj(X>_QP^qABvF zd9g$2FULCQNW|rtc+9@oH%bpi*tH43d-S-kF7J49vffwK2Em_bd91$ByU(c#Bd(kED%|roqejoCcPqDb#+(@nq?Sv| z+b@-wQ8kzhWJSbIgo_t_BRidFw>l8DSl^22nlj_ZAQvSJgfKG?`MxOgCX1`p+7}O& zSN;*XN00w!Mw@Sh_bFFR@(u&4*g2FVg201xfZm9e%5I0mB3R%sTEtB>NgKF;g@7d*JJ#7GVB_)tb^r-w?6IO9Jhh)tlPgzwQxRHVBNzMRyj@fBBy*A_3 z)g?ou569@}y7D<;%Cez$$fxpemwx)!dBmVCc{!wpA4?}`@(`+tnMj_A`^V+}%|8Yb zpjG{*T#u)+f_N(l{EfdPOE~kMzTRCatUB=7Tr@APJi*g0URN14!I4)Rc6X2LdF#z) zMgDh!S#n=;sfJt6Cv>pwe45TdnmLDj>gh4*sF={1e8%KfOCU_c%14LS69kdutz>xK zFLiY-8#Q~mJ+XM`=>#sevP!qMtjlj2(f=hNP!*&cJQ_@irdduu_`(THm~%>)TZ&5~ znUr@i=!kU`!=I&}+}ma9=7Gc|EFNl0y*q0f8u8x=?eAu@?cngkEBTV`zdQ%=!rY|- znsSV!^k<1M!>)5-t&HfXn5I_k5EY{HR)GoN*yzc z-qPN$9qZ3(2g&gkh=|^14ZclPR#u-GoCB=zPh=w%gPZ4PcWRrwr3jvzRSvx6klngu zV?pUAFsOGESIJQcN*%MZM&Ih`f1WTdvFqO*A)CqBQz9_Ib%&>6PxzVLPix0_)4LSX zFiLmfx`7@qVOMI&zHVU-0=%Wu<-JzX+F=DikSLwL_#Iq}UMsV%!=yB^ocux)VvSPa=jGl+(X37oj#`h(|{QNS*j@doK!)T`G2)SaL{Rh2Yw2qq5gRNlS{!tyt8iJW6 zF0OH~on1KnmF$TX3S-4J&D-tc$fNoYVf_KjCU{_6{ksqP)zyAgC7A+ijAeAe4%>LI zTtF_PjZua+zGyfcHQef#u$z)oq`^qcEa2;ls>S%7vtBsnY_uP#Pt{n7BN~S7;V>;) zV@ry7)JEFj89Kc`Leoyq~RhiIoLw4uv_|nRY{9Pl*=+ z34E?+)_)udA5~hF(v!3IB()hKsj9Va5PyJ2s3wWYxXke99#)jC}QvDg1BO|!Levd;3)7l!Z`z?qrSIyqQBYwvsY_o2yqunGG= z7ArC4<8=RMoWRXZBROyTW0urjUFuq`d&~aKwmcC=kiF<$`|TT~uE1lEO)tAxAof^o z9}T;${=6m5d0_lM_3O47(ideI>g)Oj&`uPZq8`N z`MB4-H4_zIZ_f2AwGFKvpW+=2%oda~38XooB&pQqP4NUxaziu82#!xt39X?E9rxYu zD#~WtU#2#Ac-bQn7XQ80>i1&6Su{ z_*=^~`$)RZ=Wy$)}zyV7+p~>mV*@xknNpTMh`52BJY3ic+zW~s_smh6)1{UEuhE{T>9K~UeoLSHd+>1O+} zz`cTh@o}JB1^qC9G}z*JF$8vGMBvmwmNzsweE024DnFU940Vv%xU%J?G3C;DBwFi6 zd5@Ug#F(&CQ=)rzJv7|HoK{?tV5ggHpD76Wg%oEB1O0&B*QFmA4i`Jpq`4X z^$Rt_E2H32Qu#`FywJvPM4FAIYBp!cUIcbKZP|zhD=YePrUC;DL0;JXAI0 zeJ%X`A|c`47&ZLHt7Y!+}2hi zQeb$?x*#Gl!cN=D=~8AP{(`|~Et2fA$DYv0?dM+q+Lq7bxo-B^gLC@dbeT}pt+-MW zSLr4E7ftZMukf_KNMAhEO}zE!$@7?>Z~XaeD01Ka)%;>P-6s<*=d2=_({khAT-yr0 zyUCc_+-CYOa6OA<$z5Z%#z7PQM^?2-Qa7@%#eexFAQx@#F&JZFwkhB6xs{)anzW9* zm2zXIzRf4$)zxj5BhNnLf1358f>f4Bl8pkje|-|Z`u-8vZ8lGiy?q0G5105R+4DvY`B~82u!p#{08YDC288|C@o$qMks_UWt!N#wZUoyL-pB^}Pn)+wE z1*YpKEO7El7Ip{=p5MI(9-b_LS?bmj&wkI>Q;lD@GUy){^g7E;4DrRO+O zeZt_@+1AJ{zrQ^Dy&IyPlS_{`q{=2WzMpMM>+t&g(9gBZW};1mBc#QLzBN5EAoyGX z(J$-Nu*6AG@Xz7X-vTNoljTSOvf!^?9uQotjlvTiA~^H6tyXh2JAqb5bo*FomN679 z9ap3v86-pZf14Z%@q%6iSM~B{*V0&Kl4X=sIBvhqs0FEAwHDMx@MUv<_wN3sT(VTI->o;E#EH;1T&l7Ka29H+dOXzYAChYwPdp@I_oI;F+xuNt<$Ky)gks#S zX;=7#>-a15(V`K+<3Th*Gu+Qieldf#jf77IHeiy(3cwhtZ0~#$=GKv>_Txg1p3Y&5 z#;}G@AeB#U2rM6${FYTJD*p3yKSXjeyJSkO&UZh>{EEHete-?D3pl8LfqWCiN{mME zAO?y)t_~E@O-@u@yV3gbFGO0-rlQU!NQiR^pK?eouE@5)EBnZ$hkJ!e{c^#!hku9? z4=OWKSM7-?9sOL0m$Pi2%-K%NYA{&H8v*3 z%^tMVE|?>l`5SxNHT~iJuE8T4Wq{ML4-*rL=#gg&AV+4k4ay~SIUtI`q@!PoIGY(t z_xyr#U)0vucIIE6%o)Whqw*l&N~xR;%U1s`BVNRGxz^%d5{|F;R!|6|@2j)(hR?6K zI37k@l=}KO(&;hLi8=!&NQ z1;+QH&JUvrILt49VZo;&G9DY+QPN91Ui*kq+@%lU3AKApGJTfdq^gS=MWSaj*y=ud zqAFG$C|B8;sJa5{R3!q%e^SMa%v(4@SSX+)H2&V*cr3xa@7S)=&y7Kx`kh4@Xl1k) z)OD+vmYXPc7vD>MH0}Jae>EnpJ8bjoaOaV$;S5wSQ~b=*W@VWFQeY$G!J4kT!vbEs z5+iW>UxLo&*XNC%TBIWD>>MljTrdsaJN!rA`brnq7aSH5Ou zP!yz~d813=eI;@4(S40v-m4O_{+-&^S}v#07^Iiy`5wcb{T(e|jwD~S#+&CwEl&X& z>6~2Jp`wmw(Wv?2Sc)(K@qD3;sok2h^oTo?us%#QYv`hWEgvqa)7PCj|J7wf?1x8r znf=s4+HcB}j31MZ|J21<5jnt01{Rp0;^7i=*N_5#9pwJ%MmA5)#M9&*+xqLMu*YZL zby^25w&t2_g06#`D$z5I8Swg^odL;S=k{)*aSR?-PmCjlCghM`kYluspBxv~?&Xfe zYps=$8cSjGu#r2BHS23}YPS5ka4%D@14)EZ$&rkS+kLNkdXT;m#MSi-iKtTxi&5Zi zivRhULZ@9ZxSgd!^!AGw4VlZoACdvuYax(yGUqQv5Ub!fz=~Uq9z|8Hjg}-NfyT0* zJXGCox%MZr62U|v3@#24G><>oF79RtynE?BoT&SE)pm7GA~IWg`N;WSsyyae>_mUU z|D3lj7-@xD6o7fMgPh_zI)LMS4fj~YlX^YKBy;On@$(i&XaysF%gwme z(Sg_RMpg_&7Cy56f(!6*0rS89C+&qz{WtVadR+iWebMxCd=f&&0mS9cfF|eec$(X& z)thNle*6s%L!tRMu3HkM1zT^V5P_R>kuXN>$cnrTzW8_6Zb85mVa4K&MNLq3Yu%xa zr#XuI?1q~n?TEvAZUkK6<6*69@e+C z_=JR+o&*--<4dnO^M%pXwS^NV|1KfRS3j+xjwqsWPU~NH|Bd01Z;6{!OV~9HlD&zj zwrw53rZhV>rKx*Knz*1XdlFCp-f&?~NeU*qF~U-`*-yn~WXO4tNSTTms+zop zYIL03xhHt|J^}ZZEU4Wp%>*CO1bOe1ooOpn`A|)=A)M}MP876Z)3x zrvJG6HRp2cug*g%LHf16t)ms6t=j00o0}FzT3z%al6IIJDLtLcmi0>d;+8odw`+M= zDI8q{!o5_qc{tPrro`BoZQ?_U%rqECPsD#GTEEp%24Ae>zQ#6b{@mUw8U`P=9U8wQ zQ5%H&Ol|KlcCJb7PI_g#!(UGHs~&8AR{7rxH=cUxV}1NtOD^nl-~CHDqjN<%1G%1F zP~r3{;UMYIBTDgj;2W69mi+VCn%j0ZS^LZ;v`lAmI0xoyD}CP9bWH0}v86Y+pZ6b1 z`PNnP#>C&`C}k4d{MrJOu-q#%^zzT`TkOT&27TH}!|19y93|B8MZgz?=~fHSZa_%p zup^%ugv9<&T@r`}?kLI+<8_8*%~6AxgiD7?uFAT)AHOQ&8*{?{o~m#{HFC#7=4tuI0%mqI|Ba643aTK#)9kMHBgs!=PH`%m{Y`ehkyGwa_=qmw~C(P@?1Iku5HtTg6P>AGvJJT=xFboO-O=zWbKC-HFOn`QdDw@?qt{m-|8$p>c>>D8h6NI28kcOZJ$PEiotCev%r zh;fQl@M+TV#z$)WHMtw+G`tK`2`KKpAJ*?g z0-R;anrrr2TOIY7_WL#%yzH2EkH%6#m6wG?%(S}xs1Rg~vJ1353(hSD(6oorX7K-3 ziF(>7fKCf64}+fCL9`*}D;5zokQcd`c$bmXa=pquc+=qQrZSFF$K89^wh~E~I1dlcft^gPFnGi>$LbFk-V9y^#*qlQUorV2dzWXGYcoK4b;%OxvSN|# z#+PEQ2;Apc=5cU>i;jo}4&u*;^+5=|NHAbId(C|SQBmsT{1A9A7a#e&AVhwJywV-% z{Rh45p&74uCzSBG@Xk%K#FaV3rBstl&-kvYSIfrP+nVmn78 zvTJHf=hFxYN$}6CSZ1sc*5qOKe2~)8$wBg{nyUn6>Jp}((IsoYxnzd>z14SpKlr%t z#=q&FSrdeDOQu*?ZBIg9rxy6prg+oa2!rRCiPLx8y~l$cW7@4iITF*#^+hBkjtT3c z+Lwpx9+4jvS1cLxanQ8tmq0v8b?HqxmCe0*1jPx0$d-?vK>GVGr<<=TGWzm7iU7`T zn{OU1C}gzVbp3u>=mAwg@ZD=m5viNk;(&6w#;5 zwuxh%htx68j3bEmDrd|L^qD%%zvUGBe~QjKn(h7n@s<0=l}O|mB^tTGV$y5q>=^c2+F<5>LwSaD`QD(Z1f6tU~)~h zx_Z~dG@}^-KimfYT)Umt-Qgk{abrj0x>a53T!8zlFrOQFzagrNJn_3<{N`8LqN%Zf zd-`ip(L2-efj_3tN3bU;bSR3n?>JslPpHM4Toz8Mt*ac#CuNWrzmP}kt&~^6S`jcE zOd6ExD9~`GjYJh{yl^J{gcN!E{!ydEU7GjR`i*sitE~m?7j2rbt5~dB**^S;G>6)0 zA@2}t+@y!IYWC_?c8*Z^Xj@9Ivn+lCd_B#yOAAzJGw1L0k@t0rUTm++KaVQJ-tc-rik$Tgo6?Wzy|>zGVo7U|Juv zX}_4;QhB|E0r$)lGr_V@4O{=6@`Lnn*Jo93o+~I$PugJd0=|*2B^1QoYUvb4X`nU! z@n`AKQQ7;>c_HILQ5ykySO2L2Tj#qILo#t*O-;@^sw{eAsU3tUA}nYuT58{rpdT`C)( z^7B{241zPQvarUZW?HJKf`Zm&w>V6E>WGFKdY>4#j>E9_Ua_O<8)L-|Ddhf_|DE!x zLmC80{hi=Mz!-S?NoB9`CW={Y4Y`P4GFFP}D5|z88(z^v`i&fE$#1|bms!n)n@h!U z?E_XUa4#qcEUpk@i>{ZeW|F8QV9|_WmESI)q9rD(dg$AU-Rqq(PnY(Gxx}=k>NdwV zM+iCF%9kMlT9nD=OU63vD$=Sh_C$T?>gn`f3b$qS?nP%EIq&@Z<)sxNA2gMRf3gcy zTO6s`8BR9dWZ>Ko0kAOnyedaCT5S|*Xm$Os-Y*BQz;&kCYL_0DHr6ZfzVM>nTB91O zcF}XolPqw?qmXAQPL8Zy#|V;x+)f=QwED7;e(x14JUOKXg%90tI$$#vGXqOEJ*Ken z`@lNnUAbC7cVPC$wZB(Y+kWV4Amnx44!n(gI_7T18|P2}!9_Vcm@P@`OD#po6PfF* zS9yMx)ZZvUKxj_S;*@g1F?*9fB!{o4gOca~ayRTIRuaX>B9B+2&wECPck{*Gl`piD zI<=53RCPF_4%E<7b(B%7*^eYqc7tJ&3?xH&R)wn3Q>CINqN90i3SP|HsXMWJOm_Wo zCbr~UI`9#c3*aVH{_!vx%1NC}(zWIm)y(>*L7-h}$0T7H{MHVlyX>-Eu46fBo{W%o zt)d)w^!^M5kEB@Uqi+c}yE92Wi}m3Vt&(2%R&z%aB&B%@S$g`sQfX%z6Y{WX4d4u^ z6;-vhtpy%WERR>J>t393Pldh&>Lh!(zJ{R>Ay!%_F?J4u*|IN+VK=i{+2iBK_@$5Z zh#Et&Ic*DKZ>QRa*KS`QIr3n`jPA$8OL7-w8hqWKZf>m}2ojaug7t<=)=IXvbF(q0CRICyh{z znFCp^()M=??d@}%3I+x~8MkQ|W@>*Zy3(QOj-eQu0$?+uK(cGrXh=jAOQw8oV(UEU zitaK5WP>os+kowFAFccLl-pCKP+Sd$mdf4&b?LK!_1e7Ge1i-6^SaM|(^1d4O4RZ+ ztvvJIz(RarvPB*EQPW^(St8R`nCNkyf39SH4jJNN>)`lg_h6je5iHB_r|P02M4U9W zh-qaOck>7DWGmyfy>)b7sVZ`pG9J8;X@F^~Mf0M#AnmW_8(Z8}#{9MOY1g55iB~L)gG4 zp?&`E?JHv(U&g(3fl2#bZM07xXL9t_rxpn4sAs2jMgrB2UwJpOmg_yOEvH4 zCI{nW#GIG&uUATc%{dRyDkssR)thEd!%|KN+mFwWMRs_nwygYZ5x;=XYOBO*MQkyo zWhi_{EE)^S@r3R65@x?~1k6C;#L9Y8#(orTp+vf~%xS!F{0{0tIVz{R?-TV7+fpM& z%Qt=Yd1Q4*X|d}Aw{DRGB<=IVe%Zr zNfgHwvyNlT7j%|x#c9TWpjAKB4Nec~e)pgcDVAV7@cDAjbpy^zoUlA|2QMi-iqcrF zE@9V`j^xyKS8LM3o|OZSTz&I%TV*JDEd@cNW#r|N-*b0fBvX%WUJtqcx5?c!XIkGT zwa?R(V>0J-O6bFNPr3Irei^6CO7{Xsy%b3xmA$XCq6QPMGfN1DC`^x#@s6I03@WyxXcQ9f?M-U)cGUFhhE{~$O7O47G|cUg|S&Q)Vo0#HJp z>vG3U8D5U994yWCG2QC?r4phpSdmLj%!E)Jy1+Y|7TzEk$DMAGQV=##LHfAO(C z-J+BJmH3tkj~)yhii6dJ0J`IxqUP+4FS^50dUwkdF2}WrYg&zxad>a`ihaOCiV4Rv z@cTQ>jCV@&zq}&Gqo_V15qRajEPgg1=2|-Nk~vH{nLqVx6QuBwf=~i~jAVrO740vh zqVB2RFFGf%7OLAXO{ChX%eW{d?Y(o^1gdydQt%^H%AUW=37G;0bl`3hg-OgC#c!?n zP;xZ;MBA5C(_K6I1*jMQ&=Cm5oJb;4GUt+?;hnx1MQ_;GJ;l4hJ6$jH`G2l&W(}gc zp;i7AcQz$Tn4Xz-`$0y~vVT<-?wGcPC_6l!1Wz%cAY#YTwagx61Oh5{J>?sg@)W9l zc|PoO;$MbG~6RdRgyQdPZo5gkk8EI}Wr*k41%p z-q4Bpcv#3p<)~rhj#Hh3y1H4BSdmsn(7km!Y-!X)-WC?gteYo`m*D}`uMm_N_a`UN z;_EbE^Kn1_=P(7{+FE51V94Y85T-58{qI!x{=}biRfhZo0;42s%C51wF;OD+8bOZ`S>d5>?Ey(_uaSZ3V?Z>9MxIrF40Yh3bzGdZO0#7bp;U*((p z52EcmLzo^56K~|*Do8e7=F+o|ZAffKw2P#4PFpWMUt!Egenx5N z6J;JIU6GqGm^uN9-fa5x!H9vf+2l&i>UUXEiBR(jf)zPiZ@9^Mw=@w`Zk>iHihld> zFG*NA$Ww_b*P8q54BEgEYE*5Jwkqf5&`Co%{0bYYtj5XR%yo1`Ik1B2MgnA<214zg zRZsdURTBtAQzeUG;3(x)!UV?~lkHzIC}TctAH*G) z%xA2P8~ofRTKo9aLHd_;le2&i&+k(ljcdQ!zWs#N%g=Qcpk? zmE93i8ZWX~n$BX!Ep!?n>oB;OPvza)nfUcv%O{x--});V5AUP0raxt5EI{L&kj7?s-T%{poAy{MK>0kn^h0Ycq|Z4#39p&o)gtHVD^O#yQS5jlkNM z{dX#dRh2vSm)y;&0Cum{ACc&bI*h&160c#pyic4wnz2LW#_|Q2DAqNmX_Y(97G|&P76>~GMNYk`O$}o|`j%V`jt35I1X%wr(D3htDd$>H zcIo(oMXQ#aoqs)>kxHMty6j$lqC->+3sy)J>&jsnTmtNZK`Ik$ZWSOG^ z^x^q}rW!RHqbxF;nFK0#9)8vg7Vpg`lKR&|PU<^PPc*u`Gp8vOM|_aqsQ&guiGu6v z`aWQu!(YPtSvl`jkPNSAHt*%Opj4ZxtN-Z9SH)N&3JlCv*sG1!r~+COokHPo4RV_f zrAnFh+e?pAt%$8nbmCfBuFFJE{cV2Q?zyCy(T;FZLEyA=e<50Iug|eRs8B{dAx0rz zJ6_yR1eUQOJ;rPzLe1_6VGOh66`w)V$t(?p`6Ud2?if*7sEOgy?XuGpuwKVlsDaej z^1x+$Wz*17N%WpTcY7<3Q36RD+WdNS~ z;~|fJ;CG0^EgWG*H0)x4@^|M)gB}aMu2WYnao7V9rTj<}<9Gc6#Zk&Im>gs_*K3V2y_<_(Z75@I*=s z$;-bYNQA0f*5!c{NsG~0MmZSE`s>_%K}B+*J{FtVEa zRvF^~SZ6szluH(EKcmb08X9Qv#-U>RdM+3vcLxfFLa8BJhNkD+|2uUaytn*56t*Fm zb~nep724?5%O)6GTS3bh#J1o~$eW^KPk>~jKJ-VW^JLe3tAj8-q8JJ-IniuuOFy+g0#hQGq z!3n`v1rU;KOY7g>-bI*nE$=DWHCuBn%L8c0q?04^u(QS)S9NTSmr{t-{M?(8u>|F9 z=IOt5eQ#Si{ZwDA@ug=3%Wrl)VsANDsSjiXTrN(7M-by%LoXr0rHx@_q*;Tv^3E#-1o zW8F!GVenl!b7RjmjlWf!C?g$~nqd=J&*V(LiX~a!X4$zv3f7Dzkwvq610`0Zx~|%> z)^T$im?Q8)2z8OxNx7xoSefX;>p)Qx9FuuGxPgAbP47w8DdOv&_Kn@N zybRtB*m-Y@&M4Jtc*$_yW@fXfH+4m>o(z5MT@T(Vs5m5N9W4^cQNgv26bp>K6&7^A z{G!27D>-U!WXCz%g)Ec-Z4yQR1CLC$w*?z(Kd@!(#_T&@kOexP_6vVX6R`2JmHoWV z!D`;mEa&=^;=MY1*_~D!i33-fd#=@)9(BEJ^C$;dq;Ndfr(q(&;3=2G<2_2>_;EyO z>{$v1(*ZiM|N0=rknjF(TeUaL;Bbl{PH(ohYI{f$lg$|eN=ZR?<6{?UI3cE%^>W9Y zpopbkOWtWc&+l8g2@RSxMKCvNTT;uTKx_S#GA0$y?C67!*Q*`$dgQChIuk5?8$6Ty zavqQaB*&-0MO3h@Kj&tr`izI*+qV=`XN)I~e0;mgG-6#K>~|hyozW`$0>vl@cNvTx zJxN3PJcGtH#@Lkz3QB+_-hE5k`u+SvEdPt!JCEJCe<%S_JVrSA#lO8QT|8yND%k_sst6W$vm4B9akf5d78sQT$ppb&w>DdPdl|vz z5wIwWQIoM}{#hu5&n*W(sB^TA!XHz}_~0ZsoY(>Mi--sc@_TtNO&wNZ*REOe?Ng3> zs=K|lM_3kb2{1S=Rv`{C;m3Z`UU44QkVHFud*g zqVi4GP{XO?1nb@w_WwNoex5S5FbAm|P>bwoz-r-mi5aBC<5ASE-=A%UA16F`scg@2 zzl8N-}V}RGCh`gay7o?f-8>*yj||&wqkp<=zElxuzHx@&xoDs4p49 zV$SbiP2l9JuHRbHz8l1hmIB;Oa%RkrlF|rKyRuyg+tXHaTs(<<&?}u`9aMa9`iS$v~zQUrWGza-%i)U zSLO{?_MeW421oypVHShnmX>~_oWKm$297XF^+u1Faj&tc1~p9b29)fJLqpB7BMES? z3lb>}NM27r4uJ3`+v9C7Nq|3Qz;@rwC^cbHW}xfHm%0xMAl4M)3u-dzU)6E?J^Qex zT_rZQgsj@F?D5fP2Ky-I>C;-Zo(2Qko_E1x=mQ@k#ayfT)vmpWHQHGBtY0c zg(quawCiQ#dyGwQ%ZFK=`#pE9y}hh0dDp&xuSxkFs0ln@DJeV156k-GJc0PQ71 zwh#k*+6NDP)CY@hG_nlHJ>7M|uAzmLkCFYgBslF>JF)u}rmAV3KV8Z2DyhDO(4Sp1;w5 zvTC3Y+N>fY9tItXrH^u@g}|l3C8Qe7MI_X=WPE9iQAY&F#QtsdPp_vDR;gvC|6dca z*NzXBVhWupQ3sY_UP*cxkCfN^J`M#()tF{Qds|rMtRol zBe~x^+X9=QaxIk*nQ<0b{z$y!zf*71nw!_&47reB`<-pv*6wDhF6}GBD;yN3&HE-k zzI~ZuP-$`Frp;K>wK$av>fMBl)h1U{@>K6=<0~fuKgPSr1*f%cbS`B8(3-DOPb5(fU^HS;|PNY8Pg>1*xnm293*9U$h`lASJrwxIA zNm%YiN%k+|3jUQziwo7%S?`QZOoZLYzo7x4eE%~rLfqqyRqk}Y-g*;^gSnoT)Y zS!@j2-tFMp={f(n)2B>yZUj$DUX7vI(DkFSfh#NcUl^**OR#3Rb1Adg(UqH0UAVq{ zv|iAEJyg5prmp|kv#~EqU5$ppZRDiWQ6%*G~$)8%KT%wb?<^z7cT#WRuy~id`a0*J>-s9T-(`aeXD3l zHur}K_?+k01ECM{{#8;(1QvZrhNYl<0ADK^>ZYybVCm)ZcB-t_tFGb9@u>w|qkHPz z)K;3)Kax)KhA*jc-j~LlE6W$g8)NFyG;NY6+SI_#WcJ}fn2HaqWz8&sfE-zhTEiSd ztH||^J9Irl?Ih`Lg3npU^WhB^UpyZM(Y0bXg6?xp&j!F+5zY_anko!!?UHg=qMYkb zT1BXPty`+phoVp^6$ci*3;0Gj!&ie$1T$V_H9^yIp4VVsnL?xi_0S4{Ss z(OyfhY@2K5o(iFGu`O5G>1eNHty`KOzWi;{3wQs0?LBeOc0axO1t%)Q7FtC*bUA57 zghq7Zk(b2|2xiQaKShLI&n9F-vWDgIdy`)*7c)-EzoQ*s9z0o~66f0fEb9cB^)%P%H2IJM`ED|Ufc4CQIUM*_$*YN#{kp^}uFE52$n3_omfi2h9n~E3?whE|R z6`lx%{MlVACs>nXd-BX%8aL1Uu|mGscIh&jKFvIR8#r=(mMUMI^Rlz`-sXAL0mTD( zO`?z)$~fS6-;zr$#}e3kA1nMm(<4MWB7jc0Sg zsT`Fx`OG_j2=)4wY%^DpNL6POmrVkwBOQ!i`w{CED#E!;)=9(N8XRWq{EwCmd3n^KPN z0Nw95{$bV~>FGnF6fMQa?4-KSOirUTqZw5?LCMQN%uV<#MP+5c1o9@pAy>l?WlV{} zz3UlINDq4Xxa=}>>ct+-m98B9xU1DPy4Q6&P4fKPRDfm6?G&ASH&cw@P>b*K1R{qh zj-vr}PdxqdGxts1IW{GMUN&YWh#cefwTRCwGEO)<8mW!woD$Tw{+Z)J_<5x@YI-Gy z*nk;X?MT8PjL?iV>Af&cQxleq6$%Yqzk7K((C*5Y^AHGo>D$WMj4enuft8csY zTV4j-%&*PGgH&#*m)~|VYLHlG!X^)o-^G7w{^zFYQ(88|O7VLjL=S{X#7J5~tTm>k zSb;RTDb_r!$Rc{sm2fpS()vfbWHNk5=jsgT7t*n(**I6*DEmy$+>$C|qnrs?GRoQt zyW9{q3R3Cpk)Bs7U(`O^k+0I({4i@_3{mmp0UT1~S`1t=;VoNgOBDy19$oA*vrS40 zu`ynj?AjbWJKyN-bEmSMC#A>cIuBaUE>4&rpSj%Thdt`8gQ;>hBzDbB?mULHC=F|y zRyjIPMjCyuKGrF7bpq?&r3Zdk!x2T6RzKVWXSg;ZnUhX_cM3=v>o01=PxVO9THTVn zW}FwK4={Ek=`K-_3z1lsl>5IV*H+7}ya=RyV@}1U8%YSayZt1qan!)MK`~Z{GE?nA2 zQ~;d7Au+wrY((4y((99Nzf=%CL%+&3>}EO683yPtaWxP_^yH7MU-}UQ_BhT3MD6CM zpGGieQ}9W}E~Pb#-;{ zOTqI%Y5GYVSP-{JYSunAe0Cs>4Ru_+uCLbyN_GlOxnf~HK7x9BRu)<%M2ApRe^(7v zg8L;^Cv1|;8Jjvcb)5bL#G+3s598WvyfmJH=HdvM(;&r6G&SO3xQ_E5p@8VSKmR+G zM6j=J)-Js8bH21iyz$yn6NR&0`JJgkB&b=qn2G}AsS%*NWTg2$C|2NWmbJYYUPirR zrm{&W$flG*>BUc))KHf+{be7q93cR) zLdU3?h~&6`_?0H>x&RDOzPD}kHg{DKVla=4+;*71G6IX2KWSNCGs)GxITUFBf{)%C)!7Z3{PlZ$2rb~gAhsbBeK@9Ok z_Y9YOqkDzWaxPhqcKlD6pW6)1<5?>mQ)JRwXOBVljv2Kb+A-RI!iV2pJP=<^zAGgp zq#LW$yEvDxBkN+n8#=pSljV_&b4jVJq~@GNtF-*Pek-4Z16%L^+<9P_GIQ4CL6$@d zSGMl;H+Su&Y1x2eWbCu~h4BM9mvox!{rBa zeD}pl4!#`##KB?--Z!?K$6JN^)K|C*jnicAxBqxKqWGymYPj8!qVYJ_0$!b!xzLwq zY$g{t-!%U_R_>c(j+bzJoqo!SFKLf{}${n&o!))>QGO$vBo&VqwWiU0FLob zYl&=CWJpqkR%=-4%x61oXKsU_O0$B4MsXW!D=%>$s*w6tqhW;q5oL8aq-Gb-165SW z!^WNh@=C4C)pS75q>`cMe8=u;e5gIp&pB@E#n!gOG_Q=l@c`kKMuRHrXf&Iw+@OfC zp4ZIo2@Ho*Wz5cQ)i3r~&5nDI z>8!m#y`!>_hRB9qDrS!ZfUGS~PEzsYMC7s6=b_-(_~pI)Fcgl~k1N0l(Cy)>v~h>4 zwqEFo!BN*BaJh}$JJVr#kK4|x{_);Qq6D7qbUyy~MUo|Or5NMm0ZF_{8Z;u#X=gJqa?CU6vB-N%qz!}6_RRR#VyQCefnGo zduX6Y0~AT}@=cn#QF2=Sj}2(f?R%`P<>iD@O6e*`?v6|{u_lYUP`OmEH?6Jx{J_f_ zNyqKCw7~aU&EiyuQ5iq@P<_D`Y9?f*;YHZD*h2RHv_`au|Q2f64IvLI0CF^?hl#o^@W^|bp={pH8l zs$HnQ#NPPiq4}3Pr*#FEnO(0dL*LFQ#AzEVe6Nn?{pphcSGnbA+w}Q8p~>rGDl#I^ zZdJhL_o|%cMRdI9YjI@Dp!APKI-V=15Rk1f9$$>Ge@c;7c}O2x+$WF|IJBFC;IhFb zwGa0z+9ga^Vw(#{qLst zzAaaNwiKOtP(utVUD{%?mo>ff_^Z>w0e_S2=0rS*$a@a&Q%a( zlPb0CUqQ{aUEo+`yt9@%A~o)}WJF=}V31Ju%0EL-Y?8gM7yZ>XKmX`SznK4oaanL9 zQUq#U|9V*LmG6hL(~`^IGp7xLL8Yi{Il?@J(CmHNV${mE-Ij|-(a1Km}&}H z#`~1z5@l2(z)))9CIbblbaZwKebw&pE`gYrE|11eB$IJ!&w_lA_B4?^SLqG0INrqq zB>}?%l1XsC$dkco?+*~gk1OBN4ixW7>a|SHMJb@M(p*xj@@i2NYO3i_9`;jU5=x76 zdiru0EpJCaneGK=E7_n5n5M95i%pkO?ORx1Qf@ri5BB5Enp&aN&jFOr9wo#)Z5*R? zJqv6TAyf5%&06Q4=nzj*M?d)XS$Csr^FZGD!}oUgO?%eOWNr(Y4Bt>ROYm>CfJY z`s;^z0&)WMJ<@h|?Y6UWSIb?7w&Cq$W#QH=k0#`0iQXp<*n|LC`xpk}Nuv|)`%_#n zCsMR6EY3Le_-#^v<|Z0+zuTPe-*&7OSXvY4F8vJ77eiUn|GF_SH*vpXcKcXX@{$?% zS#|e&BNyB_CnE5;(@69U<&90pRvBB1eZ=a!@m2${3W6^bZ^8IMU!56VYrca9XW{nV zX^OtEUEMnjq_@?b|2-1Jx)g?FDPD$i89NKYF5lF6k=c)|^E$i|=y|RXU1149q-F~MmXjo#CQ9x!v0c#~ds>*>)UxSrDNL*kmP=4K- zHNVsu^)q&Q0Q2!z0iO4Mx#5gRvb1E(Mb!G`|4zjrg4Bbeo)VE-dQCSGFOLljx}7P% z@)a&KsNk6f7xm?x1(2eZPa+*6AqXpDcemWZ zR{AfSG+J%Q@9YS6)-%AM^UZ{ogRAT9T}63R%`fxn)uzG?Dd!1j4SIHM=tHnUwHH{y zM^Fe$hcGJhi{*qP43`=92JzSBlk+L1`B=sY0ajvt%|^NxkjL1X1J)enDITj2MLsT_ zXes<7-Wqn>XjDp3-9oTzT ze@UhWPg3jIBM#F6oBTs7BeWKoD0b)}7y{#IJIm^NlrfKrY3X7B6x> zKpJudAdkgJ(Xq0>Zf31Ed=XOOwu#1*T&+_*;X?KA7Xwu8Qz`6Ws@#;+6MkkIhKHC+t){R(Q z)o+}V^}0TiVY*n8wY1dIzfAhP%)am|a&mf;GPypD$&NRjssw`@s?VVQNyH4x>=m&Pb6LxRo^8_S-v3 z%QSbQOIRWnl8k08ss&$1l(;&c<+Ggf;V<;Hn;(Qk=8vTPY`JFL%+K52 zF|!Nne4N4e7LrOz#aOaVV9P@GE5+N?*t&dKXY)M>QBqRM7UP;*?fIvFRu0|-tr-nY zX>d(FbY11l{=N+h`Fi+=_(S2kY_Q-pb6u3qpG2&~yl zlh0_MQCtyun+!$rra*@>P;32&D6Mj_4OmUha3t@m*pMdOA{hQ8@8mp9raT0Fj)!R- zIXJuOQyP>A#`Sl+^4ecGbcL02BLdDdlS-;X^dDVnalUf1`?;HKf!l(fRk1@n;P^cj zh&dVn>NgnMf+!0jE~*a3I$MM2#17-^kxhQe8zz3WQAVmqMjhB&%J1qJ;9Bdjb+BGu z7CK>6Xxl`$G#e^zFwrr0&1+{X6q(lLdKsiigQnVpLj>j9tgfGUi<7MU?it;@E8omt zHPUYT>(h`$BvJh2J8|c-1V=a*RZ%l&jR5`fpa5EXGnp3^}bANincDFhneHG7pW^rLv*VpAceg!N0XJu9SUX)ien)V5%uSyt>V>MgzS zMA&HdBo1o4{$XCI=1Qt`#LYJAO(Oat4WpN`^em+jy*^SglF9GdHpgM~j%Jy2+lnNn zR8X&)Ts&_((dlj@$&38u+V&AwOTrP+WN)9>%>gq-7gua^chkz@w?pTNNZR@+h(cee zLq0*#v|yWd%mXRyyhpDK)o&^Fed?l;oIjYoDYv}7WSiL{9Q1ry@BdBaD9m*dG|v>i z)-PWjEO<7p?J>91a9O6Uim};!lmRJoJ;padRzL8)Q&<7}VN;fUmKHB0MRDWq zS6ARPA;Yy?_7UDAHDQ5DPX#o0mTAp88BTwC3zR2FTTL0Gww0+zGNN6%Bk;#c4ok0} z8$J2HB&<^faiCQyGsUV>d~j0( zEW@S(<*i0dH7HE~IPQT0dkwRYa$%x|W9}4E#$ePJ3$9{!S4BSfX_%Dj?EjD}^fcWn z%S5$xM5IF)@<9&wL%%EU#Bl=&b9Ey(IKIicQ}<3a_`a#ki?o&r39Re#uY|{Pb_sgM z0r`u@PWUj>#_p`Vq`H=@CM0BCGB0=DGW%1!D4%+-im1sIY`vg{*`Hlo-*N$}Pk+z` zc*F#YxCK6Za$fK4lZLk@L&Yt%2;NZh4(2X}^Ja-NWvIe<9TFiIk45-sTfQw6iI5go zY|h%uuAg#AQ_%ftauE<%o?xxGmr059&P7S~;RGdvOv^^)O7PJJBZCjQvl&$hCl5$V z&&Wx;-REBl@uN(il`-pKP1PiOWRFDOJ+@67d{+PIS8PZC##|YMnMck1zW&{@l(cE& z-IBBq%CLkk!WNFS`#z%{>wGyER%(zun9Vc4a$%8PQJ^`DK3e?oNqM@L6HYtq@Up;4 zoe+;L-W4^lW?7EsGn=1C4O15k`Gh3U+B)C>hkXd}96UfjP402xh~N@r5Bn&T-pfd3|EmE%%k87BoKEZ7t^N?Vo( zsTNC-(1;L&5I1oLD*(~V%xXjGG4mh2>eWm>e;8z^83)V#j;>~fa_dou$_ z%RbF4EZ(jT=u#GhTC&1OMT7LxnxyYPea36EIf`!`W{e z@}yucy8XuPphUr|D)y_y2@=pg9t@&b^xsO&2ppn7M!fL@{KNi(N_1H35Y4zVS-5xT2gg5JZ+ z?FEe#Xnt@g9015cW*Q4|S}j4Llv_j*w;8zx6A+0&)V6nHxp4@y>*<=g~@lTz- zioE+egbnd8Zs5 zryfHucgAI^nYQ1Lp48ziFRzu+VW^yeA}9QEJV$UC3G zQ$WLog!NJIWfA=5QX+JmGTpKaw1@pGXcnkx2oX}_)0|DLn{Ukn*^pDyAkbHWka{&< zW@xtAk)!5oaa=PPzPH(R586Q_CH0iFvdQB!+ov+UjCuRm`r-ET!Z^)ePXk5mfeL%O{`Jt6zUag><>jP*>uk0TH{rBumJ?i`!>z7th1wgkjhm;Zw2p}H- z+mJ>`VScims#Yfg0QQ-GA5>ZG<+c*?-Pt`8Inr`C=sKaE3>aI2inlTa?l$xFdFD>r zSn~3AxysKxR8XqYNZ%_H-P%Kc7Fogn6o4QHB^z78m&M}{C6ssHyc2k=nR)Be1KnK;uFg?2QW3xB%IG>9HvzDuCV^+TKk;K z6ZV9Q@GM19!r_~tzC5ucaVe9t2TupmV9K8rL@th`c=S-OKzb^wBxb|Z|EW?F7Z>o7 zsE#_v^Q@g6ck|);GIoGsPw|kfOx5UJ>a{iXAD@CJBtdT(K8x3H7?xXCz+~UAoLmWi zDg&u*XmZ>kg_al1Z%ZaUs-R;%_6`2@Y2qBO3ZtqlQK4U@5$DPowm=Ei+8>9C1;DUx zuT_Cs@07|x2K-RPO9qFlf;xhO`(>gYc3rpWxg_ZNf*5~c^)dxVpS zFV+@4Fmw*={I6bG$0&YnbA=nAe9NR$KK%WDhUKU&JaOVlQ#q8aYo;I&8zfCfo3qIo zsM#o+J-=46-~y3IoP}`P_q-xG^sRdoIL{B`8RC~_j;`)>Sge_^_2OxJBq7D zaW6>5bU4MTQBn|erC*ty3x$PcIOoD{k=i59%!+MbhMinSDI3rO$H8Nrxcx-pnjUj< z#i1aOBrefb1?RY=UeU*=jWrh>tH=BScwt+ zL>QQ{l-+v(iI&(DN{WC^B)jtHsYtEL)f7hrL5p!qqpy5u@aO*)kF3J~CeVwMRFY;) z62=89HI6^;tR-}Q@9d3JF|IB}t8u|t2IwU7&?;k7W2(V=AK*s?&=P;a5);S~j4zn< z#Ql^q#hS^lq@Rd&Es2v^|DEa#H;>yX;{WAn-O5mTxE3WnN8h>pS7)umFPHVT4ue5U zM~)p4`dgko)~;Km^KT3lG;tHpUz0G>o;0BNvTdf=mM^@p%PjuRc}d_%(O5wBLDVSv zt>~!D!RkA`LdV3u%`vVW4`ZG-DH=WjcYduvC=!a&wVL}QF17s+llG2zumT*}6_W34 zV!6uBi-4WX11e;*)kxEQ+d9kQ&C;-Imi+gfI{hL5T4uGUQ^{Kg|DA%m%a|#D4sCE- zHdB!_6Mz_Ga=3}o^jNE9(*Rw2YOX~Kas(Tts)W+CEL&E$3MME(((-MR6cIntaw{7Bbt^YQ1=Q9~i1W`0td5CI2hPHYTBA~QXzX#K7S zxX6ppbh!R` zHJ``UZ16c?`1YpqLQ*-`cf6T}86BmW4%w%zE3}a42ad0Na$jP&j{pI4P4Msjwyc#6 z6`zpSLD%34I)bl%Y^zz%Tyx%@c=28g8<KEz_e@|SQ({V< zkI$86EnDp0t7yoTN%;Ji@tJj4a+uVNR6CUtF7-U%Dep;w&>Gka0aS77DCnw8OWqIt>_uG8mh?FtUNh3%2|lwd_)tZ;QA8kz~pp=ll@i`S)sId{073!mQ~R zL;;8TIgkW3_AU z0m5S`IYx--TB23UAF2R zmyF7ba9BR>1O^QXIk`HGJ^u0Ad&ZmkRTD=De#Yql3N_s=-4?qN9c}H(&8g6+LAfen zlk6aNPp#1cDwgS6y*3Eca6KTar?XW~d0sN+Yg6>Pn*l(6;L7AkSa_T5K7YL#n5lWP zZEN+^b`5q}pC6FBr_bA!{a}*q+z+1@*YhyqxhwPFZv5kU#q7T6dt;9ett#ht?g}K9 zb6woFAMwv;{&$KCiduoHuSCfqLb#Ma5pcJCEF4U-4F$)i%=Q^8ro6=d2KQkTGiD*4 zTh*c@!cl!D$GXGwNq@kZlsOdgk?2{qv%$fJV+DW$hoO0Fg5O?x^G#81EhcThW_NYi z)_|4XpYX*pVE#=b|HgW|YuGBg7Qz@EE#q*qWG;AlW!ts5r+7WuRPO7{2)(`~#`ylm zmMgLV?U{H~eQmAf>hg#1G?iP%m9967#d5>RCoE;>#nxM(`*G5=OwrJWZl4jEFc~={ zu?Q97LO(9};6>-Aqt(a!#!tJOe95Q- zFHM`IC|M+Y(DYAu`R&Ty{9^B76{&F+^p*;Z>O*Aw9!Y&IKz)LAzJX5h)?;K0{`GCM z@nFt>T*+Hc=$WRbyHfi9QFI=DN$!6e?{wNWO><@^_cnKq?3X19-|mw8jpA9IIcR#X{aM9 z8+nxF14n!A8KXy@{L9?lmV}v#WLu|A3bRB$!#(J0u8~I}6)1+ms-!LN>PBad_Fpo8 zkQoELGmpr6d6MvtoMq92vRzpV7fT|}s1%JFZ&xGLL&%dAgqr%264iUNM>3r_qT@Q2 zOxS{o=H#;E)UKMkVmyA{A+lS&VXyzeT-oaW+v8X8cz?R~rVMok?UT}w$N?~E1jRS1 zB-Q0pRLiWZ>D#+ZirEHx9te?CA*Y!}e?k)i*Q5FdnF!@aKAdh~^p+ z9aX{Jqb&`uzowtDyI-V8kJ{O??wn%rI+0C%i)q#MQs{KqG@kBSv?czjSkG$*BD;Ly zU8SBsiy~nXedy%|0OpC_sJ-ehP$&I|R#v@~t4|KJ$PJBJYZrtg@_0bM40W z-?2~yOtJ2@J*!9<7#TVpu}P4N-kKdNUq%N8=_R=C4aVG_d;s6~i<#m(~1jGz)eF(e{RZ`g^g zeL7mg;tQ6A#6}2B-}9HhPCJ4Hs8G+XB^Iqba2Aloq1RCV&S2n6cPD3K$K>*x&rjgb zZ*jxS%$L3O!a9v82>4|AaD6B8U2CbQd;wOh02jD%JRX%{gnL&w{17fbYC@*T6=iP7 zWgETOH`I*H!E`)ZT!BbGYOYS|7hoKAj6h$dYZ-f$iTYWknYH?B=5<_idQ~jd9_T}S zO_A6qEX?K9F-^^L4M^_&qC;-eHZ|BlN>w9^!hE^HJb-TVm~7=1CI9~K+>PnUB}hzf zk1&`cjDx`lmMsWw8jkQza<3#q)63z9KynE9))*9GE9u8P`>CY$L%@m=Gz{~sgcud9` zV)cXiGdb*PKYYo2vuQPPPKx4Je5K(I?#gI1RMW(cu8yXUu%p?_`6q&nr6@etY(6vK z5aRhXk+!`vz?IR$>R4e4Z{L_-OBDP`zrv^60e)G6I)j?M|bE%T>P}`Rik>RO_6@ zUuSSm)`!nr&a&dB|49{op{|{f^88_2DZ1BOx}2@j^-ap9`)0r)&5dP)#|m3Z3AQo( zP83f=Y84!cuOl}*RR_TostlU={`#wy%vo&u4#%!gHr}be$|1gl9kWas6p$>~yqO7Y zVcNU{D3#71x=mo4O2Q2hgvoh)kMB^sVV7#JksSRvtSJ>$yV82a z1!xy}JMMq?`DmTT7C7B3@0-+2$fWniaP|NUA+zSW{Il&@X%%xn4J5Zrb0FvjW#|CS>{DzD=Dpr!+W%BM-K&U{R(}KDSTl@vA$5pu^9fy zAm8CLye=Kk&!;5%oU`x>QY)6j(xR<@$|MO^+Z?L0cti7eWstOqj2I2GH~FIL7qAE^ zd~oOq;tU^|8XY{^bBai+oc(lL+waerA7AQ2oRjBCO?DsR&$)dNu^Arw$4cEIs72N+ zsmF#=6!`|BifZmh+>FpZBE*_^*A%pN^*wu&bF;)hIf_jgP1#@CQ zmvS57ua-;h<4gq8F7$U;xa@pUZ%DX(y={nk0Q-?sL-y=G+s~C1x-S*U@&sFDRUwLY z2%S`iko<@!)7t0$VS+wwX&f$B{>jW5X8sRi8C5?h@uSm4IOcCNn|XlayV1tQz8U_& z8tJ9~FUuKWrX9!{Bi!}zpmo(y=pKxjU%ha0Nb+6cG1xjIiCUlMZPD}Tjm~1)HniAA z(k?8W?+9Ff`0S#k4Po+DInO9p&Z3n32k3E=8PUZxe|?@rbjEoB?jNiCLQPMOW>ip` z>gm#FZ`V&$p&TkEvVlK|Ro-?U&J0QPzU&+88-K}&KW?eK>?8G_G&e+bH)Crm4$ZAh z8#4$FLdgy!W&jf{zM|uCqE3FD|B^j`A`($>uDqe2~ z4`@tlhMjLMQJGUG04fGIS&(DOiTZw36QlSkoy(>{kx|fpqrC^u`6BJ@C*fy{`gh{T zGVd+P>i-?Q6%C&YNtXeTp1#Jq4cRvilPdBy9XOkfNmvz(&@~gka>9cIDZB%*k>nJG z9+AMG1-Pbw|BozK^@wPNf>>DH7|_Xby01 z@^VziK8c-kdJ63o<95elve`>~Nh=H?MO9!)^1)#(B)C~SG7j4dTClFLvuoVDP`8z~Mz!O)7m@_(m6O8b4gc3ZG$N++XHE^jBS&GCX}qrZ_qQ85CglA zGt?gNcyh@1ee~RA-J3`99+Q(x<>ecs!w~x55D&}$`E`9r{atBn`KVGubuEC8hsR4e zcUD*Z*4))^QDVAQ?`=YngC%ye4)&JQh%nT#JfXh1A!HazT{WvHo)R4KHIfo1OtBhA z(U`2`opD1Y*g;k>X-LPs9O3m7fKnEAt@M{ah`hM|47_k!5wVaXyu zaL+);ud7i5EnR|N`%r4VeKWuh*A~+p4j{1cFinh)Swjvod<3EoCb`0z;DHcTSqV*U z=X}DYrh5cf6Jcspp(hDlH*ez5pi>1Z2P`N4xB(b$HN0{GbKas$foHvyednS5;RJ!1G7J0#;c*C`p{1 zXb$*mt6%&Tk8yMABdYAr4u|>pZUZjqm&FcQDiXa73e^d@s@!gbK!!sWh0<8Qj%(Oc zbgD~&z(wmIK~Z^R%1Wbxn`7!4X6F6Gm01p^UBM@(H62s9`8CXsnZH7zh7q< zIBC*vFdU^^pRwYpMSTfA1NBM;@H)_6hbQ%>t=-kEkp>}ZR8Ykc1?S2DH!_!=-!cUi ztU$VZMJJ~cXMmC9M}8azaFm>3xy=^4U*xScB000Y=SkL2RJ8=w)#7b=ZZQVTG-VWpvuhudyuTC$m~ifDhHfYzbvk8e`s#gM#cOG86 zC&1Z%^M#%7F82C+rs;>!Qi~GN!KV ztjpVXirC2PTggffe7DJA>tvsq`c!+YzLdu|I#6(Gc@24rn+8U(m#B6+G4vVoBs z{j=oc)i8w&YKXJmtF57)mX%ez(Tzd9h@JMJiv?*{94!?a-nR;f_TGjimnnB}txNG; ztjpt&!3)ePTDm7r(opF$VNtSh*Iumm{? zyWKetzQ#x%b65pHNvKj*%J2ixEZAD-M8xpgS>Ru3 zW?PAclgiJAkc!%${2YX5N8K|e(woBAKl6q6TRo7lcO3f3A0&gk9$6?QOpxf&>@(U5 z$Rt-v{|;_gjp@$eA)0ZZ1<-vY8p`w-*60sEa3a%XJoYA z{ErNmADlDKTG1FeU+4!B*VnO`a1F7N`|f=4!)&SX9gUGMael1cCTkY7)w+kc5CkDk zQ2_guHtxDgXlWGwU72{}zUq~z7zwxE1Cx^!y5f{(jb>o+kFF?WRK#^oKGf`ty`907 ze?k70hd}JV`TFN@tEE@IJ#XaAax!gyWv#ui3i93{=0It{0E=naIh`7qAtERep<9nL zx~BLB1ldb7b{L)??ft!4q@bl1Q}{o(Az4+p8MC#Em8=uI!OtUlF^z}3eP#CjH-?MN z+%;BLs$wm&qk`Mg7}dC{9t5QMIk2;SWf;~7W88k%dssRL2Fl zOH^NvyinmiZu=GgDPXZ*cR!_Z7!|sb#~NHbx}yrX@zi}2XlK0>T2Vvq>#D%$Wqz`J zUvB#1tHGGO+YUActHt5RYmI+>lJ`W=YQ<^=%d#8=Zkl@y6xn9 zB7-`{`M6Op8vQ%S_!H1|gwhSn=+r5OvXaDz zpZUw(SEkN>%cMM$di7cb2Ppe-lClpjL3-ntr5xX`cvdro68bv`!Y;L3EJ!-EaLbF| zoWwuP1}~pkg}&@W7g$;*13I)!eo(q)c)ZR(s_;|$V|UZ}OJM%v3eVb)wGvPD z9}I)ZO;S8O^A}xV1#ADEd!n|qu}-G1aoTmsJ}dC}nLH-Wf1MEJM(Yszc{9@OIIbY(B1})<9bf* zMviqb5u1GX1NGkuH(6^NcyelK;I!igvl1&;T2(NpR6(mUOIlT+5=)yox)>y| zpzxwtO{L(=Y_sGi8U&aVf>~|l7>LkY6wbZO3iZOn$_~}C9HtsI9nGIXWv{9-JvG|(Bf#q@VQ;*V@I}GQ9T_WOmc;ZDJ+o6_xI?h1h;q05<^w0=#kdx zWG;R6@C3k&DVP3cboMOheS?S11?lYQs{*2)ZPF(NI`%4=6Sk%i_SJWdB}!4+Z}PrVKst{Yth^wtED%R>%6|K zHGgD=|9e42$=S}H_}dQ@Zb*L}Zw~3CdKSJis|r{FUpA!6)T7w-+G<1LM(b|!6Y zJ!@(dqaVgbd+M1L?Cp#6$}HDR9KrF7)K&%epR??4xVdbaL1~`XV5g|FDnopd^61ak z@buCeGft&T=1X6P)8}}*{Ytd_>Z?IY8SG)Eky<3=Q2Fg`^pJ%MvWyu-Vl7vUg8w@Q zqWG@PBA#$Z;~pOlN17O&aK6NooONsfbOESn()sKpGp7B}2D71@h=A?E3Na%iqw686 z+{T_*?tJL}a!@2|c@#HEPfFv~5MRBdcDUnWf9@R8)m31pJVjKi>e4iN>RBR59xi5Rizb*ja4CHktpO^PXzWpTO}+j(cW>tqg{h$!Q@@8IB;cr|C}^Aubm- z0&jj2?{uX{lV>e%^P&%fV)|r|Vpl5+{9)aru2YOB0klH)j;#E19pQ?~28Dg0BXf_C zw~NycD;*Rqy{dc+9(!$y#s-i6E&J!4Mw8M~P8gP3ZI(0&wsS@9)&x$m21j3#iG!T9 zfXNez93*JuwBg4Kqd&CPGu)|0%+M;jmm{`23=6`NkH<*@LBqwUBL2XJtWdGCh{2^#70nS{f5a60&~Hop z2MCbRO2ss~ENL|{_$baVsnUq8 zy%jJ*T1rz;b78|oT*_WzqkukYVTvWdeQndb|2IOBpsZRy< zj|0b#hsC&x+!1drn(yv%bVNSdzE}NZ_@8DTLDO;~xdc zkp`YwPCV#wBm7E{+`gO~T*jC3W5rqd0Zh9O={HVuvNY>1S?xYepD_yW<|#hRkv89V z;Zf&(FmUJyMl;8}JxssE&VO$~=3{S#mZEWw`%z{<$1W|M3@AB5YRmk)x+2tfMZDi$ zsoVz~5e{G5)Lps#TUMJN=X0BZ);Z{(hm+JDr$H>-pX%9F~MQ_5wnlom0t{wE~=4x~#6mMQpw<@S$jBIEU z)51CpxpK2psb^}VCw#N3T0`WY0msl0DR$bJW3lGL1{z|M#oJc7Jj;hQ$+)X+tZeXX zV)Hilf%Ut9gCOm$?XhwlCqdOv!;#aehYc@RA~L&k3O?r-jH-$S?gc_b_HGJNmDxZi zfGGh|C}fR`FIav?%egyf8rlV7LCI)5d{pA@)(q*2s)NqsfHLUyeYKr_q+gY^?(J5k zKgH@_GDTe_9|4v{Job61%HcIJnX$@O-+iBvh$)fg7UM^?8{I@3_)TrxPO^aH5bY<} zl#=yN)@?&RHaUNmne*|ReYuv`m*b!SvnotKgp@#4Di};sdO+g@ZlAOm=e3DfIS%Wr zY$PSmWN^M1Ao_LkPD)Pz20~))PgLxLbXk~?PW3;!ru$Y1_L+fvOL6?wi3PrkRnfDj5J1 z$d0rb{T@2ElfAY!%&@^?7d_ zsM*AGw`XAzy3>MGh?*+yoLo6XpNw>Co(UWr<~tfAkh78m`L#WnblM{7Y{X8^sN;t8 z)N9R1+^YyE@_>Ow#M7Pg2j319 z_2RO8m!_2rcDA*jB;&4X3qIrlrRTV>i+JKmo9!JSL(vM_xE2(7H~HY-W5cEY&fQey z^&%=TPXEqE$PeAy;)uJgx5&Quc;XmNtxI#_0wSj;cr*5~ z%LDKq*U60#Z@)I9$S!(AS@(b+LHz&WmDMctUDK)rD0YbY7<@m+1gU77P?X@CEJ?F8 zGXd(U8{PcxTtmvLp1{UhYB2c}ccL)68T&qX9~dD&XjRQV&7BDz7~51gZ1cLy!94C7 zxMB`P6JF@2t|?xzHq5f?O8J2cJx-V~j%}a+t(+C`*Vo{3LC*$mzMpt2=_1NnDNe&Y zX?f%D^{>P7A{qO`YdKQM-xtwJ^$9pz>4De>N(7^TuhqW$ILQ&HXhCj<&bkW9=~fcd z=~{!xcK30+qqwHv)I$epIk2%!_>&0nY{aZFk6t10M)G*<)D5++3T?H1m;3W2zBdZ< zw+({=3ZYigC*9$E7+(oiA0oQNS*9gc2+toJ&YAq!x}!95Fdcho&af|i;_m*JtDucQ zVo1b^74qYJz*dNVTSr=%6}R*;`=#twT(TY?PCTd1Q%vePxU9nNxX($Y=KFu=_=Amg zQ{a$e3w5gjC-KVj^ydn!*Yt3g^}u~=P3;;1q^iLC@oK6lVxj#mSXfJ8VeERgX4lS% z#AsQ4aM#r1cVfpsoFA&*E8~qt42MuwVU&vhd$BxsZ`L!^&ae5>mziu(5Jg2yBU2PZE(A3J)%MYjMz?*m(ut|qWI|D}8a-=!WG4xrO)7ol^!+m=M z8T!`Ca;#KcGe%F}#u=fwRd6!yy_br;Fqq)GTm=SC8|7u4{J_TLgy5uVfr~Q%NJd)-qy`TSpefXH|?brGG#*v8UWKY z@S){g(b+{@y?T_oE0w0La>$-kr)!`aS4y6$c42phPe-I2R>&*@#|;r%%+n9A^9*&Y zZ04uQk9&aPdsabhDICmq=YCa_k&5Z;2BXSiSXrGoD%F|*m&Kk{Ec~PGy|=*Yf7L+cKVvNvtCTp; zr#l%R>q<+Rp$q|Mn@F02fN}GZU-?t@I%%{B_psO!y$Nq;_`pFS&?4J%8xbvb9GO}{ zomqVso#oEjj9WvPo%PX7o;H)nLl>mvKP@V0ai+ZeVgBYTWpk@+TO1in;Ks}@yOJg~ zXtvQlTrkVeMcbObcnx#!<;T12I7AF%YB2Bl+ctUVU~rvA2Nr9EE9vcog6&iVil||r zWhf%3N87eQZ?>dn^-Sp?jxY&oUJl|X6UrUr<|t}o;n~zevcV^zm!|)V=euV5&BV3| z##wzry`%XtMWNsSx?Z~HXqi>+VclX z7^xHw>^oyH>CTMd#!=OJN=|f>;Gh>d3Ua^H?^RyWCKF?MnI7xw8+`gl zPYC^@2|I=&>xaRw{t32M4Hew;tnIVp1`g#iA~>yl;YIm|u(E+%-f&$%n@oUU-!&yn z#=!Kt-x8~-k>t5^gkTwO+Y51RixWN{M9pku%zhF&D6_DIhZ{a=c;uO-6EkxvcYl3$ zgv94u^%ewts}lIWIZGV6alveYdj2W>!1uWb6*fxHm-_N7met=Io?_j#`Mq&6nSWK1^DGiz%fghDkb>&k4{ql7%K?0U zq0C;3K4P5y0rDo_QE(RuD~9Dq7p#YoA_Ugx4veRFI(5^171;sIbho-uiMF-hiPqMk zko86_+np`Cd$P*?r)~6w+~WH>lSD@5I*(K1_I>)4vBryIvAEd3YLt^f0%0K{2@#Ny z@%8*#sNCzjhFq}?Ib^)ltjqv`tLSQ9RvW0?n_g1~QevwOde+P9LRA&?dEVmpytJ2h zRZac7lcd^l@I@;KNGUMPV-Am|rS?nmQTWNe`%_ut=VQ> z`4zMyhHBkUkwbF1Qi5f--91Z92#eaTZ(E-Wm0GmE+U~<&8)^>5-g+g_ZGZHbHmsH% zxyvLvXZvQ*wuZqo(k5-rijVbxC9mdm_qaXN2098z%<-_AD!9KoRAiRG7rZcX>oz+i z3?-{oPZ!WUSV5tpKK05S!hTz=Drs2tr0N_QR`LS>02ANRZWbyvx5enh>x$BLOJakSnMY zM0uLhrWV-Gb)vx2_8Vpby~Z|M4OWoQt`b{MepUPeR&3S745C8QyO~|=>GPzElx%;M ze?{3}iq4nj2X;(;Imga--dYRgTKKv*XMK^s>XRHVy)Or~z)AqFED-J^-FSP%`74BC zA8>ni=kl4hPf6ctmvAe3>PAZ%!frr}4evAdBV&I)_39T~u17XDCx;8n5O{2QpQ6Kx z*;v-+NU=ds(<>BrRyQ*CZ2bEnlad;hefOX8p9#lnJoj9U(p__Wnl54F3wUbUXCD2| z*cAPwJ-?`@E*}A5vDXnRsWF7_wSul85q1x>mE$%aKF{pydZNmz!C)F`=z4X5`xlGW z?`~1sYC4SX9w26$-m+&C0*C|s6R@+n9>$+1?LHY@hHF#b{n$?FVuf%{iCWsKF)|p_ z(Y-r@t=#=CVLG0#>xs332&m86Yo5@07<)rKt|Vh9sM>OCKi6(; zqHz-`dQ^nZvTpDQe}2#kyOdw^;1)6UZd)w-6{L#PO0fT*aNRc#0=wm29#ns?-b^e@ zi^`L6&ucq9c^qvXr`*q`US+Y$bg!Mi`yHFMNGzfC%*B4Q4#;0L zHSYu@gQN|+=qOyTl%c#u4o-j|L#=3nrkl30KVs)P zVCtLgC;O=?|LsEpR(xu$c_k=-8_D!_Q{h~9b2qTrD(~ShfwvD1a+uE+V zy5Y+kJC-Rzrlvv6bdl~)r_Igj0UG${->0MpNKdOhA>M?-l^}AU`DF6o#9>;)2o<&f zz4yUb!_Df2WF_uLg488`hpn1_t^cqc!Ro3=>!Wm5(hNHaa@V;sR9@+L+vuY~+d-s5 z*NMsl&yTjlSgTxvT}|V>{3I+Q0X#)4&z+*EY(5}Ojh))pwuYy_EGVTAwAoRUw=UQqGfERSamv+l#Pj z4a}lNdQEAizbk&Fd8cCt;DS;0eyaLsoaO`T^ikq`Qi&zbvOkl)aeG9nCaimMWF16^ zm2HraTZ~IhkS7$zt+QpnTRXva?fawqBOm4bp&ldeutDSLvYH8HdX3oRoF6u7V_rJO z<`}*h*SK#0T8TzWe!%ZRBj1JU8cRN>7x+@GO0&+HrJ-OC^TI=O90V8Y6D{rW>(CP> z!P5``_rwP&3(Ve%KY3UH!ytYk-sp%Ag{zimWy3grXoo=qSY&_j1uM;t+$b(}euws! zAJzRbd~&nIi}1xvFmFRozt&6x;Z9tuYwdN~W2~cTvaEx2aQ6pd0m>%9&j`m4@?$O* zV~xk<1BWb9)+&Ne0Q?7QJF)!b*2-lMN}8ZfSw?E%gEWh5BjW&~yWLFl{cyYL+J@S} zvj;$bok|>Zq@+oH}avF2Z8z=?oDiZFfndzr%wg0;(=kiVH3586-gdLPy8xFmyYJk0KQbe9>!VPpt<(b9W2x$bT~SxX>mxhTLd_YuSifW}tq+h$9o!MH0IxrDZF=_vC1-oJ)%K1&raw87Y_8?uJRD zHCt38gf%@?ww_X#7r5gV&w0_Pxbx|Eu{R;g zMKNtd2?TQ<>Xi=DO*)PicKtd?(REtTtFg|KD|g8{aw94)6aI;Z;YR9Q#eUu0Jy=>{H*4Cz4nFo$rh1mNV@Hps#_l za)>Lnu~TwplhMkC{sIoIl|pTduky`>u0uX(Nm}rHW4h<_EbWt&M+K+!M}smp<8{V% zlVvi>DGl3bDGgoo=t+&*+V3FkK`V1u1i$BKHKcLk)>pPTI_2p_Vp$kf>%qUH??W(W z=B+R! zLB^)1gxe`&g~_JVJ{2*R?p@Rx>VuZm9Cw%9y?9ZsIgU?GCBZ5pEtS3-5!UhPq8Gkk zTm7vL?*aK1_jmsVg}89jq9x7dHw%nw&qG|~$e&8C8WKDEYGVyvB;PnmIkU$6kXhK( zro2(g(caY-UCippUa*z^u8Y0`$Y&zOBzOmXk_b^D)O;iDhACR>0?DIQrrm8!`lHp5 zqi_spl@k@T;8y~kB99R5OetXxWLKudx9>G%XjpB zx&%oB-se+gU%WiN;8go8sWCFj{2em}A{76&_bQ3{<5OV)DibNS7Cp=M|er~S!bCbkn`^mAn-rZ){B@E1=A!5R`5P3o= z4L>j&kotCU`r$C`C)H=H$qD+7V0WkYMAcR=j0^K2PSmCM0-m=%xeK?s`{X6&6|Y5( z3n+Oo@^=L-XcCVMI|DM(SKPOpCjpTRyo2a6e_5CI?D#jie_q#->+~NE5g)$3F7e+v zt2vi^g^H@IvADf=*15Nkq+auk7xiN9O(Q{MmSUbeHL%!>>9_-giw%o61u}v1EQ8gP z`&)_6hqaN|1ON8WiF(JJV|YFc{Gzgshb9U~xMehXe}TEPK07l4S<14uR%pPosNqgI zD!8YEm%n!T)hX6e>|Ie@Qfar@$j}OvE@r#AF33;b|GB%Jc~b03Dx29lqupH>2q!ss z0PdS(slB}TiMv)12gcOV=~H)w3kSwdDFbI(urq--M-s75udgV~oIa<9W#(fR=njyb z2@iGXUw2w%ABtq(m9evz5;Ba8(|@}i9Byy*;3?MWJX^awzFiAaMjP3Md6e`%(_br* z#BDnehfH@YgVXLO6j?qO|6-MIq}*RRNyl+DQKQ@m zQs1S1ZKIcGmH9~uR9&>n7d~f;T&Cr)uYJPkfWu^?igxES(t&COyu7!#zi{QQjVH=> zTWf<*plVBb33SKu5+rh}4p-~$m%Y9FwyPL) zlVoohES=n9Q72-Y=h@XQO*4f+sQsnP-#*g}Rl5>4R4%s0LWi&}K*l5ki@W7)DY(ci zXz!v`{nIxD!p3JO9`;RE{pzp0pshC(A--vKBI=>sJCmBF-2=UH-_joevu4HIED$+m zZ;C!bVy^e`ML3mfa;C^94U2J5&REJx_7Z$ygA1MaH(zX?~NvyCXsE41Y)W*^yeRnHO*hM zX!CxR`a=A?4@B+X5bg$azS&*5<43K&`Jh)dRZN;M-GdMhlNz$55d#jq)>#OTL*#CQ zMcu>1AO7}&uWpC|k~Oz|69)Wu0Y(lEOfholYWAhgR{r+#(NOWCIYjJEW8-eS94m-ul#@76=>wcX4QVi=WVmr0?KVXOAe?2E~Hwy|x4;pk;rkJM0ga{1xh z6)6b;7>=EPQtg2Qa`~J38hk#EEZ%gLPUqrJ97VTm3|d2FRwxbvbi}`BH!juH44)~} zpGUAB=PfL@jEq{d5=(j&-`H_;>XP?O@%7n>@n2AMt~2hWvnMdy;+?N zK)HOH-Jlv(aL}xO(0LPA%x1)4j>31BLLGY4&6L}Hm2$0L40A?k{EMC^hrNDr_wbVC zdntnZ<(4`fr*|#RxV*o&*?CTTRc$vrK+fXRVPoyGXqSp6zr}JTS?XG!k4wxTZ+1U4QIr0T3V$BEP_4hx%za=c7gP;Dh zU0J+iB-IrNXN?) zG6Bo0P}?qizhs=z;BQ#syYh;FM7)R*C1tR0%I^5s0c;z~hhN`zx$u5!45Iz>1G{=} z9Z#OL+ke3xzhQNGuUkN=V-_vC`HATci>Bm^`YfP$eFkR|^2 zhHejLjicF3<|4*vX+V0+Aiv@)%UZija0}x6B3Wnxr=}y&W{7wqzJx%Go?-Iq71iwE#uGlvI5o*0@tp zO>es6ESQ9{%l5!8{JSCi3|X*ZDk~#MMaaHcyrkl;^V(SWsvI-v#XUEx^EK)2iH)*) zk2_4Q2K=QEh4JIl!>VQ8Bbv(~pErr78QSIxxX088v%ttTO6L@8TcFx7MFxh+>RK>N zD+oM-OoNi^4H83=>#QEX)dj@YrYMe+L7MmO zks$D*Enhmni&> zu=|}uo0j_pnl)Y<9yIGD6=N{QS9Vm-wH)8!p9Tr%XX7!4&Ce1Q(Vi9uIP zOklYQhZNgg9RbQXhu+1FvO7CdQV$z`(w{1K?IarhdA4qF4KORO8}VgF#P75EpmoPE z>uT{(>_k^*5QoF`WCCSvJ;ceF^z$oQ2(T_Su$WZAyw=6Q($*cOVKOk+d^MHBziVn5 zh7jQ|RaOs7UEc!Uw$f2fj{ob<76TW1E!1T4UZ0t~?j@%1-eEQSYHHhT$w=8=sg3a~ zi3cv!%Mb#kx0g5Ej5cW~PQW%cZa{v+Kt$eOeC#scJDd3ye4lX;0cUg z+317#S5+ZrFz9o8W0eakqCnP17duz^Z&*eLReW3+c!`T>+9zW;O~(rkrLUE$x_zqs zZDzfn&j3!!)w$fdrD<{KAl56>6b0pz80qR5gg)V}adHzI6Hl2j9#=pQN`90L+~Upy zn)evi0J*??HsdaGxBnLKE1`c+M7A_oikYd*@RTAxND)uwx@$JQyI{fTh-*el*6EXZ z_)eI3n%_W_b(QJYqvUy68_zfw8E&`Qgu%zGukOHx&nV#6^>?~w=5zZ=2fTQx#jh=0 z06$;rK~+mlUtb5D1dk}Dq_eA#oDRO+Jv-q&*~Bg9T{DhD?jv7<9l(&Gze3>QPa_D= z&X?ElXv!2~x$aON+g8Y}Q?-4XB{|(p?Dla6j~&AUqX5M8(=-B4zJ;KSmQIyV1C((* zVm9iQjR{Wh$wwUxYnqRuxFy!+k9T(MG9|YC8hK#Zbho#&$=(LPXbpU`;MJ0!yk6_& zILz$d*4Go6-$kkb?HGUFwh(ckCD+FC_t`I2@-PlZsOSu@dHQ6A-W4g3)he&}*~|H( z!Ot1Q4ijH9=0Z|M^Y0>T)zV2iN!qhMu(Du94!6pGk@YD^@D9-T67DQUtSar^(!6Z+ zc_SM0Tv{imOP2-uPo&;dtjz-JNb98ziOj@cX| z1K8R3@HWLpR(_?uKKbI!{NB6j*;}K&zY;8^id!)%n~!%Ne;-O#k08M*j6-wEDvWoi z#M0apVp{DB2+I3bhXLK;06YMm4hqD>nU z_3yY&+w+F*l^PjU+#dvi&Q8_&{1`L7-uvW+ZfX#0Y_$j!U0lPTg}3L=P}&@~g&dt} zRvT@aG~Q#*p*m(9=7A!|Vc{qZ-G^~&vu68u{_z(8g#Var+4OtqSd9md)yUK!H>TlH zc+TD7YFTY-lCN)|F?qGwg0iac1pdR=^|Z*>Ee!*O3_eH5VlY7&|3}ezh9%j*eZ20n zv|VmZEB9WxHM_GMU}B=;*3uk^K;jlhnOd%fiWaWF1973L3>C+nqjD#%1koJ0_n!6e zyyrNs4@u4*ugxuN{CL@0YRLq69JNJ zabmL#rJ#;j{A{dnkljT1o|9CmjyrH!9ia`szwE#(u48EJ+H#PE$;S-M z)I&*xS%T;DL`EZM%9OBu#B{OG;=DtO)@%TTf1PoDn z^QWe8o(4tDXM|ynjdql;^}+DoV72q2P(fJ_RCC)p%gJchGd^{>#$46#1p~dEdAdyi7ek(agDc2uSqqFL}GWo^|I6(#zPg46Ka%9~PVd zF_+4r7FM9)bJ2cVT{-ImGz{7e$+iMT6T3k);S+`f>TeVTCEq50BvrIT@Hw?(*M4PGOl%CUP z{erwVq{a;}zAm>U32!U!|F$gy350r6(YDfOchfxhc0erGclUYpRoVQ*Z1LX{@55Ek zeTMngdklZf^UfU&KI|#~*>=lrXeTrI$~361?%h99Hy#ZiU=ZMQtO6GUi9g+))_(TZ z>)=3X&Z$yxmXCEi$icr?K1YS+v-rd^(Q#LA^HM!Y+olrxTNPEJ`++P)fNHRN?|tn?ZsuE$vIa^fK$fIXx`r_f8kmn73aja)DD-PF+g!Rd{Cvnxi0Jb*>e39Iy-eyM>lJxAxpaa}g70fN<;84&;Yyw09QYI?`I5?>nEwNb&kkKNlIyStT#(O*MX=DfZM&_KP{F{lZv-dE%rJ}7T z+!FwkHGC(^oemnDr^5K%;_Z$*bAh z>Ufh&75FQj%WDw>e^0%xs3&x91eX`4^cp4f=)Q5%A70qfO{P75FJXIDkA=tBc1+xU zTyo%4>;pA*@Uh|A?htN{&g zLM8GGVtFOuF$FGjviw1e*WOu)&&W@>@Toki;(@Cgeptq~EaCnAMuhiQ0$XL(;k~m} zJ;a?%5CGd2^azkTB%hO_d_esco|P55vm_SYJy4_JNa&B*)~=sFj~Pq8^@a(NXrcZy zWAnlnQ$7IwF7e#cQx#cP^m4*~y&W}|$em7rHqm*ME6^rdw0-orX^LyJr(A#TQpxU2#CQ|4s6S=5)My%2PJBL8K zO@d#_ekc|R^IgN|^zP15Ef-;Y*=dd2xGs4;6axm|YM$`K5k%6{-Qs8>#vzybC0-R7 zr@g9H`yWU!w!vp6E@^Tw4b}YJB~J96kh(S0y!EqGWl+irnh75n@^>zXCitX=(`>Om ze}b3}hdWVxlp}}}1-^1zJNmpn-Db;F44q39FEeg#cmkHXVQeLQ;YSi@l*|Wne?tw~ z4!kIGTu&o_0B14};j0JRZfRqfsWb;ii6P2TC7NO8u+V7f>8#QZ>;oURrKuC zI~ZgH`Qnr?97HgY``cGG=mWrA@#}?>e&zk{aRHz+CdRim?fJ?W=8_n;h6P-`w^FnK z3m7_b?YA4UF5~idh%jiYgs-8YXnW`J6T;dPwDzs5_n0M^x7WIa{<|=9TyonjQ3Z0L zqPi`XYC552d$GkTIvA?X8mNg-pt>$M{XNwkXo$jSPILJK*X-^o9L_W8)TW#QN7Lx( zj#w#d@2si>n2?!>4t@5{*f{9GsuQcJNO3DpMIGRw<{t&XWvk=BK9h6#xuX$C_+ePd zT`US!1N}A(*>Vn~Is`Ltm2PQt$p%CUxgdPu(v5SWMF3M_)3<9u90lj2GFnouTL^=R z??m;-8hTX+AV=GcsS_UXUGi*NRiOK-v8?f-tN9Krf2gAO1X3!c_AvkRS<5AJKOIa% z?Wcc75*5UQx{%r?pEpRjD}7DpKD=zdSw7U#ALA;X0h_NsVC(wi*Koa~ZgTYFcq`ID zYIU>OD0TJkDU7Qvx=<)db9l4WPqNIpT`Y!iQ+QRvUjq6WdurTDIcw#V=_S9md@J!+ zb=t+nR!J$?G`H35;BW>kLW5CMX|7;G#CRN$GtIi1$b0G8$cQk>pl3|j;ko4CUB`eX zt*_H+U$5kB{rj;2eFii|H(e2T>+bHz)wF!Wxm;R7R_2OLyAh}?)%fGVpkGtY<{J&n z&{&hePUBRM_s{Qoz1H&vc1FB$mQ7`cA*f%Ej^nX7P%Qy2t z*?1-B=CNXl3_e11FR0pFPr7j%HCuMJu#Ptsy*Gl$JQ$}d#eb#%LXLRxBZ&q1ag^%h zrWdS#1i{`mKfbTsF#7dxc%87lb=5VC-)IKNIyYzUn0sK#)(AC#lhNR`_`KB9Q#2Cv zUSnW0cm;h_RnAATHJbS{Eau0Pz7_yy{0O2unau$FC0HF6`7n zziPLBChytyk00VHvq?+t-c|wZTqP}y4IT6n?NgcqQjAWk6u&AYktQEZD{a|v# zhaGVHV)IrmwEE%;oVCgm;bq)Q-&69{7u0VTtk;b?H*&AZ%aZ~$i*>+4rv)}V3>NOPHH7rqqTkaF1XzQfB;98CMRiW3V>M~cM4uyp zpA(Wq{%cDrOF33|ouo&%Vg8r6J8pL;z6nYej6_OFdQTfkzyrp{jR2q+X4tCV>(l|` zX)1M;c%0i~kJ=k;!RzjV6C2Z*Y2|gPjtl*fnvIF+LgN_3g`}-RzYQaTP9T?6^bgv8{ z8`ww|%jjE7Ww1HktG~i|xXS|%4@G|}8dMSsu5#4LD)iI(g;@SIHD+@2QrY`$%$=b!flo5s%`m@0dZ??Zj9051X`741u??4}~rhdaOnVEt5*>{nP4j zXcY6PZ*zd$2sli@n97*g7q>UjB9MWkhRS|L`>b%U%iYl$vm$>yibALmp5!C#c< zVJW;z`fu5fHEaH{_kunNzuR;pCP!4=%cWegP}-ajDO{&VDeB8~DO(A%>WU=r^4mPj zmhaRIGKUq^R5L;*B5ttK*{9w3V?$57r_m$k=`xr6&Cn86&YV*WZkG5zYL9)BD8sPe ztDnvt*PKUOYE_FhH7=B?*WjshlJr`04H~el9^a!MmBlLXxjxt&BsetS)@zMW9TT(5 z6V5^-Q>lJBJ>V$*?hdt(gpTmxq#+NP^E z@F2NH8KVvdzcjDF0DAZ^V050gTHe$bA^ZEqqGiC%Oj~fk$r9sHMNXN?z=-h!c>aDP zfl^d931th=k8U^CZrMzyePKcazF^Xb1*~ZFzll00bL)!wQJT`fN>Ok0>!Q$dABcx2 z5^rD+#dg6<5M|ERl6c!B>@{Rz)zewS;w^T<-h{)|L&)pfs;m)~2SAb2FEQlobhw&05ecq&YBEwI`)oT+UO&f~*o zZKGIzDeNZjM3k#bzY;w}+LmmafKJ!+lTDFFmnA2{6_RgeuF!eqz>lYR?cF)D-6Vs# zodv=KI=-RWe+Cq7CxFzL49*Tc_Kn&UO$+M)Rekmn_R!Ls$TiA+S8`!_fy^f1 z9@CM8VPC({dd%P?HAQ{(A2ZHQ4$ei912?g;tv@z8F1bS=`jjDfyB*uS?Wa*+S^nvj zN^zHa!B0*m6b=Y7UDm__g>gr5)sU_wUzq;gL2=Fd+9xjio**GWH5Sh zTW2~hfgs2%%upvpC{+fQ4oQrkAoGKCI(!2I8y_ge#EZGF2t^bSr?fkDzxrPq;7{S@ z03C(oTL;W^zDtt7X)K5Hxm&=i<7ObqgOi4nT(-g*BY9`myvIsuB%8jz`gi0oI%9k< zZ0BGev5-!q12o!)ll9Tv$TQzV!)_?R4Ml<#GoA0|x$EVp@~?Jg_fZN?;oi&~whJ1U zId!fW6=8PFOVAyRM}JImcQB$M1}^Yh@@b)j0rTCFN5|MBwXvm%DiEoWaObT-yHvA-80nF#Z+1_%lF<{i}PbJCY!?j|_~xH#wZHM~LJdii*EAkv7XR za&;CCYm9K&9e`ElY#@^~`tZmLk#*cI1V@jIG+KV!US$%OZiR|!KE6Us~7ea+R;{n7zf@5BdJV%y^mPF)&%8aF4A#P?Ki z4<$UB2fBs_TLRZ?^KIcsa;_v&`Z>H$y1RmwNEjB%nJH!3 zFaj3*lNwHAEISqG!Tfqh@5?bwF)Su1q1v z+Jn;0cjcs6zpeR&!Xv?1%|#LpmZYh#8sL0d^XwfzOXO>Sy4~ zk))mHSGgV~No?g|{lu$l$F8oaTuW<_vEif|VqD%t^)^@m>=o;FH&@ur5Ul}1K|sVT z18G8A${(WBmo^#B_C`o4HGOHsP%g(yxs}=T_jN9v;bFwM2^34GmoUt>sD31mbpVB(V~wDIfAZILI%5M++dppkP!moHu}G z-l6D5b9py1;7a>@O0&I#Jzo>*SE{4l?+nI9h4d%E?XSe9U+L zLoX^;S84rWh?Rl#!{DgMhOwP{BLYRV^gm&TWXk;ZrdYzN{SCS&r{mgFrE``E%D2nb z|MwL6-!ez=q+Y0rYie_PQ%)A%Q$*U{n2w@s{i&Z=t5qEA2@UoU5Nn|peB=r3{1^Pa z+xNKFqL*QI?HseNJ#>GObmy%TLEc}ZFd&#sm?Rt@6TXxcGJ@EgB=~N{@y-rvB)v_G z#--n^bR!3>!(lgb@Lu3-M#qz~mj=|=&qpF}kc?bXz=++jz2hjoupFd54rERVAdYvj zC>D!z58(p!ln|D5m!LDz8|%G}=EdJ}Q}AI!73yI8V-^Qig_pf2IOw|R zL3z45&rkCMbxfG^=30D>yxQm0w`JpFL6?kB{9{HYFGM0+zLkpUd=|7`HvVAn)3qBa zaQFZF)M}vq6o7n3xys77P4jUjc<8+bN^@@KJRRT230NJ<#qS`np)vSpl7_EX{1?vs zY!?4eMmc-C_n(yiuKbgfGM*%7-mbSeT(7)&To?7AKVin7(zsT%2bU)V4YS4M6<}Mp zlrzz87$A9Q44`Mc_T;l9zutp;Ed*;uvXSRbBC-e1(-?q6IPdn~z3*|p%r>lSh1I{i z*25tDaQYJRQxE>DZZ7=}4=er>;D=!E^3LxWib!Y|-N<>gj8wFQ$e=NIwwmj~2~X0} zcbkjoff25d6V0T@tM*Nv%-M&eO;nue`mAbpe0H~~4IR({4oaY3{uZWHI{e4yPekCr zh*^{EJ&_E%vW4aae2S}LLZAG(OaFR~0Tgq9E4M#zT5Xwr$A39>NoioeM3>S9sV*qIepOfYDVv1ejxDrn}106f9{QAGt+=kkFy>@K%9s#Tw zol!qtax{_as;9M3hY6^gBH%Ul7!YDmv*X&{N#=D6*#zlvMl{#m)THBMxU~1F+h@Gb zb9{NALzt89;vcG{Mqo&z$h~dNXoxv<;Bexw-=XK938%}nbHXuqRa$;~Jh^`-Xhuu! zL0@M#KO)lob1xT=UO3Jm(TA5J{x~?zv>PhY5PKr~ZQGIRRpE6WQn5}=9_%;6I_ZVjH{+VqX62`g$I$tEx0d%8eoA;%N9 z5!TPRQwVWmqfV)>sZXOm7_gj6HCKe8LwErlRGEW5Bamfr&~S*SIGY$3VvLDL&`=m$ zxu=ZWoZ48hMft|O(z4VVOdGqCZbDAcac=Y7GV+$7zT^AQ*88!MF4^66@sS6{HgxpU z(gu=8d__&)x`-uJJYwczb5vVrGRbmW3#_2q`$x=if-G)<`Y^3~AN~4E?C)dQ#}n#( z)jtDruwmV&j9vN9562!-klC_!d3ULK_Lp#6x7RKTQ$7n7#K<3;qXJQ)6WacCBE zzj!jO&f2jMzoMui%ofiD4Q5(k#X2 zbhvv!xqP~=e@!EtUhWrxWd+s|l7dP*mo686{GLHPou{nO`wubMbzbX=ZHbjcbbU1D zD)gwfnC{9nVr#E#Unz~^wb31STFmk?YlY9 zV4#3Rt~GU{Iv|EiI$_*i>XugmpO+Y0S_w%XmwBl0Brn}7l;m_4L5A%cCOyK4|a z(|}v8sPyjbacdrVGL+L>(H$>>iyE@u%Avj3^roH)GeS12XRW^k%!AYJVRn5a+#ur1R&&kD)@IV8tp1!1 zN#AM{GAgf6uE4s?Jtj6s)r?A3$9w6C@ZRyTkXBu@e)=movNUL9Px^v!4qMm>Nf!PV zJC$dAEx{)kLc)$Jj$^e7J|6J%jSD6R;qUoqlot-#wy#FXH$R^U_VPYa@cdBy2;Y^Q zUz_T7ibqW%)38$R+!OB2x%+LW#hSq_=tE+qz{045E;N4tPKEDAUTAQe3#woQ|2_3z z%1Bc(A!w{jqv!jYxLS>jvBb^GEQuczFHd(k_q@EDa@t7GHbWSaU&gsb^$&7XgjbU$ zkS)WBIFOv>Kt;A9WunUu?a%BiJXB;i`@U)PF5T#K;k%%?T~9gMLB7hjaT!L3 z>E}r+;TnWwaDf&iDUB9{kqll7ht`GLI^wD~gPR)%z_Hp5im4|oAHN)Oec)|ueT{== z(+CDzwSdKR?%TFbT*1{4YnpT0$9WrvH9B@aMG+e8n;HmAEDrLP-_w0$%bvsCbY zz>O4MswdyNYM9J%L7laOy_*dR?5#X|U~NUaZc9@g*wBA8_x8JI01N!$9XBuM@AZ$HJS%l(w)!b`xTi-zI(H0j zu=6nwZz#0=IHk>)8thspc_nqpgYk|RA=$b%ykP=9Lz18Zzbp}8TG4?+)cLO`L%}_b z3Xx(V4mydLD%CPu3|r>Qu{^-qlkLR@kyrc;)>d9LsqA}9I^Mbo6H>%s9lqj6UB?*A z(NXVR-M6ZCeV-f_znhVz1TlirHpwoJupVkJXDVCVH?#O`K2589j<{d?vj9|B07{6J zp<1Dmye{}9dGM@xz;&_-QRsozxsb(AH#K}xlL5L_$)#IEaj7&Xj5Juuj0)*T*P}kU z2>hH|@69ZR{Sn%@6;Hf)a^9+DgQIvLP{Yx&>_|O52lwOMDJ7IT-XpTXpgx;M zQpW}DJ5I{)zj=g&>wUGwXj||DNe3)8sbb@Qf{QH<@o^bOGi&h7ssGRxTpwDn< z$pZt7=b?3FsRh2L%-IJeN%)Vfs{ZZ=M$+!<7@1j6xGWja!YK_aHp-!@Xr>{#!{E(4y+MxgpNj@rsDX}xG^at!Ca zk5=m(JlUTTe~SmJ?cd8R0Ixquu&fCquPG%sm8ES*q=~v_>)H3m$hBAk)#hyObdIHf zgXZh7j`UrVJfGal$9Jcq&fCF8{G9BV&YHNAamR(L9I@GB=~ZzF<95SzfICSzVVPV@ z`Dpu(Y^u*lcFbfr{(3CVnN|YCJr0BpVz|C#5LsFO%Tuy*ewb0{6gUzv+V!TdMo9Z} zf3hi%XW?h^eW+;Y1AvRU<_^4)h+{>OwlWeL_$TKhxuK#mBvSeaq;^Q(hNG>D$pr9h zKvVxyfS4k1wGU^MSU*nHdaCigsKE2GLi8(T@va;o>(V12C9?!v5MG@EC{4lk%X?>Z zETRPw(4@B6+63N3W1PmawH2Ooa|_o!kmR;Qod6AwWX_t4{h8fz8dR@yy6AZC%8}*E z(mS>G%SJVZW&XEQ?0$QF9{0`4 z3xD;giRbO$t@_ReahX%L*%@^kw-h#)yo-k-DSU3h?8;&*{qNy z>23uSm9uzXtQWccnp-6_Z%ggMquj+_J_D``=T^SYR@Rq*?)spp zsKV83Y%|(^u^t{#uNh+F&H>iMbC5fNVO?xk(k8jx)5!k?=Rv+Lv zmz>wlQ(TJCP?pb15o2$>(sA7Ec)RiMDZc3u>8^7bAm<%s6k`HKfh&#q@4&oSIUSs` zB7;?hAB*}$=RdyB62J4IPs&e%#wCI=EO~-Q?3XJFr6cqSEw+BuquY=am!M+}qo-B8 zw+n}Go+-1g-9x*q2u+lk>&J6-`OtK$`RlDdl~Q;Xf!XsF_Bp$-HbbpAs{?s6ffsV` zi-KSA$=I{((R_iMntTK;EO4L!3J-%g~!>H zq|j%A%5N}K%|Ehwdu%jlps=2~((Iz6qqmm0TyZ)l&PIV#;nfGdqD}(CUWVu5?;No{(lH9Xmid0=1B7e+kgfvm{_f+6$^ZJx}hV6*HjoQIT z>`jEcwlJT|Ov=UjX z@I)pmEq{5ag24NHn>jlET@$9I-y|Sauv`T$EbU8CD=+9N#e;pEGKN>>GkaEU6*UPT z8*hA%pZFHkB(@>>r}Bcg=ahOj{D0TS7Wq7(761JD>RqYsaYN7Zvj<;Q{##2sDrJH- z+N33XGhDv+in_eaz!N+ts=|6N$%c7^XLw13O9f({s$BcZt~gI~)py=V!|7}pnadl5 z0$^0~3Uy+6zd8QcycpKsj%R*{T|E{LzBNxt2Pl$G`XPX!HN5>>=;@^PrXfY=y!7Wm z?(+A-JtK4`PqKaPX6q(iJDKkV9`}7_I+s!C{d zY>n=Y-zTVTrL^og*37#Mq{*JhGtrAIbeC=#^y&IL>!$@uo(I@PCIjlA#dbP~4p+0b zoOQk6h~e8`+E%Z(_qgtkj;&70s$v@m^~ldr384NJN6banqHD#tFTuYSvKc z9s9;RJavULV`=Q%*FwZfKC2v#yc_DxUJdF$ENu_H zo-(~szGb*)5uQJ}R^=y>1Pf^^Nqcx}O+0W^HFGw__5o|Cr65kKaPTQ34T0m8SUp~u zFsY~5JV(2Z>giRIz5Eo7(#QDmbod-RJt3KO zF)eD~cr*#kKHDwSnLXU_q+Y=qj6^6^c_?=3V~xusC_)c^Ag12}ecjlU-{&{(DilM^ ze@Xs0rL=zuBF-Hhu)!;0C*PM2l=YWxuFoG@BQsOzPJt={9s;0i0gJK=kjq;;OY%j{ z^#kvVHnv5sw0yT3ajQz8-4p8|?4a98wDG`q%GJ;{^sXKK5OxrY>uEVhjmHJ0d7Mqa z&u+hfrCtxwx*ExaB9~_=(KdHxBg1}eZFG%VfmRFB{+C(e`9~h={oOIhg)qAH?7>te zca-OXZXo5YMzS6#hi1>-h`Cxo-EU?XJ+2fXqBv{%{xSNmGK;@kqMGu=`=z*Qb%A`P1(KNhNK1+s4;x8Df#GYK=Hd)@mx$f_^hec*o zhE%r0anLV`6U)>H5bu|!O~g}wuPBao+>ex&-#=z&>D>Qx)n=95=vniqU3QVa``>SQ zH%Y4tjI5GQ9-OSkkL5*B+TtjMfIpv4-!p*4-!VA@wD~e9Ygk@tC>MD$9k4o8SDtBJ z5g!0pj7$nCJ+z2{h>AC?w~)q!5Q)eL{DQ%q#vzSl(T>&O%|$!k<(a`dnnC$?X+|97 zAKr>fX$L9MPay9^->$NB!i}*;bL1L})?e%ZT(0iRm{_x30r7fV=b@6h|`M|3y zj>#@(Je8zUhGFK$7e5vGOXt|7Ol13kU{)8~>O7qvUVMc&EK-V**8b^3%&&6*`Q*QQ z2kIOBd+N8A*StN#Bs%UEzuuJ8!UGvJu%X{+y$z6a*YHpMbVA&)1lyamo^)-cSkw(0oGnh2D)P1YH)ZoS} zL@&~4!}aZUVuy794#5miAJ^LZZnI@~_98~-N&lNIC(bcd4#VU+1D7%E=K6l=poqiv zO(-M@Yn7~v=wNDeX~<|iPp``89JwdA4Rv!>61zYQt?bgL(h|Yl5&a za^}@H&#MPX+LFToKdw|N?~*I!7uB5OV>ZV^_cmp`AReO^jIQ?fu=o$=+xk)(&vRP6 zZX+($8mjpF?`N`D>^JFT^>*ZTZDX=vuQki~y@Xt?p+V=x6xCRoxzu?5IJhaD>cE%3 zkKa&!ypAB@q2u}+8buk3L_=rQz%%y>T3@WdHL6c4@PMC_=Qt{P+~$* zP(M@89uG5{@B7u-Ce+s^V$v2VGF&-FA+ArnIqPt5IDNu%aD`)R;Br@l79H19@1!48 zs>&n7bMW|gO$73)r*^lmwJPtNM#uT!@`~vQR`+&TnV!5u-|}%K&+jj_uo0Xww!~5J zJR78()cA`MGAggtG(zuP1s^s$?Th?gF>Bqs@7=JWH})NQwtw+1Y)|X3F!;d?S0GXouz=cIumOSR2ux>{E`flyrA8|#8DxCY@>oF zDio9gGC4Mq!U#RRjiL#3taD${zUS5L+VY}?_+L=^<=4RC-)&`g0dMV+-iR3;nJSmb zPC-$nRaT|BHAMw|JO_?RPg6+^dqYs}ZhVKVWs95-k90gX%?7w;yu3UI4q|g-18Kv1 zb!&Vu-Q0u*j{K$ioV?P96_jYAGEYSn4(2fXF+AgtI?;~yzLHzQ_$u{LtW{9RY26H3 z_rSyVtX#kIitKf4RSIity4aeemAUH-E)`p(n@te7IfE~P4om66rqDP9nRtn z_W;@`%K+1Gvse_PBlN?3SYuzJMsbb|AzV%zBBp%N5!Y~8Dl47#;BI@_-&1gL(-qYt zlP=a6N3zG;QaP=GSv%|(wzKa5YVOdhn$#48O=(Ijuqa&&rnOqOJlWMxynz!m2)Agq zly0zIIFc&ZLOKRKo(+dkT3wo}Ga${iLWlzIct43SUt9kBu-fG8RSsM%sao9)^6pj@ z!=W7djjIFM!@{zkv+q6p%{t8);4gVWB`KGfLQU4SOz@F4VX`3OYj!IF;1IbR$ubW* zfQlxc%Vdd7((F2DYQ!!=PnUTz5!Y~Mvd4!O+|V>wC2lxfPsJkb(K7+CEN(X0YIYzt#Wg{wwN5cQyZKZ`I4AT4(lAhN& zHqKNqCp1XN{IS0NKdK=qoonWGslfG|e5CJBD^Cmdnpu7ynkKg0Dl94E7M|DJl=pjTk;W9cmv*P(v#VbQnC^qn2sF$E(5=tWIe-0QyYT6_;LQ6@7crbP&<_VLbbpgkh38B2;Q3!&swH9r9Oks|wg zP~f7Z`s#sIN?Ka>oaY{CTN>XjkY&)aoW$V13TYyd-A}3m$V2NxT2?8x2QUXhn*Hw2gyUx#>biG9S~V3 zHLnp$a^(dDTcCxCMVAVP;)RK0-;Je(bZJW}EwC2XK=xiCWuG@~B9j{5(M_KXDT=`~ zuKB36)@$QAKHB{H^f|ivsJ45KhXp7N32`7gMPzG<+O9JJ8WOZH7(V^GqUg;o_r!pO zawBc3ELz{lv`%VuFxQ%HzJ`W*Jb&s<-z8Kos*U;BzLy>~Z*M0w$np|M#MG|qQ8lf| zn=Zh(pa9d+Q=Ye3Tn^KfpWRiphf5*YB-w9IlBBi2DVcBCI}>x|;L={muw9_bu9m{r z-?GDYK2A-;SrG+coe;-5#Dv+xzLwIo)sTjKV*E4g;X>=fCQoktC?B+SMkl&B&wFL= z!_!&Yn654IP51+TjmJT<_^U8UK^>jqqpV$5_*WC`337k_eJ0Krld|iVF$zurr`hg# zNy9)sIs^o8f@l& z*6zjuIp@h{Q|FUZ*E}{#tup1ApN(@UB>e)fq;fd~XvJzAvoT`_E`^Qd7(RX2IC-9} zj>&Yf7kTF`<(?$Vv_POr9?K+XVE@4~YYXvO$fsw@do|+*w4QgB0{7=u>)S1Z5-i<5 z+-27w{)RaV4=xr`htsg;WuW=(k zZSa=5z4#d@$-9%F>O_F9+)~KKLdj&5MMhvJZFcUPma&G{<;e>vw)71Eex!n`)Ug;5 zx?%C#tJT}!7IVMgald3g$yvS)Yy44gAW(RRSeIx+i)5e!gS`U%{Li=oP_qxzFZ#ZI z#S%3;k#cYOY%G+1i{DtgcrehX&d{!Y$Gg+!nJfsDZ4Z(If&NTIJ1YkZ91mM&&1z>M zYbxz+-c+a6c>v!n7C;axuVEO}CD@+B_H=$Qe8N1usV~Ed|7~l#oQ(%BCtcUX0~UOuOf_dFWj^eO zi`Qe+GX<~Q0jIP$Xl)h7fBI4SL*x}^mwV4Y$vt6f;rW5{rALz%XWjrn{^noJORTZ} z(7w-Ql^qI$R7t@aRn zH}zSd^5HGck(S~?lph$>>7M{MsZ?5W&dCfi%!7p2KE~cZ=Nh0wbe#7lY256&;JyozvZi zO^^x4(YsY3RQ^~g$l{2GM7FMPH^%pnD*h*kASgN;UVnwW1};v4Lr$x-vd)}+(R{l_ zq|9dC66x@QerPC1XP7xQPT(Dh{%FAHI0{qgA*&MrI^6OhPk5o5YhItc!u^sQ8qYmr zS8YtoM29}-19^xU8&`eA5v?!p=@)}?wV)*GrMuGw#Vhy1?F+uFDj%#X4y z%|qojY_=ddUVSq{(^{Y8aR{mB0YH{XS^V-oN6z+zt7|aV(x9pyn>uSem*&(!ePo%r zVubqmAMB@cO$R5VE(t_J=?rFb<|WmL4RkNPj(8mN=!uq!fN;=3E%r&QeWyI;+XYK< z!fqHTh|M@h`iJ84-v<)%_iPB>3*?@?46_5J_IrA*KbgsHUW0+&ai5FE0XJ2g%l(`L z%IB#6EbW~$zN{c$+b*F@(Q|fs;}R}+*kfl{7K92Wr~HArMz~_DA+F;)?ul4-DMe1V zRw}=6kt!y8tD+D`BNa`E#fIP97hO6; zKl)!Lq*1uP!>^@;{Em|ujaGR4wp7eU7&f>51l9d?4vfOKx!qZjC^-T-0G$#xl5A2E zAiy4ZXjukG2v7d+TlvDk?2DLkOn%l7%LDIR*@H;`Req?E=wr!bGnD48_)88#HdE(1 zpJ$ZcmJJTpF|WN$d}cgs_o>(o3X-E?T%cxUDAX*n02Lfr4VR(Q=5}cTp*qhzq}bO_ zw@j)B%*ji)NUOOQBDwdYzKeo53M z#9Cg}tv%5l*$7M^fYn8S9}@F)lUq}<3!~vLic=iveJ*nupQq)Xt`isgleM(0(zn2v zRB%&*p96{7oC5fDq;@o^+}a#_|HA4Hle-0LOes4_nXpVhY99_dJg#91=Gl$Xy#E(1 z^IPq1>*YeeeuX#aa$J_ye3n*gom=O)97)z)l^X*9R;YfX(f~5WwQ?^KK%FZ1v-K`3 zT~j`uqj4UeS}@@@rUT(0X8w1QV(D{euf-~3m@5>9^2d;1Om~~w{$=yZnDW5Owx%hj z$HMr9L#_nRP(Y4U!m#tev+yq?|NQ3cx-D$~+TSKGuGOvd+4?UhP0`dWt7JL#;p!mN zUK%z^FAxxsThR9V%YL|qlcQA|qySX3} zWu&q)$=TDF8gxw)uG{>`Kx)@arA5b|D zUe*kvY`3@OYH^!B^Mdx(7jixA&9Y=Z)?= zjdrh`lodS)*2)#BIk@ZxKl2H$>G*x7RWRK6%7IFJlP!GtkL$H*`N=!c+5A_0A0e%B zvb=&u9<9OE_gv_DtAQ+HUOdKb$Tmq1s0tuaAUlW?#LB3=Xy|3;;`l-`@65X5k}!1Y zZ9D5z(jMQK!bbopx+O&(;U+QTVPLdb;J2It!9P%N6V$%(*`-xWOu(A$n#1&idi>?}&-EOvYuy zzo**1&|LwU1c#Gy3BHU0M}ZmQJG<%(6`6#Y^29sf@Xxm;gbvFy#J)~@NBA^8wk|~j zKd_QLgu;oL2~JtLaODW5Ry`@jKwdbz(WB8-qkp`lpPpjOkrO$l9Ajg2qI8#q67=yxg2Ji2oxHn8^v z{#B*#J#Co*KWxlFsi6fOMF)bZi54~?ON8u`2}&zC{l3C(Na;>#$x%w`u3zSxnr3Bv zaW2V-fZ?U7dEccd^4fQpWUg6N(>ftZH^#qhkI#U6z;|us=V)&m-GyC+ZfO&fTIn?u zdlh|A3zsxiT6J+x)k@jx0zkjj)5`m!dI4k4F4dabN2+fY$N=HoO)BS5>P&=Y_Z22(OoiJ>^-Dl{<)B9V_3wfkd4 z3Ivn`C946AHwF-{_f`WaIo$oz6MEn8wT7;~LLekCHV{pBQgwC7hpO?fd+-xFPZCf0 zpop%3_g1Hme?o>jl!=!N?9LOESnJ0#sZN_q?p0UtHM2m`9;##w!;Hz6`%uiXmOH$> zkf#$t)lWBWmREI1>1P(F=-siN-w4XlX;0-64Gvn%QJTyG^s>n>oYfWaoe}bIe>$8WabY>tF`!*iJDmXEFsz zwJJl8t}Hhg=_^Q7t^?W5BT0ZlugzmtQ4g?=mq==a51jJkC~&re0q3F zc_a}0doI3jIn`AE^R{{!^x?{bzOGnD;ALWJvKGRot5S2V05Ke9NnMl)eKx#T(_&?{Qd zs`1mw9LJDh*M?@f&YP=TFF7y0a;icW#5yFDLnp@4+ykHQftx7JAZFM}&ZE@1up+uc z)SDu*t7N6^&%^J#A^Z0y@4wv>Ds+*|uurF-t5apzPke_7ner(-u1>V20-{1%WnYwP z>piffKBk^Fx%GJj53zYBLZNS6qEQF(Uumvel6cK8nu z>|&_+n5ARqh4|&6A1{bYU3>c--zlZ5)hcw~H9aXu zk~+riHFtZr*4|5V#5_LhcP+&CaKm$TZ@rT4MB6)Wg`{BSFD^2rkbI#PKpRvlgcajb zsVdfuOG!oDblbaDf2FjuyGLA-jjWe#mzVYPeb}0Qr8VvC^eYXmlG?{&RM}gkZ7W^D z<~z;id8kRz3deVbNA&N%a=`^l1|AkTYf3MySa1XQst#m-t%(5OY*%h zZT@9f)8l*Tm(IIWhLAGcIAvipy$=HfMxd)9Uf>2H>&C|3IJVT9d#_D3O8sqb zHuOf5oTbQ}z7^$r^6u>RySCn@+d_%N-kiUjA)N zLT)5Tp`Bt8q>|y#usD&K%aR5Z6=8)K;N_d!c$%`DBHFW3y`vp@Qnz>aZ{=o;VMd!; z$8{_3)6uWf=dpiL^5ko)nPEvJMUQUjCj*r820}`t>~FeBBrhAlCwC=E)fDPUPD#c( zS=lScYb`!|>#UXi*GVYe$#l`A{{Rg$`;UAu9JCj9-V6&I<)N?z1u`yRXfHd zxs`&Qy+bSf&6YU|x@CB((s;gUU)jkhU+mtWGy7WVmiFzT82a^HN>NoDS0%JjTVMOP z>DK-9+E!w)(xoV)M zK4+}sc{QWgnQ3KhlKx*i-qCdz?vlGn#oZ^Pz5f8=`fKTFbnuk8^I?_<3fjAQL`Q}T z6|j*I;jmB{889$*G0D#@h7u8zl6yN{dTpxL%XOyr`#I2$EL4=Gc}6MS{{VTb^w+PO z-ukkG+?Kw0Jd1Tyx84fH^PUgN#F8CD65(7v*W_R*71Ih-fvy|=e^uf5*A zl2$saH!2A>+K$QE-n}l>w9`(O(80CSwB2uANH28zB3UG1aTE%-E3zP{7Gt>KLpBNd z#^M7I2HspvI)7Mpgkz$Yx{}rJvRn83uGCCDT8dnr(sGPrc&ScK&i2!)R@rT)%Xxh% zsC+f>t(U+lJarYd)A)if7~km-O(oo)XNJb!^UGLP-Y+F&+`oD5O~L-)PvTT?P>RLk zeXV&;om!EEWi`m6uVu=k7VX`;`f1ILJVi>GrA$1zT_uN8O3M7)x4$;~ulNgBFPF8C z6Ka}8qfHCNb0b3};NC^&1ur)UsT;z(Bf;|bZ*jS|7UOR=WnOr6-8CH&YneA|!tZ9f z`Ag+y&w7pug;-8fQcX6OEW35pJ$`9)v$AI8mGf%0-fi8zv2fN(3~3~I4-~AyaV}9y zC_7N9`*KSA8C6+V!cKJ*d6Xk1%B`zczsdS}^>XuS!9uEv+uvHK-pSjd>XyIzZSE!h zm2Bes=8=4+1Z@yul!eMVwYw{;XXf0j#{d(BQBf^9KBc6mabhN8);qAx$}#5E^2y(4 zb!%<4yuNMT(XW4_>2u#klh|5KFh?BM3$>g5CTJE3WaU61SpyHetUxW4u-JJ< zb)6_Gip!oWmAyKpqxrA9Q;M}(jjPGKHkI4{zdqM%Nat$0Rg88s+cZ9Py|D9bVYqgb zTC0TfE$yXZXNKNDNMw#gd~|R^*DXp=sfDR2#Wtxnud2PG_G-@CFNaS{7eZ2M?ow{s z-(4?lt=V+*(50wp*3IF`<^iLU-7YTC;yzonky{xNBz40}2q$lp?8~qKF|-=DgTqwI z;mt}ngzpz^S2MHSOMX=L(f9i$I@M)2CpOxYqpRs1x>-KAv%b$usNrAUSzqY7U9)(9 zK(>2rJ4(D4ww5x(cQn|(SNlU7sUevpcw(KAp#f%H(D^9xBXZ;D(S`zyp-P-58npfR z#?qFr;@xXyvRdnXPL67huC;1OYSdd+jjY!#l6`yn-*aEXvn|z%+LOZHZ}4IQi? ztgubAZDv+*k{4!?H{iL*+CEm(30iYhoVi=GUkd2@Uz*pxy8Papyw&Q|RpV-tS|_{L zWo>QV`~23Nx8~LEEgYzd*5WB7wnm2L6D)lp8`Baa#~}fCIyf?IUo$xQzJ{GRH$qM+ zzG+6z_OiQP@58>kYxb>UI;p9)r8vK4^-`7kuDV}d&&u14)2!gsLAsaBh3+;9Qrc-@ zjf+jUaX{^s86`_@0kCin4W2Tb6O)WoecRePE8TWczK!V9U3#5VTWgk0%GcIfzsp@e zsb2Balf&A5&7|{4Zk8m9^}fRe%iCE$*!<>=WQq?h*htps)Bg8#rKHRJ_^$^CYSm=w zB`#UcD(cos_Sx$E?-%!SoV6D$0dh zZO@)~5v~-1hodVgDP1*ernypH=S8)zr&jOEuYZ-VbGrj}oMh!B*0+=GzWP7>3-uwi z(k%5D?QSGihTuyi9(A_Jn*u}o;)NO`aCd*MF>V+_yGkW_xO#9{w>x{*ioM;fD_>2X z?{(?DdX!@4RlkCVTqH9NvTRHtsR!SwX40A?|;@fMJc<#wA*PdG}_)?PWIbfW@cQ( zE*^NylSCNXvGS&oa%FM6FlLcmSRl$stel^a=jHPACogF^DM?B$4NC2`cJJ{&xpe8v zdrKc=_ za=^DNYvyg*P4-vizT0oF<+iUL*rlzN-X=VW>o%O7@ zb0moqKb;wN1UQXYlApRVOTJ}KnmJNpWp84&DA$!0Rk 1 and ps_tasks < 1: + raise ValueError('At least 1 ps task is needed for distributed training.') + + if worker_replicas >= 1 and ps_tasks > 0: + # Set up distributed training. + server = tf.train.Server(tf.train.ClusterSpec(cluster), protocol='grpc', + job_name=task_info.type, + task_index=task_info.index) + if task_info.type == 'ps': + server.join() + return + + worker_job_name = '%s/task:%d' % (task_info.type, task_info.index) + task = task_info.index + is_chief = (task_info.type == 'master') + master = server.target + + trainer.train(create_input_dict_fn, model_fn, train_config, master, task, + FLAGS.num_clones, worker_replicas, FLAGS.clone_on_cpu, ps_tasks, + worker_job_name, is_chief, FLAGS.train_dir) + + +if __name__ == '__main__': + tf.app.run() diff --git a/object_detection/trainer.py b/object_detection/trainer.py new file mode 100644 index 000000000..1c681e343 --- /dev/null +++ b/object_detection/trainer.py @@ -0,0 +1,290 @@ +# 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. +# ============================================================================== + +"""Detection model trainer. + +This file provides a generic training method that can be used to train a +DetectionModel. +""" + +import functools + +import tensorflow as tf + +from object_detection.builders import optimizer_builder +from object_detection.builders import preprocessor_builder +from object_detection.core import batcher +from object_detection.core import preprocessor +from object_detection.core import standard_fields as fields +from object_detection.utils import ops as util_ops +from object_detection.utils import variables_helper +from deployment import model_deploy + +slim = tf.contrib.slim + + +def _create_input_queue(batch_size_per_clone, create_tensor_dict_fn, + batch_queue_capacity, num_batch_queue_threads, + prefetch_queue_capacity, data_augmentation_options): + """Sets up reader, prefetcher and returns input queue. + + Args: + batch_size_per_clone: batch size to use per clone. + create_tensor_dict_fn: function to create tensor dictionary. + batch_queue_capacity: maximum number of elements to store within a queue. + num_batch_queue_threads: number of threads to use for batching. + prefetch_queue_capacity: maximum capacity of the queue used to prefetch + assembled batches. + data_augmentation_options: a list of tuples, where each tuple contains a + data augmentation function and a dictionary containing arguments and their + values (see preprocessor.py). + + Returns: + input queue: a batcher.BatchQueue object holding enqueued tensor_dicts + (which hold images, boxes and targets). To get a batch of tensor_dicts, + call input_queue.Dequeue(). + """ + tensor_dict = create_tensor_dict_fn() + + tensor_dict[fields.InputDataFields.image] = tf.expand_dims( + tensor_dict[fields.InputDataFields.image], 0) + + images = tensor_dict[fields.InputDataFields.image] + float_images = tf.to_float(images) + tensor_dict[fields.InputDataFields.image] = float_images + + if data_augmentation_options: + tensor_dict = preprocessor.preprocess(tensor_dict, + data_augmentation_options) + + input_queue = batcher.BatchQueue( + tensor_dict, + batch_size=batch_size_per_clone, + batch_queue_capacity=batch_queue_capacity, + num_batch_queue_threads=num_batch_queue_threads, + prefetch_queue_capacity=prefetch_queue_capacity) + return input_queue + + +def _get_inputs(input_queue, num_classes): + """Dequeue batch and construct inputs to object detection model. + + Args: + input_queue: BatchQueue object holding enqueued tensor_dicts. + num_classes: Number of classes. + + Returns: + images: a list of 3-D float tensor of images. + locations_list: a list of tensors of shape [num_boxes, 4] + containing the corners of the groundtruth boxes. + classes_list: a list of padded one-hot tensors containing target classes. + masks_list: a list of 3-D float tensors of shape [num_boxes, image_height, + image_width] containing instance masks for objects if present in the + input_queue. Else returns None. + """ + read_data_list = input_queue.dequeue() + label_id_offset = 1 + def extract_images_and_targets(read_data): + image = read_data[fields.InputDataFields.image] + location_gt = read_data[fields.InputDataFields.groundtruth_boxes] + classes_gt = tf.cast(read_data[fields.InputDataFields.groundtruth_classes], + tf.int32) + classes_gt -= label_id_offset + classes_gt = util_ops.padded_one_hot_encoding(indices=classes_gt, + depth=num_classes, left_pad=0) + masks_gt = read_data.get(fields.InputDataFields.groundtruth_instance_masks) + return image, location_gt, classes_gt, masks_gt + return zip(*map(extract_images_and_targets, read_data_list)) + + +def _create_losses(input_queue, create_model_fn): + """Creates loss function for a DetectionModel. + + Args: + input_queue: BatchQueue object holding enqueued tensor_dicts. + create_model_fn: A function to create the DetectionModel. + """ + detection_model = create_model_fn() + (images, groundtruth_boxes_list, groundtruth_classes_list, + groundtruth_masks_list + ) = _get_inputs(input_queue, detection_model.num_classes) + images = [detection_model.preprocess(image) for image in images] + images = tf.concat(images, 0) + if any(mask is None for mask in groundtruth_masks_list): + groundtruth_masks_list = None + + detection_model.provide_groundtruth(groundtruth_boxes_list, + groundtruth_classes_list, + groundtruth_masks_list) + prediction_dict = detection_model.predict(images) + + losses_dict = detection_model.loss(prediction_dict) + for loss_tensor in losses_dict.values(): + tf.losses.add_loss(loss_tensor) + + +def train(create_tensor_dict_fn, create_model_fn, train_config, master, task, + num_clones, worker_replicas, clone_on_cpu, ps_tasks, worker_job_name, + is_chief, train_dir): + """Training function for detection models. + + Args: + create_tensor_dict_fn: a function to create a tensor input dictionary. + create_model_fn: a function that creates a DetectionModel and generates + losses. + train_config: a train_pb2.TrainConfig protobuf. + master: BNS name of the TensorFlow master to use. + task: The task id of this training instance. + num_clones: The number of clones to run per machine. + worker_replicas: The number of work replicas to train with. + clone_on_cpu: True if clones should be forced to run on CPU. + ps_tasks: Number of parameter server tasks. + worker_job_name: Name of the worker job. + is_chief: Whether this replica is the chief replica. + train_dir: Directory to write checkpoints and training summaries to. + """ + + detection_model = create_model_fn() + data_augmentation_options = [ + preprocessor_builder.build(step) + for step in train_config.data_augmentation_options] + + with tf.Graph().as_default(): + # Build a configuration specifying multi-GPU and multi-replicas. + deploy_config = model_deploy.DeploymentConfig( + num_clones=num_clones, + clone_on_cpu=clone_on_cpu, + replica_id=task, + num_replicas=worker_replicas, + num_ps_tasks=ps_tasks, + worker_job_name=worker_job_name) + + # Place the global step on the device storing the variables. + with tf.device(deploy_config.variables_device()): + global_step = slim.create_global_step() + + with tf.device(deploy_config.inputs_device()): + input_queue = _create_input_queue(train_config.batch_size // num_clones, + create_tensor_dict_fn, + train_config.batch_queue_capacity, + train_config.num_batch_queue_threads, + train_config.prefetch_queue_capacity, + data_augmentation_options) + + # Gather initial summaries. + summaries = set(tf.get_collection(tf.GraphKeys.SUMMARIES)) + global_summaries = set([]) + + model_fn = functools.partial(_create_losses, + create_model_fn=create_model_fn) + clones = model_deploy.create_clones(deploy_config, model_fn, [input_queue]) + first_clone_scope = clones[0].scope + + # Gather update_ops from the first clone. These contain, for example, + # the updates for the batch_norm variables created by model_fn. + update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS, first_clone_scope) + + with tf.device(deploy_config.optimizer_device()): + training_optimizer = optimizer_builder.build(train_config.optimizer, + global_summaries) + + sync_optimizer = None + if train_config.sync_replicas: + training_optimizer = tf.SyncReplicasOptimizer( + training_optimizer, + replicas_to_aggregate=train_config.replicas_to_aggregate, + total_num_replicas=train_config.worker_replicas) + sync_optimizer = training_optimizer + + # Create ops required to initialize the model from a given checkpoint. + init_fn = None + if train_config.fine_tune_checkpoint: + init_fn = detection_model.restore_fn( + train_config.fine_tune_checkpoint, + from_detection_checkpoint=train_config.from_detection_checkpoint) + + with tf.device(deploy_config.optimizer_device()): + total_loss, grads_and_vars = model_deploy.optimize_clones( + clones, training_optimizer, regularization_losses=None) + total_loss = tf.check_numerics(total_loss, 'LossTensor is inf or nan.') + + # Optionally multiply bias gradients by train_config.bias_grad_multiplier. + if train_config.bias_grad_multiplier: + biases_regex_list = ['.*/biases'] + grads_and_vars = variables_helper.multiply_gradients_matching_regex( + grads_and_vars, + biases_regex_list, + multiplier=train_config.bias_grad_multiplier) + + # Optionally freeze some layers by setting their gradients to be zero. + if train_config.freeze_variables: + grads_and_vars = variables_helper.freeze_gradients_matching_regex( + grads_and_vars, train_config.freeze_variables) + + # Optionally clip gradients + if train_config.gradient_clipping_by_norm > 0: + with tf.name_scope('clip_grads'): + grads_and_vars = slim.learning.clip_gradient_norms( + grads_and_vars, train_config.gradient_clipping_by_norm) + + # Create gradient updates. + grad_updates = training_optimizer.apply_gradients(grads_and_vars, + global_step=global_step) + update_ops.append(grad_updates) + + update_op = tf.group(*update_ops) + with tf.control_dependencies([update_op]): + train_tensor = tf.identity(total_loss, name='train_op') + + # Add summaries. + for model_var in slim.get_model_variables(): + global_summaries.add(tf.summary.histogram(model_var.op.name, model_var)) + for loss_tensor in tf.losses.get_losses(): + global_summaries.add(tf.summary.scalar(loss_tensor.op.name, loss_tensor)) + global_summaries.add( + tf.summary.scalar('TotalLoss', tf.losses.get_total_loss())) + + # Add the summaries from the first clone. These contain the summaries + # created by model_fn and either optimize_clones() or _gather_clone_loss(). + summaries |= set(tf.get_collection(tf.GraphKeys.SUMMARIES, + first_clone_scope)) + summaries |= global_summaries + + # Merge all summaries together. + summary_op = tf.summary.merge(list(summaries), name='summary_op') + + # Soft placement allows placing on CPU ops without GPU implementation. + session_config = tf.ConfigProto(allow_soft_placement=True, + log_device_placement=False) + + # Save checkpoints regularly. + keep_checkpoint_every_n_hours = train_config.keep_checkpoint_every_n_hours + saver = tf.train.Saver( + keep_checkpoint_every_n_hours=keep_checkpoint_every_n_hours) + + slim.learning.train( + train_tensor, + logdir=train_dir, + master=master, + is_chief=is_chief, + session_config=session_config, + startup_delay_steps=train_config.startup_delay_steps, + init_fn=init_fn, + summary_op=summary_op, + number_of_steps=( + train_config.num_steps if train_config.num_steps else None), + save_summaries_secs=120, + sync_optimizer=sync_optimizer, + saver=saver) diff --git a/object_detection/trainer_test.py b/object_detection/trainer_test.py new file mode 100644 index 000000000..36e92752a --- /dev/null +++ b/object_detection/trainer_test.py @@ -0,0 +1,205 @@ +# 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.trainer.""" + +import tensorflow as tf + +from google.protobuf import text_format + +from object_detection import trainer +from object_detection.core import losses +from object_detection.core import model +from object_detection.core import standard_fields as fields +from object_detection.protos import train_pb2 + + +NUMBER_OF_CLASSES = 2 + + +def get_input_function(): + """A function to get test inputs. Returns an image with one box.""" + image = tf.random_uniform([32, 32, 3], dtype=tf.float32) + class_label = tf.random_uniform( + [1], minval=0, maxval=NUMBER_OF_CLASSES, dtype=tf.int32) + box_label = tf.random_uniform( + [1, 4], minval=0.4, maxval=0.6, dtype=tf.float32) + + return { + fields.InputDataFields.image: image, + fields.InputDataFields.groundtruth_classes: class_label, + fields.InputDataFields.groundtruth_boxes: box_label + } + + +class FakeDetectionModel(model.DetectionModel): + """A simple (and poor) DetectionModel for use in test.""" + + def __init__(self): + super(FakeDetectionModel, self).__init__(num_classes=NUMBER_OF_CLASSES) + self._classification_loss = losses.WeightedSigmoidClassificationLoss( + anchorwise_output=True) + self._localization_loss = losses.WeightedSmoothL1LocalizationLoss( + anchorwise_output=True) + + def preprocess(self, inputs): + """Input preprocessing, resizes images to 28x28. + + 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, 28, 28, channels] float32 tensor. + """ + return tf.image.resize_images(inputs, [28, 28]) + + def predict(self, preprocessed_inputs): + """Prediction tensors from inputs tensor. + + Args: + preprocessed_inputs: a [batch, 28, 28, channels] float32 tensor. + + Returns: + prediction_dict: a dictionary holding prediction tensors to be + passed to the Loss or Postprocess functions. + """ + flattened_inputs = tf.contrib.layers.flatten(preprocessed_inputs) + class_prediction = tf.contrib.layers.fully_connected( + flattened_inputs, self._num_classes) + box_prediction = tf.contrib.layers.fully_connected(flattened_inputs, 4) + + return { + 'class_predictions_with_background': tf.reshape( + class_prediction, [-1, 1, self._num_classes]), + 'box_encodings': tf.reshape(box_prediction, [-1, 1, 4]) + } + + def postprocess(self, prediction_dict, **params): + """Convert predicted output tensors to final detections. Unused. + + Args: + prediction_dict: a dictionary holding prediction tensors. + **params: Additional keyword arguments for specific implementations of + DetectionModel. + + Returns: + detections: a dictionary with empty fields. + """ + return { + 'detection_boxes': None, + 'detection_scores': None, + 'detection_classes': None, + 'num_detections': None + } + + def loss(self, prediction_dict): + """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 + + Returns: + a dictionary mapping strings (loss names) to scalar tensors representing + loss values. + """ + batch_reg_targets = tf.stack( + self.groundtruth_lists(fields.BoxListFields.boxes)) + batch_cls_targets = tf.stack( + self.groundtruth_lists(fields.BoxListFields.classes)) + weights = tf.constant( + 1.0, dtype=tf.float32, + shape=[len(self.groundtruth_lists(fields.BoxListFields.boxes)), 1]) + + location_losses = self._localization_loss( + prediction_dict['box_encodings'], batch_reg_targets, + weights=weights) + cls_losses = self._classification_loss( + prediction_dict['class_predictions_with_background'], batch_cls_targets, + weights=weights) + + loss_dict = { + 'localization_loss': tf.reduce_sum(location_losses), + 'classification_loss': tf.reduce_sum(cls_losses), + } + return loss_dict + + def restore_fn(self, checkpoint_path, from_detection_checkpoint=True): + """Return callable for loading a checkpoint into the tensorflow graph. + + Args: + checkpoint_path: path to checkpoint to restore. + from_detection_checkpoint: whether to restore from a full detection + checkpoint (with compatible variable names) or to restore from a + classification checkpoint for initialization prior to training. + + Returns: + a callable which takes a tf.Session and does nothing. + """ + def restore(unused_sess): + return + return restore + + +class TrainerTest(tf.test.TestCase): + + def test_configure_trainer_and_train_two_steps(self): + train_config_text_proto = """ + optimizer { + adam_optimizer { + learning_rate { + constant_learning_rate { + learning_rate: 0.01 + } + } + } + } + data_augmentation_options { + random_adjust_brightness { + max_delta: 0.2 + } + } + data_augmentation_options { + random_adjust_contrast { + min_delta: 0.7 + max_delta: 1.1 + } + } + num_steps: 2 + """ + train_config = train_pb2.TrainConfig() + text_format.Merge(train_config_text_proto, train_config) + + train_dir = self.get_temp_dir() + + trainer.train(create_tensor_dict_fn=get_input_function, + create_model_fn=FakeDetectionModel, + train_config=train_config, + master='', + task=0, + num_clones=1, + worker_replicas=1, + clone_on_cpu=True, + ps_tasks=0, + worker_job_name='worker', + is_chief=True, + train_dir=train_dir) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/utils/BUILD b/object_detection/utils/BUILD new file mode 100644 index 000000000..dc71a38c9 --- /dev/null +++ b/object_detection/utils/BUILD @@ -0,0 +1,287 @@ +# Tensorflow Object Detection API: Utility functions. + +package( + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) + +# Apache 2.0 + +py_library( + name = "category_util", + srcs = ["category_util.py"], + deps = ["//tensorflow"], +) + +py_library( + name = "dataset_util", + srcs = ["dataset_util.py"], + deps = [ + "//tensorflow", + ], +) + +py_library( + name = "label_map_util", + srcs = ["label_map_util.py"], + deps = [ + "//third_party/py/google/protobuf", + "//tensorflow", + "//tensorflow_models/object_detection/protos:string_int_label_map_py_pb2", + ], +) + +py_library( + name = "learning_schedules", + srcs = ["learning_schedules.py"], + deps = ["//tensorflow"], +) + +py_library( + name = "metrics", + srcs = ["metrics.py"], + deps = ["//third_party/py/numpy"], +) + +py_library( + name = "np_box_list", + srcs = ["np_box_list.py"], + deps = ["//tensorflow"], +) + +py_library( + name = "np_box_list_ops", + srcs = ["np_box_list_ops.py"], + deps = [ + ":np_box_list", + ":np_box_ops", + "//tensorflow", + ], +) + +py_library( + name = "np_box_ops", + srcs = ["np_box_ops.py"], + deps = ["//tensorflow"], +) + +py_library( + name = "object_detection_evaluation", + srcs = ["object_detection_evaluation.py"], + deps = [ + ":metrics", + ":per_image_evaluation", + "//tensorflow", + ], +) + +py_library( + name = "ops", + srcs = ["ops.py"], + deps = [ + ":static_shape", + "//tensorflow", + "//tensorflow_models/object_detection/core:box_list", + "//tensorflow_models/object_detection/core:box_list_ops", + "//tensorflow_models/object_detection/core:standard_fields", + ], +) + +py_library( + name = "per_image_evaluation", + srcs = ["per_image_evaluation.py"], + deps = [ + ":np_box_list", + ":np_box_list_ops", + "//tensorflow", + ], +) + +py_library( + name = "shape_utils", + srcs = ["shape_utils.py"], + deps = ["//tensorflow"], +) + +py_library( + name = "static_shape", + srcs = ["static_shape.py"], + deps = [], +) + +py_library( + name = "test_utils", + srcs = ["test_utils.py"], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/core:anchor_generator", + "//tensorflow_models/object_detection/core:box_coder", + "//tensorflow_models/object_detection/core:box_list", + "//tensorflow_models/object_detection/core:box_predictor", + "//tensorflow_models/object_detection/core:matcher", + ], +) + +py_library( + name = "variables_helper", + srcs = ["variables_helper.py"], + deps = [ + "//tensorflow", + ], +) + +py_library( + name = "visualization_utils", + srcs = ["visualization_utils.py"], + deps = [ + "//third_party/py/PIL:pil", + "//tensorflow", + ], +) + +py_test( + name = "category_util_test", + srcs = ["category_util_test.py"], + deps = [ + ":category_util", + "//tensorflow", + ], +) + +py_test( + name = "dataset_util_test", + srcs = ["dataset_util_test.py"], + deps = [ + ":dataset_util", + "//tensorflow", + ], +) + +py_test( + name = "label_map_util_test", + srcs = ["label_map_util_test.py"], + deps = [ + ":label_map_util", + "//tensorflow", + ], +) + +py_test( + name = "learning_schedules_test", + srcs = ["learning_schedules_test.py"], + deps = [ + ":learning_schedules", + "//tensorflow", + ], +) + +py_test( + name = "metrics_test", + srcs = ["metrics_test.py"], + deps = [ + ":metrics", + "//tensorflow", + ], +) + +py_test( + name = "np_box_list_test", + srcs = ["np_box_list_test.py"], + deps = [ + ":np_box_list", + "//tensorflow", + ], +) + +py_test( + name = "np_box_list_ops_test", + srcs = ["np_box_list_ops_test.py"], + deps = [ + ":np_box_list", + ":np_box_list_ops", + "//tensorflow", + ], +) + +py_test( + name = "np_box_ops_test", + srcs = ["np_box_ops_test.py"], + deps = [ + ":np_box_ops", + "//tensorflow", + ], +) + +py_test( + name = "object_detection_evaluation_test", + srcs = ["object_detection_evaluation_test.py"], + deps = [ + ":object_detection_evaluation", + "//tensorflow", + ], +) + +py_test( + name = "ops_test", + srcs = ["ops_test.py"], + deps = [ + ":ops", + "//tensorflow", + "//tensorflow_models/object_detection/core:standard_fields", + ], +) + +py_test( + name = "per_image_evaluation_test", + srcs = ["per_image_evaluation_test.py"], + deps = [ + ":per_image_evaluation", + "//tensorflow", + ], +) + +py_test( + name = "shape_utils_test", + srcs = ["shape_utils_test.py"], + deps = [ + ":shape_utils", + "//tensorflow", + ], +) + +py_test( + name = "static_shape_test", + srcs = ["static_shape_test.py"], + deps = [ + ":static_shape", + "//tensorflow", + ], +) + +py_test( + name = "test_utils_test", + srcs = ["test_utils_test.py"], + deps = [ + ":test_utils", + "//tensorflow", + ], +) + +py_test( + name = "variables_helper_test", + srcs = ["variables_helper_test.py"], + deps = [ + ":variables_helper", + "//tensorflow", + ], +) + +py_test( + name = "visualization_utils_test", + srcs = ["visualization_utils_test.py"], + deps = [ + ":visualization_utils", + "//third_party/py/PIL:pil", + ], +) diff --git a/object_detection/utils/__init__.py b/object_detection/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/object_detection/utils/category_util.py b/object_detection/utils/category_util.py new file mode 100644 index 000000000..fdd9c1c1c --- /dev/null +++ b/object_detection/utils/category_util.py @@ -0,0 +1,72 @@ +# 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 for importing/exporting Object Detection categories.""" +import csv + +import tensorflow as tf + + +def load_categories_from_csv_file(csv_path): + """Loads categories from a csv file. + + The CSV file should have one comma delimited numeric category id and string + category name pair per line. For example: + + 0,"cat" + 1,"dog" + 2,"bird" + ... + + Args: + csv_path: Path to the csv file to be parsed into categories. + Returns: + categories: A list of dictionaries representing all possible categories. + The categories will contain an integer 'id' field and a string + 'name' field. + Raises: + ValueError: If the csv file is incorrectly formatted. + """ + categories = [] + + with tf.gfile.Open(csv_path, 'r') as csvfile: + reader = csv.reader(csvfile, delimiter=',', quotechar='"') + for row in reader: + if not row: + continue + + if len(row) != 2: + raise ValueError('Expected 2 fields per row in csv: %s' % ','.join(row)) + + category_id = int(row[0]) + category_name = row[1] + categories.append({'id': category_id, 'name': category_name}) + + return categories + + +def save_categories_to_csv_file(categories, csv_path): + """Saves categories to a csv file. + + Args: + categories: A list of dictionaries representing categories to save to file. + Each category must contain an 'id' and 'name' field. + csv_path: Path to the csv file to be parsed into categories. + """ + categories.sort(key=lambda x: x['id']) + with tf.gfile.Open(csv_path, 'w') as csvfile: + writer = csv.writer(csvfile, delimiter=',', quotechar='"') + for category in categories: + writer.writerow([category['id'], category['name']]) diff --git a/object_detection/utils/category_util_test.py b/object_detection/utils/category_util_test.py new file mode 100644 index 000000000..9c99079e1 --- /dev/null +++ b/object_detection/utils/category_util_test.py @@ -0,0 +1,54 @@ +# 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.utils.category_util.""" +import os + +import tensorflow as tf + +from object_detection.utils import category_util + + +class EvalUtilTest(tf.test.TestCase): + + def test_load_categories_from_csv_file(self): + csv_data = """ + 0,"cat" + 1,"dog" + 2,"bird" + """.strip(' ') + csv_path = os.path.join(self.get_temp_dir(), 'test.csv') + with tf.gfile.Open(csv_path, 'wb') as f: + f.write(csv_data) + + categories = category_util.load_categories_from_csv_file(csv_path) + self.assertTrue({'id': 0, 'name': 'cat'} in categories) + self.assertTrue({'id': 1, 'name': 'dog'} in categories) + self.assertTrue({'id': 2, 'name': 'bird'} in categories) + + def test_save_categories_to_csv_file(self): + categories = [ + {'id': 0, 'name': 'cat'}, + {'id': 1, 'name': 'dog'}, + {'id': 2, 'name': 'bird'}, + ] + csv_path = os.path.join(self.get_temp_dir(), 'test.csv') + category_util.save_categories_to_csv_file(categories, csv_path) + saved_categories = category_util.load_categories_from_csv_file(csv_path) + self.assertEqual(saved_categories, categories) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/utils/dataset_util.py b/object_detection/utils/dataset_util.py new file mode 100644 index 000000000..014a9118d --- /dev/null +++ b/object_detection/utils/dataset_util.py @@ -0,0 +1,86 @@ +# 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. +# ============================================================================== + +"""Utility functions for creating TFRecord data sets.""" + +import tensorflow as tf + + +def int64_feature(value): + return tf.train.Feature(int64_list=tf.train.Int64List(value=[value])) + + +def int64_list_feature(value): + return tf.train.Feature(int64_list=tf.train.Int64List(value=value)) + + +def bytes_feature(value): + return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value])) + + +def bytes_list_feature(value): + return tf.train.Feature(bytes_list=tf.train.BytesList(value=value)) + + +def float_list_feature(value): + return tf.train.Feature(float_list=tf.train.FloatList(value=value)) + + +def read_examples_list(path): + """Read list of training or validation examples. + + The file is assumed to contain a single example per line where the first + token in the line is an identifier that allows us to find the image and + annotation xml for that example. + + For example, the line: + xyz 3 + would allow us to find files xyz.jpg and xyz.xml (the 3 would be ignored). + + Args: + path: absolute path to examples list file. + + Returns: + list of example identifiers (strings). + """ + with tf.gfile.GFile(path) as fid: + lines = fid.readlines() + return [line.strip().split(' ')[0] for line in lines] + + +def recursive_parse_xml_to_dict(xml): + """Recursively parses XML contents to python dict. + + We assume that `object` tags are the only ones that can appear + multiple times at the same level of a tree. + + Args: + xml: xml tree obtained by parsing XML file contents using lxml.etree + + Returns: + Python dictionary holding XML contents. + """ + if not xml: + return {xml.tag: xml.text} + result = {} + for child in xml: + child_result = recursive_parse_xml_to_dict(child) + if child.tag != 'object': + result[child.tag] = child_result[child.tag] + else: + if child.tag not in result: + result[child.tag] = [] + result[child.tag].append(child_result[child.tag]) + return {xml.tag: result} diff --git a/object_detection/utils/dataset_util_test.py b/object_detection/utils/dataset_util_test.py new file mode 100644 index 000000000..99cfb2cdf --- /dev/null +++ b/object_detection/utils/dataset_util_test.py @@ -0,0 +1,37 @@ +# 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.utils.dataset_util.""" + +import os +import tensorflow as tf + +from object_detection.utils import dataset_util + + +class DatasetUtilTest(tf.test.TestCase): + + def test_read_examples_list(self): + example_list_data = """example1 1\nexample2 2""" + example_list_path = os.path.join(self.get_temp_dir(), 'examples.txt') + with tf.gfile.Open(example_list_path, 'wb') as f: + f.write(example_list_data) + + examples = dataset_util.read_examples_list(example_list_path) + self.assertListEqual(['example1', 'example2'], examples) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/utils/label_map_util.py b/object_detection/utils/label_map_util.py new file mode 100644 index 000000000..a3b312524 --- /dev/null +++ b/object_detection/utils/label_map_util.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. +# ============================================================================== + +"""Label map utility functions.""" + +import logging + +import tensorflow as tf +from google.protobuf import text_format +from object_detection.protos import string_int_label_map_pb2 + + +def create_category_index(categories): + """Creates dictionary of COCO compatible categories keyed by category id. + + Args: + 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', 'pizza'. + + Returns: + category_index: a dict containing the same entries as categories, but keyed + by the 'id' field of each category. + """ + category_index = {} + for cat in categories: + category_index[cat['id']] = cat + return category_index + + +def convert_label_map_to_categories(label_map, + max_num_classes, + use_display_name=True): + """Loads label map proto and returns categories list compatible with eval. + + This function loads a label map and returns 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', 'pizza'. + We only allow class into the list if its id-label_id_offset is + between 0 (inclusive) and max_num_classes (exclusive). + If there are several items mapping to the same id in the label map, + we will only keep the first one in the categories list. + + Args: + label_map: a StringIntLabelMapProto or None. If None, a default categories + list is created with max_num_classes categories. + max_num_classes: maximum number of (consecutive) label indices to include. + use_display_name: (boolean) choose whether to load 'display_name' field + as category name. If False of if the display_name field does not exist, + uses 'name' field as category names instead. + Returns: + categories: a list of dictionaries representing all possible categories. + """ + categories = [] + list_of_ids_already_added = [] + if not label_map: + label_id_offset = 1 + for class_id in range(max_num_classes): + categories.append({ + 'id': class_id + label_id_offset, + 'name': 'category_{}'.format(class_id + label_id_offset) + }) + return categories + for item in label_map.item: + if not 0 < item.id <= max_num_classes: + logging.info('Ignore item %d since it falls outside of requested ' + 'label range.', item.id) + continue + if use_display_name and item.HasField('display_name'): + name = item.display_name + else: + name = item.name + if item.id not in list_of_ids_already_added: + list_of_ids_already_added.append(item.id) + categories.append({'id': item.id, 'name': name}) + return categories + + +# TODO: double check documentaion. +def load_labelmap(path): + """Loads label map proto. + + Args: + path: path to StringIntLabelMap proto text file. + Returns: + a StringIntLabelMapProto + """ + with tf.gfile.GFile(path, 'r') as fid: + label_map_string = fid.read() + label_map = string_int_label_map_pb2.StringIntLabelMap() + try: + text_format.Merge(label_map_string, label_map) + except text_format.ParseError: + label_map.ParseFromString(label_map_string) + return label_map + + +def get_label_map_dict(label_map_path): + """Reads a label map and returns a dictionary of label names to id. + + Args: + label_map_path: path to label_map. + + Returns: + A dictionary mapping label names to id. + """ + label_map = load_labelmap(label_map_path) + label_map_dict = {} + for item in label_map.item: + label_map_dict[item.name] = item.id + return label_map_dict diff --git a/object_detection/utils/label_map_util_test.py b/object_detection/utils/label_map_util_test.py new file mode 100644 index 000000000..10e0f3ddc --- /dev/null +++ b/object_detection/utils/label_map_util_test.py @@ -0,0 +1,147 @@ +# 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.utils.label_map_util.""" + +import os +import tensorflow as tf + +from google.protobuf import text_format +from object_detection.protos import string_int_label_map_pb2 +from object_detection.utils import label_map_util + + +class LabelMapUtilTest(tf.test.TestCase): + + def _generate_label_map(self, num_classes): + label_map_proto = string_int_label_map_pb2.StringIntLabelMap() + for i in range(1, num_classes + 1): + item = label_map_proto.item.add() + item.id = i + item.name = 'label_' + str(i) + item.display_name = str(i) + return label_map_proto + + def test_get_label_map_dict(self): + 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) + + label_map_dict = label_map_util.get_label_map_dict(label_map_path) + self.assertEqual(label_map_dict['dog'], 1) + self.assertEqual(label_map_dict['cat'], 2) + + def test_keep_categories_with_unique_id(self): + label_map_proto = string_int_label_map_pb2.StringIntLabelMap() + label_map_string = """ + item { + id:2 + name:'cat' + } + item { + id:1 + name:'child' + } + item { + id:1 + name:'person' + } + item { + id:1 + name:'n00007846' + } + """ + text_format.Merge(label_map_string, label_map_proto) + categories = label_map_util.convert_label_map_to_categories( + label_map_proto, max_num_classes=3) + self.assertListEqual([{ + 'id': 2, + 'name': u'cat' + }, { + 'id': 1, + 'name': u'child' + }], categories) + + def test_convert_label_map_to_categories_no_label_map(self): + categories = label_map_util.convert_label_map_to_categories( + None, max_num_classes=3) + expected_categories_list = [{ + 'name': u'category_1', + 'id': 1 + }, { + 'name': u'category_2', + 'id': 2 + }, { + 'name': u'category_3', + 'id': 3 + }] + self.assertListEqual(expected_categories_list, categories) + + def test_convert_label_map_to_coco_categories(self): + label_map_proto = self._generate_label_map(num_classes=4) + categories = label_map_util.convert_label_map_to_categories( + label_map_proto, max_num_classes=3) + expected_categories_list = [{ + 'name': u'1', + 'id': 1 + }, { + 'name': u'2', + 'id': 2 + }, { + 'name': u'3', + 'id': 3 + }] + self.assertListEqual(expected_categories_list, categories) + + def test_convert_label_map_to_coco_categories_with_few_classes(self): + label_map_proto = self._generate_label_map(num_classes=4) + cat_no_offset = label_map_util.convert_label_map_to_categories( + label_map_proto, max_num_classes=2) + expected_categories_list = [{ + 'name': u'1', + 'id': 1 + }, { + 'name': u'2', + 'id': 2 + }] + self.assertListEqual(expected_categories_list, cat_no_offset) + + def test_create_category_index(self): + categories = [{'name': u'1', 'id': 1}, {'name': u'2', 'id': 2}] + category_index = label_map_util.create_category_index(categories) + self.assertDictEqual({ + 1: { + 'name': u'1', + 'id': 1 + }, + 2: { + 'name': u'2', + 'id': 2 + } + }, category_index) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/utils/learning_schedules.py b/object_detection/utils/learning_schedules.py new file mode 100644 index 000000000..217b47a71 --- /dev/null +++ b/object_detection/utils/learning_schedules.py @@ -0,0 +1,103 @@ +# 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. +# ============================================================================== + +"""Library of common learning rate schedules.""" + +import tensorflow as tf + + +def exponential_decay_with_burnin(global_step, + learning_rate_base, + learning_rate_decay_steps, + learning_rate_decay_factor, + burnin_learning_rate=0.0, + burnin_steps=0): + """Exponential decay schedule with burn-in period. + + In this schedule, learning rate is fixed at burnin_learning_rate + for a fixed period, before transitioning to a regular exponential + decay schedule. + + Args: + global_step: int tensor representing global step. + learning_rate_base: base learning rate. + learning_rate_decay_steps: steps to take between decaying the learning rate. + Note that this includes the number of burn-in steps. + learning_rate_decay_factor: multiplicative factor by which to decay + learning rate. + burnin_learning_rate: initial learning rate during burn-in period. If + 0.0 (which is the default), then the burn-in learning rate is simply + set to learning_rate_base. + burnin_steps: number of steps to use burnin learning rate. + + Returns: + a (scalar) float tensor representing learning rate + """ + if burnin_learning_rate == 0: + burnin_learning_rate = learning_rate_base + post_burnin_learning_rate = tf.train.exponential_decay( + learning_rate_base, + global_step, + learning_rate_decay_steps, + learning_rate_decay_factor, + staircase=True) + return tf.cond( + tf.less(global_step, burnin_steps), + lambda: tf.convert_to_tensor(burnin_learning_rate), + lambda: post_burnin_learning_rate) + + +def manual_stepping(global_step, boundaries, rates): + """Manually stepped learning rate schedule. + + This function provides fine grained control over learning rates. One must + specify a sequence of learning rates as well as a set of integer steps + at which the current learning rate must transition to the next. For example, + if boundaries = [5, 10] and rates = [.1, .01, .001], then the learning + rate returned by this function is .1 for global_step=0,...,4, .01 for + global_step=5...9, and .001 for global_step=10 and onward. + + Args: + global_step: int64 (scalar) tensor representing global step. + boundaries: a list of global steps at which to switch learning + rates. This list is assumed to consist of increasing positive integers. + rates: a list of (float) learning rates corresponding to intervals between + the boundaries. The length of this list must be exactly + len(boundaries) + 1. + + Returns: + a (scalar) float tensor representing learning rate + Raises: + ValueError: if one of the following checks fails: + 1. boundaries is a strictly increasing list of positive integers + 2. len(rates) == len(boundaries) + 1 + """ + if any([b < 0 for b in boundaries]) or any( + [not isinstance(b, int) for b in boundaries]): + raise ValueError('boundaries must be a list of positive integers') + if any([bnext <= b for bnext, b in zip(boundaries[1:], boundaries[:-1])]): + raise ValueError('Entries in boundaries must be strictly increasing.') + if any([not isinstance(r, float) for r in rates]): + raise ValueError('Learning rates must be floats') + if len(rates) != len(boundaries) + 1: + raise ValueError('Number of provided learning rates must exceed ' + 'number of boundary points by exactly 1.') + step_boundaries = tf.constant(boundaries, tf.int64) + learning_rates = tf.constant(rates, tf.float32) + unreached_boundaries = tf.reshape(tf.where( + tf.greater(step_boundaries, global_step)), [-1]) + unreached_boundaries = tf.concat([unreached_boundaries, [len(boundaries)]], 0) + index = tf.reshape(tf.reduce_min(unreached_boundaries), [1]) + return tf.reshape(tf.slice(learning_rates, index, [1]), []) diff --git a/object_detection/utils/learning_schedules_test.py b/object_detection/utils/learning_schedules_test.py new file mode 100644 index 000000000..c8e6ce641 --- /dev/null +++ b/object_detection/utils/learning_schedules_test.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. +# ============================================================================== + +"""Tests for object_detection.utils.learning_schedules.""" +import tensorflow as tf + +from object_detection.utils import learning_schedules + + +class LearningSchedulesTest(tf.test.TestCase): + + def testExponentialDecayWithBurnin(self): + global_step = tf.placeholder(tf.int32, []) + learning_rate_base = 1.0 + learning_rate_decay_steps = 3 + learning_rate_decay_factor = .1 + burnin_learning_rate = .5 + burnin_steps = 2 + exp_rates = [.5, .5, 1, .1, .1, .1, .01, .01] + learning_rate = learning_schedules.exponential_decay_with_burnin( + global_step, learning_rate_base, learning_rate_decay_steps, + learning_rate_decay_factor, burnin_learning_rate, burnin_steps) + with self.test_session() as sess: + output_rates = [] + for input_global_step in range(8): + output_rate = sess.run(learning_rate, + feed_dict={global_step: input_global_step}) + output_rates.append(output_rate) + self.assertAllClose(output_rates, exp_rates) + + def testManualStepping(self): + global_step = tf.placeholder(tf.int64, []) + boundaries = [2, 3, 7] + rates = [1.0, 2.0, 3.0, 4.0] + exp_rates = [1.0, 1.0, 2.0, 3.0, 3.0, 3.0, 3.0, 4.0, 4.0, 4.0] + learning_rate = learning_schedules.manual_stepping(global_step, boundaries, + rates) + with self.test_session() as sess: + output_rates = [] + for input_global_step in range(10): + output_rate = sess.run(learning_rate, + feed_dict={global_step: input_global_step}) + output_rates.append(output_rate) + self.assertAllClose(output_rates, exp_rates) + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/utils/metrics.py b/object_detection/utils/metrics.py new file mode 100644 index 000000000..85f94efa8 --- /dev/null +++ b/object_detection/utils/metrics.py @@ -0,0 +1,144 @@ +# 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 for computing metrics like precision, recall, CorLoc and etc.""" +from __future__ import division + +import numpy as np + + +def compute_precision_recall(scores, labels, num_gt): + """Compute precision and recall. + + Args: + scores: A float numpy array representing detection score + labels: A boolean numpy array representing true/false positive labels + num_gt: Number of ground truth instances + + Raises: + ValueError: if the input is not of the correct format + + Returns: + precision: Fraction of positive instances over detected ones. This value is + None if no ground truth labels are present. + recall: Fraction of detected positive instance over all positive instances. + This value is None if no ground truth labels are present. + + """ + if not isinstance( + labels, np.ndarray) or labels.dtype != np.bool or len(labels.shape) != 1: + raise ValueError("labels must be single dimension bool numpy array") + + if not isinstance( + scores, np.ndarray) or len(scores.shape) != 1: + raise ValueError("scores must be single dimension numpy array") + + if num_gt < np.sum(labels): + raise ValueError("Number of true positives must be smaller than num_gt.") + + if len(scores) != len(labels): + raise ValueError("scores and labels must be of the same size.") + + if num_gt == 0: + return None, None + + sorted_indices = np.argsort(scores) + sorted_indices = sorted_indices[::-1] + labels = labels.astype(int) + true_positive_labels = labels[sorted_indices] + false_positive_labels = 1 - true_positive_labels + cum_true_positives = np.cumsum(true_positive_labels) + cum_false_positives = np.cumsum(false_positive_labels) + precision = cum_true_positives.astype(float) / ( + cum_true_positives + cum_false_positives) + recall = cum_true_positives.astype(float) / num_gt + return precision, recall + + +def compute_average_precision(precision, recall): + """Compute Average Precision according to the definition in VOCdevkit. + + Precision is modified to ensure that it does not decrease as recall + decrease. + + Args: + precision: A float [N, 1] numpy array of precisions + recall: A float [N, 1] numpy array of recalls + + Raises: + ValueError: if the input is not of the correct format + + Returns: + average_precison: The area under the precision recall curve. NaN if + precision and recall are None. + + """ + if precision is None: + if recall is not None: + raise ValueError("If precision is None, recall must also be None") + return np.NAN + + if not isinstance(precision, np.ndarray) or not isinstance(recall, + np.ndarray): + raise ValueError("precision and recall must be numpy array") + if precision.dtype != np.float or recall.dtype != np.float: + raise ValueError("input must be float numpy array.") + if len(precision) != len(recall): + raise ValueError("precision and recall must be of the same size.") + if not precision.size: + return 0.0 + if np.amin(precision) < 0 or np.amax(precision) > 1: + raise ValueError("Precision must be in the range of [0, 1].") + if np.amin(recall) < 0 or np.amax(recall) > 1: + raise ValueError("recall must be in the range of [0, 1].") + if not all(recall[i] <= recall[i + 1] for i in xrange(len(recall) - 1)): + raise ValueError("recall must be a non-decreasing array") + + recall = np.concatenate([[0], recall, [1]]) + precision = np.concatenate([[0], precision, [0]]) + + # Preprocess precision to be a non-decreasing array + for i in range(len(precision) - 2, -1, -1): + precision[i] = np.maximum(precision[i], precision[i + 1]) + + indices = np.where(recall[1:] != recall[:-1])[0] + 1 + average_precision = np.sum( + (recall[indices] - recall[indices - 1]) * precision[indices]) + return average_precision + + +def compute_cor_loc(num_gt_imgs_per_class, + num_images_correctly_detected_per_class): + """Compute CorLoc according to the definition in the following paper. + + https://www.robots.ox.ac.uk/~vgg/rg/papers/deselaers-eccv10.pdf + + Returns nans if there are no ground truth images for a class. + + Args: + num_gt_imgs_per_class: 1D array, representing number of images containing + at least one object instance of a particular class + num_images_correctly_detected_per_class: 1D array, representing number of + images that are correctly detected at least one object instance of a + particular class + + Returns: + corloc_per_class: A float numpy array represents the corloc score of each + class + """ + return np.where( + num_gt_imgs_per_class == 0, + np.nan, + num_images_correctly_detected_per_class / num_gt_imgs_per_class) diff --git a/object_detection/utils/metrics_test.py b/object_detection/utils/metrics_test.py new file mode 100644 index 000000000..a2064bbff --- /dev/null +++ b/object_detection/utils/metrics_test.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. +# ============================================================================== + +"""Tests for object_detection.metrics.""" + +import numpy as np +import tensorflow as tf + +from object_detection.utils import metrics + + +class MetricsTest(tf.test.TestCase): + + def test_compute_cor_loc(self): + num_gt_imgs_per_class = np.array([100, 1, 5, 1, 1], dtype=int) + num_images_correctly_detected_per_class = np.array([10, 0, 1, 0, 0], + dtype=int) + corloc = metrics.compute_cor_loc(num_gt_imgs_per_class, + num_images_correctly_detected_per_class) + expected_corloc = np.array([0.1, 0, 0.2, 0, 0], dtype=float) + self.assertTrue(np.allclose(corloc, expected_corloc)) + + def test_compute_cor_loc_nans(self): + num_gt_imgs_per_class = np.array([100, 0, 0, 1, 1], dtype=int) + num_images_correctly_detected_per_class = np.array([10, 0, 1, 0, 0], + dtype=int) + corloc = metrics.compute_cor_loc(num_gt_imgs_per_class, + num_images_correctly_detected_per_class) + expected_corloc = np.array([0.1, np.nan, np.nan, 0, 0], dtype=float) + self.assertAllClose(corloc, expected_corloc) + + def test_compute_precision_recall(self): + num_gt = 10 + scores = np.array([0.4, 0.3, 0.6, 0.2, 0.7, 0.1], dtype=float) + labels = np.array([0, 1, 1, 0, 0, 1], dtype=bool) + accumulated_tp_count = np.array([0, 1, 1, 2, 2, 3], dtype=float) + expected_precision = accumulated_tp_count / np.array([1, 2, 3, 4, 5, 6]) + expected_recall = accumulated_tp_count / num_gt + precision, recall = metrics.compute_precision_recall(scores, labels, num_gt) + self.assertAllClose(precision, expected_precision) + self.assertAllClose(recall, expected_recall) + + def test_compute_average_precision(self): + precision = np.array([0.8, 0.76, 0.9, 0.65, 0.7, 0.5, 0.55, 0], dtype=float) + recall = np.array([0.3, 0.3, 0.4, 0.4, 0.45, 0.45, 0.5, 0.5], dtype=float) + processed_precision = np.array([0.9, 0.9, 0.9, 0.7, 0.7, 0.55, 0.55, 0], + dtype=float) + recall_interval = np.array([0.3, 0, 0.1, 0, 0.05, 0, 0.05, 0], dtype=float) + expected_mean_ap = np.sum(recall_interval * processed_precision) + mean_ap = metrics.compute_average_precision(precision, recall) + self.assertAlmostEqual(expected_mean_ap, mean_ap) + + def test_compute_precision_recall_and_ap_no_groundtruth(self): + num_gt = 0 + scores = np.array([0.4, 0.3, 0.6, 0.2, 0.7, 0.1], dtype=float) + labels = np.array([0, 0, 0, 0, 0, 0], dtype=bool) + expected_precision = None + expected_recall = None + precision, recall = metrics.compute_precision_recall(scores, labels, num_gt) + self.assertEquals(precision, expected_precision) + self.assertEquals(recall, expected_recall) + ap = metrics.compute_average_precision(precision, recall) + self.assertTrue(np.isnan(ap)) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/utils/np_box_list.py b/object_detection/utils/np_box_list.py new file mode 100644 index 000000000..7df9f68f5 --- /dev/null +++ b/object_detection/utils/np_box_list.py @@ -0,0 +1,133 @@ +# 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. +# ============================================================================== + +"""Numpy BoxList classes and functions.""" + +import numpy as np + + +class BoxList(object): + """Box collection. + + BoxList represents a list of bounding boxes as numpy array, 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. + + Optionally, users can add additional related fields (such as + objectness/classification scores). + """ + + def __init__(self, data): + """Constructs box collection. + + Args: + data: a numpy array of shape [N, 4] representing box coordinates + + Raises: + ValueError: if bbox data is not a numpy array + ValueError: if invalid dimensions for bbox data + """ + if not isinstance(data, np.ndarray): + raise ValueError('data must be a numpy array.') + if len(data.shape) != 2 or data.shape[1] != 4: + raise ValueError('Invalid dimensions for box data.') + if data.dtype != np.float32 and data.dtype != np.float64: + raise ValueError('Invalid data type for box data: float is required.') + if not self._is_valid_boxes(data): + raise ValueError('Invalid box data. data must be a numpy array of ' + 'N*[y_min, x_min, y_max, x_max]') + self.data = {'boxes': data} + + def num_boxes(self): + """Return number of boxes held in collections.""" + return self.data['boxes'].shape[0] + + def get_extra_fields(self): + """Return all non-box fields.""" + return [k for k in self.data.keys() if k != 'boxes'] + + def has_field(self, field): + return field in self.data + + def add_field(self, field, field_data): + """Add data to a specified field. + + Args: + field: a string parameter used to speficy a related field to be accessed. + field_data: a numpy array of [N, ...] representing the data associated + with the field. + Raises: + ValueError: if the field is already exist or the dimension of the field + data does not matches the number of boxes. + """ + if self.has_field(field): + raise ValueError('Field ' + field + 'already exists') + if len(field_data.shape) < 1 or field_data.shape[0] != self.num_boxes(): + raise ValueError('Invalid dimensions for field data') + self.data[field] = field_data + + def get(self): + """Convenience function for accesssing box coordinates. + + Returns: + a numpy array of shape [N, 4] representing box corners + """ + return self.get_field('boxes') + + def get_field(self, field): + """Accesses data associated with the specified field in the box collection. + + Args: + field: a string parameter used to speficy a related field to be accessed. + + Returns: + a numpy 1-d array representing data of an associated field + + Raises: + ValueError: if invalid field + """ + if not self.has_field(field): + raise ValueError('field {} does not exist'.format(field)) + return self.data[field] + + def get_coordinates(self): + """Get corner coordinates of boxes. + + Returns: + a list of 4 1-d numpy arrays [y_min, x_min, y_max, x_max] + """ + box_coordinates = self.get() + y_min = box_coordinates[:, 0] + x_min = box_coordinates[:, 1] + y_max = box_coordinates[:, 2] + x_max = box_coordinates[:, 3] + return [y_min, x_min, y_max, x_max] + + def _is_valid_boxes(self, data): + """Check whether data fullfills the format of N*[ymin, xmin, ymax, xmin]. + + Args: + data: a numpy array of shape [N, 4] representing box coordinates + + Returns: + a boolean indicating whether all ymax of boxes are equal or greater than + ymin, and all xmax of boxes are equal or greater than xmin. + """ + if data.shape[0] > 0: + for i in xrange(data.shape[0]): + if data[i, 0] > data[i, 2] or data[i, 1] > data[i, 3]: + return False + return True diff --git a/object_detection/utils/np_box_list_ops.py b/object_detection/utils/np_box_list_ops.py new file mode 100644 index 000000000..cb9fee856 --- /dev/null +++ b/object_detection/utils/np_box_list_ops.py @@ -0,0 +1,555 @@ +# 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 operations for Numpy BoxLists. + +Example box operations that are supported: + * Areas: compute bounding box areas + * IOU: pairwise intersection-over-union scores +""" + +import numpy as np + +from object_detection.utils import np_box_list +from object_detection.utils import np_box_ops + + +class SortOrder(object): + """Enum class for sort order. + + Attributes: + ascend: ascend order. + descend: descend order. + """ + ASCEND = 1 + DESCEND = 2 + + +def area(boxlist): + """Computes area of boxes. + + Args: + boxlist: BoxList holding N boxes + + Returns: + a numpy array with shape [N*1] representing box areas + """ + y_min, x_min, y_max, x_max = boxlist.get_coordinates() + return (y_max - y_min) * (x_max - x_min) + + +def intersection(boxlist1, boxlist2): + """Compute pairwise intersection areas between boxes. + + Args: + boxlist1: BoxList holding N boxes + boxlist2: BoxList holding M boxes + + Returns: + a numpy array with shape [N*M] representing pairwise intersection area + """ + return np_box_ops.intersection(boxlist1.get(), boxlist2.get()) + + +def iou(boxlist1, boxlist2): + """Computes pairwise intersection-over-union between box collections. + + Args: + boxlist1: BoxList holding N boxes + boxlist2: BoxList holding M boxes + + Returns: + a numpy array with shape [N, M] representing pairwise iou scores. + """ + return np_box_ops.iou(boxlist1.get(), boxlist2.get()) + + +def ioa(boxlist1, boxlist2): + """Computes pairwise intersection-over-area between box collections. + + Intersection-over-area (ioa) between two boxes box1 and box2 is defined as + their intersection area over box2's area. Note that ioa is not symmetric, + that is, IOA(box1, box2) != IOA(box2, box1). + + Args: + boxlist1: BoxList holding N boxes + boxlist2: BoxList holding M boxes + + Returns: + a numpy array with shape [N, M] representing pairwise ioa scores. + """ + return np_box_ops.ioa(boxlist1.get(), boxlist2.get()) + + +def gather(boxlist, indices, fields=None): + """Gather boxes from BoxList according to indices and return new BoxList. + + By default, Gather returns boxes corresponding to the input index list, as + well as all additional fields stored in the boxlist (indexing into the + first dimension). However one can optionally only gather from a + subset of fields. + + Args: + boxlist: BoxList holding N boxes + indices: a 1-d numpy array of type int_ + fields: (optional) list of fields to also gather from. If None (default), + all fields are gathered from. Pass an empty fields list to only gather + the box coordinates. + + Returns: + subboxlist: a BoxList corresponding to the subset of the input BoxList + specified by indices + + Raises: + ValueError: if specified field is not contained in boxlist or if the + indices are not of type int_ + """ + if indices.size: + if np.amax(indices) >= boxlist.num_boxes() or np.amin(indices) < 0: + raise ValueError('indices are out of valid range.') + subboxlist = np_box_list.BoxList(boxlist.get()[indices, :]) + if fields is None: + fields = boxlist.get_extra_fields() + for field in fields: + extra_field_data = boxlist.get_field(field) + subboxlist.add_field(field, extra_field_data[indices, ...]) + return subboxlist + + +def sort_by_field(boxlist, field, order=SortOrder.DESCEND): + """Sort boxes and associated fields according to a scalar field. + + A common use case is reordering the boxes according to descending scores. + + Args: + boxlist: BoxList holding N boxes. + field: A BoxList field for sorting and reordering the BoxList. + order: (Optional) 'descend' or 'ascend'. Default is descend. + + Returns: + sorted_boxlist: A sorted BoxList with the field in the specified order. + + Raises: + ValueError: if specified field does not exist or is not of single dimension. + ValueError: if the order is not either descend or ascend. + """ + if not boxlist.has_field(field): + raise ValueError('Field ' + field + ' does not exist') + if len(boxlist.get_field(field).shape) != 1: + raise ValueError('Field ' + field + 'should be single dimension.') + if order != SortOrder.DESCEND and order != SortOrder.ASCEND: + raise ValueError('Invalid sort order') + + field_to_sort = boxlist.get_field(field) + sorted_indices = np.argsort(field_to_sort) + if order == SortOrder.DESCEND: + sorted_indices = sorted_indices[::-1] + return gather(boxlist, sorted_indices) + + +def non_max_suppression(boxlist, + max_output_size=10000, + iou_threshold=1.0, + score_threshold=-10.0): + """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. In each iteration, the detected bounding box with + highest score in the available pool is selected. + + Args: + boxlist: BoxList holding N boxes. Must contain a 'scores' field + representing detection scores. All scores belong to the same class. + max_output_size: maximum number of retained boxes + iou_threshold: intersection over union threshold. + score_threshold: minimum score threshold. Remove the boxes with scores + less than this value. Default value is set to -10. A very + low threshold to pass pretty much all the boxes, unless + the user sets a different score threshold. + + Returns: + a BoxList holding M boxes where M <= max_output_size + Raises: + ValueError: if 'scores' field does not exist + ValueError: if threshold is not in [0, 1] + ValueError: if max_output_size < 0 + """ + if not boxlist.has_field('scores'): + raise ValueError('Field scores does not exist') + if iou_threshold < 0. or iou_threshold > 1.0: + raise ValueError('IOU threshold must be in [0, 1]') + if max_output_size < 0: + raise ValueError('max_output_size must be bigger than 0.') + + boxlist = filter_scores_greater_than(boxlist, score_threshold) + if boxlist.num_boxes() == 0: + return boxlist + + boxlist = sort_by_field(boxlist, 'scores') + + # Prevent further computation if NMS is disabled. + if iou_threshold == 1.0: + if boxlist.num_boxes() > max_output_size: + selected_indices = np.arange(max_output_size) + return gather(boxlist, selected_indices) + else: + return boxlist + + boxes = boxlist.get() + num_boxes = boxlist.num_boxes() + # is_index_valid is True only for all remaining valid boxes, + is_index_valid = np.full(num_boxes, 1, dtype=bool) + selected_indices = [] + num_output = 0 + for i in xrange(num_boxes): + if num_output < max_output_size: + if is_index_valid[i]: + num_output += 1 + selected_indices.append(i) + is_index_valid[i] = False + valid_indices = np.where(is_index_valid)[0] + if valid_indices.size == 0: + break + + intersect_over_union = np_box_ops.iou( + np.expand_dims(boxes[i, :], axis=0), boxes[valid_indices, :]) + intersect_over_union = np.squeeze(intersect_over_union, axis=0) + is_index_valid[valid_indices] = np.logical_and( + is_index_valid[valid_indices], + intersect_over_union <= iou_threshold) + return gather(boxlist, np.array(selected_indices)) + + +def multi_class_non_max_suppression(boxlist, score_thresh, iou_thresh, + max_output_size): + """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. + + Args: + boxlist: BoxList holding N boxes. Must contain a 'scores' field + representing detection scores. This scores field is a tensor that can + be 1 dimensional (in the case of a single class) or 2-dimensional, which + which case we assume that it takes the shape [num_boxes, num_classes]. + We further assume that this rank is known statically and that + scores.shape[1] is also known (i.e., the number of classes is fixed + and known at graph construction time). + score_thresh: scalar threshold for score (low scoring boxes are removed). + iou_thresh: scalar threshold for IOU (boxes that that high IOU overlap + with previously selected boxes are removed). + max_output_size: maximum number of retained boxes per class. + + Returns: + a BoxList holding 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. + 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('thresh must be between 0 and 1') + if not isinstance(boxlist, np_box_list.BoxList): + raise ValueError('boxlist must be a BoxList') + if not boxlist.has_field('scores'): + raise ValueError('input boxlist must have \'scores\' field') + scores = boxlist.get_field('scores') + if len(scores.shape) == 1: + scores = np.reshape(scores, [-1, 1]) + elif len(scores.shape) == 2: + if scores.shape[1] is None: + raise ValueError('scores field must have statically defined second ' + 'dimension') + else: + raise ValueError('scores field must be of rank 1 or 2') + num_boxes = boxlist.num_boxes() + num_scores = scores.shape[0] + num_classes = scores.shape[1] + + if num_boxes != num_scores: + raise ValueError('Incorrect scores field length: actual vs expected.') + + selected_boxes_list = [] + for class_idx in range(num_classes): + boxlist_and_class_scores = np_box_list.BoxList(boxlist.get()) + class_scores = np.reshape(scores[0:num_scores, class_idx], [-1]) + boxlist_and_class_scores.add_field('scores', class_scores) + boxlist_filt = filter_scores_greater_than(boxlist_and_class_scores, + score_thresh) + nms_result = non_max_suppression(boxlist_filt, + max_output_size=max_output_size, + iou_threshold=iou_thresh, + score_threshold=score_thresh) + nms_result.add_field( + 'classes', np.zeros_like(nms_result.get_field('scores')) + class_idx) + selected_boxes_list.append(nms_result) + selected_boxes = concatenate(selected_boxes_list) + sorted_boxes = sort_by_field(selected_boxes, 'scores') + return sorted_boxes + + +def scale(boxlist, y_scale, x_scale): + """Scale box coordinates in x and y dimensions. + + Args: + boxlist: BoxList holding N boxes + y_scale: float + x_scale: float + + Returns: + boxlist: BoxList holding N boxes + """ + y_min, x_min, y_max, x_max = np.array_split(boxlist.get(), 4, axis=1) + y_min = y_scale * y_min + y_max = y_scale * y_max + x_min = x_scale * x_min + x_max = x_scale * x_max + scaled_boxlist = np_box_list.BoxList(np.hstack([y_min, x_min, y_max, x_max])) + + fields = boxlist.get_extra_fields() + for field in fields: + extra_field_data = boxlist.get_field(field) + scaled_boxlist.add_field(field, extra_field_data) + + return scaled_boxlist + + +def clip_to_window(boxlist, window): + """Clip bounding boxes to a window. + + This op clips input bounding boxes (represented by bounding box + corners) to a window, optionally filtering out boxes that do not + overlap at all with the window. + + Args: + boxlist: BoxList holding M_in boxes + window: a numpy array of shape [4] representing the + [y_min, x_min, y_max, x_max] window to which the op + should clip boxes. + + Returns: + a BoxList holding M_out boxes where M_out <= M_in + """ + y_min, x_min, y_max, x_max = np.array_split(boxlist.get(), 4, axis=1) + win_y_min = window[0] + win_x_min = window[1] + win_y_max = window[2] + win_x_max = window[3] + y_min_clipped = np.fmax(np.fmin(y_min, win_y_max), win_y_min) + y_max_clipped = np.fmax(np.fmin(y_max, win_y_max), win_y_min) + x_min_clipped = np.fmax(np.fmin(x_min, win_x_max), win_x_min) + x_max_clipped = np.fmax(np.fmin(x_max, win_x_max), win_x_min) + clipped = np_box_list.BoxList( + np.hstack([y_min_clipped, x_min_clipped, y_max_clipped, x_max_clipped])) + clipped = _copy_extra_fields(clipped, boxlist) + areas = area(clipped) + nonzero_area_indices = np.reshape(np.nonzero(np.greater(areas, 0.0)), + [-1]).astype(np.int32) + return gather(clipped, nonzero_area_indices) + + +def prune_non_overlapping_boxes(boxlist1, boxlist2, minoverlap=0.0): + """Prunes the boxes in boxlist1 that overlap less than thresh with boxlist2. + + For each box in boxlist1, we want its IOA to be more than minoverlap with + at least one of the boxes in boxlist2. If it does not, we remove it. + + Args: + boxlist1: BoxList holding N boxes. + boxlist2: BoxList holding M boxes. + minoverlap: Minimum required overlap between boxes, to count them as + overlapping. + + Returns: + A pruned boxlist with size [N', 4]. + """ + intersection_over_area = ioa(boxlist2, boxlist1) # [M, N] tensor + intersection_over_area = np.amax(intersection_over_area, axis=0) # [N] tensor + keep_bool = np.greater_equal(intersection_over_area, np.array(minoverlap)) + keep_inds = np.nonzero(keep_bool)[0] + new_boxlist1 = gather(boxlist1, keep_inds) + return new_boxlist1 + + +def prune_outside_window(boxlist, window): + """Prunes bounding boxes that fall outside a given window. + + This function prunes bounding boxes that even partially fall outside the given + window. See also ClipToWindow which only prunes bounding boxes that fall + completely outside the window, and clips any bounding boxes that partially + overflow. + + Args: + boxlist: a BoxList holding M_in boxes. + window: a numpy array of size 4, representing [ymin, xmin, ymax, xmax] + of the window. + + Returns: + pruned_corners: a tensor with shape [M_out, 4] where M_out <= M_in. + valid_indices: a tensor with shape [M_out] indexing the valid bounding boxes + in the input tensor. + """ + + y_min, x_min, y_max, x_max = np.array_split(boxlist.get(), 4, axis=1) + win_y_min = window[0] + win_x_min = window[1] + win_y_max = window[2] + win_x_max = window[3] + coordinate_violations = np.hstack([np.less(y_min, win_y_min), + np.less(x_min, win_x_min), + np.greater(y_max, win_y_max), + np.greater(x_max, win_x_max)]) + valid_indices = np.reshape( + np.where(np.logical_not(np.max(coordinate_violations, axis=1))), [-1]) + return gather(boxlist, valid_indices), valid_indices + + +def concatenate(boxlists, fields=None): + """Concatenate list of BoxLists. + + This op concatenates a list of input BoxLists into a larger BoxList. It also + handles concatenation of BoxList fields as long as the field tensor shapes + are equal except for the first dimension. + + Args: + boxlists: list of BoxList objects + fields: optional list of fields to also concatenate. By default, all + fields from the first BoxList in the list are included in the + concatenation. + + Returns: + a BoxList with number of boxes equal to + sum([boxlist.num_boxes() for boxlist in BoxList]) + Raises: + ValueError: if boxlists is invalid (i.e., is not a list, is empty, or + contains non BoxList objects), or if requested fields are not contained in + all boxlists + """ + if not isinstance(boxlists, list): + raise ValueError('boxlists should be a list') + if not boxlists: + raise ValueError('boxlists should have nonzero length') + for boxlist in boxlists: + if not isinstance(boxlist, np_box_list.BoxList): + raise ValueError('all elements of boxlists should be BoxList objects') + concatenated = np_box_list.BoxList( + np.vstack([boxlist.get() for boxlist in boxlists])) + if fields is None: + fields = boxlists[0].get_extra_fields() + for field in fields: + first_field_shape = boxlists[0].get_field(field).shape + first_field_shape = first_field_shape[1:] + for boxlist in boxlists: + if not boxlist.has_field(field): + raise ValueError('boxlist must contain all requested fields') + field_shape = boxlist.get_field(field).shape + field_shape = field_shape[1:] + if field_shape != first_field_shape: + raise ValueError('field %s must have same shape for all boxlists ' + 'except for the 0th dimension.' % field) + concatenated_field = np.concatenate( + [boxlist.get_field(field) for boxlist in boxlists], axis=0) + concatenated.add_field(field, concatenated_field) + return concatenated + + +def filter_scores_greater_than(boxlist, thresh): + """Filter to keep only boxes with score exceeding a given threshold. + + This op keeps the collection of boxes whose corresponding scores are + greater than the input threshold. + + Args: + boxlist: BoxList holding N boxes. Must contain a 'scores' field + representing detection scores. + thresh: scalar threshold + + Returns: + a BoxList holding M boxes where M <= N + + Raises: + ValueError: if boxlist not a BoxList object or if it does not + have a scores field + """ + if not isinstance(boxlist, np_box_list.BoxList): + raise ValueError('boxlist must be a BoxList') + if not boxlist.has_field('scores'): + raise ValueError('input boxlist must have \'scores\' field') + scores = boxlist.get_field('scores') + if len(scores.shape) > 2: + raise ValueError('Scores should have rank 1 or 2') + if len(scores.shape) == 2 and scores.shape[1] != 1: + raise ValueError('Scores should have rank 1 or have shape ' + 'consistent with [None, 1]') + high_score_indices = np.reshape(np.where(np.greater(scores, thresh)), + [-1]).astype(np.int32) + return gather(boxlist, high_score_indices) + + +def change_coordinate_frame(boxlist, window): + """Change coordinate frame of the boxlist to be relative to window's frame. + + Given a window of the form [ymin, xmin, ymax, xmax], + changes bounding box coordinates from boxlist to be relative to this window + (e.g., the min corner maps to (0,0) and the max corner maps to (1,1)). + + An example use case is data augmentation: where we are given groundtruth + boxes (boxlist) and would like to randomly crop the image to some + window (window). In this case we need to change the coordinate frame of + each groundtruth box to be relative to this new window. + + Args: + boxlist: A BoxList object holding N boxes. + window: a size 4 1-D numpy array. + + Returns: + Returns a BoxList object with N boxes. + """ + win_height = window[2] - window[0] + win_width = window[3] - window[1] + boxlist_new = scale( + np_box_list.BoxList(boxlist.get() - + [window[0], window[1], window[0], window[1]]), + 1.0 / win_height, 1.0 / win_width) + _copy_extra_fields(boxlist_new, boxlist) + + return boxlist_new + + +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 _update_valid_indices_by_removing_high_iou_boxes( + selected_indices, is_index_valid, intersect_over_union, threshold): + max_iou = np.max(intersect_over_union[:, selected_indices], axis=1) + return np.logical_and(is_index_valid, max_iou <= threshold) diff --git a/object_detection/utils/np_box_list_ops_test.py b/object_detection/utils/np_box_list_ops_test.py new file mode 100644 index 000000000..24a2cc8cf --- /dev/null +++ b/object_detection/utils/np_box_list_ops_test.py @@ -0,0 +1,414 @@ +# 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.utils.np_box_list_ops.""" + +import numpy as np +import tensorflow as tf + +from object_detection.utils import np_box_list +from object_detection.utils import np_box_list_ops + + +class AreaRelatedTest(tf.test.TestCase): + + def setUp(self): + boxes1 = np.array([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]], + dtype=float) + boxes2 = np.array([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]], + dtype=float) + self.boxlist1 = np_box_list.BoxList(boxes1) + self.boxlist2 = np_box_list.BoxList(boxes2) + + def test_area(self): + areas = np_box_list_ops.area(self.boxlist1) + expected_areas = np.array([6.0, 5.0], dtype=float) + self.assertAllClose(expected_areas, areas) + + def test_intersection(self): + intersection = np_box_list_ops.intersection(self.boxlist1, self.boxlist2) + expected_intersection = np.array([[2.0, 0.0, 6.0], [1.0, 0.0, 5.0]], + dtype=float) + self.assertAllClose(intersection, expected_intersection) + + def test_iou(self): + iou = np_box_list_ops.iou(self.boxlist1, self.boxlist2) + expected_iou = np.array([[2.0 / 16.0, 0.0, 6.0 / 400.0], + [1.0 / 16.0, 0.0, 5.0 / 400.0]], + dtype=float) + self.assertAllClose(iou, expected_iou) + + def test_ioa(self): + boxlist1 = np_box_list.BoxList( + np.array( + [[0.25, 0.25, 0.75, 0.75], [0.0, 0.0, 0.5, 0.75]], dtype= + np.float32)) + boxlist2 = np_box_list.BoxList( + np.array( + [[0.5, 0.25, 1.0, 1.0], [0.0, 0.0, 1.0, 1.0]], dtype=np.float32)) + ioa21 = np_box_list_ops.ioa(boxlist2, boxlist1) + expected_ioa21 = np.array([[0.5, 0.0], + [1.0, 1.0]], + dtype=np.float32) + self.assertAllClose(ioa21, expected_ioa21) + + def test_scale(self): + boxlist = np_box_list.BoxList( + np.array( + [[0.25, 0.25, 0.75, 0.75], [0.0, 0.0, 0.5, 0.75]], dtype= + np.float32)) + boxlist_scaled = np_box_list_ops.scale(boxlist, 2.0, 3.0) + expected_boxlist_scaled = np_box_list.BoxList( + np.array( + [[0.5, 0.75, 1.5, 2.25], [0.0, 0.0, 1.0, 2.25]], dtype=np.float32)) + self.assertAllClose(expected_boxlist_scaled.get(), boxlist_scaled.get()) + + def test_clip_to_window(self): + boxlist = np_box_list.BoxList( + np.array( + [[0.25, 0.25, 0.75, 0.75], [0.0, 0.0, 0.5, 0.75], + [-0.2, -0.3, 0.7, 1.5]], + dtype=np.float32)) + boxlist_clipped = np_box_list_ops.clip_to_window(boxlist, + [0.0, 0.0, 1.0, 1.0]) + expected_boxlist_clipped = np_box_list.BoxList( + np.array( + [[0.25, 0.25, 0.75, 0.75], [0.0, 0.0, 0.5, 0.75], + [0.0, 0.0, 0.7, 1.0]], + dtype=np.float32)) + self.assertAllClose(expected_boxlist_clipped.get(), boxlist_clipped.get()) + + def test_prune_outside_window(self): + boxlist = np_box_list.BoxList( + np.array( + [[0.25, 0.25, 0.75, 0.75], [0.0, 0.0, 0.5, 0.75], + [-0.2, -0.3, 0.7, 1.5]], + dtype=np.float32)) + boxlist_pruned, _ = np_box_list_ops.prune_outside_window( + boxlist, [0.0, 0.0, 1.0, 1.0]) + expected_boxlist_pruned = np_box_list.BoxList( + np.array( + [[0.25, 0.25, 0.75, 0.75], [0.0, 0.0, 0.5, 0.75]], dtype= + np.float32)) + self.assertAllClose(expected_boxlist_pruned.get(), boxlist_pruned.get()) + + def test_concatenate(self): + boxlist1 = np_box_list.BoxList( + np.array( + [[0.25, 0.25, 0.75, 0.75], [0.0, 0.0, 0.5, 0.75]], dtype= + np.float32)) + boxlist2 = np_box_list.BoxList( + np.array( + [[0.5, 0.25, 1.0, 1.0], [0.0, 0.0, 1.0, 1.0]], dtype=np.float32)) + boxlists = [boxlist1, boxlist2] + boxlist_concatenated = np_box_list_ops.concatenate(boxlists) + boxlist_concatenated_expected = np_box_list.BoxList( + np.array( + [[0.25, 0.25, 0.75, 0.75], [0.0, 0.0, 0.5, 0.75], + [0.5, 0.25, 1.0, 1.0], [0.0, 0.0, 1.0, 1.0]], + dtype=np.float32)) + self.assertAllClose(boxlist_concatenated_expected.get(), + boxlist_concatenated.get()) + + def test_change_coordinate_frame(self): + boxlist = np_box_list.BoxList( + np.array( + [[0.25, 0.25, 0.75, 0.75], [0.0, 0.0, 0.5, 0.75]], dtype= + np.float32)) + boxlist_coord = np_box_list_ops.change_coordinate_frame( + boxlist, np.array([0, 0, 0.5, 0.5], dtype=np.float32)) + expected_boxlist_coord = np_box_list.BoxList( + np.array([[0.5, 0.5, 1.5, 1.5], [0, 0, 1.0, 1.5]], dtype=np.float32)) + self.assertAllClose(boxlist_coord.get(), expected_boxlist_coord.get()) + + def test_filter_scores_greater_than(self): + boxlist = np_box_list.BoxList( + np.array( + [[0.25, 0.25, 0.75, 0.75], [0.0, 0.0, 0.5, 0.75]], dtype= + np.float32)) + boxlist.add_field('scores', np.array([0.8, 0.2], np.float32)) + boxlist_greater = np_box_list_ops.filter_scores_greater_than(boxlist, 0.5) + + expected_boxlist_greater = np_box_list.BoxList( + np.array([[0.25, 0.25, 0.75, 0.75]], dtype=np.float32)) + + self.assertAllClose(boxlist_greater.get(), expected_boxlist_greater.get()) + + +class GatherOpsTest(tf.test.TestCase): + + def setUp(self): + boxes = np.array([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]], + dtype=float) + self.boxlist = np_box_list.BoxList(boxes) + self.boxlist.add_field('scores', np.array([0.5, 0.7, 0.9], dtype=float)) + self.boxlist.add_field('labels', + np.array([[0, 0, 0, 1, 0], [0, 1, 0, 0, 0], + [0, 0, 0, 0, 1]], + dtype=int)) + + def test_gather_with_out_of_range_indices(self): + indices = np.array([3, 1], dtype=int) + boxlist = self.boxlist + with self.assertRaises(ValueError): + np_box_list_ops.gather(boxlist, indices) + + def test_gather_with_invalid_multidimensional_indices(self): + indices = np.array([[0, 1], [1, 2]], dtype=int) + boxlist = self.boxlist + with self.assertRaises(ValueError): + np_box_list_ops.gather(boxlist, indices) + + def test_gather_without_fields_specified(self): + indices = np.array([2, 0, 1], dtype=int) + boxlist = self.boxlist + subboxlist = np_box_list_ops.gather(boxlist, indices) + + expected_scores = np.array([0.9, 0.5, 0.7], dtype=float) + self.assertAllClose(expected_scores, subboxlist.get_field('scores')) + + expected_boxes = np.array([[0.0, 0.0, 20.0, 20.0], [3.0, 4.0, 6.0, 8.0], + [14.0, 14.0, 15.0, 15.0]], + dtype=float) + self.assertAllClose(expected_boxes, subboxlist.get()) + + expected_labels = np.array([[0, 0, 0, 0, 1], [0, 0, 0, 1, 0], + [0, 1, 0, 0, 0]], + dtype=int) + self.assertAllClose(expected_labels, subboxlist.get_field('labels')) + + def test_gather_with_invalid_field_specified(self): + indices = np.array([2, 0, 1], dtype=int) + boxlist = self.boxlist + + with self.assertRaises(ValueError): + np_box_list_ops.gather(boxlist, indices, 'labels') + + with self.assertRaises(ValueError): + np_box_list_ops.gather(boxlist, indices, ['objectness']) + + def test_gather_with_fields_specified(self): + indices = np.array([2, 0, 1], dtype=int) + boxlist = self.boxlist + subboxlist = np_box_list_ops.gather(boxlist, indices, ['labels']) + + self.assertFalse(subboxlist.has_field('scores')) + + expected_boxes = np.array([[0.0, 0.0, 20.0, 20.0], [3.0, 4.0, 6.0, 8.0], + [14.0, 14.0, 15.0, 15.0]], + dtype=float) + self.assertAllClose(expected_boxes, subboxlist.get()) + + expected_labels = np.array([[0, 0, 0, 0, 1], [0, 0, 0, 1, 0], + [0, 1, 0, 0, 0]], + dtype=int) + self.assertAllClose(expected_labels, subboxlist.get_field('labels')) + + +class SortByFieldTest(tf.test.TestCase): + + def setUp(self): + boxes = np.array([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]], + dtype=float) + self.boxlist = np_box_list.BoxList(boxes) + self.boxlist.add_field('scores', np.array([0.5, 0.9, 0.4], dtype=float)) + self.boxlist.add_field('labels', + np.array([[0, 0, 0, 1, 0], [0, 1, 0, 0, 0], + [0, 0, 0, 0, 1]], + dtype=int)) + + def test_with_invalid_field(self): + with self.assertRaises(ValueError): + np_box_list_ops.sort_by_field(self.boxlist, 'objectness') + with self.assertRaises(ValueError): + np_box_list_ops.sort_by_field(self.boxlist, 'labels') + + def test_with_invalid_sorting_order(self): + with self.assertRaises(ValueError): + np_box_list_ops.sort_by_field(self.boxlist, 'scores', 'Descending') + + def test_with_descending_sorting(self): + sorted_boxlist = np_box_list_ops.sort_by_field(self.boxlist, 'scores') + + expected_boxes = np.array([[14.0, 14.0, 15.0, 15.0], [3.0, 4.0, 6.0, 8.0], + [0.0, 0.0, 20.0, 20.0]], + dtype=float) + self.assertAllClose(expected_boxes, sorted_boxlist.get()) + + expected_scores = np.array([0.9, 0.5, 0.4], dtype=float) + self.assertAllClose(expected_scores, sorted_boxlist.get_field('scores')) + + def test_with_ascending_sorting(self): + sorted_boxlist = np_box_list_ops.sort_by_field( + self.boxlist, 'scores', np_box_list_ops.SortOrder.ASCEND) + + expected_boxes = np.array([[0.0, 0.0, 20.0, 20.0], + [3.0, 4.0, 6.0, 8.0], + [14.0, 14.0, 15.0, 15.0],], + dtype=float) + self.assertAllClose(expected_boxes, sorted_boxlist.get()) + + expected_scores = np.array([0.4, 0.5, 0.9], dtype=float) + self.assertAllClose(expected_scores, sorted_boxlist.get_field('scores')) + + +class NonMaximumSuppressionTest(tf.test.TestCase): + + def setUp(self): + self._boxes = np.array([[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]], + dtype=float) + self._boxlist = np_box_list.BoxList(self._boxes) + + def test_with_no_scores_field(self): + boxlist = np_box_list.BoxList(self._boxes) + max_output_size = 3 + iou_threshold = 0.5 + + with self.assertRaises(ValueError): + np_box_list_ops.non_max_suppression( + boxlist, max_output_size, iou_threshold) + + def test_nms_disabled_max_output_size_equals_three(self): + boxlist = np_box_list.BoxList(self._boxes) + boxlist.add_field('scores', + np.array([.9, .75, .6, .95, .2, .3], dtype=float)) + max_output_size = 3 + iou_threshold = 1. # No NMS + + expected_boxes = np.array([[0, 10, 1, 11], [0, 0, 1, 1], [0, 0.1, 1, 1.1]], + dtype=float) + nms_boxlist = np_box_list_ops.non_max_suppression( + boxlist, max_output_size, iou_threshold) + self.assertAllClose(nms_boxlist.get(), expected_boxes) + + def test_select_from_three_clusters(self): + boxlist = np_box_list.BoxList(self._boxes) + boxlist.add_field('scores', + np.array([.9, .75, .6, .95, .2, .3], dtype=float)) + max_output_size = 3 + iou_threshold = 0.5 + + expected_boxes = np.array([[0, 10, 1, 11], [0, 0, 1, 1], [0, 100, 1, 101]], + dtype=float) + nms_boxlist = np_box_list_ops.non_max_suppression( + boxlist, max_output_size, iou_threshold) + self.assertAllClose(nms_boxlist.get(), expected_boxes) + + def test_select_at_most_two_from_three_clusters(self): + boxlist = np_box_list.BoxList(self._boxes) + boxlist.add_field('scores', + np.array([.9, .75, .6, .95, .5, .3], dtype=float)) + max_output_size = 2 + iou_threshold = 0.5 + + expected_boxes = np.array([[0, 10, 1, 11], [0, 0, 1, 1]], dtype=float) + nms_boxlist = np_box_list_ops.non_max_suppression( + boxlist, max_output_size, iou_threshold) + self.assertAllClose(nms_boxlist.get(), expected_boxes) + + def test_select_at_most_thirty_from_three_clusters(self): + boxlist = np_box_list.BoxList(self._boxes) + boxlist.add_field('scores', + np.array([.9, .75, .6, .95, .5, .3], dtype=float)) + max_output_size = 30 + iou_threshold = 0.5 + + expected_boxes = np.array([[0, 10, 1, 11], [0, 0, 1, 1], [0, 100, 1, 101]], + dtype=float) + nms_boxlist = np_box_list_ops.non_max_suppression( + boxlist, max_output_size, iou_threshold) + self.assertAllClose(nms_boxlist.get(), expected_boxes) + + def test_select_from_ten_indentical_boxes(self): + boxes = np.array(10 * [[0, 0, 1, 1]], dtype=float) + boxlist = np_box_list.BoxList(boxes) + boxlist.add_field('scores', np.array(10 * [0.8])) + iou_threshold = .5 + max_output_size = 3 + expected_boxes = np.array([[0, 0, 1, 1]], dtype=float) + nms_boxlist = np_box_list_ops.non_max_suppression( + boxlist, max_output_size, iou_threshold) + self.assertAllClose(nms_boxlist.get(), expected_boxes) + + def test_different_iou_threshold(self): + boxes = np.array([[0, 0, 20, 100], [0, 0, 20, 80], [200, 200, 210, 300], + [200, 200, 210, 250]], + dtype=float) + boxlist = np_box_list.BoxList(boxes) + boxlist.add_field('scores', np.array([0.9, 0.8, 0.7, 0.6])) + max_output_size = 4 + + iou_threshold = .4 + expected_boxes = np.array([[0, 0, 20, 100], + [200, 200, 210, 300],], + dtype=float) + nms_boxlist = np_box_list_ops.non_max_suppression( + boxlist, max_output_size, iou_threshold) + self.assertAllClose(nms_boxlist.get(), expected_boxes) + + iou_threshold = .5 + expected_boxes = np.array([[0, 0, 20, 100], [200, 200, 210, 300], + [200, 200, 210, 250]], + dtype=float) + nms_boxlist = np_box_list_ops.non_max_suppression( + boxlist, max_output_size, iou_threshold) + self.assertAllClose(nms_boxlist.get(), expected_boxes) + + iou_threshold = .8 + expected_boxes = np.array([[0, 0, 20, 100], [0, 0, 20, 80], + [200, 200, 210, 300], [200, 200, 210, 250]], + dtype=float) + nms_boxlist = np_box_list_ops.non_max_suppression( + boxlist, max_output_size, iou_threshold) + self.assertAllClose(nms_boxlist.get(), expected_boxes) + + def test_multiclass_nms(self): + boxlist = np_box_list.BoxList( + np.array( + [[0.2, 0.4, 0.8, 0.8], [0.4, 0.2, 0.8, 0.8], [0.6, 0.0, 1.0, 1.0]], + dtype=np.float32)) + scores = np.array([[-0.2, 0.1, 0.5, -0.4, 0.3], + [0.7, -0.7, 0.6, 0.2, -0.9], + [0.4, 0.34, -0.9, 0.2, 0.31]], + dtype=np.float32) + boxlist.add_field('scores', scores) + boxlist_clean = np_box_list_ops.multi_class_non_max_suppression( + boxlist, score_thresh=0.25, iou_thresh=0.1, max_output_size=3) + + scores_clean = boxlist_clean.get_field('scores') + classes_clean = boxlist_clean.get_field('classes') + boxes = boxlist_clean.get() + expected_scores = np.array([0.7, 0.6, 0.34, 0.31]) + expected_classes = np.array([0, 2, 1, 4]) + expected_boxes = np.array([[0.4, 0.2, 0.8, 0.8], + [0.4, 0.2, 0.8, 0.8], + [0.6, 0.0, 1.0, 1.0], + [0.6, 0.0, 1.0, 1.0]], + dtype=np.float32) + self.assertAllClose(scores_clean, expected_scores) + self.assertAllClose(classes_clean, expected_classes) + self.assertAllClose(boxes, expected_boxes) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/utils/np_box_list_test.py b/object_detection/utils/np_box_list_test.py new file mode 100644 index 000000000..bb0ee5d28 --- /dev/null +++ b/object_detection/utils/np_box_list_test.py @@ -0,0 +1,135 @@ +# 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.utils.np_box_list_test.""" + +import numpy as np +import tensorflow as tf + +from object_detection.utils import np_box_list + + +class BoxListTest(tf.test.TestCase): + + def test_invalid_box_data(self): + with self.assertRaises(ValueError): + np_box_list.BoxList([0, 0, 1, 1]) + + with self.assertRaises(ValueError): + np_box_list.BoxList(np.array([[0, 0, 1, 1]], dtype=int)) + + with self.assertRaises(ValueError): + np_box_list.BoxList(np.array([0, 1, 1, 3, 4], dtype=float)) + + with self.assertRaises(ValueError): + np_box_list.BoxList(np.array([[0, 1, 1, 3], [3, 1, 1, 5]], dtype=float)) + + def test_has_field_with_existed_field(self): + boxes = np.array([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]], + dtype=float) + boxlist = np_box_list.BoxList(boxes) + self.assertTrue(boxlist.has_field('boxes')) + + def test_has_field_with_nonexisted_field(self): + boxes = np.array([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]], + dtype=float) + boxlist = np_box_list.BoxList(boxes) + self.assertFalse(boxlist.has_field('scores')) + + def test_get_field_with_existed_field(self): + boxes = np.array([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]], + dtype=float) + boxlist = np_box_list.BoxList(boxes) + self.assertTrue(np.allclose(boxlist.get_field('boxes'), boxes)) + + def test_get_field_with_nonexited_field(self): + boxes = np.array([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]], + dtype=float) + boxlist = np_box_list.BoxList(boxes) + with self.assertRaises(ValueError): + boxlist.get_field('scores') + + +class AddExtraFieldTest(tf.test.TestCase): + + def setUp(self): + boxes = np.array([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]], + dtype=float) + self.boxlist = np_box_list.BoxList(boxes) + + def test_add_already_existed_field(self): + with self.assertRaises(ValueError): + self.boxlist.add_field('boxes', np.array([[0, 0, 0, 1, 0]], dtype=float)) + + def test_add_invalid_field_data(self): + with self.assertRaises(ValueError): + self.boxlist.add_field('scores', np.array([0.5, 0.7], dtype=float)) + with self.assertRaises(ValueError): + self.boxlist.add_field('scores', + np.array([0.5, 0.7, 0.9, 0.1], dtype=float)) + + def test_add_single_dimensional_field_data(self): + boxlist = self.boxlist + scores = np.array([0.5, 0.7, 0.9], dtype=float) + boxlist.add_field('scores', scores) + self.assertTrue(np.allclose(scores, self.boxlist.get_field('scores'))) + + def test_add_multi_dimensional_field_data(self): + boxlist = self.boxlist + labels = np.array([[0, 0, 0, 1, 0], [0, 1, 0, 0, 0], [0, 0, 0, 0, 1]], + dtype=int) + boxlist.add_field('labels', labels) + self.assertTrue(np.allclose(labels, self.boxlist.get_field('labels'))) + + def test_get_extra_fields(self): + boxlist = self.boxlist + self.assertSameElements(boxlist.get_extra_fields(), []) + + scores = np.array([0.5, 0.7, 0.9], dtype=float) + boxlist.add_field('scores', scores) + self.assertSameElements(boxlist.get_extra_fields(), ['scores']) + + labels = np.array([[0, 0, 0, 1, 0], [0, 1, 0, 0, 0], [0, 0, 0, 0, 1]], + dtype=int) + boxlist.add_field('labels', labels) + self.assertSameElements(boxlist.get_extra_fields(), ['scores', 'labels']) + + def test_get_coordinates(self): + y_min, x_min, y_max, x_max = self.boxlist.get_coordinates() + + expected_y_min = np.array([3.0, 14.0, 0.0], dtype=float) + expected_x_min = np.array([4.0, 14.0, 0.0], dtype=float) + expected_y_max = np.array([6.0, 15.0, 20.0], dtype=float) + expected_x_max = np.array([8.0, 15.0, 20.0], dtype=float) + + self.assertTrue(np.allclose(y_min, expected_y_min)) + self.assertTrue(np.allclose(x_min, expected_x_min)) + self.assertTrue(np.allclose(y_max, expected_y_max)) + self.assertTrue(np.allclose(x_max, expected_x_max)) + + def test_num_boxes(self): + boxes = np.array([[0., 0., 100., 100.], [10., 30., 50., 70.]], dtype=float) + boxlist = np_box_list.BoxList(boxes) + expected_num_boxes = 2 + self.assertEquals(boxlist.num_boxes(), expected_num_boxes) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/utils/np_box_ops.py b/object_detection/utils/np_box_ops.py new file mode 100644 index 000000000..b4b46a756 --- /dev/null +++ b/object_detection/utils/np_box_ops.py @@ -0,0 +1,97 @@ +# 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. +# ============================================================================== + +"""Operations for [N, 4] numpy arrays representing bounding boxes. + +Example box operations that are supported: + * Areas: compute bounding box areas + * IOU: pairwise intersection-over-union scores +""" +import numpy as np + + +def area(boxes): + """Computes area of boxes. + + Args: + boxes: Numpy array with shape [N, 4] holding N boxes + + Returns: + a numpy array with shape [N*1] representing box areas + """ + return (boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1]) + + +def intersection(boxes1, boxes2): + """Compute pairwise intersection areas between boxes. + + Args: + boxes1: a numpy array with shape [N, 4] holding N boxes + boxes2: a numpy array with shape [M, 4] holding M boxes + + Returns: + a numpy array with shape [N*M] representing pairwise intersection area + """ + [y_min1, x_min1, y_max1, x_max1] = np.split(boxes1, 4, axis=1) + [y_min2, x_min2, y_max2, x_max2] = np.split(boxes2, 4, axis=1) + + all_pairs_min_ymax = np.minimum(y_max1, np.transpose(y_max2)) + all_pairs_max_ymin = np.maximum(y_min1, np.transpose(y_min2)) + intersect_heights = np.maximum( + np.zeros(all_pairs_max_ymin.shape), + all_pairs_min_ymax - all_pairs_max_ymin) + all_pairs_min_xmax = np.minimum(x_max1, np.transpose(x_max2)) + all_pairs_max_xmin = np.maximum(x_min1, np.transpose(x_min2)) + intersect_widths = np.maximum( + np.zeros(all_pairs_max_xmin.shape), + all_pairs_min_xmax - all_pairs_max_xmin) + return intersect_heights * intersect_widths + + +def iou(boxes1, boxes2): + """Computes pairwise intersection-over-union between box collections. + + Args: + boxes1: a numpy array with shape [N, 4] holding N boxes. + boxes2: a numpy array with shape [M, 4] holding N boxes. + + Returns: + a numpy array with shape [N, M] representing pairwise iou scores. + """ + intersect = intersection(boxes1, boxes2) + area1 = area(boxes1) + area2 = area(boxes2) + union = np.expand_dims(area1, axis=1) + np.expand_dims( + area2, axis=0) - intersect + return intersect / union + + +def ioa(boxes1, boxes2): + """Computes pairwise intersection-over-area between box collections. + + Intersection-over-area (ioa) between two boxes box1 and box2 is defined as + their intersection area over box2's area. Note that ioa is not symmetric, + that is, IOA(box1, box2) != IOA(box2, box1). + + Args: + boxes1: a numpy array with shape [N, 4] holding N boxes. + boxes2: a numpy array with shape [M, 4] holding N boxes. + + Returns: + a numpy array with shape [N, M] representing pairwise ioa scores. + """ + intersect = intersection(boxes1, boxes2) + areas = np.expand_dims(area(boxes2), axis=0) + return intersect / areas diff --git a/object_detection/utils/np_box_ops_test.py b/object_detection/utils/np_box_ops_test.py new file mode 100644 index 000000000..730f3d205 --- /dev/null +++ b/object_detection/utils/np_box_ops_test.py @@ -0,0 +1,68 @@ +# 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.np_box_ops.""" + +import numpy as np +import tensorflow as tf + +from object_detection.utils import np_box_ops + + +class BoxOpsTests(tf.test.TestCase): + + def setUp(self): + boxes1 = np.array([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]], + dtype=float) + boxes2 = np.array([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]], + dtype=float) + self.boxes1 = boxes1 + self.boxes2 = boxes2 + + def testArea(self): + areas = np_box_ops.area(self.boxes1) + expected_areas = np.array([6.0, 5.0], dtype=float) + self.assertAllClose(expected_areas, areas) + + def testIntersection(self): + intersection = np_box_ops.intersection(self.boxes1, self.boxes2) + expected_intersection = np.array([[2.0, 0.0, 6.0], [1.0, 0.0, 5.0]], + dtype=float) + self.assertAllClose(intersection, expected_intersection) + + def testIOU(self): + iou = np_box_ops.iou(self.boxes1, self.boxes2) + expected_iou = np.array([[2.0 / 16.0, 0.0, 6.0 / 400.0], + [1.0 / 16.0, 0.0, 5.0 / 400.0]], + dtype=float) + self.assertAllClose(iou, expected_iou) + + def testIOA(self): + boxes1 = np.array([[0.25, 0.25, 0.75, 0.75], + [0.0, 0.0, 0.5, 0.75]], + dtype=np.float32) + boxes2 = np.array([[0.5, 0.25, 1.0, 1.0], + [0.0, 0.0, 1.0, 1.0]], + dtype=np.float32) + ioa21 = np_box_ops.ioa(boxes2, boxes1) + expected_ioa21 = np.array([[0.5, 0.0], + [1.0, 1.0]], + dtype=np.float32) + self.assertAllClose(ioa21, expected_ioa21) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/utils/object_detection_evaluation.py b/object_detection/utils/object_detection_evaluation.py new file mode 100644 index 000000000..b2b14844b --- /dev/null +++ b/object_detection/utils/object_detection_evaluation.py @@ -0,0 +1,233 @@ +# 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. +# ============================================================================== + +"""object_detection_evaluation module. + +ObjectDetectionEvaluation is a class which manages ground truth information of a +object detection dataset, and computes frequently used detection metrics such as +Precision, Recall, CorLoc of the provided detection results. +It supports the following operations: +1) Add ground truth information of images sequentially. +2) Add detection result of images sequentially. +3) Evaluate detection metrics on already inserted detection results. +4) Write evaluation result into a pickle file for future processing or + visualization. + +Note: This module operates on numpy boxes and box lists. +""" + +import logging +import numpy as np + +from object_detection.utils import metrics +from object_detection.utils import per_image_evaluation + + +class ObjectDetectionEvaluation(object): + """Evaluate Object Detection Result.""" + + def __init__(self, + num_groundtruth_classes, + matching_iou_threshold=0.5, + nms_iou_threshold=1.0, + nms_max_output_boxes=10000): + self.per_image_eval = per_image_evaluation.PerImageEvaluation( + num_groundtruth_classes, matching_iou_threshold, nms_iou_threshold, + nms_max_output_boxes) + self.num_class = num_groundtruth_classes + + self.groundtruth_boxes = {} + self.groundtruth_class_labels = {} + self.groundtruth_is_difficult_list = {} + self.num_gt_instances_per_class = np.zeros(self.num_class, dtype=int) + self.num_gt_imgs_per_class = np.zeros(self.num_class, dtype=int) + + self.detection_keys = set() + self.scores_per_class = [[] for _ in range(self.num_class)] + self.tp_fp_labels_per_class = [[] for _ in range(self.num_class)] + self.num_images_correctly_detected_per_class = np.zeros(self.num_class) + self.average_precision_per_class = np.empty(self.num_class, dtype=float) + self.average_precision_per_class.fill(np.nan) + self.precisions_per_class = [] + self.recalls_per_class = [] + self.corloc_per_class = np.ones(self.num_class, dtype=float) + + def clear_detections(self): + self.detection_keys = {} + self.scores_per_class = [[] for _ in range(self.num_class)] + self.tp_fp_labels_per_class = [[] for _ in range(self.num_class)] + self.num_images_correctly_detected_per_class = np.zeros(self.num_class) + self.average_precision_per_class = np.zeros(self.num_class, dtype=float) + self.precisions_per_class = [] + self.recalls_per_class = [] + self.corloc_per_class = np.ones(self.num_class, dtype=float) + + def add_single_ground_truth_image_info(self, + image_key, + groundtruth_boxes, + groundtruth_class_labels, + groundtruth_is_difficult_list=None): + """Add ground truth info of a single image into the evaluation database. + + Args: + image_key: sha256 key of image content + groundtruth_boxes: A numpy array of shape [M, 4] representing object box + coordinates[y_min, x_min, y_max, x_max] + groundtruth_class_labels: A 1-d numpy array of length M representing class + labels + groundtruth_is_difficult_list: A length M numpy boolean array denoting + whether a ground truth box is a difficult instance or not. To support + the case that no boxes are difficult, it is by default set as None. + """ + if image_key in self.groundtruth_boxes: + logging.warn( + 'image %s has already been added to the ground truth database.', + image_key) + return + + self.groundtruth_boxes[image_key] = groundtruth_boxes + self.groundtruth_class_labels[image_key] = groundtruth_class_labels + if groundtruth_is_difficult_list is None: + num_boxes = groundtruth_boxes.shape[0] + groundtruth_is_difficult_list = np.zeros(num_boxes, dtype=bool) + self.groundtruth_is_difficult_list[ + image_key] = groundtruth_is_difficult_list.astype(dtype=bool) + self._update_ground_truth_statistics(groundtruth_class_labels, + groundtruth_is_difficult_list) + + def add_single_detected_image_info(self, image_key, detected_boxes, + detected_scores, detected_class_labels): + """Add detected result of a single image into the evaluation database. + + Args: + image_key: sha256 key of image content + detected_boxes: A numpy array of shape [N, 4] representing detected box + coordinates[y_min, x_min, y_max, x_max] + detected_scores: A 1-d numpy array of length N representing classification + score + detected_class_labels: A 1-d numpy array of length N representing class + labels + Raises: + ValueError: if detected_boxes, detected_scores and detected_class_labels + do not have the same length. + """ + if (len(detected_boxes) != len(detected_scores) or + len(detected_boxes) != len(detected_class_labels)): + raise ValueError('detected_boxes, detected_scores and ' + 'detected_class_labels should all have same lengths. Got' + '[%d, %d, %d]' % len(detected_boxes), + len(detected_scores), len(detected_class_labels)) + + if image_key in self.detection_keys: + logging.warn( + 'image %s has already been added to the detection result database', + image_key) + return + + self.detection_keys.add(image_key) + if image_key in self.groundtruth_boxes: + groundtruth_boxes = self.groundtruth_boxes[image_key] + groundtruth_class_labels = self.groundtruth_class_labels[image_key] + groundtruth_is_difficult_list = self.groundtruth_is_difficult_list[ + image_key] + else: + groundtruth_boxes = np.empty(shape=[0, 4], dtype=float) + groundtruth_class_labels = np.array([], dtype=int) + groundtruth_is_difficult_list = np.array([], dtype=bool) + scores, tp_fp_labels, is_class_correctly_detected_in_image = ( + self.per_image_eval.compute_object_detection_metrics( + detected_boxes, detected_scores, detected_class_labels, + groundtruth_boxes, groundtruth_class_labels, + groundtruth_is_difficult_list)) + for i in range(self.num_class): + self.scores_per_class[i].append(scores[i]) + self.tp_fp_labels_per_class[i].append(tp_fp_labels[i]) + (self.num_images_correctly_detected_per_class + ) += is_class_correctly_detected_in_image + + def _update_ground_truth_statistics(self, groundtruth_class_labels, + groundtruth_is_difficult_list): + """Update grouth truth statitistics. + + 1. Difficult boxes are ignored when counting the number of ground truth + instances as done in Pascal VOC devkit. + 2. Difficult boxes are treated as normal boxes when computing CorLoc related + statitistics. + + Args: + groundtruth_class_labels: An integer numpy array of length M, + representing M class labels of object instances in ground truth + groundtruth_is_difficult_list: A boolean numpy array of length M denoting + whether a ground truth box is a difficult instance or not + """ + for class_index in range(self.num_class): + num_gt_instances = np.sum(groundtruth_class_labels[ + ~groundtruth_is_difficult_list] == class_index) + self.num_gt_instances_per_class[class_index] += num_gt_instances + if np.any(groundtruth_class_labels == class_index): + self.num_gt_imgs_per_class[class_index] += 1 + + def evaluate(self): + """Compute evaluation result. + + Returns: + average_precision_per_class: float numpy array of average precision for + each class. + mean_ap: mean average precision of all classes, float scalar + precisions_per_class: List of precisions, each precision is a float numpy + array + recalls_per_class: List of recalls, each recall is a float numpy array + corloc_per_class: numpy float array + mean_corloc: Mean CorLoc score for each class, float scalar + """ + if (self.num_gt_instances_per_class == 0).any(): + logging.warn( + 'The following classes have no ground truth examples: %s', + np.squeeze(np.argwhere(self.num_gt_instances_per_class == 0))) + for class_index in range(self.num_class): + if self.num_gt_instances_per_class[class_index] == 0: + continue + scores = np.concatenate(self.scores_per_class[class_index]) + tp_fp_labels = np.concatenate(self.tp_fp_labels_per_class[class_index]) + precision, recall = metrics.compute_precision_recall( + scores, tp_fp_labels, self.num_gt_instances_per_class[class_index]) + self.precisions_per_class.append(precision) + self.recalls_per_class.append(recall) + average_precision = metrics.compute_average_precision(precision, recall) + self.average_precision_per_class[class_index] = average_precision + + self.corloc_per_class = metrics.compute_cor_loc( + self.num_gt_imgs_per_class, + self.num_images_correctly_detected_per_class) + + mean_ap = np.nanmean(self.average_precision_per_class) + mean_corloc = np.nanmean(self.corloc_per_class) + return (self.average_precision_per_class, mean_ap, + self.precisions_per_class, self.recalls_per_class, + self.corloc_per_class, mean_corloc) + + def get_eval_result(self): + return EvalResult(self.average_precision_per_class, + self.precisions_per_class, self.recalls_per_class, + self.corloc_per_class) + + +class EvalResult(object): + + def __init__(self, average_precisions, precisions, recalls, all_corloc): + self.precisions = precisions + self.recalls = recalls + self.all_corloc = all_corloc + self.average_precisions = average_precisions diff --git a/object_detection/utils/object_detection_evaluation_test.py b/object_detection/utils/object_detection_evaluation_test.py new file mode 100644 index 000000000..12bfc6b9d --- /dev/null +++ b/object_detection/utils/object_detection_evaluation_test.py @@ -0,0 +1,125 @@ +# 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.utils.object_detection_evaluation.""" + +import numpy as np +import tensorflow as tf + +from object_detection.utils import object_detection_evaluation + + +class ObjectDetectionEvaluationTest(tf.test.TestCase): + + def setUp(self): + num_groundtruth_classes = 3 + self.od_eval = object_detection_evaluation.ObjectDetectionEvaluation( + num_groundtruth_classes) + + image_key1 = "img1" + groundtruth_boxes1 = np.array([[0, 0, 1, 1], [0, 0, 2, 2], [0, 0, 3, 3]], + dtype=float) + groundtruth_class_labels1 = np.array([0, 2, 0], dtype=int) + self.od_eval.add_single_ground_truth_image_info( + image_key1, groundtruth_boxes1, groundtruth_class_labels1) + image_key2 = "img2" + groundtruth_boxes2 = np.array([[10, 10, 11, 11], [500, 500, 510, 510], + [10, 10, 12, 12]], dtype=float) + groundtruth_class_labels2 = np.array([0, 0, 2], dtype=int) + groundtruth_is_difficult_list2 = np.array([False, True, False], dtype=bool) + self.od_eval.add_single_ground_truth_image_info( + image_key2, groundtruth_boxes2, groundtruth_class_labels2, + groundtruth_is_difficult_list2) + image_key3 = "img3" + groundtruth_boxes3 = np.array([[0, 0, 1, 1]], dtype=float) + groundtruth_class_labels3 = np.array([1], dtype=int) + self.od_eval.add_single_ground_truth_image_info( + image_key3, groundtruth_boxes3, groundtruth_class_labels3) + + image_key = "img2" + detected_boxes = np.array( + [[10, 10, 11, 11], [100, 100, 120, 120], [100, 100, 220, 220]], + dtype=float) + detected_class_labels = np.array([0, 0, 2], dtype=int) + detected_scores = np.array([0.7, 0.8, 0.9], dtype=float) + self.od_eval.add_single_detected_image_info( + image_key, detected_boxes, detected_scores, detected_class_labels) + + def test_add_single_ground_truth_image_info(self): + expected_num_gt_instances_per_class = np.array([3, 1, 2], dtype=int) + expected_num_gt_imgs_per_class = np.array([2, 1, 2], dtype=int) + self.assertTrue(np.array_equal(expected_num_gt_instances_per_class, + self.od_eval.num_gt_instances_per_class)) + self.assertTrue(np.array_equal(expected_num_gt_imgs_per_class, + self.od_eval.num_gt_imgs_per_class)) + groundtruth_boxes2 = np.array([[10, 10, 11, 11], [500, 500, 510, 510], + [10, 10, 12, 12]], dtype=float) + self.assertTrue(np.allclose(self.od_eval.groundtruth_boxes["img2"], + groundtruth_boxes2)) + groundtruth_is_difficult_list2 = np.array([False, True, False], dtype=bool) + self.assertTrue(np.allclose( + self.od_eval.groundtruth_is_difficult_list["img2"], + groundtruth_is_difficult_list2)) + groundtruth_class_labels1 = np.array([0, 2, 0], dtype=int) + self.assertTrue(np.array_equal(self.od_eval.groundtruth_class_labels[ + "img1"], groundtruth_class_labels1)) + + def test_add_single_detected_image_info(self): + expected_scores_per_class = [[np.array([0.8, 0.7], dtype=float)], [], + [np.array([0.9], dtype=float)]] + expected_tp_fp_labels_per_class = [[np.array([0, 1], dtype=bool)], [], + [np.array([0], dtype=bool)]] + expected_num_images_correctly_detected_per_class = np.array([0, 0, 0], + dtype=int) + for i in range(self.od_eval.num_class): + for j in range(len(expected_scores_per_class[i])): + self.assertTrue(np.allclose(expected_scores_per_class[i][j], + self.od_eval.scores_per_class[i][j])) + self.assertTrue(np.array_equal(expected_tp_fp_labels_per_class[i][ + j], self.od_eval.tp_fp_labels_per_class[i][j])) + self.assertTrue(np.array_equal( + expected_num_images_correctly_detected_per_class, + self.od_eval.num_images_correctly_detected_per_class)) + + def test_evaluate(self): + (average_precision_per_class, mean_ap, precisions_per_class, + recalls_per_class, corloc_per_class, + mean_corloc) = self.od_eval.evaluate() + expected_precisions_per_class = [np.array([0, 0.5], dtype=float), + np.array([], dtype=float), + np.array([0], dtype=float)] + expected_recalls_per_class = [ + np.array([0, 1. / 3.], dtype=float), np.array([], dtype=float), + np.array([0], dtype=float) + ] + expected_average_precision_per_class = np.array([1. / 6., 0, 0], + dtype=float) + expected_corloc_per_class = np.array([0, np.divide(0, 0), 0], dtype=float) + expected_mean_ap = 1. / 18 + expected_mean_corloc = 0.0 + for i in range(self.od_eval.num_class): + self.assertTrue(np.allclose(expected_precisions_per_class[i], + precisions_per_class[i])) + self.assertTrue(np.allclose(expected_recalls_per_class[i], + recalls_per_class[i])) + self.assertTrue(np.allclose(expected_average_precision_per_class, + average_precision_per_class)) + self.assertTrue(np.allclose(expected_corloc_per_class, corloc_per_class)) + self.assertAlmostEqual(expected_mean_ap, mean_ap) + self.assertAlmostEqual(expected_mean_corloc, mean_corloc) + + +if __name__ == "__main__": + tf.test.main() diff --git a/object_detection/utils/ops.py b/object_detection/utils/ops.py new file mode 100644 index 000000000..989cdf3c4 --- /dev/null +++ b/object_detection/utils/ops.py @@ -0,0 +1,650 @@ +# 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 module for helper tensorflow ops.""" +import math + +import tensorflow as tf + +from object_detection.core import box_list +from object_detection.core import box_list_ops +from object_detection.core import standard_fields as fields +from object_detection.utils import static_shape + + +def expanded_shape(orig_shape, start_dim, num_dims): + """Inserts multiple ones into a shape vector. + + Inserts an all-1 vector of length num_dims at position start_dim into a shape. + Can be combined with tf.reshape to generalize tf.expand_dims. + + Args: + orig_shape: the shape into which the all-1 vector is added (int32 vector) + start_dim: insertion position (int scalar) + num_dims: length of the inserted all-1 vector (int scalar) + Returns: + An int32 vector of length tf.size(orig_shape) + num_dims. + """ + with tf.name_scope('ExpandedShape'): + start_dim = tf.expand_dims(start_dim, 0) # scalar to rank-1 + before = tf.slice(orig_shape, [0], start_dim) + add_shape = tf.ones(tf.reshape(num_dims, [1]), dtype=tf.int32) + after = tf.slice(orig_shape, start_dim, [-1]) + new_shape = tf.concat([before, add_shape, after], 0) + return new_shape + + +def normalized_to_image_coordinates(normalized_boxes, image_shape, + parallel_iterations=32): + """Converts a batch of boxes from normal to image coordinates. + + Args: + normalized_boxes: a float32 tensor of shape [None, num_boxes, 4] in + normalized coordinates. + image_shape: a float32 tensor of shape [4] containing the image shape. + parallel_iterations: parallelism for the map_fn op. + + Returns: + absolute_boxes: a float32 tensor of shape [None, num_boxes, 4] containg the + boxes in image coordinates. + """ + def _to_absolute_coordinates(normalized_boxes): + return box_list_ops.to_absolute_coordinates( + box_list.BoxList(normalized_boxes), + image_shape[1], image_shape[2], check_range=False).get() + + absolute_boxes = tf.map_fn( + _to_absolute_coordinates, + elems=(normalized_boxes), + dtype=tf.float32, + parallel_iterations=parallel_iterations, + back_prop=True) + return absolute_boxes + + +def meshgrid(x, y): + """Tiles the contents of x and y into a pair of grids. + + Multidimensional analog of numpy.meshgrid, giving the same behavior if x and y + are vectors. Generally, this will give: + + xgrid(i1, ..., i_m, j_1, ..., j_n) = x(j_1, ..., j_n) + ygrid(i1, ..., i_m, j_1, ..., j_n) = y(i_1, ..., i_m) + + Keep in mind that the order of the arguments and outputs is reverse relative + to the order of the indices they go into, done for compatibility with numpy. + The output tensors have the same shapes. Specifically: + + xgrid.get_shape() = y.get_shape().concatenate(x.get_shape()) + ygrid.get_shape() = y.get_shape().concatenate(x.get_shape()) + + Args: + x: A tensor of arbitrary shape and rank. xgrid will contain these values + varying in its last dimensions. + y: A tensor of arbitrary shape and rank. ygrid will contain these values + varying in its first dimensions. + Returns: + A tuple of tensors (xgrid, ygrid). + """ + with tf.name_scope('Meshgrid'): + x = tf.convert_to_tensor(x) + y = tf.convert_to_tensor(y) + x_exp_shape = expanded_shape(tf.shape(x), 0, tf.rank(y)) + y_exp_shape = expanded_shape(tf.shape(y), tf.rank(y), tf.rank(x)) + + xgrid = tf.tile(tf.reshape(x, x_exp_shape), y_exp_shape) + ygrid = tf.tile(tf.reshape(y, y_exp_shape), x_exp_shape) + new_shape = y.get_shape().concatenate(x.get_shape()) + xgrid.set_shape(new_shape) + ygrid.set_shape(new_shape) + + return xgrid, ygrid + + +def pad_to_multiple(tensor, multiple): + """Returns the tensor zero padded to the specified multiple. + + Appends 0s to the end of the first and second dimension (height and width) of + the tensor until both dimensions are a multiple of the input argument + 'multiple'. E.g. given an input tensor of shape [1, 3, 5, 1] and an input + multiple of 4, PadToMultiple will append 0s so that the resulting tensor will + be of shape [1, 4, 8, 1]. + + Args: + tensor: rank 4 float32 tensor, where + tensor -> [batch_size, height, width, channels]. + multiple: the multiple to pad to. + + Returns: + padded_tensor: the tensor zero padded to the specified multiple. + """ + tensor_shape = tensor.get_shape() + batch_size = static_shape.get_batch_size(tensor_shape) + tensor_height = static_shape.get_height(tensor_shape) + tensor_width = static_shape.get_width(tensor_shape) + tensor_depth = static_shape.get_depth(tensor_shape) + + if batch_size is None: + batch_size = tf.shape(tensor)[0] + + if tensor_height is None: + tensor_height = tf.shape(tensor)[1] + padded_tensor_height = tf.to_int32( + tf.ceil(tf.to_float(tensor_height) / tf.to_float(multiple))) * multiple + else: + padded_tensor_height = int( + math.ceil(float(tensor_height) / multiple) * multiple) + + if tensor_width is None: + tensor_width = tf.shape(tensor)[2] + padded_tensor_width = tf.to_int32( + tf.ceil(tf.to_float(tensor_width) / tf.to_float(multiple))) * multiple + else: + padded_tensor_width = int( + math.ceil(float(tensor_width) / multiple) * multiple) + + if tensor_depth is None: + tensor_depth = tf.shape(tensor)[3] + + # Use tf.concat instead of tf.pad to preserve static shape + height_pad = tf.zeros([ + batch_size, padded_tensor_height - tensor_height, tensor_width, + tensor_depth + ]) + padded_tensor = tf.concat([tensor, height_pad], 1) + width_pad = tf.zeros([ + batch_size, padded_tensor_height, padded_tensor_width - tensor_width, + tensor_depth + ]) + padded_tensor = tf.concat([padded_tensor, width_pad], 2) + + return padded_tensor + + +def padded_one_hot_encoding(indices, depth, left_pad): + """Returns a zero padded one-hot tensor. + + This function converts a sparse representation of indices (e.g., [4]) to a + zero padded one-hot representation (e.g., [0, 0, 0, 0, 1] with depth = 4 and + left_pad = 1). If `indices` is empty, the result will simply be a tensor of + shape (0, depth + left_pad). If depth = 0, then this function just returns + `None`. + + Args: + indices: an integer tensor of shape [num_indices]. + depth: depth for the one-hot tensor (integer). + left_pad: number of zeros to left pad the one-hot tensor with (integer). + + Returns: + padded_onehot: a tensor with shape (num_indices, depth + left_pad). Returns + `None` if the depth is zero. + + Raises: + ValueError: if `indices` does not have rank 1 or if `left_pad` or `depth are + either negative or non-integers. + + TODO: add runtime checks for depth and indices. + """ + if depth < 0 or not isinstance(depth, (int, long)): + raise ValueError('`depth` must be a non-negative integer.') + if left_pad < 0 or not isinstance(left_pad, (int, long)): + raise ValueError('`left_pad` must be a non-negative integer.') + if depth == 0: + return None + if len(indices.get_shape().as_list()) != 1: + raise ValueError('`indices` must have rank 1') + + def one_hot_and_pad(): + one_hot = tf.cast(tf.one_hot(tf.cast(indices, tf.int64), depth, + on_value=1, off_value=0), tf.float32) + return tf.pad(one_hot, [[0, 0], [left_pad, 0]], mode='CONSTANT') + result = tf.cond(tf.greater(tf.size(indices), 0), one_hot_and_pad, + lambda: tf.zeros((depth + left_pad, 0))) + return tf.reshape(result, [-1, depth + left_pad]) + + +def dense_to_sparse_boxes(dense_locations, dense_num_boxes, num_classes): + """Converts bounding boxes from dense to sparse form. + + Args: + dense_locations: a [max_num_boxes, 4] tensor in which only the first k rows + are valid bounding box location coordinates, where k is the sum of + elements in dense_num_boxes. + dense_num_boxes: a [max_num_classes] tensor indicating the counts of + various bounding box classes e.g. [1, 0, 0, 2] means that the first + bounding box is of class 0 and the second and third bounding boxes are + of class 3. The sum of elements in this tensor is the number of valid + bounding boxes. + num_classes: number of classes + + Returns: + box_locations: a [num_boxes, 4] tensor containing only valid bounding + boxes (i.e. the first num_boxes rows of dense_locations) + box_classes: a [num_boxes] tensor containing the classes of each bounding + box (e.g. dense_num_boxes = [1, 0, 0, 2] => box_classes = [0, 3, 3] + """ + + num_valid_boxes = tf.reduce_sum(dense_num_boxes) + box_locations = tf.slice(dense_locations, + tf.constant([0, 0]), tf.stack([num_valid_boxes, 4])) + tiled_classes = [tf.tile([i], tf.expand_dims(dense_num_boxes[i], 0)) + for i in range(num_classes)] + box_classes = tf.concat(tiled_classes, 0) + box_locations.set_shape([None, 4]) + return box_locations, box_classes + + +def indices_to_dense_vector(indices, + size, + indices_value=1., + default_value=0, + dtype=tf.float32): + """Creates dense vector with indices set to specific value and rest to zeros. + + This function exists because it is unclear if it is safe to use + tf.sparse_to_dense(indices, [size], 1, validate_indices=False) + with indices which are not ordered. + This function accepts a dynamic size (e.g. tf.shape(tensor)[0]) + + Args: + indices: 1d Tensor with integer indices which are to be set to + indices_values. + size: scalar with size (integer) of output Tensor. + indices_value: values of elements specified by indices in the output vector + default_value: values of other elements in the output vector. + dtype: data type. + + Returns: + dense 1D Tensor of shape [size] with indices set to indices_values and the + rest set to default_value. + """ + size = tf.to_int32(size) + zeros = tf.ones([size], dtype=dtype) * default_value + values = tf.ones_like(indices, dtype=dtype) * indices_value + + return tf.dynamic_stitch([tf.range(size), tf.to_int32(indices)], + [zeros, values]) + + +def retain_groundtruth(tensor_dict, valid_indices): + """Retains groundtruth by valid indices. + + Args: + tensor_dict: a dictionary of following groundtruth tensors - + fields.InputDataFields.groundtruth_boxes + fields.InputDataFields.groundtruth_classes + fields.InputDataFields.groundtruth_is_crowd + fields.InputDataFields.groundtruth_area + fields.InputDataFields.groundtruth_label_types + fields.InputDataFields.groundtruth_difficult + valid_indices: a tensor with valid indices for the box-level groundtruth. + + Returns: + a dictionary of tensors containing only the groundtruth for valid_indices. + + Raises: + ValueError: If the shape of valid_indices is invalid. + ValueError: field fields.InputDataFields.groundtruth_boxes is + not present in tensor_dict. + """ + input_shape = valid_indices.get_shape().as_list() + if not (len(input_shape) == 1 or + (len(input_shape) == 2 and input_shape[1] == 1)): + raise ValueError('The shape of valid_indices is invalid.') + valid_indices = tf.reshape(valid_indices, [-1]) + valid_dict = {} + if fields.InputDataFields.groundtruth_boxes in tensor_dict: + # Prevents reshape failure when num_boxes is 0. + num_boxes = tf.maximum(tf.shape( + tensor_dict[fields.InputDataFields.groundtruth_boxes])[0], 1) + for key in tensor_dict: + if key in [fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes]: + valid_dict[key] = tf.gather(tensor_dict[key], valid_indices) + # Input decoder returns empty tensor when these fields are not provided. + # Needs to reshape into [num_boxes, -1] for tf.gather() to work. + elif key in [fields.InputDataFields.groundtruth_is_crowd, + fields.InputDataFields.groundtruth_area, + fields.InputDataFields.groundtruth_difficult, + fields.InputDataFields.groundtruth_label_types]: + valid_dict[key] = tf.reshape( + tf.gather(tf.reshape(tensor_dict[key], [num_boxes, -1]), + valid_indices), [-1]) + # Fields that are not associated with boxes. + else: + valid_dict[key] = tensor_dict[key] + else: + raise ValueError('%s not present in input tensor dict.' % ( + fields.InputDataFields.groundtruth_boxes)) + return valid_dict + + +def retain_groundtruth_with_positive_classes(tensor_dict): + """Retains only groundtruth with positive class ids. + + Args: + tensor_dict: a dictionary of following groundtruth tensors - + fields.InputDataFields.groundtruth_boxes + fields.InputDataFields.groundtruth_classes + fields.InputDataFields.groundtruth_is_crowd + fields.InputDataFields.groundtruth_area + fields.InputDataFields.groundtruth_label_types + fields.InputDataFields.groundtruth_difficult + + Returns: + a dictionary of tensors containing only the groundtruth with positive + classes. + + Raises: + ValueError: If groundtruth_classes tensor is not in tensor_dict. + """ + if fields.InputDataFields.groundtruth_classes not in tensor_dict: + raise ValueError('`groundtruth classes` not in tensor_dict.') + keep_indices = tf.where(tf.greater( + tensor_dict[fields.InputDataFields.groundtruth_classes], 0)) + return retain_groundtruth(tensor_dict, keep_indices) + + +def filter_groundtruth_with_nan_box_coordinates(tensor_dict): + """Filters out groundtruth with no bounding boxes. + + Args: + tensor_dict: a dictionary of following groundtruth tensors - + fields.InputDataFields.groundtruth_boxes + fields.InputDataFields.groundtruth_classes + fields.InputDataFields.groundtruth_is_crowd + fields.InputDataFields.groundtruth_area + fields.InputDataFields.groundtruth_label_types + + Returns: + a dictionary of tensors containing only the groundtruth that have bounding + boxes. + """ + groundtruth_boxes = tensor_dict[fields.InputDataFields.groundtruth_boxes] + nan_indicator_vector = tf.greater(tf.reduce_sum(tf.to_int32( + tf.is_nan(groundtruth_boxes)), reduction_indices=[1]), 0) + valid_indicator_vector = tf.logical_not(nan_indicator_vector) + valid_indices = tf.where(valid_indicator_vector) + + return retain_groundtruth(tensor_dict, valid_indices) + + +def normalize_to_target(inputs, + target_norm_value, + dim, + epsilon=1e-7, + trainable=True, + scope='NormalizeToTarget', + summarize=True): + """L2 normalizes the inputs across the specified dimension to a target norm. + + This op implements the L2 Normalization layer introduced in + Liu, Wei, et al. "SSD: Single Shot MultiBox Detector." + and Liu, Wei, Andrew Rabinovich, and Alexander C. Berg. + "Parsenet: Looking wider to see better." and is useful for bringing + activations from multiple layers in a convnet to a standard scale. + + Note that the rank of `inputs` must be known and the dimension to which + normalization is to be applied should be statically defined. + + TODO: Add option to scale by L2 norm of the entire input. + + Args: + inputs: A `Tensor` of arbitrary size. + target_norm_value: A float value that specifies an initial target norm or + a list of floats (whose length must be equal to the depth along the + dimension to be normalized) specifying a per-dimension multiplier + after normalization. + dim: The dimension along which the input is normalized. + epsilon: A small value to add to the inputs to avoid dividing by zero. + trainable: Whether the norm is trainable or not + scope: Optional scope for variable_scope. + summarize: Whether or not to add a tensorflow summary for the op. + + Returns: + The input tensor normalized to the specified target norm. + + Raises: + ValueError: If dim is smaller than the number of dimensions in 'inputs'. + ValueError: If target_norm_value is not a float or a list of floats with + length equal to the depth along the dimension to be normalized. + """ + with tf.variable_scope(scope, 'NormalizeToTarget', [inputs]): + if not inputs.get_shape(): + raise ValueError('The input rank must be known.') + input_shape = inputs.get_shape().as_list() + input_rank = len(input_shape) + if dim < 0 or dim >= input_rank: + raise ValueError( + 'dim must be non-negative but smaller than the input rank.') + if not input_shape[dim]: + raise ValueError('input shape should be statically defined along ' + 'the specified dimension.') + depth = input_shape[dim] + if not (isinstance(target_norm_value, float) or + (isinstance(target_norm_value, list) and + len(target_norm_value) == depth) and + all([isinstance(val, float) for val in target_norm_value])): + raise ValueError('target_norm_value must be a float or a list of floats ' + 'with length equal to the depth along the dimension to ' + 'be normalized.') + if isinstance(target_norm_value, float): + initial_norm = depth * [target_norm_value] + else: + initial_norm = target_norm_value + target_norm = tf.contrib.framework.model_variable( + name='weights', dtype=tf.float32, + initializer=tf.constant(initial_norm, dtype=tf.float32), + trainable=trainable) + if summarize: + mean = tf.reduce_mean(target_norm) + mean = tf.Print(mean, ['NormalizeToTarget:', mean]) + tf.summary.scalar(tf.get_variable_scope().name, mean) + lengths = epsilon + tf.sqrt(tf.reduce_sum(tf.square(inputs), dim, True)) + mult_shape = input_rank*[1] + mult_shape[dim] = depth + return tf.reshape(target_norm, mult_shape) * tf.truediv(inputs, lengths) + + +def position_sensitive_crop_regions(image, + boxes, + box_ind, + crop_size, + num_spatial_bins, + global_pool, + extrapolation_value=None): + """Position-sensitive crop and pool rectangular regions from a feature grid. + + The output crops are split into `spatial_bins_y` vertical bins + and `spatial_bins_x` horizontal bins. For each intersection of a vertical + and a horizontal bin the output values are gathered by performing + `tf.image.crop_and_resize` (bilinear resampling) on a a separate subset of + channels of the image. This reduces `depth` by a factor of + `(spatial_bins_y * spatial_bins_x)`. + + When global_pool is True, this function implements a differentiable version + of position-sensitive RoI pooling used in + [R-FCN detection system](https://arxiv.org/abs/1605.06409). + + When global_pool is False, this function implements a differentiable version + of position-sensitive assembling operation used in + [instance FCN](https://arxiv.org/abs/1603.08678). + + Args: + image: A `Tensor`. Must be one of the following types: `uint8`, `int8`, + `int16`, `int32`, `int64`, `half`, `float32`, `float64`. + A 4-D tensor of shape `[batch, image_height, image_width, depth]`. + Both `image_height` and `image_width` need to be positive. + boxes: A `Tensor` of type `float32`. + A 2-D tensor of shape `[num_boxes, 4]`. The `i`-th row of the tensor + specifies the coordinates of a box in the `box_ind[i]` image and is + specified in normalized coordinates `[y1, x1, y2, x2]`. A normalized + coordinate value of `y` is mapped to the image coordinate at + `y * (image_height - 1)`, so as the `[0, 1]` interval of normalized image + height is mapped to `[0, image_height - 1] in image height coordinates. + We do allow y1 > y2, in which case the sampled crop is an up-down flipped + version of the original image. The width dimension is treated similarly. + Normalized coordinates outside the `[0, 1]` range are allowed, in which + case we use `extrapolation_value` to extrapolate the input image values. + box_ind: A `Tensor` of type `int32`. + A 1-D tensor of shape `[num_boxes]` with int32 values in `[0, batch)`. + The value of `box_ind[i]` specifies the image that the `i`-th box refers + to. + crop_size: A list of two integers `[crop_height, crop_width]`. All + cropped image patches are resized to this size. The aspect ratio of the + image content is not preserved. Both `crop_height` and `crop_width` need + to be positive. + num_spatial_bins: A list of two integers `[spatial_bins_y, spatial_bins_x]`. + Represents the number of position-sensitive bins in y and x directions. + Both values should be >= 1. `crop_height` should be divisible by + `spatial_bins_y`, and similarly for width. + The number of image channels should be divisible by + (spatial_bins_y * spatial_bins_x). + Suggested value from R-FCN paper: [3, 3]. + global_pool: A boolean variable. + If True, we perform average global pooling on the features assembled from + the position-sensitive score maps. + If False, we keep the position-pooled features without global pooling + over the spatial coordinates. + Note that using global_pool=True is equivalent to but more efficient than + running the function with global_pool=False and then performing global + average pooling. + extrapolation_value: An optional `float`. Defaults to `0`. + Value used for extrapolation, when applicable. + Returns: + position_sensitive_features: A 4-D tensor of shape + `[num_boxes, K, K, crop_channels]`, + where `crop_channels = depth / (spatial_bins_y * spatial_bins_x)`, + where K = 1 when global_pool is True (Average-pooled cropped regions), + and K = crop_size when global_pool is False. + Raises: + ValueError: Raised in four situations: + `num_spatial_bins` is not >= 1; + `num_spatial_bins` does not divide `crop_size`; + `(spatial_bins_y*spatial_bins_x)` does not divide `depth`; + `bin_crop_size` is not square when global_pool=False due to the + constraint in function space_to_depth. + """ + total_bins = 1 + bin_crop_size = [] + + for (num_bins, crop_dim) in zip(num_spatial_bins, crop_size): + if num_bins < 1: + raise ValueError('num_spatial_bins should be >= 1') + + if crop_dim % num_bins != 0: + raise ValueError('crop_size should be divisible by num_spatial_bins') + + total_bins *= num_bins + bin_crop_size.append(crop_dim / num_bins) + + if not global_pool and bin_crop_size[0] != bin_crop_size[1]: + raise ValueError('Only support square bin crop size for now.') + + ymin, xmin, ymax, xmax = tf.unstack(boxes, axis=1) + spatial_bins_y, spatial_bins_x = num_spatial_bins + + # Split each box into spatial_bins_y * spatial_bins_x bins. + position_sensitive_boxes = [] + for bin_y in range(spatial_bins_y): + step_y = (ymax - ymin) / spatial_bins_y + for bin_x in range(spatial_bins_x): + step_x = (xmax - xmin) / spatial_bins_x + box_coordinates = [ymin + bin_y * step_y, + xmin + bin_x * step_x, + ymin + (bin_y + 1) * step_y, + xmin + (bin_x + 1) * step_x, + ] + position_sensitive_boxes.append(tf.stack(box_coordinates, axis=1)) + + image_splits = tf.split(value=image, num_or_size_splits=total_bins, axis=3) + + image_crops = [] + for (split, box) in zip(image_splits, position_sensitive_boxes): + crop = tf.image.crop_and_resize(split, box, box_ind, bin_crop_size, + extrapolation_value=extrapolation_value) + image_crops.append(crop) + + if global_pool: + # Average over all bins. + position_sensitive_features = tf.add_n(image_crops) / len(image_crops) + # Then average over spatial positions within the bins. + position_sensitive_features = tf.reduce_mean( + position_sensitive_features, [1, 2], keep_dims=True) + else: + # Reorder height/width to depth channel. + block_size = bin_crop_size[0] + if block_size >= 2: + image_crops = [tf.space_to_depth( + crop, block_size=block_size) for crop in image_crops] + + # Pack image_crops so that first dimension is for position-senstive boxes. + position_sensitive_features = tf.stack(image_crops, axis=0) + + # Unroll the position-sensitive boxes to spatial positions. + position_sensitive_features = tf.squeeze( + tf.batch_to_space_nd(position_sensitive_features, + block_shape=[1] + num_spatial_bins, + crops=tf.zeros((3, 2), dtype=tf.int32)), + squeeze_dims=[0]) + + # Reorder back the depth channel. + if block_size >= 2: + position_sensitive_features = tf.depth_to_space( + position_sensitive_features, block_size=block_size) + + return position_sensitive_features + + +def reframe_box_masks_to_image_masks(box_masks, boxes, image_height, + image_width): + """Transforms the box masks back to full image masks. + + Embeds masks in bounding boxes of larger masks whose shapes correspond to + image shape. + + Args: + box_masks: A tf.float32 tensor of size [num_masks, mask_height, mask_width]. + boxes: 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. + image_height: Image height. The output mask will have the same height as + the image height. + image_width: Image width. The output mask will have the same width as the + image width. + + Returns: + A tf.float32 tensor of size [num_masks, image_height, image_width]. + """ + # TODO: Make this a public function. + def transform_boxes_relative_to_boxes(boxes, reference_boxes): + boxes = tf.reshape(boxes, [-1, 2, 2]) + min_corner = tf.expand_dims(reference_boxes[:, 0:2], 1) + max_corner = tf.expand_dims(reference_boxes[:, 2:4], 1) + transformed_boxes = (boxes - min_corner) / (max_corner - min_corner) + return tf.reshape(transformed_boxes, [-1, 4]) + + box_masks = tf.expand_dims(box_masks, axis=3) + num_boxes = tf.shape(box_masks)[0] + unit_boxes = tf.concat( + [tf.zeros([num_boxes, 2]), tf.ones([num_boxes, 2])], axis=1) + reverse_boxes = transform_boxes_relative_to_boxes(unit_boxes, boxes) + image_masks = tf.image.crop_and_resize(image=box_masks, + boxes=reverse_boxes, + box_ind=tf.range(num_boxes), + crop_size=[image_height, image_width], + extrapolation_value=0.0) + return tf.squeeze(image_masks, axis=3) diff --git a/object_detection/utils/ops_test.py b/object_detection/utils/ops_test.py new file mode 100644 index 000000000..1765c82a2 --- /dev/null +++ b/object_detection/utils/ops_test.py @@ -0,0 +1,1033 @@ +# 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.utils.ops.""" +import numpy as np +import tensorflow as tf + +from object_detection.core import standard_fields as fields +from object_detection.utils import ops + + +class NormalizedToImageCoordinatesTest(tf.test.TestCase): + + def test_normalized_to_image_coordinates(self): + normalized_boxes = tf.placeholder(tf.float32, shape=(None, 1, 4)) + normalized_boxes_np = np.array([[[0.0, 0.0, 1.0, 1.0]], + [[0.5, 0.5, 1.0, 1.0]]]) + image_shape = tf.convert_to_tensor([1, 4, 4, 3], dtype=tf.int32) + absolute_boxes = ops.normalized_to_image_coordinates(normalized_boxes, + image_shape, + parallel_iterations=2) + + expected_boxes = np.array([[[0, 0, 4, 4]], + [[2, 2, 4, 4]]]) + with self.test_session() as sess: + absolute_boxes = sess.run(absolute_boxes, + feed_dict={normalized_boxes: + normalized_boxes_np}) + + self.assertAllEqual(absolute_boxes, expected_boxes) + + +class MeshgridTest(tf.test.TestCase): + + def test_meshgrid_numpy_comparison(self): + """Tests meshgrid op with vectors, for which it should match numpy.""" + x = np.arange(4) + y = np.arange(6) + exp_xgrid, exp_ygrid = np.meshgrid(x, y) + xgrid, ygrid = ops.meshgrid(x, y) + with self.test_session() as sess: + xgrid_output, ygrid_output = sess.run([xgrid, ygrid]) + self.assertAllEqual(xgrid_output, exp_xgrid) + self.assertAllEqual(ygrid_output, exp_ygrid) + + def test_meshgrid_multidimensional(self): + np.random.seed(18) + x = np.random.rand(4, 1, 2).astype(np.float32) + y = np.random.rand(2, 3).astype(np.float32) + + xgrid, ygrid = ops.meshgrid(x, y) + + grid_shape = list(y.shape) + list(x.shape) + self.assertEqual(xgrid.get_shape().as_list(), grid_shape) + self.assertEqual(ygrid.get_shape().as_list(), grid_shape) + with self.test_session() as sess: + xgrid_output, ygrid_output = sess.run([xgrid, ygrid]) + + # Check the shape of the output grids + self.assertEqual(xgrid_output.shape, tuple(grid_shape)) + self.assertEqual(ygrid_output.shape, tuple(grid_shape)) + + # Check a few elements + test_elements = [((3, 0, 0), (1, 2)), + ((2, 0, 1), (0, 0)), + ((0, 0, 0), (1, 1))] + for xind, yind in test_elements: + # These are float equality tests, but the meshgrid op should not introduce + # rounding. + self.assertEqual(xgrid_output[yind + xind], x[xind]) + self.assertEqual(ygrid_output[yind + xind], y[yind]) + + +class OpsTestPadToMultiple(tf.test.TestCase): + + def test_zero_padding(self): + tensor = tf.constant([[[[0.], [0.]], [[0.], [0.]]]]) + padded_tensor = ops.pad_to_multiple(tensor, 1) + with self.test_session() as sess: + padded_tensor_out = sess.run(padded_tensor) + self.assertEqual((1, 2, 2, 1), padded_tensor_out.shape) + + def test_no_padding(self): + tensor = tf.constant([[[[0.], [0.]], [[0.], [0.]]]]) + padded_tensor = ops.pad_to_multiple(tensor, 2) + with self.test_session() as sess: + padded_tensor_out = sess.run(padded_tensor) + self.assertEqual((1, 2, 2, 1), padded_tensor_out.shape) + + def test_padding(self): + tensor = tf.constant([[[[0.], [0.]], [[0.], [0.]]]]) + padded_tensor = ops.pad_to_multiple(tensor, 4) + with self.test_session() as sess: + padded_tensor_out = sess.run(padded_tensor) + self.assertEqual((1, 4, 4, 1), padded_tensor_out.shape) + + +class OpsTestPaddedOneHotEncoding(tf.test.TestCase): + + def test_correct_one_hot_tensor_with_no_pad(self): + indices = tf.constant([1, 2, 3, 5]) + one_hot_tensor = ops.padded_one_hot_encoding(indices, depth=6, left_pad=0) + expected_tensor = np.array([[0, 1, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0], + [0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 1]], np.float32) + with self.test_session() as sess: + out_one_hot_tensor = sess.run(one_hot_tensor) + self.assertAllClose(out_one_hot_tensor, expected_tensor, rtol=1e-10, + atol=1e-10) + + def test_correct_one_hot_tensor_with_pad_one(self): + indices = tf.constant([1, 2, 3, 5]) + one_hot_tensor = ops.padded_one_hot_encoding(indices, depth=6, left_pad=1) + expected_tensor = np.array([[0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 1]], np.float32) + with self.test_session() as sess: + out_one_hot_tensor = sess.run(one_hot_tensor) + self.assertAllClose(out_one_hot_tensor, expected_tensor, rtol=1e-10, + atol=1e-10) + + def test_correct_one_hot_tensor_with_pad_three(self): + indices = tf.constant([1, 2, 3, 5]) + one_hot_tensor = ops.padded_one_hot_encoding(indices, depth=6, left_pad=3) + expected_tensor = np.array([[0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 1]], np.float32) + with self.test_session() as sess: + out_one_hot_tensor = sess.run(one_hot_tensor) + self.assertAllClose(out_one_hot_tensor, expected_tensor, rtol=1e-10, + atol=1e-10) + + def test_correct_padded_one_hot_tensor_with_empty_indices(self): + depth = 6 + pad = 2 + indices = tf.constant([]) + one_hot_tensor = ops.padded_one_hot_encoding( + indices, depth=depth, left_pad=pad) + expected_tensor = np.zeros((0, depth + pad)) + with self.test_session() as sess: + out_one_hot_tensor = sess.run(one_hot_tensor) + self.assertAllClose(out_one_hot_tensor, expected_tensor, rtol=1e-10, + atol=1e-10) + + def test_return_none_on_zero_depth(self): + indices = tf.constant([1, 2, 3, 4, 5]) + one_hot_tensor = ops.padded_one_hot_encoding(indices, depth=0, left_pad=2) + self.assertEqual(one_hot_tensor, None) + + def test_raise_value_error_on_rank_two_input(self): + indices = tf.constant(1.0, shape=(2, 3)) + with self.assertRaises(ValueError): + ops.padded_one_hot_encoding(indices, depth=6, left_pad=2) + + def test_raise_value_error_on_negative_pad(self): + indices = tf.constant(1.0, shape=(2, 3)) + with self.assertRaises(ValueError): + ops.padded_one_hot_encoding(indices, depth=6, left_pad=-1) + + def test_raise_value_error_on_float_pad(self): + indices = tf.constant(1.0, shape=(2, 3)) + with self.assertRaises(ValueError): + ops.padded_one_hot_encoding(indices, depth=6, left_pad=0.1) + + def test_raise_value_error_on_float_depth(self): + indices = tf.constant(1.0, shape=(2, 3)) + with self.assertRaises(ValueError): + ops.padded_one_hot_encoding(indices, depth=0.1, left_pad=2) + + +class OpsDenseToSparseBoxesTest(tf.test.TestCase): + + def test_return_all_boxes_when_all_input_boxes_are_valid(self): + num_classes = 4 + num_valid_boxes = 3 + code_size = 4 + dense_location_placeholder = tf.placeholder(tf.float32, + shape=(num_valid_boxes, + code_size)) + dense_num_boxes_placeholder = tf.placeholder(tf.int32, shape=(num_classes)) + box_locations, box_classes = ops.dense_to_sparse_boxes( + dense_location_placeholder, dense_num_boxes_placeholder, num_classes) + feed_dict = {dense_location_placeholder: np.random.uniform( + size=[num_valid_boxes, code_size]), + dense_num_boxes_placeholder: np.array([1, 0, 0, 2], + dtype=np.int32)} + + expected_box_locations = feed_dict[dense_location_placeholder] + expected_box_classses = np.array([0, 3, 3]) + with self.test_session() as sess: + box_locations, box_classes = sess.run([box_locations, box_classes], + feed_dict=feed_dict) + + self.assertAllClose(box_locations, expected_box_locations, rtol=1e-6, + atol=1e-6) + self.assertAllEqual(box_classes, expected_box_classses) + + def test_return_only_valid_boxes_when_input_contains_invalid_boxes(self): + num_classes = 4 + num_valid_boxes = 3 + num_boxes = 10 + code_size = 4 + + dense_location_placeholder = tf.placeholder(tf.float32, shape=(num_boxes, + code_size)) + dense_num_boxes_placeholder = tf.placeholder(tf.int32, shape=(num_classes)) + box_locations, box_classes = ops.dense_to_sparse_boxes( + dense_location_placeholder, dense_num_boxes_placeholder, num_classes) + feed_dict = {dense_location_placeholder: np.random.uniform( + size=[num_boxes, code_size]), + dense_num_boxes_placeholder: np.array([1, 0, 0, 2], + dtype=np.int32)} + + expected_box_locations = (feed_dict[dense_location_placeholder] + [:num_valid_boxes]) + expected_box_classses = np.array([0, 3, 3]) + with self.test_session() as sess: + box_locations, box_classes = sess.run([box_locations, box_classes], + feed_dict=feed_dict) + + self.assertAllClose(box_locations, expected_box_locations, rtol=1e-6, + atol=1e-6) + self.assertAllEqual(box_classes, expected_box_classses) + + +class OpsTestIndicesToDenseVector(tf.test.TestCase): + + def test_indices_to_dense_vector(self): + size = 10000 + num_indices = np.random.randint(size) + rand_indices = np.random.permutation(np.arange(size))[0:num_indices] + + expected_output = np.zeros(size, dtype=np.float32) + expected_output[rand_indices] = 1. + + tf_rand_indices = tf.constant(rand_indices) + indicator = ops.indices_to_dense_vector(tf_rand_indices, size) + + with self.test_session() as sess: + output = sess.run(indicator) + self.assertAllEqual(output, expected_output) + self.assertEqual(output.dtype, expected_output.dtype) + + def test_indices_to_dense_vector_size_at_inference(self): + size = 5000 + num_indices = 250 + all_indices = np.arange(size) + rand_indices = np.random.permutation(all_indices)[0:num_indices] + + expected_output = np.zeros(size, dtype=np.float32) + expected_output[rand_indices] = 1. + + tf_all_indices = tf.placeholder(tf.int32) + tf_rand_indices = tf.constant(rand_indices) + indicator = ops.indices_to_dense_vector(tf_rand_indices, + tf.shape(tf_all_indices)[0]) + feed_dict = {tf_all_indices: all_indices} + + with self.test_session() as sess: + output = sess.run(indicator, feed_dict=feed_dict) + self.assertAllEqual(output, expected_output) + self.assertEqual(output.dtype, expected_output.dtype) + + def test_indices_to_dense_vector_int(self): + size = 500 + num_indices = 25 + rand_indices = np.random.permutation(np.arange(size))[0:num_indices] + + expected_output = np.zeros(size, dtype=np.int64) + expected_output[rand_indices] = 1 + + tf_rand_indices = tf.constant(rand_indices) + indicator = ops.indices_to_dense_vector( + tf_rand_indices, size, 1, dtype=tf.int64) + + with self.test_session() as sess: + output = sess.run(indicator) + self.assertAllEqual(output, expected_output) + self.assertEqual(output.dtype, expected_output.dtype) + + def test_indices_to_dense_vector_custom_values(self): + size = 100 + num_indices = 10 + rand_indices = np.random.permutation(np.arange(size))[0:num_indices] + indices_value = np.random.rand(1) + default_value = np.random.rand(1) + + expected_output = np.float32(np.ones(size) * default_value) + expected_output[rand_indices] = indices_value + + tf_rand_indices = tf.constant(rand_indices) + indicator = ops.indices_to_dense_vector( + tf_rand_indices, + size, + indices_value=indices_value, + default_value=default_value) + + with self.test_session() as sess: + output = sess.run(indicator) + self.assertAllClose(output, expected_output) + self.assertEqual(output.dtype, expected_output.dtype) + + def test_indices_to_dense_vector_all_indices_as_input(self): + size = 500 + num_indices = 500 + rand_indices = np.random.permutation(np.arange(size))[0:num_indices] + + expected_output = np.ones(size, dtype=np.float32) + + tf_rand_indices = tf.constant(rand_indices) + indicator = ops.indices_to_dense_vector(tf_rand_indices, size) + + with self.test_session() as sess: + output = sess.run(indicator) + self.assertAllEqual(output, expected_output) + self.assertEqual(output.dtype, expected_output.dtype) + + def test_indices_to_dense_vector_empty_indices_as_input(self): + size = 500 + rand_indices = [] + + expected_output = np.zeros(size, dtype=np.float32) + + tf_rand_indices = tf.constant(rand_indices) + indicator = ops.indices_to_dense_vector(tf_rand_indices, size) + + with self.test_session() as sess: + output = sess.run(indicator) + self.assertAllEqual(output, expected_output) + self.assertEqual(output.dtype, expected_output.dtype) + + +class GroundtruthFilterTest(tf.test.TestCase): + + def test_filter_groundtruth(self): + input_image = tf.placeholder(tf.float32, shape=(None, None, 3)) + input_boxes = tf.placeholder(tf.float32, shape=(None, 4)) + input_classes = tf.placeholder(tf.int32, shape=(None,)) + input_is_crowd = tf.placeholder(tf.bool, shape=(None,)) + input_area = tf.placeholder(tf.float32, shape=(None,)) + input_difficult = tf.placeholder(tf.float32, shape=(None,)) + input_label_types = tf.placeholder(tf.string, shape=(None,)) + valid_indices = tf.placeholder(tf.int32, shape=(None,)) + input_tensors = { + fields.InputDataFields.image: input_image, + fields.InputDataFields.groundtruth_boxes: input_boxes, + fields.InputDataFields.groundtruth_classes: input_classes, + fields.InputDataFields.groundtruth_is_crowd: input_is_crowd, + fields.InputDataFields.groundtruth_area: input_area, + fields.InputDataFields.groundtruth_difficult: input_difficult, + fields.InputDataFields.groundtruth_label_types: input_label_types + } + output_tensors = ops.retain_groundtruth(input_tensors, valid_indices) + + image_tensor = np.random.rand(224, 224, 3) + feed_dict = { + input_image: image_tensor, + input_boxes: + np.array([[0.2, 0.4, 0.1, 0.8], [0.2, 0.4, 1.0, 0.8]], dtype=np.float), + input_classes: + np.array([1, 2], dtype=np.int32), + input_is_crowd: + np.array([False, True], dtype=np.bool), + input_area: + np.array([32, 48], dtype=np.float32), + input_difficult: + np.array([True, False], dtype=np.bool), + input_label_types: + np.array(['APPROPRIATE', 'INCORRECT'], dtype=np.string_), + valid_indices: + np.array([0], dtype=np.int32) + } + expected_tensors = { + fields.InputDataFields.image: + image_tensor, + fields.InputDataFields.groundtruth_boxes: + [[0.2, 0.4, 0.1, 0.8]], + fields.InputDataFields.groundtruth_classes: + [1], + fields.InputDataFields.groundtruth_is_crowd: + [False], + fields.InputDataFields.groundtruth_area: + [32], + fields.InputDataFields.groundtruth_difficult: + [True], + fields.InputDataFields.groundtruth_label_types: + ['APPROPRIATE'] + } + with self.test_session() as sess: + output_tensors = sess.run(output_tensors, feed_dict=feed_dict) + for key in [fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_area]: + self.assertAllClose(expected_tensors[key], output_tensors[key]) + for key in [fields.InputDataFields.groundtruth_classes, + fields.InputDataFields.groundtruth_is_crowd, + fields.InputDataFields.groundtruth_label_types]: + self.assertAllEqual(expected_tensors[key], output_tensors[key]) + + def test_filter_with_missing_fields(self): + input_boxes = tf.placeholder(tf.float32, shape=(None, 4)) + input_classes = tf.placeholder(tf.int32, shape=(None,)) + input_tensors = { + fields.InputDataFields.groundtruth_boxes: input_boxes, + fields.InputDataFields.groundtruth_classes: input_classes + } + valid_indices = tf.placeholder(tf.int32, shape=(None,)) + + feed_dict = { + input_boxes: + np.array([[0.2, 0.4, 0.1, 0.8], [0.2, 0.4, 1.0, 0.8]], dtype=np.float), + input_classes: + np.array([1, 2], dtype=np.int32), + valid_indices: + np.array([0], dtype=np.int32) + } + expected_tensors = { + fields.InputDataFields.groundtruth_boxes: + [[0.2, 0.4, 0.1, 0.8]], + fields.InputDataFields.groundtruth_classes: + [1] + } + + output_tensors = ops.retain_groundtruth(input_tensors, valid_indices) + with self.test_session() as sess: + output_tensors = sess.run(output_tensors, feed_dict=feed_dict) + for key in [fields.InputDataFields.groundtruth_boxes]: + self.assertAllClose(expected_tensors[key], output_tensors[key]) + for key in [fields.InputDataFields.groundtruth_classes]: + self.assertAllEqual(expected_tensors[key], output_tensors[key]) + + def test_filter_with_empty_fields(self): + input_boxes = tf.placeholder(tf.float32, shape=(None, 4)) + input_classes = tf.placeholder(tf.int32, shape=(None,)) + input_is_crowd = tf.placeholder(tf.bool, shape=(None,)) + input_area = tf.placeholder(tf.float32, shape=(None,)) + input_difficult = tf.placeholder(tf.float32, shape=(None,)) + valid_indices = tf.placeholder(tf.int32, shape=(None,)) + input_tensors = { + fields.InputDataFields.groundtruth_boxes: input_boxes, + fields.InputDataFields.groundtruth_classes: input_classes, + fields.InputDataFields.groundtruth_is_crowd: input_is_crowd, + fields.InputDataFields.groundtruth_area: input_area, + fields.InputDataFields.groundtruth_difficult: input_difficult + } + output_tensors = ops.retain_groundtruth(input_tensors, valid_indices) + + feed_dict = { + input_boxes: + np.array([[0.2, 0.4, 0.1, 0.8], [0.2, 0.4, 1.0, 0.8]], dtype=np.float), + input_classes: + np.array([1, 2], dtype=np.int32), + input_is_crowd: + np.array([False, True], dtype=np.bool), + input_area: + np.array([], dtype=np.float32), + input_difficult: + np.array([], dtype=np.float32), + valid_indices: + np.array([0], dtype=np.int32) + } + expected_tensors = { + fields.InputDataFields.groundtruth_boxes: + [[0.2, 0.4, 0.1, 0.8]], + fields.InputDataFields.groundtruth_classes: + [1], + fields.InputDataFields.groundtruth_is_crowd: + [False], + fields.InputDataFields.groundtruth_area: + [], + fields.InputDataFields.groundtruth_difficult: + [] + } + with self.test_session() as sess: + output_tensors = sess.run(output_tensors, feed_dict=feed_dict) + for key in [fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_area]: + self.assertAllClose(expected_tensors[key], output_tensors[key]) + for key in [fields.InputDataFields.groundtruth_classes, + fields.InputDataFields.groundtruth_is_crowd]: + self.assertAllEqual(expected_tensors[key], output_tensors[key]) + + def test_filter_with_empty_groundtruth_boxes(self): + input_boxes = tf.placeholder(tf.float32, shape=(None, 4)) + input_classes = tf.placeholder(tf.int32, shape=(None,)) + input_is_crowd = tf.placeholder(tf.bool, shape=(None,)) + input_area = tf.placeholder(tf.float32, shape=(None,)) + input_difficult = tf.placeholder(tf.float32, shape=(None,)) + valid_indices = tf.placeholder(tf.int32, shape=(None,)) + input_tensors = { + fields.InputDataFields.groundtruth_boxes: input_boxes, + fields.InputDataFields.groundtruth_classes: input_classes, + fields.InputDataFields.groundtruth_is_crowd: input_is_crowd, + fields.InputDataFields.groundtruth_area: input_area, + fields.InputDataFields.groundtruth_difficult: input_difficult + } + output_tensors = ops.retain_groundtruth(input_tensors, valid_indices) + + feed_dict = { + input_boxes: + np.array([], dtype=np.float).reshape(0, 4), + input_classes: + np.array([], dtype=np.int32), + input_is_crowd: + np.array([], dtype=np.bool), + input_area: + np.array([], dtype=np.float32), + input_difficult: + np.array([], dtype=np.float32), + valid_indices: + np.array([], dtype=np.int32) + } + with self.test_session() as sess: + output_tensors = sess.run(output_tensors, feed_dict=feed_dict) + for key in input_tensors: + if key == fields.InputDataFields.groundtruth_boxes: + self.assertAllEqual([0, 4], output_tensors[key].shape) + else: + self.assertAllEqual([0], output_tensors[key].shape) + + +class RetainGroundTruthWithPositiveClasses(tf.test.TestCase): + + def test_filter_groundtruth_with_positive_classes(self): + input_image = tf.placeholder(tf.float32, shape=(None, None, 3)) + input_boxes = tf.placeholder(tf.float32, shape=(None, 4)) + input_classes = tf.placeholder(tf.int32, shape=(None,)) + input_is_crowd = tf.placeholder(tf.bool, shape=(None,)) + input_area = tf.placeholder(tf.float32, shape=(None,)) + input_difficult = tf.placeholder(tf.float32, shape=(None,)) + input_label_types = tf.placeholder(tf.string, shape=(None,)) + valid_indices = tf.placeholder(tf.int32, shape=(None,)) + input_tensors = { + fields.InputDataFields.image: input_image, + fields.InputDataFields.groundtruth_boxes: input_boxes, + fields.InputDataFields.groundtruth_classes: input_classes, + fields.InputDataFields.groundtruth_is_crowd: input_is_crowd, + fields.InputDataFields.groundtruth_area: input_area, + fields.InputDataFields.groundtruth_difficult: input_difficult, + fields.InputDataFields.groundtruth_label_types: input_label_types + } + output_tensors = ops.retain_groundtruth_with_positive_classes(input_tensors) + + image_tensor = np.random.rand(224, 224, 3) + feed_dict = { + input_image: image_tensor, + input_boxes: + np.array([[0.2, 0.4, 0.1, 0.8], [0.2, 0.4, 1.0, 0.8]], dtype=np.float), + input_classes: + np.array([1, 0], dtype=np.int32), + input_is_crowd: + np.array([False, True], dtype=np.bool), + input_area: + np.array([32, 48], dtype=np.float32), + input_difficult: + np.array([True, False], dtype=np.bool), + input_label_types: + np.array(['APPROPRIATE', 'INCORRECT'], dtype=np.string_), + valid_indices: + np.array([0], dtype=np.int32) + } + expected_tensors = { + fields.InputDataFields.image: + image_tensor, + fields.InputDataFields.groundtruth_boxes: + [[0.2, 0.4, 0.1, 0.8]], + fields.InputDataFields.groundtruth_classes: + [1], + fields.InputDataFields.groundtruth_is_crowd: + [False], + fields.InputDataFields.groundtruth_area: + [32], + fields.InputDataFields.groundtruth_difficult: + [True], + fields.InputDataFields.groundtruth_label_types: + ['APPROPRIATE'] + } + with self.test_session() as sess: + output_tensors = sess.run(output_tensors, feed_dict=feed_dict) + for key in [fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_area]: + self.assertAllClose(expected_tensors[key], output_tensors[key]) + for key in [fields.InputDataFields.groundtruth_classes, + fields.InputDataFields.groundtruth_is_crowd, + fields.InputDataFields.groundtruth_label_types]: + self.assertAllEqual(expected_tensors[key], output_tensors[key]) + + +class GroundtruthFilterWithNanBoxTest(tf.test.TestCase): + + def test_filter_groundtruth_with_nan_box_coordinates(self): + input_tensors = { + fields.InputDataFields.groundtruth_boxes: + [[np.nan, np.nan, np.nan, np.nan], [0.2, 0.4, 0.1, 0.8]], + fields.InputDataFields.groundtruth_classes: + [1, 2], + fields.InputDataFields.groundtruth_is_crowd: + [False, True], + fields.InputDataFields.groundtruth_area: + [100.0, 238.7] + } + + expected_tensors = { + fields.InputDataFields.groundtruth_boxes: + [[0.2, 0.4, 0.1, 0.8]], + fields.InputDataFields.groundtruth_classes: + [2], + fields.InputDataFields.groundtruth_is_crowd: + [True], + fields.InputDataFields.groundtruth_area: + [238.7] + } + + output_tensors = ops.filter_groundtruth_with_nan_box_coordinates( + input_tensors) + with self.test_session() as sess: + output_tensors = sess.run(output_tensors) + for key in [fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_area]: + self.assertAllClose(expected_tensors[key], output_tensors[key]) + for key in [fields.InputDataFields.groundtruth_classes, + fields.InputDataFields.groundtruth_is_crowd]: + self.assertAllEqual(expected_tensors[key], output_tensors[key]) + + +class OpsTestNormalizeToTarget(tf.test.TestCase): + + def test_create_normalize_to_target(self): + inputs = tf.random_uniform([5, 10, 12, 3]) + target_norm_value = 4.0 + dim = 3 + with self.test_session(): + output = ops.normalize_to_target(inputs, target_norm_value, dim) + self.assertEqual(output.op.name, 'NormalizeToTarget/mul') + var_name = tf.contrib.framework.get_variables()[0].name + self.assertEqual(var_name, 'NormalizeToTarget/weights:0') + + def test_invalid_dim(self): + inputs = tf.random_uniform([5, 10, 12, 3]) + target_norm_value = 4.0 + dim = 10 + with self.assertRaisesRegexp( + ValueError, + 'dim must be non-negative but smaller than the input rank.'): + ops.normalize_to_target(inputs, target_norm_value, dim) + + def test_invalid_target_norm_values(self): + inputs = tf.random_uniform([5, 10, 12, 3]) + target_norm_value = [4.0, 4.0] + dim = 3 + with self.assertRaisesRegexp( + ValueError, 'target_norm_value must be a float or a list of floats'): + ops.normalize_to_target(inputs, target_norm_value, dim) + + def test_correct_output_shape(self): + inputs = tf.random_uniform([5, 10, 12, 3]) + target_norm_value = 4.0 + dim = 3 + with self.test_session(): + output = ops.normalize_to_target(inputs, target_norm_value, dim) + self.assertEqual(output.get_shape().as_list(), + inputs.get_shape().as_list()) + + def test_correct_initial_output_values(self): + inputs = tf.constant([[[[3, 4], [7, 24]], + [[5, -12], [-1, 0]]]], tf.float32) + target_norm_value = 10.0 + dim = 3 + expected_output = [[[[30/5.0, 40/5.0], [70/25.0, 240/25.0]], + [[50/13.0, -120/13.0], [-10, 0]]]] + with self.test_session() as sess: + normalized_inputs = ops.normalize_to_target(inputs, target_norm_value, + dim) + sess.run(tf.global_variables_initializer()) + output = normalized_inputs.eval() + self.assertAllClose(output, expected_output) + + def test_multiple_target_norm_values(self): + inputs = tf.constant([[[[3, 4], [7, 24]], + [[5, -12], [-1, 0]]]], tf.float32) + target_norm_value = [10.0, 20.0] + dim = 3 + expected_output = [[[[30/5.0, 80/5.0], [70/25.0, 480/25.0]], + [[50/13.0, -240/13.0], [-10, 0]]]] + with self.test_session() as sess: + normalized_inputs = ops.normalize_to_target(inputs, target_norm_value, + dim) + sess.run(tf.global_variables_initializer()) + output = normalized_inputs.eval() + self.assertAllClose(output, expected_output) + + +class OpsTestPositionSensitiveCropRegions(tf.test.TestCase): + + def test_position_sensitive(self): + num_spatial_bins = [3, 2] + image_shape = [1, 3, 2, 6] + + # First channel is 1's, second channel is 2's, etc. + image = tf.constant(range(1, 3 * 2 + 1) * 6, dtype=tf.float32, + shape=image_shape) + boxes = tf.random_uniform((2, 4)) + box_ind = tf.constant([0, 0], dtype=tf.int32) + + # The result for both boxes should be [[1, 2], [3, 4], [5, 6]] + # before averaging. + expected_output = np.array([3.5, 3.5]).reshape([2, 1, 1, 1]) + + for crop_size_mult in range(1, 3): + crop_size = [3 * crop_size_mult, 2 * crop_size_mult] + ps_crop_and_pool = ops.position_sensitive_crop_regions( + image, boxes, box_ind, crop_size, num_spatial_bins, global_pool=True) + + with self.test_session() as sess: + output = sess.run(ps_crop_and_pool) + self.assertAllClose(output, expected_output) + + def test_position_sensitive_with_equal_channels(self): + num_spatial_bins = [2, 2] + image_shape = [1, 3, 3, 4] + crop_size = [2, 2] + + image = tf.constant(range(1, 3 * 3 + 1), dtype=tf.float32, + shape=[1, 3, 3, 1]) + tiled_image = tf.tile(image, [1, 1, 1, image_shape[3]]) + boxes = tf.random_uniform((3, 4)) + box_ind = tf.constant([0, 0, 0], dtype=tf.int32) + + # All channels are equal so position-sensitive crop and resize should + # work as the usual crop and resize for just one channel. + crop = tf.image.crop_and_resize(image, boxes, box_ind, crop_size) + crop_and_pool = tf.reduce_mean(crop, [1, 2], keep_dims=True) + + ps_crop_and_pool = ops.position_sensitive_crop_regions( + tiled_image, + boxes, + box_ind, + crop_size, + num_spatial_bins, + global_pool=True) + + with self.test_session() as sess: + expected_output, output = sess.run((crop_and_pool, ps_crop_and_pool)) + self.assertAllClose(output, expected_output) + + def test_position_sensitive_with_single_bin(self): + num_spatial_bins = [1, 1] + image_shape = [2, 3, 3, 4] + crop_size = [2, 2] + + image = tf.random_uniform(image_shape) + boxes = tf.random_uniform((6, 4)) + box_ind = tf.constant([0, 0, 0, 1, 1, 1], dtype=tf.int32) + + # When a single bin is used, position-sensitive crop and pool should be + # the same as non-position sensitive crop and pool. + crop = tf.image.crop_and_resize(image, boxes, box_ind, crop_size) + crop_and_pool = tf.reduce_mean(crop, [1, 2], keep_dims=True) + + ps_crop_and_pool = ops.position_sensitive_crop_regions( + image, boxes, box_ind, crop_size, num_spatial_bins, global_pool=True) + + with self.test_session() as sess: + expected_output, output = sess.run((crop_and_pool, ps_crop_and_pool)) + self.assertAllClose(output, expected_output) + + def test_raise_value_error_on_num_bins_less_than_one(self): + num_spatial_bins = [1, -1] + image_shape = [1, 1, 1, 2] + crop_size = [2, 2] + + image = tf.constant(1, dtype=tf.float32, shape=image_shape) + boxes = tf.constant([[0, 0, 1, 1]], dtype=tf.float32) + box_ind = tf.constant([0], dtype=tf.int32) + + with self.assertRaisesRegexp(ValueError, 'num_spatial_bins should be >= 1'): + ops.position_sensitive_crop_regions( + image, boxes, box_ind, crop_size, num_spatial_bins, global_pool=True) + + def test_raise_value_error_on_non_divisible_crop_size(self): + num_spatial_bins = [2, 3] + image_shape = [1, 1, 1, 6] + crop_size = [3, 2] + + image = tf.constant(1, dtype=tf.float32, shape=image_shape) + boxes = tf.constant([[0, 0, 1, 1]], dtype=tf.float32) + box_ind = tf.constant([0], dtype=tf.int32) + + with self.assertRaisesRegexp( + ValueError, 'crop_size should be divisible by num_spatial_bins'): + ops.position_sensitive_crop_regions( + image, boxes, box_ind, crop_size, num_spatial_bins, global_pool=True) + + def test_raise_value_error_on_non_divisible_num_channels(self): + num_spatial_bins = [2, 2] + image_shape = [1, 1, 1, 5] + crop_size = [2, 2] + + image = tf.constant(1, dtype=tf.float32, shape=image_shape) + boxes = tf.constant([[0, 0, 1, 1]], dtype=tf.float32) + box_ind = tf.constant([0], dtype=tf.int32) + + with self.assertRaisesRegexp( + ValueError, 'Dimension size must be evenly divisible by 4 but is 5'): + ops.position_sensitive_crop_regions( + image, boxes, box_ind, crop_size, num_spatial_bins, global_pool=True) + + def test_position_sensitive_with_global_pool_false(self): + num_spatial_bins = [3, 2] + image_shape = [1, 3, 2, 6] + num_boxes = 2 + + # First channel is 1's, second channel is 2's, etc. + image = tf.constant(range(1, 3 * 2 + 1) * 6, dtype=tf.float32, + shape=image_shape) + boxes = tf.random_uniform((num_boxes, 4)) + box_ind = tf.constant([0, 0], dtype=tf.int32) + + expected_output = [] + + # Expected output, when crop_size = [3, 2]. + expected_output.append(np.expand_dims( + np.tile(np.array([[1, 2], + [3, 4], + [5, 6]]), (num_boxes, 1, 1)), + axis=-1)) + + # Expected output, when crop_size = [6, 4]. + expected_output.append(np.expand_dims( + np.tile(np.array([[1, 1, 2, 2], + [1, 1, 2, 2], + [3, 3, 4, 4], + [3, 3, 4, 4], + [5, 5, 6, 6], + [5, 5, 6, 6]]), (num_boxes, 1, 1)), + axis=-1)) + + for crop_size_mult in range(1, 3): + crop_size = [3 * crop_size_mult, 2 * crop_size_mult] + ps_crop = ops.position_sensitive_crop_regions( + image, boxes, box_ind, crop_size, num_spatial_bins, global_pool=False) + with self.test_session() as sess: + output = sess.run(ps_crop) + + self.assertAllEqual(output, expected_output[crop_size_mult - 1]) + + def test_position_sensitive_with_global_pool_false_and_known_boxes(self): + num_spatial_bins = [2, 2] + image_shape = [2, 2, 2, 4] + crop_size = [2, 2] + + image = tf.constant(range(1, 2 * 2 * 4 + 1) * 2, dtype=tf.float32, + shape=image_shape) + + # First box contains whole image, and second box contains only first row. + boxes = tf.constant(np.array([[0., 0., 1., 1.], + [0., 0., 0.5, 1.]]), dtype=tf.float32) + box_ind = tf.constant([0, 1], dtype=tf.int32) + + expected_output = [] + + # Expected output, when the box containing whole image. + expected_output.append( + np.reshape(np.array([[4, 7], + [10, 13]]), + (1, 2, 2, 1)) + ) + + # Expected output, when the box containing only first row. + expected_output.append( + np.reshape(np.array([[3, 6], + [7, 10]]), + (1, 2, 2, 1)) + ) + expected_output = np.concatenate(expected_output, axis=0) + + ps_crop = ops.position_sensitive_crop_regions( + image, boxes, box_ind, crop_size, num_spatial_bins, global_pool=False) + + with self.test_session() as sess: + output = sess.run(ps_crop) + self.assertAllEqual(output, expected_output) + + def test_position_sensitive_with_global_pool_false_and_single_bin(self): + num_spatial_bins = [1, 1] + image_shape = [2, 3, 3, 4] + crop_size = [1, 1] + + image = tf.random_uniform(image_shape) + boxes = tf.random_uniform((6, 4)) + box_ind = tf.constant([0, 0, 0, 1, 1, 1], dtype=tf.int32) + + # Since single_bin is used and crop_size = [1, 1] (i.e., no crop resize), + # the outputs are the same whatever the global_pool value is. + ps_crop_and_pool = ops.position_sensitive_crop_regions( + image, boxes, box_ind, crop_size, num_spatial_bins, global_pool=True) + ps_crop = ops.position_sensitive_crop_regions( + image, boxes, box_ind, crop_size, num_spatial_bins, global_pool=False) + + with self.test_session() as sess: + pooled_output, unpooled_output = sess.run((ps_crop_and_pool, ps_crop)) + self.assertAllClose(pooled_output, unpooled_output) + + def test_position_sensitive_with_global_pool_false_and_do_global_pool(self): + num_spatial_bins = [3, 2] + image_shape = [1, 3, 2, 6] + num_boxes = 2 + + # First channel is 1's, second channel is 2's, etc. + image = tf.constant(range(1, 3 * 2 + 1) * 6, dtype=tf.float32, + shape=image_shape) + boxes = tf.random_uniform((num_boxes, 4)) + box_ind = tf.constant([0, 0], dtype=tf.int32) + + expected_output = [] + + # Expected output, when crop_size = [3, 2]. + expected_output.append(np.mean( + np.expand_dims( + np.tile(np.array([[1, 2], + [3, 4], + [5, 6]]), (num_boxes, 1, 1)), + axis=-1), + axis=(1, 2), keepdims=True)) + + # Expected output, when crop_size = [6, 4]. + expected_output.append(np.mean( + np.expand_dims( + np.tile(np.array([[1, 1, 2, 2], + [1, 1, 2, 2], + [3, 3, 4, 4], + [3, 3, 4, 4], + [5, 5, 6, 6], + [5, 5, 6, 6]]), (num_boxes, 1, 1)), + axis=-1), + axis=(1, 2), keepdims=True)) + + for crop_size_mult in range(1, 3): + crop_size = [3 * crop_size_mult, 2 * crop_size_mult] + + # Perform global_pooling after running the function with + # global_pool=False. + ps_crop = ops.position_sensitive_crop_regions( + image, boxes, box_ind, crop_size, num_spatial_bins, global_pool=False) + ps_crop_and_pool = tf.reduce_mean( + ps_crop, reduction_indices=(1, 2), keep_dims=True) + + with self.test_session() as sess: + output = sess.run(ps_crop_and_pool) + + self.assertAllEqual(output, expected_output[crop_size_mult - 1]) + + def test_raise_value_error_on_non_square_block_size(self): + num_spatial_bins = [3, 2] + image_shape = [1, 3, 2, 6] + crop_size = [6, 2] + + image = tf.constant(1, dtype=tf.float32, shape=image_shape) + boxes = tf.constant([[0, 0, 1, 1]], dtype=tf.float32) + box_ind = tf.constant([0], dtype=tf.int32) + + with self.assertRaisesRegexp( + ValueError, 'Only support square bin crop size for now.'): + ops.position_sensitive_crop_regions( + image, boxes, box_ind, crop_size, num_spatial_bins, global_pool=False) + + +class ReframeBoxMasksToImageMasksTest(tf.test.TestCase): + + def testZeroImageOnEmptyMask(self): + box_masks = tf.constant([[[0, 0], + [0, 0]]], dtype=tf.float32) + boxes = tf.constant([[0.0, 0.0, 1.0, 1.0]], dtype=tf.float32) + image_masks = ops.reframe_box_masks_to_image_masks(box_masks, boxes, + image_height=4, + image_width=4) + np_expected_image_masks = np.array([[[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]]], dtype=np.float32) + with self.test_session() as sess: + np_image_masks = sess.run(image_masks) + self.assertAllClose(np_image_masks, np_expected_image_masks) + + def testMaskIsCenteredInImageWhenBoxIsCentered(self): + box_masks = tf.constant([[[1, 1], + [1, 1]]], dtype=tf.float32) + boxes = tf.constant([[0.25, 0.25, 0.75, 0.75]], dtype=tf.float32) + image_masks = ops.reframe_box_masks_to_image_masks(box_masks, boxes, + image_height=4, + image_width=4) + np_expected_image_masks = np.array([[[0, 0, 0, 0], + [0, 1, 1, 0], + [0, 1, 1, 0], + [0, 0, 0, 0]]], dtype=np.float32) + with self.test_session() as sess: + np_image_masks = sess.run(image_masks) + self.assertAllClose(np_image_masks, np_expected_image_masks) + + def testMaskOffCenterRemainsOffCenterInImage(self): + box_masks = tf.constant([[[1, 0], + [0, 1]]], dtype=tf.float32) + boxes = tf.constant([[0.25, 0.5, 0.75, 1.0]], dtype=tf.float32) + image_masks = ops.reframe_box_masks_to_image_masks(box_masks, boxes, + image_height=4, + image_width=4) + np_expected_image_masks = np.array([[[0, 0, 0, 0], + [0, 0, 0.6111111, 0.16666669], + [0, 0, 0.3888889, 0.83333337], + [0, 0, 0, 0]]], dtype=np.float32) + with self.test_session() as sess: + np_image_masks = sess.run(image_masks) + self.assertAllClose(np_image_masks, np_expected_image_masks) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/utils/per_image_evaluation.py b/object_detection/utils/per_image_evaluation.py new file mode 100644 index 000000000..ed39afa6f --- /dev/null +++ b/object_detection/utils/per_image_evaluation.py @@ -0,0 +1,260 @@ +# 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. +# ============================================================================== + +"""Evaluate Object Detection result on a single image. + +Annotate each detected result as true positives or false positive according to +a predefined IOU ratio. Non Maximum Supression is used by default. Multi class +detection is supported by default. +""" +import numpy as np + +from object_detection.utils import np_box_list +from object_detection.utils import np_box_list_ops + + +class PerImageEvaluation(object): + """Evaluate detection result of a single image.""" + + def __init__(self, + num_groundtruth_classes, + matching_iou_threshold=0.5, + nms_iou_threshold=0.3, + nms_max_output_boxes=50): + """Initialized PerImageEvaluation by evaluation parameters. + + Args: + num_groundtruth_classes: Number of ground truth object classes + matching_iou_threshold: A ratio of area intersection to union, which is + the threshold to consider whether a detection is true positive or not + nms_iou_threshold: IOU threshold used in Non Maximum Suppression. + nms_max_output_boxes: Number of maximum output boxes in NMS. + """ + self.matching_iou_threshold = matching_iou_threshold + self.nms_iou_threshold = nms_iou_threshold + self.nms_max_output_boxes = nms_max_output_boxes + self.num_groundtruth_classes = num_groundtruth_classes + + def compute_object_detection_metrics(self, detected_boxes, detected_scores, + detected_class_labels, groundtruth_boxes, + groundtruth_class_labels, + groundtruth_is_difficult_lists): + """Compute Object Detection related metrics from a single image. + + Args: + detected_boxes: A float numpy array of shape [N, 4], representing N + regions of detected object regions. + Each row is of the format [y_min, x_min, y_max, x_max] + detected_scores: A float numpy array of shape [N, 1], representing + the confidence scores of the detected N object instances. + detected_class_labels: A integer numpy array of shape [N, 1], repreneting + the class labels of the detected N object instances. + groundtruth_boxes: A float numpy array of shape [M, 4], representing M + regions of object instances in ground truth + groundtruth_class_labels: An integer numpy array of shape [M, 1], + representing M class labels of object instances in ground truth + groundtruth_is_difficult_lists: A boolean numpy array of length M denoting + whether a ground truth box is a difficult instance or not + + Returns: + scores: A list of C float numpy arrays. Each numpy array is of + shape [K, 1], representing K scores detected with object class + label c + tp_fp_labels: A list of C boolean numpy arrays. Each numpy array + is of shape [K, 1], representing K True/False positive label of + object instances detected with class label c + is_class_correctly_detected_in_image: a numpy integer array of + shape [C, 1], indicating whether the correponding class has a least + one instance being correctly detected in the image + """ + detected_boxes, detected_scores, detected_class_labels = ( + self._remove_invalid_boxes(detected_boxes, detected_scores, + detected_class_labels)) + scores, tp_fp_labels = self._compute_tp_fp( + detected_boxes, detected_scores, detected_class_labels, + groundtruth_boxes, groundtruth_class_labels, + groundtruth_is_difficult_lists) + is_class_correctly_detected_in_image = self._compute_cor_loc( + detected_boxes, detected_scores, detected_class_labels, + groundtruth_boxes, groundtruth_class_labels) + return scores, tp_fp_labels, is_class_correctly_detected_in_image + + def _compute_cor_loc(self, detected_boxes, detected_scores, + detected_class_labels, groundtruth_boxes, + groundtruth_class_labels): + """Compute CorLoc score for object detection result. + + Args: + detected_boxes: A float numpy array of shape [N, 4], representing N + regions of detected object regions. + Each row is of the format [y_min, x_min, y_max, x_max] + detected_scores: A float numpy array of shape [N, 1], representing + the confidence scores of the detected N object instances. + detected_class_labels: A integer numpy array of shape [N, 1], repreneting + the class labels of the detected N object instances. + groundtruth_boxes: A float numpy array of shape [M, 4], representing M + regions of object instances in ground truth + groundtruth_class_labels: An integer numpy array of shape [M, 1], + representing M class labels of object instances in ground truth + Returns: + is_class_correctly_detected_in_image: a numpy integer array of + shape [C, 1], indicating whether the correponding class has a least + one instance being correctly detected in the image + """ + is_class_correctly_detected_in_image = np.zeros( + self.num_groundtruth_classes, dtype=int) + for i in range(self.num_groundtruth_classes): + gt_boxes_at_ith_class = groundtruth_boxes[ + groundtruth_class_labels == i, :] + detected_boxes_at_ith_class = detected_boxes[ + detected_class_labels == i, :] + detected_scores_at_ith_class = detected_scores[detected_class_labels == i] + is_class_correctly_detected_in_image[i] = ( + self._compute_is_aclass_correctly_detected_in_image( + detected_boxes_at_ith_class, detected_scores_at_ith_class, + gt_boxes_at_ith_class)) + + return is_class_correctly_detected_in_image + + def _compute_is_aclass_correctly_detected_in_image( + self, detected_boxes, detected_scores, groundtruth_boxes): + """Compute CorLoc score for a single class. + + Args: + detected_boxes: A numpy array of shape [N, 4] representing detected box + coordinates + detected_scores: A 1-d numpy array of length N representing classification + score + groundtruth_boxes: A numpy array of shape [M, 4] representing ground truth + box coordinates + + Returns: + is_class_correctly_detected_in_image: An integer 1 or 0 denoting whether a + class is correctly detected in the image or not + """ + if detected_boxes.size > 0: + if groundtruth_boxes.size > 0: + max_score_id = np.argmax(detected_scores) + detected_boxlist = np_box_list.BoxList( + np.expand_dims(detected_boxes[max_score_id, :], axis=0)) + gt_boxlist = np_box_list.BoxList(groundtruth_boxes) + iou = np_box_list_ops.iou(detected_boxlist, gt_boxlist) + if np.max(iou) >= self.matching_iou_threshold: + return 1 + return 0 + + def _compute_tp_fp(self, detected_boxes, detected_scores, + detected_class_labels, groundtruth_boxes, + groundtruth_class_labels, groundtruth_is_difficult_lists): + """Labels true/false positives of detections of an image across all classes. + + Args: + detected_boxes: A float numpy array of shape [N, 4], representing N + regions of detected object regions. + Each row is of the format [y_min, x_min, y_max, x_max] + detected_scores: A float numpy array of shape [N, 1], representing + the confidence scores of the detected N object instances. + detected_class_labels: A integer numpy array of shape [N, 1], repreneting + the class labels of the detected N object instances. + groundtruth_boxes: A float numpy array of shape [M, 4], representing M + regions of object instances in ground truth + groundtruth_class_labels: An integer numpy array of shape [M, 1], + representing M class labels of object instances in ground truth + groundtruth_is_difficult_lists: A boolean numpy array of length M denoting + whether a ground truth box is a difficult instance or not + + Returns: + result_scores: A list of float numpy arrays. Each numpy array is of + shape [K, 1], representing K scores detected with object class + label c + result_tp_fp_labels: A list of boolean numpy array. Each numpy array is of + shape [K, 1], representing K True/False positive label of object + instances detected with class label c + """ + result_scores = [] + result_tp_fp_labels = [] + for i in range(self.num_groundtruth_classes): + gt_boxes_at_ith_class = groundtruth_boxes[(groundtruth_class_labels == i + ), :] + groundtruth_is_difficult_list_at_ith_class = ( + groundtruth_is_difficult_lists[groundtruth_class_labels == i]) + detected_boxes_at_ith_class = detected_boxes[(detected_class_labels == i + ), :] + detected_scores_at_ith_class = detected_scores[detected_class_labels == i] + scores, tp_fp_labels = self._compute_tp_fp_for_single_class( + detected_boxes_at_ith_class, detected_scores_at_ith_class, + gt_boxes_at_ith_class, groundtruth_is_difficult_list_at_ith_class) + result_scores.append(scores) + result_tp_fp_labels.append(tp_fp_labels) + return result_scores, result_tp_fp_labels + + def _remove_invalid_boxes(self, detected_boxes, detected_scores, + detected_class_labels): + valid_indices = np.logical_and(detected_boxes[:, 0] < detected_boxes[:, 2], + detected_boxes[:, 1] < detected_boxes[:, 3]) + return (detected_boxes[valid_indices, :], detected_scores[valid_indices], + detected_class_labels[valid_indices]) + + def _compute_tp_fp_for_single_class(self, detected_boxes, detected_scores, + groundtruth_boxes, + groundtruth_is_difficult_list): + """Labels boxes detected with the same class from the same image as tp/fp. + + Args: + detected_boxes: A numpy array of shape [N, 4] representing detected box + coordinates + detected_scores: A 1-d numpy array of length N representing classification + score + groundtruth_boxes: A numpy array of shape [M, 4] representing ground truth + box coordinates + groundtruth_is_difficult_list: A boolean numpy array of length M denoting + whether a ground truth box is a difficult instance or not + + Returns: + scores: A numpy array representing the detection scores + tp_fp_labels: a boolean numpy array indicating whether a detection is a + true positive. + + """ + if detected_boxes.size == 0: + return np.array([], dtype=float), np.array([], dtype=bool) + detected_boxlist = np_box_list.BoxList(detected_boxes) + detected_boxlist.add_field('scores', detected_scores) + detected_boxlist = np_box_list_ops.non_max_suppression( + detected_boxlist, self.nms_max_output_boxes, self.nms_iou_threshold) + + scores = detected_boxlist.get_field('scores') + + if groundtruth_boxes.size == 0: + return scores, np.zeros(detected_boxlist.num_boxes(), dtype=bool) + gt_boxlist = np_box_list.BoxList(groundtruth_boxes) + + iou = np_box_list_ops.iou(detected_boxlist, gt_boxlist) + max_overlap_gt_ids = np.argmax(iou, axis=1) + is_gt_box_detected = np.zeros(gt_boxlist.num_boxes(), dtype=bool) + tp_fp_labels = np.zeros(detected_boxlist.num_boxes(), dtype=bool) + is_matched_to_difficult_box = np.zeros( + detected_boxlist.num_boxes(), dtype=bool) + for i in range(detected_boxlist.num_boxes()): + gt_id = max_overlap_gt_ids[i] + if iou[i, gt_id] >= self.matching_iou_threshold: + if not groundtruth_is_difficult_list[gt_id]: + if not is_gt_box_detected[gt_id]: + tp_fp_labels[i] = True + is_gt_box_detected[gt_id] = True + else: + is_matched_to_difficult_box[i] = True + return scores[~is_matched_to_difficult_box], tp_fp_labels[ + ~is_matched_to_difficult_box] diff --git a/object_detection/utils/per_image_evaluation_test.py b/object_detection/utils/per_image_evaluation_test.py new file mode 100644 index 000000000..8c449f1ac --- /dev/null +++ b/object_detection/utils/per_image_evaluation_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.utils.per_image_evaluation.""" + +import numpy as np +import tensorflow as tf + +from object_detection.utils import per_image_evaluation + + +class SingleClassTpFpWithDifficultBoxesTest(tf.test.TestCase): + + def setUp(self): + num_groundtruth_classes = 1 + matching_iou_threshold = 0.5 + nms_iou_threshold = 1.0 + nms_max_output_boxes = 10000 + self.eval = per_image_evaluation.PerImageEvaluation( + num_groundtruth_classes, matching_iou_threshold, nms_iou_threshold, + nms_max_output_boxes) + + self.detected_boxes = np.array([[0, 0, 1, 1], [0, 0, 2, 2], [0, 0, 3, 3]], + dtype=float) + self.detected_scores = np.array([0.6, 0.8, 0.5], dtype=float) + self.groundtruth_boxes = np.array([[0, 0, 1, 1], [0, 0, 10, 10]], + dtype=float) + + def test_match_to_not_difficult_box(self): + groundtruth_groundtruth_is_difficult_list = np.array([False, True], + dtype=bool) + scores, tp_fp_labels = self.eval._compute_tp_fp_for_single_class( + self.detected_boxes, self.detected_scores, self.groundtruth_boxes, + groundtruth_groundtruth_is_difficult_list) + expected_scores = np.array([0.8, 0.6, 0.5], dtype=float) + expected_tp_fp_labels = np.array([False, True, False], dtype=bool) + self.assertTrue(np.allclose(expected_scores, scores)) + self.assertTrue(np.allclose(expected_tp_fp_labels, tp_fp_labels)) + + def test_match_to_difficult_box(self): + groundtruth_groundtruth_is_difficult_list = np.array([True, False], + dtype=bool) + scores, tp_fp_labels = self.eval._compute_tp_fp_for_single_class( + self.detected_boxes, self.detected_scores, self.groundtruth_boxes, + groundtruth_groundtruth_is_difficult_list) + expected_scores = np.array([0.8, 0.5], dtype=float) + expected_tp_fp_labels = np.array([False, False], dtype=bool) + self.assertTrue(np.allclose(expected_scores, scores)) + self.assertTrue(np.allclose(expected_tp_fp_labels, tp_fp_labels)) + + +class SingleClassTpFpNoDifficultBoxesTest(tf.test.TestCase): + + def setUp(self): + num_groundtruth_classes = 1 + matching_iou_threshold1 = 0.5 + matching_iou_threshold2 = 0.1 + nms_iou_threshold = 1.0 + nms_max_output_boxes = 10000 + self.eval1 = per_image_evaluation.PerImageEvaluation( + num_groundtruth_classes, matching_iou_threshold1, nms_iou_threshold, + nms_max_output_boxes) + + self.eval2 = per_image_evaluation.PerImageEvaluation( + num_groundtruth_classes, matching_iou_threshold2, nms_iou_threshold, + nms_max_output_boxes) + + self.detected_boxes = np.array([[0, 0, 1, 1], [0, 0, 2, 2], [0, 0, 3, 3]], + dtype=float) + self.detected_scores = np.array([0.6, 0.8, 0.5], dtype=float) + + def test_no_true_positives(self): + groundtruth_boxes = np.array([[100, 100, 105, 105]], dtype=float) + groundtruth_groundtruth_is_difficult_list = np.zeros(1, dtype=bool) + scores, tp_fp_labels = self.eval1._compute_tp_fp_for_single_class( + self.detected_boxes, self.detected_scores, groundtruth_boxes, + groundtruth_groundtruth_is_difficult_list) + expected_scores = np.array([0.8, 0.6, 0.5], dtype=float) + expected_tp_fp_labels = np.array([False, False, False], dtype=bool) + self.assertTrue(np.allclose(expected_scores, scores)) + self.assertTrue(np.allclose(expected_tp_fp_labels, tp_fp_labels)) + + def test_one_true_positives_with_large_iou_threshold(self): + groundtruth_boxes = np.array([[0, 0, 1, 1]], dtype=float) + groundtruth_groundtruth_is_difficult_list = np.zeros(1, dtype=bool) + scores, tp_fp_labels = self.eval1._compute_tp_fp_for_single_class( + self.detected_boxes, self.detected_scores, groundtruth_boxes, + groundtruth_groundtruth_is_difficult_list) + expected_scores = np.array([0.8, 0.6, 0.5], dtype=float) + expected_tp_fp_labels = np.array([False, True, False], dtype=bool) + self.assertTrue(np.allclose(expected_scores, scores)) + self.assertTrue(np.allclose(expected_tp_fp_labels, tp_fp_labels)) + + def test_one_true_positives_with_very_small_iou_threshold(self): + groundtruth_boxes = np.array([[0, 0, 1, 1]], dtype=float) + groundtruth_groundtruth_is_difficult_list = np.zeros(1, dtype=bool) + scores, tp_fp_labels = self.eval2._compute_tp_fp_for_single_class( + self.detected_boxes, self.detected_scores, groundtruth_boxes, + groundtruth_groundtruth_is_difficult_list) + expected_scores = np.array([0.8, 0.6, 0.5], dtype=float) + expected_tp_fp_labels = np.array([True, False, False], dtype=bool) + self.assertTrue(np.allclose(expected_scores, scores)) + self.assertTrue(np.allclose(expected_tp_fp_labels, tp_fp_labels)) + + def test_two_true_positives_with_large_iou_threshold(self): + groundtruth_boxes = np.array([[0, 0, 1, 1], [0, 0, 3.5, 3.5]], dtype=float) + groundtruth_groundtruth_is_difficult_list = np.zeros(2, dtype=bool) + scores, tp_fp_labels = self.eval1._compute_tp_fp_for_single_class( + self.detected_boxes, self.detected_scores, groundtruth_boxes, + groundtruth_groundtruth_is_difficult_list) + expected_scores = np.array([0.8, 0.6, 0.5], dtype=float) + expected_tp_fp_labels = np.array([False, True, True], dtype=bool) + self.assertTrue(np.allclose(expected_scores, scores)) + self.assertTrue(np.allclose(expected_tp_fp_labels, tp_fp_labels)) + + +class MultiClassesTpFpTest(tf.test.TestCase): + + def test_tp_fp(self): + num_groundtruth_classes = 3 + matching_iou_threshold = 0.5 + nms_iou_threshold = 1.0 + nms_max_output_boxes = 10000 + eval1 = per_image_evaluation.PerImageEvaluation(num_groundtruth_classes, + matching_iou_threshold, + nms_iou_threshold, + nms_max_output_boxes) + detected_boxes = np.array([[0, 0, 1, 1], [10, 10, 5, 5], [0, 0, 2, 2], + [5, 10, 10, 5], [10, 5, 5, 10], [0, 0, 3, 3]], + dtype=float) + detected_scores = np.array([0.8, 0.1, 0.8, 0.9, 0.7, 0.8], dtype=float) + detected_class_labels = np.array([0, 1, 1, 2, 0, 2], dtype=int) + groundtruth_boxes = np.array([[0, 0, 1, 1], [0, 0, 3.5, 3.5]], dtype=float) + groundtruth_class_labels = np.array([0, 2], dtype=int) + groundtruth_groundtruth_is_difficult_list = np.zeros(2, dtype=float) + scores, tp_fp_labels, _ = eval1.compute_object_detection_metrics( + detected_boxes, detected_scores, detected_class_labels, + groundtruth_boxes, groundtruth_class_labels, + groundtruth_groundtruth_is_difficult_list) + expected_scores = [np.array([0.8], dtype=float)] * 3 + expected_tp_fp_labels = [np.array([True]), np.array([False]), np.array([True + ])] + for i in range(len(expected_scores)): + self.assertTrue(np.allclose(expected_scores[i], scores[i])) + self.assertTrue(np.array_equal(expected_tp_fp_labels[i], tp_fp_labels[i])) + + +class CorLocTest(tf.test.TestCase): + + def test_compute_corloc_with_normal_iou_threshold(self): + num_groundtruth_classes = 3 + matching_iou_threshold = 0.5 + nms_iou_threshold = 1.0 + nms_max_output_boxes = 10000 + eval1 = per_image_evaluation.PerImageEvaluation(num_groundtruth_classes, + matching_iou_threshold, + nms_iou_threshold, + nms_max_output_boxes) + detected_boxes = np.array([[0, 0, 1, 1], [0, 0, 2, 2], [0, 0, 3, 3], + [0, 0, 5, 5]], dtype=float) + detected_scores = np.array([0.9, 0.9, 0.1, 0.9], dtype=float) + detected_class_labels = np.array([0, 1, 0, 2], dtype=int) + groundtruth_boxes = np.array([[0, 0, 1, 1], [0, 0, 3, 3], [0, 0, 6, 6]], + dtype=float) + groundtruth_class_labels = np.array([0, 0, 2], dtype=int) + + is_class_correctly_detected_in_image = eval1._compute_cor_loc( + detected_boxes, detected_scores, detected_class_labels, + groundtruth_boxes, groundtruth_class_labels) + expected_result = np.array([1, 0, 1], dtype=int) + self.assertTrue(np.array_equal(expected_result, + is_class_correctly_detected_in_image)) + + def test_compute_corloc_with_very_large_iou_threshold(self): + num_groundtruth_classes = 3 + matching_iou_threshold = 0.9 + nms_iou_threshold = 1.0 + nms_max_output_boxes = 10000 + eval1 = per_image_evaluation.PerImageEvaluation(num_groundtruth_classes, + matching_iou_threshold, + nms_iou_threshold, + nms_max_output_boxes) + detected_boxes = np.array([[0, 0, 1, 1], [0, 0, 2, 2], [0, 0, 3, 3], + [0, 0, 5, 5]], dtype=float) + detected_scores = np.array([0.9, 0.9, 0.1, 0.9], dtype=float) + detected_class_labels = np.array([0, 1, 0, 2], dtype=int) + groundtruth_boxes = np.array([[0, 0, 1, 1], [0, 0, 3, 3], [0, 0, 6, 6]], + dtype=float) + groundtruth_class_labels = np.array([0, 0, 2], dtype=int) + + is_class_correctly_detected_in_image = eval1._compute_cor_loc( + detected_boxes, detected_scores, detected_class_labels, + groundtruth_boxes, groundtruth_class_labels) + expected_result = np.array([1, 0, 0], dtype=int) + self.assertTrue(np.array_equal(expected_result, + is_class_correctly_detected_in_image)) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/utils/shape_utils.py b/object_detection/utils/shape_utils.py new file mode 100644 index 000000000..6fee6ad08 --- /dev/null +++ b/object_detection/utils/shape_utils.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. +# ============================================================================== + +"""Utils used to manipulate tensor shapes.""" + +import tensorflow as tf + + +def _is_tensor(t): + """Returns a boolean indicating whether the input is a tensor. + + Args: + t: the input to be tested. + + Returns: + a boolean that indicates whether t is a tensor. + """ + return isinstance(t, (tf.Tensor, tf.SparseTensor, tf.Variable)) + + +def _set_dim_0(t, d0): + """Sets the 0-th dimension of the input tensor. + + Args: + t: the input tensor, assuming the rank is at least 1. + d0: an integer indicating the 0-th dimension of the input tensor. + + Returns: + the tensor t with the 0-th dimension set. + """ + t_shape = t.get_shape().as_list() + t_shape[0] = d0 + t.set_shape(t_shape) + return t + + +def pad_tensor(t, length): + """Pads the input tensor with 0s along the first dimension up to the length. + + Args: + t: the input tensor, assuming the rank is at least 1. + length: a tensor of shape [1] or an integer, indicating the first dimension + of the input tensor t after padding, assuming length <= t.shape[0]. + + Returns: + padded_t: the padded tensor, whose first dimension is length. If the length + is an integer, the first dimension of padded_t is set to length + statically. + """ + t_rank = tf.rank(t) + t_shape = tf.shape(t) + t_d0 = t_shape[0] + pad_d0 = tf.expand_dims(length - t_d0, 0) + pad_shape = tf.cond( + tf.greater(t_rank, 1), lambda: tf.concat([pad_d0, t_shape[1:]], 0), + lambda: tf.expand_dims(length - t_d0, 0)) + padded_t = tf.concat([t, tf.zeros(pad_shape, dtype=t.dtype)], 0) + if not _is_tensor(length): + padded_t = _set_dim_0(padded_t, length) + return padded_t + + +def clip_tensor(t, length): + """Clips the input tensor along the first dimension up to the length. + + Args: + t: the input tensor, assuming the rank is at least 1. + length: a tensor of shape [1] or an integer, indicating the first dimension + of the input tensor t after clipping, assuming length <= t.shape[0]. + + Returns: + clipped_t: the clipped tensor, whose first dimension is length. If the + length is an integer, the first dimension of clipped_t is set to length + statically. + """ + clipped_t = tf.gather(t, tf.range(length)) + if not _is_tensor(length): + clipped_t = _set_dim_0(clipped_t, length) + return clipped_t + + +def pad_or_clip_tensor(t, length): + """Pad or clip the input tensor along the first dimension. + + Args: + t: the input tensor, assuming the rank is at least 1. + length: a tensor of shape [1] or an integer, indicating the first dimension + of the input tensor t after processing. + + Returns: + processed_t: the processed tensor, whose first dimension is length. If the + length is an integer, the first dimension of the processed tensor is set + to length statically. + """ + processed_t = tf.cond( + tf.greater(tf.shape(t)[0], length), + lambda: clip_tensor(t, length), + lambda: pad_tensor(t, length)) + if not _is_tensor(length): + processed_t = _set_dim_0(processed_t, length) + return processed_t diff --git a/object_detection/utils/shape_utils_test.py b/object_detection/utils/shape_utils_test.py new file mode 100644 index 000000000..b1fa945cb --- /dev/null +++ b/object_detection/utils/shape_utils_test.py @@ -0,0 +1,120 @@ +# 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.utils.shape_utils.""" + +import tensorflow as tf + +from object_detection.utils import shape_utils + + +class UtilTest(tf.test.TestCase): + + def test_pad_tensor_using_integer_input(self): + t1 = tf.constant([1], dtype=tf.int32) + pad_t1 = shape_utils.pad_tensor(t1, 2) + t2 = tf.constant([[0.1, 0.2]], dtype=tf.float32) + pad_t2 = shape_utils.pad_tensor(t2, 2) + + self.assertEqual(2, pad_t1.get_shape()[0]) + self.assertEqual(2, pad_t2.get_shape()[0]) + + with self.test_session() as sess: + pad_t1_result, pad_t2_result = sess.run([pad_t1, pad_t2]) + self.assertAllEqual([1, 0], pad_t1_result) + self.assertAllClose([[0.1, 0.2], [0, 0]], pad_t2_result) + + def test_pad_tensor_using_tensor_input(self): + t1 = tf.constant([1], dtype=tf.int32) + pad_t1 = shape_utils.pad_tensor(t1, tf.constant(2)) + t2 = tf.constant([[0.1, 0.2]], dtype=tf.float32) + pad_t2 = shape_utils.pad_tensor(t2, tf.constant(2)) + + with self.test_session() as sess: + pad_t1_result, pad_t2_result = sess.run([pad_t1, pad_t2]) + self.assertAllEqual([1, 0], pad_t1_result) + self.assertAllClose([[0.1, 0.2], [0, 0]], pad_t2_result) + + def test_clip_tensor_using_integer_input(self): + t1 = tf.constant([1, 2, 3], dtype=tf.int32) + clip_t1 = shape_utils.clip_tensor(t1, 2) + t2 = tf.constant([[0.1, 0.2], [0.2, 0.4], [0.5, 0.8]], dtype=tf.float32) + clip_t2 = shape_utils.clip_tensor(t2, 2) + + self.assertEqual(2, clip_t1.get_shape()[0]) + self.assertEqual(2, clip_t2.get_shape()[0]) + + with self.test_session() as sess: + clip_t1_result, clip_t2_result = sess.run([clip_t1, clip_t2]) + self.assertAllEqual([1, 2], clip_t1_result) + self.assertAllClose([[0.1, 0.2], [0.2, 0.4]], clip_t2_result) + + def test_clip_tensor_using_tensor_input(self): + t1 = tf.constant([1, 2, 3], dtype=tf.int32) + clip_t1 = shape_utils.clip_tensor(t1, tf.constant(2)) + t2 = tf.constant([[0.1, 0.2], [0.2, 0.4], [0.5, 0.8]], dtype=tf.float32) + clip_t2 = shape_utils.clip_tensor(t2, tf.constant(2)) + + with self.test_session() as sess: + clip_t1_result, clip_t2_result = sess.run([clip_t1, clip_t2]) + self.assertAllEqual([1, 2], clip_t1_result) + self.assertAllClose([[0.1, 0.2], [0.2, 0.4]], clip_t2_result) + + def test_pad_or_clip_tensor_using_integer_input(self): + t1 = tf.constant([1], dtype=tf.int32) + tt1 = shape_utils.pad_or_clip_tensor(t1, 2) + t2 = tf.constant([[0.1, 0.2]], dtype=tf.float32) + tt2 = shape_utils.pad_or_clip_tensor(t2, 2) + + t3 = tf.constant([1, 2, 3], dtype=tf.int32) + tt3 = shape_utils.clip_tensor(t3, 2) + t4 = tf.constant([[0.1, 0.2], [0.2, 0.4], [0.5, 0.8]], dtype=tf.float32) + tt4 = shape_utils.clip_tensor(t4, 2) + + self.assertEqual(2, tt1.get_shape()[0]) + self.assertEqual(2, tt2.get_shape()[0]) + self.assertEqual(2, tt3.get_shape()[0]) + self.assertEqual(2, tt4.get_shape()[0]) + + with self.test_session() as sess: + tt1_result, tt2_result, tt3_result, tt4_result = sess.run( + [tt1, tt2, tt3, tt4]) + self.assertAllEqual([1, 0], tt1_result) + self.assertAllClose([[0.1, 0.2], [0, 0]], tt2_result) + self.assertAllEqual([1, 2], tt3_result) + self.assertAllClose([[0.1, 0.2], [0.2, 0.4]], tt4_result) + + def test_pad_or_clip_tensor_using_tensor_input(self): + t1 = tf.constant([1], dtype=tf.int32) + tt1 = shape_utils.pad_or_clip_tensor(t1, tf.constant(2)) + t2 = tf.constant([[0.1, 0.2]], dtype=tf.float32) + tt2 = shape_utils.pad_or_clip_tensor(t2, tf.constant(2)) + + t3 = tf.constant([1, 2, 3], dtype=tf.int32) + tt3 = shape_utils.clip_tensor(t3, tf.constant(2)) + t4 = tf.constant([[0.1, 0.2], [0.2, 0.4], [0.5, 0.8]], dtype=tf.float32) + tt4 = shape_utils.clip_tensor(t4, tf.constant(2)) + + with self.test_session() as sess: + tt1_result, tt2_result, tt3_result, tt4_result = sess.run( + [tt1, tt2, tt3, tt4]) + self.assertAllEqual([1, 0], tt1_result) + self.assertAllClose([[0.1, 0.2], [0, 0]], tt2_result) + self.assertAllEqual([1, 2], tt3_result) + self.assertAllClose([[0.1, 0.2], [0.2, 0.4]], tt4_result) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/utils/static_shape.py b/object_detection/utils/static_shape.py new file mode 100644 index 000000000..8e4e522f1 --- /dev/null +++ b/object_detection/utils/static_shape.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. +# ============================================================================== + +"""Helper functions to access TensorShape values. + +The rank 4 tensor_shape must be of the form [batch_size, height, width, depth]. +""" + + +def get_batch_size(tensor_shape): + """Returns batch size from the tensor shape. + + Args: + tensor_shape: A rank 4 TensorShape. + + Returns: + An integer representing the batch size of the tensor. + """ + tensor_shape.assert_has_rank(rank=4) + return tensor_shape[0].value + + +def get_height(tensor_shape): + """Returns height from the tensor shape. + + Args: + tensor_shape: A rank 4 TensorShape. + + Returns: + An integer representing the height of the tensor. + """ + tensor_shape.assert_has_rank(rank=4) + return tensor_shape[1].value + + +def get_width(tensor_shape): + """Returns width from the tensor shape. + + Args: + tensor_shape: A rank 4 TensorShape. + + Returns: + An integer representing the width of the tensor. + """ + tensor_shape.assert_has_rank(rank=4) + return tensor_shape[2].value + + +def get_depth(tensor_shape): + """Returns depth from the tensor shape. + + Args: + tensor_shape: A rank 4 TensorShape. + + Returns: + An integer representing the depth of the tensor. + """ + tensor_shape.assert_has_rank(rank=4) + return tensor_shape[3].value diff --git a/object_detection/utils/static_shape_test.py b/object_detection/utils/static_shape_test.py new file mode 100644 index 000000000..99307e932 --- /dev/null +++ b/object_detection/utils/static_shape_test.py @@ -0,0 +1,50 @@ +# 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.utils.static_shape.""" + +import tensorflow as tf + +from object_detection.utils import static_shape + + +class StaticShapeTest(tf.test.TestCase): + + def test_return_correct_batchSize(self): + tensor_shape = tf.TensorShape(dims=[32, 299, 384, 3]) + self.assertEqual(32, static_shape.get_batch_size(tensor_shape)) + + def test_return_correct_height(self): + tensor_shape = tf.TensorShape(dims=[32, 299, 384, 3]) + self.assertEqual(299, static_shape.get_height(tensor_shape)) + + def test_return_correct_width(self): + tensor_shape = tf.TensorShape(dims=[32, 299, 384, 3]) + self.assertEqual(384, static_shape.get_width(tensor_shape)) + + def test_return_correct_depth(self): + tensor_shape = tf.TensorShape(dims=[32, 299, 384, 3]) + self.assertEqual(3, static_shape.get_depth(tensor_shape)) + + def test_die_on_tensor_shape_with_rank_three(self): + tensor_shape = tf.TensorShape(dims=[32, 299, 384]) + with self.assertRaises(ValueError): + static_shape.get_batch_size(tensor_shape) + static_shape.get_height(tensor_shape) + static_shape.get_width(tensor_shape) + static_shape.get_depth(tensor_shape) + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/utils/test_utils.py b/object_detection/utils/test_utils.py new file mode 100644 index 000000000..f4eb8171c --- /dev/null +++ b/object_detection/utils/test_utils.py @@ -0,0 +1,137 @@ +# 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 functions which are convenient for unit testing.""" +import numpy as np +import tensorflow as tf + +from object_detection.core import anchor_generator +from object_detection.core import box_coder +from object_detection.core import box_list +from object_detection.core import box_predictor +from object_detection.core import matcher + + +class MockBoxCoder(box_coder.BoxCoder): + """Simple `difference` BoxCoder.""" + + @property + def code_size(self): + return 4 + + def _encode(self, boxes, anchors): + return boxes.get() - anchors.get() + + def _decode(self, rel_codes, anchors): + return box_list.BoxList(rel_codes + anchors.get()) + + +class MockBoxPredictor(box_predictor.BoxPredictor): + """Simple box predictor that ignores inputs and outputs all zeros.""" + + def __init__(self, is_training, num_classes): + super(MockBoxPredictor, self).__init__(is_training, num_classes) + + def _predict(self, image_features, num_predictions_per_location): + batch_size = image_features.get_shape().as_list()[0] + num_anchors = (image_features.get_shape().as_list()[1] + * image_features.get_shape().as_list()[2]) + code_size = 4 + zero = tf.reduce_sum(0 * image_features) + box_encodings = zero + tf.zeros( + (batch_size, num_anchors, 1, code_size), dtype=tf.float32) + class_predictions_with_background = zero + tf.zeros( + (batch_size, num_anchors, self.num_classes + 1), dtype=tf.float32) + return {box_predictor.BOX_ENCODINGS: box_encodings, + box_predictor.CLASS_PREDICTIONS_WITH_BACKGROUND: + class_predictions_with_background} + + +class MockAnchorGenerator(anchor_generator.AnchorGenerator): + """Mock anchor generator.""" + + def name_scope(self): + return 'MockAnchorGenerator' + + def num_anchors_per_location(self): + return [1] + + def _generate(self, feature_map_shape_list): + num_anchors = sum([shape[0] * shape[1] for shape in feature_map_shape_list]) + return box_list.BoxList(tf.zeros((num_anchors, 4), dtype=tf.float32)) + + +class MockMatcher(matcher.Matcher): + """Simple matcher that matches first anchor to first groundtruth box.""" + + def _match(self, similarity_matrix): + return tf.constant([0, -1, -1, -1], dtype=tf.int32) + + +def create_diagonal_gradient_image(height, width, depth): + """Creates pyramid image. Useful for testing. + + For example, pyramid_image(5, 6, 1) looks like: + # [[[ 5. 4. 3. 2. 1. 0.] + # [ 6. 5. 4. 3. 2. 1.] + # [ 7. 6. 5. 4. 3. 2.] + # [ 8. 7. 6. 5. 4. 3.] + # [ 9. 8. 7. 6. 5. 4.]]] + + Args: + height: height of image + width: width of image + depth: depth of image + + Returns: + pyramid image + """ + row = np.arange(height) + col = np.arange(width)[::-1] + image_layer = np.expand_dims(row, 1) + col + image_layer = np.expand_dims(image_layer, 2) + + image = image_layer + for i in range(1, depth): + image = np.concatenate((image, image_layer * pow(10, i)), 2) + + return image.astype(np.float32) + + +def create_random_boxes(num_boxes, max_height, max_width): + """Creates random bounding boxes of specific maximum height and width. + + Args: + num_boxes: number of boxes. + max_height: maximum height of boxes. + max_width: maximum width of boxes. + + Returns: + boxes: numpy array of shape [num_boxes, 4]. Each row is in form + [y_min, x_min, y_max, x_max]. + """ + + y_1 = np.random.uniform(size=(1, num_boxes)) * max_height + y_2 = np.random.uniform(size=(1, num_boxes)) * max_height + x_1 = np.random.uniform(size=(1, num_boxes)) * max_width + x_2 = np.random.uniform(size=(1, num_boxes)) * max_width + + boxes = np.zeros(shape=(num_boxes, 4)) + boxes[:, 0] = np.minimum(y_1, y_2) + boxes[:, 1] = np.minimum(x_1, x_2) + boxes[:, 2] = np.maximum(y_1, y_2) + boxes[:, 3] = np.maximum(x_1, x_2) + + return boxes.astype(np.float32) diff --git a/object_detection/utils/test_utils_test.py b/object_detection/utils/test_utils_test.py new file mode 100644 index 000000000..1a4799c69 --- /dev/null +++ b/object_detection/utils/test_utils_test.py @@ -0,0 +1,73 @@ +# 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.utils.test_utils.""" + +import numpy as np +import tensorflow as tf + +from object_detection.utils import test_utils + + +class TestUtilsTest(tf.test.TestCase): + + def test_diagonal_gradient_image(self): + """Tests if a good pyramid image is created.""" + pyramid_image = test_utils.create_diagonal_gradient_image(3, 4, 2) + + # Test which is easy to understand. + expected_first_channel = np.array([[3, 2, 1, 0], + [4, 3, 2, 1], + [5, 4, 3, 2]], dtype=np.float32) + self.assertAllEqual(np.squeeze(pyramid_image[:, :, 0]), + expected_first_channel) + + # Actual test. + expected_image = np.array([[[3, 30], + [2, 20], + [1, 10], + [0, 0]], + [[4, 40], + [3, 30], + [2, 20], + [1, 10]], + [[5, 50], + [4, 40], + [3, 30], + [2, 20]]], dtype=np.float32) + + self.assertAllEqual(pyramid_image, expected_image) + + def test_random_boxes(self): + """Tests if valid random boxes are created.""" + num_boxes = 1000 + max_height = 3 + max_width = 5 + boxes = test_utils.create_random_boxes(num_boxes, + max_height, + max_width) + + true_column = np.ones(shape=(num_boxes)) == 1 + self.assertAllEqual(boxes[:, 0] < boxes[:, 2], true_column) + self.assertAllEqual(boxes[:, 1] < boxes[:, 3], true_column) + + self.assertTrue(boxes[:, 0].min() >= 0) + self.assertTrue(boxes[:, 1].min() >= 0) + self.assertTrue(boxes[:, 2].max() <= max_height) + self.assertTrue(boxes[:, 3].max() <= max_width) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/utils/variables_helper.py b/object_detection/utils/variables_helper.py new file mode 100644 index 000000000..1e091a144 --- /dev/null +++ b/object_detection/utils/variables_helper.py @@ -0,0 +1,133 @@ +# 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. +# ============================================================================== + +"""Helper functions for manipulating collections of variables during training. +""" +import logging +import re + +import tensorflow as tf + +slim = tf.contrib.slim + + +# TODO: Consider replacing with tf.contrib.filter_variables in +# tensorflow/contrib/framework/python/ops/variables.py +def filter_variables(variables, filter_regex_list, invert=False): + """Filters out the variables matching the filter_regex. + + Filter out the variables whose name matches the any of the regular + expressions in filter_regex_list and returns the remaining variables. + Optionally, if invert=True, the complement set is returned. + + Args: + variables: a list of tensorflow variables. + filter_regex_list: a list of string regular expressions. + invert: (boolean). If True, returns the complement of the filter set; that + is, all variables matching filter_regex are kept and all others discarded. + + Returns: + a list of filtered variables. + """ + kept_vars = [] + variables_to_ignore_patterns = filter(None, filter_regex_list) + for var in variables: + add = True + for pattern in variables_to_ignore_patterns: + if re.match(pattern, var.op.name): + add = False + break + if add != invert: + kept_vars.append(var) + return kept_vars + + +def multiply_gradients_matching_regex(grads_and_vars, regex_list, multiplier): + """Multiply gradients whose variable names match a regular expression. + + Args: + grads_and_vars: A list of gradient to variable pairs (tuples). + regex_list: A list of string regular expressions. + multiplier: A (float) multiplier to apply to each gradient matching the + regular expression. + + Returns: + grads_and_vars: A list of gradient to variable pairs (tuples). + """ + variables = [pair[1] for pair in grads_and_vars] + matching_vars = filter_variables(variables, regex_list, invert=True) + for var in matching_vars: + logging.info('Applying multiplier %f to variable [%s]', + multiplier, var.op.name) + grad_multipliers = {var: float(multiplier) for var in matching_vars} + return slim.learning.multiply_gradients(grads_and_vars, + grad_multipliers) + + +def freeze_gradients_matching_regex(grads_and_vars, regex_list): + """Freeze gradients whose variable names match a regular expression. + + Args: + grads_and_vars: A list of gradient to variable pairs (tuples). + regex_list: A list of string regular expressions. + + Returns: + grads_and_vars: A list of gradient to variable pairs (tuples) that do not + contain the variables and gradients matching the regex. + """ + variables = [pair[1] for pair in grads_and_vars] + matching_vars = filter_variables(variables, regex_list, invert=True) + kept_grads_and_vars = [pair for pair in grads_and_vars + if pair[1] not in matching_vars] + for var in matching_vars: + logging.info('Freezing variable [%s]', var.op.name) + return kept_grads_and_vars + + +def get_variables_available_in_checkpoint(variables, checkpoint_path): + """Returns the subset of variables available in the checkpoint. + + Inspects given checkpoint and returns the subset of variables that are + available in it. + + TODO: force input and output to be a dictionary. + + Args: + variables: a list or dictionary of variables to find in checkpoint. + checkpoint_path: path to the checkpoint to restore variables from. + + Returns: + A list or dictionary of variables. + Raises: + ValueError: if `variables` is not a list or dict. + """ + if isinstance(variables, list): + variable_names_map = {variable.op.name: variable for variable in variables} + elif isinstance(variables, dict): + variable_names_map = variables + else: + raise ValueError('`variables` is expected to be a list or dict.') + ckpt_reader = tf.train.NewCheckpointReader(checkpoint_path) + ckpt_vars = ckpt_reader.get_variable_to_shape_map().keys() + vars_in_ckpt = {} + for variable_name, variable in sorted(variable_names_map.iteritems()): + if variable_name in ckpt_vars: + vars_in_ckpt[variable_name] = variable + else: + logging.warning('Variable [%s] not available in checkpoint', + variable_name) + if isinstance(variables, list): + return vars_in_ckpt.values() + return vars_in_ckpt diff --git a/object_detection/utils/variables_helper_test.py b/object_detection/utils/variables_helper_test.py new file mode 100644 index 000000000..c04b11916 --- /dev/null +++ b/object_detection/utils/variables_helper_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 object_detection.utils.variables_helper.""" +import os + +import tensorflow as tf + +from object_detection.utils import variables_helper + + +class FilterVariablesTest(tf.test.TestCase): + + def _create_variables(self): + return [tf.Variable(1.0, name='FeatureExtractor/InceptionV3/weights'), + tf.Variable(1.0, name='FeatureExtractor/InceptionV3/biases'), + tf.Variable(1.0, name='StackProposalGenerator/weights'), + tf.Variable(1.0, name='StackProposalGenerator/biases')] + + def test_return_all_variables_when_empty_regex(self): + variables = self._create_variables() + out_variables = variables_helper.filter_variables(variables, ['']) + self.assertItemsEqual(out_variables, variables) + + def test_return_variables_which_do_not_match_single_regex(self): + variables = self._create_variables() + out_variables = variables_helper.filter_variables(variables, + ['FeatureExtractor/.*']) + self.assertItemsEqual(out_variables, variables[2:]) + + def test_return_variables_which_do_not_match_any_regex_in_list(self): + variables = self._create_variables() + out_variables = variables_helper.filter_variables(variables, [ + 'FeatureExtractor.*biases', 'StackProposalGenerator.*biases' + ]) + self.assertItemsEqual(out_variables, [variables[0], variables[2]]) + + def test_return_variables_matching_empty_regex_list(self): + variables = self._create_variables() + out_variables = variables_helper.filter_variables( + variables, [''], invert=True) + self.assertItemsEqual(out_variables, []) + + def test_return_variables_matching_some_regex_in_list(self): + variables = self._create_variables() + out_variables = variables_helper.filter_variables( + variables, + ['FeatureExtractor.*biases', 'StackProposalGenerator.*biases'], + invert=True) + self.assertItemsEqual(out_variables, [variables[1], variables[3]]) + + +class MultiplyGradientsMatchingRegexTest(tf.test.TestCase): + + def _create_grads_and_vars(self): + return [(tf.constant(1.0), + tf.Variable(1.0, name='FeatureExtractor/InceptionV3/weights')), + (tf.constant(2.0), + tf.Variable(2.0, name='FeatureExtractor/InceptionV3/biases')), + (tf.constant(3.0), + tf.Variable(3.0, name='StackProposalGenerator/weights')), + (tf.constant(4.0), + tf.Variable(4.0, name='StackProposalGenerator/biases'))] + + def test_multiply_all_feature_extractor_variables(self): + grads_and_vars = self._create_grads_and_vars() + regex_list = ['FeatureExtractor/.*'] + multiplier = 0.0 + grads_and_vars = variables_helper.multiply_gradients_matching_regex( + grads_and_vars, regex_list, multiplier) + exp_output = [(0.0, 1.0), (0.0, 2.0), (3.0, 3.0), (4.0, 4.0)] + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + output = sess.run(grads_and_vars) + self.assertItemsEqual(output, exp_output) + + def test_multiply_all_bias_variables(self): + grads_and_vars = self._create_grads_and_vars() + regex_list = ['.*/biases'] + multiplier = 0.0 + grads_and_vars = variables_helper.multiply_gradients_matching_regex( + grads_and_vars, regex_list, multiplier) + exp_output = [(1.0, 1.0), (0.0, 2.0), (3.0, 3.0), (0.0, 4.0)] + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + output = sess.run(grads_and_vars) + self.assertItemsEqual(output, exp_output) + + +class FreezeGradientsMatchingRegexTest(tf.test.TestCase): + + def _create_grads_and_vars(self): + return [(tf.constant(1.0), + tf.Variable(1.0, name='FeatureExtractor/InceptionV3/weights')), + (tf.constant(2.0), + tf.Variable(2.0, name='FeatureExtractor/InceptionV3/biases')), + (tf.constant(3.0), + tf.Variable(3.0, name='StackProposalGenerator/weights')), + (tf.constant(4.0), + tf.Variable(4.0, name='StackProposalGenerator/biases'))] + + def test_freeze_all_feature_extractor_variables(self): + grads_and_vars = self._create_grads_and_vars() + regex_list = ['FeatureExtractor/.*'] + grads_and_vars = variables_helper.freeze_gradients_matching_regex( + grads_and_vars, regex_list) + exp_output = [(3.0, 3.0), (4.0, 4.0)] + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + output = sess.run(grads_and_vars) + self.assertItemsEqual(output, exp_output) + + +class GetVariablesAvailableInCheckpointTest(tf.test.TestCase): + + def test_return_all_variables_from_checkpoint(self): + variables = [ + tf.Variable(1.0, name='weights'), + tf.Variable(1.0, name='biases') + ] + checkpoint_path = os.path.join(self.get_temp_dir(), 'graph.pb') + init_op = tf.global_variables_initializer() + saver = tf.train.Saver(variables) + with self.test_session() as sess: + sess.run(init_op) + saver.save(sess, checkpoint_path) + out_variables = variables_helper.get_variables_available_in_checkpoint( + variables, checkpoint_path) + self.assertItemsEqual(out_variables, variables) + + def test_return_variables_available_in_checkpoint(self): + checkpoint_path = os.path.join(self.get_temp_dir(), 'graph.pb') + graph1_variables = [ + tf.Variable(1.0, name='weights'), + ] + init_op = tf.global_variables_initializer() + saver = tf.train.Saver(graph1_variables) + with self.test_session() as sess: + sess.run(init_op) + saver.save(sess, checkpoint_path) + + graph2_variables = graph1_variables + [tf.Variable(1.0, name='biases')] + out_variables = variables_helper.get_variables_available_in_checkpoint( + graph2_variables, checkpoint_path) + self.assertItemsEqual(out_variables, graph1_variables) + + def test_return_variables_available_an_checkpoint_with_dict_inputs(self): + checkpoint_path = os.path.join(self.get_temp_dir(), 'graph.pb') + graph1_variables = [ + tf.Variable(1.0, name='ckpt_weights'), + ] + init_op = tf.global_variables_initializer() + saver = tf.train.Saver(graph1_variables) + with self.test_session() as sess: + sess.run(init_op) + saver.save(sess, checkpoint_path) + + graph2_variables_dict = { + 'ckpt_weights': tf.Variable(1.0, name='weights'), + 'ckpt_biases': tf.Variable(1.0, name='biases') + } + out_variables = variables_helper.get_variables_available_in_checkpoint( + graph2_variables_dict, checkpoint_path) + self.assertTrue(isinstance(out_variables, dict)) + self.assertItemsEqual(out_variables.keys(), ['ckpt_weights']) + self.assertTrue(out_variables['ckpt_weights'].op.name == 'weights') + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/utils/visualization_utils.py b/object_detection/utils/visualization_utils.py new file mode 100644 index 000000000..23eed7b23 --- /dev/null +++ b/object_detection/utils/visualization_utils.py @@ -0,0 +1,422 @@ +# 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 set of functions that are used for visualization. + +These functions often receive an image, perform some visualization on the image. +The functions do not return a value, instead they modify the image itself. + +""" +import collections +import numpy as np +import PIL.Image as Image +import PIL.ImageColor as ImageColor +import PIL.ImageDraw as ImageDraw +import PIL.ImageFont as ImageFont +import six +import tensorflow as tf + + +_TITLE_LEFT_MARGIN = 10 +_TITLE_TOP_MARGIN = 10 +STANDARD_COLORS = [ + 'AliceBlue', 'Chartreuse', 'Aqua', 'Aquamarine', 'Azure', 'Beige', 'Bisque', + 'BlanchedAlmond', 'BlueViolet', 'BurlyWood', 'CadetBlue', 'AntiqueWhite', + 'Chocolate', 'Coral', 'CornflowerBlue', 'Cornsilk', 'Crimson', 'Cyan', + 'DarkCyan', 'DarkGoldenRod', 'DarkGrey', 'DarkKhaki', 'DarkOrange', + 'DarkOrchid', 'DarkSalmon', 'DarkSeaGreen', 'DarkTurquoise', 'DarkViolet', + 'DeepPink', 'DeepSkyBlue', 'DodgerBlue', 'FireBrick', 'FloralWhite', + 'ForestGreen', 'Fuchsia', 'Gainsboro', 'GhostWhite', 'Gold', 'GoldenRod', + 'Salmon', 'Tan', 'HoneyDew', 'HotPink', 'IndianRed', 'Ivory', 'Khaki', + 'Lavender', 'LavenderBlush', 'LawnGreen', 'LemonChiffon', 'LightBlue', + 'LightCoral', 'LightCyan', 'LightGoldenRodYellow', 'LightGray', 'LightGrey', + 'LightGreen', 'LightPink', 'LightSalmon', 'LightSeaGreen', 'LightSkyBlue', + 'LightSlateGray', 'LightSlateGrey', 'LightSteelBlue', 'LightYellow', 'Lime', + 'LimeGreen', 'Linen', 'Magenta', 'MediumAquaMarine', 'MediumOrchid', + 'MediumPurple', 'MediumSeaGreen', 'MediumSlateBlue', 'MediumSpringGreen', + 'MediumTurquoise', 'MediumVioletRed', 'MintCream', 'MistyRose', 'Moccasin', + 'NavajoWhite', 'OldLace', 'Olive', 'OliveDrab', 'Orange', 'OrangeRed', + 'Orchid', 'PaleGoldenRod', 'PaleGreen', 'PaleTurquoise', 'PaleVioletRed', + 'PapayaWhip', 'PeachPuff', 'Peru', 'Pink', 'Plum', 'PowderBlue', 'Purple', + 'Red', 'RosyBrown', 'RoyalBlue', 'SaddleBrown', 'Green', 'SandyBrown', + 'SeaGreen', 'SeaShell', 'Sienna', 'Silver', 'SkyBlue', 'SlateBlue', + 'SlateGray', 'SlateGrey', 'Snow', 'SpringGreen', 'SteelBlue', 'GreenYellow', + 'Teal', 'Thistle', 'Tomato', 'Turquoise', 'Violet', 'Wheat', 'White', + 'WhiteSmoke', 'Yellow', 'YellowGreen' +] + + +def save_image_array_as_png(image, output_path): + """Saves an image (represented as a numpy array) to PNG. + + Args: + image: a numpy array with shape [height, width, 3]. + output_path: path to which image should be written. + """ + image_pil = Image.fromarray(np.uint8(image)).convert('RGB') + with tf.gfile.Open(output_path, 'w') as fid: + image_pil.save(fid, 'PNG') + + +def encode_image_array_as_png_str(image): + """Encodes a numpy array into a PNG string. + + Args: + image: a numpy array with shape [height, width, 3]. + + Returns: + PNG encoded image string. + """ + image_pil = Image.fromarray(np.uint8(image)) + output = six.StringIO() + image_pil.save(output, format='PNG') + png_string = output.getvalue() + output.close() + return png_string + + +def draw_bounding_box_on_image_array(image, + ymin, + xmin, + ymax, + xmax, + color='red', + thickness=4, + display_str_list=(), + use_normalized_coordinates=True): + """Adds a bounding box to an image (numpy array). + + Args: + image: a numpy array with shape [height, width, 3]. + ymin: ymin of bounding box in normalized coordinates (same below). + xmin: xmin of bounding box. + ymax: ymax of bounding box. + xmax: xmax of bounding box. + color: color to draw bounding box. Default is red. + thickness: line thickness. Default value is 4. + display_str_list: list of strings to display in box + (each to be shown on its own line). + use_normalized_coordinates: If True (default), treat coordinates + ymin, xmin, ymax, xmax as relative to the image. Otherwise treat + coordinates as absolute. + """ + image_pil = Image.fromarray(np.uint8(image)).convert('RGB') + draw_bounding_box_on_image(image_pil, ymin, xmin, ymax, xmax, color, + thickness, display_str_list, + use_normalized_coordinates) + np.copyto(image, np.array(image_pil)) + + +def draw_bounding_box_on_image(image, + ymin, + xmin, + ymax, + xmax, + color='red', + thickness=4, + display_str_list=(), + use_normalized_coordinates=True): + """Adds a bounding box to an image. + + Each string in display_str_list is displayed on a separate line above the + bounding box in black text on a rectangle filled with the input 'color'. + + Args: + image: a PIL.Image object. + ymin: ymin of bounding box. + xmin: xmin of bounding box. + ymax: ymax of bounding box. + xmax: xmax of bounding box. + color: color to draw bounding box. Default is red. + thickness: line thickness. Default value is 4. + display_str_list: list of strings to display in box + (each to be shown on its own line). + use_normalized_coordinates: If True (default), treat coordinates + ymin, xmin, ymax, xmax as relative to the image. Otherwise treat + coordinates as absolute. + """ + draw = ImageDraw.Draw(image) + im_width, im_height = image.size + if use_normalized_coordinates: + (left, right, top, bottom) = (xmin * im_width, xmax * im_width, + ymin * im_height, ymax * im_height) + else: + (left, right, top, bottom) = (xmin, xmax, ymin, ymax) + draw.line([(left, top), (left, bottom), (right, bottom), + (right, top), (left, top)], width=thickness, fill=color) + font = ImageFont.load_default() + + text_bottom = top + # Reverse list and print from bottom to top. + for display_str in display_str_list[::-1]: + text_width, text_height = font.getsize(display_str) + margin = np.ceil(0.05 * text_height) + draw.rectangle( + [(left, text_bottom - text_height - 2 * margin), (left + text_width, + text_bottom)], + fill=color) + draw.text( + (left + margin, text_bottom - text_height - margin), + display_str, + fill='black', + font=font) + text_bottom -= text_height - 2 * margin + + +def draw_bounding_boxes_on_image_array(image, + boxes, + color='red', + thickness=4, + display_str_list_list=()): + """Draws bounding boxes on image (numpy array). + + Args: + image: a numpy array object. + boxes: a 2 dimensional numpy array of [N, 4]: (ymin, xmin, ymax, xmax). + The coordinates are in normalized format between [0, 1]. + color: color to draw bounding box. Default is red. + thickness: line thickness. Default value is 4. + display_str_list_list: list of list of strings. + a list of strings for each bounding box. + The reason to pass a list of strings for a + bounding box is that it might contain + multiple labels. + + Raises: + ValueError: if boxes is not a [N, 4] array + """ + image_pil = Image.fromarray(image) + draw_bounding_boxes_on_image(image_pil, boxes, color, thickness, + display_str_list_list) + np.copyto(image, np.array(image_pil)) + + +def draw_bounding_boxes_on_image(image, + boxes, + color='red', + thickness=4, + display_str_list_list=()): + """Draws bounding boxes on image. + + Args: + image: a PIL.Image object. + boxes: a 2 dimensional numpy array of [N, 4]: (ymin, xmin, ymax, xmax). + The coordinates are in normalized format between [0, 1]. + color: color to draw bounding box. Default is red. + thickness: line thickness. Default value is 4. + display_str_list_list: list of list of strings. + a list of strings for each bounding box. + The reason to pass a list of strings for a + bounding box is that it might contain + multiple labels. + + Raises: + ValueError: if boxes is not a [N, 4] array + """ + boxes_shape = boxes.shape + if not boxes_shape: + return + if len(boxes_shape) != 2 or boxes_shape[1] != 4: + raise ValueError('Input must be of size [N, 4]') + for i in range(boxes_shape[0]): + display_str_list = () + if display_str_list_list: + display_str_list = display_str_list_list[i] + draw_bounding_box_on_image(image, boxes[i, 0], boxes[i, 1], boxes[i, 2], + boxes[i, 3], color, thickness, display_str_list) + + +def draw_keypoints_on_image_array(image, + keypoints, + color='red', + radius=2, + use_normalized_coordinates=True): + """Draws keypoints on an image (numpy array). + + Args: + image: a numpy array with shape [height, width, 3]. + keypoints: a numpy array with shape [num_keypoints, 2]. + color: color to draw the keypoints with. Default is red. + radius: keypoint radius. Default value is 2. + use_normalized_coordinates: if True (default), treat keypoint values as + relative to the image. Otherwise treat them as absolute. + """ + image_pil = Image.fromarray(np.uint8(image)).convert('RGB') + draw_keypoints_on_image(image_pil, keypoints, color, radius, + use_normalized_coordinates) + np.copyto(image, np.array(image_pil)) + + +def draw_keypoints_on_image(image, + keypoints, + color='red', + radius=2, + use_normalized_coordinates=True): + """Draws keypoints on an image. + + Args: + image: a PIL.Image object. + keypoints: a numpy array with shape [num_keypoints, 2]. + color: color to draw the keypoints with. Default is red. + radius: keypoint radius. Default value is 2. + use_normalized_coordinates: if True (default), treat keypoint values as + relative to the image. Otherwise treat them as absolute. + """ + draw = ImageDraw.Draw(image) + im_width, im_height = image.size + keypoints_x = [k[1] for k in keypoints] + keypoints_y = [k[0] for k in keypoints] + if use_normalized_coordinates: + keypoints_x = tuple([im_width * x for x in keypoints_x]) + keypoints_y = tuple([im_height * y for y in keypoints_y]) + for keypoint_x, keypoint_y in zip(keypoints_x, keypoints_y): + draw.ellipse([(keypoint_x - radius, keypoint_y - radius), + (keypoint_x + radius, keypoint_y + radius)], + outline=color, fill=color) + + +def draw_mask_on_image_array(image, mask, color='red', alpha=0.7): + """Draws mask on an image. + + Args: + image: uint8 numpy array with shape (img_height, img_height, 3) + mask: a float numpy array of shape (img_height, img_height) with + values between 0 and 1 + color: color to draw the keypoints with. Default is red. + alpha: transparency value between 0 and 1. (default: 0.7) + + Raises: + ValueError: On incorrect data type for image or masks. + """ + if image.dtype != np.uint8: + raise ValueError('`image` not of type np.uint8') + if mask.dtype != np.float32: + raise ValueError('`mask` not of type np.float32') + if np.any(np.logical_or(mask > 1.0, mask < 0.0)): + raise ValueError('`mask` elements should be in [0, 1]') + rgb = ImageColor.getrgb(color) + pil_image = Image.fromarray(image) + + solid_color = np.expand_dims( + np.ones_like(mask), axis=2) * np.reshape(list(rgb), [1, 1, 3]) + pil_solid_color = Image.fromarray(np.uint8(solid_color)).convert('RGBA') + pil_mask = Image.fromarray(np.uint8(255.0*alpha*mask)).convert('L') + pil_image = Image.composite(pil_solid_color, pil_image, pil_mask) + np.copyto(image, np.array(pil_image.convert('RGB'))) + + +def visualize_boxes_and_labels_on_image_array(image, + boxes, + classes, + scores, + category_index, + instance_masks=None, + keypoints=None, + use_normalized_coordinates=False, + max_boxes_to_draw=20, + min_score_thresh=.5, + agnostic_mode=False, + line_thickness=4): + """Overlay labeled boxes on an image with formatted scores and label names. + + This function groups boxes that correspond to the same location + and creates a display string for each detection and overlays these + on the image. Note that this function modifies the image array in-place + and does not return anything. + + Args: + image: uint8 numpy array with shape (img_height, img_width, 3) + boxes: a numpy array of shape [N, 4] + classes: a numpy array of shape [N] + scores: a numpy array of shape [N] or None. If scores=None, then + this function assumes that the boxes to be plotted are groundtruth + boxes and plot all boxes as black with no classes or scores. + category_index: a dict containing category dictionaries (each holding + category index `id` and category name `name`) keyed by category indices. + instance_masks: a numpy array of shape [N, image_height, image_width], can + be None + keypoints: a numpy array of shape [N, num_keypoints, 2], can + be None + use_normalized_coordinates: whether boxes is to be interpreted as + normalized coordinates or not. + max_boxes_to_draw: maximum number of boxes to visualize. If None, draw + all boxes. + min_score_thresh: minimum score threshold for a box to be visualized + agnostic_mode: boolean (default: False) controlling whether to evaluate in + class-agnostic mode or not. This mode will display scores but ignore + classes. + line_thickness: integer (default: 4) controlling line width of the boxes. + """ + # Create a display string (and color) for every box location, group any boxes + # that correspond to the same location. + box_to_display_str_map = collections.defaultdict(list) + box_to_color_map = collections.defaultdict(str) + box_to_instance_masks_map = {} + box_to_keypoints_map = collections.defaultdict(list) + if not max_boxes_to_draw: + max_boxes_to_draw = boxes.shape[0] + for i in range(min(max_boxes_to_draw, boxes.shape[0])): + if scores is None or scores[i] > min_score_thresh: + box = tuple(boxes[i].tolist()) + if instance_masks is not None: + box_to_instance_masks_map[box] = instance_masks[i] + if keypoints is not None: + box_to_keypoints_map[box].extend(keypoints[i]) + if scores is None: + box_to_color_map[box] = 'black' + else: + if not agnostic_mode: + if classes[i] in category_index.keys(): + class_name = category_index[classes[i]]['name'] + else: + class_name = 'N/A' + display_str = '{}: {}%'.format( + class_name, + int(100*scores[i])) + else: + display_str = 'score: {}%'.format(int(100 * scores[i])) + box_to_display_str_map[box].append(display_str) + if agnostic_mode: + box_to_color_map[box] = 'DarkOrange' + else: + box_to_color_map[box] = STANDARD_COLORS[ + classes[i] % len(STANDARD_COLORS)] + + # Draw all boxes onto image. + for box, color in box_to_color_map.iteritems(): + ymin, xmin, ymax, xmax = box + if instance_masks is not None: + draw_mask_on_image_array( + image, + box_to_instance_masks_map[box], + color=color + ) + draw_bounding_box_on_image_array( + image, + ymin, + xmin, + ymax, + xmax, + color=color, + thickness=line_thickness, + display_str_list=box_to_display_str_map[box], + use_normalized_coordinates=use_normalized_coordinates) + if keypoints is not None: + draw_keypoints_on_image_array( + image, + box_to_keypoints_map[box], + color=color, + radius=line_thickness / 2, + use_normalized_coordinates=use_normalized_coordinates) diff --git a/object_detection/utils/visualization_utils_test.py b/object_detection/utils/visualization_utils_test.py new file mode 100644 index 000000000..809d5f068 --- /dev/null +++ b/object_detection/utils/visualization_utils_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 image.understanding.object_detection.core.visualization_utils. + +Testing with visualization in the following colab: +https://drive.google.com/a/google.com/file/d/0B5HnKS_hMsNARERpU3MtU3I5RFE/view?usp=sharing + +""" + + +import numpy as np +import PIL.Image as Image +import tensorflow as tf + +from object_detection.utils import visualization_utils + + +class VisualizationUtilsTest(tf.test.TestCase): + + def create_colorful_test_image(self): + """This function creates an image that can be used to test vis functions. + + It makes an image composed of four colored rectangles. + + Returns: + colorful test numpy array image. + """ + ch255 = np.full([100, 200, 1], 255, dtype=np.uint8) + ch128 = np.full([100, 200, 1], 128, dtype=np.uint8) + ch0 = np.full([100, 200, 1], 0, dtype=np.uint8) + imr = np.concatenate((ch255, ch128, ch128), axis=2) + img = np.concatenate((ch255, ch255, ch0), axis=2) + imb = np.concatenate((ch255, ch0, ch255), axis=2) + imw = np.concatenate((ch128, ch128, ch128), axis=2) + imu = np.concatenate((imr, img), axis=1) + imd = np.concatenate((imb, imw), axis=1) + image = np.concatenate((imu, imd), axis=0) + return image + + def test_draw_bounding_box_on_image(self): + test_image = self.create_colorful_test_image() + test_image = Image.fromarray(test_image) + width_original, height_original = test_image.size + ymin = 0.25 + ymax = 0.75 + xmin = 0.4 + xmax = 0.6 + + visualization_utils.draw_bounding_box_on_image(test_image, ymin, xmin, ymax, + xmax) + width_final, height_final = test_image.size + + self.assertEqual(width_original, width_final) + self.assertEqual(height_original, height_final) + + def test_draw_bounding_box_on_image_array(self): + test_image = self.create_colorful_test_image() + width_original = test_image.shape[0] + height_original = test_image.shape[1] + ymin = 0.25 + ymax = 0.75 + xmin = 0.4 + xmax = 0.6 + + visualization_utils.draw_bounding_box_on_image_array( + test_image, ymin, xmin, ymax, xmax) + width_final = test_image.shape[0] + height_final = test_image.shape[1] + + self.assertEqual(width_original, width_final) + self.assertEqual(height_original, height_final) + + def test_draw_bounding_boxes_on_image(self): + test_image = self.create_colorful_test_image() + test_image = Image.fromarray(test_image) + width_original, height_original = test_image.size + boxes = np.array([[0.25, 0.75, 0.4, 0.6], + [0.1, 0.1, 0.9, 0.9]]) + + visualization_utils.draw_bounding_boxes_on_image(test_image, boxes) + width_final, height_final = test_image.size + + self.assertEqual(width_original, width_final) + self.assertEqual(height_original, height_final) + + def test_draw_bounding_boxes_on_image_array(self): + test_image = self.create_colorful_test_image() + width_original = test_image.shape[0] + height_original = test_image.shape[1] + boxes = np.array([[0.25, 0.75, 0.4, 0.6], + [0.1, 0.1, 0.9, 0.9]]) + + visualization_utils.draw_bounding_boxes_on_image_array(test_image, boxes) + width_final = test_image.shape[0] + height_final = test_image.shape[1] + + self.assertEqual(width_original, width_final) + self.assertEqual(height_original, height_final) + + def test_draw_keypoints_on_image(self): + test_image = self.create_colorful_test_image() + test_image = Image.fromarray(test_image) + width_original, height_original = test_image.size + keypoints = [[0.25, 0.75], [0.4, 0.6], [0.1, 0.1], [0.9, 0.9]] + + visualization_utils.draw_keypoints_on_image(test_image, keypoints) + width_final, height_final = test_image.size + + self.assertEqual(width_original, width_final) + self.assertEqual(height_original, height_final) + + def test_draw_keypoints_on_image_array(self): + test_image = self.create_colorful_test_image() + width_original = test_image.shape[0] + height_original = test_image.shape[1] + keypoints = [[0.25, 0.75], [0.4, 0.6], [0.1, 0.1], [0.9, 0.9]] + + visualization_utils.draw_keypoints_on_image_array(test_image, keypoints) + width_final = test_image.shape[0] + height_final = test_image.shape[1] + + self.assertEqual(width_original, width_final) + self.assertEqual(height_original, height_final) + + def test_draw_mask_on_image_array(self): + test_image = np.asarray([[[0, 0, 0], [0, 0, 0]], + [[0, 0, 0], [0, 0, 0]]], dtype=np.uint8) + mask = np.asarray([[0.0, 1.0], + [1.0, 1.0]], dtype=np.float32) + expected_result = np.asarray([[[0, 0, 0], [0, 0, 127]], + [[0, 0, 127], [0, 0, 127]]], dtype=np.uint8) + visualization_utils.draw_mask_on_image_array(test_image, mask, + color='Blue', alpha=.5) + self.assertAllEqual(test_image, expected_result) + + +if __name__ == '__main__': + tf.test.main() diff --git a/setup.py b/setup.py new file mode 100644 index 000000000..2ea981219 --- /dev/null +++ b/setup.py @@ -0,0 +1,16 @@ +"""Setup script for object_detection.""" + +from setuptools import find_packages +from setuptools import setup + + +REQUIRED_PACKAGES = ['Pillow>=1.0'] + +setup( + name='object_detection', + version='0.1', + install_requires=REQUIRED_PACKAGES, + include_package_data=True, + packages=[p for p in find_packages() if p.startswith('object_detection')], + description='Tensorflow Object Detection Library', +) diff --git a/slim/setup.py b/slim/setup.py new file mode 100644 index 000000000..4262a4ee3 --- /dev/null +++ b/slim/setup.py @@ -0,0 +1,13 @@ +"""Setup script for slim.""" + +from setuptools import find_packages +from setuptools import setup + + +setup( + name='slim', + version='0.1', + include_package_data=True, + packages=find_packages(), + description='tf-slim', +) -- GitLab From b0a13bfac73b4c5e9bfd190b18010d8a07b91e1a Mon Sep 17 00:00:00 2001 From: derekjchow Date: Thu, 15 Jun 2017 08:03:01 -0700 Subject: [PATCH 047/110] Clean up documentation. (#1563) --- object_detection/README.md | 3 +-- object_detection/g3doc/detection_model_zoo.md | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/object_detection/README.md b/object_detection/README.md index 833f94cec..eaf13817b 100644 --- a/object_detection/README.md +++ b/object_detection/README.md @@ -34,7 +34,7 @@ https://scholar.googleusercontent.com/scholar.bib?q=info:l291WsrB-hQJ:scholar.go Quick Start: * Quick Start: Jupyter notebook for off-the-shelf inference
    -* Quick Start: Training on a pet detector
    +* Quick Start: Training a pet detector
    Setup: * Installation
    @@ -66,7 +66,6 @@ release includes: * Region-Based Fully Convolutional Networks (R-FCN) with Resnet 101, * Faster RCNN with Resnet 101, * Faster RCNN with Inception Resnet v2 - * Mask R-CNN with Resnet 101. * Frozen weights (trained on the COCO dataset) for each of the above models to be used for out-of-the-box inference purposes. * A [Jupyter notebook](object_detection_tutorial.ipynb) for performing diff --git a/object_detection/g3doc/detection_model_zoo.md b/object_detection/g3doc/detection_model_zoo.md index da2f8e146..9ff1ad9bd 100644 --- a/object_detection/g3doc/detection_model_zoo.md +++ b/object_detection/g3doc/detection_model_zoo.md @@ -16,7 +16,7 @@ In the table below, we list each such pre-trained model including: * detector performance on COCO data as measured by the COCO mAP measure. Here, higher is better, and we only report bounding box mAP rounded to the nearest integer. -* Output types (currently only `Boxes` or `Boxes, Masks`) +* Output types (currently only `Boxes`) You can un-tar each tar.gz file via, e.g.,: @@ -40,4 +40,3 @@ Inside the un-tar'ed directory, you will find: | [rfcn_resnet101_coco](http://download.tensorflow.org/models/object_detection/rfcn_resnet101_coco_11_06_2017.tar.gz) | medium | 30 | Boxes | | [faster_rcnn_resnet101_coco](http://download.tensorflow.org/models/object_detection/faster_rcnn_resnet101_coco_11_06_2017.tar.gz) | medium | 32 | Boxes | | [faster_rcnn_inception_resnet_v2_atrous_coco](http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_resnet_v2_atrous_coco_11_06_2017.tar.gz) | slow | 37 | Boxes | -| [mask_rcnn_resnet101_coco](http://download.tensorflow.org/models/object_detection/) | medium | | Boxes, Masks | -- GitLab From 4b31d8fee78729aa2964c9201b0cd344eba6be8d Mon Sep 17 00:00:00 2001 From: Derek Chow Date: Thu, 15 Jun 2017 10:26:59 -0700 Subject: [PATCH 048/110] Reduce batchsize from 32->24 for SSD configs. --- object_detection/samples/configs/ssd_inception_v2_pets.config | 2 +- object_detection/samples/configs/ssd_mobilenet_v1_pets.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/object_detection/samples/configs/ssd_inception_v2_pets.config b/object_detection/samples/configs/ssd_inception_v2_pets.config index 801701ed5..49bdf7e06 100644 --- a/object_detection/samples/configs/ssd_inception_v2_pets.config +++ b/object_detection/samples/configs/ssd_inception_v2_pets.config @@ -134,7 +134,7 @@ model { } train_config: { - batch_size: 32 + batch_size: 24 optimizer { rms_prop_optimizer: { learning_rate: { diff --git a/object_detection/samples/configs/ssd_mobilenet_v1_pets.config b/object_detection/samples/configs/ssd_mobilenet_v1_pets.config index e8b0516f2..c8d83ddb1 100644 --- a/object_detection/samples/configs/ssd_mobilenet_v1_pets.config +++ b/object_detection/samples/configs/ssd_mobilenet_v1_pets.config @@ -140,7 +140,7 @@ model { } train_config: { - batch_size: 32 + batch_size: 24 optimizer { rms_prop_optimizer: { learning_rate: { -- GitLab From 2564541d94d1e8ecc10dffc5788fdaf0a675e0d3 Mon Sep 17 00:00:00 2001 From: Nick Johnston Date: Thu, 15 Jun 2017 12:43:59 -0700 Subject: [PATCH 049/110] Update username and add new line in READMEs. --- compression/README.md | 3 ++- compression/image_encoder/README.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compression/README.md b/compression/README.md index 2ae52f6fc..4406b268f 100644 --- a/compression/README.md +++ b/compression/README.md @@ -8,7 +8,8 @@ code for the following papers: ## Organization [Image Encoder](image_encoder/): Encoding and decoding images into their binary representation. + [Entropy Coder](entropy_coder/): Lossless compression of the binary representation. ## Contact Info -Model repository maintained by Nick Johnston ([nickj-google](https://github.com/nickj-google)). +Model repository maintained by Nick Johnston ([nmjohn](https://github.com/nmjohn)). diff --git a/compression/image_encoder/README.md b/compression/image_encoder/README.md index 916820e20..a47da977a 100644 --- a/compression/image_encoder/README.md +++ b/compression/image_encoder/README.md @@ -102,4 +102,4 @@ pixel boundaries. ## Contact Info -Model repository maintained by Nick Johnston ([nickj-google](https://github.com/nickj-google)). +Model repository maintained by Nick Johnston ([nmjohn](https://github.com/nmjohn)). -- GitLab From 11733fcafdb148878052c47dda0e4b9e76736700 Mon Sep 17 00:00:00 2001 From: Neal Wu Date: Thu, 15 Jun 2017 14:45:31 -0700 Subject: [PATCH 050/110] Change depth_radius from 5 to 2 --- tutorials/image/alexnet/alexnet_benchmark.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/image/alexnet/alexnet_benchmark.py b/tutorials/image/alexnet/alexnet_benchmark.py index 047ac3dce..39fcb109f 100644 --- a/tutorials/image/alexnet/alexnet_benchmark.py +++ b/tutorials/image/alexnet/alexnet_benchmark.py @@ -78,7 +78,7 @@ def inference(images): lrn1 = tf.nn.local_response_normalization(conv1, alpha=1e-4, beta=0.75, - depth_radius=5, + depth_radius=2, bias=2.0) # pool1 @@ -106,7 +106,7 @@ def inference(images): lrn2 = tf.nn.local_response_normalization(conv2, alpha=1e-4, beta=0.75, - depth_radius=5, + depth_radius=2, bias=2.0) # pool2 -- GitLab From 0f1bd993f1be399ce9fb849580ade94d56943c6d Mon Sep 17 00:00:00 2001 From: Neal Wu Date: Thu, 15 Jun 2017 16:37:39 -0700 Subject: [PATCH 051/110] Add object_detection to the main README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 50f5cebd3..f0f203855 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ running TensorFlow 0.12 or earlier, please - [neural_gpu](neural_gpu): highly parallel neural computer. - [neural_programmer](neural_programmer): neural network augmented with logic and mathematic operations. - [next_frame_prediction](next_frame_prediction): probabilistic future frame synthesis via cross convolutional networks. +- [object_detection](object_detection): localizing and identifying multiple objects in a single image. - [real_nvp](real_nvp): density estimation using real-valued non-volume preserving (real NVP) transformations. - [resnet](resnet): deep and wide residual networks. - [skip_thoughts](skip_thoughts): recurrent neural network sentence-to-vector encoder. -- GitLab From a7dd1e29e783bd11faae72e05f98bce4ab8a80ae Mon Sep 17 00:00:00 2001 From: Andrew Selle Date: Fri, 16 Jun 2017 09:10:45 -0700 Subject: [PATCH 052/110] Update cifar10_input.py --- tutorials/image/cifar10/cifar10_input.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tutorials/image/cifar10/cifar10_input.py b/tutorials/image/cifar10/cifar10_input.py index 10c77623d..323f2f113 100644 --- a/tutorials/image/cifar10/cifar10_input.py +++ b/tutorials/image/cifar10/cifar10_input.py @@ -175,6 +175,8 @@ def distorted_inputs(data_dir, batch_size): # Because these operations are not commutative, consider randomizing # the order their operation. + # NOTE: since per_image_standardization zeros the mean and makes + # the stddev unit, this likely has no effect see tensorflow#1458. distorted_image = tf.image.random_brightness(distorted_image, max_delta=63) distorted_image = tf.image.random_contrast(distorted_image, -- GitLab From c9f2ae14b40f043cf1f8d60a259e2c19832c7e1c Mon Sep 17 00:00:00 2001 From: derekjchow Date: Fri, 16 Jun 2017 10:49:51 -0700 Subject: [PATCH 053/110] Download model in Jupyter Notebook. (#1580) --- .../object_detection_tutorial.ipynb | 66 ++++++++++++++++--- object_detection/utils/visualization_utils.py | 2 +- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/object_detection/object_detection_tutorial.ipynb b/object_detection/object_detection_tutorial.ipynb index 331e210cf..80a1e6b0f 100644 --- a/object_detection/object_detection_tutorial.ipynb +++ b/object_detection/object_detection_tutorial.ipynb @@ -26,8 +26,11 @@ "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", @@ -89,7 +92,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Variables" + "## Variables\n", + "\n", + "See the [detection model zoo](g3doc/detection_model_zoo.md) for a list of all models to try." ] }, { @@ -100,8 +105,13 @@ }, "outputs": [], "source": [ + "# What model to download.\n", + "MODEL_NAME = 'ssd_mobilenet_v1_coco_11_06_2017'\n", + "MODEL_FILE = MODEL_NAME + '.tar.gz'\n", + "DOWNLOAD_BASE = 'http://download.tensorflow.org/models/object_detection/'\n", + "\n", "# Path to frozen detection graph. This is the actual model that is used for the object detection.\n", - "PATH_TO_CKPT = os.path.join('test_ckpt', 'ssd_inception_v2.pb')\n", + "PATH_TO_CKPT = MODEL_NAME + '/frozen_inference_graph.pb'\n", "\n", "# List of the strings that is used to add correct label for each box.\n", "PATH_TO_LABELS = os.path.join('data', 'mscoco_label_map.pbtxt')\n", @@ -113,13 +123,39 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Load a (frozen) Tensorflow model into memory." + "## Download Model" ] }, { "cell_type": "code", "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "opener = urllib.request.URLopener()\n", + "opener.retrieve(DOWNLOAD_BASE + MODEL_FILE, MODEL_FILE)\n", + "tar_file = tarfile.open(MODEL_FILE)\n", + "for file in tar_file.getmembers():\n", + " file_name = os.path.basename(file.name)\n", + " if 'frozen_inference_graph.pb' in file_name:\n", + " tar_file.extract(file, os.getcwd())" + ] + }, + { + "cell_type": "markdown", "metadata": {}, + "source": [ + "## Load a (frozen) Tensorflow model into memory." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "detection_graph = tf.Graph()\n", @@ -142,7 +178,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "label_map = label_map_util.load_labelmap(PATH_TO_LABELS)\n", @@ -201,6 +239,7 @@ "cell_type": "code", "execution_count": null, "metadata": { + "collapsed": true, "scrolled": true }, "outputs": [], @@ -237,25 +276,34 @@ " plt.figure(figsize=IMAGE_SIZE)\n", " plt.imshow(image_np)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 2", "language": "python", - "name": "python3" + "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 3 + "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.4.3" + "pygments_lexer": "ipython2", + "version": "2.7.13" } }, "nbformat": 4, diff --git a/object_detection/utils/visualization_utils.py b/object_detection/utils/visualization_utils.py index 23eed7b23..76d952458 100644 --- a/object_detection/utils/visualization_utils.py +++ b/object_detection/utils/visualization_utils.py @@ -395,7 +395,7 @@ def visualize_boxes_and_labels_on_image_array(image, classes[i] % len(STANDARD_COLORS)] # Draw all boxes onto image. - for box, color in box_to_color_map.iteritems(): + for box, color in six.iteritems(box_to_color_map): ymin, xmin, ymax, xmax = box if instance_masks is not None: draw_mask_on_image_array( -- GitLab From 329e7fa627f5ac1283c8890bfab2ef82b9d98ce8 Mon Sep 17 00:00:00 2001 From: Matthias Winkelmann Date: Fri, 16 Jun 2017 21:25:29 +0200 Subject: [PATCH 054/110] Fixed broken link to mscoco.org Thanks for the good work! --- object_detection/g3doc/detection_model_zoo.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_detection/g3doc/detection_model_zoo.md b/object_detection/g3doc/detection_model_zoo.md index 9ff1ad9bd..ba656bae6 100644 --- a/object_detection/g3doc/detection_model_zoo.md +++ b/object_detection/g3doc/detection_model_zoo.md @@ -1,7 +1,7 @@ # Tensorflow detection model zoo We provide a collection of detection models pre-trained on the -[COCO dataset](mscoco.org). +[COCO dataset](http://mscoco.org). These models can be useful for out-of-the-box inference if you are interested in categories already in COCO (e.g., humans, cars, etc). They are also useful for initializing your models when training on novel -- GitLab From cd11d77dd0d71ab70949ab1d81a480572cafb587 Mon Sep 17 00:00:00 2001 From: Neal Wu Date: Fri, 16 Jun 2017 13:32:49 -0700 Subject: [PATCH 055/110] Remove the erroneous comment from resnet_v2.py again --- slim/nets/resnet_v2.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/slim/nets/resnet_v2.py b/slim/nets/resnet_v2.py index e8e798345..f5af10334 100644 --- a/slim/nets/resnet_v2.py +++ b/slim/nets/resnet_v2.py @@ -25,8 +25,6 @@ introduced by: The key difference of the full preactivation 'v2' variant compared to the 'v1' variant in [1] is the use of batch normalization before every weight layer. -Another difference is that 'v2' ResNets do not include an activation function in -the main pathway. Also see [2; Fig. 4e]. Typical use: -- GitLab From 056965d53401e07b3f3d898f81830b3c5ccf9478 Mon Sep 17 00:00:00 2001 From: Dheera Venkatraman Date: Fri, 16 Jun 2017 14:45:14 -0700 Subject: [PATCH 056/110] URL returns 404 Fix URL --- object_detection/g3doc/detection_model_zoo.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_detection/g3doc/detection_model_zoo.md b/object_detection/g3doc/detection_model_zoo.md index 9ff1ad9bd..ba656bae6 100644 --- a/object_detection/g3doc/detection_model_zoo.md +++ b/object_detection/g3doc/detection_model_zoo.md @@ -1,7 +1,7 @@ # Tensorflow detection model zoo We provide a collection of detection models pre-trained on the -[COCO dataset](mscoco.org). +[COCO dataset](http://mscoco.org). These models can be useful for out-of-the-box inference if you are interested in categories already in COCO (e.g., humans, cars, etc). They are also useful for initializing your models when training on novel -- GitLab From e76190e83e773875ddfae38339cd299b64e7eda8 Mon Sep 17 00:00:00 2001 From: derekjchow Date: Fri, 16 Jun 2017 14:59:34 -0700 Subject: [PATCH 057/110] Use spatial_squeeze=False for ResNet feature extractors. (#1586) Fixes #1585 --- .../models/faster_rcnn_resnet_v1_feature_extractor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/object_detection/models/faster_rcnn_resnet_v1_feature_extractor.py b/object_detection/models/faster_rcnn_resnet_v1_feature_extractor.py index d71c62453..ff443ac6d 100644 --- a/object_detection/models/faster_rcnn_resnet_v1_feature_extractor.py +++ b/object_detection/models/faster_rcnn_resnet_v1_feature_extractor.py @@ -122,6 +122,7 @@ class FasterRCNNResnetV1FeatureExtractor( is_training=False, global_pool=False, output_stride=self._first_stage_features_stride, + spatial_squeeze=False, scope=var_scope) handle = scope + '/%s/block3' % self._architecture -- GitLab From ae2c506ee6553183ff641e48aa4d1dfb1a6ac6ae Mon Sep 17 00:00:00 2001 From: Jonathan Huang Date: Fri, 16 Jun 2017 15:25:10 -0700 Subject: [PATCH 058/110] Change visualizer font and jupyter notebook line thickness (#1589) --- object_detection/object_detection_tutorial.ipynb | 10 ++++++---- object_detection/utils/visualization_utils.py | 5 ++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/object_detection/object_detection_tutorial.ipynb b/object_detection/object_detection_tutorial.ipynb index 80a1e6b0f..31e189916 100644 --- a/object_detection/object_detection_tutorial.ipynb +++ b/object_detection/object_detection_tutorial.ipynb @@ -94,7 +94,9 @@ "source": [ "## Variables\n", "\n", - "See the [detection model zoo](g3doc/detection_model_zoo.md) for a list of all models to try." + "Any model exported using the `export_inference_graph.py` tool can be loaded here simply by changing `PATH_TO_CKPT` to point to a new .pb file. \n", + "\n", + "By default we use an \"SSD with Mobilenet\" model here. See the [detection model zoo](g3doc/detection_model_zoo.md) for a list of other models that can be run out-of-the-box with varying speeds and accuracies." ] }, { @@ -239,7 +241,6 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": true, "scrolled": true }, "outputs": [], @@ -272,7 +273,8 @@ " np.squeeze(classes).astype(np.int32),\n", " np.squeeze(scores),\n", " category_index,\n", - " use_normalized_coordinates=True)\n", + " use_normalized_coordinates=True,\n", + " line_thickness=8)\n", " plt.figure(figsize=IMAGE_SIZE)\n", " plt.imshow(image_np)" ] @@ -303,7 +305,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.13" + "version": "2.7.12" } }, "nbformat": 4, diff --git a/object_detection/utils/visualization_utils.py b/object_detection/utils/visualization_utils.py index 76d952458..41d80db6a 100644 --- a/object_detection/utils/visualization_utils.py +++ b/object_detection/utils/visualization_utils.py @@ -156,7 +156,10 @@ def draw_bounding_box_on_image(image, (left, right, top, bottom) = (xmin, xmax, ymin, ymax) draw.line([(left, top), (left, bottom), (right, bottom), (right, top), (left, top)], width=thickness, fill=color) - font = ImageFont.load_default() + try: + font = ImageFont.truetype('arial.ttf', 24) + except IOError: + font = ImageFont.load_default() text_bottom = top # Reverse list and print from bottom to top. -- GitLab From 8c8fc0c2f42e1b849157b904da7c22fa4f672fc1 Mon Sep 17 00:00:00 2001 From: Vivek Rathod Date: Fri, 16 Jun 2017 16:14:50 -0700 Subject: [PATCH 059/110] Change DEFINE_enum to DEFINE_string and delete unused file. --- object_detection/create_pascal_tf_record.py | 15 ++++-- object_detection/object_detection.blueprint | 56 --------------------- 2 files changed, 11 insertions(+), 60 deletions(-) delete mode 100644 object_detection/object_detection.blueprint diff --git a/object_detection/create_pascal_tf_record.py b/object_detection/create_pascal_tf_record.py index b25980ece..443862f1d 100644 --- a/object_detection/create_pascal_tf_record.py +++ b/object_detection/create_pascal_tf_record.py @@ -39,12 +39,11 @@ 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_enum('set', 'train', ['train', 'val', 'trainval', 'test'], - 'Convert training set, validation set or merged set.') +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_enum('year', 'VOC2007', ['VOC2007', 'VOC2012', 'merged'], - 'Desired challenge year.') +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') @@ -52,6 +51,9 @@ 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, @@ -139,6 +141,11 @@ def dict_to_tf_example(data, 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': diff --git a/object_detection/object_detection.blueprint b/object_detection/object_detection.blueprint deleted file mode 100644 index 9d6bbffea..000000000 --- a/object_detection/object_detection.blueprint +++ /dev/null @@ -1,56 +0,0 @@ -include "devtools/blueprint/ncl/blueprint_file.ncl"; -include "releasetools/rapid/ncl/rapid_config.ncl"; - -blueprint_file = ::blueprint::BlueprintFile( - project_name = "open_tf_object_detection", - project_grouping = ["Search", "Search Features", "Image Search", "Visual Search"], - mdb_groups = ["vale-project"], - - tech_lead = ["jonathanhuang", "kpmurphy"], - - dev_mailing_list = "object-detection-reviews@google.com", - - buganizer_component_ids = [163596], - - owned_code_depotpaths = [ - "//depot/google3/third_party/tensorflow_models/object_detection/...", - ], - buildable_units = [ - ::blueprint::BuildableUnit( - name = "open_tf_object_detection.fastbuild", - enable_continuous_build = true, - enable_release = false, - continuous_build_email = ::blueprint::ContinuousBuildEmailInfo( - build_cop_email_addrs = ["vale-project+tap@google.com"]), - build_patterns = [ - "third_party/tensorflow_models/object_detection/...", - ], - build_flags = [ - "--compilation_mode=fastbuild", - ], - test_patterns = [ - "third_party/tensorflow_models/object_detection/...", - ], - enable_coverage = true, - - ), - ::blueprint::BuildableUnit( - name = "open_tf_object_detection.opt", - enable_continuous_build = true, - enable_release = false, - continuous_build_email = ::blueprint::ContinuousBuildEmailInfo( - build_cop_email_addrs = ["vale-project+tap@google.com"]), - build_patterns = [ - "third_party/tensorflow_models/object_detection/...", - "image/understanding/object_detection/...", - ], - build_flags = [ - "--compilation_mode=opt", - ], - test_patterns = [ - "third_party/tensorflow_models/object_detection/...", - "image/understanding/object_detection/...", - ], - ), - ], -); -- GitLab From 8d91ce765a81ac596e6f8781bbdc75a0f419a65d Mon Sep 17 00:00:00 2001 From: vivek rathod Date: Fri, 16 Jun 2017 16:29:47 -0700 Subject: [PATCH 060/110] Change DEFINE_enum to DEFINE_string and delete unused file. (#1590) --- object_detection/create_pascal_tf_record.py | 15 ++++-- object_detection/object_detection.blueprint | 56 --------------------- 2 files changed, 11 insertions(+), 60 deletions(-) delete mode 100644 object_detection/object_detection.blueprint diff --git a/object_detection/create_pascal_tf_record.py b/object_detection/create_pascal_tf_record.py index b25980ece..443862f1d 100644 --- a/object_detection/create_pascal_tf_record.py +++ b/object_detection/create_pascal_tf_record.py @@ -39,12 +39,11 @@ 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_enum('set', 'train', ['train', 'val', 'trainval', 'test'], - 'Convert training set, validation set or merged set.') +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_enum('year', 'VOC2007', ['VOC2007', 'VOC2012', 'merged'], - 'Desired challenge year.') +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') @@ -52,6 +51,9 @@ 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, @@ -139,6 +141,11 @@ def dict_to_tf_example(data, 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': diff --git a/object_detection/object_detection.blueprint b/object_detection/object_detection.blueprint deleted file mode 100644 index 9d6bbffea..000000000 --- a/object_detection/object_detection.blueprint +++ /dev/null @@ -1,56 +0,0 @@ -include "devtools/blueprint/ncl/blueprint_file.ncl"; -include "releasetools/rapid/ncl/rapid_config.ncl"; - -blueprint_file = ::blueprint::BlueprintFile( - project_name = "open_tf_object_detection", - project_grouping = ["Search", "Search Features", "Image Search", "Visual Search"], - mdb_groups = ["vale-project"], - - tech_lead = ["jonathanhuang", "kpmurphy"], - - dev_mailing_list = "object-detection-reviews@google.com", - - buganizer_component_ids = [163596], - - owned_code_depotpaths = [ - "//depot/google3/third_party/tensorflow_models/object_detection/...", - ], - buildable_units = [ - ::blueprint::BuildableUnit( - name = "open_tf_object_detection.fastbuild", - enable_continuous_build = true, - enable_release = false, - continuous_build_email = ::blueprint::ContinuousBuildEmailInfo( - build_cop_email_addrs = ["vale-project+tap@google.com"]), - build_patterns = [ - "third_party/tensorflow_models/object_detection/...", - ], - build_flags = [ - "--compilation_mode=fastbuild", - ], - test_patterns = [ - "third_party/tensorflow_models/object_detection/...", - ], - enable_coverage = true, - - ), - ::blueprint::BuildableUnit( - name = "open_tf_object_detection.opt", - enable_continuous_build = true, - enable_release = false, - continuous_build_email = ::blueprint::ContinuousBuildEmailInfo( - build_cop_email_addrs = ["vale-project+tap@google.com"]), - build_patterns = [ - "third_party/tensorflow_models/object_detection/...", - "image/understanding/object_detection/...", - ], - build_flags = [ - "--compilation_mode=opt", - ], - test_patterns = [ - "third_party/tensorflow_models/object_detection/...", - "image/understanding/object_detection/...", - ], - ), - ], -); -- GitLab From 1b2c67af8a035fa90dc1dc507cdd101df3f5a589 Mon Sep 17 00:00:00 2001 From: Duc Nguyen Date: Sat, 17 Jun 2017 10:14:50 +0900 Subject: [PATCH 061/110] Fix compatibility for model_builder_test.py (#1571) iteritems -> items --- object_detection/builders/model_builder_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/object_detection/builders/model_builder_test.py b/object_detection/builders/model_builder_test.py index 513c5fab3..28b15e650 100644 --- a/object_detection/builders/model_builder_test.py +++ b/object_detection/builders/model_builder_test.py @@ -255,7 +255,7 @@ class ModelBuilderTest(tf.test.TestCase): }""" model_proto = model_pb2.DetectionModel() text_format.Merge(model_text_proto, model_proto) - for extractor_type, extractor_class in FEATURE_EXTRACTOR_MAPS.iteritems(): + for extractor_type, extractor_class in FEATURE_EXTRACTOR_MAPS.items(): model_proto.faster_rcnn.feature_extractor.type = extractor_type model = model_builder.build(model_proto, is_training=True) self.assertIsInstance(model, faster_rcnn_meta_arch.FasterRCNNMetaArch) @@ -445,7 +445,7 @@ class ModelBuilderTest(tf.test.TestCase): }""" model_proto = model_pb2.DetectionModel() text_format.Merge(model_text_proto, model_proto) - for extractor_type, extractor_class in FEATURE_EXTRACTOR_MAPS.iteritems(): + for extractor_type, extractor_class in FEATURE_EXTRACTOR_MAPS.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) -- GitLab From 9b1877049eeba44dad1cdabc8233eebdd9d66a00 Mon Sep 17 00:00:00 2001 From: Duc Nguyen Date: Sat, 17 Jun 2017 22:24:27 +0900 Subject: [PATCH 062/110] Change key of type 'tuple' to 'str' dictionary having both 'tuple' and 'str' keys cannot be 'sorted' --- object_detection/core/batcher.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/object_detection/core/batcher.py b/object_detection/core/batcher.py index fdd698c43..7b9d040ab 100644 --- a/object_detection/core/batcher.py +++ b/object_detection/core/batcher.py @@ -81,7 +81,7 @@ class BatchQueue(object): {key: tensor.get_shape() for key, tensor in tensor_dict.iteritems()}) # Remember runtime shapes to unpad tensors after batching. runtime_shapes = collections.OrderedDict( - {(key, 'runtime_shapes'): tf.shape(tensor) + {(key + '_runtime_shapes'): tf.shape(tensor) for key, tensor in tensor_dict.iteritems()}) all_tensors = tensor_dict all_tensors.update(runtime_shapes) @@ -112,8 +112,8 @@ class BatchQueue(object): for key, batched_tensor in batched_tensors.iteritems(): unbatched_tensor_list = tf.unstack(batched_tensor) for i, unbatched_tensor in enumerate(unbatched_tensor_list): - if isinstance(key, tuple) and key[1] == 'runtime_shapes': - shapes[(key[0], i)] = unbatched_tensor + if '_runtime_shapes' in key: + shapes[(key[:-15], i)] = unbatched_tensor else: tensors[(key, i)] = unbatched_tensor -- GitLab From 0f1e7911f0bc5a2f91a9d8b9d45c1fbc75a27841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E7=92=9E?= Date: Tue, 20 Jun 2017 01:16:53 +0800 Subject: [PATCH 063/110] Make slim_walkthrough ipython notebook python3 compatible (#1612) * make slim_walkthrough ipython notebook python3 compatible * make slim_walkthrough ipython notebook python3 compatible --- slim/slim_walkthrough.ipynb | 114 ++++++++++++++++++++++-------------- 1 file changed, 69 insertions(+), 45 deletions(-) diff --git a/slim/slim_walkthrough.ipynb b/slim/slim_walkthrough.ipynb index da868ef19..dff43e03b 100644 --- a/slim/slim_walkthrough.ipynb +++ b/slim/slim_walkthrough.ipynb @@ -29,11 +29,14 @@ "## Installation and setup\n", "\n", "\n", - "As of 8/28/16, the latest stable release of TF is r0.10, which does not contain the latest version of slim.\n", - "To obtain the latest version of TF-Slim, please install the most recent nightly build of TF\n", - "as explained [here](https://github.com/tensorflow/models/tree/master/slim#installing-latest-version-of-tf-slim).\n", + "Since the stable release of TF 1.0, the latest version of slim has been available as `tf.contrib.slim`.\n", + "To test that your installation is working, execute the following command; it should run without raising any errors.\n", "\n", - "To use TF-Slim for image classification (as we do in this notebook), you also have to install the TF-Slim image models library from [here](https://github.com/tensorflow/models/tree/master/slim). Let's suppose you install this into a directory called TF_MODELS. Then you should change directory to TF_MODELS/slim **before** running this notebook, so that these files are in your python path.\n", + "```\n", + "python -c \"import tensorflow.contrib.slim as slim; eval = slim.evaluation.evaluate_once\"\n", + "```\n", + "\n", + "Although, to use TF-Slim for image classification (as we do in this notebook), you also have to install the TF-Slim image models library from [here](https://github.com/tensorflow/models/tree/master/slim). Let's suppose you install this into a directory called TF_MODELS. Then you should change directory to TF_MODELS/slim **before** running this notebook, so that these files are in your python path.\n", "\n", "To check you've got these two steps to work, just execute the cell below. If it complains about unknown modules, restart the notebook after moving to the TF-Slim models directory.\n" ] @@ -42,10 +45,14 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ + "from __future__ import absolute_import\n", + "from __future__ import division\n", + "from __future__ import print_function\n", + "\n", "import matplotlib\n", "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", @@ -57,7 +64,7 @@ "from datasets import dataset_utils\n", "\n", "# Main slim library\n", - "slim = tf.contrib.slim" + "from tensorflow.contrib import slim" ] }, { @@ -143,7 +150,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -156,15 +163,15 @@ " predictions, end_points = regression_model(inputs)\n", "\n", " # Print name and shape of each tensor.\n", - " print \"Layers\"\n", + " print(\"Layers\")\n", " for k, v in end_points.items():\n", - " print 'name = {}, shape = {}'.format(v.name, v.get_shape())\n", + " print('name = {}, shape = {}'.format(v.name, v.get_shape()))\n", "\n", " # Print name and shape of parameter nodes (values not yet initialized)\n", - " print \"\\n\"\n", - " print \"Parameters\"\n", + " print(\"\\n\")\n", + " print(\"Parameters\")\n", " for v in slim.get_model_variables():\n", - " print 'name = {}, shape = {}'.format(v.name, v.get_shape())\n" + " print('name = {}, shape = {}'.format(v.name, v.get_shape()))\n" ] }, { @@ -180,7 +187,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -228,7 +235,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -280,7 +287,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -330,7 +337,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -367,7 +374,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -441,7 +448,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -468,14 +475,14 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ "from datasets import flowers\n", "import tensorflow as tf\n", "\n", - "slim = tf.contrib.slim\n", + "from tensorflow.contrib import slim\n", "\n", "with tf.Graph().as_default(): \n", " dataset = flowers.get_split('train', flowers_data_dir)\n", @@ -485,7 +492,7 @@ " \n", " with tf.Session() as sess: \n", " with slim.queues.QueueRunners(sess):\n", - " for i in xrange(4):\n", + " for i in range(4):\n", " np_image, np_label = sess.run([image, label])\n", " height, width, _ = np_image.shape\n", " class_name = name = dataset.labels_to_names[np_label]\n", @@ -547,7 +554,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -599,14 +606,14 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ "from preprocessing import inception_preprocessing\n", "import tensorflow as tf\n", "\n", - "slim = tf.contrib.slim\n", + "from tensorflow.contrib import slim\n", "\n", "\n", "def load_batch(dataset, batch_size=32, height=299, width=299, is_training=False):\n", @@ -651,7 +658,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -706,7 +713,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -771,7 +778,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -802,26 +809,30 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ "import numpy as np\n", "import os\n", "import tensorflow as tf\n", - "import urllib2\n", + "\n", + "try:\n", + " import urllib2\n", + "except ImportError:\n", + " import urllib.request as urllib\n", "\n", "from datasets import imagenet\n", "from nets import inception\n", "from preprocessing import inception_preprocessing\n", "\n", - "slim = tf.contrib.slim\n", + "from tensorflow.contrib import slim\n", "\n", "image_size = inception.inception_v1.default_image_size\n", "\n", "with tf.Graph().as_default():\n", " url = 'https://upload.wikimedia.org/wikipedia/commons/7/70/EnglishCockerSpaniel_simon.jpg'\n", - " image_string = urllib2.urlopen(url).read()\n", + " image_string = urllib.urlopen(url).read()\n", " image = tf.image.decode_jpeg(image_string, channels=3)\n", " processed_image = inception_preprocessing.preprocess_image(image, image_size, image_size, is_training=False)\n", " processed_images = tf.expand_dims(processed_image, 0)\n", @@ -902,19 +913,23 @@ "import numpy as np\n", "import os\n", "import tensorflow as tf\n", - "import urllib2\n", + "\n", + "try:\n", + " import urllib2\n", + "except ImportError:\n", + " import urllib.request as urllib\n", "\n", "from datasets import imagenet\n", "from nets import vgg\n", "from preprocessing import vgg_preprocessing\n", "\n", - "slim = tf.contrib.slim\n", + "from tensorflow.contrib import slim\n", "\n", "image_size = vgg.vgg_16.default_image_size\n", "\n", "with tf.Graph().as_default():\n", " url = 'https://upload.wikimedia.org/wikipedia/commons/d/d9/First_Student_IC_school_bus_202076.jpg'\n", - " image_string = urllib2.urlopen(url).read()\n", + " image_string = urllib.urlopen(url).read()\n", " image = tf.image.decode_jpeg(image_string, channels=3)\n", " processed_image = vgg_preprocessing.preprocess_image(image, image_size, image_size, is_training=False)\n", " processed_images = tf.expand_dims(processed_image, 0)\n", @@ -960,7 +975,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -972,7 +987,7 @@ "from nets import inception\n", "from preprocessing import inception_preprocessing\n", "\n", - "slim = tf.contrib.slim\n", + "from tensorflow.contrib import slim\n", "image_size = inception.inception_v1.default_image_size\n", "\n", "\n", @@ -1043,7 +1058,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -1052,7 +1067,7 @@ "from datasets import flowers\n", "from nets import inception\n", "\n", - "slim = tf.contrib.slim\n", + "from tensorflow.contrib import slim\n", "\n", "image_size = inception.inception_v1.default_image_size\n", "batch_size = 3\n", @@ -1080,7 +1095,7 @@ " init_fn(sess)\n", " np_probabilities, np_images_raw, np_labels = sess.run([probabilities, images_raw, labels])\n", " \n", - " for i in xrange(batch_size): \n", + " for i in range(batch_size): \n", " image = np_images_raw[i, :, :, :]\n", " true_label = np_labels[i]\n", " predicted_label = np.argmax(np_probabilities[i, :])\n", @@ -1093,27 +1108,36 @@ " plt.axis('off')\n", " plt.show()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 2", + "display_name": "Python 3", "language": "python", - "name": "python2" + "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.11" + "pygments_lexer": "ipython3", + "version": "3.6.1" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } -- GitLab From b4968012c62722cdd2a98419589b779c97f788c7 Mon Sep 17 00:00:00 2001 From: Kaz Sato Date: Tue, 20 Jun 2017 02:17:44 +0900 Subject: [PATCH 064/110] Fix ML Engine Dashboard link (#1599) from Google internal link --- object_detection/g3doc/running_pets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_detection/g3doc/running_pets.md b/object_detection/g3doc/running_pets.md index aadea479e..08fe34eb3 100644 --- a/object_detection/g3doc/running_pets.md +++ b/object_detection/g3doc/running_pets.md @@ -233,7 +233,7 @@ submit training` command is correct. ML Engine does not distinguish between training and evaluation jobs. Users can monitor and stop training and evaluation jobs on the [ML Engine -Dasboard](https://pantheon.corp.google.com/mlengine/jobs). +Dasboard](https://console.cloud.google.com/mlengine/jobs). ## Monitoring Progress with Tensorboard -- GitLab From 9c17823e147ff2893427b47cb57d171da9350d20 Mon Sep 17 00:00:00 2001 From: derekjchow Date: Mon, 19 Jun 2017 11:16:41 -0700 Subject: [PATCH 065/110] Add comment clarifying spatial squeeze. (#1613) --- slim/nets/resnet_v1.py | 13 +++++++------ slim/nets/resnet_v2.py | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/slim/nets/resnet_v1.py b/slim/nets/resnet_v1.py index 19ae0a241..841e2fb2b 100644 --- a/slim/nets/resnet_v1.py +++ b/slim/nets/resnet_v1.py @@ -161,6 +161,9 @@ def resnet_v1(inputs, max-pooling, if False excludes it. spatial_squeeze: if True, logits is of shape [B, C], if false logits is of shape [B, 1, 1, C], where B is batch_size and C is number of classes. + To use this parameter, the input images must be smaller than 300x300 + pixels, in which case the output logit layer does not contain spatial + information and can be removed. reuse: whether or not the network and its variables should be reused. To be able to reuse 'scope' must be given. scope: Optional variable_scope. @@ -200,16 +203,14 @@ def resnet_v1(inputs, if num_classes is not None: net = slim.conv2d(net, num_classes, [1, 1], activation_fn=None, normalizer_fn=None, scope='logits') - if spatial_squeeze: - logits = tf.squeeze(net, [1, 2], name='SpatialSqueeze') - else: - logits = net + if spatial_squeeze: + net = tf.squeeze(net, [1, 2], name='SpatialSqueeze') # Convert end_points_collection into a dictionary of end_points. end_points = slim.utils.convert_collection_to_dict( end_points_collection) if num_classes is not None: - end_points['predictions'] = slim.softmax(logits, scope='predictions') - return logits, end_points + end_points['predictions'] = slim.softmax(net, scope='predictions') + return net, end_points resnet_v1.default_image_size = 224 diff --git a/slim/nets/resnet_v2.py b/slim/nets/resnet_v2.py index f5af10334..0951c1edb 100644 --- a/slim/nets/resnet_v2.py +++ b/slim/nets/resnet_v2.py @@ -158,6 +158,9 @@ def resnet_v2(inputs, results of an activation-less convolution. spatial_squeeze: if True, logits is of shape [B, C], if false logits is of shape [B, 1, 1, C], where B is batch_size and C is number of classes. + To use this parameter, the input images must be smaller than 300x300 + pixels, in which case the output logit layer does not contain spatial + information and can be removed. reuse: whether or not the network and its variables should be reused. To be able to reuse 'scope' must be given. scope: Optional variable_scope. @@ -207,16 +210,14 @@ def resnet_v2(inputs, if num_classes is not None: net = slim.conv2d(net, num_classes, [1, 1], activation_fn=None, normalizer_fn=None, scope='logits') - if spatial_squeeze: - logits = tf.squeeze(net, [1, 2], name='SpatialSqueeze') - else: - logits = net + if spatial_squeeze: + net = tf.squeeze(net, [1, 2], name='SpatialSqueeze') # Convert end_points_collection into a dictionary of end_points. end_points = slim.utils.convert_collection_to_dict( end_points_collection) if num_classes is not None: - end_points['predictions'] = slim.softmax(logits, scope='predictions') - return logits, end_points + end_points['predictions'] = slim.softmax(net, scope='predictions') + return net, end_points resnet_v2.default_image_size = 224 -- GitLab From 057203e7d73c5cc4d989569dcd300dcfaa4b64e9 Mon Sep 17 00:00:00 2001 From: derekjchow Date: Tue, 20 Jun 2017 09:44:15 -0700 Subject: [PATCH 066/110] Make Record scripts python3 compatible. (#1614) --- object_detection/create_pascal_tf_record.py | 16 +++++++++------- object_detection/create_pet_tf_record.py | 16 +++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/object_detection/create_pascal_tf_record.py b/object_detection/create_pascal_tf_record.py index 443862f1d..9da40d90e 100644 --- a/object_detection/create_pascal_tf_record.py +++ b/object_detection/create_pascal_tf_record.py @@ -83,7 +83,7 @@ def dict_to_tf_example(data, """ 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) as fid: + 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) @@ -114,19 +114,21 @@ def dict_to_tf_example(data, 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']) + classes_text.append(obj['name'].encode('utf8')) classes.append(label_map_dict[obj['name']]) truncated.append(int(obj['truncated'])) - poses.append(obj['pose']) + 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']), - 'image/source_id': dataset_util.bytes_feature(data['filename']), - 'image/key/sha256': dataset_util.bytes_feature(key), + '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'), + '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), diff --git a/object_detection/create_pet_tf_record.py b/object_detection/create_pet_tf_record.py index 2bfbb4de3..d7bad283e 100644 --- a/object_detection/create_pet_tf_record.py +++ b/object_detection/create_pet_tf_record.py @@ -86,7 +86,7 @@ def dict_to_tf_example(data, 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) as fid: + 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) @@ -118,19 +118,21 @@ def dict_to_tf_example(data, xmax.append(float(obj['bndbox']['xmax']) / width) ymax.append(float(obj['bndbox']['ymax']) / height) class_name = get_class_name_from_filename(data['filename']) - classes_text.append(class_name) + classes_text.append(class_name.encode('utf8')) classes.append(label_map_dict[class_name]) truncated.append(int(obj['truncated'])) - poses.append(obj['pose']) + 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']), - 'image/source_id': dataset_util.bytes_feature(data['filename']), - 'image/key/sha256': dataset_util.bytes_feature(key), + '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'), + '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), -- GitLab From f305d2dfa7b88bcdc6d7e94503804b269072c572 Mon Sep 17 00:00:00 2001 From: "Dougal J. Sutherland" Date: Tue, 20 Jun 2017 17:45:21 +0100 Subject: [PATCH 067/110] change no-longer-existing concat_v2 to concat (#1701) --- .../tfcode/vision_baseline_lstm.py | 4 +- real_nvp/real_nvp_multiscale_dataset.py | 46 +++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/cognitive_mapping_and_planning/tfcode/vision_baseline_lstm.py b/cognitive_mapping_and_planning/tfcode/vision_baseline_lstm.py index 1b9d68772..ccf3ab23b 100644 --- a/cognitive_mapping_and_planning/tfcode/vision_baseline_lstm.py +++ b/cognitive_mapping_and_planning/tfcode/vision_baseline_lstm.py @@ -145,7 +145,7 @@ def visit_count_fc(visit_count, last_visit, embed_neurons, wt_decay, fc_dropout) on_value=10., off_value=0.) last_visit = tf.one_hot(last_visit, depth=16, axis=1, dtype=tf.float32, on_value=10., off_value=0.) - f = tf.concat_v2([visit_count, last_visit], 1) + f = tf.concat([visit_count, last_visit], 1) x, _ = tf_utils.fc_network( f, neurons=embed_neurons, wt_decay=wt_decay, name='visit_count_embed', offset=0, batch_norm_param=None, dropout_ratio=fc_dropout, @@ -201,7 +201,7 @@ def combine_setup(name, combine_type, embed_img, embed_goal, num_img_neuorons=No def preprocess_egomotion(locs, thetas): with tf.name_scope('pre_ego'): - pre_ego = tf.concat_v2([locs, tf.sin(thetas), tf.cos(thetas)], 2) + pre_ego = tf.concat([locs, tf.sin(thetas), tf.cos(thetas)], 2) sh = pre_ego.get_shape().as_list() pre_ego = tf.reshape(pre_ego, [-1, sh[-1]]) return pre_ego diff --git a/real_nvp/real_nvp_multiscale_dataset.py b/real_nvp/real_nvp_multiscale_dataset.py index a89dec8aa..d7b32ddfb 100644 --- a/real_nvp/real_nvp_multiscale_dataset.py +++ b/real_nvp/real_nvp_multiscale_dataset.py @@ -321,8 +321,8 @@ def masked_conv_aff_coupling(input_, mask_in, dim, name, input_=res, dim=channels, name="bn_in", scale=False, train=train, epsilon=1e-4, axes=[0, 1, 2]) res *= 2. - res = tf.concat_v2([res, -res], 3) - res = tf.concat_v2([res, mask], 3) + res = tf.concat([res, -res], 3) + res = tf.concat([res, mask], 3) dim_in = 2. * channels + 1 res = tf.nn.relu(res) res = resnet(input_=res, dim_in=dim_in, dim=dim, @@ -411,8 +411,8 @@ def masked_conv_add_coupling(input_, mask_in, dim, name, input_=res, dim=channels, name="bn_in", scale=False, train=train, epsilon=1e-4, axes=[0, 1, 2]) res *= 2. - res = tf.concat_v2([res, -res], 3) - res = tf.concat_v2([res, mask], 3) + res = tf.concat([res, -res], 3) + res = tf.concat([res, mask], 3) dim_in = 2. * channels + 1 res = tf.nn.relu(res) shift = resnet(input_=res, dim_in=dim_in, dim=dim, dim_out=channels, @@ -501,7 +501,7 @@ def conv_ch_aff_coupling(input_, dim, name, res = batch_norm( input_=res, dim=channels, name="bn_in", scale=False, train=train, epsilon=1e-4, axes=[0, 1, 2]) - res = tf.concat_v2([res, -res], 3) + res = tf.concat([res, -res], 3) dim_in = 2. * channels res = tf.nn.relu(res) res = resnet(input_=res, dim_in=dim_in, dim=dim, dim_out=2 * channels, @@ -551,11 +551,11 @@ def conv_ch_aff_coupling(input_, dim, name, res *= tf.exp(-.5 * log_var) log_diff -= .5 * log_var if change_bottom: - res = tf.concat_v2([input_, res], 3) - log_diff = tf.concat_v2([tf.zeros_like(log_diff), log_diff], 3) + res = tf.concat([input_, res], 3) + log_diff = tf.concat([tf.zeros_like(log_diff), log_diff], 3) else: - res = tf.concat_v2([res, input_], 3) - log_diff = tf.concat_v2([log_diff, tf.zeros_like(log_diff)], 3) + res = tf.concat([res, input_], 3) + log_diff = tf.concat([log_diff, tf.zeros_like(log_diff)], 3) return res, log_diff @@ -582,7 +582,7 @@ def conv_ch_add_coupling(input_, dim, name, res = batch_norm( input_=res, dim=channels, name="bn_in", scale=False, train=train, epsilon=1e-4, axes=[0, 1, 2]) - res = tf.concat_v2([res, -res], 3) + res = tf.concat([res, -res], 3) dim_in = 2. * channels res = tf.nn.relu(res) shift = resnet(input_=res, dim_in=dim_in, dim=dim, dim_out=channels, @@ -616,11 +616,11 @@ def conv_ch_add_coupling(input_, dim, name, res *= tf.exp(-.5 * log_var) log_diff -= .5 * log_var if change_bottom: - res = tf.concat_v2([input_, res], 3) - log_diff = tf.concat_v2([tf.zeros_like(log_diff), log_diff], 3) + res = tf.concat([input_, res], 3) + log_diff = tf.concat([tf.zeros_like(log_diff), log_diff], 3) else: - res = tf.concat_v2([res, input_], 3) - log_diff = tf.concat_v2([log_diff, tf.zeros_like(log_diff)], 3) + res = tf.concat([res, input_], 3) + log_diff = tf.concat([log_diff, tf.zeros_like(log_diff)], 3) return res, log_diff @@ -742,9 +742,9 @@ def rec_masked_conv_coupling(input_, hps, scale_idx, n_scale, input_=res_1, hps=hps, scale_idx=scale_idx + 1, n_scale=n_scale, use_batch_norm=use_batch_norm, weight_norm=weight_norm, train=train) - res = tf.concat_v2([res_1, res_2], 3) + res = tf.concat([res_1, res_2], 3) log_diff_1 += inc_log_diff - log_diff = tf.concat_v2([log_diff_1, log_diff_2], 3) + log_diff = tf.concat([log_diff_1, log_diff_2], 3) res = squeeze_2x2_ordered(res, reverse=True) log_diff = squeeze_2x2_ordered(log_diff, reverse=True) else: @@ -805,8 +805,8 @@ def rec_masked_deconv_coupling(input_, hps, scale_idx, n_scale, scale_idx=scale_idx + 1, n_scale=n_scale, use_batch_norm=use_batch_norm, weight_norm=weight_norm, train=train) - res = tf.concat_v2([res_1, res_2], 3) - log_diff = tf.concat_v2([log_diff_1, log_diff_2], 3) + res = tf.concat([res_1, res_2], 3) + log_diff = tf.concat([log_diff_1, log_diff_2], 3) res = squeeze_2x2_ordered(res, reverse=True) log_diff = squeeze_2x2_ordered(log_diff, reverse=True) else: @@ -1018,7 +1018,7 @@ class RealNVP(object): width = tf.cast(width, tf.int32) depth = tf.reshape((features["depth"], tf.int64)[0], [1]) depth = tf.cast(depth, tf.int32) - image = tf.reshape(image, tf.concat_v2([height, width, depth], 0)) + image = tf.reshape(image, tf.concat([height, width, depth], 0)) image = tf.random_crop(image, [64, 64, 3]) if FLAGS.mode == "train": image = tf.image.random_flip_left_right(image) @@ -1309,19 +1309,19 @@ class RealNVP(object): z_compressed = z_lost z_noisy = z_lost for _ in xrange(scale_idx + 1): - z_compressed = tf.concat_v2( + z_compressed = tf.concat( [z_compressed, tf.zeros_like(z_compressed)], 3) z_compressed = squeeze_2x2_ordered( z_compressed, reverse=True) - z_noisy = tf.concat_v2( + z_noisy = tf.concat( [z_noisy, tf.random_normal( z_noisy.get_shape().as_list())], 3) z_noisy = squeeze_2x2_ordered(z_noisy, reverse=True) z_compressed_list.append(z_compressed) z_noisy_list.append(z_noisy) self.z_reduced = z_lost - z_compressed = tf.concat_v2(z_compressed_list, 0) - z_noisy = tf.concat_v2(z_noisy_list, 0) + z_compressed = tf.concat(z_compressed_list, 0) + z_noisy = tf.concat(z_noisy_list, 0) noisy_images, _ = decoder( input_=z_noisy, hps=hps, n_scale=hps.n_scale, use_batch_norm=hps.use_batch_norm, weight_norm=True, -- GitLab From 434c277677ba973ae6acc21fcc3d616e1bb7f1df Mon Sep 17 00:00:00 2001 From: Sergio Guadarrama Date: Tue, 20 Jun 2017 10:37:34 -0700 Subject: [PATCH 068/110] Add export inference_graph (#1702) * Add export inference_graph * Update Readme.md to include export_inference_graph --- slim/BUILD | 23 ++++++ slim/README.md | 67 ++++++++++++++- slim/export_inference_graph.py | 122 ++++++++++++++++++++++++++++ slim/export_inference_graph_test.py | 44 ++++++++++ 4 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 slim/export_inference_graph.py create mode 100644 slim/export_inference_graph_test.py diff --git a/slim/BUILD b/slim/BUILD index 348ca7595..bc38704a3 100644 --- a/slim/BUILD +++ b/slim/BUILD @@ -390,3 +390,26 @@ py_binary( ":preprocessing_factory", ], ) + +py_binary( + name = "export_inference_graph", + srcs = ["export_inference_graph.py"], + deps = [ + ":dataset_factory", + ":nets_factory", + ], +) + +py_test( + name = "export_inference_graph_test", + size = "medium", + srcs = ["export_inference_graph_test.py"], + srcs_version = "PY2AND3", + tags = [ + "manual", + ], + deps = [ + ":export_inference_graph", + ":nets_factory", + ], +) diff --git a/slim/README.md b/slim/README.md index 6fe5a7183..179b80606 100644 --- a/slim/README.md +++ b/slim/README.md @@ -32,6 +32,8 @@ Maintainers of TF-slim: Training from scratch
    Fine tuning to a new task
    Evaluating performance
    +Exporting Inference Graph
    +Troubleshooting
    # Installation @@ -204,7 +206,6 @@ Model | TF-Slim File | Checkpoint | Top-1 Accuracy| Top-5 Accuracy | [MobileNet_v1_1.0_224](https://arxiv.org/pdf/1704.04861.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/mobilenet_v1.py)|[mobilenet_v1_1.0_224_2017_06_14.tar.gz](http://download.tensorflow.org/models/mobilenet_v1_1.0_224_2017_06_14.tar.gz)|70.7|89.5| [MobileNet_v1_0.50_160](https://arxiv.org/pdf/1704.04861.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/mobilenet_v1.py)|[mobilenet_v1_0.50_160_2017_06_14.tar.gz](http://download.tensorflow.org/models/mobilenet_v1_0.50_160_2017_06_14.tar.gz)|59.9|82.5| [MobileNet_v1_0.25_128](https://arxiv.org/pdf/1704.04861.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/mobilenet_v1.py)|[mobilenet_v1_0.25_128_2017_06_14.tar.gz](http://download.tensorflow.org/models/mobilenet_v1_0.25_128_2017_06_14.tar.gz)|41.3|66.2| - ^ ResNet V2 models use Inception pre-processing and input image size of 299 (use `--preprocessing_name inception --eval_image_size 299` when using `eval_image_classifier.py`). Performance numbers for ResNet V2 models are @@ -327,8 +328,72 @@ $ python eval_image_classifier.py \ ``` +# Exporting the Inference Graph + + +Saves out a GraphDef containing the architecture of the model. + +To use it with a model name defined by slim, run: + +```shell +$ python export_inference_graph.py \ + --alsologtostderr \ + --model_name=inception_v3 \ + --output_file=/tmp/inception_v3_inf_graph.pb + +$ python export_inference_graph.py \ + --alsologtostderr \ + --model_name=mobilenet_v1 \ + --image_size=224 \ + --output_file=/tmp/mobilenet_v1_224.pb +``` + +## Freezing the exported Graph +If you then want to use the resulting model with your own or pretrained +checkpoints as part of a mobile model, you can run freeze_graph to get a graph +def with the variables inlined as constants using: + +```shell +bazel build tensorflow/python/tools:freeze_graph + +bazel-bin/tensorflow/python/tools/freeze_graph \ + --input_graph=/tmp/inception_v3_inf_graph.pb \ + --input_checkpoint=/tmp/checkpoints/inception_v3.ckpt \ + --input_binary=true --output_graph=/tmp/frozen_inception_v3.pb \ + --output_node_names=InceptionV3/Predictions/Reshape_1 +``` + +The output node names will vary depending on the model, but you can inspect and +estimate them using the summarize_graph tool: + +```shell +bazel build tensorflow/tools/graph_transforms:summarize_graph + +bazel-bin/tensorflow/tools/graph_transforms/summarize_graph \ + --in_graph=/tmp/inception_v3_inf_graph.pb +``` + +## Run label image in C++ + +To run the resulting graph in C++, you can look at the label_image sample code: + +```shell +bazel build tensorflow/examples/label_image:label_image + +bazel-bin/tensorflow/examples/label_image/label_image \ + --image=${HOME}/Pictures/flowers.jpg \ + --input_layer=input \ + --output_layer=InceptionV3/Predictions/Reshape_1 \ + --graph=/tmp/frozen_inception_v3.pb \ + --labels=/tmp/imagenet_slim_labels.txt \ + --input_mean=0 \ + --input_std=255 \ + --logtostderr +``` + # Troubleshooting + #### The model runs out of CPU memory. diff --git a/slim/export_inference_graph.py b/slim/export_inference_graph.py new file mode 100644 index 000000000..13f10ce00 --- /dev/null +++ b/slim/export_inference_graph.py @@ -0,0 +1,122 @@ +# 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"""Saves out a GraphDef containing the architecture of the model. + +To use it, run something like this, with a model name defined by slim: + +bazel build tensorflow_models/slim:export_inference_graph +bazel-bin/tensorflow_models/slim/export_inference_graph \ +--model_name=inception_v3 --output_file=/tmp/inception_v3_inf_graph.pb + +If you then want to use the resulting model with your own or pretrained +checkpoints as part of a mobile model, you can run freeze_graph to get a graph +def with the variables inlined as constants using: + +bazel build tensorflow/python/tools:freeze_graph +bazel-bin/tensorflow/python/tools/freeze_graph \ +--input_graph=/tmp/inception_v3_inf_graph.pb \ +--input_checkpoint=/tmp/checkpoints/inception_v3.ckpt \ +--input_binary=true --output_graph=/tmp/frozen_inception_v3.pb \ +--output_node_names=InceptionV3/Predictions/Reshape_1 + +The output node names will vary depending on the model, but you can inspect and +estimate them using the summarize_graph tool: + +bazel build tensorflow/tools/graph_transforms:summarize_graph +bazel-bin/tensorflow/tools/graph_transforms/summarize_graph \ +--in_graph=/tmp/inception_v3_inf_graph.pb + +To run the resulting graph in C++, you can look at the label_image sample code: + +bazel build tensorflow/examples/label_image:label_image +bazel-bin/tensorflow/examples/label_image/label_image \ +--image=${HOME}/Pictures/flowers.jpg \ +--input_layer=input \ +--output_layer=InceptionV3/Predictions/Reshape_1 \ +--graph=/tmp/frozen_inception_v3.pb \ +--labels=/tmp/imagenet_slim_labels.txt \ +--input_mean=0 \ +--input_std=255 \ +--logtostderr + +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import tensorflow as tf + +from tensorflow.python.platform import gfile +from datasets import dataset_factory +from nets import nets_factory + + +slim = tf.contrib.slim + +tf.app.flags.DEFINE_string( + 'model_name', 'inception_v3', 'The name of the architecture to save.') + +tf.app.flags.DEFINE_boolean( + 'is_training', False, + 'Whether to save out a training-focused version of the model.') + +tf.app.flags.DEFINE_integer( + 'default_image_size', 224, + 'The image size to use if the model does not define it.') + +tf.app.flags.DEFINE_string('dataset_name', 'imagenet', + 'The name of the dataset to use with the model.') + +tf.app.flags.DEFINE_integer( + 'labels_offset', 0, + 'An offset for the labels in the dataset. This flag is primarily used to ' + 'evaluate the VGG and ResNet architectures which do not use a background ' + 'class for the ImageNet dataset.') + +tf.app.flags.DEFINE_string( + 'output_file', '', 'Where to save the resulting file to.') + +tf.app.flags.DEFINE_string( + 'dataset_dir', '', 'Directory to save intermediate dataset files to') + +FLAGS = tf.app.flags.FLAGS + + +def main(_): + if not FLAGS.output_file: + raise ValueError('You must supply the path to save to with --output_file') + tf.logging.set_verbosity(tf.logging.INFO) + with tf.Graph().as_default() as graph: + dataset = dataset_factory.get_dataset(FLAGS.dataset_name, 'validation', + FLAGS.dataset_dir) + network_fn = nets_factory.get_network_fn( + FLAGS.model_name, + num_classes=(dataset.num_classes - FLAGS.labels_offset), + is_training=FLAGS.is_training) + if hasattr(network_fn, 'default_image_size'): + image_size = network_fn.default_image_size + else: + image_size = FLAGS.default_image_size + placeholder = tf.placeholder(name='input', dtype=tf.float32, + shape=[1, image_size, image_size, 3]) + network_fn(placeholder) + graph_def = graph.as_graph_def() + with gfile.GFile(FLAGS.output_file, 'wb') as f: + f.write(graph_def.SerializeToString()) + + +if __name__ == '__main__': + tf.app.run() diff --git a/slim/export_inference_graph_test.py b/slim/export_inference_graph_test.py new file mode 100644 index 000000000..a730e67e5 --- /dev/null +++ b/slim/export_inference_graph_test.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. +# ============================================================================== + +"""Tests for export_inference_graph.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os + + +import tensorflow as tf + +from tensorflow.python.platform import gfile +from google3.third_party.tensorflow_models.slim import export_inference_graph + + +class ExportInferenceGraphTest(tf.test.TestCase): + + def testExportInferenceGraph(self): + tmpdir = self.get_temp_dir() + output_file = os.path.join(tmpdir, 'inception_v3.pb') + flags = tf.app.flags.FLAGS + flags.output_file = output_file + flags.model_name = 'inception_v3' + flags.dataset_dir = tmpdir + export_inference_graph.main(None) + self.assertTrue(gfile.Exists(output_file)) + +if __name__ == '__main__': + tf.test.main() -- GitLab From c4ba26b4d36ea8d339200bab6bd6fea6fd4af11b Mon Sep 17 00:00:00 2001 From: Jonathan Huang Date: Tue, 20 Jun 2017 14:16:51 -0700 Subject: [PATCH 069/110] Cast regularization parameters to float. (#1707) This works around a bug in earlier proto versions that automatically infer these values to be integer instead of float. --- object_detection/builders/hyperparams_builder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/object_detection/builders/hyperparams_builder.py b/object_detection/builders/hyperparams_builder.py index 6fc62a944..c8c18e39c 100644 --- a/object_detection/builders/hyperparams_builder.py +++ b/object_detection/builders/hyperparams_builder.py @@ -111,9 +111,9 @@ def _build_regularizer(regularizer): """ regularizer_oneof = regularizer.WhichOneof('regularizer_oneof') if regularizer_oneof == 'l1_regularizer': - return slim.l1_regularizer(scale=regularizer.l1_regularizer.weight) + return slim.l1_regularizer(scale=float(regularizer.l1_regularizer.weight)) if regularizer_oneof == 'l2_regularizer': - return slim.l2_regularizer(scale=regularizer.l2_regularizer.weight) + return slim.l2_regularizer(scale=float(regularizer.l2_regularizer.weight)) raise ValueError('Unknown regularizer function: {}'.format(regularizer_oneof)) -- GitLab From 477ed41e7e4e8a8443bc633846eb01e2182dc68a Mon Sep 17 00:00:00 2001 From: Jonathan Huang Date: Tue, 20 Jun 2017 16:14:33 -0700 Subject: [PATCH 070/110] Replace Oxford-IIT by Oxford-IIIT. (#1708) --- object_detection/g3doc/preparing_inputs.md | 6 +++--- object_detection/g3doc/running_locally.md | 2 +- object_detection/g3doc/running_on_cloud.md | 2 +- object_detection/g3doc/running_pets.md | 12 ++++++------ ...aster_rcnn_inception_resnet_v2_atrous_pets.config | 2 +- .../configs/faster_rcnn_resnet101_pets.config | 2 +- .../configs/faster_rcnn_resnet152_pets.config | 2 +- .../samples/configs/faster_rcnn_resnet50_pets.config | 2 +- .../samples/configs/rfcn_resnet101_pets.config | 2 +- .../samples/configs/ssd_inception_v2_pets.config | 2 +- .../samples/configs/ssd_mobilenet_v1_pets.config | 2 +- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/object_detection/g3doc/preparing_inputs.md b/object_detection/g3doc/preparing_inputs.md index a1f8f17e1..77ba7f39f 100644 --- a/object_detection/g3doc/preparing_inputs.md +++ b/object_detection/g3doc/preparing_inputs.md @@ -2,7 +2,7 @@ Tensorflow Object Detection API reads data using the TFRecord file format. Two sample scripts (`create_pascal_tf_record.py` and `create_pet_tf_record.py`) are -provided to convert from the PASCAL VOC dataset and Oxford-IIT Pet dataset to +provided to convert from the PASCAL VOC dataset and Oxford-IIIT Pet dataset to TFRecords. ## Generating the PASCAL VOC TFRecord files. @@ -26,9 +26,9 @@ pascal_val.record in the tensorflow/models/object_detection directory. The label map for the PASCAL VOC data set can be found at data/pascal_label_map.pbtxt. -## Generation the Oxford-IIT Pet TFRecord files. +## Generation the Oxford-IIIT Pet TFRecord files. -The Oxford-IIT Pet data set can be downloaded from +The Oxford-IIIT Pet data set can be downloaded from [their website](http://www.robots.ox.ac.uk/~vgg/data/pets/). Extract the tar file and run the `create_pet_tf_record` script to generate TFRecords. diff --git a/object_detection/g3doc/running_locally.md b/object_detection/g3doc/running_locally.md index 7143b6d85..dd53225b3 100644 --- a/object_detection/g3doc/running_locally.md +++ b/object_detection/g3doc/running_locally.md @@ -10,7 +10,7 @@ dependencies, compiling the configuration protobufs and setting up the Python environment. 2. A valid data set has been created. See [this page](preparing_inputs.md) for instructions on how to generate a dataset for the PASCAL VOC challenge or the -Oxford-IIT Pet dataset. +Oxford-IIIT Pet dataset. 3. A Object Detection pipeline configuration has been written. See [this page](configuring_jobs.md) for details on how to write a pipeline configuration. diff --git a/object_detection/g3doc/running_on_cloud.md b/object_detection/g3doc/running_on_cloud.md index 0d74ac4e2..b96725eaf 100644 --- a/object_detection/g3doc/running_on_cloud.md +++ b/object_detection/g3doc/running_on_cloud.md @@ -11,7 +11,7 @@ See [the Cloud ML quick start guide](https://cloud.google.com/ml-engine/docs/qui in the [installation instructions](installation.md). 3. The reader has a valid data set and stored it in a Google Cloud Storage bucket. See [this page](preparing_inputs.md) for instructions on how to generate -a dataset for the PASCAL VOC challenge or the Oxford-IIT Pet dataset. +a dataset for the PASCAL VOC challenge or the Oxford-IIIT Pet dataset. 4. The reader has configured a valid Object Detection pipeline, and stored it in a Google Cloud Storage bucket. See [this page](configuring_jobs.md) for details on how to write a pipeline configuration. diff --git a/object_detection/g3doc/running_pets.md b/object_detection/g3doc/running_pets.md index 08fe34eb3..eae858af7 100644 --- a/object_detection/g3doc/running_pets.md +++ b/object_detection/g3doc/running_pets.md @@ -1,7 +1,7 @@ -# Quick Start: Distributed Training on the Oxford-IIT Pets Dataset on Google Cloud +# Quick Start: Distributed Training on the Oxford-IIIT Pets Dataset on Google Cloud This page is a walkthrough for training an object detector using the Tensorflow -Object Detection API. In this tutorial, we'll be training on the Oxford-IIT Pets +Object Detection API. In this tutorial, we'll be training on the Oxford-IIIT Pets dataset to build a system to detect various breeds of cats and dogs. The output of the detector will look like the following: @@ -43,11 +43,11 @@ Please run through the [installation instructions](installation.md) to install Tensorflow and all it dependencies. Ensure the Protobuf libraries are compiled and the library directories are added to `PYTHONPATH`. -## Getting the Oxford-IIT Pets Dataset and Uploading it to Google Cloud Storage +## Getting the Oxford-IIIT Pets Dataset and Uploading it to Google Cloud Storage In order to train a detector, we require a dataset of images, bounding boxes and -classifications. For this demo, we'll use the Oxford-IIT Pets dataset. The raw -dataset for Oxford-IIT Pets lives +classifications. For this demo, we'll use the Oxford-IIIT Pets dataset. The raw +dataset for Oxford-IIIT Pets lives [here](http://www.robots.ox.ac.uk/~vgg/data/pets/). You will need to download both the image dataset [`images.tar.gz`](http://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz) and the groundtruth data [`annotations.tar.gz`](http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz) @@ -65,7 +65,7 @@ the tarballs, your object_detection directory should appear as follows: The Tensorflow Object Detection API expects data to be in the TFRecord format, so we'll now run the _create_pet_tf_record_ script to convert from the raw -Oxford-IIT Pet dataset into TFRecords. Run the following commands from the +Oxford-IIIT Pet dataset into TFRecords. Run the following commands from the object_detection directory: ``` bash diff --git a/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_pets.config b/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_pets.config index a09133374..fc7e14e25 100644 --- a/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_pets.config +++ b/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_pets.config @@ -1,5 +1,5 @@ # Faster R-CNN with Inception Resnet v2, Atrous version; -# Configured for Oxford-IIT Pets Dataset. +# Configured for Oxford-IIIT Pets Dataset. # Users should configure the fine_tune_checkpoint field in the train config as # well as the label_map_path and input_path fields in the train_input_reader and # eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that diff --git a/object_detection/samples/configs/faster_rcnn_resnet101_pets.config b/object_detection/samples/configs/faster_rcnn_resnet101_pets.config index b90304c2e..cee6604a4 100644 --- a/object_detection/samples/configs/faster_rcnn_resnet101_pets.config +++ b/object_detection/samples/configs/faster_rcnn_resnet101_pets.config @@ -1,4 +1,4 @@ -# Faster R-CNN with Resnet-101 (v1) configured for the Oxford-IIT Pet Dataset. +# Faster R-CNN with Resnet-101 (v1) configured for the Oxford-IIIT Pet Dataset. # Users should configure the fine_tune_checkpoint field in the train config as # well as the label_map_path and input_path fields in the train_input_reader and # eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that diff --git a/object_detection/samples/configs/faster_rcnn_resnet152_pets.config b/object_detection/samples/configs/faster_rcnn_resnet152_pets.config index 128380b9c..aae28489e 100644 --- a/object_detection/samples/configs/faster_rcnn_resnet152_pets.config +++ b/object_detection/samples/configs/faster_rcnn_resnet152_pets.config @@ -1,4 +1,4 @@ -# Faster R-CNN with Resnet-152 (v1), configured for Oxford-IIT Pets Dataset. +# Faster R-CNN with Resnet-152 (v1), configured for Oxford-IIIT Pets Dataset. # Users should configure the fine_tune_checkpoint field in the train config as # well as the label_map_path and input_path fields in the train_input_reader and # eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that diff --git a/object_detection/samples/configs/faster_rcnn_resnet50_pets.config b/object_detection/samples/configs/faster_rcnn_resnet50_pets.config index 5e929301a..110c1b4bb 100644 --- a/object_detection/samples/configs/faster_rcnn_resnet50_pets.config +++ b/object_detection/samples/configs/faster_rcnn_resnet50_pets.config @@ -1,4 +1,4 @@ -# Faster R-CNN with Resnet-50 (v1), configured for Oxford-IIT Pets Dataset. +# Faster R-CNN with Resnet-50 (v1), configured for Oxford-IIIT Pets Dataset. # Users should configure the fine_tune_checkpoint field in the train config as # well as the label_map_path and input_path fields in the train_input_reader and # eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that diff --git a/object_detection/samples/configs/rfcn_resnet101_pets.config b/object_detection/samples/configs/rfcn_resnet101_pets.config index 2b9df17ef..a2b88f9df 100644 --- a/object_detection/samples/configs/rfcn_resnet101_pets.config +++ b/object_detection/samples/configs/rfcn_resnet101_pets.config @@ -1,4 +1,4 @@ -# R-FCN with Resnet-101 (v1), configured for Oxford-IIT Pets Dataset. +# R-FCN with Resnet-101 (v1), configured for Oxford-IIIT Pets Dataset. # Users should configure the fine_tune_checkpoint field in the train config as # well as the label_map_path and input_path fields in the train_input_reader and # eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that diff --git a/object_detection/samples/configs/ssd_inception_v2_pets.config b/object_detection/samples/configs/ssd_inception_v2_pets.config index 49bdf7e06..b14fa480d 100644 --- a/object_detection/samples/configs/ssd_inception_v2_pets.config +++ b/object_detection/samples/configs/ssd_inception_v2_pets.config @@ -1,4 +1,4 @@ -# SSD with Inception v2 configured for Oxford-IIT Pets Dataset. +# SSD with Inception v2 configured for Oxford-IIIT Pets Dataset. # Users should configure the fine_tune_checkpoint field in the train config as # well as the label_map_path and input_path fields in the train_input_reader and # eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that diff --git a/object_detection/samples/configs/ssd_mobilenet_v1_pets.config b/object_detection/samples/configs/ssd_mobilenet_v1_pets.config index c8d83ddb1..429075c64 100644 --- a/object_detection/samples/configs/ssd_mobilenet_v1_pets.config +++ b/object_detection/samples/configs/ssd_mobilenet_v1_pets.config @@ -1,4 +1,4 @@ -# SSD with Mobilenet v1, configured for Oxford-IIT Pets Dataset. +# SSD with Mobilenet v1, configured for Oxford-IIIT Pets Dataset. # Users should configure the fine_tune_checkpoint field in the train config as # well as the label_map_path and input_path fields in the train_input_reader and # eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that -- GitLab From 3f9382a64435fefd4d5c755e498ab66fc3662c69 Mon Sep 17 00:00:00 2001 From: Salas Date: Thu, 22 Jun 2017 02:35:48 +0800 Subject: [PATCH 071/110] Fix compatibility of object_detection for Python3 (#1610) * make batcher compatible for py3 * make prefetcher and operations in ops compatible for py3 * use six.iteritem * make all tests in compatible with py3 * simplify usage of six and modify import order * add back the space line --- object_detection/core/batcher.py | 6 +++--- object_detection/core/post_processing.py | 2 +- object_detection/core/prefetcher.py | 2 +- object_detection/core/preprocessor_test.py | 7 ++++++- object_detection/utils/ops.py | 7 ++++--- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/object_detection/core/batcher.py b/object_detection/core/batcher.py index fdd698c43..984734ff4 100644 --- a/object_detection/core/batcher.py +++ b/object_detection/core/batcher.py @@ -78,11 +78,11 @@ class BatchQueue(object): """ # Remember static shapes to set shapes of batched tensors. static_shapes = collections.OrderedDict( - {key: tensor.get_shape() for key, tensor in tensor_dict.iteritems()}) + {key: tensor.get_shape() for key, tensor in tensor_dict.items()}) # Remember runtime shapes to unpad tensors after batching. runtime_shapes = collections.OrderedDict( {(key, 'runtime_shapes'): tf.shape(tensor) - for key, tensor in tensor_dict.iteritems()}) + for key, tensor in tensor_dict.items()}) all_tensors = tensor_dict all_tensors.update(runtime_shapes) batched_tensors = tf.train.batch( @@ -109,7 +109,7 @@ class BatchQueue(object): # Separate input tensors from tensors containing their runtime shapes. tensors = {} shapes = {} - for key, batched_tensor in batched_tensors.iteritems(): + 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 isinstance(key, tuple) and key[1] == 'runtime_shapes': diff --git a/object_detection/core/post_processing.py b/object_detection/core/post_processing.py index cda26f25e..5983ca169 100644 --- a/object_detection/core/post_processing.py +++ b/object_detection/core/post_processing.py @@ -131,7 +131,7 @@ def multiclass_non_max_suppression(boxes, boxlist_and_class_scores.add_field(fields.BoxListFields.masks, per_class_masks) if additional_fields is not None: - for key, tensor in additional_fields.iteritems(): + for key, tensor in additional_fields.items(): boxlist_and_class_scores.add_field(key, tensor) boxlist_filtered = box_list_ops.filter_greater_than( boxlist_and_class_scores, score_thresh) diff --git a/object_detection/core/prefetcher.py b/object_detection/core/prefetcher.py index ba5958f62..e690c599f 100644 --- a/object_detection/core/prefetcher.py +++ b/object_detection/core/prefetcher.py @@ -45,7 +45,7 @@ def prefetch(tensor_dict, capacity): Returns: a FIFO prefetcher queue """ - names = tensor_dict.keys() + 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, diff --git a/object_detection/core/preprocessor_test.py b/object_detection/core/preprocessor_test.py index 109df7a6c..eca135d16 100644 --- a/object_detection/core/preprocessor_test.py +++ b/object_detection/core/preprocessor_test.py @@ -15,14 +15,19 @@ """Tests for object_detection.core.preprocessor.""" -import mock import numpy as np +import six import tensorflow as tf from object_detection.core import preprocessor from object_detection.core import standard_fields as fields +if six.PY2: + import mock # pylint: disable=g-import-not-at-top +else: + from unittest import mock # pylint: disable=g-import-not-at-top + class PreprocessorTest(tf.test.TestCase): diff --git a/object_detection/utils/ops.py b/object_detection/utils/ops.py index 989cdf3c4..290cd33a8 100644 --- a/object_detection/utils/ops.py +++ b/object_detection/utils/ops.py @@ -15,6 +15,7 @@ """A module for helper tensorflow ops.""" import math +import six import tensorflow as tf @@ -197,9 +198,9 @@ def padded_one_hot_encoding(indices, depth, left_pad): TODO: add runtime checks for depth and indices. """ - if depth < 0 or not isinstance(depth, (int, long)): + if depth < 0 or not isinstance(depth, (int, long) if six.PY2 else int): raise ValueError('`depth` must be a non-negative integer.') - if left_pad < 0 or not isinstance(left_pad, (int, long)): + if left_pad < 0 or not isinstance(left_pad, (int, long) if six.PY2 else int): raise ValueError('`left_pad` must be a non-negative integer.') if depth == 0: return None @@ -548,7 +549,7 @@ def position_sensitive_crop_regions(image, raise ValueError('crop_size should be divisible by num_spatial_bins') total_bins *= num_bins - bin_crop_size.append(crop_dim / num_bins) + bin_crop_size.append(crop_dim // num_bins) if not global_pool and bin_crop_size[0] != bin_crop_size[1]: raise ValueError('Only support square bin crop size for now.') -- GitLab From 6c2a1d6bc0ec1d345b247a1f89f0ff975628b8a8 Mon Sep 17 00:00:00 2001 From: ericj974 Date: Thu, 22 Jun 2017 11:18:06 +0800 Subject: [PATCH 072/110] Naming consistency pascal_voc_ -> pascal_ --- .../samples/configs/faster_rcnn_resnet101_voc07.config | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/object_detection/samples/configs/faster_rcnn_resnet101_voc07.config b/object_detection/samples/configs/faster_rcnn_resnet101_voc07.config index 622194b8e..461898faf 100644 --- a/object_detection/samples/configs/faster_rcnn_resnet101_voc07.config +++ b/object_detection/samples/configs/faster_rcnn_resnet101_voc07.config @@ -118,9 +118,9 @@ train_config: { train_input_reader: { tf_record_input_reader { - input_path: "PATH_TO_BE_CONFIGURED/pascal_voc_train.record" + input_path: "PATH_TO_BE_CONFIGURED/pascal_train.record" } - label_map_path: "PATH_TO_BE_CONFIGURED/pascal_voc_label_map.pbtxt" + label_map_path: "PATH_TO_BE_CONFIGURED/pascal_label_map.pbtxt" } eval_config: { @@ -129,7 +129,7 @@ eval_config: { eval_input_reader: { tf_record_input_reader { - input_path: "PATH_TO_BE_CONFIGURED/pascal_voc_val.record" + input_path: "PATH_TO_BE_CONFIGURED/pascal_val.record" } - label_map_path: "PATH_TO_BE_CONFIGURED/pascal_voc_label_map.pbtxt" + label_map_path: "PATH_TO_BE_CONFIGURED/pascal_label_map.pbtxt" } -- GitLab From 9b31ed03a4d5e88ec1d2470ca9a32405bf9c5016 Mon Sep 17 00:00:00 2001 From: Hao Date: Thu, 22 Jun 2017 19:44:46 +0100 Subject: [PATCH 073/110] installation link broken --- street/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/street/README.md b/street/README.md index 1750a8843..b63b99b93 100644 --- a/street/README.md +++ b/street/README.md @@ -38,7 +38,7 @@ Avenue des Sapins ## Installing and setting up the STREET model -[Install Tensorflow](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/g3doc/get_started/os_setup.md#virtualenv-installation) +[Install Tensorflow](https://www.tensorflow.org/install/) Install numpy: -- GitLab From a4950ea41bd678ea76dcd2919d47f1afcc8420f2 Mon Sep 17 00:00:00 2001 From: Saurabh Gupta Date: Thu, 22 Jun 2017 16:04:29 -0700 Subject: [PATCH 074/110] Add back resnet v2 links. (#1738) --- slim/README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/slim/README.md b/slim/README.md index 179b80606..021673631 100644 --- a/slim/README.md +++ b/slim/README.md @@ -197,15 +197,19 @@ Model | TF-Slim File | Checkpoint | Top-1 Accuracy| Top-5 Accuracy | [Inception V3](http://arxiv.org/abs/1512.00567)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/inception_v3.py)|[inception_v3_2016_08_28.tar.gz](http://download.tensorflow.org/models/inception_v3_2016_08_28.tar.gz)|78.0|93.9| [Inception V4](http://arxiv.org/abs/1602.07261)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/inception_v4.py)|[inception_v4_2016_09_09.tar.gz](http://download.tensorflow.org/models/inception_v4_2016_09_09.tar.gz)|80.2|95.2| [Inception-ResNet-v2](http://arxiv.org/abs/1602.07261)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/inception_resnet_v2.py)|[inception_resnet_v2_2016_08_30.tar.gz](http://download.tensorflow.org/models/inception_resnet_v2_2016_08_30.tar.gz)|80.4|95.3| -[ResNet 50](https://arxiv.org/abs/1512.03385)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v1.py)|[resnet_v1_50_2016_08_28.tar.gz](http://download.tensorflow.org/models/resnet_v1_50_2016_08_28.tar.gz)|75.2|92.2| -[ResNet 101](https://arxiv.org/abs/1512.03385)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v1.py)|[resnet_v1_101_2016_08_28.tar.gz](http://download.tensorflow.org/models/resnet_v1_101_2016_08_28.tar.gz)|76.4|92.9| -[ResNet 152](https://arxiv.org/abs/1512.03385)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v1.py)|[resnet_v1_152_2016_08_28.tar.gz](http://download.tensorflow.org/models/resnet_v1_152_2016_08_28.tar.gz)|76.8|93.2| +[ResNet V1 50](https://arxiv.org/abs/1512.03385)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v1.py)|[resnet_v1_50_2016_08_28.tar.gz](http://download.tensorflow.org/models/resnet_v1_50_2016_08_28.tar.gz)|75.2|92.2| +[ResNet V1 101](https://arxiv.org/abs/1512.03385)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v1.py)|[resnet_v1_101_2016_08_28.tar.gz](http://download.tensorflow.org/models/resnet_v1_101_2016_08_28.tar.gz)|76.4|92.9| +[ResNet V1 152](https://arxiv.org/abs/1512.03385)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v1.py)|[resnet_v1_152_2016_08_28.tar.gz](http://download.tensorflow.org/models/resnet_v1_152_2016_08_28.tar.gz)|76.8|93.2| +[ResNet V2 50](https://arxiv.org/abs/1603.05027)^|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v2.py)|[resnet_v2_50_2017_04_14.tar.gz](http://download.tensorflow.org/models/resnet_v2_50_2017_04_14.tar.gz)|75.6|92.8| +[ResNet V2 101](https://arxiv.org/abs/1603.05027)^|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v2.py)|[resnet_v2_101_2017_04_14.tar.gz](http://download.tensorflow.org/models/resnet_v2_101_2017_04_14.tar.gz)|77.0|93.7| +[ResNet V2 152](https://arxiv.org/abs/1603.05027)^|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v2.py)|[resnet_v2_152_2017_04_14.tar.gz](http://download.tensorflow.org/models/resnet_v2_152_2017_04_14.tar.gz)|77.8|94.1| [ResNet V2 200](https://arxiv.org/abs/1603.05027)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/resnet_v2.py)|[TBA]()|79.9\*|95.2\*| [VGG 16](http://arxiv.org/abs/1409.1556.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/vgg.py)|[vgg_16_2016_08_28.tar.gz](http://download.tensorflow.org/models/vgg_16_2016_08_28.tar.gz)|71.5|89.8| [VGG 19](http://arxiv.org/abs/1409.1556.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/vgg.py)|[vgg_19_2016_08_28.tar.gz](http://download.tensorflow.org/models/vgg_19_2016_08_28.tar.gz)|71.1|89.8| [MobileNet_v1_1.0_224](https://arxiv.org/pdf/1704.04861.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/mobilenet_v1.py)|[mobilenet_v1_1.0_224_2017_06_14.tar.gz](http://download.tensorflow.org/models/mobilenet_v1_1.0_224_2017_06_14.tar.gz)|70.7|89.5| [MobileNet_v1_0.50_160](https://arxiv.org/pdf/1704.04861.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/mobilenet_v1.py)|[mobilenet_v1_0.50_160_2017_06_14.tar.gz](http://download.tensorflow.org/models/mobilenet_v1_0.50_160_2017_06_14.tar.gz)|59.9|82.5| [MobileNet_v1_0.25_128](https://arxiv.org/pdf/1704.04861.pdf)|[Code](https://github.com/tensorflow/models/blob/master/slim/nets/mobilenet_v1.py)|[mobilenet_v1_0.25_128_2017_06_14.tar.gz](http://download.tensorflow.org/models/mobilenet_v1_0.25_128_2017_06_14.tar.gz)|41.3|66.2| + ^ ResNet V2 models use Inception pre-processing and input image size of 299 (use `--preprocessing_name inception --eval_image_size 299` when using `eval_image_classifier.py`). Performance numbers for ResNet V2 models are @@ -214,6 +218,7 @@ reported on ImageNet valdiation set. All 16 MobileNet Models reported in the [MobileNet Paper](https://arxiv.org/abs/1704.04861) can be found [here](https://github.com/tensorflow/models/tree/master/slim/nets/mobilenet_v1.md). (\*): Results quoted from the [paper](https://arxiv.org/abs/1603.05027). + Here is an example of how to download the Inception V3 checkpoint: ```shell -- GitLab From 3a65897989c4711c2b0d8544a1a8a455ac654cec Mon Sep 17 00:00:00 2001 From: Jasmine Date: Thu, 22 Jun 2017 16:41:07 -0700 Subject: [PATCH 075/110] adding lfads --- README.md | 1 + lfads/README.md | 194 ++ lfads/distributions.py | 493 +++++ lfads/lfads.py | 1935 +++++++++++++++++ lfads/plot_lfads.py | 223 ++ lfads/run_lfads.py | 778 +++++++ lfads/synth_data/generate_chaotic_rnn_data.py | 193 ++ lfads/synth_data/generate_itb_data.py | 208 ++ lfads/synth_data/generate_labeled_rnn_data.py | 146 ++ lfads/synth_data/run_generate_synth_data.sh | 37 + lfads/synth_data/synthetic_data_utils.py | 322 +++ .../model-65000.data-00000-of-00001 | Bin 0 -> 10608 bytes .../synth_data/trained_itb/model-65000.index | Bin 0 -> 266 bytes lfads/synth_data/trained_itb/model-65000.meta | Bin 0 -> 1053549 bytes lfads/utils.py | 357 +++ 15 files changed, 4887 insertions(+) create mode 100644 lfads/README.md create mode 100644 lfads/distributions.py create mode 100644 lfads/lfads.py create mode 100644 lfads/plot_lfads.py create mode 100755 lfads/run_lfads.py create mode 100644 lfads/synth_data/generate_chaotic_rnn_data.py create mode 100644 lfads/synth_data/generate_itb_data.py create mode 100644 lfads/synth_data/generate_labeled_rnn_data.py create mode 100755 lfads/synth_data/run_generate_synth_data.sh create mode 100644 lfads/synth_data/synthetic_data_utils.py create mode 100644 lfads/synth_data/trained_itb/model-65000.data-00000-of-00001 create mode 100644 lfads/synth_data/trained_itb/model-65000.index create mode 100644 lfads/synth_data/trained_itb/model-65000.meta create mode 100644 lfads/utils.py diff --git a/README.md b/README.md index f0f203855..f3b4619b7 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ running TensorFlow 0.12 or earlier, please - [im2txt](im2txt): image-to-text neural network for image captioning. - [inception](inception): deep convolutional networks for computer vision. - [learning_to_remember_rare_events](learning_to_remember_rare_events): a large-scale life-long memory module for use in deep learning. +- [lfads](lfads): sequential variational autoencoder for analyzing neuroscience data. - [lm_1b](lm_1b): language modeling on the one billion word benchmark. - [namignizer](namignizer): recognize and generate names. - [neural_gpu](neural_gpu): highly parallel neural computer. diff --git a/lfads/README.md b/lfads/README.md new file mode 100644 index 000000000..ab4ee2b91 --- /dev/null +++ b/lfads/README.md @@ -0,0 +1,194 @@ +# LFADS - Latent Factor Analysis via Dynamical Systems + +This code implements the model from the paper "[LFADS - Latent Factor Analysis via Dynamical Systems](http://biorxiv.org/content/early/2017/06/20/152884)". It is a sequential variational auto-encoder designed specifically for investigating neuroscience data, but can be applied widely to any time series data. In an unsupervised setting, LFADS is able to decompose time series data into various factors, such as an initial condition, a generative dynamical system, control inputs to that generator, and a low dimensional description of the observed data, called the factors. Additionally, the observation model is a loss on a probability distribution, so when LFADS processes a dataset, a denoised version of the dataset is also created. For example, if the dataset is raw spike counts, then under the negative log-likeihood loss under a Poisson distribution, the denoised data would be the inferred Poisson rates. + + +## Prerequisites + +The code is written in Python 2.7.6. You will also need: + +* **TensorFlow** version 1.0.1 or greater ([install](https://www.tensorflow.org/install/)) +* **NumPy, SciPy, Matplotlib** ([install SciPy stack](https://www.scipy.org/install.html), contains all of them) +* **h5py** ([install](https://pypi.python.org/pypi/h5py)) + + +## Getting started + +Before starting, run the following: + +

    +$ export PYTHONPATH=$PYTHONPATH:/path/to/your/directory/lfads/
    +
    + +where "path/to/your/directory" is replaced with the path to the LFADS repository (you can get this path by using the `pwd` command). This allows the nested directories to access modules from their parent directory. + +## Generate synthetic data + +In order to generate the synthetic datasets first, from the top-level lfads directory, run: + +```sh +$ cd synth_data +$ ./run_generate_synth_data.sh +$ cd .. +``` + +These synthetic datasets are provided 1. to gain insight into how the LFADS algorithm operates, and 2. to give reasonable starting points for analyses you might be interested for your own data. + +## Train an LFADS model + +Now that we have our example datasets, we can train some models! To spin up an LFADS model on the synthetic data, run any of the following commands. For the examples that are in the paper, the important hyperparameters are roughly replicated. Most hyperparameters are insensitive to small changes or won't ever be changed unless you want a very fine level of control. In the first example, all hyperparameter flags are enumerated for easy copy-pasting, but for the rest of the examples only the most important flags (~the first 8) are specified for brevity. For a full list of flags, their descriptions, and their default values, refer to the top of `run_lfads.py`. Please see Table 1 in the Online Methods of the associated paper for definitions of the most important hyperparameters. + +```sh +# Run LFADS on chaotic rnn data with no input pulses (g = 1.5) +$ python run_lfads.py --kind=train \ +--data_dir=/tmp/rnn_synth_data_v1.0/ \ +--data_filename_stem=chaotic_rnn_no_inputs \ +--lfads_save_dir=/tmp/lfads_chaotic_rnn_no_inputs \ +--co_dim=0 \ +--factors_dim=20 \ +--ext_input_dim=0 \ +--controller_input_lag=1 \ +--output_dist=poisson \ +--do_causal_controller=false \ +--batch_size=128 \ +--learning_rate_init=0.01 \ +--learning_rate_stop=1e-05 \ +--learning_rate_decay_factor=0.95 \ +--learning_rate_n_to_compare=6 \ +--do_reset_learning_rate=false \ +--keep_prob=0.95 \ +--con_dim=128 \ +--gen_dim=200 \ +--ci_enc_dim=128 \ +--ic_dim=64 \ +--ic_enc_dim=128 \ +--ic_prior_var_min=0.1 \ +--gen_cell_input_weight_scale=1.0 \ +--cell_weight_scale=1.0 \ +--do_feed_factors_to_controller=true \ +--kl_start_step=0 \ +--kl_increase_steps=2000 \ +--kl_ic_weight=1.0 \ +--l2_con_scale=0.0 \ +--l2_gen_scale=2000.0 \ +--l2_start_step=0 \ +--l2_increase_steps=2000 \ +--ic_prior_var_scale=0.1 \ +--ic_post_var_min=0.0001 \ +--kl_co_weight=1.0 \ +--prior_ar_nvar=0.1 \ +--cell_clip_value=5.0 \ +--max_ckpt_to_keep_lve=5 \ +--do_train_prior_ar_atau=true \ +--co_prior_var_scale=0.1 \ +--csv_log=fitlog \ +--feedback_factors_or_rates=factors \ +--do_train_prior_ar_nvar=true \ +--max_grad_norm=200.0 \ +--device=gpu:0 \ +--num_steps_for_gen_ic=100000000 \ +--ps_nexamples_to_process=100000000 \ +--checkpoint_name=lfads_vae \ +--temporal_spike_jitter_width=0 \ +--checkpoint_pb_load_name=checkpoint \ +--inject_ext_input_to_gen=false \ +--co_mean_corr_scale=0.0 \ +--gen_cell_rec_weight_scale=1.0 \ +--max_ckpt_to_keep=5 \ +--output_filename_stem="" \ +--ic_prior_var_max=0.1 \ +--prior_ar_atau=10.0 \ +--do_train_io_only=false + +# Run LFADS on chaotic rnn data with input pulses (g = 2.5) +$ python run_lfads.py --kind=train \ +--data_dir=/tmp/rnn_synth_data_v1.0/ \ +--data_filename_stem=chaotic_rnn_inputs_g2p5 \ +--lfads_save_dir=/tmp/lfads_chaotic_rnn_inputs_g2p5 \ +--co_dim=1 \ +--factors_dim=20 + +# Run LFADS on multi-session RNN data +$ python run_lfads.py --kind=train \ +--data_dir=/tmp/rnn_synth_data_v1.0/ \ +--data_filename_stem=chaotic_rnn_multisession \ +--lfads_save_dir=/tmp/lfads_chaotic_rnn_multisession \ +--factors_dim=10 + +# Run LFADS on integration to bound model data +$ python run_lfads.py --kind=train \ +--data_dir=/tmp/rnn_synth_data_v1.0/ \ +--data_filename_stem=itb_rnn \ +--lfads_save_dir=/tmp/lfads_itb_rnn \ +--co_dim=1 \ +--factors_dim=20 \ +--controller_input_lag=0 + +# Run LFADS on chaotic RNN data with labels +$ python run_lfads.py --kind=train \ +--data_dir=/tmp/rnn_synth_data_v1.0/ \ +--data_filename_stem=chaotic_rnns_labeled \ +--lfads_save_dir=/tmp/lfads_chaotic_rnns_labeled \ +--co_dim=0 \ +--factors_dim=20 \ +--controller_input_lag=0 \ +--ext_input_dim=1 + +``` + +**Tip**: If you are running LFADS on GPU and would like to run more than one model concurrently, set the `--allow_gpu_growth=True` flag on each job, otherwise one model will take up the entire GPU for performance purposes. Also, one needs to install the TensorFlow libraries with GPU support. + + +## Visualize a training model + +To visualize training curves and various other metrics while training and LFADS model, run the following command on your model directory. To launch a tensorboard on the chaotic RNN data with input pulses, for example: + +```sh +tensorboard --logdir=/tmp/lfads_chaotic_rnn_inputs_g2p5 +``` + +## Evaluate a trained model + +Once your model is finished training, there are multiple ways you can evaluate +it. Below are some sample commands to evaluate an LFADS model trained on the +chaotic rnn data with input pulses (g = 2.5). The key differences here are +setting the `--kind` flag to the appropriate mode, as well as the +`--checkpoint_pb_load_name` flag to `checkpoint_lve` and the `--batch_size` flag +(if you'd like to make it larger or smaller). All other flags should be the +same as used in training, so that the same model architecture is built. + +```sh +# Take samples from posterior then average (denoising operation) +$ python run_lfads.py --kind=posterior_sample_and_average \ +--data_dir=/tmp/rnn_synth_data_v1.0/ \ +--data_filename_stem=chaotic_rnn_inputs_g2p5 \ +--lfads_save_dir=/tmp/lfads_chaotic_rnn_inputs_g2p5 \ +--co_dim=1 \ +--factors_dim=20 \ +--batch_size=1024 \ +--checkpoint_pb_load_name=checkpoint_lve + +# Sample from prior (generation of completely new samples) +$ python run_lfads.py --kind=prior_sample \ +--data_dir=/tmp/rnn_synth_data_v1.0/ \ +--data_filename_stem=chaotic_rnn_inputs_g2p5 \ +--lfads_save_dir=/tmp/lfads_chaotic_rnn_inputs_g2p5 \ +--co_dim=1 \ +--factors_dim=20 \ +--batch_size=50 \ +--checkpoint_pb_load_name=checkpoint_lve + +# Write down model parameters +$ python run_lfads.py --kind=write_model_params \ +--data_dir=/tmp/rnn_synth_data_v1.0/ \ +--data_filename_stem=chaotic_rnn_inputs_g2p5 \ +--lfads_save_dir=/tmp/lfads_chaotic_rnn_inputs_g2p5 \ +--co_dim=1 \ +--factors_dim=20 \ +--checkpoint_pb_load_name=checkpoint_lve +``` + +## Contact + +File any issues with the [issue tracker](https://github.com/tensorflow/models/issues). For any questions or problems, this code is maintained by [@sussillo](https://github.com/sussillo) and [@jazcollins](https://github.com/jazcollins). + diff --git a/lfads/distributions.py b/lfads/distributions.py new file mode 100644 index 000000000..56f14cfe3 --- /dev/null +++ b/lfads/distributions.py @@ -0,0 +1,493 @@ +# Copyright 2017 Google Inc. 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 numpy as np +import tensorflow as tf +from utils import linear, log_sum_exp + +class Poisson(object): + """Poisson distributon + + Computes the log probability under the model. + + """ + def __init__(self, log_rates): + """ Create Poisson distributions with log_rates parameters. + + Args: + log_rates: a tensor-like list of log rates underlying the Poisson dist. + """ + self.logr = log_rates + + def logp(self, bin_counts): + """Compute the log probability for the counts in the bin, under the model. + + Args: + bin_counts: array-like integer counts + + Returns: + The log-probability under the Poisson models for each element of + bin_counts. + """ + k = tf.to_float(bin_counts) + # log poisson(k, r) = log(r^k * e^(-r) / k!) = k log(r) - r - log k! + # log poisson(k, r=exp(x)) = k * x - exp(x) - lgamma(k + 1) + return k * self.logr - tf.exp(self.logr) - tf.lgamma(k + 1) + + +def diag_gaussian_log_likelihood(z, mu=0.0, logvar=0.0): + """Log-likelihood under a Gaussian distribution with diagonal covariance. + Returns the log-likelihood for each dimension. One should sum the + results for the log-likelihood under the full multidimensional model. + + Args: + z: The value to compute the log-likelihood. + mu: The mean of the Gaussian + logvar: The log variance of the Gaussian. + + Returns: + The log-likelihood under the Gaussian model. + """ + + return -0.5 * (logvar + np.log(2*np.pi) + \ + tf.square((z-mu)/tf.exp(0.5*logvar))) + + +def gaussian_pos_log_likelihood(unused_mean, logvar, noise): + """Gaussian log-likelihood function for a posterior in VAE + + Note: This function is specialized for a posterior distribution, that has the + form of z = mean + sigma * noise. + + Args: + unused_mean: ignore + logvar: The log variance of the distribution + noise: The noise used in the sampling of the posterior. + + Returns: + The log-likelihood under the Gaussian model. + """ + # ln N(z; mean, sigma) = - ln(sigma) - 0.5 ln 2pi - noise^2 / 2 + return - 0.5 * (logvar + np.log(2 * np.pi) + tf.square(noise)) + + +class Gaussian(object): + """Base class for Gaussian distribution classes.""" + pass + + +class DiagonalGaussian(Gaussian): + """Diagonal Gaussian with different constant mean and variances in each + dimension. + """ + + def __init__(self, batch_size, z_size, mean, logvar): + """Create a diagonal gaussian distribution. + + Args: + batch_size: The size of the batch, i.e. 0th dim in 2D tensor of samples. + z_size: The dimension of the distribution, i.e. 1st dim in 2D tensor. + mean: The N-D mean of the distribution. + logvar: The N-D log variance of the diagonal distribution. + """ + size__xz = [None, z_size] + self.mean = mean # bxn already + self.logvar = logvar # bxn already + self.noise = noise = tf.random_normal(tf.shape(logvar)) + self.sample = mean + tf.exp(0.5 * logvar) * noise + mean.set_shape(size__xz) + logvar.set_shape(size__xz) + self.sample.set_shape(size__xz) + + def logp(self, z=None): + """Compute the log-likelihood under the distribution. + + Args: + z (optional): value to compute likelihood for, if None, use sample. + + Returns: + The likelihood of z under the model. + """ + if z is None: + z = self.sample + + # This is needed to make sure that the gradients are simple. + # The value of the function shouldn't change. + if z == self.sample: + return gaussian_pos_log_likelihood(self.mean, self.logvar, self.noise) + + return diag_gaussian_log_likelihood(z, self.mean, self.logvar) + + +class LearnableDiagonalGaussian(Gaussian): + """Diagonal Gaussian whose mean and variance are learned parameters.""" + + def __init__(self, batch_size, z_size, name, mean_init=0.0, + var_init=1.0, var_min=0.0, var_max=1000000.0): + """Create a learnable diagonal gaussian distribution. + + Args: + batch_size: The size of the batch, i.e. 0th dim in 2D tensor of samples. + z_size: The dimension of the distribution, i.e. 1st dim in 2D tensor. + name: prefix name for the mean and log TF variables. + mean_init (optional): The N-D mean initialization of the distribution. + var_init (optional): The N-D variance initialization of the diagonal + distribution. + var_min (optional): The minimum value the learned variance can take in any + dimension. + var_max (optional): The maximum value the learned variance can take in any + dimension. + """ + + size_1xn = [1, z_size] + size__xn = [None, z_size] + size_bx1 = tf.stack([batch_size, 1]) + assert var_init > 0.0, "Problems" + assert var_max >= var_min, "Problems" + assert var_init >= var_min, "Problems" + assert var_max >= var_init, "Problems" + + + z_mean_1xn = tf.get_variable(name=name+"/mean", shape=size_1xn, + initializer=tf.constant_initializer(mean_init)) + self.mean_bxn = mean_bxn = tf.tile(z_mean_1xn, size_bx1) + mean_bxn.set_shape(size__xn) # tile loses shape + + log_var_init = np.log(var_init) + if var_max > var_min: + var_is_trainable = True + else: + var_is_trainable = False + + z_logvar_1xn = \ + tf.get_variable(name=(name+"/logvar"), shape=size_1xn, + initializer=tf.constant_initializer(log_var_init), + trainable=var_is_trainable) + + if var_is_trainable: + z_logit_var_1xn = tf.exp(z_logvar_1xn) + z_var_1xn = tf.nn.sigmoid(z_logit_var_1xn)*(var_max-var_min) + var_min + z_logvar_1xn = tf.log(z_var_1xn) + + logvar_bxn = tf.tile(z_logvar_1xn, size_bx1) + self.logvar_bxn = logvar_bxn + self.noise_bxn = noise_bxn = tf.random_normal(tf.shape(logvar_bxn)) + self.sample_bxn = mean_bxn + tf.exp(0.5 * logvar_bxn) * noise_bxn + + def logp(self, z=None): + """Compute the log-likelihood under the distribution. + + Args: + z (optional): value to compute likelihood for, if None, use sample. + + Returns: + The likelihood of z under the model. + """ + if z is None: + z = self.sample + + # This is needed to make sure that the gradients are simple. + # The value of the function shouldn't change. + if z == self.sample_bxn: + return gaussian_pos_log_likelihood(self.mean_bxn, self.logvar_bxn, + self.noise_bxn) + + return diag_gaussian_log_likelihood(z, self.mean_bxn, self.logvar_bxn) + + @property + def mean(self): + return self.mean_bxn + + @property + def logvar(self): + return self.logvar_bxn + + @property + def sample(self): + return self.sample_bxn + + +class DiagonalGaussianFromInput(Gaussian): + """Diagonal Gaussian whose mean and variance are conditioned on other + variables. + + Note: the parameters to convert from input to the learned mean and log + variance are held in this class. + """ + + def __init__(self, x_bxu, z_size, name, var_min=0.0): + """Create an input dependent diagonal Gaussian distribution. + + Args: + x: The input tensor from which the mean and variance are computed, + via a linear transformation of x. I.e. + mu = Wx + b, log(var) = Mx + c + z_size: The size of the distribution. + name: The name to prefix to learned variables. + var_min (optional): Minimal variance allowed. This is an additional + way to control the amount of information getting through the stochastic + layer. + """ + size_bxn = tf.stack([tf.shape(x_bxu)[0], z_size]) + self.mean_bxn = mean_bxn = linear(x_bxu, z_size, name=(name+"/mean")) + logvar_bxn = linear(x_bxu, z_size, name=(name+"/logvar")) + if var_min > 0.0: + logvar_bxn = tf.log(tf.exp(logvar_bxn) + var_min) + self.logvar_bxn = logvar_bxn + + self.noise_bxn = noise_bxn = tf.random_normal(size_bxn) + self.noise_bxn.set_shape([None, z_size]) + self.sample_bxn = mean_bxn + tf.exp(0.5 * logvar_bxn) * noise_bxn + + def logp(self, z=None): + """Compute the log-likelihood under the distribution. + + Args: + z (optional): value to compute likelihood for, if None, use sample. + + Returns: + The likelihood of z under the model. + """ + + if z is None: + z = self.sample + + # This is needed to make sure that the gradients are simple. + # The value of the function shouldn't change. + if z == self.sample_bxn: + return gaussian_pos_log_likelihood(self.mean_bxn, + self.logvar_bxn, self.noise_bxn) + + return diag_gaussian_log_likelihood(z, self.mean_bxn, self.logvar_bxn) + + @property + def mean(self): + return self.mean_bxn + + @property + def logvar(self): + return self.logvar_bxn + + @property + def sample(self): + return self.sample_bxn + + +class GaussianProcess: + """Base class for Gaussian processes.""" + pass + + +class LearnableAutoRegressive1Prior(GaussianProcess): + """AR(1) model where autocorrelation and process variance are learned + parameters. Assumed zero mean. + + """ + + def __init__(self, batch_size, z_size, + autocorrelation_taus, noise_variances, + do_train_prior_ar_atau, do_train_prior_ar_nvar, + num_steps, name): + """Create a learnable autoregressive (1) process. + + Args: + batch_size: The size of the batch, i.e. 0th dim in 2D tensor of samples. + z_size: The dimension of the distribution, i.e. 1st dim in 2D tensor. + autocorrelation_taus: The auto correlation time constant of the AR(1) + process. + A value of 0 is uncorrelated gaussian noise. + noise_variances: The variance of the additive noise, *not* the process + variance. + do_train_prior_ar_atau: Train or leave as constant, the autocorrelation? + do_train_prior_ar_nvar: Train or leave as constant, the noise variance? + num_steps: Number of steps to run the process. + name: The name to prefix to learned TF variables. + """ + + # Note the use of the plural in all of these quantities. This is intended + # to mark that even though a sample z_t from the posterior is thought of a + # single sample of a multidimensional gaussian, the prior is actually + # thought of as U AR(1) processes, where U is the dimension of the inferred + # input. + size_bx1 = tf.stack([batch_size, 1]) + size__xu = [None, z_size] + # process variance, the variance at time t over all instantiations of AR(1) + # with these parameters. + log_evar_inits_1xu = tf.expand_dims(tf.log(noise_variances), 0) + self.logevars_1xu = logevars_1xu = \ + tf.Variable(log_evar_inits_1xu, name=name+"/logevars", dtype=tf.float32, + trainable=do_train_prior_ar_nvar) + self.logevars_bxu = logevars_bxu = tf.tile(logevars_1xu, size_bx1) + logevars_bxu.set_shape(size__xu) # tile loses shape + + # \tau, which is the autocorrelation time constant of the AR(1) process + log_atau_inits_1xu = tf.expand_dims(tf.log(autocorrelation_taus), 0) + self.logataus_1xu = logataus_1xu = \ + tf.Variable(log_atau_inits_1xu, name=name+"/logatau", dtype=tf.float32, + trainable=do_train_prior_ar_atau) + + # phi in x_t = \mu + phi x_tm1 + \eps + # phi = exp(-1/tau) + # phi = exp(-1/exp(logtau)) + # phi = exp(-exp(-logtau)) + phis_1xu = tf.exp(-tf.exp(-logataus_1xu)) + self.phis_bxu = phis_bxu = tf.tile(phis_1xu, size_bx1) + phis_bxu.set_shape(size__xu) + + # process noise + # pvar = evar / (1- phi^2) + # logpvar = log ( exp(logevar) / (1 - phi^2) ) + # logpvar = logevar - log(1-phi^2) + # logpvar = logevar - (log(1-phi) + log(1+phi)) + self.logpvars_1xu = \ + logevars_1xu - tf.log(1.0-phis_1xu) - tf.log(1.0+phis_1xu) + self.logpvars_bxu = logpvars_bxu = tf.tile(self.logpvars_1xu, size_bx1) + logpvars_bxu.set_shape(size__xu) + + # process mean (zero but included in for completeness) + self.pmeans_bxu = pmeans_bxu = tf.zeros_like(phis_bxu) + + # For sampling from the prior during de-novo generation. + self.means_t = means_t = [None] * num_steps + self.logvars_t = logvars_t = [None] * num_steps + self.samples_t = samples_t = [None] * num_steps + self.gaussians_t = gaussians_t = [None] * num_steps + sample_bxu = tf.zeros_like(phis_bxu) + for t in range(num_steps): + # process variance used here to make process completely stationary + if t == 0: + logvar_pt_bxu = self.logpvars_bxu + else: + logvar_pt_bxu = self.logevars_bxu + + z_mean_pt_bxu = pmeans_bxu + phis_bxu * sample_bxu + gaussians_t[t] = DiagonalGaussian(batch_size, z_size, + mean=z_mean_pt_bxu, + logvar=logvar_pt_bxu) + sample_bxu = gaussians_t[t].sample + samples_t[t] = sample_bxu + logvars_t[t] = logvar_pt_bxu + means_t[t] = z_mean_pt_bxu + + def logp_t(self, z_t_bxu, z_tm1_bxu=None): + """Compute the log-likelihood under the distribution for a given time t, + not the whole sequence. + + Args: + z_t_bxu: sample to compute likelihood for at time t. + z_tm1_bxu (optional): sample condition probability of z_t upon. + + Returns: + The likelihood of p_t under the model at time t. i.e. + p(z_t|z_tm1) = N(z_tm1 * phis, eps^2) + + """ + if z_tm1_bxu is None: + return diag_gaussian_log_likelihood(z_t_bxu, self.pmeans_bxu, + self.logpvars_bxu) + else: + means_t_bxu = self.pmeans_bxu + self.phis_bxu * z_tm1_bxu + logp_tgtm1_bxu = diag_gaussian_log_likelihood(z_t_bxu, + means_t_bxu, + self.logevars_bxu) + return logp_tgtm1_bxu + + +class KLCost_GaussianGaussian(object): + """log p(x|z) + KL(q||p) terms for Gaussian posterior and Gaussian prior. See + eqn 10 and Appendix B in VAE for latter term, + http://arxiv.org/abs/1312.6114 + + The log p(x|z) term is the reconstruction error under the model. + The KL term represents the penalty for passing information from the encoder + to the decoder. + To sample KL(q||p), we simply sample + ln q - ln p + by drawing samples from q and averaging. + """ + + def __init__(self, zs, prior_zs): + """Create a lower bound in three parts, normalized reconstruction + cost, normalized KL divergence cost, and their sum. + + E_q[ln p(z_i | z_{i+1}) / q(z_i | x) + \int q(z) ln p(z) dz = - 0.5 ln(2pi) - 0.5 \sum (ln(sigma_p^2) + \ + sigma_q^2 / sigma_p^2 + (mean_p - mean_q)^2 / sigma_p^2) + + \int q(z) ln q(z) dz = - 0.5 ln(2pi) - 0.5 \sum (ln(sigma_q^2) + 1) + + Args: + zs: posterior z ~ q(z|x) + prior_zs: prior zs + """ + # L = -KL + log p(x|z), to maximize bound on likelihood + # -L = KL - log p(x|z), to minimize bound on NLL + # so 'KL cost' is postive KL divergence + kl_b = 0.0 + for z, prior_z in zip(zs, prior_zs): + assert isinstance(z, Gaussian) + assert isinstance(prior_z, Gaussian) + # ln(2pi) terms cancel + kl_b += 0.5 * tf.reduce_sum( + prior_z.logvar - z.logvar + + tf.exp(z.logvar - prior_z.logvar) + + tf.square((z.mean - prior_z.mean) / tf.exp(0.5 * prior_z.logvar)) + - 1.0, [1]) + + self.kl_cost_b = kl_b + self.kl_cost = tf.reduce_mean(kl_b) + + +class KLCost_GaussianGaussianProcessSampled(object): + """ log p(x|z) + KL(q||p) terms for Gaussian posterior and Gaussian process + prior via sampling. + + The log p(x|z) term is the reconstruction error under the model. + The KL term represents the penalty for passing information from the encoder + to the decoder. + To sample KL(q||p), we simply sample + ln q - ln p + by drawing samples from q and averaging. + """ + + def __init__(self, post_zs, prior_z_process): + """Create a lower bound in three parts, normalized reconstruction + cost, normalized KL divergence cost, and their sum. + + Args: + post_zs: posterior z ~ q(z|x) + prior_z_process: prior AR(1) process + """ + assert len(post_zs) > 1, "GP is for time, need more than 1 time step." + assert isinstance(prior_z_process, GaussianProcess), "Must use GP." + + # L = -KL + log p(x|z), to maximize bound on likelihood + # -L = KL - log p(x|z), to minimize bound on NLL + # so 'KL cost' is postive KL divergence + z0_bxu = post_zs[0].sample + logq_bxu = post_zs[0].logp(z0_bxu) + logp_bxu = prior_z_process.logp_t(z0_bxu) + z_tm1_bxu = z0_bxu + for z_t in post_zs[1:]: + # posterior is independent in time, prior is not + z_t_bxu = z_t.sample + logq_bxu += z_t.logp(z_t_bxu) + logp_bxu += prior_z_process.logp_t(z_t_bxu, z_tm1_bxu) + z_tm1 = z_t_bxu + + kl_bxu = logq_bxu - logp_bxu + kl_b = tf.reduce_sum(kl_bxu, [1]) + self.kl_cost_b = kl_b + self.kl_cost = tf.reduce_mean(kl_b) diff --git a/lfads/lfads.py b/lfads/lfads.py new file mode 100644 index 000000000..bc8f2bbaf --- /dev/null +++ b/lfads/lfads.py @@ -0,0 +1,1935 @@ +# Copyright 2017 Google Inc. 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. +# +# ============================================================================== +""" +LFADS - Latent Factor Analysis via Dynamical Systems. + +LFADS is an unsupervised method to decompose time series data into +various factors, such as an initial condition, a generative +dynamical system, control inputs to that generator, and a low +dimensional description of the observed data, called the factors. +Additionally, the observations have a noise model (in this case +Poisson), so a denoised version of the observations is also created +(e.g. underlying rates of a Poisson distribution given the observed +event counts). + +The main data structure being passed around is a dataset. This is a dictionary +of data dictionaries. + +DATASET: The top level dictionary is simply name (string -> dictionary). +The nested dictionary is the DATA DICTIONARY, which has the following keys: + 'train_data' and 'valid_data', whose values are the corresponding training + and validation data with shape + ExTxD, E - # examples, T - # time steps, D - # dimensions in data. + The data dictionary also has a few more keys: + 'train_ext_input' and 'valid_ext_input', if there are know external inputs + to the system being modeled, these take on dimensions: + ExTxI, E - # examples, T - # time steps, I = # dimensions in input. + 'alignment_matrix_cxf' - If you are using multiple days data, it's possible + that one can align the channels (see manuscript). If so each dataset will + contain this matrix, which will be used for both the input adapter and the + output adapter for each dataset. These matrices, if provided, must be of + size [data_dim x factors] where data_dim is the number of neurons recorded + on that day, and factors is chosen and set through the '--factors' flag. + + If one runs LFADS on data where the true rates are known for some trials, + (say simulated, testing data, as in the example shipped with the paper), then + one can add three more fields for plotting purposes. These are 'train_truth' + and 'valid_truth', and 'conversion_factor'. These have the same dimensions as + 'train_data', and 'valid_data' but represent the underlying rates of the + observations. Finally, if one needs to convert scale for plotting the true + underlying firing rates, there is the 'conversion_factor' key. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + + +import numpy as np +import os +import tensorflow as tf +from distributions import LearnableDiagonalGaussian, DiagonalGaussianFromInput +from distributions import diag_gaussian_log_likelihood +from distributions import KLCost_GaussianGaussian, Poisson +from distributions import LearnableAutoRegressive1Prior +from distributions import KLCost_GaussianGaussianProcessSampled + +from utils import init_linear, linear, list_t_bxn_to_tensor_bxtxn, write_data +from utils import log_sum_exp, flatten +from plot_lfads import plot_lfads + + +class GRU(object): + """Gated Recurrent Unit cell (cf. http://arxiv.org/abs/1406.1078). + + """ + def __init__(self, num_units, forget_bias=1.0, weight_scale=1.0, + clip_value=np.inf, collections=None): + """Create a GRU object. + + Args: + num_units: Number of units in the GRU + forget_bias (optional): Hack to help learning. + weight_scale (optional): weights are scaled by ws/sqrt(#inputs), with + ws being the weight scale. + clip_value (optional): if the recurrent values grow above this value, + clip them. + collections (optional): List of additonal collections variables should + belong to. + """ + self._num_units = num_units + self._forget_bias = forget_bias + self._weight_scale = weight_scale + self._clip_value = clip_value + self._collections = collections + + @property + def state_size(self): + return self._num_units + + @property + def output_size(self): + return self._num_units + + @property + def state_multiplier(self): + return 1 + + def output_from_state(self, state): + """Return the output portion of the state.""" + return state + + def __call__(self, inputs, state, scope=None): + """Gated recurrent unit (GRU) function. + + Args: + inputs: A 2D batch x input_dim tensor of inputs. + state: The previous state from the last time step. + scope (optional): TF variable scope for defined GRU variables. + + Returns: + A tuple (state, state), where state is the newly computed state at time t. + It is returned twice to respect an interface that works for LSTMs. + """ + + x = inputs + h = state + if inputs is not None: + xh = tf.concat(axis=1, values=[x, h]) + else: + xh = h + + with tf.variable_scope(scope or type(self).__name__): # "GRU" + with tf.variable_scope("Gates"): # Reset gate and update gate. + # We start with bias of 1.0 to not reset and not update. + r, u = tf.split(axis=1, num_or_size_splits=2, value=linear(xh, + 2 * self._num_units, + alpha=self._weight_scale, + name="xh_2_ru", + collections=self._collections)) + r, u = tf.sigmoid(r), tf.sigmoid(u + self._forget_bias) + with tf.variable_scope("Candidate"): + xrh = tf.concat(axis=1, values=[x, r * h]) + c = tf.tanh(linear(xrh, self._num_units, name="xrh_2_c", + collections=self._collections)) + new_h = u * h + (1 - u) * c + new_h = tf.clip_by_value(new_h, -self._clip_value, self._clip_value) + + return new_h, new_h + + +class GenGRU(object): + """Gated Recurrent Unit cell (cf. http://arxiv.org/abs/1406.1078). + + This version is specialized for the generator, but isn't as fast, so + we have two. Note this allows for l2 regularization on the recurrent + weights, but also implicitly rescales the inputs via the 1/sqrt(input) + scaling in the linear helper routine to be large magnitude, if there are + fewer inputs than recurrent state. + + """ + def __init__(self, num_units, forget_bias=1.0, + input_weight_scale=1.0, rec_weight_scale=1.0, clip_value=np.inf, + input_collections=None, recurrent_collections=None): + """Create a GRU object. + + Args: + num_units: Number of units in the GRU + forget_bias (optional): Hack to help learning. + input_weight_scale (optional): weights are scaled ws/sqrt(#inputs), with + ws being the weight scale. + rec_weight_scale (optional): weights are scaled ws/sqrt(#inputs), + with ws being the weight scale. + clip_value (optional): if the recurrent values grow above this value, + clip them. + input_collections (optional): List of additonal collections variables + that input->rec weights should belong to. + recurrent_collections (optional): List of additonal collections variables + that rec->rec weights should belong to. + """ + self._num_units = num_units + self._forget_bias = forget_bias + self._input_weight_scale = input_weight_scale + self._rec_weight_scale = rec_weight_scale + self._clip_value = clip_value + self._input_collections = input_collections + self._rec_collections = recurrent_collections + + @property + def state_size(self): + return self._num_units + + @property + def output_size(self): + return self._num_units + + @property + def state_multiplier(self): + return 1 + + def output_from_state(self, state): + """Return the output portion of the state.""" + return state + + def __call__(self, inputs, state, scope=None): + """Gated recurrent unit (GRU) function. + + Args: + inputs: A 2D batch x input_dim tensor of inputs. + state: The previous state from the last time step. + scope (optional): TF variable scope for defined GRU variables. + + Returns: + A tuple (state, state), where state is the newly computed state at time t. + It is returned twice to respect an interface that works for LSTMs. + """ + + x = inputs + h = state + with tf.variable_scope(scope or type(self).__name__): # "GRU" + with tf.variable_scope("Gates"): # Reset gate and update gate. + # We start with bias of 1.0 to not reset and not update. + r_x = u_x = 0.0 + if x is not None: + r_x, u_x = tf.split(axis=1, num_or_size_splits=2, value=linear(x, + 2 * self._num_units, + alpha=self._input_weight_scale, + do_bias=False, + name="x_2_ru", + normalized=False, + collections=self._input_collections)) + + r_h, u_h = tf.split(axis=1, num_or_size_splits=2, value=linear(h, + 2 * self._num_units, + do_bias=True, + alpha=self._rec_weight_scale, + name="h_2_ru", + collections=self._rec_collections)) + r = r_x + r_h + u = u_x + u_h + r, u = tf.sigmoid(r), tf.sigmoid(u + self._forget_bias) + + with tf.variable_scope("Candidate"): + c_x = 0.0 + if x is not None: + c_x = linear(x, self._num_units, name="x_2_c", do_bias=False, + alpha=self._input_weight_scale, + normalized=False, + collections=self._input_collections) + c_rh = linear(r*h, self._num_units, name="rh_2_c", do_bias=True, + alpha=self._rec_weight_scale, + collections=self._rec_collections) + c = tf.tanh(c_x + c_rh) + + new_h = u * h + (1 - u) * c + new_h = tf.clip_by_value(new_h, -self._clip_value, self._clip_value) + + return new_h, new_h + + +class LFADS(object): + """LFADS - Latent Factor Analysis via Dynamical Systems. + + LFADS is an unsupervised method to decompose time series data into + various factors, such as an initial condition, a generative + dynamical system, inferred inputs to that generator, and a low + dimensional description of the observed data, called the factors. + Additoinally, the observations have a noise model (in this case + Poisson), so a denoised version of the observations is also created + (e.g. underlying rates of a Poisson distribution given the observed + event counts). + """ + + def __init__(self, hps, kind="train", datasets=None): + """Create an LFADS model. + + train - a model for training, sampling of posteriors is used + posterior_sample_and_average - sample from the posterior, this is used + for evaluating the expected value of the outputs of LFADS, given a + specific input, by averaging over multiple samples from the approx + posterior. Also used for the lower bound on the negative + log-likelihood using IWAE error (Importance Weighed Auto-encoder). + This is the denoising operation. + prior_sample - a model for generation - sampling from priors is used + + Args: + hps: The dictionary of hyper parameters. + kind: the type of model to build (see above). + datasets: a dictionary of named data_dictionaries, see top of lfads.py + """ + print("Building graph...") + all_kinds = ['train', 'posterior_sample_and_average', 'prior_sample'] + assert kind in all_kinds, 'Wrong kind' + if hps.feedback_factors_or_rates == "rates": + assert len(hps.dataset_names) == 1, \ + "Multiple datasets not supported for rate feedback." + num_steps = hps.num_steps + ic_dim = hps.ic_dim + co_dim = hps.co_dim + ext_input_dim = hps.ext_input_dim + cell_class = GRU + gen_cell_class = GenGRU + + def makelambda(v): # Used with tf.case + return lambda: v + + # Define the data placeholder, and deal with all parts of the graph + # that are dataset dependent. + self.dataName = tf.placeholder(tf.string, shape=()) + # The batch_size to be inferred from data, as normal. + # Additionally, the data_dim will be inferred as well, allowing for a + # single placeholder for all datasets, regardless of data dimension. + if hps.output_dist == 'poisson': + # Enforce correct dtype + assert np.issubdtype( + datasets[hps.dataset_names[0]]['train_data'].dtype, int), \ + "Data dtype must be int for poisson output distribution" + data_dtype = tf.int32 + elif hps.output_dist == 'gaussian': + assert np.issubdtype( + datasets[hps.dataset_names[0]]['train_data'].dtype, float), \ + "Data dtype must be float for gaussian output dsitribution" + data_dtype = tf.float32 + else: + assert False, "NIY" + self.dataset_ph = dataset_ph = tf.placeholder(data_dtype, + [None, num_steps, None], + name="data") + self.train_step = tf.get_variable("global_step", [], tf.int64, + tf.zeros_initializer(), + trainable=False) + self.hps = hps + ndatasets = hps.ndatasets + factors_dim = hps.factors_dim + self.preds = preds = [None] * ndatasets + self.fns_in_fac_Ws = fns_in_fac_Ws = [None] * ndatasets + self.fns_in_fatcor_bs = fns_in_fac_bs = [None] * ndatasets + self.fns_out_fac_Ws = fns_out_fac_Ws = [None] * ndatasets + self.fns_out_fac_bs = fns_out_fac_bs = [None] * ndatasets + self.datasetNames = dataset_names = hps.dataset_names + self.ext_inputs = ext_inputs = None + + if len(dataset_names) == 1: # single session + if 'alignment_matrix_cxf' in datasets[dataset_names[0]].keys(): + used_in_factors_dim = factors_dim + in_identity_if_poss = False + else: + used_in_factors_dim = hps.dataset_dims[dataset_names[0]] + in_identity_if_poss = True + else: # multisession + used_in_factors_dim = factors_dim + in_identity_if_poss = False + + for d, name in enumerate(dataset_names): + data_dim = hps.dataset_dims[name] + in_mat_cxf = None + if datasets and 'alignment_matrix_cxf' in datasets[name].keys(): + dataset = datasets[name] + print("Using alignment matrix provided for dataset:", name) + in_mat_cxf = dataset['alignment_matrix_cxf'].astype(np.float32) + if in_mat_cxf.shape != (data_dim, factors_dim): + raise ValueError("""Alignment matrix must have dimensions %d x %d + (data_dim x factors_dim), but currently has %d x %d."""% + (data_dim, factors_dim, in_mat_cxf.shape[0], + in_mat_cxf.shape[1])) + + in_fac_lin = init_linear(data_dim, used_in_factors_dim, do_bias=True, + mat_init_value=in_mat_cxf, + identity_if_possible=in_identity_if_poss, + normalized=False, name="x_2_infac_"+name, + collections=['IO_transformations']) + in_fac_W, in_fac_b = in_fac_lin + fns_in_fac_Ws[d] = makelambda(in_fac_W) + fns_in_fac_bs[d] = makelambda(in_fac_b) + + with tf.variable_scope("glm"): + out_identity_if_poss = False + if len(dataset_names) == 1 and \ + factors_dim == hps.dataset_dims[dataset_names[0]]: + out_identity_if_poss = True + for d, name in enumerate(dataset_names): + data_dim = hps.dataset_dims[name] + in_mat_cxf = None + if datasets and 'alignment_matrix_cxf' in datasets[name].keys(): + dataset = datasets[name] + in_mat_cxf = dataset['alignment_matrix_cxf'].astype(np.float32) + + out_mat_cxf = None + if in_mat_cxf is not None: + out_mat_cxf = in_mat_cxf.T + + if hps.output_dist == 'poisson': + out_fac_lin = init_linear(factors_dim, data_dim, do_bias=True, + mat_init_value=out_mat_cxf, + identity_if_possible=out_identity_if_poss, + normalized=False, + name="fac_2_logrates_"+name, + collections=['IO_transformations']) + out_fac_W, out_fac_b = out_fac_lin + + elif hps.output_dist == 'gaussian': + out_fac_lin_mean = \ + init_linear(factors_dim, data_dim, do_bias=True, + mat_init_value=out_mat_cxf, + normalized=False, + name="fac_2_means_"+name, + collections=['IO_transformations']) + out_fac_lin_logvar = \ + init_linear(factors_dim, data_dim, do_bias=True, + mat_init_value=out_mat_cxf, + normalized=False, + name="fac_2_logvars_"+name, + collections=['IO_transformations']) + out_fac_W_mean, out_fac_b_mean = out_fac_lin_mean + out_fac_W_logvar, out_fac_b_logvar = out_fac_lin_logvar + out_fac_W = tf.concat( + axis=1, values=[out_fac_W_mean, out_fac_W_logvar]) + out_fac_b = tf.concat( + axis=1, values=[out_fac_b_mean, out_fac_b_logvar]) + else: + assert False, "NIY" + + preds[d] = tf.equal(tf.constant(name), self.dataName) + data_dim = hps.dataset_dims[name] + fns_out_fac_Ws[d] = makelambda(out_fac_W) + fns_out_fac_bs[d] = makelambda(out_fac_b) + + pf_pairs_in_fac_Ws = zip(preds, fns_in_fac_Ws) + pf_pairs_in_fac_bs = zip(preds, fns_in_fac_bs) + pf_pairs_out_fac_Ws = zip(preds, fns_out_fac_Ws) + pf_pairs_out_fac_bs = zip(preds, fns_out_fac_bs) + + case_default = lambda: tf.constant([-8675309.0]) + this_in_fac_W = tf.case(pf_pairs_in_fac_Ws, case_default, exclusive=True) + this_in_fac_b = tf.case(pf_pairs_in_fac_bs, case_default, exclusive=True) + this_out_fac_W = tf.case(pf_pairs_out_fac_Ws, case_default, exclusive=True) + this_out_fac_b = tf.case(pf_pairs_out_fac_bs, case_default, exclusive=True) + + # External inputs (not changing by dataset, by definition). + if hps.ext_input_dim > 0: + self.ext_input = tf.placeholder(tf.float32, + [None, num_steps, ext_input_dim], + name="ext_input") + else: + self.ext_input = None + ext_input_bxtxi = self.ext_input + + self.keep_prob = keep_prob = tf.placeholder(tf.float32, [], "keep_prob") + self.batch_size = batch_size = int(hps.batch_size) + self.learning_rate = tf.Variable(float(hps.learning_rate_init), + trainable=False, name="learning_rate") + self.learning_rate_decay_op = self.learning_rate.assign( + self.learning_rate * hps.learning_rate_decay_factor) + + # Dropout the data. + dataset_do_bxtxd = tf.nn.dropout(tf.to_float(dataset_ph), keep_prob) + if hps.ext_input_dim > 0: + ext_input_do_bxtxi = tf.nn.dropout(ext_input_bxtxi, keep_prob) + else: + ext_input_do_bxtxi = None + + # ENCODERS + def encode_data(dataset_bxtxd, enc_cell, name, forward_or_reverse, + num_steps_to_encode): + """Encode data for LFADS + Args: + dataset_bxtxd - the data to encode, as a 3 tensor, with dims + time x batch x data dims. + enc_cell: encoder cell + name: name of encoder + forward_or_reverse: string, encode in forward or reverse direction + num_steps_to_encode: number of steps to encode, 0:num_steps_to_encode + Returns: + encoded data as a list with num_steps_to_encode items, in order + """ + if forward_or_reverse == "forward": + dstr = "_fwd" + time_fwd_or_rev = range(num_steps_to_encode) + else: + dstr = "_rev" + time_fwd_or_rev = reversed(range(num_steps_to_encode)) + + with tf.variable_scope(name+"_enc"+dstr, reuse=False): + enc_state = tf.tile( + tf.Variable(tf.zeros([1, enc_cell.state_size]), + name=name+"_enc_t0"+dstr), tf.stack([batch_size, 1])) + enc_state.set_shape([None, enc_cell.state_size]) # tile loses shape + + enc_outs = [None] * num_steps_to_encode + for i, t in enumerate(time_fwd_or_rev): + with tf.variable_scope(name+"_enc"+dstr, reuse=True if i > 0 else None): + dataset_t_bxd = dataset_bxtxd[:,t,:] + in_fac_t_bxf = tf.matmul(dataset_t_bxd, this_in_fac_W) + this_in_fac_b + in_fac_t_bxf.set_shape([None, used_in_factors_dim]) + if ext_input_dim > 0 and not hps.inject_ext_input_to_gen: + ext_input_t_bxi = ext_input_do_bxtxi[:,t,:] + enc_input_t_bxfpe = tf.concat( + axis=1, values=[in_fac_t_bxf, ext_input_t_bxi]) + else: + enc_input_t_bxfpe = in_fac_t_bxf + enc_out, enc_state = enc_cell(enc_input_t_bxfpe, enc_state) + enc_outs[t] = enc_out + + return enc_outs + + # Encode initial condition means and variances + # ([x_T, x_T-1, ... x_0] and [x_0, x_1, ... x_T] -> g0/c0) + self.ic_enc_fwd = [None] * num_steps + self.ic_enc_rev = [None] * num_steps + if ic_dim > 0: + enc_ic_cell = cell_class(hps.ic_enc_dim, + weight_scale=hps.cell_weight_scale, + clip_value=hps.cell_clip_value) + ic_enc_fwd = encode_data(dataset_do_bxtxd, enc_ic_cell, + "ic", "forward", + hps.num_steps_for_gen_ic) + ic_enc_rev = encode_data(dataset_do_bxtxd, enc_ic_cell, + "ic", "reverse", + hps.num_steps_for_gen_ic) + self.ic_enc_fwd = ic_enc_fwd + self.ic_enc_rev = ic_enc_rev + + # Encoder control input means and variances, bi-directional encoding so: + # ([x_T, x_T-1, ..., x_0] and [x_0, x_1 ... x_T] -> u_t) + self.ci_enc_fwd = [None] * num_steps + self.ci_enc_rev = [None] * num_steps + if co_dim > 0: + enc_ci_cell = cell_class(hps.ci_enc_dim, + weight_scale=hps.cell_weight_scale, + clip_value=hps.cell_clip_value) + ci_enc_fwd = encode_data(dataset_do_bxtxd, enc_ci_cell, + "ci", "forward", + hps.num_steps) + if hps.do_causal_controller: + ci_enc_rev = None + else: + ci_enc_rev = encode_data(dataset_do_bxtxd, enc_ci_cell, + "ci", "reverse", + hps.num_steps) + self.ci_enc_fwd = ci_enc_fwd + self.ci_enc_rev = ci_enc_rev + + # STOCHASTIC LATENT VARIABLES, priors and posteriors + # (initial conditions g0, and control inputs, u_t) + # Note that zs represent all the stochastic latent variables. + with tf.variable_scope("z", reuse=False): + self.prior_zs_g0 = None + self.posterior_zs_g0 = None + self.g0s_val = None + if ic_dim > 0: + self.prior_zs_g0 = \ + LearnableDiagonalGaussian(batch_size, ic_dim, name="prior_g0", + mean_init=0.0, + var_min=hps.ic_prior_var_min, + var_init=hps.ic_prior_var_scale, + var_max=hps.ic_prior_var_max) + ic_enc = tf.concat(axis=1, values=[ic_enc_fwd[-1], ic_enc_rev[0]]) + ic_enc = tf.nn.dropout(ic_enc, keep_prob) + self.posterior_zs_g0 = \ + DiagonalGaussianFromInput(ic_enc, ic_dim, "ic_enc_2_post_g0", + var_min=hps.ic_post_var_min) + if kind in ["train", "posterior_sample_and_average"]: + zs_g0 = self.posterior_zs_g0 + else: + zs_g0 = self.prior_zs_g0 + if kind in ["train", "posterior_sample_and_average", "prior_sample"]: + self.g0s_val = zs_g0.sample + else: + self.g0s_val = zs_g0.mean + + # Priors for controller, 'co' for controller output + self.prior_zs_co = prior_zs_co = [None] * num_steps + self.posterior_zs_co = posterior_zs_co = [None] * num_steps + self.zs_co = zs_co = [None] * num_steps + self.prior_zs_ar_con = None + if co_dim > 0: + # Controller outputs + autocorrelation_taus = [hps.prior_ar_atau for x in range(hps.co_dim)] + noise_variances = [hps.prior_ar_nvar for x in range(hps.co_dim)] + self.prior_zs_ar_con = prior_zs_ar_con = \ + LearnableAutoRegressive1Prior(batch_size, hps.co_dim, + autocorrelation_taus, + noise_variances, + hps.do_train_prior_ar_atau, + hps.do_train_prior_ar_nvar, + num_steps, "u_prior_ar1") + + # CONTROLLER -> GENERATOR -> RATES + # (u(t) -> gen(t) -> factors(t) -> rates(t) -> p(x_t|z_t) ) + self.controller_outputs = u_t = [None] * num_steps + self.con_ics = con_state = None + self.con_states = con_states = [None] * num_steps + self.con_outs = con_outs = [None] * num_steps + self.gen_inputs = gen_inputs = [None] * num_steps + if co_dim > 0: + # gen_cell_class here for l2 penalty recurrent weights + # didn't split the cell_weight scale here, because I doubt it matters + con_cell = gen_cell_class(hps.con_dim, + input_weight_scale=hps.cell_weight_scale, + rec_weight_scale=hps.cell_weight_scale, + clip_value=hps.cell_clip_value, + recurrent_collections=['l2_con_reg']) + with tf.variable_scope("con", reuse=False): + self.con_ics = tf.tile( + tf.Variable(tf.zeros([1, hps.con_dim*con_cell.state_multiplier]), \ + name="c0"), + tf.stack([batch_size, 1])) + self.con_ics.set_shape([None, con_cell.state_size]) # tile loses shape + con_states[-1] = self.con_ics + + gen_cell = gen_cell_class(hps.gen_dim, + input_weight_scale=hps.gen_cell_input_weight_scale, + rec_weight_scale=hps.gen_cell_rec_weight_scale, + clip_value=hps.cell_clip_value, + recurrent_collections=['l2_gen_reg']) + with tf.variable_scope("gen", reuse=False): + if ic_dim == 0: + self.gen_ics = tf.tile( + tf.Variable(tf.zeros([1, gen_cell.state_size]), name="g0"), + tf.stack([batch_size, 1])) + else: + self.gen_ics = linear(self.g0s_val, gen_cell.state_size, + identity_if_possible=True, + name="g0_2_gen_ic") + + self.gen_states = gen_states = [None] * num_steps + self.gen_outs = gen_outs = [None] * num_steps + gen_states[-1] = self.gen_ics + gen_outs[-1] = gen_cell.output_from_state(gen_states[-1]) + self.factors = factors = [None] * num_steps + factors[-1] = linear(gen_outs[-1], factors_dim, do_bias=False, + normalized=True, name="gen_2_fac") + + self.rates = rates = [None] * num_steps + # rates[-1] is collected to potentially feed back to controller + with tf.variable_scope("glm", reuse=False): + if hps.output_dist == 'poisson': + log_rates_t0 = tf.matmul(factors[-1], this_out_fac_W) + this_out_fac_b + log_rates_t0.set_shape([None, None]) + rates[-1] = tf.exp(log_rates_t0) # rate + rates[-1].set_shape([None, hps.dataset_dims[hps.dataset_names[0]]]) + elif hps.output_dist == 'gaussian': + mean_n_logvars = tf.matmul(factors[-1],this_out_fac_W) + this_out_fac_b + mean_n_logvars.set_shape([None, None]) + means_t_bxd, logvars_t_bxd = tf.split(axis=1, num_or_size_splits=2, + value=mean_n_logvars) + rates[-1] = means_t_bxd + else: + assert False, "NIY" + + + # We support mulitple output distributions, for example Poisson, and also + # Gaussian. In these two cases respectively, there are one and two + # parameters (rates vs. mean and variance). So the output_dist_params + # tensor will variable sizes via tf.concat and tf.split, along the 1st + # dimension. So in the case of gaussian, for example, it'll be + # batch x (D+D), where each D dims is the mean, and then variances, + # respectively. For a distribution with 3 parameters, it would be + # batch x (D+D+D). + self.output_dist_params = dist_params = [None] * num_steps + self.log_p_xgz_b = log_p_xgz_b = 0.0 # log P(x|z) + for t in range(num_steps): + # Controller + if co_dim > 0: + # Build inputs for controller + tlag = t - hps.controller_input_lag + if tlag < 0: + con_in_f_t = tf.zeros_like(ci_enc_fwd[0]) + else: + con_in_f_t = ci_enc_fwd[tlag] + if hps.do_causal_controller: + # If controller is causal (wrt to data generation process), then it + # cannot see future data. Thus, excluding ci_enc_rev[t] is obvious. + # Less obvious is the need to exclude factors[t-1]. This arises + # because information flows from g0 through factors to the controller + # input. The g0 encoding is backwards, so we must necessarily exclude + # the factors in order to keep the controller input purely from a + # forward encoding (however unlikely it is that + # g0->factors->controller channel might actually be used in this way). + con_in_list_t = [con_in_f_t] + else: + tlag_rev = t + hps.controller_input_lag + if tlag_rev >= num_steps: + # better than zeros + con_in_r_t = tf.zeros_like(ci_enc_rev[0]) + else: + con_in_r_t = ci_enc_rev[tlag_rev] + con_in_list_t = [con_in_f_t, con_in_r_t] + + if hps.do_feed_factors_to_controller: + if hps.feedback_factors_or_rates == "factors": + con_in_list_t.append(factors[t-1]) + elif hps.feedback_factors_or_rates == "rates": + con_in_list_t.append(rates[t-1]) + else: + assert False, "NIY" + + con_in_t = tf.concat(axis=1, values=con_in_list_t) + con_in_t = tf.nn.dropout(con_in_t, keep_prob) + with tf.variable_scope("con", reuse=True if t > 0 else None): + con_outs[t], con_states[t] = con_cell(con_in_t, con_states[t-1]) + posterior_zs_co[t] = \ + DiagonalGaussianFromInput(con_outs[t], co_dim, + name="con_to_post_co") + if kind == "train": + u_t[t] = posterior_zs_co[t].sample + elif kind == "posterior_sample_and_average": + u_t[t] = posterior_zs_co[t].sample + else: + u_t[t] = prior_zs_ar_con.samples_t[t] + + # Inputs to the generator (controller output + external input) + if ext_input_dim > 0 and hps.inject_ext_input_to_gen: + ext_input_t_bxi = ext_input_do_bxtxi[:,t,:] + if co_dim > 0: + gen_inputs[t] = tf.concat(axis=1, values=[u_t[t], ext_input_t_bxi]) + else: + gen_inputs[t] = ext_input_t_bxi + else: + gen_inputs[t] = u_t[t] + + # Generator + data_t_bxd = dataset_ph[:,t,:] + with tf.variable_scope("gen", reuse=True if t > 0 else None): + gen_outs[t], gen_states[t] = gen_cell(gen_inputs[t], gen_states[t-1]) + gen_outs[t] = tf.nn.dropout(gen_outs[t], keep_prob) + with tf.variable_scope("gen", reuse=True): # ic defined it above + factors[t] = linear(gen_outs[t], factors_dim, do_bias=False, + normalized=True, name="gen_2_fac") + with tf.variable_scope("glm", reuse=True if t > 0 else None): + if hps.output_dist == 'poisson': + log_rates_t = tf.matmul(factors[t], this_out_fac_W) + this_out_fac_b + log_rates_t.set_shape([None, None]) + rates[t] = dist_params[t] = tf.exp(log_rates_t) # rates feed back + rates[t].set_shape([None, hps.dataset_dims[hps.dataset_names[0]]]) + loglikelihood_t = Poisson(log_rates_t).logp(data_t_bxd) + + elif hps.output_dist == 'gaussian': + mean_n_logvars = tf.matmul(factors[t],this_out_fac_W) + this_out_fac_b + mean_n_logvars.set_shape([None, None]) + means_t_bxd, logvars_t_bxd = tf.split(axis=1, num_or_size_splits=2, + value=mean_n_logvars) + rates[t] = means_t_bxd # rates feed back to controller + dist_params[t] = tf.concat( + axis=1, values=[means_t_bxd, tf.exp(logvars_t_bxd)]) + loglikelihood_t = \ + diag_gaussian_log_likelihood(data_t_bxd, + means_t_bxd, logvars_t_bxd) + else: + assert False, "NIY" + + log_p_xgz_b += tf.reduce_sum(loglikelihood_t, [1]) + + # Correlation of inferred inputs cost. + self.corr_cost = tf.constant(0.0) + if hps.co_mean_corr_scale > 0.0: + all_sum_corr = [] + for i in range(hps.co_dim): + for j in range(i+1, hps.co_dim): + sum_corr_ij = tf.constant(0.0) + for t in range(num_steps): + u_mean_t = posterior_zs_co[t].mean + sum_corr_ij += u_mean_t[:,i]*u_mean_t[:,j] + all_sum_corr.append(0.5 * tf.square(sum_corr_ij)) + self.corr_cost = tf.reduce_mean(all_sum_corr) # div by batch and by n*(n-1)/2 pairs + + # Variational Lower Bound on posterior, p(z|x), plus reconstruction cost. + # KL and reconstruction costs are normalized only by batch size, not by + # dimension, or by time steps. + kl_cost_g0_b = tf.zeros_like(batch_size, dtype=tf.float32) + kl_cost_co_b = tf.zeros_like(batch_size, dtype=tf.float32) + self.kl_cost = tf.constant(0.0) # VAE KL cost + self.recon_cost = tf.constant(0.0) # VAE reconstruction cost + self.nll_bound_vae = tf.constant(0.0) + self.nll_bound_iwae = tf.constant(0.0) # for eval with IWAE cost. + if kind in ["train", "posterior_sample_and_average"]: + kl_cost_g0_b = 0.0 + kl_cost_co_b = 0.0 + if ic_dim > 0: + g0_priors = [self.prior_zs_g0] + g0_posts = [self.posterior_zs_g0] + kl_cost_g0_b = KLCost_GaussianGaussian(g0_posts, g0_priors).kl_cost_b + kl_cost_g0_b = hps.kl_ic_weight * kl_cost_g0_b + if co_dim > 0: + kl_cost_co_b = \ + KLCost_GaussianGaussianProcessSampled( + posterior_zs_co, prior_zs_ar_con).kl_cost_b + kl_cost_co_b = hps.kl_co_weight * kl_cost_co_b + + # L = -KL + log p(x|z), to maximize bound on likelihood + # -L = KL - log p(x|z), to minimize bound on NLL + # so 'reconstruction cost' is negative log likelihood + self.recon_cost = - tf.reduce_mean(log_p_xgz_b) + self.kl_cost = tf.reduce_mean(kl_cost_g0_b + kl_cost_co_b) + + lb_on_ll_b = log_p_xgz_b - kl_cost_g0_b - kl_cost_co_b + + # VAE error averages outside the log + self.nll_bound_vae = -tf.reduce_mean(lb_on_ll_b) + + # IWAE error averages inside the log + k = tf.cast(tf.shape(log_p_xgz_b)[0], tf.float32) + iwae_lb_on_ll = -tf.log(k) + log_sum_exp(lb_on_ll_b) + self.nll_bound_iwae = -iwae_lb_on_ll + + # L2 regularization on the generator, normalized by number of parameters. + self.l2_cost = tf.constant(0.0) + if self.hps.l2_gen_scale > 0.0 or self.hps.l2_con_scale > 0.0: + l2_costs = [] + l2_numels = [] + l2_reg_var_lists = [tf.get_collection('l2_gen_reg'), + tf.get_collection('l2_con_reg')] + l2_reg_scales = [self.hps.l2_gen_scale, self.hps.l2_con_scale] + for l2_reg_vars, l2_scale in zip(l2_reg_var_lists, l2_reg_scales): + for v in l2_reg_vars: + numel = tf.reduce_prod(tf.concat(axis=0, values=tf.shape(v))) + numel_f = tf.cast(numel, tf.float32) + l2_numels.append(numel_f) + v_l2 = tf.reduce_sum(v*v) + l2_costs.append(0.5 * l2_scale * v_l2) + self.l2_cost = tf.add_n(l2_costs) / tf.add_n(l2_numels) + + # Compute the cost for training, part of the graph regardless. + # The KL cost can be problematic at the beginning of optimization, + # so we allow an exponential increase in weighting the KL from 0 + # to 1. + self.kl_decay_step = tf.maximum(self.train_step - hps.kl_start_step, 0) + self.l2_decay_step = tf.maximum(self.train_step - hps.l2_start_step, 0) + kl_decay_step_f = tf.cast(self.kl_decay_step, tf.float32) + l2_decay_step_f = tf.cast(self.l2_decay_step, tf.float32) + kl_increase_steps_f = tf.cast(hps.kl_increase_steps, tf.float32) + l2_increase_steps_f = tf.cast(hps.l2_increase_steps, tf.float32) + self.kl_weight = kl_weight = \ + tf.minimum(kl_decay_step_f / kl_increase_steps_f, 1.0) + self.l2_weight = l2_weight = \ + tf.minimum(l2_decay_step_f / l2_increase_steps_f, 1.0) + + self.timed_kl_cost = kl_weight * self.kl_cost + self.timed_l2_cost = l2_weight * self.l2_cost + self.weight_corr_cost = hps.co_mean_corr_scale * self.corr_cost + self.cost = self.recon_cost + self.timed_kl_cost + \ + self.timed_l2_cost + self.weight_corr_cost + + if kind != "train": + # save every so often + self.seso_saver = tf.train.Saver(tf.global_variables(), + max_to_keep=hps.max_ckpt_to_keep) + # lowest validation error + self.lve_saver = tf.train.Saver(tf.global_variables(), + max_to_keep=hps.max_ckpt_to_keep_lve) + + return + + # OPTIMIZATION + if not self.hps.do_train_io_only: + self.train_vars = tvars = \ + tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, + scope=tf.get_variable_scope().name) + else: + self.train_vars = tvars = \ + tf.get_collection('IO_transformations', + scope=tf.get_variable_scope().name) + print("done.") + print("Model Variables (to be optimized): ") + total_params = 0 + for i in range(len(tvars)): + shape = tvars[i].get_shape().as_list() + print(" ", i, tvars[i].name, shape) + total_params += np.prod(shape) + print("Total model parameters: ", total_params) + + grads = tf.gradients(self.cost, tvars) + grads, grad_global_norm = tf.clip_by_global_norm(grads, hps.max_grad_norm) + opt = tf.train.AdamOptimizer(self.learning_rate, beta1=0.9, beta2=0.999, + epsilon=1e-01) + self.grads = grads + self.grad_global_norm = grad_global_norm + self.train_op = opt.apply_gradients( + zip(grads, tvars), global_step=self.train_step) + + self.seso_saver = tf.train.Saver(tf.global_variables(), + max_to_keep=hps.max_ckpt_to_keep) + + # lowest validation error + self.lve_saver = tf.train.Saver(tf.global_variables(), + max_to_keep=hps.max_ckpt_to_keep) + + # SUMMARIES, used only during training. + # example summary + self.example_image = tf.placeholder(tf.float32, shape=[1,None,None,3], + name='image_tensor') + self.example_summ = tf.summary.image("LFADS example", self.example_image, + collections=["example_summaries"]) + + # general training summaries + self.lr_summ = tf.summary.scalar("Learning rate", self.learning_rate) + self.kl_weight_summ = tf.summary.scalar("KL weight", self.kl_weight) + self.l2_weight_summ = tf.summary.scalar("L2 weight", self.l2_weight) + self.corr_cost_summ = tf.summary.scalar("Corr cost", self.weight_corr_cost) + self.grad_global_norm_summ = tf.summary.scalar("Gradient global norm", + self.grad_global_norm) + if hps.co_dim > 0: + self.atau_summ = [None] * hps.co_dim + self.pvar_summ = [None] * hps.co_dim + for c in range(hps.co_dim): + self.atau_summ[c] = \ + tf.summary.scalar("AR Autocorrelation taus " + str(c), + tf.exp(self.prior_zs_ar_con.logataus_1xu[0,c])) + self.pvar_summ[c] = \ + tf.summary.scalar("AR Variances " + str(c), + tf.exp(self.prior_zs_ar_con.logpvars_1xu[0,c])) + + # cost summaries, separated into different collections for + # training vs validation. We make placeholders for these, because + # even though the graph computes these costs on a per-batch basis, + # we want to report the more reliable metric of per-epoch cost. + kl_cost_ph = tf.placeholder(tf.float32, shape=[], name='kl_cost_ph') + self.kl_t_cost_summ = tf.summary.scalar("KL cost (train)", kl_cost_ph, + collections=["train_summaries"]) + self.kl_v_cost_summ = tf.summary.scalar("KL cost (valid)", kl_cost_ph, + collections=["valid_summaries"]) + l2_cost_ph = tf.placeholder(tf.float32, shape=[], name='l2_cost_ph') + self.l2_cost_summ = tf.summary.scalar("L2 cost", l2_cost_ph, + collections=["train_summaries"]) + + recon_cost_ph = tf.placeholder(tf.float32, shape=[], name='recon_cost_ph') + self.recon_t_cost_summ = tf.summary.scalar("Reconstruction cost (train)", + recon_cost_ph, + collections=["train_summaries"]) + self.recon_v_cost_summ = tf.summary.scalar("Reconstruction cost (valid)", + recon_cost_ph, + collections=["valid_summaries"]) + + total_cost_ph = tf.placeholder(tf.float32, shape=[], name='total_cost_ph') + self.cost_t_summ = tf.summary.scalar("Total cost (train)", total_cost_ph, + collections=["train_summaries"]) + self.cost_v_summ = tf.summary.scalar("Total cost (valid)", total_cost_ph, + collections=["valid_summaries"]) + + self.kl_cost_ph = kl_cost_ph + self.l2_cost_ph = l2_cost_ph + self.recon_cost_ph = recon_cost_ph + self.total_cost_ph = total_cost_ph + + # Merged summaries, for easy coding later. + self.merged_examples = tf.summary.merge_all(key="example_summaries") + self.merged_generic = tf.summary.merge_all() # default key is 'summaries' + self.merged_train = tf.summary.merge_all(key="train_summaries") + self.merged_valid = tf.summary.merge_all(key="valid_summaries") + + session = tf.get_default_session() + self.logfile = os.path.join(hps.lfads_save_dir, "lfads_log") + self.writer = tf.summary.FileWriter(self.logfile, session.graph) + + def build_feed_dict(self, train_name, data_bxtxd, ext_input_bxtxi=None, + keep_prob=None): + """Build the feed dictionary, handles cases where there is no value defined. + + Args: + train_name: The key into the datasets, to set the tf.case statement for + the proper readin / readout matrices. + data_bxtxd: The data tensor + ext_input_bxtxi (optional): The external input tensor + keep_prob: The drop out keep probability. + + Returns: + The feed dictionary with TF tensors as keys and data as values, for use + with tf.Session.run() + + """ + feed_dict = {} + B, T, _ = data_bxtxd.shape + feed_dict[self.dataName] = train_name + feed_dict[self.dataset_ph] = data_bxtxd + + if self.ext_input is not None and ext_input_bxtxi is not None: + feed_dict[self.ext_input] = ext_input_bxtxi + + if keep_prob is None: + feed_dict[self.keep_prob] = self.hps.keep_prob + else: + feed_dict[self.keep_prob] = keep_prob + + return feed_dict + + @staticmethod + def get_batch(data_extxd, ext_input_extxi=None, batch_size=None, + example_idxs=None): + """Get a batch of data, either randomly chosen, or specified directly. + + Args: + data_extxd: The data to model, numpy tensors with shape: + # examples x # time steps x # dimensions + ext_input_extxi (optional): The external inputs, numpy tensor with shape: + # examples x # time steps x # external input dimensions + batch_size: The size of the batch to return + example_idxs (optional): The example indices used to select examples. + + Returns: + A tuple with two parts: + 1. Batched data numpy tensor with shape: + batch_size x # time steps x # dimensions + 2. Batched external input numpy tensor with shape: + batch_size x # time steps x # external input dims + """ + assert batch_size is not None or example_idxs is not None, "Problems" + E, T, D = data_extxd.shape + if example_idxs is None: + example_idxs = np.random.choice(E, batch_size) + + ext_input_bxtxi = None + if ext_input_extxi is not None: + ext_input_bxtxi = ext_input_extxi[example_idxs,:,:] + + return data_extxd[example_idxs,:,:], ext_input_bxtxi + + @staticmethod + def example_idxs_mod_batch_size(nexamples, batch_size): + """Given a number of examples, E, and a batch_size, B, generate indices + [0, 1, 2, ... B-1; + [B, B+1, ... 2*B-1; + ... + ] + returning those indices as a 2-dim tensor shaped like E/B x B. Note that + shape is only correct if E % B == 0. If not, then an extra row is generated + so that the remainder of examples is included. The extra examples are + explicitly to to the zero index (see randomize_example_idxs_mod_batch_size) + for randomized behavior. + + Args: + nexamples: The number of examples to batch up. + batch_size: The size of the batch. + Returns: + 2-dim tensor as described above. + """ + bmrem = batch_size - (nexamples % batch_size) + bmrem_examples = [] + if bmrem < batch_size: + #bmrem_examples = np.zeros(bmrem, dtype=np.int32) + ridxs = np.random.permutation(nexamples)[0:bmrem].astype(np.int32) + bmrem_examples = np.sort(ridxs) + example_idxs = range(nexamples) + list(bmrem_examples) + example_idxs_e_x_edivb = np.reshape(example_idxs, [-1, batch_size]) + return example_idxs_e_x_edivb, bmrem + + @staticmethod + def randomize_example_idxs_mod_batch_size(nexamples, batch_size): + """Indices 1:nexamples, randomized, in 2D form of + shape = (nexamples / batch_size) x batch_size. The remainder + is managed by drawing randomly from 1:nexamples. + + Args: + nexamples: number of examples to randomize + batch_size: number of elements in batch + + Returns: + The randomized, properly shaped indicies. + """ + assert nexamples > batch_size, "Problems" + bmrem = batch_size - nexamples % batch_size + bmrem_examples = [] + if bmrem < batch_size: + bmrem_examples = np.random.choice(range(nexamples), + size=bmrem, replace=False) + example_idxs = range(nexamples) + list(bmrem_examples) + mixed_example_idxs = np.random.permutation(example_idxs) + example_idxs_e_x_edivb = np.reshape(mixed_example_idxs, [-1, batch_size]) + return example_idxs_e_x_edivb, bmrem + + def shuffle_spikes_in_time(self, data_bxtxd): + """Shuffle the spikes in the temporal dimension. This is useful to + help the LFADS system avoid overfitting to individual spikes or fast + oscillations found in the data that are irrelevant to behavior. A + pure 'tabula rasa' approach would avoid this, but LFADS is sensitive + enough to pick up dynamics that you may not want. + + Args: + data_bxtxd: numpy array of spike count data to be shuffled. + Returns: + S_bxtxd, a numpy array with the same dimensions and contents as + data_bxtxd, but shuffled appropriately. + + """ + + B, T, N = data_bxtxd.shape + w = self.hps.temporal_spike_jitter_width + + if w == 0: + return data_bxtxd + + max_counts = np.max(data_bxtxd) + S_bxtxd = np.zeros([B,T,N]) + + # Intuitively, shuffle spike occurances, 0 or 1, but since we have counts, + # Do it over and over again up to the max count. + for mc in range(1,max_counts+1): + idxs = np.nonzero(data_bxtxd >= mc) + + data_ones = np.zeros_like(data_bxtxd) + data_ones[data_bxtxd >= mc] = 1 + + nfound = len(idxs[0]) + shuffles_incrs_in_time = np.random.randint(-w, w, size=nfound) + + shuffle_tidxs = idxs[1].copy() + shuffle_tidxs += shuffles_incrs_in_time + + # Reflect on the boundaries to not lose mass. + shuffle_tidxs[shuffle_tidxs < 0] = -shuffle_tidxs[shuffle_tidxs < 0] + shuffle_tidxs[shuffle_tidxs > T-1] = \ + (T-1)-(shuffle_tidxs[shuffle_tidxs > T-1] -(T-1)) + + for iii in zip(idxs[0], shuffle_tidxs, idxs[2]): + S_bxtxd[iii] += 1 + + return S_bxtxd + + def shuffle_and_flatten_datasets(self, datasets, kind='train'): + """Since LFADS supports multiple datasets in the same dynamical model, + we have to be careful to use all the data in a single training epoch. But + since the datasets my have different data dimensionality, we cannot batch + examples from data dictionaries together. Instead, we generate random + batches within each data dictionary, and then randomize these batches + while holding onto the dataname, so that when it's time to feed + the graph, the correct in/out matrices can be selected, per batch. + + Args: + datasets: A dict of data dicts. The dataset dict is simply a + name(string)-> data dictionary mapping (See top of lfads.py). + kind: 'train' or 'valid' + + Returns: + A flat list, in which each element is a pair ('name', indices). + """ + batch_size = self.hps.batch_size + ndatasets = len(datasets) + random_example_idxs = {} + epoch_idxs = {} + all_name_example_idx_pairs = [] + kind_data = kind + '_data' + for name, data_dict in datasets.items(): + nexamples, ntime, data_dim = data_dict[kind_data].shape + epoch_idxs[name] = 0 + random_example_idxs, _ = \ + self.randomize_example_idxs_mod_batch_size(nexamples, batch_size) + + epoch_size = random_example_idxs.shape[0] + names = [name] * epoch_size + all_name_example_idx_pairs += zip(names, random_example_idxs) + + np.random.shuffle(all_name_example_idx_pairs) # shuffle in place + + return all_name_example_idx_pairs + + def train_epoch(self, datasets, batch_size=None, do_save_ckpt=True): + """Train the model through the entire dataset once. + + Args: + datasets: A dict of data dicts. The dataset dict is simply a + name(string)-> data dictionary mapping (See top of lfads.py). + batch_size (optional): The batch_size to use + do_save_ckpt (optional): Should the routine save a checkpoint on this + training epoch? + + Returns: + A tuple with 6 float values: + (total cost of the epoch, epoch reconstruction cost, + epoch kl cost, KL weight used this training epoch, + total l2 cost on generator, and the corresponding weight). + """ + ops_to_eval = [self.cost, self.recon_cost, + self.kl_cost, self.kl_weight, + self.l2_cost, self.l2_weight, + self.train_op] + collected_op_values = self.run_epoch(datasets, ops_to_eval, kind="train") + + total_cost = total_recon_cost = total_kl_cost = 0.0 + # normalizing by batch done in distributions.py + epoch_size = len(collected_op_values) + for op_values in collected_op_values: + total_cost += op_values[0] + total_recon_cost += op_values[1] + total_kl_cost += op_values[2] + + kl_weight = collected_op_values[-1][3] + l2_cost = collected_op_values[-1][4] + l2_weight = collected_op_values[-1][5] + + epoch_total_cost = total_cost / epoch_size + epoch_recon_cost = total_recon_cost / epoch_size + epoch_kl_cost = total_kl_cost / epoch_size + + if do_save_ckpt: + session = tf.get_default_session() + checkpoint_path = os.path.join(self.hps.lfads_save_dir, + self.hps.checkpoint_name + '.ckpt') + self.seso_saver.save(session, checkpoint_path, + global_step=self.train_step) + + return epoch_total_cost, epoch_recon_cost, epoch_kl_cost, \ + kl_weight, l2_cost, l2_weight + + + def run_epoch(self, datasets, ops_to_eval, kind="train", batch_size=None, + do_collect=True, keep_prob=None): + """Run the model through the entire dataset once. + + Args: + datasets: A dict of data dicts. The dataset dict is simply a + name(string)-> data dictionary mapping (See top of lfads.py). + ops_to_eval: A list of tensorflow operations that will be evaluated in + the tf.session.run() call. + batch_size (optional): The batch_size to use + do_collect (optional): Should the routine collect all session.run + output as a list, and return it? + keep_prob (optional): The dropout keep probability. + + Returns: + A list of lists, the internal list is the return for the ops for each + session.run() call. The outer list collects over the epoch. + """ + hps = self.hps + all_name_example_idx_pairs = \ + self.shuffle_and_flatten_datasets(datasets, kind) + + kind_data = kind + '_data' + kind_ext_input = kind + '_ext_input' + + total_cost = total_recon_cost = total_kl_cost = 0.0 + session = tf.get_default_session() + epoch_size = len(all_name_example_idx_pairs) + evaled_ops_list = [] + for name, example_idxs in all_name_example_idx_pairs: + data_dict = datasets[name] + data_extxd = data_dict[kind_data] + if hps.output_dist == 'poisson' and hps.temporal_spike_jitter_width > 0: + data_extxd = self.shuffle_spikes_in_time(data_extxd) + + ext_input_extxi = data_dict[kind_ext_input] + data_bxtxd, ext_input_bxtxi = self.get_batch(data_extxd, ext_input_extxi, + example_idxs=example_idxs) + + feed_dict = self.build_feed_dict(name, data_bxtxd, ext_input_bxtxi, + keep_prob=keep_prob) + evaled_ops_np = session.run(ops_to_eval, feed_dict=feed_dict) + if do_collect: + evaled_ops_list.append(evaled_ops_np) + + return evaled_ops_list + + def summarize_all(self, datasets, summary_values): + """Plot and summarize stuff in tensorboard. + + Note that everything done in the current function is otherwise done on + a single, randomly selected dataset (except for summary_values, which are + passed in.) + + Args: + datasets, the dictionary of datasets used in the study. + summary_values: These summary values are created from the training loop, + and so summarize the entire set of datasets. + """ + hps = self.hps + tr_kl_cost = summary_values['tr_kl_cost'] + tr_recon_cost = summary_values['tr_recon_cost'] + tr_total_cost = summary_values['tr_total_cost'] + kl_weight = summary_values['kl_weight'] + l2_weight = summary_values['l2_weight'] + l2_cost = summary_values['l2_cost'] + has_any_valid_set = summary_values['has_any_valid_set'] + i = summary_values['nepochs'] + + session = tf.get_default_session() + train_summ, train_step = session.run([self.merged_train, + self.train_step], + feed_dict={self.l2_cost_ph:l2_cost, + self.kl_cost_ph:tr_kl_cost, + self.recon_cost_ph:tr_recon_cost, + self.total_cost_ph:tr_total_cost}) + self.writer.add_summary(train_summ, train_step) + if has_any_valid_set: + ev_kl_cost = summary_values['ev_kl_cost'] + ev_recon_cost = summary_values['ev_recon_cost'] + ev_total_cost = summary_values['ev_total_cost'] + eval_summ = session.run(self.merged_valid, + feed_dict={self.kl_cost_ph:ev_kl_cost, + self.recon_cost_ph:ev_recon_cost, + self.total_cost_ph:ev_total_cost}) + self.writer.add_summary(eval_summ, train_step) + print("Epoch:%d, step:%d (TRAIN, VALID): total: %.2f, %.2f\ + recon: %.2f, %.2f, kl: %.2f, %.2f, l2: %.5f,\ + kl weight: %.2f, l2 weight: %.2f" % \ + (i, train_step, tr_total_cost, ev_total_cost, + tr_recon_cost, ev_recon_cost, tr_kl_cost, ev_kl_cost, + l2_cost, kl_weight, l2_weight)) + + csv_outstr = "epoch,%d, step,%d, total,%.2f,%.2f, \ + recon,%.2f,%.2f, kl,%.2f,%.2f, l2,%.5f, \ + klweight,%.2f, l2weight,%.2f\n"% \ + (i, train_step, tr_total_cost, ev_total_cost, + tr_recon_cost, ev_recon_cost, tr_kl_cost, ev_kl_cost, + l2_cost, kl_weight, l2_weight) + + else: + print("Epoch:%d, step:%d TRAIN: total: %.2f recon: %.2f, kl: %.2f,\ + l2: %.5f, kl weight: %.2f, l2 weight: %.2f" % \ + (i, train_step, tr_total_cost, tr_recon_cost, tr_kl_cost, + l2_cost, kl_weight, l2_weight)) + csv_outstr = "epoch,%d, step,%d, total,%.2f, recon,%.2f, kl,%.2f, \ + l2,%.5f, klweight,%.2f, l2weight,%.2f\n"% \ + (i, train_step, tr_total_cost, tr_recon_cost, + tr_kl_cost, l2_cost, kl_weight, l2_weight) + + if self.hps.csv_log: + csv_file = os.path.join(self.hps.lfads_save_dir, self.hps.csv_log+'.csv') + with open(csv_file, "a") as myfile: + myfile.write(csv_outstr) + + + def plot_single_example(self, datasets): + """Plot an image relating to a randomly chosen, specific example. We use + posterior sample and average by taking one example, and filling a whole + batch with that example, sample from the posterior, and then average the + quantities. + + """ + hps = self.hps + all_data_names = datasets.keys() + data_name = np.random.permutation(all_data_names)[0] + data_dict = datasets[data_name] + has_valid_set = True if data_dict['valid_data'] is not None else False + cf = 1.0 # plotting concern + + # posterior sample and average here + E, _, _ = data_dict['train_data'].shape + eidx = np.random.choice(E) + example_idxs = eidx * np.ones(hps.batch_size, dtype=np.int32) + + train_data_bxtxd, train_ext_input_bxtxi = \ + self.get_batch(data_dict['train_data'], data_dict['train_ext_input'], + example_idxs=example_idxs) + + truth_train_data_bxtxd = None + if 'train_truth' in data_dict and data_dict['train_truth'] is not None: + truth_train_data_bxtxd, _ = self.get_batch(data_dict['train_truth'], + example_idxs=example_idxs) + cf = data_dict['conversion_factor'] + + # plotter does averaging + train_model_values = self.eval_model_runs_batch(data_name, + train_data_bxtxd, + train_ext_input_bxtxi, + do_average_batch=False) + + train_step = train_model_values['train_steps'] + feed_dict = self.build_feed_dict(data_name, train_data_bxtxd, + train_ext_input_bxtxi, keep_prob=1.0) + + session = tf.get_default_session() + generic_summ = session.run(self.merged_generic, feed_dict=feed_dict) + self.writer.add_summary(generic_summ, train_step) + + valid_data_bxtxd = valid_model_values = valid_ext_input_bxtxi = None + truth_valid_data_bxtxd = None + if has_valid_set: + E, _, _ = data_dict['valid_data'].shape + eidx = np.random.choice(E) + example_idxs = eidx * np.ones(hps.batch_size, dtype=np.int32) + valid_data_bxtxd, valid_ext_input_bxtxi = \ + self.get_batch(data_dict['valid_data'], + data_dict['valid_ext_input'], + example_idxs=example_idxs) + if 'valid_truth' in data_dict and data_dict['valid_truth'] is not None: + truth_valid_data_bxtxd, _ = self.get_batch(data_dict['valid_truth'], + example_idxs=example_idxs) + else: + truth_valid_data_bxtxd = None + + # plotter does averaging + valid_model_values = self.eval_model_runs_batch(data_name, + valid_data_bxtxd, + valid_ext_input_bxtxi, + do_average_batch=False) + + example_image = plot_lfads(train_bxtxd=train_data_bxtxd, + train_model_vals=train_model_values, + train_ext_input_bxtxi=train_ext_input_bxtxi, + train_truth_bxtxd=truth_train_data_bxtxd, + valid_bxtxd=valid_data_bxtxd, + valid_model_vals=valid_model_values, + valid_ext_input_bxtxi=valid_ext_input_bxtxi, + valid_truth_bxtxd=truth_valid_data_bxtxd, + bidx=None, cf=cf, output_dist=hps.output_dist) + example_image = np.expand_dims(example_image, axis=0) + example_summ = session.run(self.merged_examples, + feed_dict={self.example_image : example_image}) + self.writer.add_summary(example_summ) + + def train_model(self, datasets): + """Train the model, print per-epoch information, and save checkpoints. + + Loop over training epochs. The function that actually does the + training is train_epoch. This function iterates over the training + data, one epoch at a time. The learning rate schedule is such + that it will stay the same until the cost goes up in comparison to + the last few values, then it will drop. + + Args: + datasets: A dict of data dicts. The dataset dict is simply a + name(string)-> data dictionary mapping (See top of lfads.py). + """ + hps = self.hps + has_any_valid_set = False + for data_dict in datasets.values(): + if data_dict['valid_data'] is not None: + has_any_valid_set = True + break + + session = tf.get_default_session() + lr = session.run(self.learning_rate) + lr_stop = hps.learning_rate_stop + i = -1 + train_costs = [] + valid_costs = [] + ev_total_cost = ev_recon_cost = ev_kl_cost = 0.0 + lowest_ev_cost = np.Inf + while True: + i += 1 + do_save_ckpt = True if i % 10 ==0 else False + tr_total_cost, tr_recon_cost, tr_kl_cost, kl_weight, l2_cost, l2_weight = \ + self.train_epoch(datasets, do_save_ckpt=do_save_ckpt) + + # Evaluate the validation cost, and potentially save. Note that this + # routine will not save a validation checkpoint until the kl weight and + # l2 weights are equal to 1.0. + if has_any_valid_set: + ev_total_cost, ev_recon_cost, ev_kl_cost = \ + self.eval_cost_epoch(datasets, kind='valid') + valid_costs.append(ev_total_cost) + + # > 1 may give more consistent results, but not the actual lowest vae. + # == 1 gives the lowest vae seen so far. + n_lve = 1 + run_avg_lve = np.mean(valid_costs[-n_lve:]) + + # conditions for saving checkpoints: + # KL weight must have finished stepping (>=1.0), AND + # L2 weight must have finished stepping OR L2 is not being used, AND + # the current run has a lower LVE than previous runs AND + # len(valid_costs > n_lve) (not sure what that does) + if kl_weight >= 1.0 and \ + (l2_weight >= 1.0 or \ + (self.hps.l2_gen_scale == 0.0 and self.hps.l2_con_scale == 0.0)) \ + and (len(valid_costs) > n_lve and run_avg_lve < lowest_ev_cost): + + lowest_ev_cost = run_avg_lve + checkpoint_path = os.path.join(self.hps.lfads_save_dir, + self.hps.checkpoint_name + '_lve.ckpt') + self.lve_saver.save(session, checkpoint_path, + global_step=self.train_step, + latest_filename='checkpoint_lve') + + # Plot and summarize. + values = {'nepochs':i, 'has_any_valid_set': has_any_valid_set, + 'tr_total_cost':tr_total_cost, 'ev_total_cost':ev_total_cost, + 'tr_recon_cost':tr_recon_cost, 'ev_recon_cost':ev_recon_cost, + 'tr_kl_cost':tr_kl_cost, 'ev_kl_cost':ev_kl_cost, + 'l2_weight':l2_weight, 'kl_weight':kl_weight, + 'l2_cost':l2_cost} + self.summarize_all(datasets, values) + self.plot_single_example(datasets) + + # Manage learning rate. + train_res = tr_total_cost + n_lr = hps.learning_rate_n_to_compare + if len(train_costs) > n_lr and train_res > np.max(train_costs[-n_lr:]): + _ = session.run(self.learning_rate_decay_op) + lr = session.run(self.learning_rate) + print(" Decreasing learning rate to %f." % lr) + # Force the system to run n_lr times while at this lr. + train_costs.append(np.inf) + else: + train_costs.append(train_res) + + if lr < lr_stop: + print("Stopping optimization based on learning rate criteria.") + break + + def eval_cost_epoch(self, datasets, kind='train', ext_input_extxi=None, + batch_size=None): + """Evaluate the cost of the epoch. + + Args: + data_dict: The dictionary of data (training and validation) used for + training and evaluation of the model, respectively. + + Returns: + a 3 tuple of costs: + (epoch total cost, epoch reconstruction cost, epoch KL cost) + """ + ops_to_eval = [self.cost, self.recon_cost, self.kl_cost] + collected_op_values = self.run_epoch(datasets, ops_to_eval, kind=kind, + keep_prob=1.0) + + total_cost = total_recon_cost = total_kl_cost = 0.0 + # normalizing by batch done in distributions.py + epoch_size = len(collected_op_values) + for op_values in collected_op_values: + total_cost += op_values[0] + total_recon_cost += op_values[1] + total_kl_cost += op_values[2] + + epoch_total_cost = total_cost / epoch_size + epoch_recon_cost = total_recon_cost / epoch_size + epoch_kl_cost = total_kl_cost / epoch_size + + return epoch_total_cost, epoch_recon_cost, epoch_kl_cost + + def eval_model_runs_batch(self, data_name, data_bxtxd, ext_input_bxtxi=None, + do_eval_cost=False, do_average_batch=False): + """Returns all the goodies for the entire model, per batch. + + Args: + data_name: The name of the data dict, to select which in/out matrices + to use. + data_bxtxd: Numpy array training data with shape: + batch_size x # time steps x # dimensions + ext_input_bxtxi: Numpy array training external input with shape: + batch_size x # time steps x # external input dims + do_eval_cost (optional): If true, the IWAE (Importance Weighted + Autoencoder) log likeihood bound, instead of the VAE version. + do_average_batch (optional): average over the batch, useful for getting + good IWAE costs, and model outputs for a single data point. + + Returns: + A dictionary with the outputs of the model decoder, namely: + prior g0 mean, prior g0 variance, approx. posterior mean, approx + posterior mean, the generator initial conditions, the control inputs (if + enabled), the state of the generator, the factors, and the rates. + """ + session = tf.get_default_session() + feed_dict = self.build_feed_dict(data_name, data_bxtxd, + ext_input_bxtxi, keep_prob=1.0) + + # Non-temporal signals will be batch x dim. + # Temporal signals are list length T with elements batch x dim. + tf_vals = [self.gen_ics, self.gen_states, self.factors, + self.output_dist_params] + tf_vals.append(self.cost) + tf_vals.append(self.nll_bound_vae) + tf_vals.append(self.nll_bound_iwae) + tf_vals.append(self.train_step) # not train_op! + if self.hps.ic_dim > 0: + tf_vals += [self.prior_zs_g0.mean, self.prior_zs_g0.logvar, + self.posterior_zs_g0.mean, self.posterior_zs_g0.logvar] + if self.hps.co_dim > 0: + tf_vals.append(self.controller_outputs) + tf_vals_flat, fidxs = flatten(tf_vals) + + np_vals_flat = session.run(tf_vals_flat, feed_dict=feed_dict) + + ff = 0 + gen_ics = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + gen_states = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + factors = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + out_dist_params = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + costs = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + nll_bound_vaes = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + nll_bound_iwaes = [np_vals_flat[f] for f in fidxs[ff]]; ff +=1 + train_steps = [np_vals_flat[f] for f in fidxs[ff]]; ff +=1 + if self.hps.ic_dim > 0: + prior_g0_mean = [np_vals_flat[f] for f in fidxs[ff]]; ff +=1 + prior_g0_logvar = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + post_g0_mean = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + post_g0_logvar = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + if self.hps.co_dim > 0: + controller_outputs = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + + # [0] are to take out the non-temporal items from lists + gen_ics = gen_ics[0] + costs = costs[0] + nll_bound_vaes = nll_bound_vaes[0] + nll_bound_iwaes = nll_bound_iwaes[0] + train_steps = train_steps[0] + + # Convert to full tensors, not lists of tensors in time dim. + gen_states = list_t_bxn_to_tensor_bxtxn(gen_states) + factors = list_t_bxn_to_tensor_bxtxn(factors) + out_dist_params = list_t_bxn_to_tensor_bxtxn(out_dist_params) + if self.hps.ic_dim > 0: + prior_g0_mean = prior_g0_mean[0] + prior_g0_logvar = prior_g0_logvar[0] + post_g0_mean = post_g0_mean[0] + post_g0_logvar = post_g0_logvar[0] + if self.hps.co_dim > 0: + controller_outputs = list_t_bxn_to_tensor_bxtxn(controller_outputs) + + if do_average_batch: + gen_ics = np.mean(gen_ics, axis=0) + gen_states = np.mean(gen_states, axis=0) + factors = np.mean(factors, axis=0) + out_dist_params = np.mean(out_dist_params, axis=0) + if self.hps.ic_dim > 0: + prior_g0_mean = np.mean(prior_g0_mean, axis=0) + prior_g0_logvar = np.mean(prior_g0_logvar, axis=0) + post_g0_mean = np.mean(post_g0_mean, axis=0) + post_g0_logvar = np.mean(post_g0_logvar, axis=0) + if self.hps.co_dim > 0: + controller_outputs = np.mean(controller_outputs, axis=0) + + model_vals = {} + model_vals['gen_ics'] = gen_ics + model_vals['gen_states'] = gen_states + model_vals['factors'] = factors + model_vals['output_dist_params'] = out_dist_params + model_vals['costs'] = costs + model_vals['nll_bound_vaes'] = nll_bound_vaes + model_vals['nll_bound_iwaes'] = nll_bound_iwaes + model_vals['train_steps'] = train_steps + if self.hps.ic_dim > 0: + model_vals['prior_g0_mean'] = prior_g0_mean + model_vals['prior_g0_logvar'] = prior_g0_logvar + model_vals['post_g0_mean'] = post_g0_mean + model_vals['post_g0_logvar'] = post_g0_logvar + if self.hps.co_dim > 0: + model_vals['controller_outputs'] = controller_outputs + + return model_vals + + def eval_model_runs_avg_epoch(self, data_name, data_extxd, + ext_input_extxi=None): + """Returns all the expected value for goodies for the entire model. + + The expected value is taken over hidden (z) variables, namely the initial + conditions and the control inputs. The expected value is approximate, and + accomplished via sampling (batch_size) samples for every examples. + + Args: + data_name: The name of the data dict, to select which in/out matrices + to use. + data_extxd: Numpy array training data with shape: + # examples x # time steps x # dimensions + ext_input_extxi (optional): Numpy array training external input with + shape: # examples x # time steps x # external input dims + + Returns: + A dictionary with the averaged outputs of the model decoder, namely: + prior g0 mean, prior g0 variance, approx. posterior mean, approx + posterior mean, the generator initial conditions, the control inputs (if + enabled), the state of the generator, the factors, and the output + distribution parameters, e.g. (rates or mean and variances). + """ + hps = self.hps + batch_size = hps.batch_size + E, T, D = data_extxd.shape + E_to_process = hps.ps_nexamples_to_process + if E_to_process > E: + print("Setting number of posterior samples to process to : ", E) + E_to_process = E + + if hps.ic_dim > 0: + prior_g0_mean = np.zeros([E_to_process, hps.ic_dim]) + prior_g0_logvar = np.zeros([E_to_process, hps.ic_dim]) + post_g0_mean = np.zeros([E_to_process, hps.ic_dim]) + post_g0_logvar = np.zeros([E_to_process, hps.ic_dim]) + + if hps.co_dim > 0: + controller_outputs = np.zeros([E_to_process, T, hps.co_dim]) + gen_ics = np.zeros([E_to_process, hps.gen_dim]) + gen_states = np.zeros([E_to_process, T, hps.gen_dim]) + factors = np.zeros([E_to_process, T, hps.factors_dim]) + + if hps.output_dist == 'poisson': + out_dist_params = np.zeros([E_to_process, T, D]) + elif hps.output_dist == 'gaussian': + out_dist_params = np.zeros([E_to_process, T, D+D]) + else: + assert False, "NIY" + + costs = np.zeros(E_to_process) + nll_bound_vaes = np.zeros(E_to_process) + nll_bound_iwaes = np.zeros(E_to_process) + train_steps = np.zeros(E_to_process) + for es_idx in range(E_to_process): + print("Running %d of %d." % (es_idx+1, E_to_process)) + example_idxs = es_idx * np.ones(batch_size, dtype=np.int32) + data_bxtxd, ext_input_bxtxi = self.get_batch(data_extxd, + ext_input_extxi, + batch_size=batch_size, + example_idxs=example_idxs) + model_values = self.eval_model_runs_batch(data_name, data_bxtxd, + ext_input_bxtxi, + do_eval_cost=True, + do_average_batch=True) + + if self.hps.ic_dim > 0: + prior_g0_mean[es_idx,:] = model_values['prior_g0_mean'] + prior_g0_logvar[es_idx,:] = model_values['prior_g0_logvar'] + post_g0_mean[es_idx,:] = model_values['post_g0_mean'] + post_g0_logvar[es_idx,:] = model_values['post_g0_logvar'] + gen_ics[es_idx,:] = model_values['gen_ics'] + + if self.hps.co_dim > 0: + controller_outputs[es_idx,:,:] = model_values['controller_outputs'] + gen_states[es_idx,:,:] = model_values['gen_states'] + factors[es_idx,:,:] = model_values['factors'] + out_dist_params[es_idx,:,:] = model_values['output_dist_params'] + costs[es_idx] = model_values['costs'] + nll_bound_vaes[es_idx] = model_values['nll_bound_vaes'] + nll_bound_iwaes[es_idx] = model_values['nll_bound_iwaes'] + train_steps[es_idx] = model_values['train_steps'] + print('bound nll(vae): %.3f, bound nll(iwae): %.3f' \ + % (nll_bound_vaes[es_idx], nll_bound_iwaes[es_idx])) + + model_runs = {} + if self.hps.ic_dim > 0: + model_runs['prior_g0_mean'] = prior_g0_mean + model_runs['prior_g0_logvar'] = prior_g0_logvar + model_runs['post_g0_mean'] = post_g0_mean + model_runs['post_g0_logvar'] = post_g0_logvar + model_runs['gen_ics'] = gen_ics + + if self.hps.co_dim > 0: + model_runs['controller_outputs'] = controller_outputs + model_runs['gen_states'] = gen_states + model_runs['factors'] = factors + model_runs['output_dist_params'] = out_dist_params + model_runs['costs'] = costs + model_runs['nll_bound_vaes'] = nll_bound_vaes + model_runs['nll_bound_iwaes'] = nll_bound_iwaes + model_runs['train_steps'] = train_steps + return model_runs + + def write_model_runs(self, datasets, output_fname=None): + """Run the model on the data in data_dict, and save the computed values. + + LFADS generates a number of outputs for each examples, and these are all + saved. They are: + The mean and variance of the prior of g0. + The mean and variance of approximate posterior of g0. + The control inputs (if enabled) + The initial conditions, g0, for all examples. + The generator states for all time. + The factors for all time. + The output distribution parameters (e.g. rates) for all time. + + Args: + datasets: a dictionary of named data_dictionaries, see top of lfads.py + output_fname: a file name stem for the output files. + """ + hps = self.hps + kind = hps.kind + + for data_name, data_dict in datasets.items(): + data_tuple = [('train', data_dict['train_data'], + data_dict['train_ext_input']), + ('valid', data_dict['valid_data'], + data_dict['valid_ext_input'])] + for data_kind, data_extxd, ext_input_extxi in data_tuple: + if not output_fname: + fname = "model_runs_" + data_name + '_' + data_kind + '_' + kind + else: + fname = output_fname + data_name + '_' + data_kind + '_' + kind + + print("Writing data for %s data and kind %s." % (data_name, data_kind)) + model_runs = self.eval_model_runs_avg_epoch(data_name, data_extxd, + ext_input_extxi) + full_fname = os.path.join(hps.lfads_save_dir, fname) + write_data(full_fname, model_runs, compression='gzip') + print("Done.") + + def write_model_samples(self, dataset_name, output_fname=None): + """Use the prior distribution to generate batch_size number of samples + from the model. + + LFADS generates a number of outputs for each sample, and these are all + saved. They are: + The mean and variance of the prior of g0. + The control inputs (if enabled) + The initial conditions, g0, for all examples. + The generator states for all time. + The factors for all time. + The output distribution parameters (e.g. rates) for all time. + + Args: + dataset_name: The name of the dataset to grab the factors -> rates + alignment matrices from. + output_fname: The name of the file in which to save the generated + samples. + """ + hps = self.hps + batch_size = hps.batch_size + + print("Generating %d samples" % (batch_size)) + tf_vals = [self.factors, self.gen_states, self.gen_ics, + self.cost, self.output_dist_params] + if hps.ic_dim > 0: + tf_vals += [self.prior_zs_g0.mean, self.prior_zs_g0.logvar] + if hps.co_dim > 0: + tf_vals += [self.prior_zs_ar_con.samples_t] + tf_vals_flat, fidxs = flatten(tf_vals) + + session = tf.get_default_session() + feed_dict = {} + feed_dict[self.dataName] = dataset_name + feed_dict[self.keep_prob] = 1.0 + + np_vals_flat = session.run(tf_vals_flat, feed_dict=feed_dict) + + ff = 0 + factors = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + gen_states = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + gen_ics = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + costs = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + output_dist_params = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + if hps.ic_dim > 0: + prior_g0_mean = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + prior_g0_logvar = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + if hps.co_dim > 0: + prior_zs_ar_con = [np_vals_flat[f] for f in fidxs[ff]]; ff += 1 + + # [0] are to take out the non-temporal items from lists + gen_ics = gen_ics[0] + costs = costs[0] + + # Convert to full tensors, not lists of tensors in time dim. + gen_states = list_t_bxn_to_tensor_bxtxn(gen_states) + factors = list_t_bxn_to_tensor_bxtxn(factors) + output_dist_params = list_t_bxn_to_tensor_bxtxn(output_dist_params) + if hps.ic_dim > 0: + prior_g0_mean = prior_g0_mean[0] + prior_g0_logvar = prior_g0_logvar[0] + if hps.co_dim > 0: + prior_zs_ar_con = list_t_bxn_to_tensor_bxtxn(prior_zs_ar_con) + + model_vals = {} + model_vals['gen_ics'] = gen_ics + model_vals['gen_states'] = gen_states + model_vals['factors'] = factors + model_vals['output_dist_params'] = output_dist_params + model_vals['costs'] = costs.reshape(1) + if hps.ic_dim > 0: + model_vals['prior_g0_mean'] = prior_g0_mean + model_vals['prior_g0_logvar'] = prior_g0_logvar + if hps.co_dim > 0: + model_vals['prior_zs_ar_con'] = prior_zs_ar_con + + full_fname = os.path.join(hps.lfads_save_dir, output_fname) + write_data(full_fname, model_vals, compression='gzip') + print("Done.") + + @staticmethod + def eval_model_parameters(use_nested=True, include_strs=None): + """Evaluate and return all of the TF variables in the model. + + Args: + use_nested (optional): For returning values, use a nested dictoinary, based + on variable scoping, or return all variables in a flat dictionary. + include_strs (optional): A list of strings to use as a filter, to reduce the + number of variables returned. A variable name must contain at least one + string in include_strs as a sub-string in order to be returned. + + Returns: + The parameters of the model. This can be in a flat + dictionary, or a nested dictionary, where the nesting is by variable + scope. + """ + all_tf_vars = tf.global_variables() + session = tf.get_default_session() + all_tf_vars_eval = session.run(all_tf_vars) + vars_dict = {} + strs = ["LFADS"] + if include_strs: + strs += include_strs + + for i, (var, var_eval) in enumerate(zip(all_tf_vars, all_tf_vars_eval)): + if any(s in include_strs for s in var.name): + if not isinstance(var_eval, np.ndarray): # for H5PY + print(var.name, """ is not numpy array, saving as numpy array + with value: """, var_eval, type(var_eval)) + e = np.array(var_eval) + print(e, type(e)) + else: + e = var_eval + vars_dict[var.name] = e + + if not use_nested: + return vars_dict + + var_names = vars_dict.keys() + nested_vars_dict = {} + current_dict = nested_vars_dict + for v, var_name in enumerate(var_names): + var_split_name_list = var_name.split('/') + split_name_list_len = len(var_split_name_list) + current_dict = nested_vars_dict + for p, part in enumerate(var_split_name_list): + if p < split_name_list_len - 1: + if part in current_dict: + current_dict = current_dict[part] + else: + current_dict[part] = {} + current_dict = current_dict[part] + else: + current_dict[part] = vars_dict[var_name] + + return nested_vars_dict + + @staticmethod + def spikify_rates(rates_bxtxd): + """Randomly spikify underlying rates according a Poisson distribution + + Args: + rates_bxtxd: a numpy tensor with shape: + + Returns: + A numpy array with the same shape as rates_bxtxd, but with the event + counts. + """ + + B,T,N = rates_bxtxd.shape + assert all([B > 0, N > 0]), "problems" + + # Because the rates are changing, there is nesting + spikes_bxtxd = np.zeros([B,T,N], dtype=np.int32) + for b in range(B): + for t in range(T): + for n in range(N): + rate = rates_bxtxd[b,t,n] + count = np.random.poisson(rate) + spikes_bxtxd[b,t,n] = count + + return spikes_bxtxd diff --git a/lfads/plot_lfads.py b/lfads/plot_lfads.py new file mode 100644 index 000000000..b4ebba9f4 --- /dev/null +++ b/lfads/plot_lfads.py @@ -0,0 +1,223 @@ +# Copyright 2017 Google Inc. 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. +# +# ============================================================================== +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import matplotlib +matplotlib.use('Agg') +from matplotlib import pyplot as plt +import numpy as np +import tensorflow as tf + +def _plot_item(W, name, full_name, nspaces): + plt.figure() + if W.shape == (): + print(name, ": ", W) + elif W.shape[0] == 1: + plt.stem(W.T) + plt.title(full_name) + elif W.shape[1] == 1: + plt.stem(W) + plt.title(full_name) + else: + plt.imshow(np.abs(W), interpolation='nearest', cmap='jet'); + plt.colorbar() + plt.title(full_name) + + +def all_plot(d, full_name="", exclude="", nspaces=0): + """Recursively plot all the LFADS model parameters in the nested + dictionary.""" + for k, v in d.iteritems(): + this_name = full_name+"/"+k + if isinstance(v, dict): + all_plot(v, full_name=this_name, exclude=exclude, nspaces=nspaces+4) + else: + if exclude == "" or exclude not in this_name: + _plot_item(v, name=k, full_name=full_name+"/"+k, nspaces=nspaces+4) + + +def plot_priors(): + g0s_prior_mean_bxn = train_modelvals['prior_g0_mean'] + g0s_prior_var_bxn = train_modelvals['prior_g0_var'] + g0s_post_mean_bxn = train_modelvals['posterior_g0_mean'] + g0s_post_var_bxn = train_modelvals['posterior_g0_var'] + + plt.figure(figsize=(10,4), tight_layout=True); + plt.subplot(1,2,1) + plt.hist(g0s_post_mean_bxn.flatten(), bins=20, color='b'); + plt.hist(g0s_prior_mean_bxn.flatten(), bins=20, color='g'); + + plt.title('Histogram of Prior/Posterior Mean Values') + plt.subplot(1,2,2) + plt.hist((g0s_post_var_bxn.flatten()), bins=20, color='b'); + plt.hist((g0s_prior_var_bxn.flatten()), bins=20, color='g'); + plt.title('Histogram of Prior/Posterior Log Variance Values') + + plt.figure(figsize=(10,10), tight_layout=True) + plt.subplot(2,2,1) + plt.imshow(g0s_prior_mean_bxn.T, interpolation='nearest', cmap='jet') + plt.colorbar(fraction=0.025, pad=0.04) + plt.title('Prior g0 means') + + plt.subplot(2,2,2) + plt.imshow(g0s_post_mean_bxn.T, interpolation='nearest', cmap='jet') + plt.colorbar(fraction=0.025, pad=0.04) + plt.title('Posterior g0 means'); + + plt.subplot(2,2,3) + plt.imshow(g0s_prior_var_bxn.T, interpolation='nearest', cmap='jet') + plt.colorbar(fraction=0.025, pad=0.04) + plt.title('Prior g0 variance Values') + + plt.subplot(2,2,4) + plt.imshow(g0s_post_var_bxn.T, interpolation='nearest', cmap='jet') + plt.colorbar(fraction=0.025, pad=0.04) + plt.title('Posterior g0 variance Values') + + plt.figure(figsize=(10,5)) + plt.stem(np.sort(np.log(g0s_post_mean_bxn.std(axis=0)))); + plt.title('Log standard deviation of h0 means'); + + +def plot_time_series(vals_bxtxn, bidx=None, n_to_plot=np.inf, scale=1.0, + color='r', title=None): + + if bidx is None: + vals_txn = np.mean(vals_bxtxn, axis=0) + else: + vals_txn = vals_bxtxn[bidx,:,:] + + T, N = vals_txn.shape + if n_to_plot > N: + n_to_plot = N + + plt.plot(vals_txn[:,0:n_to_plot] + scale*np.array(range(n_to_plot)), + color=color, lw=1.0) + plt.axis('tight') + if title: + plt.title(title) + + +def plot_lfads_timeseries(data_bxtxn, model_vals, ext_input_bxtxi=None, + truth_bxtxn=None, bidx=None, output_dist="poisson", + conversion_factor=1.0, subplot_cidx=0, + col_title=None): + + n_to_plot = 10 + scale = 1.0 + nrows = 7 + plt.subplot(nrows,2,1+subplot_cidx) + + if output_dist == 'poisson': + rates = means = conversion_factor * model_vals['output_dist_params'] + plot_time_series(rates, bidx, n_to_plot=n_to_plot, scale=scale, + title=col_title + " rates (LFADS - red, Truth - black)") + elif output_dist == 'gaussian': + means_vars = model_vals['output_dist_params'] + means, vars = np.split(means_vars,2, axis=2) # bxtxn + stds = np.sqrt(vars) + plot_time_series(means, bidx, n_to_plot=n_to_plot, scale=scale, + title=col_title + " means (LFADS - red, Truth - black)") + plot_time_series(means+stds, bidx, n_to_plot=n_to_plot, scale=scale, + color='c') + plot_time_series(means-stds, bidx, n_to_plot=n_to_plot, scale=scale, + color='c') + else: + assert 'NIY' + + + if truth_bxtxn is not None: + plot_time_series(truth_bxtxn, bidx, n_to_plot=n_to_plot, color='k', + scale=scale) + + input_title = "" + if "controller_outputs" in model_vals.keys(): + input_title += " Controller Output" + plt.subplot(nrows,2,3+subplot_cidx) + u_t = model_vals['controller_outputs'][0:-1] + plot_time_series(u_t, bidx, n_to_plot=n_to_plot, color='c', scale=1.0, + title=col_title + input_title) + + if ext_input_bxtxi is not None: + input_title += " External Input" + plot_time_series(ext_input_bxtxi, n_to_plot=n_to_plot, color='b', + scale=scale, title=col_title + input_title) + + plt.subplot(nrows,2,5+subplot_cidx) + plot_time_series(means, bidx, + n_to_plot=n_to_plot, scale=1.0, + title=col_title + " Spikes (LFADS - red, Spikes - black)") + plot_time_series(data_bxtxn, bidx, n_to_plot=n_to_plot, color='k', scale=1.0) + + plt.subplot(nrows,2,7+subplot_cidx) + plot_time_series(model_vals['factors'], bidx, n_to_plot=n_to_plot, color='b', + scale=2.0, title=col_title + " Factors") + + plt.subplot(nrows,2,9+subplot_cidx) + plot_time_series(model_vals['gen_states'], bidx, n_to_plot=n_to_plot, + color='g', scale=1.0, title=col_title + " Generator State") + + if bidx is not None: + data_nxt = data_bxtxn[bidx,:,:].T + params_nxt = model_vals['output_dist_params'][bidx,:,:].T + else: + data_nxt = np.mean(data_bxtxn, axis=0).T + params_nxt = np.mean(model_vals['output_dist_params'], axis=0).T + if output_dist == 'poisson': + means_nxt = params_nxt + elif output_dist == 'gaussian': # (means+vars) x time + means_nxt = np.vsplit(params_nxt,2)[0] # get means + else: + assert "NIY" + + plt.subplot(nrows,2,11+subplot_cidx) + plt.imshow(data_nxt, aspect='auto', interpolation='nearest') + plt.title(col_title + ' Data') + + plt.subplot(nrows,2,13+subplot_cidx) + plt.imshow(means_nxt, aspect='auto', interpolation='nearest') + plt.title(col_title + ' Means') + + +def plot_lfads(train_bxtxd, train_model_vals, + train_ext_input_bxtxi=None, train_truth_bxtxd=None, + valid_bxtxd=None, valid_model_vals=None, + valid_ext_input_bxtxi=None, valid_truth_bxtxd=None, + bidx=None, cf=1.0, output_dist='poisson'): + + # Plotting + f = plt.figure(figsize=(18,20), tight_layout=True) + plot_lfads_timeseries(train_bxtxd, train_model_vals, + train_ext_input_bxtxi, + truth_bxtxn=train_truth_bxtxd, + conversion_factor=cf, bidx=bidx, + output_dist=output_dist, col_title='Train') + plot_lfads_timeseries(valid_bxtxd, valid_model_vals, + valid_ext_input_bxtxi, + truth_bxtxn=valid_truth_bxtxd, + conversion_factor=cf, bidx=bidx, + output_dist=output_dist, + subplot_cidx=1, col_title='Valid') + + # Convert from figure to an numpy array width x height x 3 (last for RGB) + f.canvas.draw() + data = np.fromstring(f.canvas.tostring_rgb(), dtype=np.uint8, sep='') + data_wxhx3 = data.reshape(f.canvas.get_width_height()[::-1] + (3,)) + plt.close() + + return data_wxhx3 diff --git a/lfads/run_lfads.py b/lfads/run_lfads.py new file mode 100755 index 000000000..74c5bd00a --- /dev/null +++ b/lfads/run_lfads.py @@ -0,0 +1,778 @@ +# Copyright 2017 Google Inc. 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. +# +# ============================================================================== +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from lfads import LFADS +import numpy as np +import os +import tensorflow as tf +import re +import utils + +# Lots of hyperparameters, but most are pretty insensitive. The +# explanation of these hyperparameters is found below, in the flags +# session. + +CHECKPOINT_PB_LOAD_NAME = "checkpoint" +CHECKPOINT_NAME = "lfads_vae" +CSV_LOG = "fitlog" +OUTPUT_FILENAME_STEM = "" +DEVICE = "gpu:0" # "cpu:0", or other gpus, e.g. "gpu:1" +MAX_CKPT_TO_KEEP = 5 +MAX_CKPT_TO_KEEP_LVE = 5 +PS_NEXAMPLES_TO_PROCESS = 1e8 # if larger than number of examples, process all +EXT_INPUT_DIM = 0 +IC_DIM = 64 +FACTORS_DIM = 50 +IC_ENC_DIM = 128 +GEN_DIM = 200 +GEN_CELL_INPUT_WEIGHT_SCALE = 1.0 +GEN_CELL_REC_WEIGHT_SCALE = 1.0 +CELL_WEIGHT_SCALE = 1.0 +BATCH_SIZE = 128 +LEARNING_RATE_INIT = 0.01 +LEARNING_RATE_DECAY_FACTOR = 0.95 +LEARNING_RATE_STOP = 0.00001 +LEARNING_RATE_N_TO_COMPARE = 6 +INJECT_EXT_INPUT_TO_GEN = False +DO_TRAIN_IO_ONLY = False +DO_RESET_LEARNING_RATE = False +FEEDBACK_FACTORS_OR_RATES = "factors" + +# Calibrated just above the average value for the rnn synthetic data. +MAX_GRAD_NORM = 200.0 +CELL_CLIP_VALUE = 5.0 +KEEP_PROB = 0.95 +TEMPORAL_SPIKE_JITTER_WIDTH = 0 +OUTPUT_DISTRIBUTION = 'poisson' # 'poisson' or 'gaussian' +NUM_STEPS_FOR_GEN_IC = np.inf # set to num_steps if greater than num_steps + +DATA_DIR = "/tmp/rnn_synth_data_v1.0/" +DATA_FILENAME_STEM = "chaotic_rnn_inputs_g1p5" +LFADS_SAVE_DIR = "/tmp/lfads_chaotic_rnn_inputs_g1p5/" +CO_DIM = 1 +DO_CAUSAL_CONTROLLER = False +DO_FEED_FACTORS_TO_CONTROLLER = True +CONTROLLER_INPUT_LAG = 1 +PRIOR_AR_AUTOCORRELATION = 10.0 +PRIOR_AR_PROCESS_VAR = 0.1 +DO_TRAIN_PRIOR_AR_ATAU = True +DO_TRAIN_PRIOR_AR_NVAR = True +CI_ENC_DIM = 128 +CON_DIM = 128 +CO_PRIOR_VAR_SCALE = 0.1 +KL_INCREASE_STEPS = 2000 +L2_INCREASE_STEPS = 2000 +L2_GEN_SCALE = 2000.0 +L2_CON_SCALE = 0.0 +# scale of regularizer on time correlation of inferred inputs +CO_MEAN_CORR_SCALE = 0.0 +KL_IC_WEIGHT = 1.0 +KL_CO_WEIGHT = 1.0 +KL_START_STEP = 0 +L2_START_STEP = 0 +IC_PRIOR_VAR_MIN = 0.1 +IC_PRIOR_VAR_SCALE = 0.1 +IC_PRIOR_VAR_MAX = 0.1 +IC_POST_VAR_MIN = 0.0001 # protection from KL blowing up + +flags = tf.app.flags +flags.DEFINE_string("kind", "train", + "Type of model to build {train, \ + posterior_sample_and_average, \ + prior_sample, write_model_params") +flags.DEFINE_string("output_dist", OUTPUT_DISTRIBUTION, + "Type of output distribution, 'poisson' or 'gaussian'") +flags.DEFINE_boolean("allow_gpu_growth", False, + "If true, only allocate amount of memory needed for \ + Session. Otherwise, use full GPU memory.") + +# DATA +flags.DEFINE_string("data_dir", DATA_DIR, "Data for training") +flags.DEFINE_string("data_filename_stem", DATA_FILENAME_STEM, + "Filename stem for data dictionaries.") +flags.DEFINE_string("lfads_save_dir", LFADS_SAVE_DIR, "model save dir") +flags.DEFINE_string("checkpoint_pb_load_name", CHECKPOINT_PB_LOAD_NAME, + "Name of checkpoint files, use 'checkpoint_lve' for best \ + error") +flags.DEFINE_string("checkpoint_name", CHECKPOINT_NAME, + "Name of checkpoint files (.ckpt appended)") +flags.DEFINE_string("output_filename_stem", OUTPUT_FILENAME_STEM, + "Name of output file (postfix will be added)") +flags.DEFINE_string("device", DEVICE, + "Which device to use (default: \"gpu:0\", can also be \ + \"cpu:0\", \"gpu:1\", etc)") +flags.DEFINE_string("csv_log", CSV_LOG, + "Name of file to keep running log of fit likelihoods, \ + etc (.csv appended)") +flags.DEFINE_integer("max_ckpt_to_keep", MAX_CKPT_TO_KEEP, + "Max # of checkpoints to keep (rolling)") +flags.DEFINE_integer("ps_nexamples_to_process", PS_NEXAMPLES_TO_PROCESS, + "Number of examples to process for posterior sample and \ + average (not number of samples to average over).") +flags.DEFINE_integer("max_ckpt_to_keep_lve", MAX_CKPT_TO_KEEP_LVE, + "Max # of checkpoints to keep for lowest validation error \ + models (rolling)") +flags.DEFINE_integer("ext_input_dim", EXT_INPUT_DIM, "Dimension of external \ +inputs") +flags.DEFINE_integer("num_steps_for_gen_ic", NUM_STEPS_FOR_GEN_IC, + "Number of steps to train the generator initial conditon.") + + +# If there are observed inputs, there are two ways to add that observed +# input to the model. The first is by treating as something to be +# inferred, and thus encoding the observed input via the encoders, and then +# input to the generator via the "inferred inputs" channel. Second, one +# can input the input directly into the generator. This has the downside +# of making the generation process strictly dependent on knowing the +# observed input for any generated trial. +flags.DEFINE_boolean("inject_ext_input_to_gen", + INJECT_EXT_INPUT_TO_GEN, + "Should observed inputs be input to model via encoders, \ + or injected directly into generator?") + +# CELL + +# The combined recurrent and input weights of the encoder and +# controller cells are by default set to scale at ws/sqrt(#inputs), +# with ws=1.0. You can change this scaling with this parameter. +flags.DEFINE_float("cell_weight_scale", CELL_WEIGHT_SCALE, + "Input scaling for input weights in generator.") + + +# GENERATION + +# Note that the dimension of the initial conditions is separated from the +# dimensions of the generator initial conditions (and a linear matrix will +# adapt the shapes if necessary). This is just another way to control +# complexity. In all likelihood, setting the ic dims to the size of the +# generator hidden state is just fine. +flags.DEFINE_integer("ic_dim", IC_DIM, "Dimension of h0") +# Setting the dimensions of the factors to something smaller than the data +# dimension is a way to get a reduced dimensionality representation of your +# data. +flags.DEFINE_integer("factors_dim", FACTORS_DIM, + "Number of factors from generator") +flags.DEFINE_integer("ic_enc_dim", IC_ENC_DIM, + "Cell hidden size, encoder of h0") + +# Controlling the size of the generator is one way to control complexity of +# the dynamics (there is also l2, which will squeeze out unnecessary +# dynamics also). The modern deep learning approach is to make these cells +# as large as tolerable (from a waiting perspective), and then regularize +# them to death with drop out or whatever. I don't know if this is correct +# for the LFADS application or not. +flags.DEFINE_integer("gen_dim", GEN_DIM, + "Cell hidden size, generator.") +# The weights of the generator cell by default set to scale at +# ws/sqrt(#inputs), with ws=1.0. You can change ws for +# the input weights or the recurrent weights with these hyperparameters. +flags.DEFINE_float("gen_cell_input_weight_scale", GEN_CELL_INPUT_WEIGHT_SCALE, + "Input scaling for input weights in generator.") +flags.DEFINE_float("gen_cell_rec_weight_scale", GEN_CELL_REC_WEIGHT_SCALE, + "Input scaling for rec weights in generator.") + +# KL DISTRIBUTIONS +# If you don't know what you are donig here, please leave alone, the +# defaults should be fine for most cases, irregardless of other parameters. +# +# If you don't want the prior variance to be learned, set the +# following values to the same thing: ic_prior_var_min, +# ic_prior_var_scale, ic_prior_var_max. The prior mean will be +# learned regardless. +flags.DEFINE_float("ic_prior_var_min", IC_PRIOR_VAR_MIN, + "Minimum variance in posterior h0 codes.") +flags.DEFINE_float("ic_prior_var_scale", IC_PRIOR_VAR_SCALE, + "Variance of ic prior distribution") +flags.DEFINE_float("ic_prior_var_max", IC_PRIOR_VAR_MAX, + "Maximum variance of IC prior distribution.") +# If you really want to limit the information from encoder to decoder, +# Increase ic_post_var_min above 0.0. +flags.DEFINE_float("ic_post_var_min", IC_POST_VAR_MIN, + "Minimum variance of IC posterior distribution.") +flags.DEFINE_float("co_prior_var_scale", CO_PRIOR_VAR_SCALE, + "Variance of control input prior distribution.") + + +flags.DEFINE_float("prior_ar_atau", PRIOR_AR_AUTOCORRELATION, + "Initial autocorrelation of AR(1) priors.") +flags.DEFINE_float("prior_ar_nvar", PRIOR_AR_PROCESS_VAR, + "Initial noise variance for AR(1) priors.") +flags.DEFINE_boolean("do_train_prior_ar_atau", DO_TRAIN_PRIOR_AR_ATAU, + "Is the value for atau an init, or the constant value?") +flags.DEFINE_boolean("do_train_prior_ar_nvar", DO_TRAIN_PRIOR_AR_NVAR, + "Is the value for noise variance an init, or the constant \ + value?") + +# CONTROLLER +# This parameter critically controls whether or not there is a controller +# (along with controller encoders placed into the LFADS graph. If CO_DIM > +# 1, that means there is a 1 dimensional controller outputs, if equal to 0, +# then no controller. +flags.DEFINE_integer("co_dim", CO_DIM, + "Number of control net outputs (>0 builds that graph).") + +# The controller will be more powerful if it can see the encoding of the entire +# trial. However, this allows the controller to create inferred inputs that are +# acausal with respect to the actual data generation process. E.g. the data +# generator could have an input at time t, but the controller, after seeing the +# entirety of the trial could infer that the input is coming a little before +# time t, because there are no restrictions on the data the controller sees. +# One can force the controller to be causal (with respect to perturbations in +# the data generator) so that it only sees forward encodings of the data at time +# t that originate at times before or at time t. One can also control the data +# the controller sees by using an input lag (forward encoding at time [t-tlag] +# for controller input at time t. The same can be done in the reverse direction +# (controller input at time t from reverse encoding at time [t+tlag], in the +# case of an acausal controller). Setting this lag > 0 (even lag=1) can be a +# powerful way of avoiding very spiky decodes. Finally, one can manually control +# whether the factors at time t-1 are fed to the controller at time t. +# +# If you don't care about any of this, and just want to smooth your data, set +# do_causal_controller = False +# do_feed_factors_to_controller = True +# causal_input_lag = 0 +flags.DEFINE_boolean("do_causal_controller", + DO_CAUSAL_CONTROLLER, + "Restrict the controller create only causal inferred \ + inputs?") +# Strictly speaking, feeding either the factors or the rates to the controller +# violates causality, since the g0 gets to see all the data. This may or may not +# be only a theoretical concern. +flags.DEFINE_boolean("do_feed_factors_to_controller", + DO_FEED_FACTORS_TO_CONTROLLER, + "Should factors[t-1] be input to controller at time t?") +flags.DEFINE_string("feedback_factors_or_rates", FEEDBACK_FACTORS_OR_RATES, + "Feedback the factors or the rates to the controller? \ + Acceptable values: 'factors' or 'rates'.") +flags.DEFINE_integer("controller_input_lag", CONTROLLER_INPUT_LAG, + "Time lag on the encoding to controller t-lag for \ + forward, t+lag for reverse.") + +flags.DEFINE_integer("ci_enc_dim", CI_ENC_DIM, + "Cell hidden size, encoder of control inputs") +flags.DEFINE_integer("con_dim", CON_DIM, + "Cell hidden size, controller") + + +# OPTIMIZATION +flags.DEFINE_integer("batch_size", BATCH_SIZE, + "Batch size to use during training.") +flags.DEFINE_float("learning_rate_init", LEARNING_RATE_INIT, + "Learning rate initial value") +flags.DEFINE_float("learning_rate_decay_factor", LEARNING_RATE_DECAY_FACTOR, + "Learning rate decay, decay by this fraction every so \ + often.") +flags.DEFINE_float("learning_rate_stop", LEARNING_RATE_STOP, + "The lr is adaptively reduced, stop training at this value.") +# Rather put the learning rate on an exponentially decreasiong schedule, +# the current algorithm pays attention to the learning rate, and if it +# isn't regularly decreasing, it will decrease the learning rate. So far, +# it works fine, though it is not perfect. +flags.DEFINE_integer("learning_rate_n_to_compare", LEARNING_RATE_N_TO_COMPARE, + "Number of previous costs current cost has to be worse \ + than, to lower learning rate.") + +# This sets a value, above which, the gradients will be clipped. This hp +# is extremely useful to avoid an infrequent, but highly pathological +# problem whereby the gradient is so large that it destroys the +# optimziation by setting parameters too large, leading to a vicious cycle +# that ends in NaNs. If it's too large, it's useless, if it's too small, +# it essentially becomes the learning rate. It's pretty insensitive, though. +flags.DEFINE_float("max_grad_norm", MAX_GRAD_NORM, + "Max norm of gradient before clipping.") + +# If your optimizations start "NaN-ing out", reduce this value so that +# the values of the network don't grow out of control. Typically, once +# this parameter is set to a reasonable value, one stops having numerical +# problems. +flags.DEFINE_float("cell_clip_value", CELL_CLIP_VALUE, + "Max value recurrent cell can take before being clipped.") + +# This flag is used for an experiment where one sees if training a model with +# many days data can be used to learn the dynamics from a held-out days data. +# If you don't care about that particular experiment, this flag should always be +# false. +flags.DEFINE_boolean("do_train_io_only", DO_TRAIN_IO_ONLY, + "Train only the input (readin) and output (readout) \ + affine functions.") + +flags.DEFINE_boolean("do_reset_learning_rate", DO_RESET_LEARNING_RATE, + "Reset the learning rate to initial value.") + + +# OVERFITTING +# Dropout is done on the input data, on controller inputs (from +# encoder), on outputs from generator to factors. +flags.DEFINE_float("keep_prob", KEEP_PROB, "Dropout keep probability.") +# It appears that the system will happily fit spikes (blessing or +# curse, depending). You may not want this. Jittering the spikes a +# bit will help (-/+ bin size, as specified here). +flags.DEFINE_integer("temporal_spike_jitter_width", + TEMPORAL_SPIKE_JITTER_WIDTH, + "Shuffle spikes around this window.") + +# General note about helping ascribe controller inputs vs dynamics: +# +# If controller is heavily penalized, then it won't have any output. +# If dynamics are heavily penalized, then generator won't make +# dynamics. Note this l2 penalty is only on the recurrent portion of +# the RNNs, as dropout is also available, penalizing the feed-forward +# connections. +flags.DEFINE_float("l2_gen_scale", L2_GEN_SCALE, + "L2 regularization cost for the generator only.") +flags.DEFINE_float("l2_con_scale", L2_CON_SCALE, + "L2 regularization cost for the controller only.") +flags.DEFINE_float("co_mean_corr_scale", CO_MEAN_CORR_SCALE, + "Cost of correlation (thru time)in the means of \ + controller output.") + +# UNDERFITTING +# If the primary task of LFADS is "filtering" of data and not +# generation, then it is possible that the KL penalty is too strong. +# Empirically, we have found this to be the case. So we add a +# hyperparameter in front of the the two KL terms (one for the initial +# conditions to the generator, the other for the controller outputs). +# You should always think of the the default values as 1.0, and that +# leads to a standard VAE formulation whereby the numbers that are +# optimized are a lower-bound on the log-likelihood of the data. When +# these 2 HPs deviate from 1.0, one cannot make any statement about +# what those LL lower bounds mean anymore, and they cannot be compared +# (AFAIK). +flags.DEFINE_float("kl_ic_weight", KL_IC_WEIGHT, + "Strength of KL weight on initial conditions KL penatly.") +flags.DEFINE_float("kl_co_weight", KL_CO_WEIGHT, + "Strength of KL weight on controller output KL penalty.") + +# Sometimes the task can be sufficiently hard to learn that the +# optimizer takes the 'easy route', and simply minimizes the KL +# divergence, setting it to near zero, and the optimization gets +# stuck. These two parameters will help avoid that by by getting the +# optimization to 'latch' on to the main optimization, and only +# turning in the regularizers later. +flags.DEFINE_integer("kl_start_step", KL_START_STEP, + "Start increasing weight after this many steps.") +# training passes, not epochs, increase by 0.5 every kl_increase_steps +flags.DEFINE_integer("kl_increase_steps", KL_INCREASE_STEPS, + "Increase weight of kl cost to avoid local minimum.") +# Same story for l2 regularizer. One wants a simple generator, for scientific +# reasons, but not at the expense of hosing the optimization. +flags.DEFINE_integer("l2_start_step", L2_START_STEP, + "Start increasing l2 weight after this many steps.") +flags.DEFINE_integer("l2_increase_steps", L2_INCREASE_STEPS, + "Increase weight of l2 cost to avoid local minimum.") + +FLAGS = flags.FLAGS + + +def build_model(hps, kind="train", datasets=None): + """Builds a model from either random initialization, or saved parameters. + + Args: + hps: The hyper parameters for the model. + kind: (optional) The kind of model to build. Training vs inference require + different graphs. + datasets: The datasets structure (see top of lfads.py). + + Returns: + an LFADS model. + """ + + build_kind = kind + if build_kind == "write_model_params": + build_kind = "train" + with tf.variable_scope("LFADS", reuse=None): + model = LFADS(hps, kind=build_kind, datasets=datasets) + + if not os.path.exists(hps.lfads_save_dir): + print("Save directory %s does not exist, creating it." % hps.lfads_save_dir) + os.makedirs(hps.lfads_save_dir) + + cp_pb_ln = hps.checkpoint_pb_load_name + cp_pb_ln = 'checkpoint' if cp_pb_ln == "" else cp_pb_ln + if cp_pb_ln == 'checkpoint': + print("Loading latest training checkpoint in: ", hps.lfads_save_dir) + saver = model.seso_saver + elif cp_pb_ln == 'checkpoint_lve': + print("Loading lowest validation checkpoint in: ", hps.lfads_save_dir) + saver = model.lve_saver + else: + print("Loading checkpoint: ", cp_pb_ln, ", in: ", hps.lfads_save_dir) + saver = model.seso_saver + + ckpt = tf.train.get_checkpoint_state(hps.lfads_save_dir, + latest_filename=cp_pb_ln) + + session = tf.get_default_session() + print("ckpt: ", ckpt) + if ckpt and tf.train.checkpoint_exists(ckpt.model_checkpoint_path): + print("Reading model parameters from %s" % ckpt.model_checkpoint_path) + saver.restore(session, ckpt.model_checkpoint_path) + else: + print("Created model with fresh parameters.") + if kind in ["posterior_sample_and_average", "prior_sample", + "write_model_params"]: + print("Possible error!!! You are running ", kind, " on a newly \ + initialized model!") + print("Are you sure you sure ", ckpt.model_checkpoint_path, " exists?") + + tf.global_variables_initializer().run() + + if ckpt: + train_step_str = re.search('-[0-9]+$', ckpt.model_checkpoint_path).group() + else: + train_step_str = '-0' + + fname = 'hyperparameters' + train_step_str + '.txt' + hp_fname = os.path.join(hps.lfads_save_dir, fname) + hps_for_saving = jsonify_dict(hps) + utils.write_data(hp_fname, hps_for_saving, use_json=True) + + return model + + +def jsonify_dict(d): + """Turns python booleans into strings so hps dict can be written in json. + Creates a shallow-copied dictionary first, then accomplishes string + conversion. + + Args: + d: hyperparameter dictionary + + Returns: hyperparameter dictionary with bool's as strings + """ + + d2 = d.copy() # shallow copy is fine by assumption of d being shallow + def jsonify_bool(boolean_value): + if boolean_value: + return "true" + else: + return "false" + + for key in d2.keys(): + if isinstance(d2[key], bool): + d2[key] = jsonify_bool(d2[key]) + return d2 + + +def build_hyperparameter_dict(flags): + """Simple script for saving hyper parameters. Under the hood the + flags structure isn't a dictionary, so it has to be simplified since we + want to be able to view file as text. + + Args: + flags: From tf.app.flags + + Returns: + dictionary of hyper parameters (ignoring other flag types). + """ + d = {} + # Data + d['output_dist'] = flags.output_dist + d['data_dir'] = flags.data_dir + d['lfads_save_dir'] = flags.lfads_save_dir + d['checkpoint_pb_load_name'] = flags.checkpoint_pb_load_name + d['checkpoint_name'] = flags.checkpoint_name + d['output_filename_stem'] = flags.output_filename_stem + d['max_ckpt_to_keep'] = flags.max_ckpt_to_keep + d['max_ckpt_to_keep_lve'] = flags.max_ckpt_to_keep_lve + d['ps_nexamples_to_process'] = flags.ps_nexamples_to_process + d['ext_input_dim'] = flags.ext_input_dim + d['data_filename_stem'] = flags.data_filename_stem + d['device'] = flags.device + d['csv_log'] = flags.csv_log + d['num_steps_for_gen_ic'] = flags.num_steps_for_gen_ic + d['inject_ext_input_to_gen'] = flags.inject_ext_input_to_gen + # Cell + d['cell_weight_scale'] = flags.cell_weight_scale + # Generation + d['ic_dim'] = flags.ic_dim + d['factors_dim'] = flags.factors_dim + d['ic_enc_dim'] = flags.ic_enc_dim + d['gen_dim'] = flags.gen_dim + d['gen_cell_input_weight_scale'] = flags.gen_cell_input_weight_scale + d['gen_cell_rec_weight_scale'] = flags.gen_cell_rec_weight_scale + # KL distributions + d['ic_prior_var_min'] = flags.ic_prior_var_min + d['ic_prior_var_scale'] = flags.ic_prior_var_scale + d['ic_prior_var_max'] = flags.ic_prior_var_max + d['ic_post_var_min'] = flags.ic_post_var_min + d['co_prior_var_scale'] = flags.co_prior_var_scale + d['prior_ar_atau'] = flags.prior_ar_atau + d['prior_ar_nvar'] = flags.prior_ar_nvar + d['do_train_prior_ar_atau'] = flags.do_train_prior_ar_atau + d['do_train_prior_ar_nvar'] = flags.do_train_prior_ar_nvar + # Controller + d['do_causal_controller'] = flags.do_causal_controller + d['controller_input_lag'] = flags.controller_input_lag + d['do_feed_factors_to_controller'] = flags.do_feed_factors_to_controller + d['feedback_factors_or_rates'] = flags.feedback_factors_or_rates + d['co_dim'] = flags.co_dim + d['ci_enc_dim'] = flags.ci_enc_dim + d['con_dim'] = flags.con_dim + d['co_mean_corr_scale'] = flags.co_mean_corr_scale + # Optimization + d['batch_size'] = flags.batch_size + d['learning_rate_init'] = flags.learning_rate_init + d['learning_rate_decay_factor'] = flags.learning_rate_decay_factor + d['learning_rate_stop'] = flags.learning_rate_stop + d['learning_rate_n_to_compare'] = flags.learning_rate_n_to_compare + d['max_grad_norm'] = flags.max_grad_norm + d['cell_clip_value'] = flags.cell_clip_value + d['do_train_io_only'] = flags.do_train_io_only + d['do_reset_learning_rate'] = flags.do_reset_learning_rate + + # Overfitting + d['keep_prob'] = flags.keep_prob + d['temporal_spike_jitter_width'] = flags.temporal_spike_jitter_width + d['l2_gen_scale'] = flags.l2_gen_scale + d['l2_con_scale'] = flags.l2_con_scale + # Underfitting + d['kl_ic_weight'] = flags.kl_ic_weight + d['kl_co_weight'] = flags.kl_co_weight + d['kl_start_step'] = flags.kl_start_step + d['kl_increase_steps'] = flags.kl_increase_steps + d['l2_start_step'] = flags.l2_start_step + d['l2_increase_steps'] = flags.l2_increase_steps + + return d + + +class hps_dict_to_obj(dict): + """Helper class allowing us to access hps dictionary more easily.""" + + def __getattr__(self, key): + if key in self: + return self[key] + else: + assert False, ("%s does not exist." % key) + def __setattr__(self, key, value): + self[key] = value + + +def train(hps, datasets): + """Train the LFADS model. + + Args: + hps: The dictionary of hyperparameters. + datasets: A dictionary of data dictionaries. The dataset dict is simply a + name(string)-> data dictionary mapping (See top of lfads.py). + """ + model = build_model(hps, kind="train", datasets=datasets) + if hps.do_reset_learning_rate: + sess = tf.get_default_session() + sess.run(model.learning_rate.initializer) + + model.train_model(datasets) + + +def write_model_runs(hps, datasets, output_fname=None): + """Run the model on the data in data_dict, and save the computed values. + + LFADS generates a number of outputs for each examples, and these are all + saved. They are: + The mean and variance of the prior of g0. + The mean and variance of approximate posterior of g0. + The control inputs (if enabled) + The initial conditions, g0, for all examples. + The generator states for all time. + The factors for all time. + The rates for all time. + + Args: + hps: The dictionary of hyperparameters. + datasets: A dictionary of data dictionaries. The dataset dict is simply a + name(string)-> data dictionary mapping (See top of lfads.py). + output_fname (optional): output filename stem to write the model runs. + """ + model = build_model(hps, kind=hps.kind, datasets=datasets) + model.write_model_runs(datasets, output_fname) + + +def write_model_samples(hps, datasets, dataset_name=None, output_fname=None): + """Use the prior distribution to generate samples from the model. + Generates batch_size number of samples (set through FLAGS). + + LFADS generates a number of outputs for each examples, and these are all + saved. They are: + The mean and variance of the prior of g0. + The control inputs (if enabled) + The initial conditions, g0, for all examples. + The generator states for all time. + The factors for all time. + The output distribution parameters (e.g. rates) for all time. + + Args: + hps: The dictionary of hyperparameters. + datasets: A dictionary of data dictionaries. The dataset dict is simply a + name(string)-> data dictionary mapping (See top of lfads.py). + dataset_name: The name of the dataset to grab the factors -> rates + alignment matrices from. Only a concern with models trained on + multi-session data. By default, uses the first dataset in the data dict. + output_fname: The name prefix of the file in which to save the generated + samples. + """ + if not output_fname: + output_fname = "model_runs_" + hps.kind + else: + output_fname = output_fname + "model_runs_" + hps.kind + if not dataset_name: + dataset_name = datasets.keys()[0] + else: + if dataset_name not in datasets.keys(): + raise ValueError("Invalid dataset name '%s'."%(dataset_name)) + model = build_model(hps, kind=hps.kind, datasets=datasets) + model.write_model_samples(dataset_name, output_fname) + + +def write_model_parameters(hps, output_fname=None, datasets=None): + """Save all the model parameters + + Save all the parameters to hps.lfads_save_dir. + + Args: + hps: The dictionary of hyperparameters. + output_fname: The prefix of the file in which to save the generated + samples. + datasets: A dictionary of data dictionaries. The dataset dict is simply a + name(string)-> data dictionary mapping (See top of lfads.py). + """ + if not output_fname: + output_fname = "model_params" + else: + output_fname = output_fname + "_model_params" + fname = os.path.join(hps.lfads_save_dir, output_fname) + print("Writing model parameters to: ", fname) + # save the optimizer params as well + model = build_model(hps, kind="write_model_params", datasets=datasets) + model_params = model.eval_model_parameters(use_nested=False, + include_strs="LFADS") + utils.write_data(fname, model_params, compression=None) + print("Done.") + + +def clean_data_dict(data_dict): + """Add some key/value pairs to the data dict, if they are missing. + Args: + data_dict - dictionary containing data for LFADS + Returns: + data_dict with some keys filled in, if they are absent. + """ + + keys = ['train_truth', 'train_ext_input', 'valid_data', + 'valid_truth', 'valid_ext_input', 'valid_train'] + for k in keys: + if k not in data_dict: + data_dict[k] = None + + return data_dict + + +def load_datasets(data_dir, data_filename_stem): + """Load the datasets from a specified directory. + + Example files look like + >data_dir/my_dataset_first_day + >data_dir/my_dataset_second_day + + If my_dataset (filename) stem is in the directory, the read routine will try + and load it. The datasets dictionary will then look like + dataset['first_day'] -> (first day data dictionary) + dataset['second_day'] -> (first day data dictionary) + + Args: + data_dir: The directory from which to load the datasets. + data_filename_stem: The stem of the filename for the datasets. + + Returns: + datasets: a dataset dictionary, with one name->data dictionary pair for + each dataset file. + """ + print("Reading data from ", data_dir) + datasets = utils.read_datasets(data_dir, data_filename_stem) + for k, data_dict in datasets.items(): + datasets[k] = clean_data_dict(data_dict) + + train_total_size = len(data_dict['train_data']) + if train_total_size == 0: + print("Did not load training set.") + else: + print("Found training set with number examples: ", train_total_size) + + valid_total_size = len(data_dict['valid_data']) + if valid_total_size == 0: + print("Did not load validation set.") + else: + print("Found validation set with number examples: ", valid_total_size) + + return datasets + + +def main(_): + """Get this whole shindig off the ground.""" + d = build_hyperparameter_dict(FLAGS) + hps = hps_dict_to_obj(d) # hyper parameters + kind = FLAGS.kind + + # Read the data, if necessary. + train_set = valid_set = None + if kind in ["train", "posterior_sample_and_average", "prior_sample", + "write_model_params"]: + datasets = load_datasets(hps.data_dir, hps.data_filename_stem) + else: + raise ValueError('Kind {} is not supported.'.format(kind)) + + # infer the dataset names and dataset dimensions from the loaded files + hps.kind = kind # needs to be added here, cuz not saved as hyperparam + hps.dataset_names = [] + hps.dataset_dims = {} + for key in datasets: + hps.dataset_names.append(key) + hps.dataset_dims[key] = datasets[key]['data_dim'] + + # also store down the dimensionality of the data + # - just pull from one set, required to be same for all sets + hps.num_steps = datasets.values()[0]['num_steps'] + hps.ndatasets = len(hps.dataset_names) + + if hps.num_steps_for_gen_ic > hps.num_steps: + hps.num_steps_for_gen_ic = hps.num_steps + + # Build and run the model, for varying purposes. + config = tf.ConfigProto(allow_soft_placement=True, + log_device_placement=False) + if FLAGS.allow_gpu_growth: + config.gpu_options.allow_growth = True + sess = tf.Session(config=config) + with sess.as_default(): + with tf.device(hps.device): + if kind == "train": + train(hps, datasets) + elif kind == "posterior_sample_and_average": + write_model_runs(hps, datasets, hps.output_filename_stem) + elif kind == "prior_sample": + write_model_samples(hps, datasets, hps.output_filename_stem) + elif kind == "write_model_params": + write_model_parameters(hps, hps.output_filename_stem, datasets) + else: + assert False, ("Kind %s is not implemented. " % kind) + + +if __name__ == "__main__": + tf.app.run() + diff --git a/lfads/synth_data/generate_chaotic_rnn_data.py b/lfads/synth_data/generate_chaotic_rnn_data.py new file mode 100644 index 000000000..a89936df6 --- /dev/null +++ b/lfads/synth_data/generate_chaotic_rnn_data.py @@ -0,0 +1,193 @@ +# Copyright 2017 Google Inc. 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. +# +# ============================================================================== +from __future__ import print_function + +import h5py +import numpy as np +import os +import tensorflow as tf # used for flags here + +from utils import write_datasets +from synthetic_data_utils import add_alignment_projections, generate_data +from synthetic_data_utils import generate_rnn, get_train_n_valid_inds +from synthetic_data_utils import nparray_and_transpose +from synthetic_data_utils import spikify_data, split_list_by_inds +import matplotlib +import matplotlib.pyplot as plt +import scipy.signal + +matplotlib.rcParams['image.interpolation'] = 'nearest' +DATA_DIR = "rnn_synth_data_v1.0" + +flags = tf.app.flags +flags.DEFINE_string("save_dir", "/tmp/" + DATA_DIR + "/", + "Directory for saving data.") +flags.DEFINE_string("datafile_name", "thits_data", + "Name of data file for input case.") +flags.DEFINE_integer("synth_data_seed", 5, "Random seed for RNN generation.") +flags.DEFINE_float("T", 1.0, "Time in seconds to generate.") +flags.DEFINE_integer("C", 100, "Number of conditions") +flags.DEFINE_integer("N", 50, "Number of units for the RNN") +flags.DEFINE_integer("S", 50, "Number of sampled units from RNN") +flags.DEFINE_integer("npcs", 10, "Number of PCS for multi-session case.") +flags.DEFINE_float("train_percentage", 4.0/5.0, + "Percentage of train vs validation trials") +flags.DEFINE_integer("nspikifications", 40, + "Number of spikifications of the same underlying rates.") +flags.DEFINE_float("g", 1.5, "Complexity of dynamics") +flags.DEFINE_float("x0_std", 1.0, + "Volume from which to pull initial conditions (affects diversity of dynamics.") +flags.DEFINE_float("tau", 0.025, "Time constant of RNN") +flags.DEFINE_float("dt", 0.010, "Time bin") +flags.DEFINE_float("input_magnitude", 20.0, + "For the input case, what is the value of the input?") +flags.DEFINE_float("max_firing_rate", 30.0, "Map 1.0 of RNN to a spikes per second") +FLAGS = flags.FLAGS + + +# Note that with N small, (as it is 25 above), the finite size effects +# will have pretty dramatic effects on the dynamics of the random RNN. +# If you want more complex dynamics, you'll have to run the script a +# lot, or increase N (or g). + +# Getting hard vs. easy data can be a little stochastic, so we set the seed. + +# Pull out some commonly used parameters. +# These are user parameters (configuration) +rng = np.random.RandomState(seed=FLAGS.synth_data_seed) +T = FLAGS.T +C = FLAGS.C +N = FLAGS.N +S = FLAGS.S +input_magnitude = FLAGS.input_magnitude +nspikifications = FLAGS.nspikifications +E = nspikifications * C # total number of trials +# S is the number of measurements in each datasets, w/ each +# dataset having a different set of observations. +ndatasets = N/S # ok if rounded down +train_percentage = FLAGS.train_percentage +ntime_steps = int(T / FLAGS.dt) +# End of user parameters + +rnn = generate_rnn(rng, N, FLAGS.g, FLAGS.tau, FLAGS.dt, FLAGS.max_firing_rate) + +# Check to make sure the RNN is the one we used in the paper. +if N == 50: + assert abs(rnn['W'][0,0] - 0.06239899) < 1e-8, 'Error in random seed?' + rem_check = nspikifications * train_percentage + assert abs(rem_check - int(rem_check)) < 1e-8, \ + 'Train percentage * nspikifications should be integral number.' + + +# Initial condition generation, and condition label generation. This +# happens outside of the dataset loop, so that all datasets have the +# same conditions, which is similar to a neurophys setup. +condition_number = 0 +x0s = [] +condition_labels = [] +for c in range(C): + x0 = FLAGS.x0_std * rng.randn(N, 1) + x0s.append(np.tile(x0, nspikifications)) # replicate x0 nspikifications times + # replicate the condition label nspikifications times + for ns in range(nspikifications): + condition_labels.append(condition_number) + condition_number += 1 +x0s = np.concatenate(x0s, axis=1) + +# Containers for storing data across data. +datasets = {} +for n in range(ndatasets): + print(n+1, " of ", ndatasets) + + # First generate all firing rates. in the next loop, generate all + # spikifications this allows the random state for rate generation to be + # independent of n_spikifications. + dataset_name = 'dataset_N' + str(N) + '_S' + str(S) + if S < N: + dataset_name += '_n' + str(n+1) + + # Sample neuron subsets. The assumption is the PC axes of the RNN + # are not unit aligned, so sampling units is adequate to sample all + # the high-variance PCs. + P_sxn = np.eye(S,N) + for m in range(n): + P_sxn = np.roll(P_sxn, S, axis=1) + + if input_magnitude > 0.0: + # time of "hits" randomly chosen between [1/4 and 3/4] of total time + input_times = rng.choice(int(ntime_steps/2), size=[E]) + int(ntime_steps/4) + else: + input_times = None + + rates, x0s, inputs = \ + generate_data(rnn, T=T, E=E, x0s=x0s, P_sxn=P_sxn, + input_magnitude=input_magnitude, + input_times=input_times) + spikes = spikify_data(rates, rng, rnn['dt'], rnn['max_firing_rate']) + + # split into train and validation sets + train_inds, valid_inds = get_train_n_valid_inds(E, train_percentage, + nspikifications) + + # Split the data, inputs, labels and times into train vs. validation. + rates_train, rates_valid = \ + split_list_by_inds(rates, train_inds, valid_inds) + spikes_train, spikes_valid = \ + split_list_by_inds(spikes, train_inds, valid_inds) + input_train, inputs_valid = \ + split_list_by_inds(inputs, train_inds, valid_inds) + condition_labels_train, condition_labels_valid = \ + split_list_by_inds(condition_labels, train_inds, valid_inds) + input_times_train, input_times_valid = \ + split_list_by_inds(input_times, train_inds, valid_inds) + + # Turn rates, spikes, and input into numpy arrays. + rates_train = nparray_and_transpose(rates_train) + rates_valid = nparray_and_transpose(rates_valid) + spikes_train = nparray_and_transpose(spikes_train) + spikes_valid = nparray_and_transpose(spikes_valid) + input_train = nparray_and_transpose(input_train) + inputs_valid = nparray_and_transpose(inputs_valid) + + # Note that we put these 'truth' rates and input into this + # structure, the only data that is used in LFADS are the spike + # trains. The rest is either for printing or posterity. + data = {'train_truth': rates_train, + 'valid_truth': rates_valid, + 'input_train_truth' : input_train, + 'input_valid_truth' : inputs_valid, + 'train_data' : spikes_train, + 'valid_data' : spikes_valid, + 'train_percentage' : train_percentage, + 'nspikifications' : nspikifications, + 'dt' : rnn['dt'], + 'input_magnitude' : input_magnitude, + 'input_times_train' : input_times_train, + 'input_times_valid' : input_times_valid, + 'P_sxn' : P_sxn, + 'condition_labels_train' : condition_labels_train, + 'condition_labels_valid' : condition_labels_valid, + 'conversion_factor': 1.0 / rnn['conversion_factor']} + datasets[dataset_name] = data + +if S < N: + # Note that this isn't necessary for this synthetic example, but + # it's useful to see how the input factor matrices were initialized + # for actual neurophysiology data. + datasets = add_alignment_projections(datasets, npcs=FLAGS.npcs) + +# Write out the datasets. +write_datasets(FLAGS.save_dir, FLAGS.datafile_name, datasets) diff --git a/lfads/synth_data/generate_itb_data.py b/lfads/synth_data/generate_itb_data.py new file mode 100644 index 000000000..e2e54179e --- /dev/null +++ b/lfads/synth_data/generate_itb_data.py @@ -0,0 +1,208 @@ +# Copyright 2017 Google Inc. 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. +# +# ============================================================================== +from __future__ import print_function + +import h5py +import numpy as np +import os +import tensorflow as tf + +from utils import write_datasets +from synthetic_data_utils import normalize_rates +from synthetic_data_utils import get_train_n_valid_inds, nparray_and_transpose +from synthetic_data_utils import spikify_data, split_list_by_inds + +DATA_DIR = "rnn_synth_data_v1.0" + +flags = tf.app.flags +flags.DEFINE_string("save_dir", "/tmp/" + DATA_DIR + "/", + "Directory for saving data.") +flags.DEFINE_string("datafile_name", "itb_rnn", + "Name of data file for input case.") +flags.DEFINE_integer("synth_data_seed", 5, "Random seed for RNN generation.") +flags.DEFINE_float("T", 1.0, "Time in seconds to generate.") +flags.DEFINE_integer("C", 800, "Number of conditions") +flags.DEFINE_integer("N", 50, "Number of units for the RNN") +flags.DEFINE_float("train_percentage", 4.0/5.0, + "Percentage of train vs validation trials") +flags.DEFINE_integer("nspikifications", 5, + "Number of spikifications of the same underlying rates.") +flags.DEFINE_float("tau", 0.025, "Time constant of RNN") +flags.DEFINE_float("dt", 0.010, "Time bin") +flags.DEFINE_float("max_firing_rate", 30.0, + "Map 1.0 of RNN to a spikes per second") +flags.DEFINE_float("u_std", 0.25, + "Std dev of input to integration to bound model") +flags.DEFINE_string("checkpoint_path", "SAMPLE_CHECKPOINT", + """Path to directory with checkpoints of model + trained on integration to bound task. Currently this + is a placeholder which tells the code to grab the + checkpoint that is provided with the code + (in /trained_itb/..). If you have your own checkpoint + you would like to restore, you would point it to + that path.""") +FLAGS = flags.FLAGS + + +class IntegrationToBoundModel: + def __init__(self, N): + scale = 0.8 / float(N**0.5) + self.N = N + self.Wh_nxn = tf.Variable(tf.random_normal([N, N], stddev=scale)) + self.b_1xn = tf.Variable(tf.zeros([1, N])) + self.Bu_1xn = tf.Variable(tf.zeros([1, N])) + self.Wro_nxo = tf.Variable(tf.random_normal([N, 1], stddev=scale)) + self.bro_o = tf.Variable(tf.zeros([1])) + + def call(self, h_tm1_bxn, u_bx1): + act_t_bxn = tf.matmul(h_tm1_bxn, self.Wh_nxn) + self.b_1xn + u_bx1 * self.Bu_1xn + h_t_bxn = tf.nn.tanh(act_t_bxn) + z_t = tf.nn.xw_plus_b(h_t_bxn, self.Wro_nxo, self.bro_o) + return z_t, h_t_bxn + +def get_data_batch(batch_size, T, rng, u_std): + u_bxt = rng.randn(batch_size, T) * u_std + running_sum_b = np.zeros([batch_size]) + labels_bxt = np.zeros([batch_size, T]) + for t in xrange(T): + running_sum_b += u_bxt[:, t] + labels_bxt[:, t] += running_sum_b + labels_bxt = np.clip(labels_bxt, -1, 1) + return u_bxt, labels_bxt + + +rng = np.random.RandomState(seed=FLAGS.synth_data_seed) +u_rng = np.random.RandomState(seed=FLAGS.synth_data_seed+1) +T = FLAGS.T +C = FLAGS.C +N = FLAGS.N # must be same N as in trained model (provided example is N = 50) +nspikifications = FLAGS.nspikifications +E = nspikifications * C # total number of trials +train_percentage = FLAGS.train_percentage +ntimesteps = int(T / FLAGS.dt) +batch_size = 1 # gives one example per ntrial + +model = IntegrationToBoundModel(N) +inputs_ph_t = [tf.placeholder(tf.float32, + shape=[None, 1]) for _ in range(ntimesteps)] +state = tf.zeros([batch_size, N]) +saver = tf.train.Saver() + +P_nxn = rng.randn(N,N) / np.sqrt(N) # random projections + +# unroll RNN for T timesteps +outputs_t = [] +states_t = [] + +for inp in inputs_ph_t: + output, state = model.call(state, inp) + outputs_t.append(output) + states_t.append(state) + +with tf.Session() as sess: + # restore the latest model ckpt + if FLAGS.checkpoint_path == "SAMPLE_CHECKPOINT": + dir_path = os.path.dirname(os.path.realpath(__file__)) + model_checkpoint_path = os.path.join(dir_path, "trained_itb/model-65000") + else: + model_checkpoint_path = FLAGS.checkpoint_path + try: + saver.restore(sess, model_checkpoint_path) + print ('Model restored from', model_checkpoint_path) + except: + assert False, ("No checkpoints to restore from, is the path %s correct?" + %model_checkpoint_path) + + # generate data for trials + data_e = [] + u_e = [] + outs_e = [] + for c in range(C): + u_1xt, outs_1xt = get_data_batch(batch_size, ntimesteps, u_rng, FLAGS.u_std) + + feed_dict = {} + for t in xrange(ntimesteps): + feed_dict[inputs_ph_t[t]] = np.reshape(u_1xt[:,t], (batch_size,-1)) + + states_t_bxn, outputs_t_bxn = sess.run([states_t, outputs_t], + feed_dict=feed_dict) + states_nxt = np.transpose(np.squeeze(np.asarray(states_t_bxn))) + outputs_t_bxn = np.squeeze(np.asarray(outputs_t_bxn)) + r_sxt = np.dot(P_nxn, states_nxt) + + for s in xrange(nspikifications): + data_e.append(r_sxt) + u_e.append(u_1xt) + outs_e.append(outputs_t_bxn) + + truth_data_e = normalize_rates(data_e, E, N) + +spiking_data_e = spikify_data(truth_data_e, rng, dt=FLAGS.dt, + max_firing_rate=FLAGS.max_firing_rate) +train_inds, valid_inds = get_train_n_valid_inds(E, train_percentage, + nspikifications) + +data_train_truth, data_valid_truth = split_list_by_inds(truth_data_e, + train_inds, + valid_inds) +data_train_spiking, data_valid_spiking = split_list_by_inds(spiking_data_e, + train_inds, + valid_inds) + +data_train_truth = nparray_and_transpose(data_train_truth) +data_valid_truth = nparray_and_transpose(data_valid_truth) +data_train_spiking = nparray_and_transpose(data_train_spiking) +data_valid_spiking = nparray_and_transpose(data_valid_spiking) + +# save down the inputs used to generate this data +train_inputs_u, valid_inputs_u = split_list_by_inds(u_e, + train_inds, + valid_inds) +train_inputs_u = nparray_and_transpose(train_inputs_u) +valid_inputs_u = nparray_and_transpose(valid_inputs_u) + +# save down the network outputs (may be useful later) +train_outputs_u, valid_outputs_u = split_list_by_inds(outs_e, + train_inds, + valid_inds) +train_outputs_u = np.array(train_outputs_u) +valid_outputs_u = np.array(valid_outputs_u) + + +data = { 'train_truth': data_train_truth, + 'valid_truth': data_valid_truth, + 'train_data' : data_train_spiking, + 'valid_data' : data_valid_spiking, + 'train_percentage' : train_percentage, + 'nspikifications' : nspikifications, + 'dt' : FLAGS.dt, + 'u_std' : FLAGS.u_std, + 'max_firing_rate': FLAGS.max_firing_rate, + 'train_inputs_u': train_inputs_u, + 'valid_inputs_u': valid_inputs_u, + 'train_outputs_u': train_outputs_u, + 'valid_outputs_u': valid_outputs_u, + 'conversion_factor' : FLAGS.max_firing_rate/(1.0/FLAGS.dt) } + +# just one dataset here +datasets = {} +dataset_name = 'dataset_N' + str(N) +datasets[dataset_name] = data + +# write out the dataset +write_datasets(FLAGS.save_dir, FLAGS.datafile_name, datasets) +print ('Saved to ', os.path.join(FLAGS.save_dir, + FLAGS.datafile_name + '_' + dataset_name)) diff --git a/lfads/synth_data/generate_labeled_rnn_data.py b/lfads/synth_data/generate_labeled_rnn_data.py new file mode 100644 index 000000000..8cb40908a --- /dev/null +++ b/lfads/synth_data/generate_labeled_rnn_data.py @@ -0,0 +1,146 @@ +# Copyright 2017 Google Inc. 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. +# +# ============================================================================== +from __future__ import print_function + +import os +import h5py +import numpy as np + +from synthetic_data_utils import generate_data, generate_rnn +from synthetic_data_utils import get_train_n_valid_inds +from synthetic_data_utils import nparray_and_transpose +from synthetic_data_utils import spikify_data, split_list_by_inds +import tensorflow as tf +from utils import write_datasets + +DATA_DIR = "rnn_synth_data_v1.0" + +flags = tf.app.flags +flags.DEFINE_string("save_dir", "/tmp/" + DATA_DIR + "/", + "Directory for saving data.") +flags.DEFINE_string("datafile_name", "conditioned_rnn_data", + "Name of data file for input case.") +flags.DEFINE_integer("synth_data_seed", 5, "Random seed for RNN generation.") +flags.DEFINE_float("T", 1.0, "Time in seconds to generate.") +flags.DEFINE_integer("C", 400, "Number of conditions") +flags.DEFINE_integer("N", 50, "Number of units for the RNN") +flags.DEFINE_float("train_percentage", 4.0/5.0, + "Percentage of train vs validation trials") +flags.DEFINE_integer("nspikifications", 10, + "Number of spikifications of the same underlying rates.") +flags.DEFINE_float("g", 1.5, "Complexity of dynamics") +flags.DEFINE_float("x0_std", 1.0, + "Volume from which to pull initial conditions (affects diversity of dynamics.") +flags.DEFINE_float("tau", 0.025, "Time constant of RNN") +flags.DEFINE_float("dt", 0.010, "Time bin") +flags.DEFINE_float("max_firing_rate", 30.0, "Map 1.0 of RNN to a spikes per second") +FLAGS = flags.FLAGS + +rng = np.random.RandomState(seed=FLAGS.synth_data_seed) +rnn_rngs = [np.random.RandomState(seed=FLAGS.synth_data_seed+1), + np.random.RandomState(seed=FLAGS.synth_data_seed+2)] +T = FLAGS.T +C = FLAGS.C +N = FLAGS.N +nspikifications = FLAGS.nspikifications +E = nspikifications * C +train_percentage = FLAGS.train_percentage +ntimesteps = int(T / FLAGS.dt) + +rnn_a = generate_rnn(rnn_rngs[0], N, FLAGS.g, FLAGS.tau, FLAGS.dt, + FLAGS.max_firing_rate) +rnn_b = generate_rnn(rnn_rngs[1], N, FLAGS.g, FLAGS.tau, FLAGS.dt, + FLAGS.max_firing_rate) +rnns = [rnn_a, rnn_b] + +# pick which RNN is used on each trial +rnn_to_use = rng.randint(2, size=E) +ext_input = np.repeat(np.expand_dims(rnn_to_use, axis=1), ntimesteps, axis=1) +ext_input = np.expand_dims(ext_input, axis=2) # these are "a's" in the paper + +x0s = [] +condition_labels = [] +condition_number = 0 +for c in range(C): + x0 = FLAGS.x0_std * rng.randn(N, 1) + x0s.append(np.tile(x0, nspikifications)) + for ns in range(nspikifications): + condition_labels.append(condition_number) + condition_number += 1 +x0s = np.concatenate(x0s, axis=1) + +P_nxn = rng.randn(N, N) / np.sqrt(N) + +# generate trials for both RNNs +rates_a, x0s_a, _ = generate_data(rnn_a, T=T, E=E, x0s=x0s, P_sxn=P_nxn, + input_magnitude=0.0, input_times=None) +spikes_a = spikify_data(rates_a, rng, rnn_a['dt'], rnn_a['max_firing_rate']) + +rates_b, x0s_b, _ = generate_data(rnn_b, T=T, E=E, x0s=x0s, P_sxn=P_nxn, + input_magnitude=0.0, input_times=None) +spikes_b = spikify_data(rates_b, rng, rnn_b['dt'], rnn_b['max_firing_rate']) + +# not the best way to do this but E is small enough +rates = [] +spikes = [] +for trial in xrange(E): + if rnn_to_use[trial] == 0: + rates.append(rates_a[trial]) + spikes.append(spikes_a[trial]) + else: + rates.append(rates_b[trial]) + spikes.append(spikes_b[trial]) + +# split into train and validation sets +train_inds, valid_inds = get_train_n_valid_inds(E, train_percentage, + nspikifications) + +rates_train, rates_valid = split_list_by_inds(rates, train_inds, valid_inds) +spikes_train, spikes_valid = split_list_by_inds(spikes, train_inds, valid_inds) +condition_labels_train, condition_labels_valid = split_list_by_inds( + condition_labels, train_inds, valid_inds) +ext_input_train, ext_input_valid = split_list_by_inds( + ext_input, train_inds, valid_inds) + +rates_train = nparray_and_transpose(rates_train) +rates_valid = nparray_and_transpose(rates_valid) +spikes_train = nparray_and_transpose(spikes_train) +spikes_valid = nparray_and_transpose(spikes_valid) + +# add train_ext_input and valid_ext input +data = {'train_truth': rates_train, + 'valid_truth': rates_valid, + 'train_data' : spikes_train, + 'valid_data' : spikes_valid, + 'train_ext_input' : np.array(ext_input_train), + 'valid_ext_input': np.array(ext_input_valid), + 'train_percentage' : train_percentage, + 'nspikifications' : nspikifications, + 'dt' : FLAGS.dt, + 'P_sxn' : P_nxn, + 'condition_labels_train' : condition_labels_train, + 'condition_labels_valid' : condition_labels_valid, + 'conversion_factor': 1.0 / rnn_a['conversion_factor']} + +# just one dataset here +datasets = {} +dataset_name = 'dataset_N' + str(N) +datasets[dataset_name] = data + +# write out the dataset +write_datasets(FLAGS.save_dir, FLAGS.datafile_name, datasets) +print ('Saved to ', os.path.join(FLAGS.save_dir, + FLAGS.datafile_name + '_' + dataset_name)) diff --git a/lfads/synth_data/run_generate_synth_data.sh b/lfads/synth_data/run_generate_synth_data.sh new file mode 100755 index 000000000..c73fee5b1 --- /dev/null +++ b/lfads/synth_data/run_generate_synth_data.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# Copyright 2017 Google Inc. 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. +# +# ============================================================================== + +SYNTH_PATH=/tmp/rnn_synth_data_v1.0/ + +echo "Generating chaotic rnn data with no input pulses (g=1.5)" +python generate_chaotic_rnn_data.py --save_dir=$SYNTH_PATH --datafile_name=chaotic_rnn_no_inputs --synth_data_seed=5 --T=1.0 --C=400 --N=50 --S=50 --train_percentage=0.8 --nspikifications=10 --g=1.5 --x0_std=1.0 --tau=0.025 --dt=0.01 --input_magnitude=0.0 --max_firing_rate=30.0 + +echo "Generating chaotic rnn data with input pulses (g=1.5)" +python generate_chaotic_rnn_data.py --save_dir=$SYNTH_PATH --datafile_name=chaotic_rnn_inputs_g1p5 --synth_data_seed=5 --T=1.0 --C=400 --N=50 --S=50 --train_percentage=0.8 --nspikifications=10 --g=1.5 --x0_std=1.0 --tau=0.025 --dt=0.01 --input_magnitude=20.0 --max_firing_rate=30.0 + +echo "Generating chaotic rnn data with input pulses (g=2.5)" +python generate_chaotic_rnn_data.py --save_dir=$SYNTH_PATH --datafile_name=chaotic_rnn_inputs_g2p5 --synth_data_seed=5 --T=1.0 --C=400 --N=50 --S=50 --train_percentage=0.8 --nspikifications=10 --g=2.5 --x0_std=1.0 --tau=0.025 --dt=0.01 --input_magnitude=20.0 --max_firing_rate=30.0 + +echo "Generate the multi-session RNN data (no multi-session synth example in paper)" +python generate_chaotic_rnn_data.py --save_dir=$SYNTH_PATH --datafile_name=chaotic_rnn_multisession --synth_data_seed=5 --T=1.0 --C=150 --N=100 --S=20 --npcs=10 --train_percentage=0.8 --nspikifications=40 --g=1.5 --x0_std=1.0 --tau=0.025 --dt=0.01 --input_magnitude=0.0 --max_firing_rate=30.0 + +echo "Generating Integration-to-bound RNN data" +python generate_itb_data.py --save_dir=$SYNTH_PATH --datafile_name=itb_rnn --u_std=0.25 --checkpoint_path=SAMPLE_CHECKPOINT --synth_data_seed=5 --T=1.0 --C=800 --N=50 --train_percentage=0.8 --nspikifications=5 --tau=0.025 --dt=0.01 --max_firing_rate=30.0 + +echo "Generating chaotic rnn data with external input labels (no external input labels example in paper)" +python generate_labeled_rnn_data.py --save_dir=$SYNTH_PATH --datafile_name=chaotic_rnns_labeled --synth_data_seed=5 --T=1.0 --C=400 --N=50 --train_percentage=0.8 --nspikifications=10 --g=1.5 --x0_std=1.0 --tau=0.025 --dt=0.01 --max_firing_rate=30.0 diff --git a/lfads/synth_data/synthetic_data_utils.py b/lfads/synth_data/synthetic_data_utils.py new file mode 100644 index 000000000..d01031c19 --- /dev/null +++ b/lfads/synth_data/synthetic_data_utils.py @@ -0,0 +1,322 @@ +# Copyright 2017 Google Inc. 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. +# +# ============================================================================== +from __future__ import print_function + +import h5py +import numpy as np +import os + +from utils import write_datasets +import matplotlib +import matplotlib.pyplot as plt +import scipy.signal + + +def generate_rnn(rng, N, g, tau, dt, max_firing_rate): + """Create a (vanilla) RNN with a bunch of hyper parameters for generating +chaotic data. + Args: + rng: numpy random number generator + N: number of hidden units + g: scaling of recurrent weight matrix in g W, with W ~ N(0,1/N) + tau: time scale of individual unit dynamics + dt: time step for equation updates + max_firing_rate: how to resecale the -1,1 firing rates + Returns: + the dictionary of these parameters, plus some others. +""" + rnn = {} + rnn['N'] = N + rnn['W'] = rng.randn(N,N)/np.sqrt(N) + rnn['Bin'] = rng.randn(N)/np.sqrt(1.0) + rnn['Bin2'] = rng.randn(N)/np.sqrt(1.0) + rnn['b'] = np.zeros(N) + rnn['g'] = g + rnn['tau'] = tau + rnn['dt'] = dt + rnn['max_firing_rate'] = max_firing_rate + mfr = rnn['max_firing_rate'] # spikes / sec + nbins_per_sec = 1.0/rnn['dt'] # bins / sec + # Used for plotting in LFADS + rnn['conversion_factor'] = mfr / nbins_per_sec # spikes / bin + return rnn + + +def generate_data(rnn, T, E, x0s=None, P_sxn=None, input_magnitude=0.0, + input_times=None): + """ Generates data from an randomly initialized RNN. + Args: + rnn: the rnn + T: Time in seconds to run (divided by rnn['dt'] to get steps, rounded down. + E: total number of examples + S: number of samples (subsampling N) + Returns: + A list of length E of NxT tensors of the network being run. + """ + N = rnn['N'] + def run_rnn(rnn, x0, ntime_steps, input_time=None): + rs = np.zeros([N,ntime_steps]) + x_tm1 = x0 + r_tm1 = np.tanh(x0) + tau = rnn['tau'] + dt = rnn['dt'] + alpha = (1.0-dt/tau) + W = dt/tau*rnn['W']*rnn['g'] + Bin = dt/tau*rnn['Bin'] + Bin2 = dt/tau*rnn['Bin2'] + b = dt/tau*rnn['b'] + + us = np.zeros([1, ntime_steps]) + for t in range(ntime_steps): + x_t = alpha*x_tm1 + np.dot(W,r_tm1) + b + if input_time is not None and t == input_time: + us[0,t] = input_magnitude + x_t += Bin * us[0,t] # DCS is this what was used? + r_t = np.tanh(x_t) + x_tm1 = x_t + r_tm1 = r_t + rs[:,t] = r_t + return rs, us + + if P_sxn is None: + P_sxn = np.eye(N) + ntime_steps = int(T / rnn['dt']) + data_e = [] + inputs_e = [] + for e in range(E): + input_time = input_times[e] if input_times is not None else None + r_nxt, u_uxt = run_rnn(rnn, x0s[:,e], ntime_steps, input_time) + r_sxt = np.dot(P_sxn, r_nxt) + inputs_e.append(u_uxt) + data_e.append(r_sxt) + + S = P_sxn.shape[0] + data_e = normalize_rates(data_e, E, S) + + return data_e, x0s, inputs_e + + +def normalize_rates(data_e, E, S): + # Normalization, made more complex because of the P matrices. + # Normalize by min and max in each channel. This normalization will + # cause offset differences between identical rnn runs, but different + # t hits. + for e in range(E): + r_sxt = data_e[e] + for i in range(S): + rmin = np.min(r_sxt[i,:]) + rmax = np.max(r_sxt[i,:]) + assert rmax - rmin != 0, 'Something wrong' + r_sxt[i,:] = (r_sxt[i,:] - rmin)/(rmax-rmin) + data_e[e] = r_sxt + return data_e + + +def spikify_data(data_e, rng, dt=1.0, max_firing_rate=100): + """ Apply spikes to a continuous dataset whose values are between 0.0 and 1.0 + Args: + data_e: nexamples length list of NxT trials + dt: how often the data are sampled + max_firing_rate: the firing rate that is associated with a value of 1.0 + Returns: + spikified_data_e: a list of length b of the data represented as spikes, + sampled from the underlying poisson process. + """ + + spikifies_data_e = [] + E = len(data_e) + spikes_e = [] + for e in range(E): + data = data_e[e] + N,T = data.shape + data_s = np.zeros([N,T]).astype(np.int) + for n in range(N): + f = data[n,:] + s = rng.poisson(f*max_firing_rate*dt, size=T) + data_s[n,:] = s + spikes_e.append(data_s) + + return spikes_e + + +def get_train_n_valid_inds(num_trials, train_fraction, nspikifications): + """Split the numbers between 0 and num_trials-1 into two portions for + training and validation, based on the train fraction. + Args: + num_trials: the number of trials + train_fraction: (e.g. .80) + nspikifications: the number of spiking trials per initial condition + Returns: + a 2-tuple of two lists: the training indices and validation indices + """ + train_inds = [] + valid_inds = [] + for i in range(num_trials): + # This line divides up the trials so that within one initial condition, + # the randomness of spikifying the condition is shared among both + # training and validation data splits. + if (i % nspikifications)+1 > train_fraction * nspikifications: + valid_inds.append(i) + else: + train_inds.append(i) + + return train_inds, valid_inds + + +def split_list_by_inds(data, inds1, inds2): + """Take the data, a list, and split it up based on the indices in inds1 and + inds2. + Args: + data: the list of data to split + inds1, the first list of indices + inds2, the second list of indices + Returns: a 2-tuple of two lists. + """ + if data is None or len(data) == 0: + return [], [] + else: + dout1 = [data[i] for i in inds1] + dout2 = [data[i] for i in inds2] + return dout1, dout2 + + +def nparray_and_transpose(data_a_b_c): + """Convert the list of items in data to a numpy array, and transpose it + Args: + data: data_asbsc: a nested, nested list of length a, with sublist length + b, with sublist length c. + Returns: + a numpy 3-tensor with dimensions a x c x b +""" + data_axbxc = np.array([datum_b_c for datum_b_c in data_a_b_c]) + data_axcxb = np.transpose(data_axbxc, axes=[0,2,1]) + return data_axcxb + + +def add_alignment_projections(datasets, npcs, ntime=None, nsamples=None): + """Create a matrix that aligns the datasets a bit, under + the assumption that each dataset is observing the same underlying dynamical + system. + + Args: + datasets: The dictionary of dataset structures. + npcs: The number of pcs for each, basically like lfads factors. + nsamples (optional): Number of samples to take for each dataset. + ntime (optional): Number of time steps to take in each sample. + + Returns: + The dataset structures, with the field alignment_matrix_cxf added. + This is # channels x npcs dimension +""" + nchannels_all = 0 + channel_idxs = {} + conditions_all = {} + nconditions_all = 0 + for name, dataset in datasets.items(): + cidxs = np.where(dataset['P_sxn'])[1] # non-zero entries in columns + channel_idxs[name] = [cidxs[0], cidxs[-1]+1] + nchannels_all += cidxs[-1]+1 - cidxs[0] + conditions_all[name] = np.unique(dataset['condition_labels_train']) + + all_conditions_list = \ + np.unique(np.ndarray.flatten(np.array(conditions_all.values()))) + nconditions_all = all_conditions_list.shape[0] + + if ntime is None: + ntime = dataset['train_data'].shape[1] + if nsamples is None: + nsamples = dataset['train_data'].shape[0] + + # In the data workup in the paper, Chethan did intra condition + # averaging, so let's do that here. + avg_data_all = {} + for name, conditions in conditions_all.items(): + dataset = datasets[name] + avg_data_all[name] = {} + for cname in conditions: + td_idxs = np.argwhere(np.array(dataset['condition_labels_train'])==cname) + data = np.squeeze(dataset['train_data'][td_idxs,:,:], axis=1) + avg_data = np.mean(data, axis=0) + avg_data_all[name][cname] = avg_data + + # Visualize this in the morning. + all_data_nxtc = np.zeros([nchannels_all, ntime * nconditions_all]) + for name, dataset in datasets.items(): + cidx_s = channel_idxs[name][0] + cidx_f = channel_idxs[name][1] + for cname in conditions_all[name]: + cidxs = np.argwhere(all_conditions_list == cname) + if cidxs.shape[0] > 0: + cidx = cidxs[0][0] + all_tidxs = np.arange(0, ntime+1) + cidx*ntime + all_data_nxtc[cidx_s:cidx_f, all_tidxs[0]:all_tidxs[-1]] = \ + avg_data_all[name][cname].T + + # A bit of filtering. We don't care about spectral properties, or + # filtering artifacts, simply correlate time steps a bit. + filt_len = 6 + bc_filt = np.ones([filt_len])/float(filt_len) + for c in range(nchannels_all): + all_data_nxtc[c,:] = scipy.signal.filtfilt(bc_filt, [1.0], all_data_nxtc[c,:]) + + # Compute the PCs. + all_data_mean_nx1 = np.mean(all_data_nxtc, axis=1, keepdims=True) + all_data_zm_nxtc = all_data_nxtc - all_data_mean_nx1 + corr_mat_nxn = np.dot(all_data_zm_nxtc, all_data_zm_nxtc.T) + evals_n, evecs_nxn = np.linalg.eigh(corr_mat_nxn) + sidxs = np.flipud(np.argsort(evals_n)) # sort such that 0th is highest + evals_n = evals_n[sidxs] + evecs_nxn = evecs_nxn[:,sidxs] + + # Project all the channels data onto the low-D PCA basis, where + # low-d is the npcs parameter. + all_data_pca_pxtc = np.dot(evecs_nxn[:, 0:npcs].T, all_data_zm_nxtc) + + # Now for each dataset, we regress the channel data onto the top + # pcs, and this will be our alignment matrix for that dataset. + # |B - A*W|^2 + for name, dataset in datasets.items(): + cidx_s = channel_idxs[name][0] + cidx_f = channel_idxs[name][1] + all_data_zm_chxtc = all_data_zm_nxtc[cidx_s:cidx_f,:] # ch for channel + W_chxp, _, _, _ = \ + np.linalg.lstsq(all_data_zm_chxtc.T, all_data_pca_pxtc.T) + dataset['alignment_matrix_cxf'] = W_chxp + + do_debug_plot = False + if do_debug_plot: + pc_vecs = evecs_nxn[:,0:npcs] + ntoplot = 400 + + plt.figure() + plt.plot(np.log10(evals_n), '-x') + plt.figure() + plt.subplot(311) + plt.imshow(all_data_pca_pxtc) + plt.colorbar() + + plt.subplot(312) + plt.imshow(np.dot(W_chxp.T, all_data_zm_chxtc)) + plt.colorbar() + + plt.subplot(313) + plt.imshow(np.dot(all_data_zm_chxtc.T, W_chxp).T - all_data_pca_pxtc) + plt.colorbar() + + import pdb + pdb.set_trace() + + return datasets diff --git a/lfads/synth_data/trained_itb/model-65000.data-00000-of-00001 b/lfads/synth_data/trained_itb/model-65000.data-00000-of-00001 new file mode 100644 index 0000000000000000000000000000000000000000..9459a2a1b72f56dc16b3eca210911f14081e7fd5 GIT binary patch literal 10608 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 literal 0 HcmV?d00001 diff --git a/lfads/synth_data/trained_itb/model-65000.index b/lfads/synth_data/trained_itb/model-65000.index new file mode 100644 index 0000000000000000000000000000000000000000..dd9c793acf8dc79e07833d1c0edc8a2fa86d806a GIT binary patch literal 266 zcmZQzVB=tvV&Y(A;1CH*EXqtw%1Py56ygwK;xGbXjR}6HS!cgK*n1su#m3<_^Ra%Oi5k#b>ZC?WC9Qd?w0|O&- h2E!Jx9GED)JZJ4rMvh~Q{2*ocLHO^6Zk1B^+W_>PF*yJL literal 0 HcmV?d00001 diff --git a/lfads/synth_data/trained_itb/model-65000.meta b/lfads/synth_data/trained_itb/model-65000.meta new file mode 100644 index 0000000000000000000000000000000000000000..07bd2b9688eda16e329e7b08492151a65a88fb8a GIT binary patch literal 1053549 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

    {7f^1NeSjsKlnr|(^2eI{Piv*1kDhxoI$ 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 d`nVsZKE9ZL2@Yzq=Q_a6-Hjg%eN^1mc5r)U5G literal 0 HcmV?d00001 diff --git a/lfads/utils.py b/lfads/utils.py new file mode 100644 index 000000000..7eb1db84f --- /dev/null +++ b/lfads/utils.py @@ -0,0 +1,357 @@ +# Copyright 2017 Google Inc. 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. +# +# ============================================================================== +from __future__ import print_function + +import os +import h5py +import json + +import numpy as np +import tensorflow as tf + + +def log_sum_exp(x_k): + """Computes log \sum exp in a numerically stable way. + log ( sum_i exp(x_i) ) + log ( sum_i exp(x_i - m + m) ), with m = max(x_i) + log ( sum_i exp(x_i - m)*exp(m) ) + log ( sum_i exp(x_i - m) + m + + Args: + x_k - k -dimensional list of arguments to log_sum_exp. + + Returns: + log_sum_exp of the arguments. + """ + m = tf.reduce_max(x_k) + x1_k = x_k - m + u_k = tf.exp(x1_k) + z = tf.reduce_sum(u_k) + return tf.log(z) + m + + +def linear(x, out_size, do_bias=True, alpha=1.0, identity_if_possible=False, + normalized=False, name=None, collections=None): + """Linear (affine) transformation, y = x W + b, for a variety of + configurations. + + Args: + x: input The tensor to tranformation. + out_size: The integer size of non-batch output dimension. + do_bias (optional): Add a learnable bias vector to the operation. + alpha (optional): A multiplicative scaling for the weight initialization + of the matrix, in the form \alpha * 1/\sqrt{x.shape[1]}. + identity_if_possible (optional): just return identity, + if x.shape[1] == out_size. + normalized (optional): Option to divide out by the norms of the rows of W. + name (optional): The name prefix to add to variables. + collections (optional): List of additional collections. (Placed in + tf.GraphKeys.GLOBAL_VARIABLES already, so no need for that.) + + Returns: + In the equation, y = x W + b, returns the tensorflow op that yields y. + """ + in_size = int(x.get_shape()[1]) # from Dimension(10) -> 10 + stddev = alpha/np.sqrt(float(in_size)) + mat_init = tf.random_normal_initializer(0.0, stddev) + wname = (name + "/W") if name else "/W" + + if identity_if_possible and in_size == out_size: + # Sometimes linear layers are nothing more than size adapters. + return tf.identity(x, name=(wname+'_ident')) + + W,b = init_linear(in_size, out_size, do_bias=do_bias, alpha=alpha, + normalized=normalized, name=name, collections=collections) + + if do_bias: + return tf.matmul(x, W) + b + else: + return tf.matmul(x, W) + + +def init_linear(in_size, out_size, do_bias=True, mat_init_value=None, alpha=1.0, + identity_if_possible=False, normalized=False, + name=None, collections=None): + """Linear (affine) transformation, y = x W + b, for a variety of + configurations. + + Args: + in_size: The integer size of the non-batc input dimension. [(x),y] + out_size: The integer size of non-batch output dimension. [x,(y)] + do_bias (optional): Add a learnable bias vector to the operation. + mat_init_value (optional): numpy constant for matrix initialization, if None + , do random, with additional parameters. + alpha (optional): A multiplicative scaling for the weight initialization + of the matrix, in the form \alpha * 1/\sqrt{x.shape[1]}. + identity_if_possible (optional): just return identity, + if x.shape[1] == out_size. + normalized (optional): Option to divide out by the norms of the rows of W. + name (optional): The name prefix to add to variables. + collections (optional): List of additional collections. (Placed in + tf.GraphKeys.GLOBAL_VARIABLES already, so no need for that.) + + Returns: + In the equation, y = x W + b, returns the pair (W, b). + """ + + if mat_init_value is not None and mat_init_value.shape != (in_size, out_size): + raise ValueError( + 'Provided mat_init_value must have shape [%d, %d].'%(in_size, out_size)) + + if mat_init_value is None: + stddev = alpha/np.sqrt(float(in_size)) + mat_init = tf.random_normal_initializer(0.0, stddev) + + wname = (name + "/W") if name else "/W" + + if identity_if_possible and in_size == out_size: + return (tf.constant(np.eye(in_size).astype(np.float32)), + tf.zeros(in_size)) + + # Note the use of get_variable vs. tf.Variable. this is because get_variable + # does not allow the initialization of the variable with a value. + if normalized: + w_collections = [tf.GraphKeys.GLOBAL_VARIABLES, "norm-variables"] + if collections: + w_collections += collections + if mat_init_value is not None: + w = tf.Variable(mat_init_value, name=wname, collections=w_collections) + else: + w = tf.get_variable(wname, [in_size, out_size], initializer=mat_init, + collections=w_collections) + w = tf.nn.l2_normalize(w, dim=0) # x W, so xW_j = \sum_i x_bi W_ij + else: + w_collections = [tf.GraphKeys.GLOBAL_VARIABLES] + if collections: + w_collections += collections + if mat_init_value is not None: + w = tf.Variable(mat_init_value, name=wname, collections=w_collections) + else: + w = tf.get_variable(wname, [in_size, out_size], initializer=mat_init, + collections=w_collections) + + if do_bias: + b_collections = [tf.GraphKeys.GLOBAL_VARIABLES] + if collections: + b_collections += collections + bname = (name + "/b") if name else "/b" + b = tf.get_variable(bname, [1, out_size], + initializer=tf.zeros_initializer(), + collections=b_collections) + else: + b = None + + return (w, b) + + +def write_data(data_fname, data_dict, use_json=False, compression=None): + """Write data in HD5F format. + + Args: + data_fname: The filename of teh file in which to write the data. + data_dict: The dictionary of data to write. The keys are strings + and the values are numpy arrays. + use_json (optional): human readable format for simple items + compression (optional): The compression to use for h5py (disabled by + default because the library borks on scalars, otherwise try 'gzip'). + """ + + dir_name = os.path.dirname(data_fname) + if not os.path.exists(dir_name): + os.makedirs(dir_name) + + if use_json: + the_file = open(data_fname,'w') + json.dump(data_dict, the_file) + the_file.close() + else: + try: + with h5py.File(data_fname, 'w') as hf: + for k, v in data_dict.items(): + clean_k = k.replace('/', '_') + if clean_k is not k: + print('Warning: saving variable with name: ', k, ' as ', clean_k) + else: + print('Saving variable with name: ', clean_k) + hf.create_dataset(clean_k, data=v, compression=compression) + except IOError: + print("Cannot open %s for writing.", data_fname) + raise + + +def read_data(data_fname): + """ Read saved data in HDF5 format. + + Args: + data_fname: The filename of the file from which to read the data. + Returns: + A dictionary whose keys will vary depending on dataset (but should + always contain the keys 'train_data' and 'valid_data') and whose + values are numpy arrays. + """ + + try: + with h5py.File(data_fname, 'r') as hf: + data_dict = {k: np.array(v) for k, v in hf.items()} + return data_dict + except IOError: + print("Cannot open %s for reading." % data_fname) + raise + + +def write_datasets(data_path, data_fname_stem, dataset_dict, compression=None): + """Write datasets in HD5F format. + + This function assumes the dataset_dict is a mapping ( string -> + to data_dict ). It calls write_data for each data dictionary, + post-fixing the data filename with the key of the dataset. + + Args: + data_path: The path to the save directory. + data_fname_stem: The filename stem of the file in which to write the data. + dataset_dict: The dictionary of datasets. The keys are strings + and the values data dictionaries (str -> numpy arrays) associations. + compression (optional): The compression to use for h5py (disabled by + default because the library borks on scalars, otherwise try 'gzip'). + """ + + full_name_stem = os.path.join(data_path, data_fname_stem) + for s, data_dict in dataset_dict.items(): + write_data(full_name_stem + "_" + s, data_dict, compression=compression) + + +def read_datasets(data_path, data_fname_stem): + """Read dataset sin HD5F format. + + This function assumes the dataset_dict is a mapping ( string -> + to data_dict ). It calls write_data for each data dictionary, + post-fixing the data filename with the key of the dataset. + + Args: + data_path: The path to the save directory. + data_fname_stem: The filename stem of the file in which to write the data. + """ + + dataset_dict = {} + fnames = os.listdir(data_path) + + print ('loading data from ' + data_path + ' with stem ' + data_fname_stem) + for fname in fnames: + if fname.startswith(data_fname_stem): + data_dict = read_data(os.path.join(data_path,fname)) + idx = len(data_fname_stem) + 1 + key = fname[idx:] + data_dict['data_dim'] = data_dict['train_data'].shape[2] + data_dict['num_steps'] = data_dict['train_data'].shape[1] + dataset_dict[key] = data_dict + + if len(dataset_dict) == 0: + raise ValueError("Failed to load any datasets, are you sure that the " + "'--data_dir' and '--data_filename_stem' flag values " + "are correct?") + + print (str(len(dataset_dict)) + ' datasets loaded') + return dataset_dict + + +# NUMPY utility functions +def list_t_bxn_to_list_b_txn(values_t_bxn): + """Convert a length T list of BxN numpy tensors of length B list of TxN numpy + tensors. + + Args: + values_t_bxn: The length T list of BxN numpy tensors. + + Returns: + The length B list of TxN numpy tensors. + """ + T = len(values_t_bxn) + B, N = values_t_bxn[0].shape + values_b_txn = [] + for b in range(B): + values_pb_txn = np.zeros([T,N]) + for t in range(T): + values_pb_txn[t,:] = values_t_bxn[t][b,:] + values_b_txn.append(values_pb_txn) + + return values_b_txn + + +def list_t_bxn_to_tensor_bxtxn(values_t_bxn): + """Convert a length T list of BxN numpy tensors to single numpy tensor with + shape BxTxN. + + Args: + values_t_bxn: The length T list of BxN numpy tensors. + + Returns: + values_bxtxn: The BxTxN numpy tensor. + """ + + T = len(values_t_bxn) + B, N = values_t_bxn[0].shape + values_bxtxn = np.zeros([B,T,N]) + for t in range(T): + values_bxtxn[:,t,:] = values_t_bxn[t] + + return values_bxtxn + + +def tensor_bxtxn_to_list_t_bxn(tensor_bxtxn): + """Convert a numpy tensor with shape BxTxN to a length T list of numpy tensors + with shape BxT. + + Args: + tensor_bxtxn: The BxTxN numpy tensor. + + Returns: + A length T list of numpy tensors with shape BxT. + """ + + values_t_bxn = [] + B, T, N = tensor_bxtxn.shape + for t in range(T): + values_t_bxn.append(np.squeeze(tensor_bxtxn[:,t,:])) + + return values_t_bxn + + +def flatten(list_of_lists): + """Takes a list of lists and returns a list of the elements. + + Args: + list_of_lists: List of lists. + + Returns: + flat_list: Flattened list. + flat_list_idxs: Flattened list indices. + """ + flat_list = [] + flat_list_idxs = [] + start_idx = 0 + for item in list_of_lists: + if isinstance(item, list): + flat_list += item + l = len(item) + idxs = range(start_idx, start_idx+l) + start_idx = start_idx+l + else: # a value + flat_list.append(item) + idxs = [start_idx] + start_idx += 1 + flat_list_idxs.append(idxs) + + return flat_list, flat_list_idxs -- GitLab From f2c86f92babbddf03c8ea7e578a412418db6f13e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E7=92=9E?= Date: Fri, 23 Jun 2017 09:48:25 +0800 Subject: [PATCH 076/110] Comment wrong in adversarial_crypto model According to the code below: ```python if key is not None: combined_message = tf.concat(axis=1, values=[message, key]) else: combined_message = message ```python If the key=None, combined_message is just message, not the key. --- adversarial_crypto/train_eval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adversarial_crypto/train_eval.py b/adversarial_crypto/train_eval.py index 09de7e513..6f5a5914b 100644 --- a/adversarial_crypto/train_eval.py +++ b/adversarial_crypto/train_eval.py @@ -118,7 +118,7 @@ class AdversarialCrypto(object): def model(self, collection, message, key=None): """The model for Alice, Bob, and Eve. If key=None, the first FC layer - takes only the Key as inputs. Otherwise, it uses both the key + takes only the message as inputs. Otherwise, it uses both the key and the message. Args: -- GitLab From 3ae3df73408167d242731ac163d8dec96df5fa30 Mon Sep 17 00:00:00 2001 From: Thibaut Mattio Date: Sat, 24 Jun 2017 07:19:10 +0800 Subject: [PATCH 077/110] Fix typo in Engine Dashboard (#1746) --- object_detection/g3doc/running_on_cloud.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_detection/g3doc/running_on_cloud.md b/object_detection/g3doc/running_on_cloud.md index b96725eaf..b691c0e5b 100644 --- a/object_detection/g3doc/running_on_cloud.md +++ b/object_detection/g3doc/running_on_cloud.md @@ -88,7 +88,7 @@ training checkpoints and events will be written to and Google Cloud Storage. Users can monitor the progress of their training job on the [ML Engine -Dasboard](https://pantheon.corp.google.com/mlengine/jobs). +Dashboard](https://pantheon.corp.google.com/mlengine/jobs). ## Running an Evaluation Job on Cloud -- GitLab From f47880a3ce97e57db756e8c87cf76537864bb6f9 Mon Sep 17 00:00:00 2001 From: Ben Mabey Date: Sat, 24 Jun 2017 16:21:28 -0600 Subject: [PATCH 078/110] fix docs on data generation for object detection --- object_detection/g3doc/preparing_inputs.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/object_detection/g3doc/preparing_inputs.md b/object_detection/g3doc/preparing_inputs.md index 77ba7f39f..8e4933c78 100644 --- a/object_detection/g3doc/preparing_inputs.md +++ b/object_detection/g3doc/preparing_inputs.md @@ -14,9 +14,9 @@ Extract the tar file and run the `create_pascal_tf_record` script: ``` # From tensorflow/models/object_detection tar -xvf VOCtrainval_11-May-2012.tar -./create_pascal_tf_record --data_dir=VOCdevkit \ +python create_pascal_tf_record.py --data_dir=VOCdevkit \ --year=VOC2012 --set=train --output_path=pascal_train.record -./create_pascal_tf_record --data_dir=/home/user/VOCdevkit \ +python create_pascal_tf_record.py --data_dir=/home/user/VOCdevkit \ --year=VOC2012 --set=val --output_path=pascal_val.record ``` @@ -36,7 +36,7 @@ file and run the `create_pet_tf_record` script to generate TFRecords. # From tensorflow/models/object_detection tar -xvf annotations.tar.gz tar -xvf images.tar.gz -./create_pet_tf_record --data_dir=`pwd` --output_dir=`pwd` +python create_pet_tf_record.py --data_dir=`pwd` --output_dir=`pwd` ``` You should end up with two TFRecord files named pet_train.record and -- GitLab From 093de0aff9af123afd1a3c8d5e35765c7c60ab8e Mon Sep 17 00:00:00 2001 From: Ben Mabey Date: Sat, 24 Jun 2017 17:50:27 -0600 Subject: [PATCH 079/110] fix typo in object_detection docs --- object_detection/g3doc/running_pets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_detection/g3doc/running_pets.md b/object_detection/g3doc/running_pets.md index eae858af7..a72ed4af8 100644 --- a/object_detection/g3doc/running_pets.md +++ b/object_detection/g3doc/running_pets.md @@ -146,7 +146,7 @@ upload your edited file onto GCS, making note of the path it was uploaded to sed -i "s|PATH_TO_BE_CONFIGURED|"gs://${YOUR_GCS_BUCKET}"/data|g" \ object_detection/samples/configs/faster_rcnn_resnet101_pets.config -# Copy editted template to cloud. +# Copy edited template to cloud. gsutil cp object_detection/samples/configs/faster_rcnn_resnet101_pets.config \ gs://${YOUR_GCS_BUCKET}/data/faster_rcnn_resnet101_pets.config ``` -- GitLab From 8f16def11bd9ef4b874fbd86bb0f2792abbb07a0 Mon Sep 17 00:00:00 2001 From: Byeongjoo Ahn Date: Mon, 26 Jun 2017 18:25:42 +0900 Subject: [PATCH 080/110] Fix compatibility for Python3 iteritems() -> items() --- object_detection/utils/variables_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_detection/utils/variables_helper.py b/object_detection/utils/variables_helper.py index 1e091a144..b27f814f1 100644 --- a/object_detection/utils/variables_helper.py +++ b/object_detection/utils/variables_helper.py @@ -122,7 +122,7 @@ def get_variables_available_in_checkpoint(variables, checkpoint_path): ckpt_reader = tf.train.NewCheckpointReader(checkpoint_path) ckpt_vars = ckpt_reader.get_variable_to_shape_map().keys() vars_in_ckpt = {} - for variable_name, variable in sorted(variable_names_map.iteritems()): + for variable_name, variable in sorted(variable_names_map.items()): if variable_name in ckpt_vars: vars_in_ckpt[variable_name] = variable else: -- GitLab From 47a617617bdf402fa93bf535b44d9f3b3b7a199e Mon Sep 17 00:00:00 2001 From: Jasmine Date: Mon, 26 Jun 2017 10:49:51 -0700 Subject: [PATCH 081/110] update README's TF version --- lfads/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lfads/README.md b/lfads/README.md index ab4ee2b91..0dacb79db 100644 --- a/lfads/README.md +++ b/lfads/README.md @@ -7,7 +7,9 @@ This code implements the model from the paper "[LFADS - Latent Factor Analysis v The code is written in Python 2.7.6. You will also need: -* **TensorFlow** version 1.0.1 or greater ([install](https://www.tensorflow.org/install/)) +* **TensorFlow** version 1.1 ([install](http://tflearn.org/installation/)) - + there is an incompatibility with LFADS and TF v1.2, which we are in the + process of resolving * **NumPy, SciPy, Matplotlib** ([install SciPy stack](https://www.scipy.org/install.html), contains all of them) * **h5py** ([install](https://pypi.python.org/pypi/h5py)) -- GitLab From 2f3666ed8e339e5b3d67609cded3f741596de205 Mon Sep 17 00:00:00 2001 From: Duc Nguyen Date: Tue, 27 Jun 2017 10:57:15 +0900 Subject: [PATCH 082/110] Change key of type 'tuple' to 'str' dictionary having both 'tuple' and 'str' keys cannot be 'sorted' --- object_detection/core/batcher.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/object_detection/core/batcher.py b/object_detection/core/batcher.py index 7b9d040ab..4c816457b 100644 --- a/object_detection/core/batcher.py +++ b/object_detection/core/batcher.py @@ -20,6 +20,8 @@ import tensorflow as tf from object_detection.core import prefetcher +rt_shape_str = '_runtime_shapes' + class BatchQueue(object): """BatchQueue class. @@ -81,7 +83,7 @@ class BatchQueue(object): {key: tensor.get_shape() for key, tensor in tensor_dict.iteritems()}) # Remember runtime shapes to unpad tensors after batching. runtime_shapes = collections.OrderedDict( - {(key + '_runtime_shapes'): tf.shape(tensor) + {(key + rt_shape_str): tf.shape(tensor) for key, tensor in tensor_dict.iteritems()}) all_tensors = tensor_dict all_tensors.update(runtime_shapes) @@ -112,8 +114,8 @@ class BatchQueue(object): for key, batched_tensor in batched_tensors.iteritems(): unbatched_tensor_list = tf.unstack(batched_tensor) for i, unbatched_tensor in enumerate(unbatched_tensor_list): - if '_runtime_shapes' in key: - shapes[(key[:-15], i)] = unbatched_tensor + if rt_shape_str in key: + shapes[(key[:-len(rt_shape_str)], i)] = unbatched_tensor else: tensors[(key, i)] = unbatched_tensor -- GitLab From 7fcddd76f17fad959c83fe88166e5cb6a6d4027c Mon Sep 17 00:00:00 2001 From: James Pruegsanusak Date: Tue, 27 Jun 2017 11:50:34 +0800 Subject: [PATCH 083/110] Fix typo in object_detection's preparing_inputs.md --- object_detection/g3doc/preparing_inputs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_detection/g3doc/preparing_inputs.md b/object_detection/g3doc/preparing_inputs.md index 8e4933c78..8c95eb87c 100644 --- a/object_detection/g3doc/preparing_inputs.md +++ b/object_detection/g3doc/preparing_inputs.md @@ -16,7 +16,7 @@ Extract the tar file and run the `create_pascal_tf_record` script: tar -xvf VOCtrainval_11-May-2012.tar python create_pascal_tf_record.py --data_dir=VOCdevkit \ --year=VOC2012 --set=train --output_path=pascal_train.record -python create_pascal_tf_record.py --data_dir=/home/user/VOCdevkit \ +python create_pascal_tf_record.py --data_dir=VOCdevkit \ --year=VOC2012 --set=val --output_path=pascal_val.record ``` -- GitLab From 1e093b26a5c5200856f90ad8ad9c794f2dd721fc Mon Sep 17 00:00:00 2001 From: James Pruegsanusak Date: Tue, 27 Jun 2017 11:58:40 +0800 Subject: [PATCH 084/110] Fix markdown style for better readability --- object_detection/g3doc/preparing_inputs.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/object_detection/g3doc/preparing_inputs.md b/object_detection/g3doc/preparing_inputs.md index 8c95eb87c..1e80bebb0 100644 --- a/object_detection/g3doc/preparing_inputs.md +++ b/object_detection/g3doc/preparing_inputs.md @@ -11,7 +11,7 @@ The raw 2012 PASCAL VOC data set can be downloaded [here](http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar). Extract the tar file and run the `create_pascal_tf_record` script: -``` +```bash # From tensorflow/models/object_detection tar -xvf VOCtrainval_11-May-2012.tar python create_pascal_tf_record.py --data_dir=VOCdevkit \ @@ -20,11 +20,11 @@ python create_pascal_tf_record.py --data_dir=VOCdevkit \ --year=VOC2012 --set=val --output_path=pascal_val.record ``` -You should end up with two TFRecord files named pascal_train.record and -pascal_val.record in the tensorflow/models/object_detection directory. +You should end up with two TFRecord files named `pascal_train.record` and +`pascal_val.record` in the `tensorflow/models/object_detection` directory. The label map for the PASCAL VOC data set can be found at -data/pascal_label_map.pbtxt. +`data/pascal_label_map.pbtxt`. ## Generation the Oxford-IIIT Pet TFRecord files. @@ -32,14 +32,14 @@ The Oxford-IIIT Pet data set can be downloaded from [their website](http://www.robots.ox.ac.uk/~vgg/data/pets/). Extract the tar file and run the `create_pet_tf_record` script to generate TFRecords. -``` +```bash # From tensorflow/models/object_detection tar -xvf annotations.tar.gz tar -xvf images.tar.gz python create_pet_tf_record.py --data_dir=`pwd` --output_dir=`pwd` ``` -You should end up with two TFRecord files named pet_train.record and -pet_val.record in the tensorflow/models/object_detection directory. +You should end up with two TFRecord files named `pet_train.record` and +`pet_val.record` in the `tensorflow/models/object_detection` directory. -The label map for the Pet dataset can be found at data/pet_label_map.pbtxt. +The label map for the Pet dataset can be found at `data/pet_label_map.pbtxt`. -- GitLab From 22ca3a44409c59cb41b5df5aa58696b15ee5d898 Mon Sep 17 00:00:00 2001 From: akssri Date: Tue, 27 Jun 2017 11:30:21 +0530 Subject: [PATCH 085/110] _strict_random_crop_image: fix mask slice --- object_detection/core/preprocessor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/object_detection/core/preprocessor.py b/object_detection/core/preprocessor.py index 3fdcb6138..f88ae90ef 100644 --- a/object_detection/core/preprocessor.py +++ b/object_detection/core/preprocessor.py @@ -707,8 +707,8 @@ def _strict_random_crop_image(image, 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) - masks_box_begin = [im_box_begin[2], im_box_begin[0], im_box_begin[1]] - masks_box_size = [im_box_size[2], im_box_size[0], im_box_size[1]] + masks_box_begin = [0, im_box_begin[0], im_box_begin[1]] + masks_box_size = [-1, im_box_size[0], im_box_size[1]] new_masks = tf.slice( masks_of_boxes_completely_inside_window, masks_box_begin, masks_box_size) -- GitLab From ee1089eedd1aea0a4edea40902807d373b912b51 Mon Sep 17 00:00:00 2001 From: akssri Date: Tue, 27 Jun 2017 11:54:18 +0530 Subject: [PATCH 086/110] random_pixel_value_scale has no boxes involved --- object_detection/core/preprocessor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/object_detection/core/preprocessor.py b/object_detection/core/preprocessor.py index 3fdcb6138..25bd0cbaf 100644 --- a/object_detection/core/preprocessor.py +++ b/object_detection/core/preprocessor.py @@ -341,7 +341,6 @@ def random_pixel_value_scale(image, minval=0.9, maxval=1.1, seed=None): 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('RandomPixelValueScale', values=[image]): color_coef = tf.random_uniform( -- GitLab From 801f892acc21f3a4359c8ebaa4ff3b44dd02b5e0 Mon Sep 17 00:00:00 2001 From: James Pruegsanusak Date: Tue, 27 Jun 2017 21:56:15 +0800 Subject: [PATCH 087/110] Fix typos and inline code style --- object_detection/g3doc/running_pets.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/object_detection/g3doc/running_pets.md b/object_detection/g3doc/running_pets.md index a72ed4af8..a218bae93 100644 --- a/object_detection/g3doc/running_pets.md +++ b/object_detection/g3doc/running_pets.md @@ -64,7 +64,7 @@ the tarballs, your object_detection directory should appear as follows: ``` The Tensorflow Object Detection API expects data to be in the TFRecord format, -so we'll now run the _create_pet_tf_record_ script to convert from the raw +so we'll now run the `create_pet_tf_record` script to convert from the raw Oxford-IIIT Pet dataset into TFRecords. Run the following commands from the object_detection directory: @@ -83,12 +83,12 @@ python object_detection/create_pet_tf_record.py \ Note: It is normal to see some warnings when running this script. You may ignore them. -Two TFRecord files named pet_train.record and pet_val.record should be generated +Two TFRecord files named `pet_train.record` and `pet_val.record` should be generated in the object_detection/ directory. Now that the data has been generated, we'll need to upload it to Google Cloud Storage so the data can be accessed by ML Engine. Run the following command to -copy the files into your GCS bucket (substituting ${YOUR_GCS_BUCKET}): +copy the files into your GCS bucket (substituting `${YOUR_GCS_BUCKET}`): ``` bash # From tensorflow/models/ @@ -109,7 +109,7 @@ parameters to initialize our new model. Download our [COCO-pretrained Faster R-CNN with Resnet-101 model](http://storage.googleapis.com/download.tensorflow.org/models/object_detection/faster_rcnn_resnet101_coco_11_06_2017.tar.gz). -Unzip the contents of the folder and copy the model.ckpt* files into your GCS +Unzip the contents of the folder and copy the `model.ckpt*` files into your GCS Bucket. ``` bash @@ -134,7 +134,7 @@ text editor. We'll need to configure some paths in order for the template to work. Search the file for instances of `PATH_TO_BE_CONFIGURED` and replace them with the -appropriate value (typically "gs://${YOUR_GCS_BUCKET}/data/"). Afterwards +appropriate value (typically `gs://${YOUR_GCS_BUCKET}/data/`). Afterwards upload your edited file onto GCS, making note of the path it was uploaded to (we'll need it when starting the training/eval jobs). @@ -171,7 +171,7 @@ the following: ``` You can inspect your bucket using the [Google Cloud Storage -browser](pantheon.corp.google.com/storage). +browser](https://console.cloud.google.com/storage/browser). ## Starting Training and Evaluation Jobs on Google Cloud ML Engine @@ -194,7 +194,7 @@ and `slim/dist/slim-0.1.tar.gz`. For running the training Cloud ML job, we'll configure the cluster to use 10 training jobs (1 master + 9 workers) and three parameters servers. The -configuration file can be found at object_detection/samples/cloud/cloud.yml. +configuration file can be found at `object_detection/samples/cloud/cloud.yml`. To start training, execute the following command from the tensorflow/models/ directory: @@ -233,7 +233,7 @@ submit training` command is correct. ML Engine does not distinguish between training and evaluation jobs. Users can monitor and stop training and evaluation jobs on the [ML Engine -Dasboard](https://console.cloud.google.com/mlengine/jobs). +Dashboard](https://console.cloud.google.com/mlengine/jobs). ## Monitoring Progress with Tensorboard @@ -263,15 +263,15 @@ Note: It takes roughly 10 minutes for a job to get started on ML Engine, and roughly an hour for the system to evaluate the validation dataset. It may take some time to populate the dashboards. If you do not see any entries after half an hour, check the logs from the [ML Engine -Dasboard](https://pantheon.corp.google.com/mlengine/jobs). +Dashboard](https://console.cloud.google.com/mlengine/jobs). ## Exporting the Tensorflow Graph After your model has been trained, you should export it to a Tensorflow graph proto. First, you need to identify a candidate checkpoint to export. You can search your bucket using the [Google Cloud Storage -Browser](https://pantheon.corp.google.com/storage/browser). The file should be -stored under ${YOUR_GCS_BUCKET}/train. The checkpoint will typically consist of +Browser](https://console.cloud.google.com/storage/browser). The file should be +stored under `${YOUR_GCS_BUCKET}/train`. The checkpoint will typically consist of three files: * model.ckpt-${CHECKPOINT_NUMBER}.data-00000-of-00001, @@ -291,7 +291,7 @@ python object_detection/export_inference_graph \ --inference_graph_path output_inference_graph.pb ``` -Afterwards, you should see a graph named output_inference_graph.pb. +Afterwards, you should see a graph named `output_inference_graph.pb`. ## What's Next -- GitLab From b02ab963cdd414486751bb6ba39ec6b6dea9d750 Mon Sep 17 00:00:00 2001 From: James Pruegsanusak Date: Tue, 27 Jun 2017 22:04:18 +0800 Subject: [PATCH 088/110] Use inline code style for directory names --- object_detection/g3doc/running_pets.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/object_detection/g3doc/running_pets.md b/object_detection/g3doc/running_pets.md index a218bae93..23943c794 100644 --- a/object_detection/g3doc/running_pets.md +++ b/object_detection/g3doc/running_pets.md @@ -51,8 +51,8 @@ dataset for Oxford-IIIT Pets lives [here](http://www.robots.ox.ac.uk/~vgg/data/pets/). You will need to download both the image dataset [`images.tar.gz`](http://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz) and the groundtruth data [`annotations.tar.gz`](http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz) -to the tensorflow/models directory. This may take some time. After downloading -the tarballs, your object_detection directory should appear as follows: +to the `tensorflow/models` directory. This may take some time. After downloading +the tarballs, your `object_detection` directory should appear as follows: ```lang-none + object_detection/ @@ -66,7 +66,7 @@ the tarballs, your object_detection directory should appear as follows: The Tensorflow Object Detection API expects data to be in the TFRecord format, so we'll now run the `create_pet_tf_record` script to convert from the raw Oxford-IIIT Pet dataset into TFRecords. Run the following commands from the -object_detection directory: +`object_detection` directory: ``` bash # From tensorflow/models/ @@ -84,7 +84,7 @@ Note: It is normal to see some warnings when running this script. You may ignore them. Two TFRecord files named `pet_train.record` and `pet_val.record` should be generated -in the object_detection/ directory. +in the `object_detection` directory. Now that the data has been generated, we'll need to upload it to Google Cloud Storage so the data can be accessed by ML Engine. Run the following command to @@ -127,7 +127,7 @@ In the Tensorflow Object Detection API, the model parameters, training parameters and eval parameters are all defined by a config file. More details can be found [here](configuring_jobs.md). For this tutorial, we will use some predefined templates provided with the source code. In the -object_detection/samples/configs folder, there are skeleton object_detection +`object_detection/samples/configs` folder, there are skeleton object_detection configuration files. We will use `faster_rcnn_resnet101_pets.config` as a starting point for configuring the pipeline. Open the file with your favourite text editor. @@ -181,7 +181,7 @@ Before we can start a job on Google Cloud ML Engine, we must: 2. Write a cluster configuration for our Google Cloud ML job. To package the Tensorflow Object Detection code, run the following commands from -the tensorflow/models/ directory: +the `tensorflow/models/` directory: ``` bash # From tensorflow/models/ @@ -196,7 +196,7 @@ For running the training Cloud ML job, we'll configure the cluster to use 10 training jobs (1 master + 9 workers) and three parameters servers. The configuration file can be found at `object_detection/samples/cloud/cloud.yml`. -To start training, execute the following command from the tensorflow/models/ +To start training, execute the following command from the `tensorflow/models/` directory: ``` bash @@ -274,12 +274,12 @@ Browser](https://console.cloud.google.com/storage/browser). The file should be stored under `${YOUR_GCS_BUCKET}/train`. The 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 +* `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/object_detection: +command from `tensorflow/models/object_detection`: ``` bash # From tensorflow/models -- GitLab From f33ffcc224fe6549bc2c10cf3597b702459d64fc Mon Sep 17 00:00:00 2001 From: Vivek Rathod Date: Tue, 27 Jun 2017 10:11:30 -0700 Subject: [PATCH 089/110] Add option to export graph with input node that accepts encoded jpeg or png string --- object_detection/export_inference_graph.py | 19 ++- object_detection/exporter.py | 44 ++++-- object_detection/exporter_test.py | 164 ++++++++++++++++++--- 3 files changed, 194 insertions(+), 33 deletions(-) diff --git a/object_detection/export_inference_graph.py b/object_detection/export_inference_graph.py index c6e8a827c..d7ffa4152 100644 --- a/object_detection/export_inference_graph.py +++ b/object_detection/export_inference_graph.py @@ -18,21 +18,27 @@ r"""Tool to export an object detection model for inference. Prepares an object detection tensorflow graph for inference using model configuration and an optional trained checkpoint. -The inference graph contains one of two input nodes depending on the user +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] + * `encoded_image_string_tensor`: Accepts a scalar string tensor of encoded PNG + or JPEG image. * `tf_example`: Accepts a serialized TFExample proto. The batch size in this case is always 1. -and the following output nodes: - * `num_detections` : Outputs float32 tensors of the form [batch] +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 + * `detection_boxes`: Outputs float32 tensors of the form [batch, num_boxes, 4] containing detected boxes. - * `detection_scores` : Outputs float32 tensors of the form + * `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. + * `detection_masks`: 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. Note that currently `batch` is always 1, but we will support `batch` > 1 in the future. @@ -61,7 +67,8 @@ slim = tf.contrib.slim flags = tf.app.flags flags.DEFINE_string('input_type', 'image_tensor', 'Type of input node. Can be ' - 'one of [`image_tensor` `tf_example_proto`]') + 'one of [`image_tensor`, `encoded_image_string_tensor`, ' + '`tf_example`]') flags.DEFINE_string('pipeline_config_path', '', 'Path to a pipeline_pb2.TrainEvalPipelineConfig config ' 'file.') diff --git a/object_detection/exporter.py b/object_detection/exporter.py index a57913f7c..24b654a55 100644 --- a/object_detection/exporter.py +++ b/object_detection/exporter.py @@ -30,8 +30,8 @@ from object_detection.data_decoders import tf_example_decoder slim = tf.contrib.slim -# TODO: Replace with freeze_graph.freeze_graph_with_def_protos when newer -# version of Tensorflow becomes more common. +# TODO: Replace with freeze_graph.freeze_graph_with_def_protos when +# newer version of Tensorflow becomes more common. def freeze_graph_with_def_protos( input_graph_def, input_saver_def, @@ -48,12 +48,12 @@ def freeze_graph_with_def_protos( # 'input_checkpoint' may be a prefix if we're using Saver V2 format if not saver_lib.checkpoint_exists(input_checkpoint): - logging.info('Input checkpoint "' + input_checkpoint + '" does not exist!') - return -1 + raise ValueError( + 'Input checkpoint "' + input_checkpoint + '" does not exist!') if not output_node_names: - logging.info('You must supply the name of a node to --output_node_names.') - return -1 + raise ValueError( + 'You must supply the name of a node to --output_node_names.') # Remove all the explicit device specifications for this node. This helps to # make the graph more portable. @@ -101,7 +101,7 @@ def freeze_graph_with_def_protos( def _tf_example_input_placeholder(): tf_example_placeholder = tf.placeholder( tf.string, shape=[], name='tf_example') - tensor_dict = tf_example_decoder.TfExampleDecoder().Decode( + tensor_dict = tf_example_decoder.TfExampleDecoder().decode( tf_example_placeholder) image = tensor_dict[fields.InputDataFields.image] return tf.expand_dims(image, axis=0) @@ -112,9 +112,21 @@ def _image_tensor_input_placeholder(): shape=(1, None, None, 3), name='image_tensor') + +def _encoded_image_string_tensor_input_placeholder(): + image_str = tf.placeholder(dtype=tf.string, + shape=[], + name='encoded_image_string_tensor') + image_tensor = tf.image.decode_image(image_str, channels=3) + image_tensor.set_shape((None, None, 3)) + return tf.expand_dims(image_tensor, axis=0) + + 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, - 'image_tensor': _image_tensor_input_placeholder } @@ -129,23 +141,31 @@ def _add_output_tensor_nodes(postprocessed_tensors): containing scores for the detected boxes. * detection_classes: float32 tensor of shape [batch_size, num_boxes] containing class predictions for the detected boxes. + * 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_classes': [batch, max_detections] + 'detection_masks': [batch, max_detections, mask_height, mask_width] + (optional). 'num_detections': [batch] """ label_id_offset = 1 boxes = postprocessed_tensors.get('detection_boxes') scores = postprocessed_tensors.get('detection_scores') classes = postprocessed_tensors.get('detection_classes') + label_id_offset + masks = postprocessed_tensors.get('detection_masks') num_detections = postprocessed_tensors.get('num_detections') tf.identity(boxes, name='detection_boxes') tf.identity(scores, name='detection_scores') tf.identity(classes, name='detection_classes') tf.identity(num_detections, name='num_detections') + if masks is not None: + tf.identity(masks, name='detection_masks') def _write_inference_graph(inference_graph_path, @@ -201,6 +221,7 @@ def _export_inference_graph(input_type, use_moving_averages, checkpoint_path, inference_graph_path): + """Export helper.""" if input_type not in input_placeholder_fn_map: raise ValueError('Unknown input type: {}'.format(input_type)) inputs = tf.to_float(input_placeholder_fn_map[input_type]()) @@ -208,8 +229,13 @@ def _export_inference_graph(input_type, output_tensors = detection_model.predict(preprocessed_inputs) postprocessed_tensors = detection_model.postprocess(output_tensors) _add_output_tensor_nodes(postprocessed_tensors) + out_node_names = ['num_detections', 'detection_scores,' + 'detection_boxes', 'detection_classes'] + if 'detection_masks' in postprocessed_tensors: + out_node_names.append('detection_masks') _write_inference_graph(inference_graph_path, checkpoint_path, - use_moving_averages) + use_moving_averages, + output_node_names=','.join(out_node_names)) def export_inference_graph(input_type, pipeline_config, checkpoint_path, diff --git a/object_detection/exporter_test.py b/object_detection/exporter_test.py index 5b16fc8e9..eb7c742d8 100644 --- a/object_detection/exporter_test.py +++ b/object_detection/exporter_test.py @@ -26,24 +26,28 @@ from object_detection.protos import pipeline_pb2 class FakeModel(model.DetectionModel): + def __init__(self, add_detection_masks=False): + self._add_detection_masks = add_detection_masks + def preprocess(self, inputs): - return (tf.identity(inputs) * - tf.get_variable('dummy', shape=(), - initializer=tf.constant_initializer(2), - dtype=tf.float32)) + return tf.identity(inputs) def predict(self, preprocessed_inputs): - return {'image': tf.identity(preprocessed_inputs)} + return {'image': tf.layers.conv2d(preprocessed_inputs, 3, 1)} def postprocess(self, prediction_dict): with tf.control_dependencies(prediction_dict.values()): - return { + postprocessed_tensors = { 'detection_boxes': tf.constant([[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.8, 0.8]], tf.float32), 'detection_scores': tf.constant([[0.7, 0.6]], tf.float32), 'detection_classes': tf.constant([[0, 1]], tf.float32), 'num_detections': tf.constant([2], tf.float32) } + if self._add_detection_masks: + postprocessed_tensors['detection_masks'] = tf.constant( + np.arange(32).reshape([2, 4, 4]), tf.float32) + return postprocessed_tensors def restore_fn(self, checkpoint_path, from_detection_checkpoint): pass @@ -58,8 +62,11 @@ class ExportInferenceGraphTest(tf.test.TestCase): use_moving_averages): g = tf.Graph() with g.as_default(): - mock_model = FakeModel(num_classes=1) - mock_model.preprocess(tf.constant([1, 3, 4, 3], tf.float32)) + mock_model = FakeModel() + preprocessed_inputs = mock_model.preprocess( + tf.ones([1, 3, 4, 3], tf.float32)) + predictions = mock_model.predict(preprocessed_inputs) + mock_model.postprocess(predictions) if use_moving_averages: tf.train.ExponentialMovingAverage(0.0).apply() saver = tf.train.Saver() @@ -93,7 +100,7 @@ class ExportInferenceGraphTest(tf.test.TestCase): def test_export_graph_with_image_tensor_input(self): with mock.patch.object( model_builder, 'build', autospec=True) as mock_builder: - mock_builder.return_value = FakeModel(num_classes=1) + mock_builder.return_value = FakeModel() inference_graph_path = os.path.join(self.get_temp_dir(), 'exported_graph.pbtxt') @@ -108,7 +115,7 @@ class ExportInferenceGraphTest(tf.test.TestCase): def test_export_graph_with_tf_example_input(self): with mock.patch.object( model_builder, 'build', autospec=True) as mock_builder: - mock_builder.return_value = FakeModel(num_classes=1) + mock_builder.return_value = FakeModel() inference_graph_path = os.path.join(self.get_temp_dir(), 'exported_graph.pbtxt') pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() @@ -119,6 +126,20 @@ class ExportInferenceGraphTest(tf.test.TestCase): checkpoint_path=None, inference_graph_path=inference_graph_path) + def test_export_graph_with_encoded_image_string_input(self): + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + inference_graph_path = os.path.join(self.get_temp_dir(), + 'exported_graph.pbtxt') + 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, + checkpoint_path=None, + inference_graph_path=inference_graph_path) + def test_export_frozen_graph(self): checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') self._save_checkpoint_from_mock_model(checkpoint_path, @@ -127,7 +148,7 @@ class ExportInferenceGraphTest(tf.test.TestCase): 'exported_graph.pb') with mock.patch.object( model_builder, 'build', autospec=True) as mock_builder: - mock_builder.return_value = FakeModel(num_classes=1) + mock_builder.return_value = FakeModel() pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() pipeline_config.eval_config.use_moving_averages = False exporter.export_inference_graph( @@ -144,7 +165,7 @@ class ExportInferenceGraphTest(tf.test.TestCase): 'exported_graph.pb') with mock.patch.object( model_builder, 'build', autospec=True) as mock_builder: - mock_builder.return_value = FakeModel(num_classes=1) + mock_builder.return_value = FakeModel() pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() pipeline_config.eval_config.use_moving_averages = True exporter.export_inference_graph( @@ -153,6 +174,55 @@ class ExportInferenceGraphTest(tf.test.TestCase): checkpoint_path=checkpoint_path, inference_graph_path=inference_graph_path) + def test_export_model_with_all_output_nodes(self): + checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') + self._save_checkpoint_from_mock_model(checkpoint_path, + use_moving_averages=False) + inference_graph_path = os.path.join(self.get_temp_dir(), + 'exported_graph.pb') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel(add_detection_masks=True) + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + exporter.export_inference_graph( + input_type='image_tensor', + pipeline_config=pipeline_config, + checkpoint_path=checkpoint_path, + inference_graph_path=inference_graph_path) + 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_classes:0') + inference_graph.get_tensor_by_name('detection_masks:0') + inference_graph.get_tensor_by_name('num_detections:0') + + def test_export_model_with_detection_only_nodes(self): + checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') + self._save_checkpoint_from_mock_model(checkpoint_path, + use_moving_averages=False) + inference_graph_path = os.path.join(self.get_temp_dir(), + 'exported_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, + checkpoint_path=checkpoint_path, + inference_graph_path=inference_graph_path) + 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_classes:0') + inference_graph.get_tensor_by_name('num_detections:0') + with self.assertRaises(KeyError): + inference_graph.get_tensor_by_name('detection_masks:0') + def test_export_and_run_inference_with_image_tensor(self): checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') self._save_checkpoint_from_mock_model(checkpoint_path, @@ -161,7 +231,7 @@ class ExportInferenceGraphTest(tf.test.TestCase): 'exported_graph.pb') with mock.patch.object( model_builder, 'build', autospec=True) as mock_builder: - mock_builder.return_value = FakeModel(num_classes=1) + mock_builder.return_value = FakeModel(add_detection_masks=True) pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() pipeline_config.eval_config.use_moving_averages = False exporter.export_inference_graph( @@ -176,16 +246,72 @@ class ExportInferenceGraphTest(tf.test.TestCase): 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') + masks = inference_graph.get_tensor_by_name('detection_masks:0') num_detections = inference_graph.get_tensor_by_name('num_detections:0') - (boxes, scores, classes, num_detections) = sess.run( - [boxes, scores, classes, num_detections], + (boxes, scores, classes, masks, num_detections) = sess.run( + [boxes, scores, classes, masks, num_detections], feed_dict={image_tensor: np.ones((1, 4, 4, 3)).astype(np.uint8)}) self.assertAllClose(boxes, [[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.8, 0.8]]) self.assertAllClose(scores, [[0.7, 0.6]]) self.assertAllClose(classes, [[1, 2]]) + self.assertAllClose(masks, np.arange(32).reshape([2, 4, 4])) self.assertAllClose(num_detections, [2]) + 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): + checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') + self._save_checkpoint_from_mock_model(checkpoint_path, + use_moving_averages=False) + inference_graph_path = os.path.join(self.get_temp_dir(), + 'exported_graph.pb') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel(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, + checkpoint_path=checkpoint_path, + inference_graph_path=inference_graph_path) + + 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') + classes = inference_graph.get_tensor_by_name('detection_classes: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]: + (boxes_np, scores_np, classes_np, masks_np, + num_detections_np) = sess.run( + [boxes, scores, classes, masks, num_detections], + feed_dict={image_str_tensor: image_str}) + self.assertAllClose(boxes_np, [[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]]) + self.assertAllClose(scores_np, [[0.7, 0.6]]) + self.assertAllClose(classes_np, [[1, 2]]) + self.assertAllClose(masks_np, np.arange(32).reshape([2, 4, 4])) + self.assertAllClose(num_detections_np, [2]) + def test_export_and_run_inference_with_tf_example(self): checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') self._save_checkpoint_from_mock_model(checkpoint_path, @@ -194,7 +320,7 @@ class ExportInferenceGraphTest(tf.test.TestCase): 'exported_graph.pb') with mock.patch.object( model_builder, 'build', autospec=True) as mock_builder: - mock_builder.return_value = FakeModel(num_classes=1) + mock_builder.return_value = FakeModel(add_detection_masks=True) pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() pipeline_config.eval_config.use_moving_averages = False exporter.export_inference_graph( @@ -209,15 +335,17 @@ class ExportInferenceGraphTest(tf.test.TestCase): 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') + masks = inference_graph.get_tensor_by_name('detection_masks:0') num_detections = inference_graph.get_tensor_by_name('num_detections:0') - (boxes, scores, classes, num_detections) = sess.run( - [boxes, scores, classes, num_detections], + (boxes, scores, classes, masks, num_detections) = sess.run( + [boxes, scores, classes, masks, num_detections], feed_dict={tf_example: self._create_tf_example( np.ones((4, 4, 3)).astype(np.uint8))}) self.assertAllClose(boxes, [[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.8, 0.8]]) self.assertAllClose(scores, [[0.7, 0.6]]) self.assertAllClose(classes, [[1, 2]]) + self.assertAllClose(masks, np.arange(32).reshape([2, 4, 4])) self.assertAllClose(num_detections, [2]) -- GitLab From f8b0067f803286953f124c83d2a4e9ba4bd63a32 Mon Sep 17 00:00:00 2001 From: Vivek Rathod Date: Tue, 27 Jun 2017 10:11:30 -0700 Subject: [PATCH 090/110] Add option to export graph with input node that accepts encoded jpeg or png string --- object_detection/export_inference_graph.py | 19 ++- object_detection/exporter.py | 42 +++++- object_detection/exporter_test.py | 164 ++++++++++++++++++--- 3 files changed, 193 insertions(+), 32 deletions(-) diff --git a/object_detection/export_inference_graph.py b/object_detection/export_inference_graph.py index c6e8a827c..d7ffa4152 100644 --- a/object_detection/export_inference_graph.py +++ b/object_detection/export_inference_graph.py @@ -18,21 +18,27 @@ r"""Tool to export an object detection model for inference. Prepares an object detection tensorflow graph for inference using model configuration and an optional trained checkpoint. -The inference graph contains one of two input nodes depending on the user +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] + * `encoded_image_string_tensor`: Accepts a scalar string tensor of encoded PNG + or JPEG image. * `tf_example`: Accepts a serialized TFExample proto. The batch size in this case is always 1. -and the following output nodes: - * `num_detections` : Outputs float32 tensors of the form [batch] +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 + * `detection_boxes`: Outputs float32 tensors of the form [batch, num_boxes, 4] containing detected boxes. - * `detection_scores` : Outputs float32 tensors of the form + * `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. + * `detection_masks`: 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. Note that currently `batch` is always 1, but we will support `batch` > 1 in the future. @@ -61,7 +67,8 @@ slim = tf.contrib.slim flags = tf.app.flags flags.DEFINE_string('input_type', 'image_tensor', 'Type of input node. Can be ' - 'one of [`image_tensor` `tf_example_proto`]') + 'one of [`image_tensor`, `encoded_image_string_tensor`, ' + '`tf_example`]') flags.DEFINE_string('pipeline_config_path', '', 'Path to a pipeline_pb2.TrainEvalPipelineConfig config ' 'file.') diff --git a/object_detection/exporter.py b/object_detection/exporter.py index a57913f7c..3bc617b33 100644 --- a/object_detection/exporter.py +++ b/object_detection/exporter.py @@ -30,8 +30,8 @@ from object_detection.data_decoders import tf_example_decoder slim = tf.contrib.slim -# TODO: Replace with freeze_graph.freeze_graph_with_def_protos when newer -# version of Tensorflow becomes more common. +# TODO: Replace with freeze_graph.freeze_graph_with_def_protos when +# newer version of Tensorflow becomes more common. def freeze_graph_with_def_protos( input_graph_def, input_saver_def, @@ -48,12 +48,12 @@ def freeze_graph_with_def_protos( # 'input_checkpoint' may be a prefix if we're using Saver V2 format if not saver_lib.checkpoint_exists(input_checkpoint): - logging.info('Input checkpoint "' + input_checkpoint + '" does not exist!') - return -1 + raise ValueError( + 'Input checkpoint "' + input_checkpoint + '" does not exist!') if not output_node_names: - logging.info('You must supply the name of a node to --output_node_names.') - return -1 + raise ValueError( + 'You must supply the name of a node to --output_node_names.') # Remove all the explicit device specifications for this node. This helps to # make the graph more portable. @@ -112,9 +112,21 @@ def _image_tensor_input_placeholder(): shape=(1, None, None, 3), name='image_tensor') + +def _encoded_image_string_tensor_input_placeholder(): + image_str = tf.placeholder(dtype=tf.string, + shape=[], + name='encoded_image_string_tensor') + image_tensor = tf.image.decode_image(image_str, channels=3) + image_tensor.set_shape((None, None, 3)) + return tf.expand_dims(image_tensor, axis=0) + + 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, - 'image_tensor': _image_tensor_input_placeholder } @@ -129,23 +141,31 @@ def _add_output_tensor_nodes(postprocessed_tensors): containing scores for the detected boxes. * detection_classes: float32 tensor of shape [batch_size, num_boxes] containing class predictions for the detected boxes. + * 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_classes': [batch, max_detections] + 'detection_masks': [batch, max_detections, mask_height, mask_width] + (optional). 'num_detections': [batch] """ label_id_offset = 1 boxes = postprocessed_tensors.get('detection_boxes') scores = postprocessed_tensors.get('detection_scores') classes = postprocessed_tensors.get('detection_classes') + label_id_offset + masks = postprocessed_tensors.get('detection_masks') num_detections = postprocessed_tensors.get('num_detections') tf.identity(boxes, name='detection_boxes') tf.identity(scores, name='detection_scores') tf.identity(classes, name='detection_classes') tf.identity(num_detections, name='num_detections') + if masks is not None: + tf.identity(masks, name='detection_masks') def _write_inference_graph(inference_graph_path, @@ -201,6 +221,7 @@ def _export_inference_graph(input_type, use_moving_averages, checkpoint_path, inference_graph_path): + """Export helper.""" if input_type not in input_placeholder_fn_map: raise ValueError('Unknown input type: {}'.format(input_type)) inputs = tf.to_float(input_placeholder_fn_map[input_type]()) @@ -208,8 +229,13 @@ def _export_inference_graph(input_type, output_tensors = detection_model.predict(preprocessed_inputs) postprocessed_tensors = detection_model.postprocess(output_tensors) _add_output_tensor_nodes(postprocessed_tensors) + out_node_names = ['num_detections', 'detection_scores,' + 'detection_boxes', 'detection_classes'] + if 'detection_masks' in postprocessed_tensors: + out_node_names.append('detection_masks') _write_inference_graph(inference_graph_path, checkpoint_path, - use_moving_averages) + use_moving_averages, + output_node_names=','.join(out_node_names)) def export_inference_graph(input_type, pipeline_config, checkpoint_path, diff --git a/object_detection/exporter_test.py b/object_detection/exporter_test.py index 5b16fc8e9..eb7c742d8 100644 --- a/object_detection/exporter_test.py +++ b/object_detection/exporter_test.py @@ -26,24 +26,28 @@ from object_detection.protos import pipeline_pb2 class FakeModel(model.DetectionModel): + def __init__(self, add_detection_masks=False): + self._add_detection_masks = add_detection_masks + def preprocess(self, inputs): - return (tf.identity(inputs) * - tf.get_variable('dummy', shape=(), - initializer=tf.constant_initializer(2), - dtype=tf.float32)) + return tf.identity(inputs) def predict(self, preprocessed_inputs): - return {'image': tf.identity(preprocessed_inputs)} + return {'image': tf.layers.conv2d(preprocessed_inputs, 3, 1)} def postprocess(self, prediction_dict): with tf.control_dependencies(prediction_dict.values()): - return { + postprocessed_tensors = { 'detection_boxes': tf.constant([[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.8, 0.8]], tf.float32), 'detection_scores': tf.constant([[0.7, 0.6]], tf.float32), 'detection_classes': tf.constant([[0, 1]], tf.float32), 'num_detections': tf.constant([2], tf.float32) } + if self._add_detection_masks: + postprocessed_tensors['detection_masks'] = tf.constant( + np.arange(32).reshape([2, 4, 4]), tf.float32) + return postprocessed_tensors def restore_fn(self, checkpoint_path, from_detection_checkpoint): pass @@ -58,8 +62,11 @@ class ExportInferenceGraphTest(tf.test.TestCase): use_moving_averages): g = tf.Graph() with g.as_default(): - mock_model = FakeModel(num_classes=1) - mock_model.preprocess(tf.constant([1, 3, 4, 3], tf.float32)) + mock_model = FakeModel() + preprocessed_inputs = mock_model.preprocess( + tf.ones([1, 3, 4, 3], tf.float32)) + predictions = mock_model.predict(preprocessed_inputs) + mock_model.postprocess(predictions) if use_moving_averages: tf.train.ExponentialMovingAverage(0.0).apply() saver = tf.train.Saver() @@ -93,7 +100,7 @@ class ExportInferenceGraphTest(tf.test.TestCase): def test_export_graph_with_image_tensor_input(self): with mock.patch.object( model_builder, 'build', autospec=True) as mock_builder: - mock_builder.return_value = FakeModel(num_classes=1) + mock_builder.return_value = FakeModel() inference_graph_path = os.path.join(self.get_temp_dir(), 'exported_graph.pbtxt') @@ -108,7 +115,7 @@ class ExportInferenceGraphTest(tf.test.TestCase): def test_export_graph_with_tf_example_input(self): with mock.patch.object( model_builder, 'build', autospec=True) as mock_builder: - mock_builder.return_value = FakeModel(num_classes=1) + mock_builder.return_value = FakeModel() inference_graph_path = os.path.join(self.get_temp_dir(), 'exported_graph.pbtxt') pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() @@ -119,6 +126,20 @@ class ExportInferenceGraphTest(tf.test.TestCase): checkpoint_path=None, inference_graph_path=inference_graph_path) + def test_export_graph_with_encoded_image_string_input(self): + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + inference_graph_path = os.path.join(self.get_temp_dir(), + 'exported_graph.pbtxt') + 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, + checkpoint_path=None, + inference_graph_path=inference_graph_path) + def test_export_frozen_graph(self): checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') self._save_checkpoint_from_mock_model(checkpoint_path, @@ -127,7 +148,7 @@ class ExportInferenceGraphTest(tf.test.TestCase): 'exported_graph.pb') with mock.patch.object( model_builder, 'build', autospec=True) as mock_builder: - mock_builder.return_value = FakeModel(num_classes=1) + mock_builder.return_value = FakeModel() pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() pipeline_config.eval_config.use_moving_averages = False exporter.export_inference_graph( @@ -144,7 +165,7 @@ class ExportInferenceGraphTest(tf.test.TestCase): 'exported_graph.pb') with mock.patch.object( model_builder, 'build', autospec=True) as mock_builder: - mock_builder.return_value = FakeModel(num_classes=1) + mock_builder.return_value = FakeModel() pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() pipeline_config.eval_config.use_moving_averages = True exporter.export_inference_graph( @@ -153,6 +174,55 @@ class ExportInferenceGraphTest(tf.test.TestCase): checkpoint_path=checkpoint_path, inference_graph_path=inference_graph_path) + def test_export_model_with_all_output_nodes(self): + checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') + self._save_checkpoint_from_mock_model(checkpoint_path, + use_moving_averages=False) + inference_graph_path = os.path.join(self.get_temp_dir(), + 'exported_graph.pb') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel(add_detection_masks=True) + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + exporter.export_inference_graph( + input_type='image_tensor', + pipeline_config=pipeline_config, + checkpoint_path=checkpoint_path, + inference_graph_path=inference_graph_path) + 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_classes:0') + inference_graph.get_tensor_by_name('detection_masks:0') + inference_graph.get_tensor_by_name('num_detections:0') + + def test_export_model_with_detection_only_nodes(self): + checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') + self._save_checkpoint_from_mock_model(checkpoint_path, + use_moving_averages=False) + inference_graph_path = os.path.join(self.get_temp_dir(), + 'exported_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, + checkpoint_path=checkpoint_path, + inference_graph_path=inference_graph_path) + 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_classes:0') + inference_graph.get_tensor_by_name('num_detections:0') + with self.assertRaises(KeyError): + inference_graph.get_tensor_by_name('detection_masks:0') + def test_export_and_run_inference_with_image_tensor(self): checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') self._save_checkpoint_from_mock_model(checkpoint_path, @@ -161,7 +231,7 @@ class ExportInferenceGraphTest(tf.test.TestCase): 'exported_graph.pb') with mock.patch.object( model_builder, 'build', autospec=True) as mock_builder: - mock_builder.return_value = FakeModel(num_classes=1) + mock_builder.return_value = FakeModel(add_detection_masks=True) pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() pipeline_config.eval_config.use_moving_averages = False exporter.export_inference_graph( @@ -176,16 +246,72 @@ class ExportInferenceGraphTest(tf.test.TestCase): 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') + masks = inference_graph.get_tensor_by_name('detection_masks:0') num_detections = inference_graph.get_tensor_by_name('num_detections:0') - (boxes, scores, classes, num_detections) = sess.run( - [boxes, scores, classes, num_detections], + (boxes, scores, classes, masks, num_detections) = sess.run( + [boxes, scores, classes, masks, num_detections], feed_dict={image_tensor: np.ones((1, 4, 4, 3)).astype(np.uint8)}) self.assertAllClose(boxes, [[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.8, 0.8]]) self.assertAllClose(scores, [[0.7, 0.6]]) self.assertAllClose(classes, [[1, 2]]) + self.assertAllClose(masks, np.arange(32).reshape([2, 4, 4])) self.assertAllClose(num_detections, [2]) + 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): + checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') + self._save_checkpoint_from_mock_model(checkpoint_path, + use_moving_averages=False) + inference_graph_path = os.path.join(self.get_temp_dir(), + 'exported_graph.pb') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel(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, + checkpoint_path=checkpoint_path, + inference_graph_path=inference_graph_path) + + 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') + classes = inference_graph.get_tensor_by_name('detection_classes: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]: + (boxes_np, scores_np, classes_np, masks_np, + num_detections_np) = sess.run( + [boxes, scores, classes, masks, num_detections], + feed_dict={image_str_tensor: image_str}) + self.assertAllClose(boxes_np, [[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]]) + self.assertAllClose(scores_np, [[0.7, 0.6]]) + self.assertAllClose(classes_np, [[1, 2]]) + self.assertAllClose(masks_np, np.arange(32).reshape([2, 4, 4])) + self.assertAllClose(num_detections_np, [2]) + def test_export_and_run_inference_with_tf_example(self): checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') self._save_checkpoint_from_mock_model(checkpoint_path, @@ -194,7 +320,7 @@ class ExportInferenceGraphTest(tf.test.TestCase): 'exported_graph.pb') with mock.patch.object( model_builder, 'build', autospec=True) as mock_builder: - mock_builder.return_value = FakeModel(num_classes=1) + mock_builder.return_value = FakeModel(add_detection_masks=True) pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() pipeline_config.eval_config.use_moving_averages = False exporter.export_inference_graph( @@ -209,15 +335,17 @@ class ExportInferenceGraphTest(tf.test.TestCase): 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') + masks = inference_graph.get_tensor_by_name('detection_masks:0') num_detections = inference_graph.get_tensor_by_name('num_detections:0') - (boxes, scores, classes, num_detections) = sess.run( - [boxes, scores, classes, num_detections], + (boxes, scores, classes, masks, num_detections) = sess.run( + [boxes, scores, classes, masks, num_detections], feed_dict={tf_example: self._create_tf_example( np.ones((4, 4, 3)).astype(np.uint8))}) self.assertAllClose(boxes, [[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.8, 0.8]]) self.assertAllClose(scores, [[0.7, 0.6]]) self.assertAllClose(classes, [[1, 2]]) + self.assertAllClose(masks, np.arange(32).reshape([2, 4, 4])) self.assertAllClose(num_detections, [2]) -- GitLab From e5a7bb9acca6805a88fdddb5c1009e27e8a0412a Mon Sep 17 00:00:00 2001 From: Vivek Rathod Date: Tue, 27 Jun 2017 10:41:04 -0700 Subject: [PATCH 091/110] change decode -> Decode --- object_detection/exporter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_detection/exporter.py b/object_detection/exporter.py index 24b654a55..3bc617b33 100644 --- a/object_detection/exporter.py +++ b/object_detection/exporter.py @@ -101,7 +101,7 @@ def freeze_graph_with_def_protos( def _tf_example_input_placeholder(): tf_example_placeholder = tf.placeholder( tf.string, shape=[], name='tf_example') - tensor_dict = tf_example_decoder.TfExampleDecoder().decode( + tensor_dict = tf_example_decoder.TfExampleDecoder().Decode( tf_example_placeholder) image = tensor_dict[fields.InputDataFields.image] return tf.expand_dims(image, axis=0) -- GitLab From 3ab727658b171c6803bfc22384524c8bbb1bcbf8 Mon Sep 17 00:00:00 2001 From: Derek Chow Date: Tue, 27 Jun 2017 13:14:43 -0700 Subject: [PATCH 092/110] snake_case Decode function --- .../builders/input_reader_builder.py | 2 +- object_detection/core/data_decoder.py | 3 +-- .../data_decoders/tf_example_decoder.py | 2 +- .../data_decoders/tf_example_decoder_test.py | 18 +++++++++--------- object_detection/exporter.py | 2 +- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/object_detection/builders/input_reader_builder.py b/object_detection/builders/input_reader_builder.py index 98ad6127a..5baa08137 100644 --- a/object_detection/builders/input_reader_builder.py +++ b/object_detection/builders/input_reader_builder.py @@ -60,6 +60,6 @@ def build(input_reader_config): capacity=input_reader_config.queue_capacity, min_after_dequeue=input_reader_config.min_after_dequeue) - return tf_example_decoder.TfExampleDecoder().Decode(string_tensor) + return tf_example_decoder.TfExampleDecoder().decode(string_tensor) raise ValueError('Unsupported input_reader_config.') diff --git a/object_detection/core/data_decoder.py b/object_detection/core/data_decoder.py index 84be4db59..9ae18c1f9 100644 --- a/object_detection/core/data_decoder.py +++ b/object_detection/core/data_decoder.py @@ -26,9 +26,8 @@ class DataDecoder(object): """Interface for data decoders.""" __metaclass__ = ABCMeta - # TODO: snake_case this method. @abstractmethod - def Decode(self, data): + def decode(self, data): """Return a single image and associated labels. Args: diff --git a/object_detection/data_decoders/tf_example_decoder.py b/object_detection/data_decoders/tf_example_decoder.py index fcea12cb4..7426f466e 100644 --- a/object_detection/data_decoders/tf_example_decoder.py +++ b/object_detection/data_decoders/tf_example_decoder.py @@ -82,7 +82,7 @@ class TfExampleDecoder(data_decoder.DataDecoder): slim_example_decoder.Tensor('image/segmentation/object/class')), } - def Decode(self, tf_example_string_tensor): + def decode(self, tf_example_string_tensor): """Decodes serialized tensorflow example and returns a tensor dictionary. Args: diff --git a/object_detection/data_decoders/tf_example_decoder_test.py b/object_detection/data_decoders/tf_example_decoder_test.py index 4a28419a7..de23bec15 100644 --- a/object_detection/data_decoders/tf_example_decoder_test.py +++ b/object_detection/data_decoders/tf_example_decoder_test.py @@ -64,7 +64,7 @@ class TfExampleDecoderTest(tf.test.TestCase): })).SerializeToString() example_decoder = tf_example_decoder.TfExampleDecoder() - tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + tensor_dict = example_decoder.decode(tf.convert_to_tensor(example)) self.assertAllEqual((tensor_dict[fields.InputDataFields.image]. get_shape().as_list()), [None, None, 3]) @@ -84,7 +84,7 @@ class TfExampleDecoderTest(tf.test.TestCase): })).SerializeToString() example_decoder = tf_example_decoder.TfExampleDecoder() - tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + tensor_dict = example_decoder.decode(tf.convert_to_tensor(example)) with self.test_session() as sess: tensor_dict = sess.run(tensor_dict) @@ -103,7 +103,7 @@ class TfExampleDecoderTest(tf.test.TestCase): })).SerializeToString() example_decoder = tf_example_decoder.TfExampleDecoder() - tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + tensor_dict = example_decoder.decode(tf.convert_to_tensor(example)) self.assertAllEqual((tensor_dict[fields.InputDataFields.image]. get_shape().as_list()), [None, None, 3]) @@ -130,7 +130,7 @@ class TfExampleDecoderTest(tf.test.TestCase): })).SerializeToString() example_decoder = tf_example_decoder.TfExampleDecoder() - tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + tensor_dict = example_decoder.decode(tf.convert_to_tensor(example)) self.assertAllEqual((tensor_dict[fields.InputDataFields.groundtruth_boxes]. get_shape().as_list()), [None, 4]) @@ -153,7 +153,7 @@ class TfExampleDecoderTest(tf.test.TestCase): })).SerializeToString() example_decoder = tf_example_decoder.TfExampleDecoder() - tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + tensor_dict = example_decoder.decode(tf.convert_to_tensor(example)) self.assertAllEqual((tensor_dict[ fields.InputDataFields.groundtruth_classes].get_shape().as_list()), @@ -176,7 +176,7 @@ class TfExampleDecoderTest(tf.test.TestCase): })).SerializeToString() example_decoder = tf_example_decoder.TfExampleDecoder() - tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + tensor_dict = example_decoder.decode(tf.convert_to_tensor(example)) self.assertAllEqual((tensor_dict[fields.InputDataFields.groundtruth_area]. get_shape().as_list()), [None]) @@ -197,7 +197,7 @@ class TfExampleDecoderTest(tf.test.TestCase): })).SerializeToString() example_decoder = tf_example_decoder.TfExampleDecoder() - tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + tensor_dict = example_decoder.decode(tf.convert_to_tensor(example)) self.assertAllEqual((tensor_dict[ fields.InputDataFields.groundtruth_is_crowd].get_shape().as_list()), @@ -220,7 +220,7 @@ class TfExampleDecoderTest(tf.test.TestCase): })).SerializeToString() example_decoder = tf_example_decoder.TfExampleDecoder() - tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + tensor_dict = example_decoder.decode(tf.convert_to_tensor(example)) self.assertAllEqual((tensor_dict[ fields.InputDataFields.groundtruth_difficult].get_shape().as_list()), @@ -263,7 +263,7 @@ class TfExampleDecoderTest(tf.test.TestCase): 'image/segmentation/object/class': self._Int64Feature( instance_segmentation_classes)})).SerializeToString() example_decoder = tf_example_decoder.TfExampleDecoder() - tensor_dict = example_decoder.Decode(tf.convert_to_tensor(example)) + tensor_dict = example_decoder.decode(tf.convert_to_tensor(example)) self.assertAllEqual(( tensor_dict[fields.InputDataFields.groundtruth_instance_masks]. diff --git a/object_detection/exporter.py b/object_detection/exporter.py index 3bc617b33..24b654a55 100644 --- a/object_detection/exporter.py +++ b/object_detection/exporter.py @@ -101,7 +101,7 @@ def freeze_graph_with_def_protos( def _tf_example_input_placeholder(): tf_example_placeholder = tf.placeholder( tf.string, shape=[], name='tf_example') - tensor_dict = tf_example_decoder.TfExampleDecoder().Decode( + tensor_dict = tf_example_decoder.TfExampleDecoder().decode( tf_example_placeholder) image = tensor_dict[fields.InputDataFields.image] return tf.expand_dims(image, axis=0) -- GitLab From d9d10fbbb938534af72f405983cabb85258ac5f3 Mon Sep 17 00:00:00 2001 From: Derek Chow Date: Tue, 27 Jun 2017 15:04:51 -0700 Subject: [PATCH 093/110] Add capability to export as SavedModel in exporter script. --- object_detection/export_inference_graph.py | 8 +- object_detection/exporter.py | 153 ++++++++++++++++----- object_detection/exporter_test.py | 46 ++++++- 3 files changed, 169 insertions(+), 38 deletions(-) diff --git a/object_detection/export_inference_graph.py b/object_detection/export_inference_graph.py index d7ffa4152..e9836e997 100644 --- a/object_detection/export_inference_graph.py +++ b/object_detection/export_inference_graph.py @@ -16,7 +16,8 @@ r"""Tool to export an object detection model for inference. Prepares an object detection tensorflow graph for inference using model -configuration and an optional trained checkpoint. +configuration and an optional trained checkpoint. Outputs either an inference +graph or 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. @@ -77,6 +78,8 @@ flags.DEFINE_string('checkpoint_path', '', 'Optional path to checkpoint file. ' 'the graph.') flags.DEFINE_string('inference_graph_path', '', 'Path to write the output ' 'inference graph.') +flags.DEFINE_bool('export_as_saved_model', False, 'Whether the exported graph ' + 'should be saved as a SavedModel') FLAGS = flags.FLAGS @@ -90,7 +93,8 @@ def main(_): text_format.Merge(f.read(), pipeline_config) exporter.export_inference_graph(FLAGS.input_type, pipeline_config, FLAGS.checkpoint_path, - FLAGS.inference_graph_path) + FLAGS.inference_graph_path, + FLAGS.export_as_saved_model) if __name__ == '__main__': diff --git a/object_detection/exporter.py b/object_detection/exporter.py index 24b654a55..24e84b339 100644 --- a/object_detection/exporter.py +++ b/object_detection/exporter.py @@ -22,6 +22,7 @@ from tensorflow.python.client import session from tensorflow.python.framework import graph_util from tensorflow.python.framework import importer from tensorflow.python.platform import gfile +from tensorflow.python.saved_model import signature_constants from tensorflow.python.training import saver as saver_lib from object_detection.builders import model_builder from object_detection.core import standard_fields as fields @@ -39,7 +40,6 @@ def freeze_graph_with_def_protos( output_node_names, restore_op_name, filename_tensor_name, - output_graph, clear_devices, initializer_nodes, variable_names_blacklist=''): @@ -92,9 +92,30 @@ def freeze_graph_with_def_protos( output_node_names.split(','), variable_names_blacklist=variable_names_blacklist) - with gfile.GFile(output_graph, 'wb') as f: - f.write(output_graph_def.SerializeToString()) - logging.info('%d ops in the final graph.', len(output_graph_def.node)) + return output_graph_def + + +def get_frozen_graph_def(inference_graph_def, use_moving_averages, + input_checkpoint, output_node_names): + """Freezes all variables in a graph definition.""" + saver = None + if use_moving_averages: + variable_averages = tf.train.ExponentialMovingAverage(0.0) + variables_to_restore = variable_averages.variables_to_restore() + saver = tf.train.Saver(variables_to_restore) + else: + saver = tf.train.Saver() + + frozen_graph_def = freeze_graph_with_def_protos( + input_graph_def=inference_graph_def, + input_saver_def=saver.as_saver_def(), + input_checkpoint=input_checkpoint, + output_node_names=output_node_names, + restore_op_name='save/restore_all', + filename_tensor_name='save/Const:0', + clear_devices=True, + initializer_nodes='') + return frozen_graph_def # TODO: Support batch tf example inputs. @@ -153,6 +174,9 @@ def _add_output_tensor_nodes(postprocessed_tensors): 'detection_masks': [batch, max_detections, mask_height, mask_width] (optional). 'num_detections': [batch] + + Returns: + A tensor dict containing the added output tensor nodes. """ label_id_offset = 1 boxes = postprocessed_tensors.get('detection_boxes') @@ -160,12 +184,14 @@ def _add_output_tensor_nodes(postprocessed_tensors): classes = postprocessed_tensors.get('detection_classes') + label_id_offset masks = postprocessed_tensors.get('detection_masks') num_detections = postprocessed_tensors.get('num_detections') - tf.identity(boxes, name='detection_boxes') - tf.identity(scores, name='detection_scores') - tf.identity(classes, name='detection_classes') - tf.identity(num_detections, name='num_detections') + outputs = {} + outputs['detection_boxes'] = tf.identity(boxes, name='detection_boxes') + outputs['detection_scores'] = tf.identity(scores, name='detection_scores') + outputs['detection_classes'] = tf.identity(classes, name='detection_classes') + outputs['num_detections'] = tf.identity(num_detections, name='num_detections') if masks is not None: - tf.identity(masks, name='detection_masks') + outputs['detection_masks'] = tf.identity(masks, name='detection_masks') + return outputs def _write_inference_graph(inference_graph_path, @@ -192,23 +218,17 @@ def _write_inference_graph(inference_graph_path, """ inference_graph_def = tf.get_default_graph().as_graph_def() if checkpoint_path: - saver = None - if use_moving_averages: - variable_averages = tf.train.ExponentialMovingAverage(0.0) - variables_to_restore = variable_averages.variables_to_restore() - saver = tf.train.Saver(variables_to_restore) - else: - saver = tf.train.Saver() - freeze_graph_with_def_protos( - input_graph_def=inference_graph_def, - input_saver_def=saver.as_saver_def(), + output_graph_def = get_frozen_graph_def( + inference_graph_def=inference_graph_def, + use_moving_averages=use_moving_averages, input_checkpoint=checkpoint_path, 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='') + ) + + with gfile.GFile(inference_graph_path, 'wb') as f: + f.write(output_graph_def.SerializeToString()) + logging.info('%d ops in the final graph.', len(output_graph_def.node)) + return tf.train.write_graph(inference_graph_def, os.path.dirname(inference_graph_path), @@ -216,11 +236,70 @@ def _write_inference_graph(inference_graph_path, as_text=False) +def _write_saved_model(inference_graph_path, inputs, outputs, + checkpoint_path=None, use_moving_averages=False): + """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: + inference_graph_path: Path to write inference graph. + inputs: The input image tensor to use for detection. + outputs: A tensor dictionary containing the outputs of a DetectionModel. + checkpoint_path: Optional path to the checkpoint file. + use_moving_averages: Whether to export the original or the moving averages + of the trainable variables from the checkpoint. + """ + inference_graph_def = tf.get_default_graph().as_graph_def() + checkpoint_graph_def = None + if checkpoint_path: + output_node_names = ','.join(outputs.keys()) + checkpoint_graph_def = get_frozen_graph_def( + inference_graph_def=inference_graph_def, + use_moving_averages=use_moving_averages, + input_checkpoint=checkpoint_path, + output_node_names=output_node_names + ) + + with tf.Graph().as_default(): + with session.Session() as sess: + + tf.import_graph_def(checkpoint_graph_def) + + builder = tf.saved_model.builder.SavedModelBuilder(inference_graph_path) + + 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=signature_constants.PREDICT_METHOD_NAME)) + + builder.add_meta_graph_and_variables( + sess, [tf.saved_model.tag_constants.SERVING], + signature_def_map={ + 'signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY': + detection_signature, + }, + ) + builder.save() + + def _export_inference_graph(input_type, detection_model, use_moving_averages, checkpoint_path, - inference_graph_path): + inference_graph_path, + export_as_saved_model=False): """Export helper.""" if input_type not in input_placeholder_fn_map: raise ValueError('Unknown input type: {}'.format(input_type)) @@ -228,18 +307,19 @@ def _export_inference_graph(input_type, preprocessed_inputs = detection_model.preprocess(inputs) output_tensors = detection_model.predict(preprocessed_inputs) postprocessed_tensors = detection_model.postprocess(output_tensors) - _add_output_tensor_nodes(postprocessed_tensors) - out_node_names = ['num_detections', 'detection_scores,' - 'detection_boxes', 'detection_classes'] - if 'detection_masks' in postprocessed_tensors: - out_node_names.append('detection_masks') - _write_inference_graph(inference_graph_path, checkpoint_path, - use_moving_averages, - output_node_names=','.join(out_node_names)) + outputs = _add_output_tensor_nodes(postprocessed_tensors) + out_node_names = list(outputs.keys()) + if export_as_saved_model: + _write_saved_model(inference_graph_path, inputs, outputs, checkpoint_path, + use_moving_averages) + else: + _write_inference_graph(inference_graph_path, checkpoint_path, + use_moving_averages, + output_node_names=','.join(out_node_names)) def export_inference_graph(input_type, pipeline_config, checkpoint_path, - inference_graph_path): + inference_graph_path, export_as_saved_model=False): """Exports inference graph for the model specified in the pipeline config. Args: @@ -248,9 +328,12 @@ def export_inference_graph(input_type, pipeline_config, checkpoint_path, pipeline_config: pipeline_pb2.TrainAndEvalPipelineConfig proto. checkpoint_path: Path to the checkpoint file to freeze. inference_graph_path: Path to write inference graph to. + export_as_saved_model: If the model should be exported as a SavedModel. If + false, it is saved as an inference graph. """ detection_model = model_builder.build(pipeline_config.model, is_training=False) _export_inference_graph(input_type, detection_model, pipeline_config.eval_config.use_moving_averages, - checkpoint_path, inference_graph_path) + checkpoint_path, inference_graph_path, + export_as_saved_model) diff --git a/object_detection/exporter_test.py b/object_detection/exporter_test.py index eb7c742d8..d613a7f1c 100644 --- a/object_detection/exporter_test.py +++ b/object_detection/exporter_test.py @@ -15,14 +15,19 @@ """Tests for object_detection.export_inference_graph.""" import os -import mock import numpy as np +import six import tensorflow as tf from object_detection import exporter from object_detection.builders import model_builder from object_detection.core import model from object_detection.protos import pipeline_pb2 +if six.PY2: + import mock # pylint: disable=g-import-not-at-top +else: + from unittest import mock # pylint: disable=g-import-not-at-top + class FakeModel(model.DetectionModel): @@ -348,6 +353,45 @@ class ExportInferenceGraphTest(tf.test.TestCase): self.assertAllClose(masks, np.arange(32).reshape([2, 4, 4])) self.assertAllClose(num_detections, [2]) + def test_export_saved_model_and_run_inference(self): + checkpoint_path = os.path.join(self.get_temp_dir(), 'model-ckpt') + self._save_checkpoint_from_mock_model(checkpoint_path, + use_moving_averages=False) + inference_graph_path = os.path.join(self.get_temp_dir(), + 'saved_model') + + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel(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, + checkpoint_path=checkpoint_path, + inference_graph_path=inference_graph_path, + export_as_saved_model=True) + + with tf.Graph().as_default() as od_graph: + with self.test_session(graph=od_graph) as sess: + tf.saved_model.loader.load( + sess, [tf.saved_model.tag_constants.SERVING], inference_graph_path) + tf_example = od_graph.get_tensor_by_name('import/tf_example:0') + boxes = od_graph.get_tensor_by_name('import/detection_boxes:0') + scores = od_graph.get_tensor_by_name('import/detection_scores:0') + classes = od_graph.get_tensor_by_name('import/detection_classes:0') + masks = od_graph.get_tensor_by_name('import/detection_masks:0') + num_detections = od_graph.get_tensor_by_name('import/num_detections:0') + (boxes, scores, classes, masks, num_detections) = sess.run( + [boxes, scores, classes, masks, num_detections], + feed_dict={tf_example: self._create_tf_example( + np.ones((4, 4, 3)).astype(np.uint8))}) + self.assertAllClose(boxes, [[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]]) + self.assertAllClose(scores, [[0.7, 0.6]]) + self.assertAllClose(classes, [[1, 2]]) + self.assertAllClose(masks, np.arange(32).reshape([2, 4, 4])) + self.assertAllClose(num_detections, [2]) if __name__ == '__main__': tf.test.main() -- GitLab From c9bb58f2c86375dce38a80853f55fcf282a8b6cd Mon Sep 17 00:00:00 2001 From: James Pruegsanusak Date: Wed, 28 Jun 2017 14:25:08 +0800 Subject: [PATCH 094/110] Fix typo in obj detection's running_pets.md --- object_detection/g3doc/running_pets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_detection/g3doc/running_pets.md b/object_detection/g3doc/running_pets.md index 23943c794..6975b1966 100644 --- a/object_detection/g3doc/running_pets.md +++ b/object_detection/g3doc/running_pets.md @@ -284,7 +284,7 @@ command from `tensorflow/models/object_detection`: ``` bash # From tensorflow/models gsutil cp gs://${YOUR_GCS_BUCKET}/train/model.ckpt-${CHECKPOINT_NUMBER}.* . -python object_detection/export_inference_graph \ +python object_detection/export_inference_graph.py \ --input_type image_tensor \ --pipeline_config_path object_detection/samples/configs/faster_rcnn_resnet101_pets.config \ --checkpoint_path model.ckpt-${CHECKPOINT_NUMBER} \ -- GitLab From 65f0fa2e597127a19f57c1a875f5138891daa359 Mon Sep 17 00:00:00 2001 From: Ben Mabey Date: Sat, 24 Jun 2017 23:14:46 -0600 Subject: [PATCH 095/110] updates object_detection/eval.py to be python3 compatible --- object_detection/evaluator.py | 2 +- object_detection/utils/metrics.py | 3 ++- object_detection/utils/np_box_list.py | 3 ++- object_detection/utils/visualization_utils.py | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/object_detection/evaluator.py b/object_detection/evaluator.py index 28ac1183d..45f03dc76 100644 --- a/object_detection/evaluator.py +++ b/object_detection/evaluator.py @@ -154,7 +154,7 @@ def evaluate(create_input_dict_fn, create_model_fn, eval_config, categories, """ if batch_index >= eval_config.num_visualizations: if 'original_image' in tensor_dict: - tensor_dict = {k: v for (k, v) in tensor_dict.iteritems() + tensor_dict = {k: v for (k, v) in tensor_dict.items() if k != 'original_image'} try: (result_dict, _) = sess.run([tensor_dict, update_op]) diff --git a/object_detection/utils/metrics.py b/object_detection/utils/metrics.py index 85f94efa8..cfce1e9ce 100644 --- a/object_detection/utils/metrics.py +++ b/object_detection/utils/metrics.py @@ -17,6 +17,7 @@ from __future__ import division import numpy as np +from six import moves def compute_precision_recall(scores, labels, num_gt): @@ -103,7 +104,7 @@ def compute_average_precision(precision, recall): raise ValueError("Precision must be in the range of [0, 1].") if np.amin(recall) < 0 or np.amax(recall) > 1: raise ValueError("recall must be in the range of [0, 1].") - if not all(recall[i] <= recall[i + 1] for i in xrange(len(recall) - 1)): + if not all(recall[i] <= recall[i + 1] for i in moves.range(len(recall) - 1)): raise ValueError("recall must be a non-decreasing array") recall = np.concatenate([[0], recall, [1]]) diff --git a/object_detection/utils/np_box_list.py b/object_detection/utils/np_box_list.py index 7df9f68f5..13a1fde90 100644 --- a/object_detection/utils/np_box_list.py +++ b/object_detection/utils/np_box_list.py @@ -16,6 +16,7 @@ """Numpy BoxList classes and functions.""" import numpy as np +from six import moves class BoxList(object): @@ -127,7 +128,7 @@ class BoxList(object): ymin, and all xmax of boxes are equal or greater than xmin. """ if data.shape[0] > 0: - for i in xrange(data.shape[0]): + for i in moves.range(data.shape[0]): if data[i, 0] > data[i, 2] or data[i, 1] > data[i, 3]: return False return True diff --git a/object_detection/utils/visualization_utils.py b/object_detection/utils/visualization_utils.py index 41d80db6a..1d0802c36 100644 --- a/object_detection/utils/visualization_utils.py +++ b/object_detection/utils/visualization_utils.py @@ -80,7 +80,7 @@ def encode_image_array_as_png_str(image): PNG encoded image string. """ image_pil = Image.fromarray(np.uint8(image)) - output = six.StringIO() + output = six.BytesIO() image_pil.save(output, format='PNG') png_string = output.getvalue() output.close() -- GitLab From 817bd396ffd3194d63a265ccd96c2d58a180f545 Mon Sep 17 00:00:00 2001 From: jiaphuan Date: Wed, 28 Jun 2017 15:35:03 -0700 Subject: [PATCH 096/110] Fix evaluation mistake 1 should not shuffle during evaluation; 2 num_readers should be one, or each sample would be evaluate for (num_reader) times and only (num_examples / num_reader) distinct samples are evaluted. --- object_detection/samples/configs/ssd_mobilenet_v1_pets.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/object_detection/samples/configs/ssd_mobilenet_v1_pets.config b/object_detection/samples/configs/ssd_mobilenet_v1_pets.config index 429075c64..8aeb73870 100644 --- a/object_detection/samples/configs/ssd_mobilenet_v1_pets.config +++ b/object_detection/samples/configs/ssd_mobilenet_v1_pets.config @@ -183,4 +183,6 @@ eval_input_reader: { input_path: "PATH_TO_BE_CONFIGURED/pet_val.record" } label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" + shuffle: false + num_readers: 1 } -- GitLab From 4fddaada2f00a194d3f2d35eef1d0082a4d988da Mon Sep 17 00:00:00 2001 From: jiaphuan Date: Wed, 28 Jun 2017 15:57:10 -0700 Subject: [PATCH 097/110] Update faster_rcnn_inception_resnet_v2_atrous_pets.config --- .../configs/faster_rcnn_inception_resnet_v2_atrous_pets.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_pets.config b/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_pets.config index fc7e14e25..e27c58e7e 100644 --- a/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_pets.config +++ b/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_pets.config @@ -133,4 +133,6 @@ eval_input_reader: { input_path: "PATH_TO_BE_CONFIGURED/pet_val.record" } label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" + shuffle: false + num_readers: 1 } -- GitLab From 95c8506589239cebf8ba23808c0c74ac5fdcc0d3 Mon Sep 17 00:00:00 2001 From: jiaphuan Date: Wed, 28 Jun 2017 15:57:32 -0700 Subject: [PATCH 098/110] Update faster_rcnn_resnet101_pets.config --- .../samples/configs/faster_rcnn_resnet101_pets.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/object_detection/samples/configs/faster_rcnn_resnet101_pets.config b/object_detection/samples/configs/faster_rcnn_resnet101_pets.config index cee6604a4..e61d5ff7a 100644 --- a/object_detection/samples/configs/faster_rcnn_resnet101_pets.config +++ b/object_detection/samples/configs/faster_rcnn_resnet101_pets.config @@ -131,4 +131,6 @@ eval_input_reader: { input_path: "PATH_TO_BE_CONFIGURED/pet_val.record" } label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" + shuffle: false + num_readers: 1 } -- GitLab From 633f705c3dce50c571120f72db3a4b25f1ee38f3 Mon Sep 17 00:00:00 2001 From: jiaphuan Date: Wed, 28 Jun 2017 15:57:48 -0700 Subject: [PATCH 099/110] Update faster_rcnn_resnet101_voc07.config --- .../samples/configs/faster_rcnn_resnet101_voc07.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/object_detection/samples/configs/faster_rcnn_resnet101_voc07.config b/object_detection/samples/configs/faster_rcnn_resnet101_voc07.config index 461898faf..e23622418 100644 --- a/object_detection/samples/configs/faster_rcnn_resnet101_voc07.config +++ b/object_detection/samples/configs/faster_rcnn_resnet101_voc07.config @@ -132,4 +132,6 @@ eval_input_reader: { input_path: "PATH_TO_BE_CONFIGURED/pascal_val.record" } label_map_path: "PATH_TO_BE_CONFIGURED/pascal_label_map.pbtxt" + shuffle: false + num_readers: 1 } -- GitLab From 47585db72553e7d4f69a1df193594d6fe8647f9d Mon Sep 17 00:00:00 2001 From: jiaphuan Date: Wed, 28 Jun 2017 15:58:05 -0700 Subject: [PATCH 100/110] Update faster_rcnn_resnet152_pets.config --- .../samples/configs/faster_rcnn_resnet152_pets.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/object_detection/samples/configs/faster_rcnn_resnet152_pets.config b/object_detection/samples/configs/faster_rcnn_resnet152_pets.config index aae28489e..8a466ee6d 100644 --- a/object_detection/samples/configs/faster_rcnn_resnet152_pets.config +++ b/object_detection/samples/configs/faster_rcnn_resnet152_pets.config @@ -131,4 +131,6 @@ eval_input_reader: { input_path: "PATH_TO_BE_CONFIGURED/pet_val.record" } label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" + shuffle: false + num_readers: 1 } -- GitLab From 92adbbfad16de3b725089548153558b106c31fe8 Mon Sep 17 00:00:00 2001 From: jiaphuan Date: Wed, 28 Jun 2017 15:58:31 -0700 Subject: [PATCH 101/110] Update faster_rcnn_resnet50_pets.config --- .../samples/configs/faster_rcnn_resnet50_pets.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/object_detection/samples/configs/faster_rcnn_resnet50_pets.config b/object_detection/samples/configs/faster_rcnn_resnet50_pets.config index 110c1b4bb..9764844d7 100644 --- a/object_detection/samples/configs/faster_rcnn_resnet50_pets.config +++ b/object_detection/samples/configs/faster_rcnn_resnet50_pets.config @@ -131,4 +131,6 @@ eval_input_reader: { input_path: "PATH_TO_BE_CONFIGURED/pet_val.record" } label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" + shuffle: false + num_readers: 1 } -- GitLab From 314207c14eff3795e7e653b9f3c50c747f9b734e Mon Sep 17 00:00:00 2001 From: jiaphuan Date: Wed, 28 Jun 2017 15:58:52 -0700 Subject: [PATCH 102/110] Update rfcn_resnet101_pets.config --- object_detection/samples/configs/rfcn_resnet101_pets.config | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/object_detection/samples/configs/rfcn_resnet101_pets.config b/object_detection/samples/configs/rfcn_resnet101_pets.config index a2b88f9df..5750563ac 100644 --- a/object_detection/samples/configs/rfcn_resnet101_pets.config +++ b/object_detection/samples/configs/rfcn_resnet101_pets.config @@ -128,4 +128,6 @@ eval_input_reader: { input_path: "PATH_TO_BE_CONFIGURED/pet_val.record" } label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" -} \ No newline at end of file + shuffle: false + num_readers: 1 +} -- GitLab From 3e71035cd6c9a0c2a33b2d9ae211655a283b189d Mon Sep 17 00:00:00 2001 From: jiaphuan Date: Wed, 28 Jun 2017 15:59:18 -0700 Subject: [PATCH 103/110] Update ssd_inception_v2_pets.config --- object_detection/samples/configs/ssd_inception_v2_pets.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/object_detection/samples/configs/ssd_inception_v2_pets.config b/object_detection/samples/configs/ssd_inception_v2_pets.config index b14fa480d..fd799b4ca 100644 --- a/object_detection/samples/configs/ssd_inception_v2_pets.config +++ b/object_detection/samples/configs/ssd_inception_v2_pets.config @@ -177,4 +177,6 @@ eval_input_reader: { input_path: "PATH_TO_BE_CONFIGURED/pet_val.record" } label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" + shuffle: false + num_readers: 1 } -- GitLab From 6117a4e51559da3e36f256c7a349f0a587887c0c Mon Sep 17 00:00:00 2001 From: Nils Lattek Date: Thu, 29 Jun 2017 11:04:42 +0200 Subject: [PATCH 104/110] Fixed dictionary key for SavedModel --- object_detection/exporter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_detection/exporter.py b/object_detection/exporter.py index 24e84b339..b6dd46408 100644 --- a/object_detection/exporter.py +++ b/object_detection/exporter.py @@ -287,7 +287,7 @@ def _write_saved_model(inference_graph_path, inputs, outputs, builder.add_meta_graph_and_variables( sess, [tf.saved_model.tag_constants.SERVING], signature_def_map={ - 'signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY': + signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: detection_signature, }, ) -- GitLab From d7d87e3d2ed045602829365d9fd3e64a2fa15b65 Mon Sep 17 00:00:00 2001 From: Ryan Sepassi Date: Thu, 29 Jun 2017 13:40:51 -0700 Subject: [PATCH 105/110] Fix KL when num_classes != 2 (#1820) Fix to [issue 1724](https://github.com/tensorflow/models/issues/1724) --- adversarial_text/adversarial_losses.py | 1 + 1 file changed, 1 insertion(+) diff --git a/adversarial_text/adversarial_losses.py b/adversarial_text/adversarial_losses.py index 46a0b371b..7ca994666 100644 --- a/adversarial_text/adversarial_losses.py +++ b/adversarial_text/adversarial_losses.py @@ -212,6 +212,7 @@ def _kl_divergence_with_logits(q_logits, p_logits, weights): # For softmax regression else: + q = tf.nn.softmax(q_logits) kl = tf.reduce_sum( q * (tf.nn.log_softmax(q_logits) - tf.nn.log_softmax(p_logits)), 1) -- GitLab From 90f63a1e1653bfa17fde8260a4aa20231b269b7d Mon Sep 17 00:00:00 2001 From: Alex Lee Date: Thu, 29 Jun 2017 18:09:05 -0700 Subject: [PATCH 106/110] Fix CDNA transformation bug and speed up its implementation. - Fix CDNA transformation bug where transformed channels of color and masks were combined incorrectly. - Remove for loop over batch size in implementation of CDNA transformation. This speeds up the building of the graph. --- video_prediction/prediction_model.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/video_prediction/prediction_model.py b/video_prediction/prediction_model.py index 4ebbcbd2e..ebdc15d7c 100644 --- a/video_prediction/prediction_model.py +++ b/video_prediction/prediction_model.py @@ -261,6 +261,8 @@ def cdna_transformation(prev_image, cdna_input, num_masks, color_channels): List of images transformed by the predicted CDNA kernels. """ batch_size = int(cdna_input.get_shape()[0]) + height = int(prev_image.get_shape()[1]) + width = int(prev_image.get_shape()[2]) # Predict kernels using linear function of last hidden layer. cdna_kerns = slim.layers.fully_connected( @@ -276,20 +278,22 @@ def cdna_transformation(prev_image, cdna_input, num_masks, color_channels): norm_factor = tf.reduce_sum(cdna_kerns, [1, 2, 3], keep_dims=True) cdna_kerns /= norm_factor - cdna_kerns = tf.tile(cdna_kerns, [1, 1, 1, color_channels, 1]) - cdna_kerns = tf.split(axis=0, num_or_size_splits=batch_size, value=cdna_kerns) - prev_images = tf.split(axis=0, num_or_size_splits=batch_size, value=prev_image) + # Treat the color channel dimension as the batch dimension since the same + # transformation is applied to each color channel. + # Treat the batch dimension as the channel dimension so that + # depthwise_conv2d can apply a different transformation to each sample. + cdna_kerns = tf.transpose(cdna_kerns, [1, 2, 0, 4, 3]) + cdna_kerns = tf.reshape(cdna_kerns, [DNA_KERN_SIZE, DNA_KERN_SIZE, batch_size, num_masks]) + # Swap the batch and channel dimensions. + prev_image = tf.transpose(prev_image, [3, 1, 2, 0]) # Transform image. - transformed = [] - for kernel, preimg in zip(cdna_kerns, prev_images): - kernel = tf.squeeze(kernel) - if len(kernel.get_shape()) == 3: - kernel = tf.expand_dims(kernel, -1) - transformed.append( - tf.nn.depthwise_conv2d(preimg, kernel, [1, 1, 1, 1], 'SAME')) - transformed = tf.concat(axis=0, values=transformed) - transformed = tf.split(axis=3, num_or_size_splits=num_masks, value=transformed) + transformed = tf.nn.depthwise_conv2d(prev_image, cdna_kerns, [1, 1, 1, 1], 'SAME') + + # Transpose the dimensions to where they belong. + transformed = tf.reshape(transformed, [color_channels, height, width, batch_size, num_masks]) + transformed = tf.transpose(transformed, [3, 1, 2, 0, 4]) + transformed = tf.unstack(transformed, axis=-1) return transformed -- GitLab From e29fc00e769ab3b29fde2fd976f0483b7f4364de Mon Sep 17 00:00:00 2001 From: James Pruegsanusak Date: Fri, 30 Jun 2017 14:37:07 +0800 Subject: [PATCH 107/110] Minor typo obj detection's preparing_inputs.md --- object_detection/g3doc/preparing_inputs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_detection/g3doc/preparing_inputs.md b/object_detection/g3doc/preparing_inputs.md index 1e80bebb0..fc5609532 100644 --- a/object_detection/g3doc/preparing_inputs.md +++ b/object_detection/g3doc/preparing_inputs.md @@ -26,7 +26,7 @@ You should end up with two TFRecord files named `pascal_train.record` and The label map for the PASCAL VOC data set can be found at `data/pascal_label_map.pbtxt`. -## Generation the Oxford-IIIT Pet TFRecord files. +## Generating the Oxford-IIIT Pet TFRecord files. The Oxford-IIIT Pet data set can be downloaded from [their website](http://www.robots.ox.ac.uk/~vgg/data/pets/). Extract the tar -- GitLab From 3cb3c4f90d67f7aaf7aa10636872e9b4452c9ee7 Mon Sep 17 00:00:00 2001 From: James Pruegsanusak Date: Fri, 30 Jun 2017 19:01:58 +0800 Subject: [PATCH 108/110] Add Installation link to jupyter notebook tutorial Also use the url link to github because *.md files are not rendered when using jupyter notebook --- object_detection/object_detection_tutorial.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/object_detection/object_detection_tutorial.ipynb b/object_detection/object_detection_tutorial.ipynb index 31e189916..5ea0bf9ab 100644 --- a/object_detection/object_detection_tutorial.ipynb +++ b/object_detection/object_detection_tutorial.ipynb @@ -5,7 +5,7 @@ "metadata": {}, "source": [ "# Object Detection Demo\n", - "Welcome to the object detection inference walkthrough! This notebook will walk you step by step through the process of using a pre-trained model to detect objects in an image." + "Welcome to the object detection inference walkthrough! This notebook will walk you step by step through the process of using a pre-trained model to detect objects in an image. Make sure to follow the [installation instructions](https://github.com/tensorflow/models/blob/master/object_detection/g3doc/installation.md) before you start." ] }, { @@ -96,7 +96,7 @@ "\n", "Any model exported using the `export_inference_graph.py` tool can be loaded here simply by changing `PATH_TO_CKPT` to point to a new .pb file. \n", "\n", - "By default we use an \"SSD with Mobilenet\" model here. See the [detection model zoo](g3doc/detection_model_zoo.md) for a list of other models that can be run out-of-the-box with varying speeds and accuracies." + "By default we use an \"SSD with Mobilenet\" model here. See the [detection model zoo](https://github.com/tensorflow/models/blob/master/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." ] }, { -- GitLab From afae86618d0693035ab6cd37ed89a491acfb7e50 Mon Sep 17 00:00:00 2001 From: James Pruegsanusak Date: Fri, 30 Jun 2017 19:06:48 +0800 Subject: [PATCH 109/110] Move up Installation link in obj detection README --- object_detection/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/object_detection/README.md b/object_detection/README.md index eaf13817b..092607992 100644 --- a/object_detection/README.md +++ b/object_detection/README.md @@ -31,13 +31,15 @@ https://scholar.googleusercontent.com/scholar.bib?q=info:l291WsrB-hQJ:scholar.go ## Table of contents +Before You Start: +* Installation
    + Quick Start: * Quick Start: Jupyter notebook for off-the-shelf inference
    * Quick Start: Training a pet detector
    Setup: -* Installation
    * Configuring an object detection pipeline
    * Preparing inputs
    -- GitLab From 0f6337652c9320f1e69052a9f24d83183d864bd5 Mon Sep 17 00:00:00 2001 From: ozw Date: Wed, 5 Jul 2017 13:14:32 +0900 Subject: [PATCH 110/110] Fix compatibility for object_detection/core/batcher.py --- object_detection/core/batcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_detection/core/batcher.py b/object_detection/core/batcher.py index 2325a5ede..c5dfb7121 100644 --- a/object_detection/core/batcher.py +++ b/object_detection/core/batcher.py @@ -84,7 +84,7 @@ class BatchQueue(object): # 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.iteritems()}) + for key, tensor in tensor_dict.items()}) all_tensors = tensor_dict all_tensors.update(runtime_shapes) -- GitLab

    8exLvT{mT7ytfu+i@hRIR z_hNtr{xN3PSXt5!^%TUWrC5m2_bpW-qqW=hO|#%IJFa{+rTdw?u>iBZ&-E;eJLn`< z2{)49rTUi%FdemCP;lB7e>C1v;nhbgUF?%I;RJk*$pm7!`@A8-a)-ZBM+Gb(?=c4b zhPM#%(Asg54LHa!ebs?SQI2h}OD}3{q?a52$_<+X@g@P8-nQ>=Lxw>fLT|F>l^eEP z#B5KZ9bpJUvfP^O4vGTe#b$<^vmYh|wRcQU|HA7N;h6qBxka?0BKuN2Q<-^412(BP zwY}DOqe6CAAUAm>OiMm@JI+|EPUr$InmN2<>!9aFo?9$Z&P?>>9kaFhizBQZ6=b%$Qt$lq<_NJL!OC^Jw^Vv&LnxBrl${)U}hG`4F%ulL{ zJcy^Bb%C{ZtjB2V*VF6}U>``+80RVKFk4L;`a_j-$A=EZmfjP|3(gx~)QcbtJfXb? zd1V#pPOq^D3gKwy13yUJr~U16w|bfQ&V!nsS+Vu`E+BZvXVep~_}Og5pF3SghCr}4 zUtEGFhAegJX~*6ujQPFW zCTehVRmn2ODx2r{Afc>8waImTxBAssAGrzj0P$%Hq{T|M$^xFn3fNoQiduekZ22Oe zV8e@f`T$$z7nwV<0d;?UDrq86{Cd{+%FUube~m#{|H&kkXL6*!tn?#?YEx(ks>GS9 z)RtKt3e0VP3hFZU&0Y5zjzdYjIWt8w5{zqdyb@noY_?=q>OL08fSu9~w=35|EU$m} zaRyJfMQW-AQl9!RD{F;01?m7(O}|5%Ak~gEBm5j#s~N{_2NK*WsegJm&X@hlVkoI(|?S&E(!j%j{Q#>@_(_A7S_Do${jhG*za)OhF&F@QKu--4uce1wER7N6u2cD zy_qZ#(cj8IV0RoD_Mn4m<|X)NSX!2w*BHXU&A5Xs8Q&e4T(JfoKD`p;%z9|d8`HT^ zqOMz+jOUy+&}-7h`Rc;*x2c?+UIC^IFravZ!Fk(?R!_aP;S}%d6UtO;(m)za%6kar zEkuZh-BAl7BrCjNg-fzBBMhiTwBgkp%)A!V8^3xN%5#XMA zyn_nX8eAa(e|JR4*x!(3mfI5Gr_2O)SghcN3l7DLR!5Llm!8mO0Z#A5+kynM3souy z@5`uQYm=4heI*P-nXF5*@#@ww-I7jKGQYS7c46~*y}Vu>u^iY`B~)m(VRdVT-BkoN zO+)eC456A+;I9ST@4!-fpBN@?5Yth#Sl`ZMqM_QnH+tCxc`(|z@fDvXFXaxz@Hc0S zx$s4f%aEc6bMxr%c4Mt$D0w)wG{Hi}{FP^-#qnmYGhq#~7$TKyiFs$ImTb6XKKnY0 zgr!opYcYgcL9KXu0@h=s(cWsE1SVs-`*Pxel!-oR5km{r2?ADtqYaQp7uFI8>6ML zGA;~vc#84R`vSXcCmT%8m(@hYxR46eQ#j9U6^4v|HZ2xVWioMfeiaz@u)H5Eh^wqM zEM0pnVohlXKAx&OSJ#c!lX-R)*iF3LEpkia-f7lr`tP7vAehHqwef!+6i5Hpq*wx} zHt<~{9iT2tkM(^&+ic8zgaSLKfQ?|(*dN48#+zw7uNk{bv1;OyQ_P}47ptIshUPvU zbyBJ2^>>WA!q2m;cKS8_Z%z@E!cUA&9Qu8}YY)%cWVGWmidqh#!yj_A(rOoo!`nQR zhsaF?>80Ld<}OBJs4nQ#%9)JxJG@n%ul+<)wmzsf5m(5&)T8)Xwvx3wb21#JHUrDk zj~JA7I`Z)0{bFzS{#ss4Bx{_)bQ ze(f{>cCKPf_2HRkgx0;1o#@r|pmt5<+p%dO?o7v)=^NH~Q_kJIgMo2YJClP-#v31T z^Mv%MrQ``O%h$RG25k%4Si1p_%NtzbypG@3iMOYolfBv7qlXF5c*1wPZSxX(+(`RG z^}NQ^Kp4@hcarqTy;EoyY|$Pl(;c`;r{c3eUH>=?FVr%#!-BH?bX?b?U^!Z?Vrd~r z_*MGsOqw5UZKgW0JA9>RT=;JKbMP|_)x83dIycT$+1n-6mug1xu+=!DTPQPfl`rA% zKY3cTR0>Z8?8wegh`MD%`*louK@Svey#1?ccOsf#{FGW zsM0un9^uzs^=PLSW>!^0Qs3UK)}lRd!*3L|#GwNlgJvIcosD{bQP8gNbgl)Ld&k<5 zjCQZG^@s6zcsA54Gq0I1#mYo(H_CI_$Arl!iNQU7DX!M5D3`}Jv7x;9Ls3Rbv$<%17h-VVyPFku^W9PKYIZ*Kh2-UefSSSZk<^HS|Oo4SL35HrusRkq|(Q}Rm}om0+feNQPD zMl~ZDr$hrA*ETp1nyX8`CB943b@rSyymI$L_m*jjd$E+W49cJ@p6L@ov!yq^>ZJU> zc2bimJ2Zbwpz`3oeclAbp!a*z?EAgjdDt)D&~s8>uTf9?OhISg;JYs!A$YUSp(5*+ z6kWHh^dY%=H}r=3-N%neMG|4#xMX6Vls=dpz&ddMeQ}Nc<9zO-2+{cwK6OW;`4>J6 zAhcFL4A_!Z5Pq6QXe}if!|>J4JAz&W24uy7=Mhy z4wJd})$43;wTp#9A8LhE4xmJS+wG zKE<9sd)jg>_qtmN8{MVO%FBVWZwHl6AtEEyjhY}nlrsdv8oP$_ENx$|zLzL&4hNNV z2|XZ_J-ZZ8f0hll>_W9HAQnz#dhaDqkScOfZjKUft7vjmknfWapIJgw$ggtCQhg$E z?$r4cVJE6F5KUoEjiNrBIm&{Lyp2$*g+0?Nm|Y3f8dsc84{ zs|L?Xw_RRR-uN6EK;xOo9DZ|e&qQ-#!Xtw$utX~zTeSd7Ma24GlO5Dfw1bMTDr|5+ z7^|d-b3;obXPTu`y}VB2lP0Qy`uY?62F>Wsdv{=qNtZ9CvPS)z(znEUAC=&0svd+1 zSp8E@HT9{zRaWAVgQ}0Xwe{9HC#}^VSs$0C0mX=Of6?G~M#I(eo^DA`Pk#yv4w%Yz zyypcbw;xncM+QLP{Ez&kntfLSNekXcLJ@~*aC7Wl9anqDug-v9T`Pa2 z%h%I*dZH+VCj4T$8kW_aYXT*{&*y6En<6LrNBdg@a@)FhjA6`h7k>U`_U_3YLHm+l zKYjygPRf<8?$l>J$+h$<7J*hC5V8Iiz0f{9)`bdArl+RZ;5hgaSo%`SL) z{4aa|7JukEO|w>N98B(3u(tL2a!E1$kSgOjN|)U>FS*y)416Jdnh7+JTs4u_fGJ|p zqhO|H?P=|JM)N;xUo?(Sy8A5*@IHJWO`IG*G0J}Rq4{R^`SI2Mivt=G(vf62DzyZj zDweuX67z=S0!pJmH^K8yzur%=MNbVM1h3%GZrSZG#)A5?yuk8#zyG-JE!+%k;R%ib zL&y96!M`Vv!&|pN{#W17ag;*tp49RMH_kiHo&k7lpHc40MMBI88gn8~q)pDbwY%a`)`D3mmBI&4F>vB66(wC|6y(wgrX2JCTwYu+}w`jo3;KCFW$ z>YIu&e{`S%2k;cedGonualn=@obnI}z?l79=D;UhAVkd{rfjQ@y-}oOJ_nOZv!aFd z&zAB*yu;{cVxxvZID~XH0XP+@d{Ms#oB)h|+U4;ra3_-0$3@u%7PRZskhtZ4$c zXy(#hnUr?o1jyr@7|A4(&FoQ)&s0RX_j;gQ+^LHqCs8t7obBm?(mX6Ci9U)nQx!Lf z0KTYWBCP+|iZ=p2j4@%Y&Xjl=l!5QW-r*tJ1XK#_P?S z?k8{I)2`VogZ4epFg0g%DL!KEmi(;SPS{R8nK6#ViV z9QN^$!+*mR!I(Ki;U!QtHqB<^(WkY+SU9~Id{*Np-lTRO+(RSAVUtMPsCvGc`FwAW z-v_Y^iU=wj*^UvK#;lVWOa$*sH3PzbA;Q2U4qDS%vZPWnWamkLCsU8i0#msl6rXzeXVK8}@OF_Y17yosXMj+4ciU%UPW05XegAdCvaOyzYx zrk{ZMr!UW_hz5`}@qeJOk^`_oN7X1%&A49)wb_kLCv*J-JZ!}qGS&EBAm8ID8yw20 zNr1?Dn7N%_(&tsx{w5w6Inf&r4tvHZxFifZJ*tJGa!qEbiSV%oOI|>jA(<=QRM~{MGmWG5s@_$O{?Aqa3@RCPH)NF18Eo3Lm>e~YA`GdWk5H8W}#71Z0G&P zlJ}Rl3;o6sG>P47T`ceualh&<7)nySeCOK*ZF1-X^!XCyK+t;cR``6DXyxNgV*chsjqT>{a4B{Yb@ZFd?V%kI1yRJ* zJ5Zl}iTHD`uldnYZbt`reYO*WM$E@Jgz>b%`oQyTn(6(FLk_5w0W@m`tlvg|7zDm}YvMDm&=(!lMghlm#{TMRG*TfdFP8$e z+Eft%+PYJ!iGbHav!0XP_3}bR$TObdG`AJDyUXt^p`;UBsa|Z)0L%8~*K5wU8X2}g zgj{~^UzO6*$u825j4MIB6790(O>B4SxHWFr@&zDoTyme)xp?qD>QduapxT3 zEa}S;N%ULa&6WWX0Y{LHGP3#l$M;l5-#pfxw%akO@F~8J9)WKPNKmz@#{u&iqNIrH zgFEDKDepzjqY?_SrYv^Q*7X_JS9YLs&t5%y>BS2$xyvJL<~Gvv%X9 z4Ev;|hFo;=_XUTfNxSx5dC59YKV^?D`5!!{H>PH@XorhloSJ&+#DUjuzkObx_D&M$ z+zMRhAbdr)i(6KCD3kBLDpR@en2 zm1IfedkMn^LQ(L!GAk+>N=!l@PjElPAa=;l0KpFtZorS*-RykY^SMvhMxi~;M?U{i zeV&UO&;56hhbTMS1GxwYxa+|h$KIUM-rsa<2sn2#Kx>Tl^n8kD2z=i=1Z^}H`4S`TM|Loj4tnhI4t)%Y;c_6U zNQTx1u_dCY0?PEiQ9)EX%8_tNk{a+hCm*|Az2zY=@6DP!*@|OMUM825#wLS#_~f&N zL6&Mr;`s)Nfr$ah5ibbyjfqQIVmJAcBFMZ+rE|>Obnu}cE}TP zt=YraHQP&GLM>^nnYMb6t?-^p7yyTZgab$gXAJE{D4l2t(W6#An#VKB$=0dA&a6LZ zhEkRq$k7GC7kYGgrS_#wbM7HJ(9G^We|g3Z1G~<@=Ixv=D-g)IP#xGns$FUirDb7I zm@n~=FFr#x@vW+SOG_duBMKdI&LXS267W@1wdB=v7NFLekA46jG-!cRB83(8#Pw%l z@9*4I_3*i`XkM96Z$g`AGuVR9hPjBmY|OdMvOVcMd3|sH`}f(NLw!$C8uudMw?2&= z@-KD_S$r;XbLjhz2u?C1z4k5^hD# zP<;<$0c^$*b>J0CROEc3goXlG`K5>Hs4(A!u=jbZI4&<9V2MNexf&HXmJ?xt|1gp% z<4etY5B6P0EL9>m8Bg27A)Uz|6>4=<{dOVeR1fEx!wH%+WE`{wY;Oz|XDnuGa0WsN zbs;8WTDYF73zy%3eHYyfwL=UecR7)Wu!LI33?A5*k|Cq;^-vu!?t zUzOx$5Aq~Ok9;J^y7A`h=F&c!ANc+IO`AVC;2izNX?>EjvzmMs(oB&Wv`36#VZMwy ze3HcwzRP=z&}jR5p?{6Y_Iv-4*S(u^P>!?W-@wM}V3tr-K#F36fk+r@;f7ndag|76Z_vV@p_Z z+D*~eOG9|kY2Vi~hdy*N$kZT7-h7f0CB!W0&M6T@9a7Y0XBUhR`@64j{reA%Wm&9LCOe!j!6DQD1^8VWrGWQ*k#U%7*=YS8ZI-NfpE|3OnfcD32cB(`HysZ zIJ|RP-x+qvCX%m@Q_BQ^G#oKOD+eWps2zOxGNU z0o`|*BZ;$BdUZECE_eDZ?OIa4k@vZ`tTiyq$gZrl$c$%?LkqgwHmIu@(#i1uSpbT3 zE?cZXs;sZlUY`4c_cUviOzh-R-|7i?ju&_=hJVpexQ-s(l1v4={s8N$8!{j)czQ6dS~fHAngMuPb43$$0lN72za&RZQ=dNTNR5|D}ggF{dv?t zWcv3AT-t7Pk4<-6#>xpoESwF?TnfCiP-XOC^7VBlM@uO3CB4n$e4=bpd9{T)BpjwH z@!H%>jW&axP1{XL4Wk&Y z<)&7S^&eg@f!y#icsgosu^;a&yYpo`h}J@w;E`TRLs7{p2IqcsRB$A+_Od$p1MBQ> z8mPnTq0v3ed<&qNWVhwo0(61(`Q7SXcI}11n=J%7`brEY`TN@xbmrUpOyUdP{ve) z^Z;vn0j)&dZdPvo)K-E>7*mGZnCg2QhwgZSfPTz0EXi01_%M#6lG;ZX-)WKjPX#9g z@68F<i-1*1>c)wngx~0Jfp$%yoUFI3q+b$u4GJJN5SRJHF zW>V&ea!Deq~jL;3YYE3<+YMn zJk2Xm;Fx!d)(Lkz*n&0q55!UoLvgBNyqMiuYckAH0|{vZWt|pJ;>^P~#VO-+p1B5h zi#ax2=T>!n-kUt5oYiCNsxys<-Pi=n!X9sRuX+p28AU202;SyJ6WHOttocZshhjPn zpp~b>_@0AcwE{tr2jg!oLw3xOGeMe14a5r>mg$3$k zVSRsIVsa6Ez#@*}>w!#Lu07#73sz%2;31kk@fJ;aPv3iC8qnFA**Z#tEn!V0eA6z0 zBv-taaM+P>V6jD^0rVzVzI>IKp*!_T7Pq5Ju6vVY;&gm7;0RhNM1{4NcAMG8WRR4P zVZNCNAR*bg@(yc*uZ^dUC~Jl@`3{VOq(9Dhw=?y4JNYOp09Mq>OiV2ggPg|evJ*@r z!e#KSQet+kv@82lqfE6u$xQKjcrF!~cSqJ;Sh#tXz!3fGB9M9@wJ2Fr8d;A;uX>|$$A zCad^X8n=YCG7)4Mq64&ZP^)Fv=i!b*(if`!i|ONhAN!0Ya!m95~a$sA7V;PSMF(Neg0n2I?Ca3tBiJh{m3c z+?26^s6d*-4B;L`5bYrbHHAVM@jS9x9n4>HVFtmLHFLH%yfFo5F1e*?yXIy6d63=L zJLHd{MRn2b$7wf}tY~O?yqp3ruz~0NU+M&3)V+Y0pan6D<|vC-AUoz*tm6e|Ta~u5 zM1((BqMl%Lip9HWL&_di>sgq*E57zL9La80zdxMO%xniG(z!=qJ;{0hJ@|ND>8BPg z88k`?cc9{w1?nsz(l}U7Z_L|yX-|J8J8rNakXKeqOQs-OjnpKWpkR~14c!aYngap^ zjCx5M^*dWdSK1G_Yut`+w+#mw&F)z!qVGk;D`izmu5P$qNu~UJ6+Y^XJJ6hDCB~F( zX}VGX`mPw~VQs9RpkC(vDgx!07DFoU5}DPJr%UInZ6qubu^C(%1&lYPk_m{5ucmo> z@&@zaF$}a4zDk*lDRD@}4Y$Anl0_hN+xN?>R?%@v{Vb;RRN$HgS^G$hd?nKbTsJ&KaSgpJJ&baMflm%M^K(t+QH$;UYey^5!(4T zuj=OGc*=+PHeecOL zsF;$YNa5qBkEwJNeG_CaK>kF|uXxXWZ3iX$1pv~cc#FDD;S`s6<(6`Pv1|VcLGXALghn?=rDt7v%z1Qa3Wy2<*5p;$5C^aRuXUz5y(kd zt$Hsq{%F1TE1ow$KZu}W>nxw&ymZrPMUp^CkcX` zr2y$VOhLmHdbprhRm&27`BS`V0AADO#)8G_d8Y@^`~^W^zYM$i`)le;D9+d3;t#<0 zod_}@IYSOA_75_pGbBNW&f4&oOTU(%v^C*s=Y z{aBM|#~_ouvHlz=FO9hn+DZxfWm)8D=}sy_$m^Q;2jT7$=uElR{v+9sT|#QQ7-HSj z;zYW@zjXQCL`QiH(?md5DFfB6czmH1k;v}<_l6FfhR|AX!%xrfcUu&3Z>mr_cB1ma~2cFi; zeK35N;X&Vy#^^eXV3|??!u@va!J6QT^-NiKUKFZhW0%u8R_2{HG7BCZd)eF^AV+m_Yx#h$O3frzyCRl6`iGyFCAXW4}-;H&?8^k#}qo!Bl(NwInV zw{+lloOar2*`Z~;$tlDY;p`dH{(TWx4Aq0|x`(xZ|K0v!M=I$y?2e9ZUfg+D!2-k1 zLXjc6HJy(v)dK*>dTa{qV(8NvO~-K<;T|=Levv0?TZJ-5jTSbPX_-|A@!6rvd!^fF zEzAi=n}@gUla7w`Gy^da5N57ANgPsQOev;kq$J!ETzj2_kje(+0b9nQ$^J+g^BJ$J z_AwUuCo7&Y^0T)Aj!wOjNpJl&2k5IPTni4^h#A_LBKt;^ncH}HqZg%7Be>iwuR8L{ z*Jay)jr0?7f#jlBapZecXb6$_l{LTy6R*N|QL0xFB6}R4wknPsCD1qvaO`O-ju<-X zIGzL#f2RM@{u-;}&Qz@waz1VgDa1MdY++Gkj6?`f$W!(%zVoD-3dJ{K44np)m0!t0 zEE9*RaHsax5s`Ty_rFhqJPHkg*9XZlAjFGC*0KHcSiT8nNOu5>gxdHxS% z^Y?RW+(!NHeU317^276gyPuQ@`h6}ze9 zNH08a6;cbpA*Ll-kPylx5v;9^qSV`sTKir1>lfTy;@~urxpV6|8}#tg$@>~_7Bqn# zeFqlD-v@5L65z4MK^b`pnD!@&XC>-1`BNyc+l*nQBNT^K@uf-beMyztTV6h$NF2^@ z@-C2CqQC*I zy&QA~ZU_Y&%z~UhpcFH%sm+^3#GwIUAtBm~6JQ+GbTGSnVO!}D*r@vX=ftk z1J_x+J$Io_c;47H*%{khG*|T=DVE*ZQY)_k#`qi^EbyBSOCLg5>s!v20o^HLqPEY) zU+pTzfav_j5U*yxerc^f*O6Zx~z(&k9+gsRldTGDHdCmYXQ5?7anc zk;~iaa|UqNrywsBaV!UO`)Mt3Zl0s`UH&@i1wQ^gce~gDzPk1zgVU+%*8U;75WtJ& zpRWB`-z9r3S8E{bGc<|q}7AWtJ6p9(`ieBx_UjInbg zy_X*$tDXYG18NU`#jym?$wfX=o^GT;HOeRP>MQl)Fxx7IBHHU6w=`7tRxXyV1IGFr zqV5Sr(BF~776=$*RgaE=pteS2mpxoU@F%qM&3i($sRepioZIqmw($5p8^V$OsgB;i zftg0Csy?@nZ>xhVy%XM4Azb;N`n*4824J!Zyv9Km92y#)koGzI>by1o$tcV6X*|U8 zomMsA;JTZf*uz0Yh8XqAJx30$3yQ?o4tZE3t9ERdR9 zu;*#aLoGce=*ZSL{cO5*{+2nJ=B>BTfd59>cuk8q)QHPuk+58BsDODXO{=5rCpQ-x z9@XM#m2vZT{0===^+!Nzby~Pd?Ou&<+nr)YV_JzZY#%|YmNk3pWAP|I`M}@ za8b+T;LG}obuwx3;l1w9wW zd?-nI#eo}Qn+8L9~p*}!nuDqWiD#g za#pw3xKd4wrrq?n7c-Sl^Jpy%U5MXx8Bz&g2?xUDj#BxSV4 z(JqK9dU90FM(LTgDm9@MVNz4SIWLq+fejqUtw;{ij@3)YmwP+Mij>k0NbUMH2arOQ zToXxE5=%N>HP6n8c=GFXI3J5@r^0k-$rqHt+Lfb=7&R4i^Na1nsgy2iPC1kV-dvh0 zEbxPf@;+Q_I>t67@=N_OvyQ`t^u3F?MRQg|eFiBX$6GMlgK<0uIqvhL+M{8GZ z=CjGY6Gayh=2hZ3C!nHMpTd)>CbDCf|aUuU%B1%+}U2Cx|w29j-U-*pN7ii1J5#7@mSn;kZf=k|M zxvF&H`@BXWkbcPadX95WZeT;vSd(P1ECE{Q#7a0s>gkZKm^GF;^5Rhygz@qmRC&uR zkn#e3tgH#*REa$uEzD}Fma%+J+bZ00hZx3a7Z<+yDH89HzsG#~i?}Zz&fQMFsVoL4 zmT$SdoK)QXR|g^%$>05dM%6Siyv$`G&CEY(t5Lq%N&H-MeULyB-k!PEw||jk*J3)) z%Kn^o^UOkTH~k2a@S`w2_Hpk}{a#M(iE%Z?)w*?F6t08uhL3)DPnqo|KkVqjlNpP& zhq<0`Ts;qLCa>p9p;6kQ5@fI&bpT!PXqmj-%Jh&gJ#m_6-^9|W)sTF{sn1K8Rc0(y z;G@}J)=EFoIcV-h?MeLdx*=Ne$Fg=DjDF=8Oa`isA5j-6c(vc;`G;Lt zuh*&wn2nIXfT$HGvmvnxES7zqovS=1*$tAGABUg`$QKS2$L%%I;+I;WjmZ|2{$Y{cO zQE0&tNwf?@k1%-TdnQ%si*`IU(%vo&fFsS1wcjG;crufT9VWXZjY4vqwi68Fk#g`1 zZkfWTXn|o?ok?-(>QgJGMbxVN1yx?HS#X5IM#g-B^%el$bT6h*Z7TX0EPnPbW zEDzPeEH$N4Oj^-)Oi-Rtv*OYjrRbfr|1T}TTi(~%j!nZhD5-h4{6;GZb-qPQzHdq4 zb`*Z9XstfP$iQ5cU$&-AL`4<8b+TNV@SuTBy*=8{bk+bQkWwWPppQg)^;zp|jI}L% z(R>Y6<=kT7&QNi;fpQFPp~t)uSCM@cNcpPm@=zY)JeYgKp$B2{wSBF=uR7Auu4EFm zhp_ty7O8iM3Upt~2drfkrs=UdO}Bw+#8gdMw|6EhjTFph7_9NNoDgKC z;^VV<$WC$9Su``Y885l~U<|iZZU3gDd{kdn_&T9=zhKOtec~JT#QMFhgcAyPtceXx zi!1w-qp@T-%KT~Ii7|Hw(k7wd$$)4_FLwH%cYi_Q&w%dl%4Wq{oS)#3F2j$S4#Q^f z;G&a)N(fVQRtz!z)Ni&-wES$R7|o0X6ZT4O+RSO)Uit9K*W2&S(_6`G(g^tSVdhPL zE2DIl)>pNd&JR%eBi}Uqd7KHu`})JEIOP7g8ZdqT3%`&2vFr0xT*iOY;D2FaHW>9w zNmjZzGWiI`*QUQuO^+uk(A`~uJI_z00qTsvD2_W3XSrwN4r9FC)j^v-7pGjESwvXc zM%-2qhxr_T2jM-R_Dxe4xR!htNeRWN%QUd0F))l&V&5ZsHZI}h*SX$Ic?%dFcjv88 z@bu%scF~tyZXIm|Ke-59DXM>IM=;E!Jd@c;`(mA1<R4#wa4fxW zbB@k~f+O6b6<-Q818=>C>U-Bb_Wefe%RNs)eMTDAo)iC8(zRCF_-QwL4Pv$}cygJL zjI%uoJxg7dP=-*k=gH;LFYkG^C9PK5V?&cdZ4-f3#ro6gc5af<9hJx|O5xl?mi_r4 zF|p>Suo=C1uLpaQ%dYoWOZFOBRkJ3S0*miOyDJi&zoxDV`!Qk${3!mjTMYWKhy3{R z@A)U^lsh_E;O9VS@79x<>c8#s$p5Qd?(gzQeYXbqud)CG!bm@EB>vtw4>l6?{kAZ3 zV5~C34IRhB3XlIF!ih;N&&Ya%hCSAZn0ntFn*yWExZIV!yFJ3c*}arkMv?3z6N?_S z5?euhEQ2TIqyx5h20E>U^Y?Fy2Wwr`Und)qC)eg1QOBn~CrU(-5~76mCA533MapHC zIw9RJM>3Tv69%|%q zd56``mWmuZkQ!L(x64F)YNP_u41`0h3JY=+j-ql`XjST*=u)-he$8df9Jn*Y>Ncv` z)EgvLpkk)4K?mQK+M8%!sE%^O8IG7PrF zEFBQAK$2D&rQ9Uz`kYgT_-IfFTi4k<+Fb4>w*rZG&lWMM8fdce%kzT1NXS*Pbalpc zl1|6TnU`mE^lfK!I0>G${NDIt6Wf{9%EgG5WgOqoP5{j}#^0UYKgG8pn_Cs2s2vwp zww&VRqWrMUtDk7n5#PCKVcHBO%|(~3kU#gik##DS^|lDvL*f&Dx#&N>sm{ZHLjXBB z5!EBq%+G>6G0XBTT6Z2k!pagy?4I|856*IF*APYSn(B!h(-&4Q3Lm`{VSY-)wR7e$ zdIZsu-KNwI6XBXvWoJ~ILemsZJBHrNnB6!kR+^o>L$Udqpl62kBjM984&d*UM(B@` zzt`0@XGb2?0I}Mi$IXAnYnRl2SpU!C>30^D7(i-ug%T*y10^*bX|#Lu@&z#Z11TKm zoZ}`PA6BbW0ASt7w`(lO5~+)BjRGikK{LsTDnbiP<5O%tWljCpcEXPxfv4gL0cp|m z_i*JcdvEd@?mY-4!@jK0JD$kcIfOZ*ZY7rhj1i>OBovhiUzbBRUMr=P*7#PBJV&9mXnv-+;Krdyp@!6Z43H8&B?I$1XKyiX(qL<@9ca?}_M*R_%vzf5@(qkqCxTIp$_s($osoa0scM zHj;^3DCl|wI&n7H#zSI#ahfzUsH_2zOj?--70k_EvIkuqpwb_=wV;9Xd_2l~WYqpr&NBWdEC~1wk zHL{AkA0Q=Mk=Z;FC?$c<%)>Zij#tr`4w1~oTmwyu{?$PIhxo3_oZ6r!4B&THutWhn zzYNCyP@JsS^qB+9K%&D3u$fSQv9#JI?e6U2S==BL+TO_%@o+VMXVs)^%3sL!Ud5&x z@}w7=VN@_$&2z&Z@7=$|GJbM5bc|-$Xxi(L;5FGZ$`;V-cPzh1<;LD7;T7sJPbp%3 z@u_U7l&wJpl9#=pe!P9YqM%H3x>1Q-hvjg3&i<5ub+kUyV^WxYu+%CR-MF+gkzJS4 zV9UIpJk5*Y;;>%i=lW8wsK{BsuOYT1=tXqa$=b=@WF@DX)fT3|VX}H^=+fBIiJgR3 zNAdgb0b9+wirESL%d?%dEO_V5-<`5I)dZ*pDa~vn$l*L4PRsdZjx=Fc1>}mEXB+hRGV;~i2s(!W^b`IneB+vRXsL_?{(jLRddpF zdwsAt8UC;cqpZE3KiMsL7PKh$?DTjzz@n?jmnC{A7~64fhcp--SgFD<;c4BYP>0St<~DCsH)Z4o6;f_ zEwxusTWqmMhf!K1R_ztk-ibY{R;}2H*gJwEv3c_C@Atd!`*~i^{rSi1#V5R+>$=W4 z*ZaKR=VaKoiiuQ_Am8qHfeN&v7uwy-Y=Z) z6J|n0dH8{aw^b(#Mkh#A6Fly0{Cd?-Zr8uapc%HxSc!e%ec^LQL42eusaxs!&|Q&t ze5cB68%>OO={2CifWrefy9J5+v1&B9JEsznnc&59y~i4QW#Jkl=Le>Uee*dBiMTbO zzo;dq)jukT|FYUYHvIQhLz)DCT0xq{ z+8x*`$4U(t6K--oA`YQHIb(&4d*yriolmHBe3ZpkeN%PG`EBAO>^!t=SlV=e6!y%G z3szbe)%UJK2uMFL!gMScT7C2oj&KW{_7a&$N{wqAyiwfgrGS*E-+BW6yy*3w^lG}8 z{*-7LZN^qW7099d(tWVI@R+{H)in98W^(1rxLdAK{WM}4qB~KnJuwd3c~5&%UG}X5q>O=bZEG#`&AO!O9Qau! z2fIE3$MzpLu?@j=OJ)S`iD|4!C~kO4rv6@<(%8V!`~clOy%Os#Qc__?!aw&1$oGII zik#n0Pvj+6Ku*s*rD}LYiLsV-FMLJ8=?XtMvAS>h?j4i&ZSgu07L5wEWo}3soQ>_O zug`;Xa%d3hv5#7$fmMH*&fY=&G>)o2PX|?ZGx*^O$kMyhmBIp_T!*grHC(6A=|3)A z#B8rbQUm;t7O2|7*aDBzcl@un6|j8;yq5&TgnKFdfc(Ql%k4-vCPId~V(Q#^4Np6I zPj41+@UGPOxA&ZVYRpd(&|j8JvMFDlTpO%APhqgnG3&-TK5kJxS_`?yq0gZ?mPD@IQyhgg8e0V3-`Fx7bSva`MyEmuR zYGYZle)(!=nFCCE)HtRf^T=YUv&;TuMD-S>-f#SL>+k{gAmr-WW)?5&6+p1Rxa>M>n;i4S&9=D<+#GRy zOY4>0_M`jub&Iz@owEF2MJup+WYzd_@{K1~d6Dg4DK~}7wIw?h(cH>^PJDK(1SloBU zq%Ut9V4u0^8&D~%A|v-v1MaV9wa?kncFJihs9Zz zA#29C8`u{1nel}&Q>+w+IkSOXz6nKbbwgldEWyoL!X4X?Sv`Ct__>s-c?Pa*Se29I-^F_M>tlHy%Kb>QXHS-UmoGvB5f>s?7-VqghAIP6U zRs74$CvrNmq(k!aRR(>X-Dbl0)Pt*`E zCII!}#wu1vHdwYG^PI)rtyWF0EIcq*e6X-?$ne~U8>{FO+UGTy)L&A=<(c(%RU`1L zvztjtkyyr>-A=V(S5X>e{hLYrCw`m>E@Dl@r0_t|q@t!`tf~C0wlg{4tP$Fs?BL%%GHlh4V=R=8l@^UR^a z{* zCo95cG;SM>t!-Lj397S_;Sp+@F{4)Y;C8d-eqr@Y|Gm4!3e~DJ=~0~AaU9#lZWuuJ zY_}8gn>cEMxEIY%GXHhCMEtE))#0ffVjJ!N*jG4+0?SSXYiOTX=)j8G#kIzwH|fRD z^91j*YOj5VaIx0CfEN8XPa$UvUk;L2D5o!fnPE=ZZ~F+7`@V)bQ8`vTje>|>*d<_{)tJ+qi<Jk5PW+1yoCwPYV}`=4MgyKi95=y zAou+6MC*mEdo=|W_Ja~;RhcM=)%MRxGk>c2p^MJeOItmF|5}@@$9m zZ(`*Bzb8hvW?NzAfL)g!KuP3T@FxFh-BoBC zU2xUAqKw;Tx+;)j^XmPMQ#$&Zgs8ivw`yJb$9(fjDm8a38q9Wi)Gc?^P7Qb}8(Z)-RbBPw-;mp+{lBv=Ki>cp-L<^(y6+}^ifn_vR z6dr?_C;WyFt!fZM|wTCOA-rMl-nH5)lg6q=m*xl<8W&y-Bbaj}WZ|X@5 z{p1f@Ds_eaW_3to(Ox?7b;LE?n4|$Ve6{%X*=Lc%&#KN@eEZKEZ1qFap&Pv&<6WD; z#)rDvd8TDQ-BAz70XH97iPr06q=XxTA|~%NdWiO*e_6Yt1XID2od}|1qiRLbcMY7i zc-g}D%<#fZPbbLPZ+$pPrCm_v#*`?0oL>yt9)m_;%)^cTClubsa&wV>5MAZYXj%ZtkXY-FtsRyU(_lc)hFbTc=jM zI(%d{z5Mw3!K3c(V<~jp?7^VgtisEdjGQ4cltL(NWq?nap@r`;F|Duth`n4{^b<^0_= zO&MnpO~jrzrZ)xBDLkzL9Q^Vk?a$VtwWJK3hvJ@QsdeqDAm#=a3<ANN*wjg5exM`LMsNz##hy33PtH1*2=Wr@ZU)q~jqe`~B(a_^(*CBCra0mqNwnzf0OyY!*l|pQ|ZHwu^4XpJPM;ZAslLji$Cc5BGU8XaU*H zH{{TBog|uff`VrBvi48!nMgceqojvZwoDtjpFr#oZ!Rgc$UE||#!rkfk!v`G8v5VK zB%eY%9%#4txl8>3SIBbOt{)OY@K$xa;9O2CY;`qVZ1%pRen&05U7_)9tg`b!NB9HF zN(TAY8=51G_C{u^iszh>DEn)-mknv?qoQ(#jzr5eeVSX>kt|-N+tPW`rW|jEa`T!KCdxHt%{^(DiG?j&rWV zOwveKIt245Y{EXT3?G)2(G2yJ1`>o$?aKOJHo^|wOlrxCH7Xi6|pPYTW#Slg5ZOi8^f zF+|-<`@g08hvok(VM{FQaj@$QD-}&};{biz!U6?xGtP=3|F{Ft*?^ww*MtG)fY+~n zh=u3_ZaoJq=WyBpB)_g(xLAY(EWVyeutWnCN}kJ{GdLPtXj-d&Db^_u%kWQ4T$zoV z61FR)f2Nw^QNtVSM0Wc{+B1ZPk4eJZZCFjmF!&&(T_L8V0$j0+lTFZOy~hwAiWY6f z#t|OMuX_typd&gST>)eZ(vhA(_|_?ezWln!f|mr71wZ}J4g~`fm^krKqFN{Gr3ODZ zL0$}%9Ew2^H?scxE`d?`urODDj=Ljuzkz>vZNsvL(bxKtK7qaYk6FEDxPJY|qQn^y z62R~(gRW!~*xj~g=FXRM)0fu@+1@vV1T&FMdyLGE>oWYnOMZhrd)0ttxRW8Z;@|N4 zK+bC0y!3rfPF~~pH-~SZP!`c-w90P0e{L|YgliD~S=1MZO}zz>99T06HL!vEDO%`!YpPbeYbQBAjAkiu)MY;iQBXJzu$ z$$%)3YTAouzbJzTp0+;PIxLqvIiUH+t=Ir^;>F5a0qmJAfGj)G+Y~w4e=GHW%KnD8 ze_ZQdUq$@#U)>y@-yOEDUf^07uqJ!gF=Hxanx#Fb>0v-5w=z#i>4J zd3Ni&&s~`4vLo}S5LE2u7t~0~6=E|P26;Ayhd#Vj1m7X=3 zcMOK0#^2}N?$Lr_ku60+<{Nx37AX#XZsHvcr5CCq$@ZtO@vGO2@2e}9^i<~=3|G`u zhVD;~Q4bh&dQK-A>sfcHI^D)8<I?5pweeYv?`9zIu?sNLM;Fp&q(DJ=2y-Y&~> z5DIALkisUbA!JRC*+zDxaBL=N$mq!pB%^Lwmb-moV8dr5Jp;Dx-g#Pzb?M2-5 z<5A&uWH^V~6&Gn%(3Ywlwfu{*cvo7Ttb)(x_pY=7uDX1t*ZxIfeNFj8WitS9_bcMP z2U#gwhLlWv{q0(pao1K=eSIFgv1`r#zl**{#!~Tf3lD^(qxy?-`o4vTx#SPBufgD> zul_jpPqG*098XBjM(qAvVkFbi>X;LuL;+}L@>>_AIbaR1@7^%EYE;vaBSWYjo#L&`XBu9y$OoyMGK`n&kux=IX6GnF|h(-#2H?(Zm-(al|*BF3ze-+?hrLF zSB1ZoCyySC%eh|@)?j|&H_{*(ez#!^p_TNrD>fUgrnvdFlzvaLxS*!Pr;|Kv<*^Jr zL&f9K6qHU6L=~WbW#vRPOKD{w_e?CJ>R+9aJeK8en=AY6d?a*0Ec6F&WSx! zbq=Zx3+C@2)r^o6G?T9sX8;d|9EUh1k6b92f76tnK0We$SALtkvH~Zavwyo9 zUwvd2^=@x{f{n}d=Qu-Fz~@{QVv$*qp?3WLyn%jW6)7is|u%J_ClBT2G zazIo4WZDH)oB2Z_PQdqI%p*VyFCeP^)!C+*q{8!C>h>rNpu_xKGW$0wruvJsvIn`~ zK_(GFV*BEktCSvweKsK`p?=)pTnA-t6Je{uk@$dYUGgsz3MO0QzAVP|8U|OGt!L=r zUaUt|Ma6u3p8jV)m~&*Gymm2sTs0G^>LJd(RR=^~t%T!76-=bu-u(D=`zG-XB)Fo2 zjaHZ{4w9ckA-KpJ*|HVB-;tc8d&z^LW6V`9>oQJQ>ES0|`;b__mk=$1*{kRIJY}}_ zamA(O32P+3sEkW4yySKEDrF%_JyNV>DpaRQJqMMnM2mCcH>B`MZhFm`d71ul^yTYm z9iHyAGvS74&+FYL|Sq71kj>||gc;1EUvxM_TM@ivJqEpeH)ozIrL zTPfLbbAY48{UFla^PRx8=*z}T(xb_sv+IDC({$&+gLbOm)te&Ax}o~ghJPtUJB)v^ zsQX-%abA_^#LVHhzHuSiWyf5(fH(U5 zPJo1f31>jh{x4%tGbEjff+7Ake4Bf7)qUYhs3an+z-%qzZ4CaxJ@tL#WUg>wH-(Ny z&x=ycR8m?B1{qU?CV6AC7GeeFQa||wX`)<sfXa@o=^FmxzL;e;6G(SWOc+Uz}{>Bj$$II*+fT*>CQjm}sybN?{_JEr1BY504irzK$G{MMX$gE z6M*7vK+xgkdD340B*0W|i{zDE6$7S6>S{y7&prE6>uAH(`;O-3z7iW5p$G>&m_NzL zy?9~bw>E-Vw_&n+|EMBv{@z%4CjD&~>iYCp4{w5U*}nerfa;>!_|Xx&YzqQy3RLu` z-%qS=G~Q#HO*47p9Dl9gIs`9C&i>RP+jhXHp+~{H-A!ZfM}Q7T`RFk8UXiH>(~rJV z{u`whcwe9V9wbV0#@zkwREm*Bz>_aNj|csBerg^HYxW_p1`RMcqfvR{V~G`e@5N!# zr-Fe-3mrRG>{`CB7@{uUns|7!q{KNwt6Idds8zS{TGJsazl$v<6H%o0rEYaZiqhhJ zy;FX`B~nqIt+j07@3tsFQ~>gqCV#QOc6KiqMzsE!GLfGgH+M-~g>^rG z?CBAee9T)d(zdjI#Q_PBP4eGM@6J)z`2S2V|3THu|Bb2K`*d4@m~jF=-U7hAh@v9r zP(I?~uB8G338QlJli15cHsTSQJ1b}<+x?~~cd8e(rwvfN1&}?FGxPt`dd3Ez(k6Y% zLIrsJma|q$^?cQ=Hq%vI{JUA{vy$6v2gE|N(<-_22OXECY%%WM!<>$2Wy9XaF|wA} zVo!=VEJ39g&B1T8O*!0~czCj6_=>XOAVku6id*QDMO3y?qlVd!eb15@k2_Qb^P;aP z{Wz0JPk^^bgog4C2*8JHckEd>Rt=LPemV(Wvz6S?ddaTybiGtDmvQT(($yjcu?rA@ zeYnpX$&*mB{8K5Y(aoXMYk5{&fP!y2L>F}QFtT}F;YL!y`bWhlYOfj}w&m;|AILp^0=!kMx8d(6*=DeP@+(lk_(<_dXIeyzVH} z^YVqqilormori`IGtz&YeSf-sym8IAuXvbrO*1!CVAjwnV$U>Sx#r#!m)7wr#X-XF z@fiK$746QC0@`iY+9HYBr*pvmXy)0rGuhE9ml41)3II_&I$EKZm)It;?eTmbLs0%$ z)Ia!_e2)1GUi?o;<3E8>O#Wdox0PGn(EuEFYkmboh@A8T2$(qn&JLmj4^QQe=!092 zid#u{EsOwXtJHx<&KIYdr1wbW2pV#iXClFuM{<`E!OsEn+g+H>PQ^y=K_6x%KpxVj zC+mJFQA83QcIr2=-5D)=)umPQAro({v-_Zg4d59$QLhS8LOY5A2-MDit?g~M48W_y zLjZ&=&|CZ!z{2=JlRjE4`(A6IYe-u|o*ij$JfP`pIgSX_)bLacTBW?d2bjSSd**q2 zXZ8J#WDo!l=Q9B8Ql30enF<}im^60$@qW*uPeQ-y!A!hd#YX{Nbcibfx-Chc-dg5< z@#IPz!S4}A^+45u+%SwQjwJg#JDnj;-$R)q=Sb?~$!FZHOlfQ85wiTogU!L4`I7-F-DJ8glN zueI!cJ&Jkih_>B~2mHiYGhZjRIlB{_ScL(n-rQj$02YaigZ*vRwz_GJ!!S|dP7Ia& zjabD`uPP;oa5U56lWUKdy$S`-o8Nzbt2QrQ^)&Ab_@*MDB1qi!4WN8EVD8!@yOLgX8^SB>ZEHO%em(}Y0)>PcnC;Y!e54iIf1a^pJ z>hs}}W?D|;yK{-_44u~|^y5=! z4tVWX&!S(0@TErR$?0{VNNOpta%8BMy0N+$9Hs-^xh^^jZ>$;Jo|<@6YK@esX8e{Y zv;Gy~hIysMEW#;K-$swfuw;Wz+rs7EH9kirY;93=$*L5OzlwWd??vY@3j}Rp+cbvdhb->+mm!?<-e92xZu*>vqD3#>S-nwIyJ}^7QZ>DJbK> zkIHJk!j)R!v4-SuW>SrFg&EXB6S+X9*un*Bu_-jF7j_3)6^Xh4N-X(E4=t$32$$Z6ZY|K z^3S2}xu($;APsu8P3%t6a?f0ub734?d>`LY8Ej!O80PU*pckJGqf4-R9Y;*k!90*oW+4Q_KtsIu>pO zl@`-`tiSkm#cP@OYNuA`D=4tu*zB-PBvt*i7qjoS39}SVRrec5kn-4BSdH;|y~~oT z=|aHaK)SGNz%JLVYZ)+$Kf#h-D0|UVF(xri0|kRYFR*5lMqern_ysM&9>r5we!Hd` z>E;7UEF7^FCU)Bf;#p<|dOgut3Ga8A0SyBKV2}Z$mt6P~EW?@mi8{!36v; z?pWJNWH9ZZ_2O%qo4E_eRe4{o%zrQk1q5Wn($j6R*6K+4dn)7<0j@%^H8fx~M&4!4 zkN_Vn?o&VpFmDh)kqEm`XoaPl`3}W13i&^#~n^5C>&;wOUjk2_PZmuW0Iyk7oCDxZBQORo6 zogAEo&&-tgNSGnzUFEf6C^$uoVrw&S#}lhjIQ_c?P$+DAQtUCex{POp zO_yt0tvi_4??>z-h;BZFQPu4PMHX%~2{-)aKVKYofAevIu$%4WN`De3o`D=nek|*N z)a6Kw*N=iRcuVy2xdAD+uwLlH;Q=6L>07N_y6Gc>Mju6F`+1ixGJg4IEUe|jM_Ah0 z^e&TkGAuVNa-y*WndF&}YSaRj0oBz;&JVwH=&2$I5)H`?avHJM1Mj?m z8V=-Jg6zwD)BT__??l zmO1Njr@J6tw;*9vx~L*$(OqjOd4L26q;~G3spXo*&b^ynSINh{d&~>}Db0Y!Va3fg{0qAl4_H+;oF#$+LoqRMFORJrI4lgk1-+NWZRl+P4j8KRIa_z*jr)QKYLd-j{f| z(aV(??=jy-(g<*RQ3(sWL`;?$H$UjLAU}=B2F`gp&3pxlY*xb94-Of^fC-uWYK;8b zakPXGNTFqWVT(e0{u%9F0Mu0OFch}$oYv~GqIKxIREcaa=+{(31-Z1s6k6CfkO^w$ zw^b#B7MfscJI0%A(!3k%vANKuG_DQYTr;tqunuTsz!RiN901t1O9B!TbyAUZJ^Kt} zzZG#B0nM=nZr~?P&03rIdoy?g%ElN7Am7vUNa$b-L<5=NFuADIiF$!tie{5@TUI%I zSU{un_4M?#F|1sd*c7uw>@4RhFR!)hpT(CBGLcmV)HRW9 z+1U^L-m#6!cKrGkYT`qnrqaKxcf%FIuzKYnRN0P-@B3xj+pC{MI;N(! znr{P@Pz;4bApB;tG0S;koIZBOJz-F)KuAxSP`Pn^(cl5%LlZH+pxj${0Vk)em&e4> z>I&(Fh4h7In4LhIOsoA>I1K0AKLi0%!Qcu(^J>hjSmh+;jdt)x-3R~-yG^--5@aDP_&u!W9}Y0R-ffNo~sikEopKd z>)|lPFe;CwY9k{tuv2Y7+N7Br5t1>&foca9+83PK#J*XA@E&LdWBP}`xo0OCAPBeh z;Xn`v2S;aTCnjb)uYYGM5cS3RQ|Hd*#nQm~$hPcU32|%8#V^0lhWS5;S?;T@i|)#1 zJ>D9*=#UqyvWzVUM)FyBZuB+4KAlnpROzLW#6BYh|mOXepv)&7cPpAqd`e$L28$t#4^t ziPUJ<(U01q#m#aDKpEGw7#NMF|MqS>2gW&OE+gQ)ueKlBml;%qrUmiTvQ*Q+Mk4r| z-3_4U!GD5?^fpcw374bc51*h>0o%_&il0umc-qMS7*#uBX11BulithyzO{u$RM+y^ zxjPh`qI(BYjRHcRgEZhAc0sP|Bi&J|Re$6L7wb4d&-G(!0ynB)`)fn!G!!y7j8o>= zdt*2I9D42al#`O`0Aw$M9V|~RuM=HjQTCp3U~ z?j;2Ub2K(5x*{3)-mK#gbBP`}RIS5gSz+AuKMCdjFOhMYvUZ~%OWyAtyF2SVDF+p` zxN2yP*{hOE%Q474*ockogc5h!`Y${aG~>9BO-0goIR@4tA2eSNg>Onhjq{?C;v7Yz zFK}`f(Xh)SL{Jd!+axE>(Fybq)5EBQwPgf9hy%A5Z*q=Jw=3m;*+9*cjt z>LNJyI17+r&zDtFdv#1!z}61L+tRtmO}hS3jyVTajIx;1~+$9HSqQAK#pCgqebcb8FuswjxaLK*qH%j7eXVfX(aF(K)i7wS2wZH4nGC45 zB7Fq1NQGa?bBe~CXBhL_@JRa~Rlq)l5!mi+Ckaa%$9pWHJVBR3awi;N(JCi|%X2WO zRB7~4*+kh~%)vw#g0s}jzX0N{h~(f<=1h?FIy(&7iNuT>l42$%CgjaBVT%{%P^d08 zT`dV{((0>2w1PT_D~gScku|ZVRX=|Ddi%7VS;D~{Pr1qoLeDhTW5v*Ixy@#rdjlsv z*b+d7aer{*W3|l?L=3Dv=Z!%xQL}q*&W&j!={;Z*yJjmSR6& zRg|!}xcAmnrPK8a+79ukQgr6g84YyrxT$pE*Ec38ze&UUNFyz1x=4sZdtz+@+GT8R zZVqP+X%I>A1O*-7oIzC_cdvXXo!QG6OqZIJWsc(5pfEf7_|16&KH>X@cA6Y~or*2s z1#~9_LYV%@AYfW^4Z+M~kB0M?vZPF+Fh&TQWTJ=rkCg~C2{o`T5<33xTAs27}51hScoDa)o)+hk&{ zz#mYu#vCj!X%d5*Zae}O zWZ?G_Ss5Ep3XC1AIDOz<2Gbp@Qr~fOQnPWAU53IGIh29T zXG52Jm+8l~u!2lpwZ22TEvb$cGshw4nVEaHQlR#!Ww42f=b%iVEurYI%*3v8<$*d# z8m=fb=-KNVoF>h#CP+MwUKllN%(7GqP;9N$=R%!$%r6>Xn@nrOja={RvHTqt+xZE~ z8nC=RYWCPG^9qVR03gf%o|qK=^JJ|_H14=i&zpO0AoG^Gql4DF9Q2-4!T3(m zPA;?jzY`47qX+`Hz7p9oOons?O5UI`xJO-Wq27aYU$Hib+KEAg`eWNBzd=XW#Ds36 zuNU!TDdn2m-AR1>{ys5=b|x;ptLIBiMW>3D{_@v`rtSl;W@Ogu_DjT%iA^B_|Kz}s zYqDI0F+(8xJkadnIQQbVwVS_Ydgiv6D1uYMK-XCq2*xyV7Y@$_9rVN+v5g)QEz;sh z&40G-MOe-znt#Pz0+hU@vS)ILBJOUd343U0Xc(VQchNHK-Z4RH^evR_3N$A@Jv0I6 zaGAEPpJ(Ie<}w!uN-8skx6d$wZEbrPM^pOuR5$Uyh(cT&P6M{t(mi3>`x;uD;7bJ?Pq_DdPa$KUTa-N0grqaKQ<{M_mxZ+!f0g3P8IMaQ@W#(yY7 zi=0g>><5(P_QPSNsj2n;?1lG5fa6TDFkuZyP6Mo^f*n*gYPPP;RoiYT74Z=3m)Msi zY?NqeJIylyOcb7)bmnFvyx*)D)#v5PtJF@SrBygJDKaUPj4F-&r{T84{eB<+vp++g zMnn5e8WQk)7>9C{$56i0r)4)E2Sv4U5EPbPJJLHEKW1NB!Zg*+m!PV9GB9gN;pF7R z>UZ$WQ!B!-bYgHKacfGgQ&ADhJmet42wfDe-9JDk@NAAnRLyQTI^7P%xZ68$F`^J= zP`2ep6V510?9@bTmB#%nr!Fxyv#RZ}0L@dUo5W_U;@umfA#`b*SS&VQ8MhrXp$7i5 z?PD%s6-LcA82>}tUxv44=j}UgXZRkInA21h+z$@__Kngg0W8@sc}&(dP0P{ZE_jp`Xd*NBhf`ks-f1=BK6=7lYicfz?$4oOnG1ZL%@fWDkWF!p5OvVo%?F`p)^vz$QFJ z-RM16#8#b=W<{*1yz0S~5aL^YA#8}bHYcq9gaGk^fyc0kkB%ED);MJh zC9S!w6ZJ%dpFZYxZ?d&fMxLw`L!98qS6bDx>rps6<~7tIiLI(?JpIQs*y4vAVm5cE zRwTvuq{fQ@3e>eglxdO2~bON`RHw3pkY z0BQEMp-jYPrwM501U031{rbr#_8SzRuG9lQ8hasJ@g*|&ue9|4;Q~|y{e~A07Z`Bq zx?x4lP3J|n7-yS3W7BFZQh(p`Zh?ubvCQSD8HhQW>uF=KQ`;ihCJKj#hX{c@$UqLN zIUc9U6%(snKoeK4XSV|Ih@ zEs)zCbx2>s1)f0vT2(LZa`FOtplDKQk3Ike){HQ=6mkXuhf2lwula!9j7Dgk!b?hr zonEar&X%GXRSgoS$NlVOB;0{rAfC1BP^fTXO|=Qzj2L_GDt0QaB`K28(~rH7PVAGh zsaF15XaS+Ln^)ZNklmryV#uBID(=TOd>SyRnu+%(eL%%^xJolCsBguef`2TP5){I2 zL+wbKwB$#4_s%vGrkm@ew?QvgrM68RM)^G@y`J8~VM~}d>fIZ%$wP1~1iYv5xV`K* z0_zDP3kZNKAl%{lH`)fdDT=KOtk0|6xdvV|Iuih7^ZqeNm^t%*?{yRR&z4$P&-6c}~+!9QozK;;HIu6vn%(h-DR}5ZL0GBE!be z&)n*0;3^s-5tnaejvF+dLbZ+czzFPA@mN1be(9)g& zyGmgJY|X;e7mkN6HFZU}P`2d;KOQ^xrFab zz%g`DNn7i(4VNviPljs(WS$I zewi$j)od{qry=s9-uaCmjWG`>H(Xj<_USy(nfqt)f?2#Q(~yp^+vMSYKv#ysls)G`8NA zvU_NE`Pxq-t+daT1@wIQqE_yYF(~opvIJ%>nX~t9eig4n!OL1_m$DZBOc?L12neX> zefXK8;G9HTS!-|rq_tU>A<)}@3^g7aDHEB(jIxNW0im?33LuGY1SloM_;IbaK+3no za}Tx?pXnEa#38os0idWjmAz(2Ml5HpRcYe*y}__-SW~7ui>6lRHor4necaT(J9Mv1 zJ>o!mGIp#6_8piQKXet|q2VX4v8tkqKBRQvJbpg-2wR_M9$yC!KeuqUQ$9B4UsFkU zywmN;+uLP-x6$<>_boS3hDW_xAdnEhKHsG^Z?BpV=&Hb4KJHr5ScGNjA)}3`12=yF_va-Wdl`sUZ!^0lDO@ZqWfGRcN=A` zmfI>-N&d2d4q_kowf|>6J@u7rpj@SN8zdiU zd9148yMI!udik6`Y2O{86!RN;9RmqW#H!hgrV!S)?{M_2c&%OhEVaJ(86gSgNsO1o z<$`-tT9}PK;hR71_~8~0qktktPzw=?+0x(LKoC;6`h4p`t!F^6-<6^y4rph4Gb7Q| zMk?7FH}VS)?yiaCN) zqV(si9a*}1yt@ps5Vg(t(U{$ieaoSM!{E@VhS$6_rctC)xE~d=>pnqdGL-)VO)qrX zQxDmWZWgku)jsB3;Y&iuCf2)+_v(=w&uMJ_3>^T*#XtJ8!ZZD2F@KYyV|#ZI7==;! z=~hnKpQ*|OarrphAaC7ijT$`kK+VyD94$+LR%HQaFUo*Sh1IVN`rYVgKz2OvEzwhU z*W!TN`vGJqks*&mW4}oXpikfN^D=bhDDR&YHC4vm(jCU`SS!k31ip>sv@E~Zj;t1G z@Dx{Ip@1`oK93WeSjMv0siA#?IAmI4I@vnG%?%tjAg{LpnLrX^$N~h2HiP1?d+xQFP{RrL8YL$ z5>8MC)-MY7xPOdWZcE(Yd!vq&87NJXaOW;4BjH-{j($2z>%K!1r)-Uo7b-7{#t4=! z7Mqutzo7p}uQ31$vkI|P0DM}%n8TWTrZobWC zGJKw6QeKo?NGC!J$h?KOmxU(d#+u&* zlQ`H*$Wu^Y@7TXFLU(i%Usr0ajo4iJ2~GLdK-X7!wOTmAy;wF~$6QStfq7t}to7hV z%9mmdK0coJ6H|qLAx*HR9gu3g0aABHW5-^lPjWoCBoM;fUFtg~GXk~dsrDDaJZazl zsj-SFs*baQP=`Y3_=@2i^%+idzp653z(G>2Da5GA~V~-{`3m7q)(ajPFxb@~;UR=IIPS zD5L9WXp(T{TNbtAgq`Dm>S{~2Wa%ryR_W+4eS0lpO~uCuq6)_Qd?#Cpt4;3Qm`5!A(;F0~d|KZr1H_-L^7I+VFQ&eSI1=sXRU zKIp)ZOL0Zs3X{9xqFpI%HkTrEMu z%iskhy43{X*`Ee+`?>Aa=h?v1gv~d>VDl5K{Yuu(Gn$g}y@zIJ|F{PXKWgqiug5+_ zh}8l02K^YD&_1j^47lsgNVJ@u^Rc}0epE9iVL$6|m-QEU|Im1ph=t0vCCO&Bxu7GFImmVm;!$6zWM&H5t4+6V8{D3wxx5-9gpO!mq%_`29 zVI}9JZR*~YkWa%alioMPTQSK$S-9?hEmmV1&v-nM!OS0hla6s^E+kfX^ z|L_S|w5k8DWv0u2BR2m)+y7c$%Z7)+rwRFXv@gY+W~NZ6h#XrY>gt-NkbOIJp{=cL zch}`!DE0cb<=^-35z{=`Ch@3p(`Mg=ju7#vxqT!N3?)9}#l+mVXy>#$ibcwMH)}^| z_3sB*Z}5wg_}{ZjF>MVzpQ(2*)y0-MM=c$F6Pxwk9}r^;I{RZ$siMp9&&%^1-Fc6K zsd?2$i9cN0nyx;)yRd0rYgpcLvRUuGh7``D{EImKpZ2~ptf^&fo4wVo8&qV|6cCgy zRho2Ax^$@$5`y&Jdr5RFDj*`+4qXX064-4-6~ley2^bUY3)K3#`0J5K>uET&yn;_wvpsq4P4d z#4^CSC!lT)i&VY!195R)1o;ukI6aTL@koH|fJdtj2(^}(nwlP9vO7CFaZq@&#CcEB z>H!%ZECUgG;{r4M*8!ioHpGR?*O?^T%%cvHDn6dh$t$_K+OSyQQ*LidP{|=C9rx*@C^6no!jGpK#`Mn_*)4#V&Tnuz&}8q z2YMWB&q@FGg|KSeGzKRe*g76;$JnDmz&p{f*=W^q*ECmG>zFP)?geF0L&{0(z?ny|@`DO*SLuMvV%53e|VB50yAXVx1paLAQ zNx4x;2|TBYd>mfBOFDZ}*ANCALh&stD(Xq&y-XU1I&mE9fY{{k@^ZbPAm+KL0_1!Y zQey_k#H~v_*}71ZIpow^PrUW~q!EyqZ<*@*6@s&~bI;FAC17~tZi3Xbc@nbv48*=o z>c1tnWXw&&JleJ~HxNdwO|}|w2iRK~Vf(&ki{T**6@&!})*b|IAIb1vCEhOA(KNp3 z=KJ#HOF$jLZoHUou|Yl((WuM3MI5S&o!wVeSapi>VUoF2u5+a81!ukx5>H}7XmT1t5h3j_+l7{ znONA(nApXIt$OeFIggvLi%U=Ncznv)=>jC~E@@9{lL}JOdy!18?)_9H6nJ&><(={r z#KlLOyL~#;Q=|nR^uah+^q1$9>{tFIzGV-zdg>O=A{TVn4kc^{ziuTqL*_^6;PK00 z9T*op9m;k6w$yGjqpN6QXCRp8%AeF++BD(H9_6HhERw|Ke--C_urYygov7tC&aXb2 zPjqM65c_dRu#A%RS^SY&vjKd23rKZunlEuZKe_oR=xnhk4cI=-!U5qSBE$ekbEKuE zO^|yB2bUk+2a=2on49MNIH|`ngBk`2WOczFkTMK0rDTwr4sxQ;6eX#Rj4a?buzE>p zR|e=1DJ}W~Q;NYfNLQa0{tk0&-+`Dlkbqsh_oTT@MkX6|hqTy%2QvR(d|(PE zncOv*ZbvkzcEi?0#gWvFfVVReG2jD%EYQV@=WoDBYd{)%do_ClrtVA_uJ6`t=e(Z*d>rP)FXO-+Ui+#RQpdzwAF=8+@{=lRzr`Ly@m=uF@qT1KI7} zB0>L1VDgy_RlY&Q>^outCZ&H;YPnVk@JBal-X5-J&}@pduBsq~ zeUdKS3Le0Lcqis?t?R-#xfp;^`J(tVmN#>o)P-D6ywTYO_(Ru)masM3u(Z3ENwYIR zDQIm}9zRpiZRWZzPxvIR{GOuDxqf^1bd<}|cmT)4*uu%h)zZ!c1R%-C79|rGaAvr^ ziQ|UivmJTn!%|>Zc=AD62CtD$#Ot}CkoYaVMG5U9nF==>XZun-Zm`1xUGvb}-^Ig= z-HWNkJuEGDCkNe?&7+YVqMAuh*W57OpRFYhhQ=rI(ccE{&xH=nE#Kz4tAfQk)XK=! zldRZtGttAwva>}-omEPLNGY+4;|#V?@LL;WM-x0wDMJldQ4Yt>t)iMYO(t8ru}=ulqzH)MXOao$t=->8aH{0|_ZH ze@(^M9WE~QRF&oo&PXQ%o|PC;Dc~d8)1yiiqIwL;ACnooq1m}Wu)3`^1zTHOE+Z}k zVov@D0UiHEWjb@bJ*t7;;1}r9dWTe>+Zt+W+USQpzZ8G~j1@7w#}JV(7nuM6zQtFAZ=W}wJ-uqk=gm|1<{7tgO#ODh=2pjc{pcwO4Kgu3 z=^T2E0>n|N_r%_%q^fSw%A`H!^Rk07BgWt5aG|QqbG74mXm4qIr(iJUNM06Vih2sd zP|=2$zV0ca&AWY8u*17Ut*>c*k=SfC1##@|xWronMz$@rM;2>C8(LcLw6waC-p#jQ z;EsCFyxtszJYm0mGPQ&q>Rn%>(P&!iU+U>PQB_fV+%%oP7WJWB5tK8)e!e=M`vS{p zQV!eIks@`HPT%FLV1x}BdadyInSFI`I#%5|AN=9V*Kc~!Dqjjk&3e0W+0U{ddbOoe z(ls}_1iGT4)&zuE@;W0T`6CL9v?ENuI0ol^ix!Ofg9QA~&0^&Dx!|zX7p@^hap^y0 zx|dz~?OQ1`b^wR8;aV_@=5UPA?81LK^zGB%;dMl`EoUud#R&|trf0=YFdkz&W>wq`}X}Rce1e=U< z;hnvimXw62U$qFc9ox5;okG$)c{AZ)v&55m{g#yDd9d|v2(+Lp;&q86X(H4lt*tqO ze)0QmWx{z+tB?J0LZ?lQnY_>HU`r9(N=`c>dTYxk%&6pnDa^)tKWGk;es0Cex#$G| zdqe){h97DH0L(@xmL->u0&z`*pd=pB|l3L-D&Z4Xkth#qiWvPs~=W>0V4X3&& z$rqVq11w101?or?_-M(X0hVxq2AQdor=9Zo zE-91p=Dz_p(%nxBPhu(ju1H2&o~?b>Ia~ff=T5c z7J$cxF97D5yIt`;#i4;(Uw^&nPrF!?Zt54XQAPx8LQ$Rf7(h>*9?VE+$5{C$g(vJ1 z>p$1wtP1dEYT1#QICyynkWe7F`46Bxs7i5+OFQY)BT^%>Ytv2PfmYs6{FwdhgygA+ znVU-${TzUGI9-4zWRzSWW_`kWuV_tB{i9*6Q{-I%d^1vo8-&KsEp@69!}4u?-9e=Kkd zBG&t4+=%PKZ$M9@;6YV{$$u~!21_#&(RR+T^$itRd9{RynLw0wryyZdqh~puNA=v~ zG|V5NaeKREgw6K{lVv1>`bs$;m<>7<}2e$FBA#q##( z?4sJDMC1&OD%XOsW3r(kQi>u3ztoU%AD;L zg}Aw(ku{zH0r>Q#3anZf4;NQ$m2M&kEPd8>6bF|y1|Q{QRft4?8D8!C+HQkw07B)-2y0D~d-92a4a$OUZJu`T~H7)U<^5Q`AS( zRL*mzr44v%<%zZn3tSqh^*vdwmJA6=@{&);#e^S-xm%^_nZ$kRiKj=>EpWD_NB1r^ zu8#T<^sHrL28^`{x%0?@YJw1 z`7igcrmN{aMJ3!rnV5Y{gytCWIyzz`M}8DX(-<1_@N`D_(BDkvb2QO2faKZDh<9H* z3p((LOg53Pl7qkqv?n5GfL*p#xF_%9aMa)b_DfOG++v5m=Z@`sT#I5PX(^bC6H#+I ze+v-l>Yz9@SN5bPE3_!9H9ofI^zgvd+Em$BtvZZ#X^>Kt_N) zy@r!QvOc$|D>Fm`8IR?mIj_^*DU7g2wwd(xLrq6pMO$N2Im5G03F#t^ViCi|D~^cf zAA@d-!YSoti5i!nO%@diD6ud1i5?id{h;55c zk8iCgh9)F3TIaK#>xMW@bfyODd!w3pio2Qkuf}E4iIr{F;{eGk>A6q$FY9nQQgqAi z^y|llPxV7(>=D%lNnr3vV*GSl2QG>Q?qHGn;3!t4UElZPpEo1B3Q%$;pk)8~Lw#t9 zq`njY-uasE&fQ5y+rYp?BQj=T6Xav{KGDZ9HpM0dI3iDUU;Tc(>|t#cRGs{pHm?(z zViGS4^*6qE&PUc;0meukqHyCPYM~-Ok#h^zTq`B0w>E!SX ztp_Q{=kM-Js@zPE5B=jB2{I4A1L(Y2_bQFc7$!x_uPNhwy0~R)W2CB$GK=T$Ben57 zirLrr;wD+h+>(AyU#8tT)w-Z^zMB3WmFWjiTp@C15MK4)JKBI}D^pVWds6<2jfstp z4+~NJWIuT0%Ju=PA=|cnWA;}$+FnugCfzxmGzsbJ^V>7J8^t1-!b-YyzP{ zU1kP#=#c!bpa22y-#vbzJd3{O`3D&pgTiB_M+QeJ8TZzRRmc{-#{&9%mHv^^ZT@JJ z`We2IxyTpWK1$&EMhb40jXK0G#~7Y8^6r1Vd8 zbCeTw%Qq<$v1mCFB_hJs<=jk%lT;#>{Gs^o2+7QNgqxSo(XN5`fRSlp3Jm7$WfSy@ zjWpJpyR|;+DTFPYZGrd!3;ahSD`1%(Jo68Lt;ZIzSmJZ~dIlVv^`shYz3%ygMX{9=y5)-K<`0Cl;CHQrsX5c`M=ogBZqKgP8-!jUg$>H7i6}Zhbad3J_tnkI zVf*82&IKLq4+;^0kIppo)nI_b-fKQ2y{RJ9!~Tn?l#sMI3S(dr^<@tcE1wf=>+TO& z3>hh*s*>H8Y^BY6a}{w86Qg__lF|$w zqC|a@`6_7v^gzgfhnC|pk(4-{-3|Fa!bzAZIkLWaEhADxq1yNL&z&t85evhsY;*v; z{ufk)_Zn}dVo@GX`R=h{)brn z53%^~h(%*9kyu2$iky*POVg*YbzmaJY!`(RG5C{o=&8@bFRmd#PY zpyuoyaEjrhUqC`-&z6^tw&NFC0P-s-M?{bxywi>ggabiBi+n8vzWI0Tcgfxbsv1D# zirt-TU^bt@JW)D=NvEKhj$TkB6$y#Q3;khh-m>^Sd6`++LubH?a7#4pqs_@wY1m$^ z@9?|l#gzFJ1(ql-6&)csI@weVn|-R35tnp`B~PASoIQ20HF{?7U+`ZI{ZG6iB)`75 zaqvgToPN$qw1ISSc{!3L`IO!2BMC&-nK;gG9$5&@n04{cZvz&w3Bq zy-42;4|cknWSVEak%TgX+XuLa#e8mS4c!pv&!EF~VLSbRJN2{(r|V9{ro&G8{0NK?ArBU*9nM!A;Q!>)5b2TaJ7Sw z0NLOjKJ4^tke{eYwzM7S&ew@cfj1F4&X$(;jt~`4C3%S&8F6NH1_&q%ks{^4-Op~n9@a@DXG;*Dr= zrJdr5h?p1WL3l^RNu2wkVFIMY8Y`LWw%+`UobuxI%&?81=9Q2S zUKgAkj`83-Cx@rO-9_mKnG3;yydM58)SnVm3~Pz1PXd#bez*Jfk^NRgvwpl65A%N2 z1vWjtfp6s(T;t{T0s_*ulvoEA_L7nXTrBo>z1G$aD)3XOR>D*i0i!HRBAq^c$B>cW z7dT;h*>Q8eXwBvZn2!1EB{-!eElD$sH#~kBWKCnc3G0sDWPz1-I32uVzY%=K@woYq zTU4<*fK077Z~#{wg^+P^9W+BQMDv|Ng_@ zm}k+UWa#>d1rpV5QPXKfilRTiCK?_dUh->UJD;FnxLT_2_l2Dyr+otc1lVxUnNiX! zQmdc#%KuwQ!lN~?Mk*QFCV{GTi zn*FuVQaJ1D8SNXcn;Pu;t$ys2D~Wd76}jhOGV>8CU~ods^Qc9C9ZsNQvQ@&v_m|6l z(wMatxbelpm+yRBp&cWtibMn?^SO!m8TjY+fAue}I$AHSDlkN39Qx8albnFTOEqI! zm6Zm6+wYkLnj}5x)2tAfY7lKiA%O<1ZEt(HPc4&}9hrK1plMJl)2z3;~O*q7|^(1jO}@U2i{ub!vq7{>u6f$!KV zq&_^~L{T4G4(f|i`rGDIjOzDGtDtsD-BywxHFzlu0aj*|=lI)heJk7l_NuSvV%@ra z(hFDrYr`Z)QAiO`mFVRS=!ztkhyS&H;UmuFemihaht4#3I@s)ua$1j_X6O32U04-~ zkEYu^+jThXPScPMqEQeaV>GGRz$fBdf{wXj$SZE2{qZ2+Y!|yWLX}cf&@dIAULj9x zM3=|oOk^JlCv?Ba0zM4K&562P_@ZZh?b<1gHapca-T=0hsa7z;&bqHF;o|)Js|$*9 zyy7IbrCwL>cN%#*lan`bx`**SeG2GxaB|A6$qHKBvrc)OWR#|oCNwnmSE}D2X1i-D zoZa|=n#!;1HsMcp%%4sdg5xhxZr#-7-|2u8LBxP|A++vn}P? zO1$A<;yPXn0~)oLn)md2)yPP22s-s?w7rU%+Ef;|G3E?^thd$w@{~6=EK^B~Lu8be zD8D)>VCpkx=9S@`#lnh)X7b}|*2kISu-tTSS`1GPlvp`fdshSHnaImmjl$7GbCeR- z`a%s<*lE{}6pzum?2`_q;#H?po2KX{*$mbf#Y_3R+&~^^O&iX=3p%E(>G@#QOKII` zcMxFqQEp{N^$+{jFBXsB6R+N0xl0P~n9Bmnjw-@A@JG^%GH9s?nHXQDWQb_0ZnZl& zltHGN*DdUoGx<2SWM-?Wu*bgRY^<~?c~(3kz{(nL#djBu8GJU7d*3%GTit_uPb;PG z^m!9?`_05a)`coz+X*{S$2Qd7^@;#^k?}iDHgVEKFT76OeZlWti;DWv$BC`B%S)vK zQSDRiCjr7WT~JxR(l!}~Z+Y+3@E91~jwz10#Y)WjSk4hvSerh?&FbSSF}0+T(aRZ* z{cv3pR)wo&C?>(cCfQy3=ltrFd;7tCyM)7+-+6BB=2*~BrD5&k-1wre4{8BDRua8y zVG{q4U?F2)BE{bXX*?bO)WfpuEoFqHv3~vQI`dNKu2e23)njGFt(q=o>2rwERlmB& zHIX~9m3ALYRY!MAi!PQut;dfiR=cSI=4-AL)&!#So#gM!G}CbvMX5C&SEGvinzV9J zsQendT$#Mg2Te|kM^Ps)?J|aHQx)3nifk>;)+v;xTdibPeNT?pUYK!({KW4o6=Y7) zJycgQ3u=dy@}@SWfeL@Ttv!ojw+wyvwiQb+y`g?LT6rY0lER zY4*^AP7(R$!}ycbw=Lh?CyZPrwox3*l1{~vr;7XIe?5IJbxwHL?C9v}?Jt?fnV6Z; zMJuGf?_MY#TE(9n058>~G>3k%h>%Bw2{ z;H=Q{U8ii%4odR})-G_sXOO;lH;8U;#GW)2>-J|byuS`UQmHBvoL@`3OI#pi(oTU2 z`3k9S8{;)j4@{GOzfxyuFUnA3hOzBt&O`pGPAXLD5Ey_vSRd0jM7*Ln+(O3185-93 zZZK<+W=7qBv(ESQY#y!OH^)dBllITF11hJ=+c{MiFi|T5 zsi~xvdv>QvY(aGgsjs1=>y^W>a-b63=^Impr@z;)dRZyqvV z1qn2po?FxBdaEN21Aem(ufV3l^4lyd*T3Kd^IQ&m#01iCY{V@$Z%tQgOMBMFWxv3QeGXg7=FFAA_ZyY>>Wn9@BXeBt znTcmZe^FF7B;G7=QoSW!JSqW~tmQJlL%}j8>{|`&wG_^ms9sO2f*23g)8gN_xP;qB#t; z&Q4uo-_F|nCKx2@^QCG<7porL6$P#p{V4A@ZO}s4cH$9o+l$rE$n2_i>Ur$4-X6s! zepOYfa?E?KqGF5DxIiTzi8(s{f2ZIJyPM^|}l(L}E4M}}G-4i6K_opwb-dB0x+ zPAX>|UyiQgd<|mnipiAveR2wdp5IK~%HQ>lGRR}kBlma>mn}YaM;eBP=LU++ z2bdG)5$)n1ER)By&~wX9)5mi%6rCT zuE=NMMFM$pmwCSH(xP3_t?i1uq?9;IzheTo1L$XvsiLBIHI=Rv*TzUkS`lBMrE zV%{xI%iq6ix{s1RD=KFxdB3)}y7A_91SqbkTXRwa+92Y5FSlQX~K6=6i{ltQxp*; z6ezn}wbB%ay*#3W26=6NJmzg0?e7r|xa$+_1zjLu(_dFo+iFiRGcmk-mH*ZrgZFdP zxbV~6ucLd(JKZoOMTfkOZ7%6@5P~L8e;$f&9cYY6E?%Rpxpz2vN67R+O z9*-kNM<+ulViTF|cO)z3Ro(z6M_3n{X`_SW1lu+ZR#x_2bJgZgCxI&rRvR?>=7+Oc z4mJ(M(Dar92Rn{P1_y@ZAoA9SB6VQ--v8PB!_w5ZGN6knM8cOf2Q) zrL5vKPxU>5dh!PxXqUn=HtHiwyDr2yzD8QAOv{p-LtMl|W0k9_W{jB3HkOQb`Zs5% zkq0@&OrHH+!9w>_!`?2^a%9>V%Xj2x_&0sZ<+q5<&e*5h=|`{l8f9!IV1m~KQ*^Re zSu4pk8rr!c5#5}e3;s?dT&tsIn;oW-(rDir9(ti8$C7B;k+q}GhRYup8Z>pHWtD0# zLIzV@h_g%+anSY%7nv!`C|0k~R_YKe9rl1iD||%IA7hiG#JxQ|9`&rC3Wo*6Wo1wW z(oc;Ii#|8|6gmX>1u@ot343i+r`@O<{8a#MYwl)Yh5)KPkhq_<3U?27HZjx> zblh8=RsU{i#?c3bhL534IzqqhSc+3q1QfT{S(5x>GStlmT3O<6&YITAPcN_VRF*)Nd!31>YPOv#ML?<)QnA z%x^rIJLz)`weUPUZafargKc`16=(R-czgKClob>Qbl%P%b}d+Cxs@#4e}|Q#fgUDa zT{#t=S(V6)-keKNmAfm|E~M)G8E(GX)OF$7+Isravpvdg{!lglJiMW=Y0V=mh%5-3 zx?kG(22TM9zOq#Rw)pX7w$v39s(S^nT6CX@&QI!rO0scxUzx#K+1R|!xQNo!(z+IL zu9`DLxq~r{$QeW+55NSAa$}~>k8G0tYm)Y_h}(afH$v@k>*{X_e@ve9kGj6kBgh)y z9I&d=x1rVTZDD8f)0==hUx1^>oYN=s@KUbXUI(TmTsvihTZGvmMfC9=4u9 Ybx)dZg~mh#5i=euYb)g|nE&~|02KBoO#lD@ literal 0 HcmV?d00001 diff --git a/object_detection/g3doc/installation.md b/object_detection/g3doc/installation.md new file mode 100644 index 000000000..833f5fc24 --- /dev/null +++ b/object_detection/g3doc/installation.md @@ -0,0 +1,79 @@ +# Installation + +## Dependencies + +Tensorflow Object Detection API depends on the following libraries: + +* Protobuf 2.6 +* Pillow 1.0 +* lxml +* tf Slim (which is included in the "tensorflow/models" checkout) +* Jupyter notebook +* Matplotlib +* Tensorflow + +For detailed steps to install Tensorflow, follow the +[Tensorflow installation instructions](https://www.tensorflow.org/install/). +A typically user can install Tensorflow using one of the following commands: + +``` bash +# For CPU +pip install tensorflow +# For GPU +pip install tensorflow-gpu +``` + +The remaining libraries can be installed on Ubuntu 16.04 using via apt-get: + +``` bash +sudo apt-get install protobuf-compiler python-pil python-lxml +sudo pip install jupyter +sudo pip install matplotlib +``` + +Alternatively, users can install dependencies using pip: + +``` bash +sudo pip install pillow +sudo pip install lxml +sudo pip install jupyter +sudo pip install matplotlib +``` + +## Protobuf Compilation + +The Tensorflow Object Detection API uses Protobufs to configure model and +training parameters. Before the framework can be used, the Protobuf libraries +must be compiled. This should be done by running the following command from +the tensorflow/models directory: + + +``` bash +# From tensorflow/models/ +protoc object_detection/protos/*.proto --python_out=. +``` + +## Add Libraries to PYTHONPATH + +When running locally, the tensorflow/models/ and slim directories should be +appended to PYTHONPATH. This can be done by running the following from +tensorflow/models/: + + +``` bash +# From tensorflow/models/ +export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim +``` + +Note: This command needs to run from every new terminal you start. If you wish +to avoid running this manually, you can add it as a new line to the end of your +~/.bashrc file. + +# Testing the Installation + +You can test that you have correctly installed the Tensorflow Object Detection\ +API by running the following command: + +``` bash +python object_detection/builders/model_builder_test.py +``` diff --git a/object_detection/g3doc/preparing_inputs.md b/object_detection/g3doc/preparing_inputs.md new file mode 100644 index 000000000..a1f8f17e1 --- /dev/null +++ b/object_detection/g3doc/preparing_inputs.md @@ -0,0 +1,45 @@ +# Preparing Inputs + +Tensorflow Object Detection API reads data using the TFRecord file format. Two +sample scripts (`create_pascal_tf_record.py` and `create_pet_tf_record.py`) are +provided to convert from the PASCAL VOC dataset and Oxford-IIT Pet dataset to +TFRecords. + +## Generating the PASCAL VOC TFRecord files. + +The raw 2012 PASCAL VOC data set can be downloaded +[here](http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar). +Extract the tar file and run the `create_pascal_tf_record` script: + +``` +# From tensorflow/models/object_detection +tar -xvf VOCtrainval_11-May-2012.tar +./create_pascal_tf_record --data_dir=VOCdevkit \ + --year=VOC2012 --set=train --output_path=pascal_train.record +./create_pascal_tf_record --data_dir=/home/user/VOCdevkit \ + --year=VOC2012 --set=val --output_path=pascal_val.record +``` + +You should end up with two TFRecord files named pascal_train.record and +pascal_val.record in the tensorflow/models/object_detection directory. + +The label map for the PASCAL VOC data set can be found at +data/pascal_label_map.pbtxt. + +## Generation the Oxford-IIT Pet TFRecord files. + +The Oxford-IIT Pet data set can be downloaded from +[their website](http://www.robots.ox.ac.uk/~vgg/data/pets/). Extract the tar +file and run the `create_pet_tf_record` script to generate TFRecords. + +``` +# From tensorflow/models/object_detection +tar -xvf annotations.tar.gz +tar -xvf images.tar.gz +./create_pet_tf_record --data_dir=`pwd` --output_dir=`pwd` +``` + +You should end up with two TFRecord files named pet_train.record and +pet_val.record in the tensorflow/models/object_detection directory. + +The label map for the Pet dataset can be found at data/pet_label_map.pbtxt. diff --git a/object_detection/g3doc/running_locally.md b/object_detection/g3doc/running_locally.md new file mode 100644 index 000000000..7143b6d85 --- /dev/null +++ b/object_detection/g3doc/running_locally.md @@ -0,0 +1,81 @@ +# Running Locally + +This page walks through the steps required to train an object detection model +on a local machine. It assumes the reader has completed the +following prerequisites: + +1. The Tensorflow Object Detection API has been installed as documented in the +[installation instructions](installation.md). This includes installing library +dependencies, compiling the configuration protobufs and setting up the Python +environment. +2. A valid data set has been created. See [this page](preparing_inputs.md) for +instructions on how to generate a dataset for the PASCAL VOC challenge or the +Oxford-IIT Pet dataset. +3. A Object Detection pipeline configuration has been written. See +[this page](configuring_jobs.md) for details on how to write a pipeline configuration. + +## Recommended Directory Structure for Training and Evaluation + +``` ++data + -label_map file + -train TFRecord file + -eval TFRecord file ++models + + model + -pipeline config file + +train + +eval +``` + +## Running the Training Job + +A local training job can be run with the following command: + +```bash +# From the tensorflow/models/ directory +python object_detection/train.py \ + --logtostderr \ + --pipeline_config_path=${PATH_TO_YOUR_PIPELINE_CONFIG} \ + --train_dir=${PATH_TO_TRAIN_DIR} +``` + +where `${PATH_TO_YOUR_PIPELINE_CONFIG}` points to the pipeline config and +`${PATH_TO_TRAIN_DIR}` points to the directory in which training checkpoints +and events will be written to. By default, the training job will +run indefinitely until the user kills it. + +## Running the Evaluation Job + +Evaluation is run as a separate job. The eval job will periodically poll the +train directory for new checkpoints and evaluate them on a test dataset. The +job can be run using the following command: + +```bash +# From the tensorflow/models/ directory +python object_detection/eval.py \ + --logtostderr \ + --pipeline_config_path=${PATH_TO_YOUR_PIPELINE_CONFIG} \ + --checkpoint_dir=${PATH_TO_TRAIN_DIR} \ + --eval_dir=${PATH_TO_EVAL_DIR} +``` + +where `${PATH_TO_YOUR_PIPELINE_CONFIG}` points to the pipeline config, +`${PATH_TO_TRAIN_DIR}` points to the directory in which training checkpoints +were saved (same as the training job) and `${PATH_TO_EVAL_DIR}` points to the +directory in which evaluation events will be saved. As with the training job, +the eval job run until terminated by default. + +## Running Tensorboard + +Progress for training and eval jobs can be inspected using Tensorboard. If +using the recommended directory structure, Tensorboard can be run using the +following command: + +```bash +tensorboard --logdir=${PATH_TO_MODEL_DIRECTORY} +``` + +where `${PATH_TO_MODEL_DIRECTORY}` points to the directory that contains the +train and eval directories. Please note it make take Tensorboard a couple +minutes to populate with data. diff --git a/object_detection/g3doc/running_notebook.md b/object_detection/g3doc/running_notebook.md new file mode 100644 index 000000000..8d7948d82 --- /dev/null +++ b/object_detection/g3doc/running_notebook.md @@ -0,0 +1,15 @@ +# Quick Start: Jupyter notebook for off-the-shelf inference + +If you'd like to hit the ground running and run detection on a few example +images right out of the box, we recommend trying out the Jupyter notebook demo. +To run the Jupyter notebook, run the following command from +`tensorflow/models/object_detection`: + +``` +# From tensorflow/models/object_detection +jupyter notebook +``` + +The notebook should open in your favorite web browser. Click the +[`object_detection_tutorial.ipynb`](../object_detection_tutorial.ipynb) link +to open the demo. diff --git a/object_detection/g3doc/running_on_cloud.md b/object_detection/g3doc/running_on_cloud.md new file mode 100644 index 000000000..0d74ac4e2 --- /dev/null +++ b/object_detection/g3doc/running_on_cloud.md @@ -0,0 +1,128 @@ +# Running on Google Cloud Platform + +The Tensorflow Object Detection API supports distributed training on Google +Cloud ML Engine. This section documents instructions on how to train and +evaluate your model using Cloud ML. The reader should complete the following +prerequistes: + +1. The reader has created and configured a project on Google Cloud Platform. +See [the Cloud ML quick start guide](https://cloud.google.com/ml-engine/docs/quickstarts/command-line). +2. The reader has installed the Tensorflow Object Detection API as documented +in the [installation instructions](installation.md). +3. The reader has a valid data set and stored it in a Google Cloud Storage +bucket. See [this page](preparing_inputs.md) for instructions on how to generate +a dataset for the PASCAL VOC challenge or the Oxford-IIT Pet dataset. +4. The reader has configured a valid Object Detection pipeline, and stored it +in a Google Cloud Storage bucket. See [this page](configuring_jobs.md) for +details on how to write a pipeline configuration. + +Additionally, it is recommended users test their job by running training and +evaluation jobs for a few iterations +[locally on their own machines](running_locally.md). + +## Packaging + +In order to run the Tensorflow Object Detection API on Cloud ML, it must be +packaged (along with it's TF-Slim dependency). The required packages can be +created with the following command + +``` bash +# From tensorflow/models/ +python setup.py sdist +(cd slim && python setup.py sdist) +``` + +This will create python packages in dist/object_detection-0.1.tar.gz and +slim/dist/slim-0.1.tar.gz. + +## Running a Multiworker Training Job + +Google Cloud ML requires a YAML configuration file for a multiworker training +job using GPUs. A sample YAML file is given below: + +``` +trainingInput: + runtimeVersion: "1.0" + scaleTier: CUSTOM + masterType: standard_gpu + workerCount: 9 + workerType: standard_gpu + parameterServerCount: 3 + parameterServerType: standard + + +``` + +Please keep the following guidelines in mind when writing the YAML +configuration: + +* A job with n workers will have n + 1 training machines (n workers + 1 master). +* The number of parameters servers used should be an odd number to prevent + a parameter server from storing only weight variables or only bias variables + (due to round robin parameter scheduling). +* The learning rate in the training config should be decreased when using a + larger number of workers. Some experimentation is required to find the + optimal learning rate. + +The YAML file should be saved on the local machine (not on GCP). Once it has +been written, a user can start a training job on Cloud ML Engine using the +following command: + +``` bash +# From tensorflow/models/ +gcloud ml-engine jobs submit training object_detection_`date +%s` \ + --job-dir=gs://${TRAIN_DIR} \ + --packages dist/object_detection-0.1.tar.gz,slim/dist/slim-0.1.tar.gz \ + --module-name object_detection.train \ + --region us-central1 \ + --config ${PATH_TO_LOCAL_YAML_FILE} \ + -- \ + --train_dir=gs://${TRAIN_DIR} \ + --pipeline_config_path=gs://${PIPELINE_CONFIG_PATH} +``` + +Where `${PATH_TO_LOCAL_YAML_FILE}` is the local path to the YAML configuration, +`gs://${TRAIN_DIR}` specifies the directory on Google Cloud Storage where the +training checkpoints and events will be written to and +`gs://${PIPELINE_CONFIG_PATH}` points to the pipeline configuration stored on +Google Cloud Storage. + +Users can monitor the progress of their training job on the [ML Engine +Dasboard](https://pantheon.corp.google.com/mlengine/jobs). + +## Running an Evaluation Job on Cloud + +Evaluation jobs run on a single machine, so it is not necessary to write a YAML +configuration for evaluation. Run the following command to start the evaluation +job: + +``` bash +gcloud ml-engine jobs submit training object_detection_eval_`date +%s` \ + --job-dir=gs://${TRAIN_DIR} \ + --packages dist/object_detection-0.1.tar.gz,slim/dist/slim-0.1.tar.gz \ + --module-name object_detection.eval \ + --region us-central1 \ + --scale-tier BASIC_GPU \ + -- \ + --checkpoint_dir=gs://${TRAIN_DIR} \ + --eval_dir=gs://${EVAL_DIR} \ + --pipeline_config_path=gs://${PIPELINE_CONFIG_PATH} +``` + +Where `gs://${TRAIN_DIR}` points to the directory on Google Cloud Storage where +training checkpoints are saved (same as the training job), `gs://${EVAL_DIR}` +points to where evaluation events will be saved on Google Cloud Storage and +`gs://${PIPELINE_CONFIG_PATH}` points to where the pipeline configuration is +stored on Google Cloud Storage. + +## Running Tensorboard + +You can run Tensorboard locally on your own machine to view progress of your +training and eval jobs on Google Cloud ML. Run the following command to start +Tensorboard: + +``` bash +tensorboard --logdir=gs://${YOUR_CLOUD_BUCKET} +``` + +Note it may Tensorboard a few minutes to populate with results. diff --git a/object_detection/g3doc/running_pets.md b/object_detection/g3doc/running_pets.md new file mode 100644 index 000000000..aadea479e --- /dev/null +++ b/object_detection/g3doc/running_pets.md @@ -0,0 +1,303 @@ +# Quick Start: Distributed Training on the Oxford-IIT Pets Dataset on Google Cloud + +This page is a walkthrough for training an object detector using the Tensorflow +Object Detection API. In this tutorial, we'll be training on the Oxford-IIT Pets +dataset to build a system to detect various breeds of cats and dogs. The output +of the detector will look like the following: + +![](img/oxford_pet.png) + +## Setting up a Project on Google Cloud + +To accelerate the process, we'll run training and evaluation on [Google Cloud +ML Engine](https://cloud.google.com/ml-engine/) to leverage multiple GPUs. To +begin, you will have to set up Google Cloud via the following steps (if you have +already done this, feel free to skip to the next section): + +1. [Create a GCP project](https://cloud.google.com/resource-manager/docs/creating-managing-projects). +2. [Install the Google Cloud SDK](https://cloud.google.com/sdk/downloads) on +your workstation or laptop. +This will provide the tools you need to upload files to Google Cloud Storage and +start ML training jobs. +3. [Enable the ML Engine +APIs](https://console.cloud.google.com/flows/enableapi?apiid=ml.googleapis.com,compute_component&_ga=1.73374291.1570145678.1496689256). +By default, a new GCP project does not enable APIs to start ML Engine training +jobs. Use the above link to explicitly enable them. +4. [Set up a Google Cloud Storage (GCS) +bucket](https://cloud.google.com/storage/docs/creating-buckets). ML Engine +training jobs can only access files on a Google Cloud Storage bucket. In this +tutorial, we'll be required to upload our dataset and configuration to GCS. + +Please remember the name of your GCS bucket, as we will reference it multiple +times in this document. Substitute `${YOUR_GCS_BUCKET}` with the name of +your bucket in this document. For your convenience, you should define the +environment variable below: + +``` bash +export YOUR_GCS_BUCKET=${YOUR_GCS_BUCKET} +``` + +## Installing Tensorflow and the Tensorflow Object Detection API + +Please run through the [installation instructions](installation.md) to install +Tensorflow and all it dependencies. Ensure the Protobuf libraries are +compiled and the library directories are added to `PYTHONPATH`. + +## Getting the Oxford-IIT Pets Dataset and Uploading it to Google Cloud Storage + +In order to train a detector, we require a dataset of images, bounding boxes and +classifications. For this demo, we'll use the Oxford-IIT Pets dataset. The raw +dataset for Oxford-IIT Pets lives +[here](http://www.robots.ox.ac.uk/~vgg/data/pets/). You will need to download +both the image dataset [`images.tar.gz`](http://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz) +and the groundtruth data [`annotations.tar.gz`](http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz) +to the tensorflow/models directory. This may take some time. After downloading +the tarballs, your object_detection directory should appear as follows: + +```lang-none ++ object_detection/ + + data/ + - images.tar.gz + - annotations.tar.gz + - create_pet_tf_record.py + ... other files and directories +``` + +The Tensorflow Object Detection API expects data to be in the TFRecord format, +so we'll now run the _create_pet_tf_record_ script to convert from the raw +Oxford-IIT Pet dataset into TFRecords. Run the following commands from the +object_detection directory: + +``` bash +# From tensorflow/models/ +wget http://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz +wget http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz +tar -xvf annotations.tar.gz +tar -xvf images.tar.gz +python object_detection/create_pet_tf_record.py \ + --label_map_path=object_detection/data/pet_label_map.pbtxt \ + --data_dir=`pwd` \ + --output_dir=`pwd` +``` + +Note: It is normal to see some warnings when running this script. You may ignore +them. + +Two TFRecord files named pet_train.record and pet_val.record should be generated +in the object_detection/ directory. + +Now that the data has been generated, we'll need to upload it to Google Cloud +Storage so the data can be accessed by ML Engine. Run the following command to +copy the files into your GCS bucket (substituting ${YOUR_GCS_BUCKET}): + +``` bash +# From tensorflow/models/ +gsutil cp pet_train.record gs://${YOUR_GCS_BUCKET}/data/pet_train.record +gsutil cp pet_val.record gs://${YOUR_GCS_BUCKET}/data/pet_val.record +gsutil cp object_detection/data/pet_label_map.pbtxt gs://${YOUR_GCS_BUCKET}/data/pet_label_map.pbtxt +``` + +Please remember the path where you upload the data to, as we will need this +information when configuring the pipeline in a following step. + +## Downloading a COCO-pretrained Model for Transfer Learning + +Training a state of the art object detector from scratch can take days, even +when using multiple GPUs! In order to speed up training, we'll take an object +detector trained on a different dataset (COCO), and reuse some of it's +parameters to initialize our new model. + +Download our [COCO-pretrained Faster R-CNN with Resnet-101 +model](http://storage.googleapis.com/download.tensorflow.org/models/object_detection/faster_rcnn_resnet101_coco_11_06_2017.tar.gz). +Unzip the contents of the folder and copy the model.ckpt* files into your GCS +Bucket. + +``` bash +wget http://storage.googleapis.com/download.tensorflow.org/models/object_detection/faster_rcnn_resnet101_coco_11_06_2017.tar.gz +tar -xvf faster_rcnn_resnet101_coco_11_06_2017.tar.gz +gsutil cp faster_rcnn_resnet101_coco_11_06_2017/model.ckpt.* gs://${YOUR_GCS_BUCKET}/data/ +``` + +Remember the path where you uploaded the model checkpoint to, as we will need it +in the following step. + +## Configuring the Object Detection Pipeline + +In the Tensorflow Object Detection API, the model parameters, training +parameters and eval parameters are all defined by a config file. More details +can be found [here](configuring_jobs.md). For this tutorial, we will use some +predefined templates provided with the source code. In the +object_detection/samples/configs folder, there are skeleton object_detection +configuration files. We will use `faster_rcnn_resnet101_pets.config` as a +starting point for configuring the pipeline. Open the file with your favourite +text editor. + +We'll need to configure some paths in order for the template to work. Search the +file for instances of `PATH_TO_BE_CONFIGURED` and replace them with the +appropriate value (typically "gs://${YOUR_GCS_BUCKET}/data/"). Afterwards +upload your edited file onto GCS, making note of the path it was uploaded to +(we'll need it when starting the training/eval jobs). + +``` bash +# From tensorflow/models/ + +# Edit the faster_rcnn_resnet101_pets.config template. Please note that there +# are multiple places where PATH_TO_BE_CONFIGURED needs to be set. +sed -i "s|PATH_TO_BE_CONFIGURED|"gs://${YOUR_GCS_BUCKET}"/data|g" \ + object_detection/samples/configs/faster_rcnn_resnet101_pets.config + +# Copy editted template to cloud. +gsutil cp object_detection/samples/configs/faster_rcnn_resnet101_pets.config \ + gs://${YOUR_GCS_BUCKET}/data/faster_rcnn_resnet101_pets.config +``` + +## Checking Your Google Cloud Storage Bucket + +At this point in the tutorial, you should have uploaded the training/validation +datasets (including label map), our COCO trained FasterRCNN finetune checkpoint and your job +configuration to your Google Cloud Storage Bucket. Your bucket should look like +the following: + +```lang-none ++ ${YOUR_GCS_BUCKET}/ + + data/ + - faster_rcnn_resnet101_pets.config + - model.ckpt.index + - model.ckpt.meta + - model.ckpt.data-00000-of-00001 + - pet_label_map.pbtxt + - pet_train.record + - pet_val.record +``` + +You can inspect your bucket using the [Google Cloud Storage +browser](pantheon.corp.google.com/storage). + +## Starting Training and Evaluation Jobs on Google Cloud ML Engine + +Before we can start a job on Google Cloud ML Engine, we must: + +1. Package the Tensorflow Object Detection code. +2. Write a cluster configuration for our Google Cloud ML job. + +To package the Tensorflow Object Detection code, run the following commands from +the tensorflow/models/ directory: + +``` bash +# From tensorflow/models/ +python setup.py sdist +(cd slim && python setup.py sdist) +``` + +You should see two tar.gz files created at `dist/object_detection-0.1.tar.gz` +and `slim/dist/slim-0.1.tar.gz`. + +For running the training Cloud ML job, we'll configure the cluster to use 10 +training jobs (1 master + 9 workers) and three parameters servers. The +configuration file can be found at object_detection/samples/cloud/cloud.yml. + +To start training, execute the following command from the tensorflow/models/ +directory: + +``` bash +# From tensorflow/models/ +gcloud ml-engine jobs submit training `whoami`_object_detection_`date +%s` \ + --job-dir=gs://${YOUR_GCS_BUCKET}/train \ + --packages dist/object_detection-0.1.tar.gz,slim/dist/slim-0.1.tar.gz \ + --module-name object_detection.train \ + --region us-central1 \ + --config object_detection/samples/cloud/cloud.yml \ + -- \ + --train_dir=gs://${YOUR_GCS_BUCKET}/train \ + --pipeline_config_path=gs://${YOUR_GCS_BUCKET}/data/faster_rcnn_resnet101_pets.config +``` + +Once training has started, we can run an evaluation concurrently: + +``` bash +# From tensorflow/models/ +gcloud ml-engine jobs submit training `whoami`_object_detection_eval_`date +%s` \ + --job-dir=gs://${YOUR_GCS_BUCKET}/train \ + --packages dist/object_detection-0.1.tar.gz,slim/dist/slim-0.1.tar.gz \ + --module-name object_detection.eval \ + --region us-central1 \ + --scale-tier BASIC_GPU \ + -- \ + --checkpoint_dir=gs://${YOUR_GCS_BUCKET}/train \ + --eval_dir=gs://${YOUR_GCS_BUCKET}/eval \ + --pipeline_config_path=gs://${YOUR_GCS_BUCKET}/data/faster_rcnn_resnet101_pets.config +``` + +Note: Even though we're running an evaluation job, the `gcloud ml-engine jobs +submit training` command is correct. ML Engine does not distinguish between +training and evaluation jobs. + +Users can monitor and stop training and evaluation jobs on the [ML Engine +Dasboard](https://pantheon.corp.google.com/mlengine/jobs). + +## Monitoring Progress with Tensorboard + +You can monitor progress of the training and eval jobs by running Tensorboard on +your local machine: + +``` bash +# This command needs to be run once to allow your local machine to access your +# GCS bucket. +gcloud auth application-default login + +tensorboard --logdir=gs://${YOUR_GCS_BUCKET} +``` + +Once Tensorboard is running, navigate to `localhost:6006` from your favourite +web browser. You should something similar see the following: + +![](img/tensorboard.png) + +You will also want to click on the images tab to see example detections made by +the model while it trains. After about an hour and a half of training, you can +expect to see something like this: + +![](img/tensorboard2.png) + +Note: It takes roughly 10 minutes for a job to get started on ML Engine, and +roughly an hour for the system to evaluate the validation dataset. It may take +some time to populate the dashboards. If you do not see any entries after half +an hour, check the logs from the [ML Engine +Dasboard](https://pantheon.corp.google.com/mlengine/jobs). + +## Exporting the Tensorflow Graph + +After your model has been trained, you should export it to a Tensorflow +graph proto. First, you need to identify a candidate checkpoint to export. You +can search your bucket using the [Google Cloud Storage +Browser](https://pantheon.corp.google.com/storage/browser). The file should be +stored under ${YOUR_GCS_BUCKET}/train. The 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/object_detection: + +``` bash +# From tensorflow/models +gsutil cp gs://${YOUR_GCS_BUCKET}/train/model.ckpt-${CHECKPOINT_NUMBER}.* . +python object_detection/export_inference_graph \ + --input_type image_tensor \ + --pipeline_config_path object_detection/samples/configs/faster_rcnn_resnet101_pets.config \ + --checkpoint_path model.ckpt-${CHECKPOINT_NUMBER} \ + --inference_graph_path output_inference_graph.pb +``` + +Afterwards, you should see a graph named output_inference_graph.pb. + +## What's Next + +Congratulations, you have now trained an object detector for various cats and +dogs! There different things you can do now: + +1. [Test your exported model using the provided Jupyter notebook.](running_notebook.md) +2. [Experiment with different model configurations.](configuring_jobs.md) +3. Train an object detector using your own data. diff --git a/object_detection/matchers/BUILD b/object_detection/matchers/BUILD new file mode 100644 index 000000000..1bc5992f5 --- /dev/null +++ b/object_detection/matchers/BUILD @@ -0,0 +1,51 @@ +# Tensorflow Object Detection API: Matcher implementations. + +package( + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) + +# Apache 2.0 +py_library( + name = "argmax_matcher", + srcs = [ + "argmax_matcher.py", + ], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/core:matcher", + ], +) + +py_test( + name = "argmax_matcher_test", + srcs = ["argmax_matcher_test.py"], + deps = [ + ":argmax_matcher", + "//tensorflow", + ], +) + +py_library( + name = "bipartite_matcher", + srcs = [ + "bipartite_matcher.py", + ], + deps = [ + "//tensorflow", + "//tensorflow/contrib/image:image_py", + "//tensorflow_models/object_detection/core:matcher", + ], +) + +py_test( + name = "bipartite_matcher_test", + srcs = [ + "bipartite_matcher_test.py", + ], + deps = [ + ":bipartite_matcher", + "//tensorflow", + ], +) diff --git a/object_detection/matchers/__init__.py b/object_detection/matchers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/object_detection/matchers/argmax_matcher.py b/object_detection/matchers/argmax_matcher.py new file mode 100644 index 000000000..97d851858 --- /dev/null +++ b/object_detection/matchers/argmax_matcher.py @@ -0,0 +1,189 @@ +# 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. +# ============================================================================== + +"""Argmax matcher implementation. + +This class takes a similarity matrix and matches columns to rows based on the +maximum value per column. One can specify matched_thresholds and +to prevent columns from matching to rows (generally resulting in a negative +training example) and unmatched_theshold to ignore the match (generally +resulting in neither a positive or negative training example). + +This matcher is used in Fast(er)-RCNN. + +Note: matchers are used in TargetAssigners. There is a create_target_assigner +factory function for popular implementations. +""" + +import tensorflow as tf + +from object_detection.core import matcher + + +class ArgMaxMatcher(matcher.Matcher): + """Matcher based on highest value. + + This class computes matches from a similarity matrix. Each column is matched + to a single row. + + To support object detection target assignment this class enables setting both + matched_threshold (upper threshold) and unmatched_threshold (lower thresholds) + defining three categories of similarity which define whether examples are + positive, negative, or ignored: + (1) similarity >= matched_threshold: Highest similarity. Matched/Positive! + (2) matched_threshold > similarity >= unmatched_threshold: Medium similarity. + Depending on negatives_lower_than_unmatched, this is either + Unmatched/Negative OR Ignore. + (3) unmatched_threshold > similarity: Lowest similarity. Depending on flag + negatives_lower_than_unmatched, either Unmatched/Negative OR Ignore. + For ignored matches this class sets the values in the Match object to -2. + """ + + def __init__(self, + matched_threshold, + unmatched_threshold=None, + negatives_lower_than_unmatched=True, + force_match_for_each_row=False): + """Construct ArgMaxMatcher. + + Args: + matched_threshold: Threshold for positive matches. Positive if + sim >= matched_threshold, where sim is the maximum value of the + similarity matrix for a given column. Set to None for no threshold. + unmatched_threshold: Threshold for negative matches. Negative if + sim < unmatched_threshold. Defaults to matched_threshold + when set to None. + negatives_lower_than_unmatched: Boolean which defaults to True. If True + then negative matches are the ones below the unmatched_threshold, + whereas ignored matches are in between the matched and umatched + threshold. If False, then negative matches are in between the matched + and unmatched threshold, and everything lower than unmatched is ignored. + force_match_for_each_row: If True, ensures that each row is matched to + at least one column (which is not guaranteed otherwise if the + matched_threshold is high). Defaults to False. See + argmax_matcher_test.testMatcherForceMatch() for an example. + + Raises: + ValueError: if unmatched_threshold is set but matched_threshold is not set + or if unmatched_threshold > matched_threshold. + """ + if (matched_threshold is None) and (unmatched_threshold is not None): + raise ValueError('Need to also define matched_threshold when' + 'unmatched_threshold is defined') + self._matched_threshold = matched_threshold + if unmatched_threshold is None: + self._unmatched_threshold = matched_threshold + else: + if unmatched_threshold > matched_threshold: + raise ValueError('unmatched_threshold needs to be smaller or equal' + 'to matched_threshold') + self._unmatched_threshold = unmatched_threshold + if not negatives_lower_than_unmatched: + if self._unmatched_threshold == self._matched_threshold: + raise ValueError('When negatives are in between matched and ' + 'unmatched thresholds, these cannot be of equal ' + 'value. matched: %s, unmatched: %s', + self._matched_threshold, self._unmatched_threshold) + self._force_match_for_each_row = force_match_for_each_row + self._negatives_lower_than_unmatched = negatives_lower_than_unmatched + + def _match(self, similarity_matrix): + """Tries to match each column of the similarity matrix to a row. + + Args: + similarity_matrix: tensor of shape [N, M] representing any similarity + metric. + + Returns: + Match object with corresponding matches for each of M columns. + """ + + def _match_when_rows_are_empty(): + """Performs matching when the rows of similarity matrix are empty. + + When the rows are empty, all detections are false positives. So we return + a tensor of -1's to indicate that the columns do not match to any rows. + + Returns: + matches: int32 tensor indicating the row each column matches to. + """ + return -1 * tf.ones([tf.shape(similarity_matrix)[1]], dtype=tf.int32) + + def _match_when_rows_are_non_empty(): + """Performs matching when the rows of similarity matrix are non empty. + + Returns: + matches: int32 tensor indicating the row each column matches to. + """ + # Matches for each column + matches = tf.argmax(similarity_matrix, 0) + + # Deal with matched and unmatched threshold + if self._matched_threshold is not None: + # Get logical indices of ignored and unmatched columns as tf.int64 + matched_vals = tf.reduce_max(similarity_matrix, 0) + below_unmatched_threshold = tf.greater(self._unmatched_threshold, + matched_vals) + between_thresholds = tf.logical_and( + tf.greater_equal(matched_vals, self._unmatched_threshold), + tf.greater(self._matched_threshold, matched_vals)) + + if self._negatives_lower_than_unmatched: + matches = self._set_values_using_indicator(matches, + below_unmatched_threshold, + -1) + matches = self._set_values_using_indicator(matches, + between_thresholds, + -2) + else: + matches = self._set_values_using_indicator(matches, + below_unmatched_threshold, + -2) + matches = self._set_values_using_indicator(matches, + between_thresholds, + -1) + + if self._force_match_for_each_row: + forced_matches_ids = tf.cast(tf.argmax(similarity_matrix, 1), tf.int32) + + # Set matches[forced_matches_ids] = [0, ..., R], R is number of rows. + row_range = tf.range(tf.shape(similarity_matrix)[0]) + col_range = tf.range(tf.shape(similarity_matrix)[1]) + forced_matches_values = tf.cast(row_range, matches.dtype) + keep_matches_ids, _ = tf.setdiff1d(col_range, forced_matches_ids) + keep_matches_values = tf.gather(matches, keep_matches_ids) + matches = tf.dynamic_stitch( + [forced_matches_ids, + keep_matches_ids], [forced_matches_values, keep_matches_values]) + + return tf.cast(matches, tf.int32) + + return tf.cond( + tf.greater(tf.shape(similarity_matrix)[0], 0), + _match_when_rows_are_non_empty, _match_when_rows_are_empty) + + def _set_values_using_indicator(self, x, indicator, val): + """Set the indicated fields of x to val. + + Args: + x: tensor. + indicator: boolean with same shape as x. + val: scalar with value to set. + + Returns: + modified tensor. + """ + indicator = tf.cast(indicator, x.dtype) + return tf.add(tf.multiply(x, 1 - indicator), val * indicator) diff --git a/object_detection/matchers/argmax_matcher_test.py b/object_detection/matchers/argmax_matcher_test.py new file mode 100644 index 000000000..36740f4b6 --- /dev/null +++ b/object_detection/matchers/argmax_matcher_test.py @@ -0,0 +1,237 @@ +# 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.matchers.argmax_matcher.""" + +import numpy as np +import tensorflow as tf + +from object_detection.matchers import argmax_matcher + + +class ArgMaxMatcherTest(tf.test.TestCase): + + def test_return_correct_matches_with_default_thresholds(self): + similarity = np.array([[1., 1, 1, 3, 1], + [2, -1, 2, 0, 4], + [3, 0, -1, 0, 0]]) + + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=None) + expected_matched_rows = np.array([2, 0, 1, 0, 1]) + + sim = tf.constant(similarity) + match = matcher.match(sim) + matched_cols = match.matched_column_indices() + matched_rows = match.matched_row_indices() + unmatched_cols = match.unmatched_column_indices() + + with self.test_session() as sess: + res_matched_cols = sess.run(matched_cols) + res_matched_rows = sess.run(matched_rows) + res_unmatched_cols = sess.run(unmatched_cols) + + self.assertAllEqual(res_matched_rows, expected_matched_rows) + self.assertAllEqual(res_matched_cols, np.arange(similarity.shape[1])) + self.assertEmpty(res_unmatched_cols) + + def test_return_correct_matches_with_empty_rows(self): + + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=None) + sim = 0.2*tf.ones([0, 5]) + match = matcher.match(sim) + unmatched_cols = match.unmatched_column_indices() + + with self.test_session() as sess: + res_unmatched_cols = sess.run(unmatched_cols) + self.assertAllEqual(res_unmatched_cols, np.arange(5)) + + def test_return_correct_matches_with_matched_threshold(self): + similarity = np.array([[1, 1, 1, 3, 1], + [2, -1, 2, 0, 4], + [3, 0, -1, 0, 0]], dtype=np.int32) + + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=3) + expected_matched_cols = np.array([0, 3, 4]) + expected_matched_rows = np.array([2, 0, 1]) + expected_unmatched_cols = np.array([1, 2]) + + sim = tf.constant(similarity) + match = matcher.match(sim) + matched_cols = match.matched_column_indices() + matched_rows = match.matched_row_indices() + unmatched_cols = match.unmatched_column_indices() + + init_op = tf.global_variables_initializer() + + with self.test_session() as sess: + sess.run(init_op) + res_matched_cols = sess.run(matched_cols) + res_matched_rows = sess.run(matched_rows) + res_unmatched_cols = sess.run(unmatched_cols) + + self.assertAllEqual(res_matched_rows, expected_matched_rows) + self.assertAllEqual(res_matched_cols, expected_matched_cols) + self.assertAllEqual(res_unmatched_cols, expected_unmatched_cols) + + def test_return_correct_matches_with_matched_and_unmatched_threshold(self): + similarity = np.array([[1, 1, 1, 3, 1], + [2, -1, 2, 0, 4], + [3, 0, -1, 0, 0]], dtype=np.int32) + + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=3, + unmatched_threshold=2) + expected_matched_cols = np.array([0, 3, 4]) + expected_matched_rows = np.array([2, 0, 1]) + expected_unmatched_cols = np.array([1]) # col 2 has too high maximum val + + sim = tf.constant(similarity) + match = matcher.match(sim) + matched_cols = match.matched_column_indices() + matched_rows = match.matched_row_indices() + unmatched_cols = match.unmatched_column_indices() + + with self.test_session() as sess: + res_matched_cols = sess.run(matched_cols) + res_matched_rows = sess.run(matched_rows) + res_unmatched_cols = sess.run(unmatched_cols) + + self.assertAllEqual(res_matched_rows, expected_matched_rows) + self.assertAllEqual(res_matched_cols, expected_matched_cols) + self.assertAllEqual(res_unmatched_cols, expected_unmatched_cols) + + def test_return_correct_matches_negatives_lower_than_unmatched_false(self): + similarity = np.array([[1, 1, 1, 3, 1], + [2, -1, 2, 0, 4], + [3, 0, -1, 0, 0]], dtype=np.int32) + + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=3, + unmatched_threshold=2, + negatives_lower_than_unmatched=False) + expected_matched_cols = np.array([0, 3, 4]) + expected_matched_rows = np.array([2, 0, 1]) + expected_unmatched_cols = np.array([2]) # col 1 has too low maximum val + + sim = tf.constant(similarity) + match = matcher.match(sim) + matched_cols = match.matched_column_indices() + matched_rows = match.matched_row_indices() + unmatched_cols = match.unmatched_column_indices() + + with self.test_session() as sess: + res_matched_cols = sess.run(matched_cols) + res_matched_rows = sess.run(matched_rows) + res_unmatched_cols = sess.run(unmatched_cols) + + self.assertAllEqual(res_matched_rows, expected_matched_rows) + self.assertAllEqual(res_matched_cols, expected_matched_cols) + self.assertAllEqual(res_unmatched_cols, expected_unmatched_cols) + + def test_return_correct_matches_unmatched_row_not_using_force_match(self): + similarity = np.array([[1, 1, 1, 3, 1], + [-1, 0, -2, -2, -1], + [3, 0, -1, 2, 0]], dtype=np.int32) + + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=3, + unmatched_threshold=2) + expected_matched_cols = np.array([0, 3]) + expected_matched_rows = np.array([2, 0]) + expected_unmatched_cols = np.array([1, 2, 4]) + + sim = tf.constant(similarity) + match = matcher.match(sim) + matched_cols = match.matched_column_indices() + matched_rows = match.matched_row_indices() + unmatched_cols = match.unmatched_column_indices() + + with self.test_session() as sess: + res_matched_cols = sess.run(matched_cols) + res_matched_rows = sess.run(matched_rows) + res_unmatched_cols = sess.run(unmatched_cols) + + self.assertAllEqual(res_matched_rows, expected_matched_rows) + self.assertAllEqual(res_matched_cols, expected_matched_cols) + self.assertAllEqual(res_unmatched_cols, expected_unmatched_cols) + + def test_return_correct_matches_unmatched_row_while_using_force_match(self): + similarity = np.array([[1, 1, 1, 3, 1], + [-1, 0, -2, -2, -1], + [3, 0, -1, 2, 0]], dtype=np.int32) + + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=3, + unmatched_threshold=2, + force_match_for_each_row=True) + expected_matched_cols = np.array([0, 1, 3]) + expected_matched_rows = np.array([2, 1, 0]) + expected_unmatched_cols = np.array([2, 4]) # col 2 has too high max val + + sim = tf.constant(similarity) + match = matcher.match(sim) + matched_cols = match.matched_column_indices() + matched_rows = match.matched_row_indices() + unmatched_cols = match.unmatched_column_indices() + + with self.test_session() as sess: + res_matched_cols = sess.run(matched_cols) + res_matched_rows = sess.run(matched_rows) + res_unmatched_cols = sess.run(unmatched_cols) + + self.assertAllEqual(res_matched_rows, expected_matched_rows) + self.assertAllEqual(res_matched_cols, expected_matched_cols) + self.assertAllEqual(res_unmatched_cols, expected_unmatched_cols) + + def test_valid_arguments_corner_case(self): + argmax_matcher.ArgMaxMatcher(matched_threshold=1, + unmatched_threshold=1) + + def test_invalid_arguments_corner_case_negatives_lower_than_thres_false(self): + with self.assertRaises(ValueError): + argmax_matcher.ArgMaxMatcher(matched_threshold=1, + unmatched_threshold=1, + negatives_lower_than_unmatched=False) + + def test_invalid_arguments_no_matched_threshold(self): + with self.assertRaises(ValueError): + argmax_matcher.ArgMaxMatcher(matched_threshold=None, + unmatched_threshold=4) + + def test_invalid_arguments_unmatched_thres_larger_than_matched_thres(self): + with self.assertRaises(ValueError): + argmax_matcher.ArgMaxMatcher(matched_threshold=1, + unmatched_threshold=2) + + def test_set_values_using_indicator(self): + input_a = np.array([3, 4, 5, 1, 4, 3, 2]) + expected_b = np.array([3, 0, 0, 1, 0, 3, 2]) # Set a>3 to 0 + expected_c = np.array( + [3., 4., 5., -1., 4., 3., -1.]) # Set a<3 to -1. Float32 + idxb_ = input_a > 3 + idxc_ = input_a < 3 + + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=None) + + a = tf.constant(input_a) + idxb = tf.constant(idxb_) + idxc = tf.constant(idxc_) + b = matcher._set_values_using_indicator(a, idxb, 0) + c = matcher._set_values_using_indicator(tf.cast(a, tf.float32), idxc, -1) + with self.test_session() as sess: + res_b = sess.run(b) + res_c = sess.run(c) + self.assertAllEqual(res_b, expected_b) + self.assertAllEqual(res_c, expected_c) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/matchers/bipartite_matcher.py b/object_detection/matchers/bipartite_matcher.py new file mode 100644 index 000000000..3d717d12f --- /dev/null +++ b/object_detection/matchers/bipartite_matcher.py @@ -0,0 +1,53 @@ +# 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. +# ============================================================================== + +"""Bipartite matcher implementation.""" + +import tensorflow as tf + +from tensorflow.contrib.image.python.ops import image_ops +from object_detection.core import matcher + + +class GreedyBipartiteMatcher(matcher.Matcher): + """Wraps a Tensorflow greedy bipartite matcher.""" + + def _match(self, similarity_matrix, num_valid_rows=-1): + """Bipartite matches a collection rows and columns. A greedy bi-partite. + + TODO: Add num_valid_columns options to match only that many columns with + all the rows. + + Args: + similarity_matrix: Float tensor of shape [N, M] with pairwise similarity + where higher values mean more similar. + num_valid_rows: A scalar or a 1-D tensor with one element describing the + number of valid rows of similarity_matrix to consider for the bipartite + matching. If set to be negative, then all rows from similarity_matrix + are used. + + Returns: + match_results: int32 tensor of shape [M] with match_results[i]=-1 + meaning that column i is not matched and otherwise that it is matched to + row match_results[i]. + """ + # Convert similarity matrix to distance matrix as tf.image.bipartite tries + # to find minimum distance matches. + distance_matrix = -1 * similarity_matrix + _, match_results = image_ops.bipartite_match( + distance_matrix, num_valid_rows) + match_results = tf.reshape(match_results, [-1]) + match_results = tf.cast(match_results, tf.int32) + return match_results diff --git a/object_detection/matchers/bipartite_matcher_test.py b/object_detection/matchers/bipartite_matcher_test.py new file mode 100644 index 000000000..2ee45a80d --- /dev/null +++ b/object_detection/matchers/bipartite_matcher_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 object_detection.core.bipartite_matcher.""" + +import tensorflow as tf + +from object_detection.matchers import bipartite_matcher + + +class GreedyBipartiteMatcherTest(tf.test.TestCase): + + def test_get_expected_matches_when_all_rows_are_valid(self): + similarity_matrix = tf.constant([[0.50, 0.1, 0.8], [0.15, 0.2, 0.3]]) + num_valid_rows = 2 + expected_match_results = [-1, 1, 0] + + matcher = bipartite_matcher.GreedyBipartiteMatcher() + match = matcher.match(similarity_matrix, num_valid_rows=num_valid_rows) + with self.test_session() as sess: + match_results_out = sess.run(match._match_results) + self.assertAllEqual(match_results_out, expected_match_results) + + def test_get_expected_matches_with_valid_rows_set_to_minus_one(self): + similarity_matrix = tf.constant([[0.50, 0.1, 0.8], [0.15, 0.2, 0.3]]) + num_valid_rows = -1 + expected_match_results = [-1, 1, 0] + + matcher = bipartite_matcher.GreedyBipartiteMatcher() + match = matcher.match(similarity_matrix, num_valid_rows=num_valid_rows) + with self.test_session() as sess: + match_results_out = sess.run(match._match_results) + self.assertAllEqual(match_results_out, expected_match_results) + + def test_get_no_matches_with_zero_valid_rows(self): + similarity_matrix = tf.constant([[0.50, 0.1, 0.8], [0.15, 0.2, 0.3]]) + num_valid_rows = 0 + expected_match_results = [-1, -1, -1] + + matcher = bipartite_matcher.GreedyBipartiteMatcher() + match = matcher.match(similarity_matrix, num_valid_rows=num_valid_rows) + with self.test_session() as sess: + match_results_out = sess.run(match._match_results) + self.assertAllEqual(match_results_out, expected_match_results) + + def test_get_expected_matches_with_only_one_valid_row(self): + similarity_matrix = tf.constant([[0.50, 0.1, 0.8], [0.15, 0.2, 0.3]]) + num_valid_rows = 1 + expected_match_results = [-1, -1, 0] + + matcher = bipartite_matcher.GreedyBipartiteMatcher() + match = matcher.match(similarity_matrix, num_valid_rows=num_valid_rows) + with self.test_session() as sess: + match_results_out = sess.run(match._match_results) + self.assertAllEqual(match_results_out, expected_match_results) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/meta_architectures/BUILD b/object_detection/meta_architectures/BUILD new file mode 100644 index 000000000..5a9dcdc3e --- /dev/null +++ b/object_detection/meta_architectures/BUILD @@ -0,0 +1,109 @@ +# Tensorflow Object Detection API: Meta-architectures. + +package( + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) + +# Apache 2.0 + +py_library( + name = "ssd_meta_arch", + srcs = ["ssd_meta_arch.py"], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/core:box_coder", + "//tensorflow_models/object_detection/core:box_list", + "//tensorflow_models/object_detection/core:box_predictor", + "//tensorflow_models/object_detection/core:model", + "//tensorflow_models/object_detection/core:target_assigner", + "//tensorflow_models/object_detection/utils:variables_helper", + ], +) + +py_test( + name = "ssd_meta_arch_test", + srcs = ["ssd_meta_arch_test.py"], + deps = [ + ":ssd_meta_arch", + "//tensorflow", + "//tensorflow/python:training", + "//tensorflow_models/object_detection/core:anchor_generator", + "//tensorflow_models/object_detection/core:box_list", + "//tensorflow_models/object_detection/core:losses", + "//tensorflow_models/object_detection/core:post_processing", + "//tensorflow_models/object_detection/core:region_similarity_calculator", + "//tensorflow_models/object_detection/utils:test_utils", + ], +) + +py_library( + name = "faster_rcnn_meta_arch", + srcs = [ + "faster_rcnn_meta_arch.py", + ], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/anchor_generators:grid_anchor_generator", + "//tensorflow_models/object_detection/core:balanced_positive_negative_sampler", + "//tensorflow_models/object_detection/core:box_list", + "//tensorflow_models/object_detection/core:box_list_ops", + "//tensorflow_models/object_detection/core:box_predictor", + "//tensorflow_models/object_detection/core:losses", + "//tensorflow_models/object_detection/core:model", + "//tensorflow_models/object_detection/core:post_processing", + "//tensorflow_models/object_detection/core:standard_fields", + "//tensorflow_models/object_detection/core:target_assigner", + "//tensorflow_models/object_detection/utils:ops", + "//tensorflow_models/object_detection/utils:variables_helper", + ], +) + +py_library( + name = "faster_rcnn_meta_arch_test_lib", + srcs = [ + "faster_rcnn_meta_arch_test_lib.py", + ], + deps = [ + ":faster_rcnn_meta_arch", + "//tensorflow", + "//tensorflow_models/object_detection/anchor_generators:grid_anchor_generator", + "//tensorflow_models/object_detection/builders:box_predictor_builder", + "//tensorflow_models/object_detection/builders:hyperparams_builder", + "//tensorflow_models/object_detection/builders:post_processing_builder", + "//tensorflow_models/object_detection/core:losses", + "//tensorflow_models/object_detection/protos:box_predictor_py_pb2", + "//tensorflow_models/object_detection/protos:hyperparams_py_pb2", + "//tensorflow_models/object_detection/protos:post_processing_py_pb2", + ], +) + +py_test( + name = "faster_rcnn_meta_arch_test", + srcs = ["faster_rcnn_meta_arch_test.py"], + deps = [ + ":faster_rcnn_meta_arch_test_lib", + ], +) + +py_library( + name = "rfcn_meta_arch", + srcs = ["rfcn_meta_arch.py"], + deps = [ + ":faster_rcnn_meta_arch", + "//tensorflow", + "//tensorflow_models/object_detection/core:box_predictor", + "//tensorflow_models/object_detection/utils:ops", + ], +) + +py_test( + name = "rfcn_meta_arch_test", + srcs = ["rfcn_meta_arch_test.py"], + deps = [ + ":faster_rcnn_meta_arch_test_lib", + ":rfcn_meta_arch", + "//tensorflow", + ], +) diff --git a/object_detection/meta_architectures/__init__.py b/object_detection/meta_architectures/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/object_detection/meta_architectures/faster_rcnn_meta_arch.py b/object_detection/meta_architectures/faster_rcnn_meta_arch.py new file mode 100644 index 000000000..baf6d38fa --- /dev/null +++ b/object_detection/meta_architectures/faster_rcnn_meta_arch.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. +# ============================================================================== + +"""Faster R-CNN meta-architecture definition. + +General tensorflow implementation of Faster R-CNN detection models. + +See Faster R-CNN: Ren, Shaoqing, et al. +"Faster R-CNN: Towards real-time object detection with region proposal +networks." Advances in neural information processing systems. 2015. + +We allow for two modes: first_stage_only=True and first_stage_only=False. In +the former setting, all of the user facing methods (e.g., predict, postprocess, +loss) can be used as if the model consisted only of the RPN, returning class +agnostic proposals (these can be thought of as approximate detections with no +associated class information). In the latter setting, proposals are computed, +then passed through a second stage "box classifier" to yield (multi-class) +detections. + +Implementations of Faster R-CNN models must define a new +FasterRCNNFeatureExtractor and override three methods: `preprocess`, +`_extract_proposal_features` (the first stage of the model), and +`_extract_box_classifier_features` (the second stage of the model). Optionally, +the `restore_fn` method can be overridden. See tests for an example. + +A few important notes: ++ Batching conventions: We support batched inference and training where +all images within a batch have the same resolution. Batch sizes are determined +dynamically via the shape of the input tensors (rather than being specified +directly as, e.g., a model constructor). + +A complication is that due to non-max suppression, we are not guaranteed to get +the same number of proposals from the first stage RPN (region proposal network) +for each image (though in practice, we should often get the same number of +proposals). For this reason we pad to a max number of proposals per image +within a batch. This `self.max_num_proposals` property is set to the +`first_stage_max_proposals` parameter at inference time and the +`second_stage_batch_size` at training time since we subsample the batch to +be sent through the box classifier during training. + +For the second stage of the pipeline, we arrange the proposals for all images +within the batch along a single batch dimension. For example, the input to +_extract_box_classifier_features is a tensor of shape +`[total_num_proposals, crop_height, crop_width, depth]` where +total_num_proposals is batch_size * self.max_num_proposals. (And note that per +the above comment, a subset of these entries correspond to zero paddings.) + ++ Coordinate representations: +Following the API (see model.DetectionModel definition), our outputs after +postprocessing operations are always normalized boxes however, internally, we +sometimes convert to absolute --- e.g. for loss computation. In particular, +anchors and proposal_boxes are both represented as absolute coordinates. + +TODO: Support TPU implementations and sigmoid loss. +""" +from abc import abstractmethod +from functools import partial +import tensorflow as tf + +from object_detection.anchor_generators import grid_anchor_generator +from object_detection.core import balanced_positive_negative_sampler as sampler +from object_detection.core import box_list +from object_detection.core import box_list_ops +from object_detection.core import box_predictor +from object_detection.core import losses +from object_detection.core import model +from object_detection.core import post_processing +from object_detection.core import standard_fields as fields +from object_detection.core import target_assigner +from object_detection.utils import ops +from object_detection.utils import variables_helper + +slim = tf.contrib.slim + + +class FasterRCNNFeatureExtractor(object): + """Faster R-CNN Feature Extractor definition.""" + + def __init__(self, + is_training, + first_stage_features_stride, + reuse_weights=None, + weight_decay=0.0): + """Constructor. + + Args: + is_training: A boolean indicating whether the training version of the + computation graph should be constructed. + first_stage_features_stride: Output stride of extracted RPN feature map. + reuse_weights: Whether to reuse variables. Default is None. + weight_decay: float weight decay for feature extractor (default: 0.0). + """ + self._is_training = is_training + self._first_stage_features_stride = first_stage_features_stride + self._reuse_weights = reuse_weights + self._weight_decay = weight_decay + + @abstractmethod + def preprocess(self, resized_inputs): + """Feature-extractor specific preprocessing (minus image resizing).""" + pass + + def extract_proposal_features(self, preprocessed_inputs, scope): + """Extracts first stage RPN features. + + This function is responsible for extracting feature maps from preprocessed + images. These features are used by the region proposal network (RPN) to + predict proposals. + + Args: + preprocessed_inputs: A [batch, height, width, channels] float tensor + representing a batch of images. + scope: A scope name. + + Returns: + rpn_feature_map: A tensor with shape [batch, height, width, depth] + """ + with tf.variable_scope(scope, values=[preprocessed_inputs]): + return self._extract_proposal_features(preprocessed_inputs, scope) + + @abstractmethod + def _extract_proposal_features(self, preprocessed_inputs, scope): + """Extracts first stage RPN features, to be overridden.""" + pass + + def extract_box_classifier_features(self, proposal_feature_maps, scope): + """Extracts second stage box classifier features. + + Args: + proposal_feature_maps: A 4-D float tensor with shape + [batch_size * self.max_num_proposals, crop_height, crop_width, depth] + representing the feature map cropped to each proposal. + scope: A scope name. + + Returns: + proposal_classifier_features: A 4-D float tensor with shape + [batch_size * self.max_num_proposals, height, width, depth] + representing box classifier features for each proposal. + """ + with tf.variable_scope(scope, values=[proposal_feature_maps]): + return self._extract_box_classifier_features(proposal_feature_maps, scope) + + @abstractmethod + def _extract_box_classifier_features(self, proposal_feature_maps, scope): + """Extracts second stage box classifier features, to be overridden.""" + pass + + def restore_from_classification_checkpoint_fn( + self, + checkpoint_path, + first_stage_feature_extractor_scope, + second_stage_feature_extractor_scope): + """Returns callable for loading a checkpoint into the tensorflow graph. + + Args: + checkpoint_path: path to checkpoint to restore. + first_stage_feature_extractor_scope: A scope name for the first stage + feature extractor. + second_stage_feature_extractor_scope: A scope name for the second stage + feature extractor. + + Returns: + a callable which takes a tf.Session as input and loads a checkpoint when + run. + """ + variables_to_restore = {} + for variable in tf.global_variables(): + for scope_name in [first_stage_feature_extractor_scope, + second_stage_feature_extractor_scope]: + if variable.op.name.startswith(scope_name): + var_name = variable.op.name.replace(scope_name + '/', '') + variables_to_restore[var_name] = variable + variables_to_restore = ( + variables_helper.get_variables_available_in_checkpoint( + variables_to_restore, checkpoint_path)) + saver = tf.train.Saver(variables_to_restore) + def restore(sess): + saver.restore(sess, checkpoint_path) + return restore + + +class FasterRCNNMetaArch(model.DetectionModel): + """Faster R-CNN Meta-architecture definition.""" + + def __init__(self, + is_training, + num_classes, + image_resizer_fn, + feature_extractor, + first_stage_only, + first_stage_anchor_generator, + first_stage_atrous_rate, + first_stage_box_predictor_arg_scope, + first_stage_box_predictor_kernel_size, + first_stage_box_predictor_depth, + first_stage_minibatch_size, + first_stage_positive_balance_fraction, + first_stage_nms_score_threshold, + first_stage_nms_iou_threshold, + first_stage_max_proposals, + first_stage_localization_loss_weight, + first_stage_objectness_loss_weight, + initial_crop_size, + maxpool_kernel_size, + maxpool_stride, + second_stage_mask_rcnn_box_predictor, + second_stage_batch_size, + second_stage_balance_fraction, + second_stage_non_max_suppression_fn, + second_stage_score_conversion_fn, + second_stage_localization_loss_weight, + second_stage_classification_loss_weight, + hard_example_miner, + parallel_iterations=16): + """FasterRCNNMetaArch Constructor. + + Args: + is_training: A boolean indicating whether the training version of the + computation graph should be constructed. + 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}). + image_resizer_fn: A callable for image resizing. This callable always + takes a rank-3 image tensor (corresponding to a single image) and + returns a rank-3 image tensor, possibly with new spatial dimensions. + See builders/image_resizer_builder.py. + feature_extractor: A FasterRCNNFeatureExtractor object. + first_stage_only: Whether to construct only the Region Proposal Network + (RPN) part of the model. + first_stage_anchor_generator: An anchor_generator.AnchorGenerator object + (note that currently we only support + grid_anchor_generator.GridAnchorGenerator objects) + first_stage_atrous_rate: A single integer indicating the atrous rate for + the single convolution op which is applied to the `rpn_features_to_crop` + tensor to obtain a tensor to be used for box prediction. Some feature + extractors optionally allow for producing feature maps computed at + denser resolutions. The atrous rate is used to compensate for the + denser feature maps by using an effectively larger receptive field. + (This should typically be set to 1). + first_stage_box_predictor_arg_scope: Slim arg_scope for conv2d, + separable_conv2d and fully_connected ops for the RPN box predictor. + first_stage_box_predictor_kernel_size: Kernel size to use for the + convolution op just prior to RPN box predictions. + first_stage_box_predictor_depth: Output depth for the convolution op + just prior to RPN box predictions. + first_stage_minibatch_size: The "batch size" to use for computing the + objectness and location loss of the region proposal network. This + "batch size" refers to the number of anchors selected as contributing + to the loss function for any given image within the image batch and is + only called "batch_size" due to terminology from the Faster R-CNN paper. + first_stage_positive_balance_fraction: Fraction of positive examples + per image for the RPN. The recommended value for Faster RCNN is 0.5. + first_stage_nms_score_threshold: Score threshold for non max suppression + for the Region Proposal Network (RPN). This value is expected to be in + [0, 1] as it is applied directly after a softmax transformation. The + recommended value for Faster R-CNN is 0. + first_stage_nms_iou_threshold: The Intersection Over Union (IOU) threshold + for performing Non-Max Suppression (NMS) on the boxes predicted by the + Region Proposal Network (RPN). + first_stage_max_proposals: Maximum number of boxes to retain after + performing Non-Max Suppression (NMS) on the boxes predicted by the + Region Proposal Network (RPN). + first_stage_localization_loss_weight: A float + first_stage_objectness_loss_weight: A float + initial_crop_size: A single integer indicating the output size + (width and height are set to be the same) of the initial bilinear + interpolation based cropping during ROI pooling. + maxpool_kernel_size: A single integer indicating the kernel size of the + max pool op on the cropped feature map during ROI pooling. + maxpool_stride: A single integer indicating the stride of the max pool + op on the cropped feature map during ROI pooling. + second_stage_mask_rcnn_box_predictor: Mask R-CNN box predictor to use for + the second stage. + second_stage_batch_size: The batch size used for computing the + classification and refined location loss of the box classifier. This + "batch size" refers to the number of proposals selected as contributing + to the loss function for any given image within the image batch and is + only called "batch_size" due to terminology from the Faster R-CNN paper. + second_stage_balance_fraction: Fraction of positive examples to use + per image for the box classifier. The recommended value for Faster RCNN + is 0.25. + second_stage_non_max_suppression_fn: batch_multiclass_non_max_suppression + callable that takes `boxes`, `scores`, optional `clip_window` and + optional (kwarg) `mask` inputs (with all other inputs already set) + and returns a dictionary containing tensors with keys: + `detection_boxes`, `detection_scores`, `detection_classes`, + `num_detections`, and (optionally) `detection_masks`. See + `post_processing.batch_multiclass_non_max_suppression` for the type and + shape of these tensors. + second_stage_score_conversion_fn: Callable elementwise nonlinearity + (that takes tensors as inputs and returns tensors). This is usually + used to convert logits to probabilities. + second_stage_localization_loss_weight: A float + second_stage_classification_loss_weight: A float + hard_example_miner: A losses.HardExampleMiner object (can be None). + parallel_iterations: (Optional) The number of iterations allowed to run + in parallel for calls to tf.map_fn. + Raises: + ValueError: If `second_stage_batch_size` > `first_stage_max_proposals` + ValueError: If first_stage_anchor_generator is not of type + grid_anchor_generator.GridAnchorGenerator. + """ + super(FasterRCNNMetaArch, self).__init__(num_classes=num_classes) + + if second_stage_batch_size > first_stage_max_proposals: + raise ValueError('second_stage_batch_size should be no greater than ' + 'first_stage_max_proposals.') + if not isinstance(first_stage_anchor_generator, + grid_anchor_generator.GridAnchorGenerator): + raise ValueError('first_stage_anchor_generator must be of type ' + 'grid_anchor_generator.GridAnchorGenerator.') + + self._is_training = is_training + self._image_resizer_fn = image_resizer_fn + self._feature_extractor = feature_extractor + self._first_stage_only = first_stage_only + + # The first class is reserved as background. + unmatched_cls_target = tf.constant( + [1] + self._num_classes * [0], dtype=tf.float32) + self._proposal_target_assigner = target_assigner.create_target_assigner( + 'FasterRCNN', 'proposal') + self._detector_target_assigner = target_assigner.create_target_assigner( + 'FasterRCNN', 'detection', unmatched_cls_target=unmatched_cls_target) + # Both proposal and detector target assigners use the same box coder + self._box_coder = self._proposal_target_assigner.box_coder + + # (First stage) Region proposal network parameters + self._first_stage_anchor_generator = first_stage_anchor_generator + self._first_stage_atrous_rate = first_stage_atrous_rate + self._first_stage_box_predictor_arg_scope = ( + first_stage_box_predictor_arg_scope) + self._first_stage_box_predictor_kernel_size = ( + first_stage_box_predictor_kernel_size) + self._first_stage_box_predictor_depth = first_stage_box_predictor_depth + self._first_stage_minibatch_size = first_stage_minibatch_size + self._first_stage_sampler = sampler.BalancedPositiveNegativeSampler( + positive_fraction=first_stage_positive_balance_fraction) + self._first_stage_box_predictor = box_predictor.ConvolutionalBoxPredictor( + self._is_training, num_classes=1, + conv_hyperparams=self._first_stage_box_predictor_arg_scope, + min_depth=0, max_depth=0, num_layers_before_predictor=0, + use_dropout=False, dropout_keep_prob=1.0, kernel_size=1, + box_code_size=self._box_coder.code_size) + + self._first_stage_nms_score_threshold = first_stage_nms_score_threshold + self._first_stage_nms_iou_threshold = first_stage_nms_iou_threshold + self._first_stage_max_proposals = first_stage_max_proposals + + self._first_stage_localization_loss = ( + losses.WeightedSmoothL1LocalizationLoss(anchorwise_output=True)) + self._first_stage_objectness_loss = ( + losses.WeightedSoftmaxClassificationLoss(anchorwise_output=True)) + self._first_stage_loc_loss_weight = first_stage_localization_loss_weight + self._first_stage_obj_loss_weight = first_stage_objectness_loss_weight + + # Per-region cropping parameters + self._initial_crop_size = initial_crop_size + self._maxpool_kernel_size = maxpool_kernel_size + self._maxpool_stride = maxpool_stride + + self._mask_rcnn_box_predictor = second_stage_mask_rcnn_box_predictor + + self._second_stage_batch_size = second_stage_batch_size + self._second_stage_sampler = sampler.BalancedPositiveNegativeSampler( + positive_fraction=second_stage_balance_fraction) + + self._second_stage_nms_fn = second_stage_non_max_suppression_fn + self._second_stage_score_conversion_fn = second_stage_score_conversion_fn + + self._second_stage_localization_loss = ( + losses.WeightedSmoothL1LocalizationLoss(anchorwise_output=True)) + self._second_stage_classification_loss = ( + losses.WeightedSoftmaxClassificationLoss(anchorwise_output=True)) + self._second_stage_loc_loss_weight = second_stage_localization_loss_weight + self._second_stage_cls_loss_weight = second_stage_classification_loss_weight + self._hard_example_miner = hard_example_miner + self._parallel_iterations = parallel_iterations + + @property + def first_stage_feature_extractor_scope(self): + return 'FirstStageFeatureExtractor' + + @property + def second_stage_feature_extractor_scope(self): + return 'SecondStageFeatureExtractor' + + @property + def first_stage_box_predictor_scope(self): + return 'FirstStageBoxPredictor' + + @property + def second_stage_box_predictor_scope(self): + return 'SecondStageBoxPredictor' + + @property + def max_num_proposals(self): + """Max number of proposals (to pad to) for each image in the input batch. + + At training time, this is set to be the `second_stage_batch_size` if hard + example miner is not configured, else it is set to + `first_stage_max_proposals`. At inference time, this is always set to + `first_stage_max_proposals`. + + Returns: + A positive integer. + """ + if self._is_training and not self._hard_example_miner: + return self._second_stage_batch_size + return self._first_stage_max_proposals + + def preprocess(self, inputs): + """Feature-extractor specific preprocessing. + + See base class. + + For Faster R-CNN, we perform image resizing in the base class --- each + class subclassing FasterRCNNMetaArch is responsible for any additional + preprocessing (e.g., scaling pixel values to be in [-1, 1]). + + Args: + inputs: a [batch, height_in, width_in, channels] float tensor representing + a batch of images with values between 0 and 255.0. + + Returns: + preprocessed_inputs: a [batch, height_out, width_out, channels] float + tensor representing a batch of images. + Raises: + ValueError: if inputs tensor does not have type tf.float32 + """ + if inputs.dtype is not tf.float32: + raise ValueError('`preprocess` expects a tf.float32 tensor') + with tf.name_scope('Preprocessor'): + resized_inputs = tf.map_fn(self._image_resizer_fn, + elems=inputs, + dtype=tf.float32, + parallel_iterations=self._parallel_iterations) + return self._feature_extractor.preprocess(resized_inputs) + + def predict(self, preprocessed_inputs): + """Predicts unpostprocessed tensors from input tensor. + + This function takes an input batch of images and runs it through the + forward pass of the network to yield "raw" un-postprocessed predictions. + If `first_stage_only` is True, this function only returns first stage + RPN predictions (un-postprocessed). Otherwise it returns both + first stage RPN predictions as well as second stage box classifier + predictions. + + Other remarks: + + Anchor pruning vs. clipping: following the recommendation of the Faster + R-CNN paper, we prune anchors that venture outside the image window at + training time and clip anchors to the image window at inference time. + + Proposal padding: as described at the top of the file, proposals are + padded to self._max_num_proposals and flattened so that proposals from all + images within the input batch are arranged along the same batch dimension. + + Args: + preprocessed_inputs: a [batch, height, width, channels] float tensor + representing a batch of images. + + Returns: + prediction_dict: a dictionary holding "raw" prediction tensors: + 1) rpn_box_predictor_features: A 4-D float32 tensor with shape + [batch_size, height, width, depth] to be used for predicting proposal + boxes and corresponding objectness scores. + 2) rpn_features_to_crop: A 4-D float32 tensor with shape + [batch_size, height, width, depth] representing image features to crop + using the proposal boxes predicted by the RPN. + 3) image_shape: a 1-D tensor of shape [4] representing the input + image shape. + 4) rpn_box_encodings: 3-D float tensor of shape + [batch_size, num_anchors, self._box_coder.code_size] containing + predicted boxes. + 5) rpn_objectness_predictions_with_background: 3-D float tensor of shape + [batch_size, num_anchors, 2] containing class + predictions (logits) for each of the anchors. Note that this + tensor *includes* background class predictions (at class index 0). + 6) anchors: A 2-D tensor of shape [num_anchors, 4] representing anchors + for the first stage RPN (in absolute coordinates). Note that + `num_anchors` can differ depending on whether the model is created in + training or inference mode. + + (and if first_stage_only=False): + 7) refined_box_encodings: a 3-D tensor with shape + [total_num_proposals, num_classes, 4] representing predicted + (final) refined box encodings, where + total_num_proposals=batch_size*self._max_num_proposals + 8) class_predictions_with_background: a 3-D tensor with shape + [total_num_proposals, num_classes + 1] containing class + predictions (logits) for each of the anchors, where + total_num_proposals=batch_size*self._max_num_proposals. + Note that this tensor *includes* background class predictions + (at class index 0). + 9) num_proposals: An int32 tensor of shape [batch_size] representing the + number of proposals generated by the RPN. `num_proposals` allows us + to keep track of which entries are to be treated as zero paddings and + which are not since we always pad the number of proposals to be + `self.max_num_proposals` for each image. + 10) proposal_boxes: A float32 tensor of shape + [batch_size, self.max_num_proposals, 4] representing + decoded proposal bounding boxes (in absolute coordinates). + 11) mask_predictions: (optional) a 4-D tensor with shape + [total_num_padded_proposals, num_classes, mask_height, mask_width] + containing instance mask predictions. + """ + (rpn_box_predictor_features, rpn_features_to_crop, anchors_boxlist, + image_shape) = self._extract_rpn_feature_maps(preprocessed_inputs) + (rpn_box_encodings, rpn_objectness_predictions_with_background + ) = self._predict_rpn_proposals(rpn_box_predictor_features) + + # The Faster R-CNN paper recommends pruning anchors that venture outside + # the image window at training time and clipping at inference time. + clip_window = tf.to_float(tf.stack([0, 0, image_shape[1], image_shape[2]])) + if self._is_training: + (rpn_box_encodings, rpn_objectness_predictions_with_background, + anchors_boxlist) = self._remove_invalid_anchors_and_predictions( + rpn_box_encodings, rpn_objectness_predictions_with_background, + anchors_boxlist, clip_window) + else: + anchors_boxlist = box_list_ops.clip_to_window( + anchors_boxlist, clip_window) + + anchors = anchors_boxlist.get() + prediction_dict = { + 'rpn_box_predictor_features': rpn_box_predictor_features, + 'rpn_features_to_crop': rpn_features_to_crop, + 'image_shape': image_shape, + 'rpn_box_encodings': rpn_box_encodings, + 'rpn_objectness_predictions_with_background': + rpn_objectness_predictions_with_background, + 'anchors': anchors + } + + if not self._first_stage_only: + prediction_dict.update(self._predict_second_stage( + rpn_box_encodings, + rpn_objectness_predictions_with_background, + rpn_features_to_crop, + anchors, image_shape)) + return prediction_dict + + def _predict_second_stage(self, rpn_box_encodings, + rpn_objectness_predictions_with_background, + rpn_features_to_crop, + anchors, + image_shape): + """Predicts the output tensors from second stage of Faster R-CNN. + + Args: + rpn_box_encodings: 4-D float tensor of shape + [batch_size, num_valid_anchors, self._box_coder.code_size] containing + predicted boxes. + rpn_objectness_predictions_with_background: 2-D float tensor of shape + [batch_size, num_valid_anchors, 2] containing class + predictions (logits) for each of the anchors. Note that this + tensor *includes* background class predictions (at class index 0). + rpn_features_to_crop: A 4-D float32 tensor with shape + [batch_size, height, width, depth] representing image features to crop + using the proposal boxes predicted by the RPN. + anchors: 2-D float tensor of shape + [num_anchors, self._box_coder.code_size]. + image_shape: A 1D int32 tensors of size [4] containing the image shape. + + Returns: + prediction_dict: a dictionary holding "raw" prediction tensors: + 1) refined_box_encodings: a 3-D tensor with shape + [total_num_proposals, num_classes, 4] representing predicted + (final) refined box encodings, where + total_num_proposals=batch_size*self._max_num_proposals + 2) class_predictions_with_background: a 3-D tensor with shape + [total_num_proposals, num_classes + 1] containing class + predictions (logits) for each of the anchors, where + total_num_proposals=batch_size*self._max_num_proposals. + Note that this tensor *includes* background class predictions + (at class index 0). + 3) num_proposals: An int32 tensor of shape [batch_size] representing the + number of proposals generated by the RPN. `num_proposals` allows us + to keep track of which entries are to be treated as zero paddings and + which are not since we always pad the number of proposals to be + `self.max_num_proposals` for each image. + 4) proposal_boxes: A float32 tensor of shape + [batch_size, self.max_num_proposals, 4] representing + decoded proposal bounding boxes (in absolute coordinates). + 5) mask_predictions: (optional) a 4-D tensor with shape + [total_num_padded_proposals, num_classes, mask_height, mask_width] + containing instance mask predictions. + """ + proposal_boxes_normalized, _, num_proposals = self._postprocess_rpn( + rpn_box_encodings, rpn_objectness_predictions_with_background, + anchors, image_shape) + + flattened_proposal_feature_maps = ( + self._compute_second_stage_input_feature_maps( + rpn_features_to_crop, proposal_boxes_normalized)) + + box_classifier_features = ( + self._feature_extractor.extract_box_classifier_features( + flattened_proposal_feature_maps, + scope=self.second_stage_feature_extractor_scope)) + + box_predictions = self._mask_rcnn_box_predictor.predict( + box_classifier_features, + num_predictions_per_location=1, + scope=self.second_stage_box_predictor_scope) + refined_box_encodings = tf.squeeze( + box_predictions[box_predictor.BOX_ENCODINGS], axis=1) + class_predictions_with_background = tf.squeeze(box_predictions[ + box_predictor.CLASS_PREDICTIONS_WITH_BACKGROUND], axis=1) + + absolute_proposal_boxes = ops.normalized_to_image_coordinates( + proposal_boxes_normalized, image_shape, self._parallel_iterations) + + prediction_dict = { + 'refined_box_encodings': refined_box_encodings, + 'class_predictions_with_background': + class_predictions_with_background, + 'num_proposals': num_proposals, + 'proposal_boxes': absolute_proposal_boxes, + } + return prediction_dict + + def _extract_rpn_feature_maps(self, preprocessed_inputs): + """Extracts RPN features. + + This function extracts two feature maps: a feature map to be directly + fed to a box predictor (to predict location and objectness scores for + proposals) and a feature map from which to crop regions which will then + be sent to the second stage box classifier. + + Args: + preprocessed_inputs: a [batch, height, width, channels] image tensor. + + Returns: + rpn_box_predictor_features: A 4-D float32 tensor with shape + [batch, height, width, depth] to be used for predicting proposal boxes + and corresponding objectness scores. + rpn_features_to_crop: A 4-D float32 tensor with shape + [batch, height, width, depth] representing image features to crop using + the proposals boxes. + anchors: A BoxList representing anchors (for the RPN) in + absolute coordinates. + image_shape: A 1-D tensor representing the input image shape. + """ + image_shape = tf.shape(preprocessed_inputs) + rpn_features_to_crop = self._feature_extractor.extract_proposal_features( + preprocessed_inputs, scope=self.first_stage_feature_extractor_scope) + + feature_map_shape = tf.shape(rpn_features_to_crop) + anchors = self._first_stage_anchor_generator.generate( + [(feature_map_shape[1], feature_map_shape[2])]) + with slim.arg_scope(self._first_stage_box_predictor_arg_scope): + kernel_size = self._first_stage_box_predictor_kernel_size + rpn_box_predictor_features = slim.conv2d( + rpn_features_to_crop, + self._first_stage_box_predictor_depth, + kernel_size=[kernel_size, kernel_size], + rate=self._first_stage_atrous_rate, + activation_fn=tf.nn.relu6) + return (rpn_box_predictor_features, rpn_features_to_crop, + anchors, image_shape) + + def _predict_rpn_proposals(self, rpn_box_predictor_features): + """Adds box predictors to RPN feature map to predict proposals. + + Note resulting tensors will not have been postprocessed. + + Args: + rpn_box_predictor_features: A 4-D float32 tensor with shape + [batch, height, width, depth] to be used for predicting proposal boxes + and corresponding objectness scores. + + Returns: + box_encodings: 3-D float tensor of shape + [batch_size, num_anchors, self._box_coder.code_size] containing + predicted boxes. + objectness_predictions_with_background: 3-D float tensor of shape + [batch_size, num_anchors, 2] containing class + predictions (logits) for each of the anchors. Note that this + tensor *includes* background class predictions (at class index 0). + + Raises: + RuntimeError: if the anchor generator generates anchors corresponding to + multiple feature maps. We currently assume that a single feature map + is generated for the RPN. + """ + num_anchors_per_location = ( + self._first_stage_anchor_generator.num_anchors_per_location()) + if len(num_anchors_per_location) != 1: + raise RuntimeError('anchor_generator is expected to generate anchors ' + 'corresponding to a single feature map.') + box_predictions = self._first_stage_box_predictor.predict( + rpn_box_predictor_features, + num_anchors_per_location[0], + scope=self.first_stage_box_predictor_scope) + + box_encodings = box_predictions[box_predictor.BOX_ENCODINGS] + objectness_predictions_with_background = box_predictions[ + box_predictor.CLASS_PREDICTIONS_WITH_BACKGROUND] + return (tf.squeeze(box_encodings, axis=2), + objectness_predictions_with_background) + + def _remove_invalid_anchors_and_predictions( + self, + box_encodings, + objectness_predictions_with_background, + anchors_boxlist, + clip_window): + """Removes anchors that (partially) fall outside an image. + + Also removes associated box encodings and objectness predictions. + + Args: + box_encodings: 3-D float tensor of shape + [batch_size, num_anchors, self._box_coder.code_size] containing + predicted boxes. + objectness_predictions_with_background: 3-D float tensor of shape + [batch_size, num_anchors, 2] containing class + predictions (logits) for each of the anchors. Note that this + tensor *includes* background class predictions (at class index 0). + anchors_boxlist: A BoxList representing num_anchors anchors (for the RPN) + in absolute coordinates. + clip_window: a 1-D tensor representing the [ymin, xmin, ymax, xmax] + extent of the window to clip/prune to. + + Returns: + box_encodings: 4-D float tensor of shape + [batch_size, num_valid_anchors, self._box_coder.code_size] containing + predicted boxes, where num_valid_anchors <= num_anchors + objectness_predictions_with_background: 2-D float tensor of shape + [batch_size, num_valid_anchors, 2] containing class + predictions (logits) for each of the anchors, where + num_valid_anchors <= num_anchors. Note that this + tensor *includes* background class predictions (at class index 0). + anchors: A BoxList representing num_valid_anchors anchors (for the RPN) in + absolute coordinates. + """ + pruned_anchors_boxlist, keep_indices = box_list_ops.prune_outside_window( + anchors_boxlist, clip_window) + def _batch_gather_kept_indices(predictions_tensor): + return tf.map_fn( + partial(tf.gather, indices=keep_indices), + elems=predictions_tensor, + dtype=tf.float32, + parallel_iterations=self._parallel_iterations, + back_prop=True) + return (_batch_gather_kept_indices(box_encodings), + _batch_gather_kept_indices(objectness_predictions_with_background), + pruned_anchors_boxlist) + + def _flatten_first_two_dimensions(self, inputs): + """Flattens `K-d` tensor along batch dimension to be a `(K-1)-d` tensor. + + Converts `inputs` with shape [A, B, ..., depth] into a tensor of shape + [A * B, ..., depth]. + + Args: + inputs: A float tensor with shape [A, B, ..., depth]. Note that the first + two and last dimensions must be statically defined. + Returns: + A float tensor with shape [A * B, ..., depth] (where the first and last + dimension are statically defined. + """ + inputs_shape = inputs.get_shape().as_list() + flattened_shape = tf.concat([ + [inputs_shape[0]*inputs_shape[1]], tf.shape(inputs)[2:-1], + [inputs_shape[-1]]], 0) + return tf.reshape(inputs, flattened_shape) + + def postprocess(self, prediction_dict): + """Convert prediction tensors to final detections. + + This function converts raw predictions tensors to final detection results. + See base class for output format conventions. Note also that by default, + scores are to be interpreted as logits, but if a score_converter is used, + then scores are remapped (and may thus have a different interpretation). + + If first_stage_only=True, the returned results represent proposals from the + first stage RPN and are padded to have self.max_num_proposals for each + image; otherwise, the results can be interpreted as multiclass detections + from the full two-stage model and are padded to self._max_detections. + + Args: + prediction_dict: a dictionary holding prediction tensors (see the + documentation for the predict method. If first_stage_only=True, we + expect prediction_dict to contain `rpn_box_encodings`, + `rpn_objectness_predictions_with_background`, `rpn_features_to_crop`, + `image_shape`, and `anchors` fields. Otherwise we expect + prediction_dict to additionally contain `refined_box_encodings`, + `class_predictions_with_background`, `num_proposals`, + `proposal_boxes` and, optionally, `mask_predictions` fields. + + Returns: + detections: a dictionary containing the following fields + detection_boxes: [batch, max_detection, 4] + detection_scores: [batch, max_detections] + detection_classes: [batch, max_detections] + (this entry is only created if rpn_mode=False) + num_detections: [batch] + """ + with tf.name_scope('FirstStagePostprocessor'): + image_shape = prediction_dict['image_shape'] + if self._first_stage_only: + proposal_boxes, proposal_scores, num_proposals = self._postprocess_rpn( + prediction_dict['rpn_box_encodings'], + prediction_dict['rpn_objectness_predictions_with_background'], + prediction_dict['anchors'], + image_shape) + return { + 'detection_boxes': proposal_boxes, + 'detection_scores': proposal_scores, + 'num_detections': num_proposals + } + with tf.name_scope('SecondStagePostprocessor'): + mask_predictions = prediction_dict.get(box_predictor.MASK_PREDICTIONS) + detections_dict = self._postprocess_box_classifier( + prediction_dict['refined_box_encodings'], + prediction_dict['class_predictions_with_background'], + prediction_dict['proposal_boxes'], + prediction_dict['num_proposals'], + image_shape, + mask_predictions=mask_predictions) + return detections_dict + + def _postprocess_rpn(self, + rpn_box_encodings_batch, + rpn_objectness_predictions_with_background_batch, + anchors, + image_shape): + """Converts first stage prediction tensors from the RPN to proposals. + + This function decodes the raw RPN predictions, runs non-max suppression + on the result. + + Note that the behavior of this function is slightly modified during + training --- specifically, we stop the gradient from passing through the + proposal boxes and we only return a balanced sampled subset of proposals + with size `second_stage_batch_size`. + + Args: + rpn_box_encodings_batch: A 3-D float32 tensor of shape + [batch_size, num_anchors, self._box_coder.code_size] containing + predicted proposal box encodings. + rpn_objectness_predictions_with_background_batch: A 3-D float tensor of + shape [batch_size, num_anchors, 2] containing objectness predictions + (logits) for each of the anchors with 0 corresponding to background + and 1 corresponding to object. + anchors: A 2-D tensor of shape [num_anchors, 4] representing anchors + for the first stage RPN. Note that `num_anchors` can differ depending + on whether the model is created in training or inference mode. + image_shape: A 1-D tensor representing the input image shape. + + Returns: + proposal_boxes: A float tensor with shape + [batch_size, max_num_proposals, 4] representing the (potentially zero + padded) proposal boxes for all images in the batch. These boxes are + represented as normalized coordinates. + proposal_scores: A float tensor with shape + [batch_size, max_num_proposals] representing the (potentially zero + padded) proposal objectness scores for all images in the batch. + num_proposals: A Tensor of type `int32`. A 1-D tensor of shape [batch] + representing the number of proposals predicted for each image in + the batch. + """ + clip_window = tf.to_float(tf.stack([0, 0, image_shape[1], image_shape[2]])) + if self._is_training: + (groundtruth_boxlists, groundtruth_classes_with_background_list + ) = self._format_groundtruth_data(image_shape) + + proposal_boxes_list = [] + proposal_scores_list = [] + num_proposals_list = [] + for (batch_index, + (rpn_box_encodings, + rpn_objectness_predictions_with_background)) in enumerate(zip( + tf.unstack(rpn_box_encodings_batch), + tf.unstack(rpn_objectness_predictions_with_background_batch))): + decoded_boxes = self._box_coder.decode( + rpn_box_encodings, box_list.BoxList(anchors)) + objectness_scores = tf.unstack( + tf.nn.softmax(rpn_objectness_predictions_with_background), axis=1)[1] + proposal_boxlist = post_processing.multiclass_non_max_suppression( + tf.expand_dims(decoded_boxes.get(), 1), + tf.expand_dims(objectness_scores, 1), + self._first_stage_nms_score_threshold, + self._first_stage_nms_iou_threshold, self._first_stage_max_proposals, + clip_window=clip_window) + + if self._is_training: + proposal_boxlist.set(tf.stop_gradient(proposal_boxlist.get())) + if not self._hard_example_miner: + proposal_boxlist = self._sample_box_classifier_minibatch( + proposal_boxlist, groundtruth_boxlists[batch_index], + groundtruth_classes_with_background_list[batch_index]) + + normalized_proposals = box_list_ops.to_normalized_coordinates( + proposal_boxlist, image_shape[1], image_shape[2], + check_range=False) + + # pad proposals to max_num_proposals + padded_proposals = box_list_ops.pad_or_clip_box_list( + normalized_proposals, num_boxes=self.max_num_proposals) + proposal_boxes_list.append(padded_proposals.get()) + proposal_scores_list.append( + padded_proposals.get_field(fields.BoxListFields.scores)) + num_proposals_list.append(tf.minimum(normalized_proposals.num_boxes(), + self.max_num_proposals)) + + return (tf.stack(proposal_boxes_list), tf.stack(proposal_scores_list), + tf.stack(num_proposals_list)) + + def _format_groundtruth_data(self, image_shape): + """Helper function for preparing groundtruth data for target assignment. + + In order to be consistent with the model.DetectionModel interface, + groundtruth boxes are specified in normalized coordinates and classes are + specified as label indices with no assumed background category. To prepare + for target assignment, we: + 1) convert boxes to absolute coordinates, + 2) add a background class at class index 0 + + Args: + image_shape: A 1-D int32 tensor of shape [4] representing the shape of the + input image batch. + + Returns: + groundtruth_boxlists: A list of BoxLists containing (absolute) coordinates + of the groundtruth boxes. + groundtruth_classes_with_background_list: A list of 2-D one-hot + (or k-hot) tensors of shape [num_boxes, num_classes+1] containing the + class targets with the 0th index assumed to map to the background class. + """ + groundtruth_boxlists = [ + box_list_ops.to_absolute_coordinates( + box_list.BoxList(boxes), image_shape[1], image_shape[2]) + for boxes in self.groundtruth_lists(fields.BoxListFields.boxes)] + groundtruth_classes_with_background_list = [ + tf.to_float( + tf.pad(one_hot_encoding, [[0, 0], [1, 0]], mode='CONSTANT')) + for one_hot_encoding in self.groundtruth_lists( + fields.BoxListFields.classes)] + return groundtruth_boxlists, groundtruth_classes_with_background_list + + def _sample_box_classifier_minibatch(self, + proposal_boxlist, + groundtruth_boxlist, + groundtruth_classes_with_background): + """Samples a mini-batch of proposals to be sent to the box classifier. + + Helper function for self._postprocess_rpn. + + Args: + proposal_boxlist: A BoxList containing K proposal boxes in absolute + coordinates. + groundtruth_boxlist: A Boxlist containing N groundtruth object boxes in + absolute coordinates. + groundtruth_classes_with_background: A tensor with shape + `[N, self.num_classes + 1]` representing groundtruth classes. The + classes are assumed to be k-hot encoded, and include background as the + zero-th class. + + Returns: + a BoxList contained sampled proposals. + """ + (cls_targets, cls_weights, _, _, _) = self._detector_target_assigner.assign( + proposal_boxlist, groundtruth_boxlist, + groundtruth_classes_with_background) + # Selects all boxes as candidates if none of them is selected according + # to cls_weights. This could happen as boxes within certain IOU ranges + # are ignored. If triggered, the selected boxes will still be ignored + # during loss computation. + cls_weights += tf.to_float(tf.equal(tf.reduce_sum(cls_weights), 0)) + positive_indicator = tf.greater(tf.argmax(cls_targets, axis=1), 0) + sampled_indices = self._second_stage_sampler.subsample( + tf.cast(cls_weights, tf.bool), + self._second_stage_batch_size, + positive_indicator) + return box_list_ops.boolean_mask(proposal_boxlist, sampled_indices) + + def _compute_second_stage_input_feature_maps(self, features_to_crop, + proposal_boxes_normalized): + """Crops to a set of proposals from the feature map for a batch of images. + + Helper function for self._postprocess_rpn. This function calls + `tf.image.crop_and_resize` to create the feature map to be passed to the + second stage box classifier for each proposal. + + Args: + features_to_crop: A float32 tensor with shape + [batch_size, height, width, depth] + proposal_boxes_normalized: A float32 tensor with shape [batch_size, + num_proposals, box_code_size] containing proposal boxes in + normalized coordinates. + + Returns: + A float32 tensor with shape [K, new_height, new_width, depth]. + """ + def get_box_inds(proposals): + proposals_shape = proposals.get_shape().as_list() + if any(dim is None for dim in proposals_shape): + proposals_shape = tf.shape(proposals) + ones_mat = tf.ones(proposals_shape[:2], dtype=tf.int32) + multiplier = tf.expand_dims( + tf.range(start=0, limit=proposals_shape[0]), 1) + return tf.reshape(ones_mat * multiplier, [-1]) + + cropped_regions = tf.image.crop_and_resize( + features_to_crop, + self._flatten_first_two_dimensions(proposal_boxes_normalized), + get_box_inds(proposal_boxes_normalized), + (self._initial_crop_size, self._initial_crop_size)) + return slim.max_pool2d( + cropped_regions, + [self._maxpool_kernel_size, self._maxpool_kernel_size], + stride=self._maxpool_stride) + + def _postprocess_box_classifier(self, + refined_box_encodings, + class_predictions_with_background, + proposal_boxes, + num_proposals, + image_shape, + mask_predictions=None, + mask_threshold=0.5): + """Converts predictions from the second stage box classifier to detections. + + Args: + refined_box_encodings: a 3-D tensor with shape + [total_num_padded_proposals, num_classes, 4] representing predicted + (final) refined box encodings. + class_predictions_with_background: a 3-D tensor with shape + [total_num_padded_proposals, num_classes + 1] containing class + predictions (logits) for each of the proposals. Note that this tensor + *includes* background class predictions (at class index 0). + proposal_boxes: [batch_size, self.max_num_proposals, 4] representing + decoded proposal bounding boxes. + num_proposals: A Tensor of type `int32`. A 1-D tensor of shape [batch] + representing the number of proposals predicted for each image in + the batch. + image_shape: a 1-D tensor representing the input image shape. + mask_predictions: (optional) a 4-D tensor with shape + [total_num_padded_proposals, num_classes, mask_height, mask_width] + containing instance mask predictions. + mask_threshold: a scalar threshold determining which mask values are + rounded to 0 or 1. + + Returns: + A dictionary containing: + `detection_boxes`: [batch, max_detection, 4] + `detection_scores`: [batch, max_detections] + `detection_classes`: [batch, max_detections] + `num_detections`: [batch] + `detection_masks`: + (optional) [batch, max_detections, mask_height, mask_width] + """ + refined_box_encodings_batch = tf.reshape(refined_box_encodings, + [-1, self.max_num_proposals, + self.num_classes, + self._box_coder.code_size]) + class_predictions_with_background_batch = tf.reshape( + class_predictions_with_background, + [-1, self.max_num_proposals, self.num_classes + 1] + ) + refined_decoded_boxes_batch = self._batch_decode_refined_boxes( + refined_box_encodings_batch, proposal_boxes) + class_predictions_with_background_batch = ( + self._second_stage_score_conversion_fn( + class_predictions_with_background_batch)) + class_predictions_batch = tf.reshape( + tf.slice(class_predictions_with_background_batch, + [0, 0, 1], [-1, -1, -1]), + [-1, self.max_num_proposals, self.num_classes]) + clip_window = tf.to_float(tf.stack([0, 0, image_shape[1], image_shape[2]])) + + mask_predictions_batch = None + if mask_predictions is not None: + mask_height = mask_predictions.shape[2].value + mask_width = mask_predictions.shape[3].value + mask_predictions_batch = tf.reshape( + mask_predictions, [-1, self.max_num_proposals, + self.num_classes, mask_height, mask_width]) + detections = self._second_stage_nms_fn( + refined_decoded_boxes_batch, + class_predictions_batch, + clip_window=clip_window, + change_coordinate_frame=True, + num_valid_boxes=num_proposals, + masks=mask_predictions_batch) + if mask_predictions is not None: + detections['detection_masks'] = tf.to_float( + tf.greater_equal(detections['detection_masks'], mask_threshold)) + return detections + + def _batch_decode_refined_boxes(self, refined_box_encodings, proposal_boxes): + """Decode tensor of refined box encodings. + + Args: + refined_box_encodings: a 3-D tensor with shape + [batch_size, max_num_proposals, num_classes, self._box_coder.code_size] + representing predicted (final) refined box encodings. + proposal_boxes: [batch_size, self.max_num_proposals, 4] representing + decoded proposal bounding boxes. + + Returns: + refined_box_predictions: a [batch_size, max_num_proposals, num_classes, 4] + float tensor representing (padded) refined bounding box predictions + (for each image in batch, proposal and class). + """ + tiled_proposal_boxes = tf.tile( + tf.expand_dims(proposal_boxes, 2), [1, 1, self.num_classes, 1]) + tiled_proposals_boxlist = box_list.BoxList( + tf.reshape(tiled_proposal_boxes, [-1, 4])) + decoded_boxes = self._box_coder.decode( + tf.reshape(refined_box_encodings, [-1, self._box_coder.code_size]), + tiled_proposals_boxlist) + return tf.reshape(decoded_boxes.get(), + [-1, self.max_num_proposals, self.num_classes, 4]) + + def loss(self, prediction_dict, scope=None): + """Compute scalar loss tensors given prediction tensors. + + If first_stage_only=True, only RPN related losses are computed (i.e., + `rpn_localization_loss` and `rpn_objectness_loss`). Otherwise all + losses are computed. + + Args: + prediction_dict: a dictionary holding prediction tensors (see the + documentation for the predict method. If first_stage_only=True, we + expect prediction_dict to contain `rpn_box_encodings`, + `rpn_objectness_predictions_with_background`, `rpn_features_to_crop`, + `image_shape`, and `anchors` fields. Otherwise we expect + prediction_dict to additionally contain `refined_box_encodings`, + `class_predictions_with_background`, `num_proposals`, and + `proposal_boxes` fields. + scope: Optional scope name. + + Returns: + a dictionary mapping loss keys (`first_stage_localization_loss`, + `first_stage_objectness_loss`, 'second_stage_localization_loss', + 'second_stage_classification_loss') to scalar tensors representing + corresponding loss values. + """ + with tf.name_scope(scope, 'Loss', prediction_dict.values()): + (groundtruth_boxlists, groundtruth_classes_with_background_list + ) = self._format_groundtruth_data(prediction_dict['image_shape']) + loss_dict = self._loss_rpn( + prediction_dict['rpn_box_encodings'], + prediction_dict['rpn_objectness_predictions_with_background'], + prediction_dict['anchors'], + groundtruth_boxlists, + groundtruth_classes_with_background_list) + if not self._first_stage_only: + loss_dict.update( + self._loss_box_classifier( + prediction_dict['refined_box_encodings'], + prediction_dict['class_predictions_with_background'], + prediction_dict['proposal_boxes'], + prediction_dict['num_proposals'], + groundtruth_boxlists, + groundtruth_classes_with_background_list)) + return loss_dict + + def _loss_rpn(self, + rpn_box_encodings, + rpn_objectness_predictions_with_background, + anchors, + groundtruth_boxlists, + groundtruth_classes_with_background_list): + """Computes scalar RPN loss tensors. + + Uses self._proposal_target_assigner to obtain regression and classification + targets for the first stage RPN, samples a "minibatch" of anchors to + participate in the loss computation, and returns the RPN losses. + + Args: + rpn_box_encodings: A 4-D float tensor of shape + [batch_size, num_anchors, self._box_coder.code_size] containing + predicted proposal box encodings. + rpn_objectness_predictions_with_background: A 2-D float tensor of shape + [batch_size, num_anchors, 2] containing objectness predictions + (logits) for each of the anchors with 0 corresponding to background + and 1 corresponding to object. + anchors: A 2-D tensor of shape [num_anchors, 4] representing anchors + for the first stage RPN. Note that `num_anchors` can differ depending + on whether the model is created in training or inference mode. + groundtruth_boxlists: A list of BoxLists containing coordinates of the + groundtruth boxes. + groundtruth_classes_with_background_list: A list of 2-D one-hot + (or k-hot) tensors of shape [num_boxes, num_classes+1] containing the + class targets with the 0th index assumed to map to the background class. + + Returns: + a dictionary mapping loss keys (`first_stage_localization_loss`, + `first_stage_objectness_loss`) to scalar tensors representing + corresponding loss values. + """ + with tf.name_scope('RPNLoss'): + (batch_cls_targets, batch_cls_weights, batch_reg_targets, + batch_reg_weights, _) = target_assigner.batch_assign_targets( + self._proposal_target_assigner, box_list.BoxList(anchors), + groundtruth_boxlists, len(groundtruth_boxlists)*[None]) + batch_cls_targets = tf.squeeze(batch_cls_targets, axis=2) + + def _minibatch_subsample_fn(inputs): + cls_targets, cls_weights = inputs + return self._first_stage_sampler.subsample( + tf.cast(cls_weights, tf.bool), + self._first_stage_minibatch_size, tf.cast(cls_targets, tf.bool)) + batch_sampled_indices = tf.to_float(tf.map_fn( + _minibatch_subsample_fn, + [batch_cls_targets, batch_cls_weights], + dtype=tf.bool, + parallel_iterations=self._parallel_iterations, + back_prop=True)) + + # Normalize by number of examples in sampled minibatch + normalizer = tf.reduce_sum(batch_sampled_indices, axis=1) + batch_one_hot_targets = tf.one_hot( + tf.to_int32(batch_cls_targets), depth=2) + sampled_reg_indices = tf.multiply(batch_sampled_indices, + batch_reg_weights) + + localization_losses = self._first_stage_localization_loss( + rpn_box_encodings, batch_reg_targets, weights=sampled_reg_indices) + objectness_losses = self._first_stage_objectness_loss( + rpn_objectness_predictions_with_background, + batch_one_hot_targets, weights=batch_sampled_indices) + localization_loss = tf.reduce_mean( + tf.reduce_sum(localization_losses, axis=1) / normalizer) + objectness_loss = tf.reduce_mean( + tf.reduce_sum(objectness_losses, axis=1) / normalizer) + loss_dict = { + 'first_stage_localization_loss': + self._first_stage_loc_loss_weight * localization_loss, + 'first_stage_objectness_loss': + self._first_stage_obj_loss_weight * objectness_loss, + } + return loss_dict + + def _loss_box_classifier(self, + refined_box_encodings, + class_predictions_with_background, + proposal_boxes, + num_proposals, + groundtruth_boxlists, + groundtruth_classes_with_background_list): + """Computes scalar box classifier loss tensors. + + Uses self._detector_target_assigner to obtain regression and classification + targets for the second stage box classifier, optionally performs + hard mining, and returns losses. All losses are computed independently + for each image and then averaged across the batch. + + This function assumes that the proposal boxes in the "padded" regions are + actually zero (and thus should not be matched to). + + Args: + refined_box_encodings: a 3-D tensor with shape + [total_num_proposals, num_classes, box_coder.code_size] representing + predicted (final) refined box encodings. + class_predictions_with_background: a 3-D tensor with shape + [total_num_proposals, num_classes + 1] containing class + predictions (logits) for each of the anchors. Note that this tensor + *includes* background class predictions (at class index 0). + proposal_boxes: [batch_size, self.max_num_proposals, 4] representing + decoded proposal bounding boxes. + num_proposals: A Tensor of type `int32`. A 1-D tensor of shape [batch] + representing the number of proposals predicted for each image in + the batch. + groundtruth_boxlists: a list of BoxLists containing coordinates of the + groundtruth boxes. + groundtruth_classes_with_background_list: a list of 2-D one-hot + (or k-hot) tensors of shape [num_boxes, num_classes + 1] containing the + class targets with the 0th index assumed to map to the background class. + + Returns: + a dictionary mapping loss keys ('second_stage_localization_loss', + 'second_stage_classification_loss') to scalar tensors representing + corresponding loss values. + """ + with tf.name_scope('BoxClassifierLoss'): + paddings_indicator = self._padded_batched_proposals_indicator( + num_proposals, self.max_num_proposals) + proposal_boxlists = [ + box_list.BoxList(proposal_boxes_single_image) + for proposal_boxes_single_image in tf.unstack(proposal_boxes)] + batch_size = len(proposal_boxlists) + + num_proposals_or_one = tf.to_float(tf.expand_dims( + tf.maximum(num_proposals, tf.ones_like(num_proposals)), 1)) + normalizer = tf.tile(num_proposals_or_one, + [1, self.max_num_proposals]) * batch_size + + (batch_cls_targets_with_background, batch_cls_weights, batch_reg_targets, + batch_reg_weights, _) = target_assigner.batch_assign_targets( + self._detector_target_assigner, proposal_boxlists, + groundtruth_boxlists, groundtruth_classes_with_background_list) + + # We only predict refined location encodings for the non background + # classes, but we now pad it to make it compatible with the class + # predictions + flat_cls_targets_with_background = tf.reshape( + batch_cls_targets_with_background, + [batch_size * self.max_num_proposals, -1]) + refined_box_encodings_with_background = tf.pad( + refined_box_encodings, [[0, 0], [1, 0], [0, 0]]) + refined_box_encodings_masked_by_class_targets = tf.boolean_mask( + refined_box_encodings_with_background, + tf.greater(flat_cls_targets_with_background, 0)) + reshaped_refined_box_encodings = tf.reshape( + refined_box_encodings_masked_by_class_targets, + [batch_size, -1, 4]) + + second_stage_loc_losses = self._second_stage_localization_loss( + reshaped_refined_box_encodings, + batch_reg_targets, weights=batch_reg_weights) / normalizer + second_stage_cls_losses = self._second_stage_classification_loss( + class_predictions_with_background, + batch_cls_targets_with_background, + weights=batch_cls_weights) / normalizer + second_stage_loc_loss = tf.reduce_sum( + tf.boolean_mask(second_stage_loc_losses, paddings_indicator)) + second_stage_cls_loss = tf.reduce_sum( + tf.boolean_mask(second_stage_cls_losses, paddings_indicator)) + + if self._hard_example_miner: + (second_stage_loc_loss, second_stage_cls_loss + ) = self._unpad_proposals_and_apply_hard_mining( + proposal_boxlists, second_stage_loc_losses, + second_stage_cls_losses, num_proposals) + loss_dict = { + 'second_stage_localization_loss': + (self._second_stage_loc_loss_weight * second_stage_loc_loss), + 'second_stage_classification_loss': + (self._second_stage_cls_loss_weight * second_stage_cls_loss), + } + return loss_dict + + def _padded_batched_proposals_indicator(self, + num_proposals, + max_num_proposals): + """Creates indicator matrix of non-pad elements of padded batch proposals. + + Args: + num_proposals: Tensor of type tf.int32 with shape [batch_size]. + max_num_proposals: Maximum number of proposals per image (integer). + + Returns: + A Tensor of type tf.bool with shape [batch_size, max_num_proposals]. + """ + batch_size = tf.size(num_proposals) + tiled_num_proposals = tf.tile( + tf.expand_dims(num_proposals, 1), [1, max_num_proposals]) + tiled_proposal_index = tf.tile( + tf.expand_dims(tf.range(max_num_proposals), 0), [batch_size, 1]) + return tf.greater(tiled_num_proposals, tiled_proposal_index) + + def _unpad_proposals_and_apply_hard_mining(self, + proposal_boxlists, + second_stage_loc_losses, + second_stage_cls_losses, + num_proposals): + """Unpads proposals and applies hard mining. + + Args: + proposal_boxlists: A list of `batch_size` BoxLists each representing + `self.max_num_proposals` representing decoded proposal bounding boxes + for each image. + second_stage_loc_losses: A Tensor of type `float32`. A tensor of shape + `[batch_size, self.max_num_proposals]` representing per-anchor + second stage localization loss values. + second_stage_cls_losses: A Tensor of type `float32`. A tensor of shape + `[batch_size, self.max_num_proposals]` representing per-anchor + second stage classification loss values. + num_proposals: A Tensor of type `int32`. A 1-D tensor of shape [batch] + representing the number of proposals predicted for each image in + the batch. + + Returns: + second_stage_loc_loss: A scalar float32 tensor representing the second + stage localization loss. + second_stage_cls_loss: A scalar float32 tensor representing the second + stage classification loss. + """ + for (proposal_boxlist, single_image_loc_loss, single_image_cls_loss, + single_image_num_proposals) in zip( + proposal_boxlists, + tf.unstack(second_stage_loc_losses), + tf.unstack(second_stage_cls_losses), + tf.unstack(num_proposals)): + proposal_boxlist = box_list.BoxList( + tf.slice(proposal_boxlist.get(), + [0, 0], [single_image_num_proposals, -1])) + single_image_loc_loss = tf.slice(single_image_loc_loss, + [0], [single_image_num_proposals]) + single_image_cls_loss = tf.slice(single_image_cls_loss, + [0], [single_image_num_proposals]) + return self._hard_example_miner( + location_losses=tf.expand_dims(single_image_loc_loss, 0), + cls_losses=tf.expand_dims(single_image_cls_loss, 0), + decoded_boxlist_list=[proposal_boxlist]) + + def restore_fn(self, checkpoint_path, from_detection_checkpoint=True): + """Returns callable for loading a checkpoint into the tensorflow graph. + + Args: + checkpoint_path: path to checkpoint to restore. + from_detection_checkpoint: whether to restore from a detection checkpoint + (with compatible variable names) or to restore from a classification + checkpoint for initialization prior to training. Note that when + from_detection_checkpoint=True, the current implementation only + supports restoration from an (exactly) identical model (with exception + of the num_classes parameter). + + Returns: + a callable which takes a tf.Session as input and loads a checkpoint when + run. + """ + if not from_detection_checkpoint: + return self._feature_extractor.restore_from_classification_checkpoint_fn( + checkpoint_path, + self.first_stage_feature_extractor_scope, + self.second_stage_feature_extractor_scope) + + variables_to_restore = tf.global_variables() + variables_to_restore.append(slim.get_or_create_global_step()) + # Only load feature extractor variables to be consistent with loading from + # a classification checkpoint. + first_stage_variables = tf.contrib.framework.filter_variables( + variables_to_restore, + include_patterns=[self.first_stage_feature_extractor_scope, + self.second_stage_feature_extractor_scope]) + + saver = tf.train.Saver(first_stage_variables) + + def restore(sess): + saver.restore(sess, checkpoint_path) + return restore diff --git a/object_detection/meta_architectures/faster_rcnn_meta_arch_test.py b/object_detection/meta_architectures/faster_rcnn_meta_arch_test.py new file mode 100644 index 000000000..527e24b4e --- /dev/null +++ b/object_detection/meta_architectures/faster_rcnn_meta_arch_test.py @@ -0,0 +1,84 @@ +# 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.meta_architectures.faster_rcnn_meta_arch.""" + +import tensorflow as tf + +from object_detection.meta_architectures import faster_rcnn_meta_arch_test_lib + + +class FasterRCNNMetaArchTest( + faster_rcnn_meta_arch_test_lib.FasterRCNNMetaArchTestBase): + + def test_postprocess_second_stage_only_inference_mode_with_masks(self): + model = self._build_model( + is_training=False, first_stage_only=False, second_stage_batch_size=6) + + batch_size = 2 + total_num_padded_proposals = batch_size * model.max_num_proposals + proposal_boxes = tf.constant( + [[[1, 1, 2, 3], + [0, 0, 1, 1], + [.5, .5, .6, .6], + 4*[0], 4*[0], 4*[0], 4*[0], 4*[0]], + [[2, 3, 6, 8], + [1, 2, 5, 3], + 4*[0], 4*[0], 4*[0], 4*[0], 4*[0], 4*[0]]], dtype=tf.float32) + num_proposals = tf.constant([3, 2], dtype=tf.int32) + refined_box_encodings = tf.zeros( + [total_num_padded_proposals, model.num_classes, 4], dtype=tf.float32) + class_predictions_with_background = tf.ones( + [total_num_padded_proposals, model.num_classes+1], dtype=tf.float32) + image_shape = tf.constant([batch_size, 36, 48, 3], dtype=tf.int32) + + mask_height = 2 + mask_width = 2 + mask_predictions = .6 * tf.ones( + [total_num_padded_proposals, model.num_classes, + mask_height, mask_width], dtype=tf.float32) + exp_detection_masks = [[[[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]], + [[0, 0], [0, 0]]]] + + detections = model.postprocess({ + 'refined_box_encodings': refined_box_encodings, + 'class_predictions_with_background': class_predictions_with_background, + 'num_proposals': num_proposals, + 'proposal_boxes': proposal_boxes, + 'image_shape': image_shape, + 'mask_predictions': mask_predictions + }) + with self.test_session() as sess: + detections_out = sess.run(detections) + self.assertAllEqual(detections_out['detection_boxes'].shape, [2, 5, 4]) + self.assertAllClose(detections_out['detection_scores'], + [[1, 1, 1, 1, 1], [1, 1, 1, 1, 0]]) + self.assertAllClose(detections_out['detection_classes'], + [[0, 0, 0, 1, 1], [0, 0, 1, 1, 0]]) + self.assertAllClose(detections_out['num_detections'], [5, 4]) + self.assertAllClose(detections_out['detection_masks'], + exp_detection_masks) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/meta_architectures/faster_rcnn_meta_arch_test_lib.py b/object_detection/meta_architectures/faster_rcnn_meta_arch_test_lib.py new file mode 100644 index 000000000..17e1f62b2 --- /dev/null +++ b/object_detection/meta_architectures/faster_rcnn_meta_arch_test_lib.py @@ -0,0 +1,1035 @@ +# 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.meta_architectures.faster_rcnn_meta_arch.""" +import numpy as np +import tensorflow as tf +from google.protobuf import text_format +from object_detection.anchor_generators import grid_anchor_generator +from object_detection.builders import box_predictor_builder +from object_detection.builders import hyperparams_builder +from object_detection.builders import post_processing_builder +from object_detection.core import losses +from object_detection.meta_architectures import faster_rcnn_meta_arch +from object_detection.protos import box_predictor_pb2 +from object_detection.protos import hyperparams_pb2 +from object_detection.protos import post_processing_pb2 + +slim = tf.contrib.slim +BOX_CODE_SIZE = 4 + + +class FakeFasterRCNNFeatureExtractor( + faster_rcnn_meta_arch.FasterRCNNFeatureExtractor): + """Fake feature extracture to use in tests.""" + + def __init__(self): + super(FakeFasterRCNNFeatureExtractor, self).__init__( + is_training=False, + first_stage_features_stride=32, + reuse_weights=None, + weight_decay=0.0) + + def preprocess(self, resized_inputs): + return tf.identity(resized_inputs) + + def _extract_proposal_features(self, preprocessed_inputs, scope): + with tf.variable_scope('mock_model'): + return 0 * slim.conv2d(preprocessed_inputs, + num_outputs=3, kernel_size=1, scope='layer1') + + def _extract_box_classifier_features(self, proposal_feature_maps, scope): + with tf.variable_scope('mock_model'): + return 0 * slim.conv2d(proposal_feature_maps, + num_outputs=3, kernel_size=1, scope='layer2') + + +class FasterRCNNMetaArchTestBase(tf.test.TestCase): + """Base class to test Faster R-CNN and R-FCN meta architectures.""" + + def _build_arg_scope_with_hyperparams(self, + hyperparams_text_proto, + is_training): + hyperparams = hyperparams_pb2.Hyperparams() + text_format.Merge(hyperparams_text_proto, hyperparams) + return hyperparams_builder.build(hyperparams, is_training=is_training) + + def _get_second_stage_box_predictor_text_proto(self): + box_predictor_text_proto = """ + mask_rcnn_box_predictor { + fc_hyperparams { + op: FC + activation: NONE + regularizer { + l2_regularizer { + weight: 0.0005 + } + } + initializer { + variance_scaling_initializer { + factor: 1.0 + uniform: true + mode: FAN_AVG + } + } + } + } + """ + return box_predictor_text_proto + + def _get_second_stage_box_predictor(self, num_classes, is_training): + box_predictor_proto = box_predictor_pb2.BoxPredictor() + text_format.Merge(self._get_second_stage_box_predictor_text_proto(), + box_predictor_proto) + return box_predictor_builder.build( + hyperparams_builder.build, + box_predictor_proto, + num_classes=num_classes, + is_training=is_training) + + def _get_model(self, box_predictor, **common_kwargs): + return faster_rcnn_meta_arch.FasterRCNNMetaArch( + initial_crop_size=3, + maxpool_kernel_size=1, + maxpool_stride=1, + second_stage_mask_rcnn_box_predictor=box_predictor, + **common_kwargs) + + def _build_model(self, + is_training, + first_stage_only, + second_stage_batch_size, + first_stage_max_proposals=8, + num_classes=2, + hard_mining=False): + + def image_resizer_fn(image): + return tf.identity(image) + + # anchors in this test are designed so that a subset of anchors are inside + # the image and a subset of anchors are outside. + first_stage_anchor_scales = (0.001, 0.005, 0.1) + first_stage_anchor_aspect_ratios = (0.5, 1.0, 2.0) + first_stage_anchor_strides = (1, 1) + first_stage_anchor_generator = grid_anchor_generator.GridAnchorGenerator( + first_stage_anchor_scales, + first_stage_anchor_aspect_ratios, + anchor_stride=first_stage_anchor_strides) + + fake_feature_extractor = FakeFasterRCNNFeatureExtractor() + + first_stage_box_predictor_hyperparams_text_proto = """ + op: CONV + activation: RELU + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + } + } + """ + first_stage_box_predictor_arg_scope = ( + self._build_arg_scope_with_hyperparams( + first_stage_box_predictor_hyperparams_text_proto, is_training)) + + first_stage_box_predictor_kernel_size = 3 + first_stage_atrous_rate = 1 + first_stage_box_predictor_depth = 512 + first_stage_minibatch_size = 3 + first_stage_positive_balance_fraction = .5 + + first_stage_nms_score_threshold = -1.0 + first_stage_nms_iou_threshold = 1.0 + first_stage_max_proposals = first_stage_max_proposals + + first_stage_localization_loss_weight = 1.0 + first_stage_objectness_loss_weight = 1.0 + + post_processing_text_proto = """ + batch_non_max_suppression { + score_threshold: -20.0 + iou_threshold: 1.0 + max_detections_per_class: 5 + max_total_detections: 5 + } + """ + post_processing_config = post_processing_pb2.PostProcessing() + text_format.Merge(post_processing_text_proto, post_processing_config) + second_stage_non_max_suppression_fn, _ = post_processing_builder.build( + post_processing_config) + second_stage_balance_fraction = 1.0 + + second_stage_score_conversion_fn = tf.identity + second_stage_localization_loss_weight = 1.0 + second_stage_classification_loss_weight = 1.0 + + hard_example_miner = None + if hard_mining: + hard_example_miner = losses.HardExampleMiner( + num_hard_examples=1, + iou_threshold=0.99, + loss_type='both', + cls_loss_weight=second_stage_classification_loss_weight, + loc_loss_weight=second_stage_localization_loss_weight, + max_negatives_per_positive=None) + + common_kwargs = { + 'is_training': is_training, + 'num_classes': num_classes, + 'image_resizer_fn': image_resizer_fn, + 'feature_extractor': fake_feature_extractor, + 'first_stage_only': first_stage_only, + 'first_stage_anchor_generator': first_stage_anchor_generator, + 'first_stage_atrous_rate': first_stage_atrous_rate, + 'first_stage_box_predictor_arg_scope': + first_stage_box_predictor_arg_scope, + '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_positive_balance_fraction': + first_stage_positive_balance_fraction, + 'first_stage_nms_score_threshold': first_stage_nms_score_threshold, + 'first_stage_nms_iou_threshold': first_stage_nms_iou_threshold, + 'first_stage_max_proposals': first_stage_max_proposals, + 'first_stage_localization_loss_weight': + first_stage_localization_loss_weight, + 'first_stage_objectness_loss_weight': + first_stage_objectness_loss_weight, + 'second_stage_batch_size': second_stage_batch_size, + 'second_stage_balance_fraction': second_stage_balance_fraction, + '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_weight': + second_stage_classification_loss_weight, + 'hard_example_miner': hard_example_miner} + + return self._get_model(self._get_second_stage_box_predictor( + num_classes=num_classes, is_training=is_training), **common_kwargs) + + def test_predict_gives_correct_shapes_in_inference_mode_first_stage_only( + self): + test_graph = tf.Graph() + with test_graph.as_default(): + model = self._build_model( + is_training=False, first_stage_only=True, second_stage_batch_size=2) + batch_size = 2 + height = 10 + width = 12 + input_image_shape = (batch_size, height, width, 3) + + preprocessed_inputs = tf.placeholder(dtype=tf.float32, + shape=(batch_size, None, None, 3)) + prediction_dict = model.predict(preprocessed_inputs) + + # In inference mode, anchors are clipped to the image window, but not + # pruned. Since MockFasterRCNN.extract_proposal_features returns a + # tensor with the same shape as its input, the expected number of anchors + # is height * width * the number of anchors per location (i.e. 3x3). + expected_num_anchors = height * width * 3 * 3 + expected_output_keys = set([ + 'rpn_box_predictor_features', 'rpn_features_to_crop', 'image_shape', + 'rpn_box_encodings', 'rpn_objectness_predictions_with_background', + 'anchors']) + expected_output_shapes = { + 'rpn_box_predictor_features': (batch_size, height, width, 512), + 'rpn_features_to_crop': (batch_size, height, width, 3), + 'rpn_box_encodings': (batch_size, expected_num_anchors, 4), + 'rpn_objectness_predictions_with_background': + (batch_size, expected_num_anchors, 2), + 'anchors': (expected_num_anchors, 4) + } + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + prediction_out = sess.run(prediction_dict, + feed_dict={ + preprocessed_inputs: + np.zeros(input_image_shape) + }) + + self.assertEqual(set(prediction_out.keys()), expected_output_keys) + + self.assertAllEqual(prediction_out['image_shape'], input_image_shape) + for output_key, expected_shape in expected_output_shapes.iteritems(): + self.assertAllEqual(prediction_out[output_key].shape, expected_shape) + + # Check that anchors are clipped to window. + anchors = prediction_out['anchors'] + self.assertTrue(np.all(np.greater_equal(anchors, 0))) + self.assertTrue(np.all(np.less_equal(anchors[:, 0], height))) + self.assertTrue(np.all(np.less_equal(anchors[:, 1], width))) + self.assertTrue(np.all(np.less_equal(anchors[:, 2], height))) + self.assertTrue(np.all(np.less_equal(anchors[:, 3], width))) + + def test_predict_gives_valid_anchors_in_training_mode_first_stage_only(self): + test_graph = tf.Graph() + with test_graph.as_default(): + model = self._build_model( + is_training=True, first_stage_only=True, second_stage_batch_size=2) + batch_size = 2 + height = 10 + width = 12 + input_image_shape = (batch_size, height, width, 3) + preprocessed_inputs = tf.placeholder(dtype=tf.float32, + shape=(batch_size, None, None, 3)) + prediction_dict = model.predict(preprocessed_inputs) + + expected_output_keys = set([ + 'rpn_box_predictor_features', 'rpn_features_to_crop', 'image_shape', + 'rpn_box_encodings', 'rpn_objectness_predictions_with_background', + 'anchors']) + # At training time, anchors that exceed image bounds are pruned. Thus + # the `expected_num_anchors` in the above inference mode test is now + # a strict upper bound on the number of anchors. + num_anchors_strict_upper_bound = height * width * 3 * 3 + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + prediction_out = sess.run(prediction_dict, + feed_dict={ + preprocessed_inputs: + np.zeros(input_image_shape) + }) + + self.assertEqual(set(prediction_out.keys()), expected_output_keys) + self.assertAllEqual(prediction_out['image_shape'], input_image_shape) + + # Check that anchors have less than the upper bound and + # are clipped to window. + anchors = prediction_out['anchors'] + self.assertTrue(len(anchors.shape) == 2 and anchors.shape[1] == 4) + num_anchors_out = anchors.shape[0] + self.assertTrue(num_anchors_out < num_anchors_strict_upper_bound) + + self.assertTrue(np.all(np.greater_equal(anchors, 0))) + self.assertTrue(np.all(np.less_equal(anchors[:, 0], height))) + self.assertTrue(np.all(np.less_equal(anchors[:, 1], width))) + self.assertTrue(np.all(np.less_equal(anchors[:, 2], height))) + self.assertTrue(np.all(np.less_equal(anchors[:, 3], width))) + + self.assertAllEqual(prediction_out['rpn_box_encodings'].shape, + (batch_size, num_anchors_out, 4)) + self.assertAllEqual( + prediction_out['rpn_objectness_predictions_with_background'].shape, + (batch_size, num_anchors_out, 2)) + + def test_predict_gives_correct_shapes_in_inference_mode_both_stages(self): + test_graph = tf.Graph() + with test_graph.as_default(): + model = self._build_model( + is_training=False, first_stage_only=False, second_stage_batch_size=2) + batch_size = 2 + image_size = 10 + image_shape = (batch_size, image_size, image_size, 3) + preprocessed_inputs = tf.zeros(image_shape, dtype=tf.float32) + result_tensor_dict = model.predict(preprocessed_inputs) + expected_num_anchors = image_size * image_size * 3 * 3 + + expected_shapes = { + 'rpn_box_predictor_features': + (2, image_size, image_size, 512), + 'rpn_features_to_crop': (2, image_size, image_size, 3), + 'image_shape': (4,), + 'rpn_box_encodings': (2, expected_num_anchors, 4), + 'rpn_objectness_predictions_with_background': + (2, expected_num_anchors, 2), + 'anchors': (expected_num_anchors, 4), + 'refined_box_encodings': (2 * 8, 2, 4), + 'class_predictions_with_background': (2 * 8, 2 + 1), + 'num_proposals': (2,), + 'proposal_boxes': (2, 8, 4), + } + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + tensor_dict_out = sess.run(result_tensor_dict) + self.assertEqual(set(tensor_dict_out.keys()), + set(expected_shapes.keys())) + for key in expected_shapes: + self.assertAllEqual(tensor_dict_out[key].shape, expected_shapes[key]) + + def test_predict_gives_correct_shapes_in_train_mode_both_stages(self): + test_graph = tf.Graph() + with test_graph.as_default(): + model = self._build_model( + is_training=True, first_stage_only=False, second_stage_batch_size=7) + batch_size = 2 + image_size = 10 + image_shape = (batch_size, image_size, image_size, 3) + preprocessed_inputs = tf.zeros(image_shape, dtype=tf.float32) + groundtruth_boxes_list = [ + tf.constant([[0, 0, .5, .5], [.5, .5, 1, 1]], dtype=tf.float32), + tf.constant([[0, .5, .5, 1], [.5, 0, 1, .5]], dtype=tf.float32)] + groundtruth_classes_list = [ + tf.constant([[1, 0], [0, 1]], dtype=tf.float32), + tf.constant([[1, 0], [1, 0]], dtype=tf.float32)] + + model.provide_groundtruth(groundtruth_boxes_list, + groundtruth_classes_list) + + result_tensor_dict = model.predict(preprocessed_inputs) + expected_shapes = { + 'rpn_box_predictor_features': + (2, image_size, image_size, 512), + 'rpn_features_to_crop': (2, image_size, image_size, 3), + 'image_shape': (4,), + 'refined_box_encodings': (2 * 7, 2, 4), + 'class_predictions_with_background': (2 * 7, 2 + 1), + 'num_proposals': (2,), + 'proposal_boxes': (2, 7, 4), + } + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + tensor_dict_out = sess.run(result_tensor_dict) + self.assertEqual(set(tensor_dict_out.keys()), + set(expected_shapes.keys()).union(set([ + 'rpn_box_encodings', + 'rpn_objectness_predictions_with_background', + 'anchors']))) + for key in expected_shapes: + self.assertAllEqual(tensor_dict_out[key].shape, expected_shapes[key]) + + anchors_shape_out = tensor_dict_out['anchors'].shape + self.assertEqual(2, len(anchors_shape_out)) + self.assertEqual(4, anchors_shape_out[1]) + num_anchors_out = anchors_shape_out[0] + self.assertAllEqual(tensor_dict_out['rpn_box_encodings'].shape, + (2, num_anchors_out, 4)) + self.assertAllEqual( + tensor_dict_out['rpn_objectness_predictions_with_background'].shape, + (2, num_anchors_out, 2)) + + def test_postprocess_first_stage_only_inference_mode(self): + model = self._build_model( + is_training=False, first_stage_only=True, second_stage_batch_size=6) + batch_size = 2 + anchors = tf.constant( + [[0, 0, 16, 16], + [0, 16, 16, 32], + [16, 0, 32, 16], + [16, 16, 32, 32]], dtype=tf.float32) + rpn_box_encodings = tf.zeros( + [batch_size, anchors.get_shape().as_list()[0], + BOX_CODE_SIZE], dtype=tf.float32) + # use different numbers for the objectness category to break ties in + # order of boxes returned by NMS + rpn_objectness_predictions_with_background = tf.constant([ + [[-10, 13], + [10, -10], + [10, -11], + [-10, 12]], + [[10, -10], + [-10, 13], + [-10, 12], + [10, -11]]], dtype=tf.float32) + rpn_features_to_crop = tf.ones((batch_size, 8, 8, 10), dtype=tf.float32) + image_shape = tf.constant([batch_size, 32, 32, 3], dtype=tf.int32) + proposals = model.postprocess({ + 'rpn_box_encodings': rpn_box_encodings, + 'rpn_objectness_predictions_with_background': + rpn_objectness_predictions_with_background, + 'rpn_features_to_crop': rpn_features_to_crop, + 'anchors': anchors, + 'image_shape': image_shape}) + expected_proposal_boxes = [ + [[0, 0, .5, .5], [.5, .5, 1, 1], [0, .5, .5, 1], [.5, 0, 1.0, .5]] + + 4 * [4 * [0]], + [[0, .5, .5, 1], [.5, 0, 1.0, .5], [0, 0, .5, .5], [.5, .5, 1, 1]] + + 4 * [4 * [0]]] + expected_proposal_scores = [[1, 1, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 0, 0, 0]] + expected_num_proposals = [4, 4] + + expected_output_keys = set(['detection_boxes', 'detection_scores', + 'num_detections']) + self.assertEqual(set(proposals.keys()), expected_output_keys) + with self.test_session() as sess: + proposals_out = sess.run(proposals) + self.assertAllClose(proposals_out['detection_boxes'], + expected_proposal_boxes) + self.assertAllClose(proposals_out['detection_scores'], + expected_proposal_scores) + self.assertAllEqual(proposals_out['num_detections'], + expected_num_proposals) + + def test_postprocess_first_stage_only_train_mode(self): + model = self._build_model( + is_training=True, first_stage_only=True, second_stage_batch_size=2) + batch_size = 2 + anchors = tf.constant( + [[0, 0, 16, 16], + [0, 16, 16, 32], + [16, 0, 32, 16], + [16, 16, 32, 32]], dtype=tf.float32) + rpn_box_encodings = tf.zeros( + [batch_size, anchors.get_shape().as_list()[0], + BOX_CODE_SIZE], dtype=tf.float32) + # use different numbers for the objectness category to break ties in + # order of boxes returned by NMS + rpn_objectness_predictions_with_background = tf.constant([ + [[-10, 13], + [-10, 12], + [-10, 11], + [-10, 10]], + [[-10, 13], + [-10, 12], + [-10, 11], + [-10, 10]]], dtype=tf.float32) + rpn_features_to_crop = tf.ones((batch_size, 8, 8, 10), dtype=tf.float32) + image_shape = tf.constant([batch_size, 32, 32, 3], dtype=tf.int32) + groundtruth_boxes_list = [ + tf.constant([[0, 0, .5, .5], [.5, .5, 1, 1]], dtype=tf.float32), + tf.constant([[0, .5, .5, 1], [.5, 0, 1, .5]], dtype=tf.float32)] + groundtruth_classes_list = [tf.constant([[1, 0], [0, 1]], dtype=tf.float32), + tf.constant([[1, 0], [1, 0]], dtype=tf.float32)] + + model.provide_groundtruth(groundtruth_boxes_list, + groundtruth_classes_list) + proposals = model.postprocess({ + 'rpn_box_encodings': rpn_box_encodings, + 'rpn_objectness_predictions_with_background': + rpn_objectness_predictions_with_background, + 'rpn_features_to_crop': rpn_features_to_crop, + 'anchors': anchors, + 'image_shape': image_shape}) + expected_proposal_boxes = [ + [[0, 0, .5, .5], [.5, .5, 1, 1]], [[0, .5, .5, 1], [.5, 0, 1, .5]]] + expected_proposal_scores = [[1, 1], + [1, 1]] + expected_num_proposals = [2, 2] + + expected_output_keys = set(['detection_boxes', 'detection_scores', + 'num_detections']) + self.assertEqual(set(proposals.keys()), expected_output_keys) + + with self.test_session() as sess: + proposals_out = sess.run(proposals) + self.assertAllClose(proposals_out['detection_boxes'], + expected_proposal_boxes) + self.assertAllClose(proposals_out['detection_scores'], + expected_proposal_scores) + self.assertAllEqual(proposals_out['num_detections'], + expected_num_proposals) + + def test_postprocess_second_stage_only_inference_mode(self): + model = self._build_model( + is_training=False, first_stage_only=False, second_stage_batch_size=6) + + batch_size = 2 + total_num_padded_proposals = batch_size * model.max_num_proposals + proposal_boxes = tf.constant( + [[[1, 1, 2, 3], + [0, 0, 1, 1], + [.5, .5, .6, .6], + 4*[0], 4*[0], 4*[0], 4*[0], 4*[0]], + [[2, 3, 6, 8], + [1, 2, 5, 3], + 4*[0], 4*[0], 4*[0], 4*[0], 4*[0], 4*[0]]], dtype=tf.float32) + num_proposals = tf.constant([3, 2], dtype=tf.int32) + refined_box_encodings = tf.zeros( + [total_num_padded_proposals, model.num_classes, 4], dtype=tf.float32) + class_predictions_with_background = tf.ones( + [total_num_padded_proposals, model.num_classes+1], dtype=tf.float32) + image_shape = tf.constant([batch_size, 36, 48, 3], dtype=tf.int32) + + detections = model.postprocess({ + 'refined_box_encodings': refined_box_encodings, + 'class_predictions_with_background': class_predictions_with_background, + 'num_proposals': num_proposals, + 'proposal_boxes': proposal_boxes, + 'image_shape': image_shape + }) + with self.test_session() as sess: + detections_out = sess.run(detections) + self.assertAllEqual(detections_out['detection_boxes'].shape, [2, 5, 4]) + self.assertAllClose(detections_out['detection_scores'], + [[1, 1, 1, 1, 1], [1, 1, 1, 1, 0]]) + self.assertAllClose(detections_out['detection_classes'], + [[0, 0, 0, 1, 1], [0, 0, 1, 1, 0]]) + self.assertAllClose(detections_out['num_detections'], [5, 4]) + + def test_loss_first_stage_only_mode(self): + model = self._build_model( + is_training=True, first_stage_only=True, second_stage_batch_size=6) + batch_size = 2 + anchors = tf.constant( + [[0, 0, 16, 16], + [0, 16, 16, 32], + [16, 0, 32, 16], + [16, 16, 32, 32]], dtype=tf.float32) + + rpn_box_encodings = tf.zeros( + [batch_size, + anchors.get_shape().as_list()[0], + BOX_CODE_SIZE], dtype=tf.float32) + # use different numbers for the objectness category to break ties in + # order of boxes returned by NMS + rpn_objectness_predictions_with_background = tf.constant([ + [[-10, 13], + [10, -10], + [10, -11], + [-10, 12]], + [[10, -10], + [-10, 13], + [-10, 12], + [10, -11]]], dtype=tf.float32) + image_shape = tf.constant([batch_size, 32, 32, 3], dtype=tf.int32) + + groundtruth_boxes_list = [ + tf.constant([[0, 0, .5, .5], [.5, .5, 1, 1]], dtype=tf.float32), + tf.constant([[0, .5, .5, 1], [.5, 0, 1, .5]], dtype=tf.float32)] + groundtruth_classes_list = [tf.constant([[1, 0], [0, 1]], dtype=tf.float32), + tf.constant([[1, 0], [1, 0]], dtype=tf.float32)] + + prediction_dict = { + 'rpn_box_encodings': rpn_box_encodings, + 'rpn_objectness_predictions_with_background': + rpn_objectness_predictions_with_background, + 'image_shape': image_shape, + 'anchors': anchors + } + model.provide_groundtruth(groundtruth_boxes_list, + groundtruth_classes_list) + loss_dict = model.loss(prediction_dict) + with self.test_session() as sess: + loss_dict_out = sess.run(loss_dict) + self.assertAllClose(loss_dict_out['first_stage_localization_loss'], 0) + self.assertAllClose(loss_dict_out['first_stage_objectness_loss'], 0) + self.assertTrue('second_stage_localization_loss' not in loss_dict_out) + self.assertTrue('second_stage_classification_loss' not in loss_dict_out) + + def test_loss_full(self): + model = self._build_model( + is_training=True, first_stage_only=False, second_stage_batch_size=6) + batch_size = 2 + anchors = tf.constant( + [[0, 0, 16, 16], + [0, 16, 16, 32], + [16, 0, 32, 16], + [16, 16, 32, 32]], dtype=tf.float32) + rpn_box_encodings = tf.zeros( + [batch_size, + anchors.get_shape().as_list()[0], + BOX_CODE_SIZE], dtype=tf.float32) + # use different numbers for the objectness category to break ties in + # order of boxes returned by NMS + rpn_objectness_predictions_with_background = tf.constant([ + [[-10, 13], + [10, -10], + [10, -11], + [-10, 12]], + [[10, -10], + [-10, 13], + [-10, 12], + [10, -11]]], dtype=tf.float32) + image_shape = tf.constant([batch_size, 32, 32, 3], dtype=tf.int32) + + num_proposals = tf.constant([6, 6], dtype=tf.int32) + proposal_boxes = tf.constant( + 2 * [[[0, 0, 16, 16], + [0, 16, 16, 32], + [16, 0, 32, 16], + [16, 16, 32, 32], + [0, 0, 16, 16], + [0, 16, 16, 32]]], dtype=tf.float32) + refined_box_encodings = tf.zeros( + (batch_size * model.max_num_proposals, + model.num_classes, + BOX_CODE_SIZE), dtype=tf.float32) + class_predictions_with_background = tf.constant( + [[-10, 10, -10], # first image + [10, -10, -10], + [10, -10, -10], + [-10, -10, 10], + [-10, 10, -10], + [10, -10, -10], + [10, -10, -10], # second image + [-10, 10, -10], + [-10, 10, -10], + [10, -10, -10], + [10, -10, -10], + [-10, 10, -10]], dtype=tf.float32) + + groundtruth_boxes_list = [ + tf.constant([[0, 0, .5, .5], [.5, .5, 1, 1]], dtype=tf.float32), + tf.constant([[0, .5, .5, 1], [.5, 0, 1, .5]], dtype=tf.float32)] + groundtruth_classes_list = [tf.constant([[1, 0], [0, 1]], dtype=tf.float32), + tf.constant([[1, 0], [1, 0]], dtype=tf.float32)] + + prediction_dict = { + 'rpn_box_encodings': rpn_box_encodings, + 'rpn_objectness_predictions_with_background': + rpn_objectness_predictions_with_background, + 'image_shape': image_shape, + 'anchors': anchors, + 'refined_box_encodings': refined_box_encodings, + 'class_predictions_with_background': class_predictions_with_background, + 'proposal_boxes': proposal_boxes, + 'num_proposals': num_proposals + } + model.provide_groundtruth(groundtruth_boxes_list, + groundtruth_classes_list) + loss_dict = model.loss(prediction_dict) + + with self.test_session() as sess: + loss_dict_out = sess.run(loss_dict) + self.assertAllClose(loss_dict_out['first_stage_localization_loss'], 0) + self.assertAllClose(loss_dict_out['first_stage_objectness_loss'], 0) + self.assertAllClose(loss_dict_out['second_stage_localization_loss'], 0) + self.assertAllClose(loss_dict_out['second_stage_classification_loss'], 0) + + def test_loss_full_zero_padded_proposals(self): + model = self._build_model( + is_training=True, first_stage_only=False, second_stage_batch_size=6) + batch_size = 1 + anchors = tf.constant( + [[0, 0, 16, 16], + [0, 16, 16, 32], + [16, 0, 32, 16], + [16, 16, 32, 32]], dtype=tf.float32) + rpn_box_encodings = tf.zeros( + [batch_size, + anchors.get_shape().as_list()[0], + BOX_CODE_SIZE], dtype=tf.float32) + # use different numbers for the objectness category to break ties in + # order of boxes returned by NMS + rpn_objectness_predictions_with_background = tf.constant([ + [[-10, 13], + [10, -10], + [10, -11], + [10, -12]],], dtype=tf.float32) + image_shape = tf.constant([batch_size, 32, 32, 3], dtype=tf.int32) + + # box_classifier_batch_size is 6, but here we assume that the number of + # actual proposals (not counting zero paddings) is fewer (3). + num_proposals = tf.constant([3], dtype=tf.int32) + proposal_boxes = tf.constant( + [[[0, 0, 16, 16], + [0, 16, 16, 32], + [16, 0, 32, 16], + [0, 0, 0, 0], # begin paddings + [0, 0, 0, 0], + [0, 0, 0, 0]]], dtype=tf.float32) + + refined_box_encodings = tf.zeros( + (batch_size * model.max_num_proposals, + model.num_classes, + BOX_CODE_SIZE), dtype=tf.float32) + class_predictions_with_background = tf.constant( + [[-10, 10, -10], + [10, -10, -10], + [10, -10, -10], + [0, 0, 0], # begin paddings + [0, 0, 0], + [0, 0, 0]], dtype=tf.float32) + + groundtruth_boxes_list = [ + tf.constant([[0, 0, .5, .5]], dtype=tf.float32)] + groundtruth_classes_list = [tf.constant([[1, 0]], dtype=tf.float32)] + + prediction_dict = { + 'rpn_box_encodings': rpn_box_encodings, + 'rpn_objectness_predictions_with_background': + rpn_objectness_predictions_with_background, + 'image_shape': image_shape, + 'anchors': anchors, + 'refined_box_encodings': refined_box_encodings, + 'class_predictions_with_background': class_predictions_with_background, + 'proposal_boxes': proposal_boxes, + 'num_proposals': num_proposals + } + model.provide_groundtruth(groundtruth_boxes_list, + groundtruth_classes_list) + loss_dict = model.loss(prediction_dict) + + with self.test_session() as sess: + loss_dict_out = sess.run(loss_dict) + self.assertAllClose(loss_dict_out['first_stage_localization_loss'], 0) + self.assertAllClose(loss_dict_out['first_stage_objectness_loss'], 0) + self.assertAllClose(loss_dict_out['second_stage_localization_loss'], 0) + self.assertAllClose(loss_dict_out['second_stage_classification_loss'], 0) + + def test_loss_full_zero_padded_proposals_nonzero_loss_with_two_images(self): + model = self._build_model( + is_training=True, first_stage_only=False, second_stage_batch_size=6) + batch_size = 2 + anchors = tf.constant( + [[0, 0, 16, 16], + [0, 16, 16, 32], + [16, 0, 32, 16], + [16, 16, 32, 32]], dtype=tf.float32) + rpn_box_encodings = tf.zeros( + [batch_size, + anchors.get_shape().as_list()[0], + BOX_CODE_SIZE], dtype=tf.float32) + # use different numbers for the objectness category to break ties in + # order of boxes returned by NMS + rpn_objectness_predictions_with_background = tf.constant( + [[[-10, 13], + [10, -10], + [10, -11], + [10, -12]], + [[-10, 13], + [10, -10], + [10, -11], + [10, -12]]], dtype=tf.float32) + image_shape = tf.constant([batch_size, 32, 32, 3], dtype=tf.int32) + + # box_classifier_batch_size is 6, but here we assume that the number of + # actual proposals (not counting zero paddings) is fewer (3). + num_proposals = tf.constant([3, 2], dtype=tf.int32) + proposal_boxes = tf.constant( + [[[0, 0, 16, 16], + [0, 16, 16, 32], + [16, 0, 32, 16], + [0, 0, 0, 0], # begin paddings + [0, 0, 0, 0], + [0, 0, 0, 0]], + [[0, 0, 16, 16], + [0, 16, 16, 32], + [0, 0, 0, 0], + [0, 0, 0, 0], # begin paddings + [0, 0, 0, 0], + [0, 0, 0, 0]]], dtype=tf.float32) + + refined_box_encodings = tf.zeros( + (batch_size * model.max_num_proposals, + model.num_classes, + BOX_CODE_SIZE), dtype=tf.float32) + class_predictions_with_background = tf.constant( + [[-10, 10, -10], # first image + [10, -10, -10], + [10, -10, -10], + [0, 0, 0], # begin paddings + [0, 0, 0], + [0, 0, 0], + [-10, -10, 10], # second image + [10, -10, -10], + [0, 0, 0], # begin paddings + [0, 0, 0], + [0, 0, 0], + [0, 0, 0],], dtype=tf.float32) + + # The first groundtruth box is 4/5 of the anchor size in both directions + # experiencing a loss of: + # 2 * SmoothL1(5 * log(4/5)) / num_proposals + # = 2 * (abs(5 * log(1/2)) - .5) / 3 + # The second groundtruth box is identical to the prediction and thus + # experiences zero loss. + # Total average loss is (abs(5 * log(1/2)) - .5) / 3. + groundtruth_boxes_list = [ + tf.constant([[0.05, 0.05, 0.45, 0.45]], dtype=tf.float32), + tf.constant([[0.0, 0.0, 0.5, 0.5]], dtype=tf.float32)] + groundtruth_classes_list = [tf.constant([[1, 0]], dtype=tf.float32), + tf.constant([[0, 1]], dtype=tf.float32)] + exp_loc_loss = (-5 * np.log(.8) - 0.5) / 3.0 + + prediction_dict = { + 'rpn_box_encodings': rpn_box_encodings, + 'rpn_objectness_predictions_with_background': + rpn_objectness_predictions_with_background, + 'image_shape': image_shape, + 'anchors': anchors, + 'refined_box_encodings': refined_box_encodings, + 'class_predictions_with_background': class_predictions_with_background, + 'proposal_boxes': proposal_boxes, + 'num_proposals': num_proposals + } + model.provide_groundtruth(groundtruth_boxes_list, + groundtruth_classes_list) + loss_dict = model.loss(prediction_dict) + + with self.test_session() as sess: + loss_dict_out = sess.run(loss_dict) + self.assertAllClose(loss_dict_out['first_stage_localization_loss'], + exp_loc_loss) + self.assertAllClose(loss_dict_out['first_stage_objectness_loss'], 0) + self.assertAllClose(loss_dict_out['second_stage_localization_loss'], + exp_loc_loss) + self.assertAllClose(loss_dict_out['second_stage_classification_loss'], 0) + + def test_loss_with_hard_mining(self): + model = self._build_model(is_training=True, + first_stage_only=False, + second_stage_batch_size=None, + first_stage_max_proposals=6, + hard_mining=True) + batch_size = 1 + anchors = tf.constant( + [[0, 0, 16, 16], + [0, 16, 16, 32], + [16, 0, 32, 16], + [16, 16, 32, 32]], dtype=tf.float32) + rpn_box_encodings = tf.zeros( + [batch_size, + anchors.get_shape().as_list()[0], + BOX_CODE_SIZE], dtype=tf.float32) + # use different numbers for the objectness category to break ties in + # order of boxes returned by NMS + rpn_objectness_predictions_with_background = tf.constant( + [[[-10, 13], + [-10, 12], + [10, -11], + [10, -12]]], dtype=tf.float32) + image_shape = tf.constant([batch_size, 32, 32, 3], dtype=tf.int32) + + # box_classifier_batch_size is 6, but here we assume that the number of + # actual proposals (not counting zero paddings) is fewer (3). + num_proposals = tf.constant([3], dtype=tf.int32) + proposal_boxes = tf.constant( + [[[0, 0, 16, 16], + [0, 16, 16, 32], + [16, 0, 32, 16], + [0, 0, 0, 0], # begin paddings + [0, 0, 0, 0], + [0, 0, 0, 0]]], dtype=tf.float32) + + refined_box_encodings = tf.zeros( + (batch_size * model.max_num_proposals, + model.num_classes, + BOX_CODE_SIZE), dtype=tf.float32) + class_predictions_with_background = tf.constant( + [[-10, 10, -10], # first image + [-10, -10, 10], + [10, -10, -10], + [0, 0, 0], # begin paddings + [0, 0, 0], + [0, 0, 0]], dtype=tf.float32) + + # The first groundtruth box is 4/5 of the anchor size in both directions + # experiencing a loss of: + # 2 * SmoothL1(5 * log(4/5)) / num_proposals + # = 2 * (abs(5 * log(1/2)) - .5) / 3 + # The second groundtruth box is 46/50 of the anchor size in both directions + # experiencing a loss of: + # 2 * SmoothL1(5 * log(42/50)) / num_proposals + # = 2 * (.5(5 * log(.92))^2 - .5) / 3. + # Since the first groundtruth box experiences greater loss, and we have + # set num_hard_examples=1 in the HardMiner, the final localization loss + # corresponds to that of the first groundtruth box. + groundtruth_boxes_list = [ + tf.constant([[0.05, 0.05, 0.45, 0.45], + [0.02, 0.52, 0.48, 0.98],], dtype=tf.float32)] + groundtruth_classes_list = [tf.constant([[1, 0], [0, 1]], dtype=tf.float32)] + exp_loc_loss = 2 * (-5 * np.log(.8) - 0.5) / 3.0 + + prediction_dict = { + 'rpn_box_encodings': rpn_box_encodings, + 'rpn_objectness_predictions_with_background': + rpn_objectness_predictions_with_background, + 'image_shape': image_shape, + 'anchors': anchors, + 'refined_box_encodings': refined_box_encodings, + 'class_predictions_with_background': class_predictions_with_background, + 'proposal_boxes': proposal_boxes, + 'num_proposals': num_proposals + } + model.provide_groundtruth(groundtruth_boxes_list, + groundtruth_classes_list) + loss_dict = model.loss(prediction_dict) + + with self.test_session() as sess: + loss_dict_out = sess.run(loss_dict) + self.assertAllClose(loss_dict_out['second_stage_localization_loss'], + exp_loc_loss) + self.assertAllClose(loss_dict_out['second_stage_classification_loss'], 0) + + def test_restore_fn_classification(self): + # Define mock tensorflow classification graph and save variables. + test_graph_classification = tf.Graph() + with test_graph_classification.as_default(): + image = tf.placeholder(dtype=tf.float32, shape=[1, 20, 20, 3]) + with tf.variable_scope('mock_model'): + net = slim.conv2d(image, num_outputs=3, kernel_size=1, scope='layer1') + slim.conv2d(net, num_outputs=3, kernel_size=1, scope='layer2') + + init_op = tf.global_variables_initializer() + saver = tf.train.Saver() + save_path = self.get_temp_dir() + with self.test_session() as sess: + sess.run(init_op) + saved_model_path = saver.save(sess, save_path) + + # Create tensorflow detection graph and load variables from + # classification checkpoint. + test_graph_detection = tf.Graph() + with test_graph_detection.as_default(): + model = self._build_model( + is_training=False, first_stage_only=False, second_stage_batch_size=6) + + inputs_shape = (2, 20, 20, 3) + inputs = tf.to_float(tf.random_uniform( + inputs_shape, minval=0, maxval=255, dtype=tf.int32)) + preprocessed_inputs = model.preprocess(inputs) + prediction_dict = model.predict(preprocessed_inputs) + model.postprocess(prediction_dict) + restore_fn = model.restore_fn(saved_model_path, + from_detection_checkpoint=False) + with self.test_session() as sess: + restore_fn(sess) + + def test_restore_fn_detection(self): + # Define first detection graph and save variables. + test_graph_detection1 = tf.Graph() + with test_graph_detection1.as_default(): + model = self._build_model( + is_training=False, first_stage_only=False, second_stage_batch_size=6) + inputs_shape = (2, 20, 20, 3) + inputs = tf.to_float(tf.random_uniform( + inputs_shape, minval=0, maxval=255, dtype=tf.int32)) + preprocessed_inputs = model.preprocess(inputs) + prediction_dict = model.predict(preprocessed_inputs) + model.postprocess(prediction_dict) + init_op = tf.global_variables_initializer() + saver = tf.train.Saver() + save_path = self.get_temp_dir() + with self.test_session() as sess: + sess.run(init_op) + saved_model_path = saver.save(sess, save_path) + + # Define second detection graph and restore variables. + test_graph_detection2 = tf.Graph() + with test_graph_detection2.as_default(): + model2 = self._build_model(is_training=False, first_stage_only=False, + second_stage_batch_size=6, num_classes=42) + + inputs_shape2 = (2, 20, 20, 3) + inputs2 = tf.to_float(tf.random_uniform( + inputs_shape2, minval=0, maxval=255, dtype=tf.int32)) + preprocessed_inputs2 = model2.preprocess(inputs2) + prediction_dict2 = model2.predict(preprocessed_inputs2) + model2.postprocess(prediction_dict2) + restore_fn = model2.restore_fn(saved_model_path, + from_detection_checkpoint=True) + with self.test_session() as sess: + restore_fn(sess) + for var in sess.run(tf.report_uninitialized_variables()): + self.assertNotIn(model2.first_stage_feature_extractor_scope, var.name) + self.assertNotIn(model2.second_stage_feature_extractor_scope, + var.name) + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/meta_architectures/rfcn_meta_arch.py b/object_detection/meta_architectures/rfcn_meta_arch.py new file mode 100644 index 000000000..7f712ba4d --- /dev/null +++ b/object_detection/meta_architectures/rfcn_meta_arch.py @@ -0,0 +1,267 @@ +# 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-FCN meta-architecture definition. + +R-FCN: Dai, Jifeng, et al. "R-FCN: Object Detection via Region-based +Fully Convolutional Networks." arXiv preprint arXiv:1605.06409 (2016). + +The R-FCN meta architecture is similar to Faster R-CNN and only differs in the +second stage. Hence this class inherits FasterRCNNMetaArch and overrides only +the `_predict_second_stage` method. + +Similar to Faster R-CNN we allow for two modes: first_stage_only=True and +first_stage_only=False. In the former setting, all of the user facing methods +(e.g., predict, postprocess, loss) can be used as if the model consisted +only of the RPN, returning class agnostic proposals (these can be thought of as +approximate detections with no associated class information). In the latter +setting, proposals are computed, then passed through a second stage +"box classifier" to yield (multi-class) detections. + +Implementations of R-FCN models must define a new FasterRCNNFeatureExtractor and +override three methods: `preprocess`, `_extract_proposal_features` (the first +stage of the model), and `_extract_box_classifier_features` (the second stage of +the model). Optionally, the `restore_fn` method can be overridden. See tests +for an example. + +See notes in the documentation of Faster R-CNN meta-architecture as they all +apply here. +""" +import tensorflow as tf + +from object_detection.core import box_predictor +from object_detection.meta_architectures import faster_rcnn_meta_arch +from object_detection.utils import ops + + +class RFCNMetaArch(faster_rcnn_meta_arch.FasterRCNNMetaArch): + """R-FCN Meta-architecture definition.""" + + def __init__(self, + is_training, + num_classes, + image_resizer_fn, + feature_extractor, + first_stage_only, + first_stage_anchor_generator, + first_stage_atrous_rate, + first_stage_box_predictor_arg_scope, + first_stage_box_predictor_kernel_size, + first_stage_box_predictor_depth, + first_stage_minibatch_size, + first_stage_positive_balance_fraction, + first_stage_nms_score_threshold, + first_stage_nms_iou_threshold, + first_stage_max_proposals, + first_stage_localization_loss_weight, + first_stage_objectness_loss_weight, + second_stage_rfcn_box_predictor, + second_stage_batch_size, + second_stage_balance_fraction, + second_stage_non_max_suppression_fn, + second_stage_score_conversion_fn, + second_stage_localization_loss_weight, + second_stage_classification_loss_weight, + hard_example_miner, + parallel_iterations=16): + """RFCNMetaArch Constructor. + + Args: + is_training: A boolean indicating whether the training version of the + computation graph should be constructed. + 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}). + image_resizer_fn: A callable for image resizing. This callable always + takes a rank-3 image tensor (corresponding to a single image) and + returns a rank-3 image tensor, possibly with new spatial dimensions. + See builders/image_resizer_builder.py. + feature_extractor: A FasterRCNNFeatureExtractor object. + first_stage_only: Whether to construct only the Region Proposal Network + (RPN) part of the model. + first_stage_anchor_generator: An anchor_generator.AnchorGenerator object + (note that currently we only support + grid_anchor_generator.GridAnchorGenerator objects) + first_stage_atrous_rate: A single integer indicating the atrous rate for + the single convolution op which is applied to the `rpn_features_to_crop` + tensor to obtain a tensor to be used for box prediction. Some feature + extractors optionally allow for producing feature maps computed at + denser resolutions. The atrous rate is used to compensate for the + denser feature maps by using an effectively larger receptive field. + (This should typically be set to 1). + first_stage_box_predictor_arg_scope: Slim arg_scope for conv2d, + separable_conv2d and fully_connected ops for the RPN box predictor. + first_stage_box_predictor_kernel_size: Kernel size to use for the + convolution op just prior to RPN box predictions. + first_stage_box_predictor_depth: Output depth for the convolution op + just prior to RPN box predictions. + first_stage_minibatch_size: The "batch size" to use for computing the + objectness and location loss of the region proposal network. This + "batch size" refers to the number of anchors selected as contributing + to the loss function for any given image within the image batch and is + only called "batch_size" due to terminology from the Faster R-CNN paper. + first_stage_positive_balance_fraction: Fraction of positive examples + per image for the RPN. The recommended value for Faster RCNN is 0.5. + first_stage_nms_score_threshold: Score threshold for non max suppression + for the Region Proposal Network (RPN). This value is expected to be in + [0, 1] as it is applied directly after a softmax transformation. The + recommended value for Faster R-CNN is 0. + first_stage_nms_iou_threshold: The Intersection Over Union (IOU) threshold + for performing Non-Max Suppression (NMS) on the boxes predicted by the + Region Proposal Network (RPN). + first_stage_max_proposals: Maximum number of boxes to retain after + performing Non-Max Suppression (NMS) on the boxes predicted by the + Region Proposal Network (RPN). + first_stage_localization_loss_weight: A float + first_stage_objectness_loss_weight: A float + second_stage_rfcn_box_predictor: RFCN box predictor to use for + second stage. + second_stage_batch_size: The batch size used for computing the + classification and refined location loss of the box classifier. This + "batch size" refers to the number of proposals selected as contributing + to the loss function for any given image within the image batch and is + only called "batch_size" due to terminology from the Faster R-CNN paper. + second_stage_balance_fraction: Fraction of positive examples to use + per image for the box classifier. The recommended value for Faster RCNN + is 0.25. + second_stage_non_max_suppression_fn: batch_multiclass_non_max_suppression + callable that takes `boxes`, `scores`, optional `clip_window` and + optional (kwarg) `mask` inputs (with all other inputs already set) + and returns a dictionary containing tensors with keys: + `detection_boxes`, `detection_scores`, `detection_classes`, + `num_detections`, and (optionally) `detection_masks`. See + `post_processing.batch_multiclass_non_max_suppression` for the type and + shape of these tensors. + second_stage_score_conversion_fn: Callable elementwise nonlinearity + (that takes tensors as inputs and returns tensors). This is usually + used to convert logits to probabilities. + second_stage_localization_loss_weight: A float + second_stage_classification_loss_weight: A float + hard_example_miner: A losses.HardExampleMiner object (can be None). + parallel_iterations: (Optional) The number of iterations allowed to run + in parallel for calls to tf.map_fn. + Raises: + ValueError: If `second_stage_batch_size` > `first_stage_max_proposals` + ValueError: If first_stage_anchor_generator is not of type + grid_anchor_generator.GridAnchorGenerator. + """ + super(RFCNMetaArch, self).__init__( + is_training, + num_classes, + image_resizer_fn, + feature_extractor, + first_stage_only, + first_stage_anchor_generator, + first_stage_atrous_rate, + first_stage_box_predictor_arg_scope, + first_stage_box_predictor_kernel_size, + first_stage_box_predictor_depth, + first_stage_minibatch_size, + first_stage_positive_balance_fraction, + first_stage_nms_score_threshold, + first_stage_nms_iou_threshold, + first_stage_max_proposals, + first_stage_localization_loss_weight, + first_stage_objectness_loss_weight, + None, # initial_crop_size is not used in R-FCN + None, # maxpool_kernel_size is not use in R-FCN + None, # maxpool_stride is not use in R-FCN + None, # fully_connected_box_predictor is not used in R-FCN. + second_stage_batch_size, + second_stage_balance_fraction, + second_stage_non_max_suppression_fn, + second_stage_score_conversion_fn, + second_stage_localization_loss_weight, + second_stage_classification_loss_weight, + hard_example_miner, + parallel_iterations) + + self._rfcn_box_predictor = second_stage_rfcn_box_predictor + + def _predict_second_stage(self, rpn_box_encodings, + rpn_objectness_predictions_with_background, + rpn_features, + anchors, + image_shape): + """Predicts the output tensors from 2nd stage of FasterRCNN. + + Args: + rpn_box_encodings: 4-D float tensor of shape + [batch_size, num_valid_anchors, self._box_coder.code_size] containing + predicted boxes. + rpn_objectness_predictions_with_background: 2-D float tensor of shape + [batch_size, num_valid_anchors, 2] containing class + predictions (logits) for each of the anchors. Note that this + tensor *includes* background class predictions (at class index 0). + rpn_features: A 4-D float32 tensor with shape + [batch_size, height, width, depth] representing image features from the + RPN. + anchors: 2-D float tensor of shape + [num_anchors, self._box_coder.code_size]. + image_shape: A 1D int32 tensors of size [4] containing the image shape. + + Returns: + prediction_dict: a dictionary holding "raw" prediction tensors: + 1) refined_box_encodings: a 3-D tensor with shape + [total_num_proposals, num_classes, 4] representing predicted + (final) refined box encodings, where + total_num_proposals=batch_size*self._max_num_proposals + 2) class_predictions_with_background: a 3-D tensor with shape + [total_num_proposals, num_classes + 1] containing class + predictions (logits) for each of the anchors, where + total_num_proposals=batch_size*self._max_num_proposals. + Note that this tensor *includes* background class predictions + (at class index 0). + 3) num_proposals: An int32 tensor of shape [batch_size] representing the + number of proposals generated by the RPN. `num_proposals` allows us + to keep track of which entries are to be treated as zero paddings and + which are not since we always pad the number of proposals to be + `self.max_num_proposals` for each image. + 4) proposal_boxes: A float32 tensor of shape + [batch_size, self.max_num_proposals, 4] representing + decoded proposal bounding boxes (in absolute coordinates). + """ + proposal_boxes_normalized, _, num_proposals = self._postprocess_rpn( + rpn_box_encodings, rpn_objectness_predictions_with_background, + anchors, image_shape) + + box_classifier_features = ( + self._feature_extractor.extract_box_classifier_features( + rpn_features, + scope=self.second_stage_feature_extractor_scope)) + + box_predictions = self._rfcn_box_predictor.predict( + box_classifier_features, + num_predictions_per_location=1, + scope=self.second_stage_box_predictor_scope, + proposal_boxes=proposal_boxes_normalized) + refined_box_encodings = tf.squeeze( + box_predictions[box_predictor.BOX_ENCODINGS], axis=1) + class_predictions_with_background = tf.squeeze( + box_predictions[box_predictor.CLASS_PREDICTIONS_WITH_BACKGROUND], + axis=1) + + absolute_proposal_boxes = ops.normalized_to_image_coordinates( + proposal_boxes_normalized, image_shape, + parallel_iterations=self._parallel_iterations) + + prediction_dict = { + 'refined_box_encodings': refined_box_encodings, + 'class_predictions_with_background': + class_predictions_with_background, + 'num_proposals': num_proposals, + 'proposal_boxes': absolute_proposal_boxes, + } + return prediction_dict diff --git a/object_detection/meta_architectures/rfcn_meta_arch_test.py b/object_detection/meta_architectures/rfcn_meta_arch_test.py new file mode 100644 index 000000000..5a7ad8baa --- /dev/null +++ b/object_detection/meta_architectures/rfcn_meta_arch_test.py @@ -0,0 +1,56 @@ +# 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.meta_architectures.rfcn_meta_arch.""" + +import tensorflow as tf + +from object_detection.meta_architectures import faster_rcnn_meta_arch_test_lib +from object_detection.meta_architectures import rfcn_meta_arch + + +class RFCNMetaArchTest( + faster_rcnn_meta_arch_test_lib.FasterRCNNMetaArchTestBase): + + def _get_second_stage_box_predictor_text_proto(self): + box_predictor_text_proto = """ + rfcn_box_predictor { + conv_hyperparams { + op: CONV + activation: NONE + regularizer { + l2_regularizer { + weight: 0.0005 + } + } + initializer { + variance_scaling_initializer { + factor: 1.0 + uniform: true + mode: FAN_AVG + } + } + } + } + """ + return box_predictor_text_proto + + def _get_model(self, box_predictor, **common_kwargs): + return rfcn_meta_arch.RFCNMetaArch( + second_stage_rfcn_box_predictor=box_predictor, **common_kwargs) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/meta_architectures/ssd_meta_arch.py b/object_detection/meta_architectures/ssd_meta_arch.py new file mode 100644 index 000000000..c23bd3a24 --- /dev/null +++ b/object_detection/meta_architectures/ssd_meta_arch.py @@ -0,0 +1,594 @@ +# 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. +# ============================================================================== + +"""SSD Meta-architecture definition. + +General tensorflow implementation of convolutional Multibox/SSD detection +models. +""" +from abc import abstractmethod + +import re +import tensorflow as tf + +from object_detection.core import box_coder as bcoder +from object_detection.core import box_list +from object_detection.core import box_predictor as bpredictor +from object_detection.core import model +from object_detection.core import standard_fields as fields +from object_detection.core import target_assigner +from object_detection.utils import variables_helper + +slim = tf.contrib.slim + + +class SSDFeatureExtractor(object): + """SSD Feature Extractor definition.""" + + def __init__(self, + depth_multiplier, + min_depth, + conv_hyperparams, + reuse_weights=None): + self._depth_multiplier = depth_multiplier + self._min_depth = min_depth + self._conv_hyperparams = conv_hyperparams + self._reuse_weights = reuse_weights + + @abstractmethod + def preprocess(self, resized_inputs): + """Preprocesses images for feature extraction (minus image resizing). + + Args: + resized_inputs: a [batch, height, width, channels] float tensor + representing a batch of images. + + Returns: + preprocessed_inputs: a [batch, height, width, channels] float tensor + representing a batch of images. + """ + pass + + @abstractmethod + def extract_features(self, preprocessed_inputs): + """Extracts features from preprocessed inputs. + + This function is responsible for extracting feature maps from preprocessed + images. + + Args: + preprocessed_inputs: a [batch, height, width, channels] float tensor + representing a batch of images. + + Returns: + feature_maps: a list of tensors where the ith tensor has shape + [batch, height_i, width_i, depth_i] + """ + pass + + +class SSDMetaArch(model.DetectionModel): + """SSD Meta-architecture definition.""" + + def __init__(self, + is_training, + anchor_generator, + box_predictor, + box_coder, + feature_extractor, + matcher, + region_similarity_calculator, + image_resizer_fn, + non_max_suppression_fn, + score_conversion_fn, + classification_loss, + localization_loss, + classification_loss_weight, + localization_loss_weight, + normalize_loss_by_num_matches, + hard_example_miner, + add_summaries=True): + """SSDMetaArch Constructor. + + TODO: group NMS parameters + score converter into + a class and loss parameters into a class and write config protos for + postprocessing and losses. + + Args: + is_training: A boolean indicating whether the training version of the + computation graph should be constructed. + anchor_generator: an anchor_generator.AnchorGenerator object. + box_predictor: a box_predictor.BoxPredictor object. + box_coder: a box_coder.BoxCoder object. + feature_extractor: a SSDFeatureExtractor object. + matcher: a matcher.Matcher object. + region_similarity_calculator: a + region_similarity_calculator.RegionSimilarityCalculator object. + image_resizer_fn: a callable for image resizing. This callable always + takes a rank-3 image tensor (corresponding to a single image) and + returns a rank-3 image tensor, possibly with new spatial dimensions. + See builders/image_resizer_builder.py. + non_max_suppression_fn: batch_multiclass_non_max_suppression + callable that takes `boxes`, `scores` and optional `clip_window` + inputs (with all other inputs already set) and returns a dictionary + hold tensors with keys: `detection_boxes`, `detection_scores`, + `detection_classes` and `num_detections`. See `post_processing. + batch_multiclass_non_max_suppression` for the type and shape of these + tensors. + score_conversion_fn: callable elementwise nonlinearity (that takes tensors + as inputs and returns tensors). This is usually used to convert logits + to probabilities. + classification_loss: an object_detection.core.losses.Loss object. + localization_loss: a object_detection.core.losses.Loss object. + classification_loss_weight: float + localization_loss_weight: float + normalize_loss_by_num_matches: boolean + hard_example_miner: a losses.HardExampleMiner object (can be None) + add_summaries: boolean (default: True) controlling whether summary ops + should be added to tensorflow graph. + """ + super(SSDMetaArch, self).__init__(num_classes=box_predictor.num_classes) + self._is_training = is_training + + # Needed for fine-tuning from classification checkpoints whose + # variables do not have the feature extractor scope. + self._extract_features_scope = 'FeatureExtractor' + + self._anchor_generator = anchor_generator + self._box_predictor = box_predictor + + self._box_coder = box_coder + self._feature_extractor = feature_extractor + self._matcher = matcher + self._region_similarity_calculator = region_similarity_calculator + + # TODO: handle agnostic mode and positive/negative class weights + unmatched_cls_target = None + unmatched_cls_target = tf.constant([1] + self.num_classes * [0], tf.float32) + self._target_assigner = target_assigner.TargetAssigner( + self._region_similarity_calculator, + self._matcher, + self._box_coder, + positive_class_weight=1.0, + negative_class_weight=1.0, + unmatched_cls_target=unmatched_cls_target) + + self._classification_loss = classification_loss + self._localization_loss = localization_loss + self._classification_loss_weight = classification_loss_weight + self._localization_loss_weight = localization_loss_weight + self._normalize_loss_by_num_matches = normalize_loss_by_num_matches + self._hard_example_miner = hard_example_miner + + self._image_resizer_fn = image_resizer_fn + self._non_max_suppression_fn = non_max_suppression_fn + self._score_conversion_fn = score_conversion_fn + + self._anchors = None + self._add_summaries = add_summaries + + @property + def anchors(self): + if not self._anchors: + raise RuntimeError('anchors have not been constructed yet!') + if not isinstance(self._anchors, box_list.BoxList): + raise RuntimeError('anchors should be a BoxList object, but is not.') + return self._anchors + + def preprocess(self, inputs): + """Feature-extractor specific preprocessing. + + See base class. + + Args: + inputs: a [batch, height_in, width_in, channels] float tensor representing + a batch of images with values between 0 and 255.0. + + Returns: + preprocessed_inputs: a [batch, height_out, width_out, channels] float + tensor representing a batch of images. + Raises: + ValueError: if inputs tensor does not have type tf.float32 + """ + if inputs.dtype is not tf.float32: + raise ValueError('`preprocess` expects a tf.float32 tensor') + with tf.name_scope('Preprocessor'): + # TODO: revisit whether to always use batch size as the number of + # parallel iterations vs allow for dynamic batching. + resized_inputs = tf.map_fn(self._image_resizer_fn, + elems=inputs, + dtype=tf.float32) + return self._feature_extractor.preprocess(resized_inputs) + + def predict(self, preprocessed_inputs): + """Predicts unpostprocessed tensors from input tensor. + + This function takes an input batch of images and runs it through the forward + pass of the network to yield unpostprocessesed predictions. + + A side effect of calling the predict method is that self._anchors is + populated with a box_list.BoxList of anchors. These anchors must be + constructed before the postprocess or loss functions can be called. + + Args: + preprocessed_inputs: a [batch, height, width, channels] image tensor. + + Returns: + prediction_dict: a dictionary holding "raw" prediction tensors: + 1) box_encodings: 4-D float tensor of shape [batch_size, num_anchors, + box_code_dimension] containing predicted boxes. + 2) class_predictions_with_background: 3-D float tensor of shape + [batch_size, num_anchors, num_classes+1] containing class predictions + (logits) for each of the anchors. Note that this tensor *includes* + background class predictions (at class index 0). + 3) feature_maps: a list of tensors where the ith tensor has shape + [batch, height_i, width_i, depth_i]. + """ + with tf.variable_scope(None, self._extract_features_scope, + [preprocessed_inputs]): + feature_maps = self._feature_extractor.extract_features( + preprocessed_inputs) + feature_map_spatial_dims = self._get_feature_map_spatial_dims(feature_maps) + self._anchors = self._anchor_generator.generate(feature_map_spatial_dims) + (box_encodings, class_predictions_with_background + ) = self._add_box_predictions_to_feature_maps(feature_maps) + predictions_dict = { + 'box_encodings': box_encodings, + 'class_predictions_with_background': class_predictions_with_background, + 'feature_maps': feature_maps + } + return predictions_dict + + def _add_box_predictions_to_feature_maps(self, feature_maps): + """Adds box predictors to each feature map and returns concatenated results. + + Args: + feature_maps: a list of tensors where the ith tensor has shape + [batch, height_i, width_i, depth_i] + + Returns: + box_encodings: 4-D float tensor of shape [batch_size, num_anchors, + box_code_dimension] containing predicted boxes. + class_predictions_with_background: 2-D float tensor of shape + [batch_size, num_anchors, num_classes+1] containing class predictions + (logits) for each of the anchors. Note that this tensor *includes* + background class predictions (at class index 0). + + Raises: + RuntimeError: if the number of feature maps extracted via the + extract_features method does not match the length of the + num_anchors_per_locations list that was passed to the constructor. + RuntimeError: if box_encodings from the box_predictor does not have + shape of the form [batch_size, num_anchors, 1, code_size]. + """ + num_anchors_per_location_list = ( + self._anchor_generator.num_anchors_per_location()) + if len(feature_maps) != len(num_anchors_per_location_list): + raise RuntimeError('the number of feature maps must match the ' + 'length of self.anchors.NumAnchorsPerLocation().') + box_encodings_list = [] + cls_predictions_with_background_list = [] + for idx, (feature_map, num_anchors_per_location + ) in enumerate(zip(feature_maps, num_anchors_per_location_list)): + box_predictor_scope = 'BoxPredictor_{}'.format(idx) + box_predictions = self._box_predictor.predict(feature_map, + num_anchors_per_location, + box_predictor_scope) + box_encodings = box_predictions[bpredictor.BOX_ENCODINGS] + cls_predictions_with_background = box_predictions[ + bpredictor.CLASS_PREDICTIONS_WITH_BACKGROUND] + + box_encodings_shape = box_encodings.get_shape().as_list() + if len(box_encodings_shape) != 4 or box_encodings_shape[2] != 1: + raise RuntimeError('box_encodings from the box_predictor must be of ' + 'shape `[batch_size, num_anchors, 1, code_size]`; ' + 'actual shape', box_encodings_shape) + box_encodings = tf.squeeze(box_encodings, axis=2) + box_encodings_list.append(box_encodings) + cls_predictions_with_background_list.append( + cls_predictions_with_background) + + num_predictions = sum( + [tf.shape(box_encodings)[1] for box_encodings in box_encodings_list]) + num_anchors = self.anchors.num_boxes() + anchors_assert = tf.assert_equal(num_anchors, num_predictions, [ + 'Mismatch: number of anchors vs number of predictions', num_anchors, + num_predictions + ]) + with tf.control_dependencies([anchors_assert]): + box_encodings = tf.concat(box_encodings_list, 1) + class_predictions_with_background = tf.concat( + cls_predictions_with_background_list, 1) + return box_encodings, class_predictions_with_background + + def _get_feature_map_spatial_dims(self, feature_maps): + """Return list of spatial dimensions for each feature map in a list. + + Args: + feature_maps: a list of tensors where the ith tensor has shape + [batch, height_i, width_i, depth_i]. + + Returns: + a list of pairs (height, width) for each feature map in feature_maps + """ + feature_map_shapes = [ + feature_map.get_shape().as_list() for feature_map in feature_maps + ] + return [(shape[1], shape[2]) for shape in feature_map_shapes] + + def postprocess(self, prediction_dict): + """Converts prediction tensors to final detections. + + This function converts raw predictions tensors to final detection results by + slicing off the background class, decoding box predictions and applying + non max suppression and clipping to the image window. + + See base class for output format conventions. Note also that by default, + scores are to be interpreted as logits, but if a score_conversion_fn is + used, then scores are remapped (and may thus have a different + interpretation). + + Args: + prediction_dict: a dictionary holding prediction tensors with + 1) box_encodings: 4-D float tensor of shape [batch_size, num_anchors, + box_code_dimension] containing predicted boxes. + 2) class_predictions_with_background: 2-D float tensor of shape + [batch_size, num_anchors, num_classes+1] containing class predictions + (logits) for each of the anchors. Note that this tensor *includes* + background class predictions. + + Returns: + detections: a dictionary containing the following fields + detection_boxes: [batch, max_detection, 4] + detection_scores: [batch, max_detections] + detection_classes: [batch, max_detections] + num_detections: [batch] + Raises: + ValueError: if prediction_dict does not contain `box_encodings` or + `class_predictions_with_background` fields. + """ + if ('box_encodings' not in prediction_dict or + 'class_predictions_with_background' not in prediction_dict): + raise ValueError('prediction_dict does not contain expected entries.') + with tf.name_scope('Postprocessor'): + box_encodings = prediction_dict['box_encodings'] + class_predictions = prediction_dict['class_predictions_with_background'] + detection_boxes = bcoder.batch_decode(box_encodings, self._box_coder, + self.anchors) + detection_boxes = tf.expand_dims(detection_boxes, axis=2) + + class_predictions_without_background = tf.slice(class_predictions, + [0, 0, 1], + [-1, -1, -1]) + detection_scores = self._score_conversion_fn( + class_predictions_without_background) + clip_window = tf.constant([0, 0, 1, 1], tf.float32) + detections = self._non_max_suppression_fn(detection_boxes, + detection_scores, + clip_window=clip_window) + return detections + + def loss(self, prediction_dict, scope=None): + """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 prediction tensors with + 1) box_encodings: 4-D float tensor of shape [batch_size, num_anchors, + box_code_dimension] containing predicted boxes. + 2) class_predictions_with_background: 2-D float tensor of shape + [batch_size, num_anchors, num_classes+1] containing class predictions + (logits) for each of the anchors. Note that this tensor *includes* + background class predictions. + scope: Optional scope name. + + Returns: + a dictionary mapping loss keys (`localization_loss` and + `classification_loss`) to scalar tensors representing corresponding loss + values. + """ + with tf.name_scope(scope, 'Loss', prediction_dict.values()): + (batch_cls_targets, batch_cls_weights, batch_reg_targets, + batch_reg_weights, match_list) = self._assign_targets( + self.groundtruth_lists(fields.BoxListFields.boxes), + self.groundtruth_lists(fields.BoxListFields.classes)) + if self._add_summaries: + self._summarize_input( + self.groundtruth_lists(fields.BoxListFields.boxes), match_list) + num_matches = tf.stack( + [match.num_matched_columns() for match in match_list]) + location_losses = self._localization_loss( + prediction_dict['box_encodings'], + batch_reg_targets, + weights=batch_reg_weights) + cls_losses = self._classification_loss( + prediction_dict['class_predictions_with_background'], + batch_cls_targets, + weights=batch_cls_weights) + + # Optionally apply hard mining on top of loss values + localization_loss = tf.reduce_sum(location_losses) + classification_loss = tf.reduce_sum(cls_losses) + if self._hard_example_miner: + (localization_loss, classification_loss) = self._apply_hard_mining( + location_losses, cls_losses, prediction_dict, match_list) + if self._add_summaries: + self._hard_example_miner.summarize() + + # Optionally normalize by number of positive matches + normalizer = tf.constant(1.0, dtype=tf.float32) + if self._normalize_loss_by_num_matches: + normalizer = tf.maximum(tf.to_float(tf.reduce_sum(num_matches)), 1.0) + + loss_dict = { + 'localization_loss': (self._localization_loss_weight / normalizer) * + localization_loss, + 'classification_loss': (self._classification_loss_weight / + normalizer) * classification_loss + } + return loss_dict + + def _assign_targets(self, groundtruth_boxes_list, groundtruth_classes_list): + """Assign groundtruth targets. + + Adds a background class to each one-hot encoding of groundtruth classes + and uses target assigner to obtain regression and classification targets. + + Args: + groundtruth_boxes_list: a list of 2-D 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 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. + + 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], + 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_list: a 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. + """ + groundtruth_boxlists = [ + box_list.BoxList(boxes) for boxes in groundtruth_boxes_list + ] + groundtruth_classes_with_background_list = [ + tf.pad(one_hot_encoding, [[0, 0], [1, 0]], mode='CONSTANT') + for one_hot_encoding in groundtruth_classes_list + ] + return target_assigner.batch_assign_targets( + self._target_assigner, self.anchors, groundtruth_boxlists, + groundtruth_classes_with_background_list) + + def _summarize_input(self, groundtruth_boxes_list, match_list): + """Creates tensorflow summaries for the input boxes and anchors. + + This function creates four summaries corresponding to the average + number (over images in a batch) of (1) groundtruth boxes, (2) anchors + marked as positive, (3) anchors marked as negative, and (4) anchors marked + as ignored. + + Args: + groundtruth_boxes_list: a list of 2-D tensors of shape [num_boxes, 4] + containing corners of the groundtruth boxes. + match_list: a 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. + """ + num_boxes_per_image = tf.stack( + [tf.shape(x)[0] for x in groundtruth_boxes_list]) + pos_anchors_per_image = tf.stack( + [match.num_matched_columns() for match in match_list]) + neg_anchors_per_image = tf.stack( + [match.num_unmatched_columns() for match in match_list]) + ignored_anchors_per_image = tf.stack( + [match.num_ignored_columns() for match in match_list]) + tf.summary.scalar('Input/AvgNumGroundtruthBoxesPerImage', + tf.reduce_mean(tf.to_float(num_boxes_per_image))) + tf.summary.scalar('Input/AvgNumPositiveAnchorsPerImage', + tf.reduce_mean(tf.to_float(pos_anchors_per_image))) + tf.summary.scalar('Input/AvgNumNegativeAnchorsPerImage', + tf.reduce_mean(tf.to_float(neg_anchors_per_image))) + tf.summary.scalar('Input/AvgNumIgnoredAnchorsPerImage', + tf.reduce_mean(tf.to_float(ignored_anchors_per_image))) + + def _apply_hard_mining(self, location_losses, cls_losses, prediction_dict, + match_list): + """Applies hard mining to anchorwise losses. + + Args: + location_losses: Float tensor of shape [batch_size, num_anchors] + representing anchorwise location losses. + cls_losses: Float tensor of shape [batch_size, num_anchors] + representing anchorwise classification losses. + prediction_dict: p a dictionary holding prediction tensors with + 1) box_encodings: 4-D float tensor of shape [batch_size, num_anchors, + box_code_dimension] containing predicted boxes. + 2) class_predictions_with_background: 2-D float tensor of shape + [batch_size, num_anchors, num_classes+1] containing class predictions + (logits) for each of the anchors. Note that this tensor *includes* + background class predictions. + match_list: a 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. + + 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. + """ + class_pred_shape = [-1, self.anchors.num_boxes_static(), self.num_classes] + class_predictions = tf.reshape( + tf.slice(prediction_dict['class_predictions_with_background'], + [0, 0, 1], class_pred_shape), class_pred_shape) + + decoded_boxes = bcoder.batch_decode(prediction_dict['box_encodings'], + self._box_coder, self.anchors) + decoded_box_tensors_list = tf.unstack(decoded_boxes) + class_prediction_list = tf.unstack(class_predictions) + decoded_boxlist_list = [] + for box_location, box_score in zip(decoded_box_tensors_list, + class_prediction_list): + decoded_boxlist = box_list.BoxList(box_location) + decoded_boxlist.add_field('scores', box_score) + decoded_boxlist_list.append(decoded_boxlist) + return self._hard_example_miner( + location_losses=location_losses, + cls_losses=cls_losses, + decoded_boxlist_list=decoded_boxlist_list, + match_list=match_list) + + def restore_fn(self, checkpoint_path, from_detection_checkpoint=True): + """Return callable for loading a checkpoint into the tensorflow graph. + + Args: + checkpoint_path: path to checkpoint to restore. + from_detection_checkpoint: whether to restore from a full detection + checkpoint (with compatible variable names) or to restore from a + classification checkpoint for initialization prior to training. + + Returns: + a callable which takes a tf.Session as input and loads a checkpoint when + run. + """ + variables_to_restore = {} + for variable in tf.all_variables(): + if variable.op.name.startswith(self._extract_features_scope): + var_name = variable.op.name + if not from_detection_checkpoint: + var_name = ( + re.split('^' + self._extract_features_scope + '/', var_name)[-1]) + variables_to_restore[var_name] = variable + # TODO: Load variables selectively using scopes. + variables_to_restore = ( + variables_helper.get_variables_available_in_checkpoint( + variables_to_restore, checkpoint_path)) + saver = tf.train.Saver(variables_to_restore) + + def restore(sess): + saver.restore(sess, checkpoint_path) + return restore diff --git a/object_detection/meta_architectures/ssd_meta_arch_test.py b/object_detection/meta_architectures/ssd_meta_arch_test.py new file mode 100644 index 000000000..8096da9a6 --- /dev/null +++ b/object_detection/meta_architectures/ssd_meta_arch_test.py @@ -0,0 +1,258 @@ +# 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.meta_architectures.ssd_meta_arch.""" +import functools +import numpy as np +import tensorflow as tf + +from tensorflow.python.training import saver as tf_saver +from object_detection.core import anchor_generator +from object_detection.core import box_list +from object_detection.core import losses +from object_detection.core import post_processing +from object_detection.core import region_similarity_calculator as sim_calc +from object_detection.meta_architectures import ssd_meta_arch +from object_detection.utils import test_utils + +slim = tf.contrib.slim + + +class FakeSSDFeatureExtractor(ssd_meta_arch.SSDFeatureExtractor): + + def __init__(self): + super(FakeSSDFeatureExtractor, self).__init__( + depth_multiplier=0, min_depth=0, conv_hyperparams=None) + + def preprocess(self, resized_inputs): + return tf.identity(resized_inputs) + + def extract_features(self, preprocessed_inputs): + with tf.variable_scope('mock_model'): + features = slim.conv2d(inputs=preprocessed_inputs, num_outputs=32, + kernel_size=[1, 1], scope='layer1') + return [features] + + +class MockAnchorGenerator2x2(anchor_generator.AnchorGenerator): + """Sets up a simple 2x2 anchor grid on the unit square.""" + + def name_scope(self): + return 'MockAnchorGenerator' + + def num_anchors_per_location(self): + return [1] + + def _generate(self, feature_map_shape_list): + return box_list.BoxList( + tf.constant([[0, 0, .5, .5], + [0, .5, .5, 1], + [.5, 0, 1, .5], + [.5, .5, 1, 1]], tf.float32)) + + +class SsdMetaArchTest(tf.test.TestCase): + + def setUp(self): + """Set up mock SSD model. + + Here we set up a simple mock SSD model that will always predict 4 + detections that happen to always be exactly the anchors that are set up + in the above MockAnchorGenerator. Because we let max_detections=5, + we will also always end up with an extra padded row in the detection + results. + """ + is_training = False + self._num_classes = 1 + mock_anchor_generator = MockAnchorGenerator2x2() + mock_box_predictor = test_utils.MockBoxPredictor( + is_training, self._num_classes) + mock_box_coder = test_utils.MockBoxCoder() + fake_feature_extractor = FakeSSDFeatureExtractor() + mock_matcher = test_utils.MockMatcher() + region_similarity_calculator = sim_calc.IouSimilarity() + + def image_resizer_fn(image): + return tf.identity(image) + + classification_loss = losses.WeightedSigmoidClassificationLoss( + anchorwise_output=True) + localization_loss = losses.WeightedSmoothL1LocalizationLoss( + anchorwise_output=True) + non_max_suppression_fn = functools.partial( + post_processing.batch_multiclass_non_max_suppression, + score_thresh=-20.0, + iou_thresh=1.0, + max_size_per_class=5, + max_total_size=5) + classification_loss_weight = 1.0 + localization_loss_weight = 1.0 + normalize_loss_by_num_matches = False + + # This hard example miner is expected to be a no-op. + hard_example_miner = losses.HardExampleMiner( + num_hard_examples=None, + iou_threshold=1.0) + + self._num_anchors = 4 + self._code_size = 4 + self._model = ssd_meta_arch.SSDMetaArch( + is_training, mock_anchor_generator, mock_box_predictor, mock_box_coder, + fake_feature_extractor, mock_matcher, region_similarity_calculator, + image_resizer_fn, non_max_suppression_fn, tf.identity, + classification_loss, localization_loss, classification_loss_weight, + localization_loss_weight, normalize_loss_by_num_matches, + hard_example_miner) + + def test_predict_results_have_correct_keys_and_shapes(self): + batch_size = 3 + preprocessed_input = tf.random_uniform((batch_size, 2, 2, 3), + dtype=tf.float32) + prediction_dict = self._model.predict(preprocessed_input) + + self.assertTrue('box_encodings' in prediction_dict) + self.assertTrue('class_predictions_with_background' in prediction_dict) + self.assertTrue('feature_maps' in prediction_dict) + + expected_box_encodings_shape_out = ( + batch_size, self._num_anchors, self._code_size) + expected_class_predictions_with_background_shape_out = ( + batch_size, self._num_anchors, self._num_classes+1) + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + prediction_out = sess.run(prediction_dict) + self.assertAllEqual(prediction_out['box_encodings'].shape, + expected_box_encodings_shape_out) + self.assertAllEqual( + prediction_out['class_predictions_with_background'].shape, + expected_class_predictions_with_background_shape_out) + + def test_postprocess_results_are_correct(self): + batch_size = 2 + preprocessed_input = tf.random_uniform((batch_size, 2, 2, 3), + dtype=tf.float32) + prediction_dict = self._model.predict(preprocessed_input) + detections = self._model.postprocess(prediction_dict) + + expected_boxes = np.array([[[0, 0, .5, .5], + [0, .5, .5, 1], + [.5, 0, 1, .5], + [.5, .5, 1, 1], + [0, 0, 0, 0]], + [[0, 0, .5, .5], + [0, .5, .5, 1], + [.5, 0, 1, .5], + [.5, .5, 1, 1], + [0, 0, 0, 0]]]) + expected_scores = np.array([[0, 0, 0, 0, 0], + [0, 0, 0, 0, 0]]) + expected_classes = np.array([[0, 0, 0, 0, 0], + [0, 0, 0, 0, 0]]) + expected_num_detections = np.array([4, 4]) + + self.assertTrue('detection_boxes' in detections) + self.assertTrue('detection_scores' in detections) + self.assertTrue('detection_classes' in detections) + self.assertTrue('num_detections' in detections) + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + detections_out = sess.run(detections) + self.assertAllClose(detections_out['detection_boxes'], expected_boxes) + self.assertAllClose(detections_out['detection_scores'], expected_scores) + self.assertAllClose(detections_out['detection_classes'], expected_classes) + self.assertAllClose(detections_out['num_detections'], + expected_num_detections) + + def test_loss_results_are_correct(self): + batch_size = 2 + preprocessed_input = tf.random_uniform((batch_size, 2, 2, 3), + dtype=tf.float32) + groundtruth_boxes_list = [tf.constant([[0, 0, .5, .5]], dtype=tf.float32), + tf.constant([[0, 0, .5, .5]], dtype=tf.float32)] + groundtruth_classes_list = [tf.constant([[1]], dtype=tf.float32), + tf.constant([[1]], dtype=tf.float32)] + self._model.provide_groundtruth(groundtruth_boxes_list, + groundtruth_classes_list) + prediction_dict = self._model.predict(preprocessed_input) + loss_dict = self._model.loss(prediction_dict) + + self.assertTrue('localization_loss' in loss_dict) + self.assertTrue('classification_loss' in loss_dict) + + expected_localization_loss = 0.0 + expected_classification_loss = (batch_size * self._num_anchors + * (self._num_classes+1) * np.log(2.0)) + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + losses_out = sess.run(loss_dict) + + self.assertAllClose(losses_out['localization_loss'], + expected_localization_loss) + self.assertAllClose(losses_out['classification_loss'], + expected_classification_loss) + + def test_restore_fn_detection(self): + init_op = tf.global_variables_initializer() + saver = tf_saver.Saver() + save_path = self.get_temp_dir() + with self.test_session() as sess: + sess.run(init_op) + saved_model_path = saver.save(sess, save_path) + restore_fn = self._model.restore_fn(saved_model_path, + from_detection_checkpoint=True) + restore_fn(sess) + for var in sess.run(tf.report_uninitialized_variables()): + self.assertNotIn('FeatureExtractor', var.name) + + def test_restore_fn_classification(self): + # Define mock tensorflow classification graph and save variables. + test_graph_classification = tf.Graph() + with test_graph_classification.as_default(): + image = tf.placeholder(dtype=tf.float32, shape=[1, 20, 20, 3]) + with tf.variable_scope('mock_model'): + net = slim.conv2d(image, num_outputs=32, kernel_size=1, scope='layer1') + slim.conv2d(net, num_outputs=3, kernel_size=1, scope='layer2') + + init_op = tf.global_variables_initializer() + saver = tf.train.Saver() + save_path = self.get_temp_dir() + with self.test_session() as sess: + sess.run(init_op) + saved_model_path = saver.save(sess, save_path) + + # Create tensorflow detection graph and load variables from + # classification checkpoint. + test_graph_detection = tf.Graph() + with test_graph_detection.as_default(): + inputs_shape = [2, 2, 2, 3] + inputs = tf.to_float(tf.random_uniform( + inputs_shape, minval=0, maxval=255, dtype=tf.int32)) + preprocessed_inputs = self._model.preprocess(inputs) + prediction_dict = self._model.predict(preprocessed_inputs) + self._model.postprocess(prediction_dict) + restore_fn = self._model.restore_fn(saved_model_path, + from_detection_checkpoint=False) + with self.test_session() as sess: + restore_fn(sess) + for var in sess.run(tf.report_uninitialized_variables()): + self.assertNotIn('FeatureExtractor', var.name) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/models/BUILD b/object_detection/models/BUILD new file mode 100644 index 000000000..f4af73682 --- /dev/null +++ b/object_detection/models/BUILD @@ -0,0 +1,135 @@ +# Tensorflow Object Detection API: Models. + +package( + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) + +# Apache 2.0 + +py_library( + name = "feature_map_generators", + srcs = [ + "feature_map_generators.py", + ], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/utils:ops", + ], +) + +py_test( + name = "feature_map_generators_test", + srcs = [ + "feature_map_generators_test.py", + ], + deps = [ + ":feature_map_generators", + "//tensorflow", + ], +) + +py_library( + name = "ssd_feature_extractor_test", + srcs = [ + "ssd_feature_extractor_test.py", + ], + deps = [ + "//tensorflow", + ], +) + +py_library( + name = "ssd_inception_v2_feature_extractor", + srcs = [ + "ssd_inception_v2_feature_extractor.py", + ], + deps = [ + ":feature_map_generators", + "//tensorflow", + "//tensorflow_models/object_detection/meta_architectures:ssd_meta_arch", + "//tensorflow_models/slim:inception_v2", + ], +) + +py_library( + name = "ssd_mobilenet_v1_feature_extractor", + srcs = ["ssd_mobilenet_v1_feature_extractor.py"], + deps = [ + ":feature_map_generators", + "//tensorflow", + "//tensorflow_models/object_detection/meta_architectures:ssd_meta_arch", + "//tensorflow_models/slim:mobilenet_v1", + ], +) + +py_test( + name = "ssd_inception_v2_feature_extractor_test", + srcs = [ + "ssd_inception_v2_feature_extractor_test.py", + ], + deps = [ + ":ssd_feature_extractor_test", + ":ssd_inception_v2_feature_extractor", + "//tensorflow", + ], +) + +py_test( + name = "ssd_mobilenet_v1_feature_extractor_test", + srcs = ["ssd_mobilenet_v1_feature_extractor_test.py"], + deps = [ + ":ssd_feature_extractor_test", + ":ssd_mobilenet_v1_feature_extractor", + "//tensorflow", + ], +) + +py_library( + name = "faster_rcnn_inception_resnet_v2_feature_extractor", + srcs = [ + "faster_rcnn_inception_resnet_v2_feature_extractor.py", + ], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/meta_architectures:faster_rcnn_meta_arch", + "//tensorflow_models/object_detection/utils:variables_helper", + "//tensorflow_models/slim:inception_resnet_v2", + ], +) + +py_test( + name = "faster_rcnn_inception_resnet_v2_feature_extractor_test", + srcs = [ + "faster_rcnn_inception_resnet_v2_feature_extractor_test.py", + ], + deps = [ + ":faster_rcnn_inception_resnet_v2_feature_extractor", + "//tensorflow", + ], +) + +py_library( + name = "faster_rcnn_resnet_v1_feature_extractor", + srcs = [ + "faster_rcnn_resnet_v1_feature_extractor.py", + ], + deps = [ + "//tensorflow", + "//tensorflow_models/object_detection/meta_architectures:faster_rcnn_meta_arch", + "//tensorflow_models/slim:resnet_utils", + "//tensorflow_models/slim:resnet_v1", + ], +) + +py_test( + name = "faster_rcnn_resnet_v1_feature_extractor_test", + srcs = [ + "faster_rcnn_resnet_v1_feature_extractor_test.py", + ], + deps = [ + ":faster_rcnn_resnet_v1_feature_extractor", + "//tensorflow", + ], +) diff --git a/object_detection/models/__init__.py b/object_detection/models/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/object_detection/models/faster_rcnn_inception_resnet_v2_feature_extractor.py b/object_detection/models/faster_rcnn_inception_resnet_v2_feature_extractor.py new file mode 100644 index 000000000..f8c86e0c0 --- /dev/null +++ b/object_detection/models/faster_rcnn_inception_resnet_v2_feature_extractor.py @@ -0,0 +1,216 @@ +# 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. +# ============================================================================== + +"""Inception Resnet v2 Faster R-CNN implementation. + +See "Inception-v4, Inception-ResNet and the Impact of Residual Connections on +Learning" by Szegedy et al. (https://arxiv.org/abs/1602.07261) +as well as +"Speed/accuracy trade-offs for modern convolutional object detectors" by +Huang et al. (https://arxiv.org/abs/1611.10012) +""" + +import tensorflow as tf + +from object_detection.meta_architectures import faster_rcnn_meta_arch +from object_detection.utils import variables_helper +from nets import inception_resnet_v2 + +slim = tf.contrib.slim + + +class FasterRCNNInceptionResnetV2FeatureExtractor( + faster_rcnn_meta_arch.FasterRCNNFeatureExtractor): + """Faster R-CNN with Inception Resnet v2 feature extractor implementation.""" + + def __init__(self, + is_training, + first_stage_features_stride, + reuse_weights=None, + weight_decay=0.0): + """Constructor. + + Args: + is_training: See base class. + first_stage_features_stride: See base class. + reuse_weights: See base class. + weight_decay: See base class. + + Raises: + ValueError: If `first_stage_features_stride` is not 8 or 16. + """ + if first_stage_features_stride != 8 and first_stage_features_stride != 16: + raise ValueError('`first_stage_features_stride` must be 8 or 16.') + super(FasterRCNNInceptionResnetV2FeatureExtractor, self).__init__( + is_training, first_stage_features_stride, reuse_weights, weight_decay) + + def preprocess(self, resized_inputs): + """Faster R-CNN with Inception Resnet v2 preprocessing. + + Maps pixel values to the range [-1, 1]. + + Args: + resized_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. + + """ + return (2.0 / 255.0) * resized_inputs - 1.0 + + def _extract_proposal_features(self, preprocessed_inputs, scope): + """Extracts first stage RPN features. + + Extracts features using the first half of the Inception Resnet v2 network. + We construct the network in `align_feature_maps=True` mode, which means + that all VALID paddings in the network are changed to SAME padding so that + the feature maps are aligned. + + Args: + preprocessed_inputs: A [batch, height, width, channels] float32 tensor + representing a batch of images. + scope: A scope name. + + Returns: + rpn_feature_map: A tensor with shape [batch, height, width, depth] + Raises: + InvalidArgumentError: If the spatial size of `preprocessed_inputs` + (height or width) is less than 33. + ValueError: If the created network is missing the required activation. + """ + if len(preprocessed_inputs.get_shape().as_list()) != 4: + raise ValueError('`preprocessed_inputs` must be 4 dimensional, got a ' + 'tensor of shape %s' % preprocessed_inputs.get_shape()) + + with slim.arg_scope(inception_resnet_v2.inception_resnet_v2_arg_scope( + weight_decay=self._weight_decay)): + # Forces is_training to False to disable batch norm update. + with slim.arg_scope([slim.batch_norm], is_training=False): + with tf.variable_scope('InceptionResnetV2', + reuse=self._reuse_weights) as scope: + rpn_feature_map, _ = ( + inception_resnet_v2.inception_resnet_v2_base( + preprocessed_inputs, final_endpoint='PreAuxLogits', + scope=scope, output_stride=self._first_stage_features_stride, + align_feature_maps=True)) + return rpn_feature_map + + def _extract_box_classifier_features(self, proposal_feature_maps, scope): + """Extracts second stage box classifier features. + + This function reconstructs the "second half" of the Inception ResNet v2 + network after the part defined in `_extract_proposal_features`. + + Args: + proposal_feature_maps: A 4-D float tensor with shape + [batch_size * self.max_num_proposals, crop_height, crop_width, depth] + representing the feature map cropped to each proposal. + scope: A scope name. + + Returns: + proposal_classifier_features: A 4-D float tensor with shape + [batch_size * self.max_num_proposals, height, width, depth] + representing box classifier features for each proposal. + """ + with tf.variable_scope('InceptionResnetV2', reuse=self._reuse_weights): + with slim.arg_scope(inception_resnet_v2.inception_resnet_v2_arg_scope( + weight_decay=self._weight_decay)): + # Forces is_training to False to disable batch norm update. + with slim.arg_scope([slim.batch_norm], is_training=False): + with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d], + stride=1, padding='SAME'): + with tf.variable_scope('Mixed_7a'): + with tf.variable_scope('Branch_0'): + tower_conv = slim.conv2d(proposal_feature_maps, + 256, 1, scope='Conv2d_0a_1x1') + tower_conv_1 = slim.conv2d( + tower_conv, 384, 3, stride=2, + padding='VALID', scope='Conv2d_1a_3x3') + with tf.variable_scope('Branch_1'): + tower_conv1 = slim.conv2d( + proposal_feature_maps, 256, 1, scope='Conv2d_0a_1x1') + tower_conv1_1 = slim.conv2d( + tower_conv1, 288, 3, stride=2, + padding='VALID', scope='Conv2d_1a_3x3') + with tf.variable_scope('Branch_2'): + tower_conv2 = slim.conv2d( + proposal_feature_maps, 256, 1, scope='Conv2d_0a_1x1') + tower_conv2_1 = slim.conv2d(tower_conv2, 288, 3, + scope='Conv2d_0b_3x3') + tower_conv2_2 = slim.conv2d( + tower_conv2_1, 320, 3, stride=2, + padding='VALID', scope='Conv2d_1a_3x3') + with tf.variable_scope('Branch_3'): + tower_pool = slim.max_pool2d( + proposal_feature_maps, 3, stride=2, padding='VALID', + scope='MaxPool_1a_3x3') + net = tf.concat( + [tower_conv_1, tower_conv1_1, tower_conv2_2, tower_pool], 3) + net = slim.repeat(net, 9, inception_resnet_v2.block8, scale=0.20) + net = inception_resnet_v2.block8(net, activation_fn=None) + proposal_classifier_features = slim.conv2d( + net, 1536, 1, scope='Conv2d_7b_1x1') + return proposal_classifier_features + + def restore_from_classification_checkpoint_fn( + self, + checkpoint_path, + first_stage_feature_extractor_scope, + second_stage_feature_extractor_scope): + """Returns callable for loading a checkpoint into the tensorflow graph. + + Note that this overrides the default implementation in + faster_rcnn_meta_arch.FasterRCNNFeatureExtractor which does not work for + InceptionResnetV2 checkpoints. + + TODO: revisit whether it's possible to force the `Repeat` namescope as + created in `_extract_box_classifier_features` to start counting at 2 (e.g. + `Repeat_2`) so that the default restore_fn can be used. + + Args: + checkpoint_path: Path to checkpoint to restore. + first_stage_feature_extractor_scope: A scope name for the first stage + feature extractor. + second_stage_feature_extractor_scope: A scope name for the second stage + feature extractor. + + Returns: + a callable which takes a tf.Session as input and loads a checkpoint when + run. + """ + variables_to_restore = {} + for variable in tf.global_variables(): + if variable.op.name.startswith( + first_stage_feature_extractor_scope): + var_name = variable.op.name.replace( + first_stage_feature_extractor_scope + '/', '') + variables_to_restore[var_name] = variable + if variable.op.name.startswith( + second_stage_feature_extractor_scope): + var_name = variable.op.name.replace( + second_stage_feature_extractor_scope + + '/InceptionResnetV2/Repeat', 'InceptionResnetV2/Repeat_2') + var_name = var_name.replace( + second_stage_feature_extractor_scope + '/', '') + variables_to_restore[var_name] = variable + variables_to_restore = ( + variables_helper.get_variables_available_in_checkpoint( + variables_to_restore, checkpoint_path)) + saver = tf.train.Saver(variables_to_restore) + def restore(sess): + saver.restore(sess, checkpoint_path) + return restore diff --git a/object_detection/models/faster_rcnn_inception_resnet_v2_feature_extractor_test.py b/object_detection/models/faster_rcnn_inception_resnet_v2_feature_extractor_test.py new file mode 100644 index 000000000..cdb70187c --- /dev/null +++ b/object_detection/models/faster_rcnn_inception_resnet_v2_feature_extractor_test.py @@ -0,0 +1,108 @@ +# 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 models.faster_rcnn_inception_resnet_v2_feature_extractor.""" + +import tensorflow as tf + +from object_detection.models import faster_rcnn_inception_resnet_v2_feature_extractor as frcnn_inc_res + + +class FasterRcnnInceptionResnetV2FeatureExtractorTest(tf.test.TestCase): + + def _build_feature_extractor(self, first_stage_features_stride): + return frcnn_inc_res.FasterRCNNInceptionResnetV2FeatureExtractor( + is_training=False, + first_stage_features_stride=first_stage_features_stride, + reuse_weights=None, + weight_decay=0.0) + + def test_extract_proposal_features_returns_expected_size(self): + feature_extractor = self._build_feature_extractor( + first_stage_features_stride=16) + preprocessed_inputs = tf.random_uniform( + [1, 299, 299, 3], maxval=255, dtype=tf.float32) + rpn_feature_map = feature_extractor.extract_proposal_features( + preprocessed_inputs, scope='TestScope') + features_shape = tf.shape(rpn_feature_map) + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + features_shape_out = sess.run(features_shape) + self.assertAllEqual(features_shape_out, [1, 19, 19, 1088]) + + def test_extract_proposal_features_stride_eight(self): + feature_extractor = self._build_feature_extractor( + first_stage_features_stride=8) + preprocessed_inputs = tf.random_uniform( + [1, 224, 224, 3], maxval=255, dtype=tf.float32) + rpn_feature_map = feature_extractor.extract_proposal_features( + preprocessed_inputs, scope='TestScope') + features_shape = tf.shape(rpn_feature_map) + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + features_shape_out = sess.run(features_shape) + self.assertAllEqual(features_shape_out, [1, 28, 28, 1088]) + + def test_extract_proposal_features_half_size_input(self): + feature_extractor = self._build_feature_extractor( + first_stage_features_stride=16) + preprocessed_inputs = tf.random_uniform( + [1, 112, 112, 3], maxval=255, dtype=tf.float32) + rpn_feature_map = feature_extractor.extract_proposal_features( + preprocessed_inputs, scope='TestScope') + features_shape = tf.shape(rpn_feature_map) + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + features_shape_out = sess.run(features_shape) + self.assertAllEqual(features_shape_out, [1, 7, 7, 1088]) + + def test_extract_proposal_features_dies_on_invalid_stride(self): + with self.assertRaises(ValueError): + self._build_feature_extractor(first_stage_features_stride=99) + + def test_extract_proposal_features_dies_with_incorrect_rank_inputs(self): + feature_extractor = self._build_feature_extractor( + first_stage_features_stride=16) + preprocessed_inputs = tf.random_uniform( + [224, 224, 3], maxval=255, dtype=tf.float32) + with self.assertRaises(ValueError): + feature_extractor.extract_proposal_features( + preprocessed_inputs, scope='TestScope') + + def test_extract_box_classifier_features_returns_expected_size(self): + feature_extractor = self._build_feature_extractor( + first_stage_features_stride=16) + proposal_feature_maps = tf.random_uniform( + [2, 17, 17, 1088], maxval=255, dtype=tf.float32) + proposal_classifier_features = ( + feature_extractor.extract_box_classifier_features( + proposal_feature_maps, scope='TestScope')) + features_shape = tf.shape(proposal_classifier_features) + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + features_shape_out = sess.run(features_shape) + self.assertAllEqual(features_shape_out, [2, 8, 8, 1536]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/models/faster_rcnn_resnet_v1_feature_extractor.py b/object_detection/models/faster_rcnn_resnet_v1_feature_extractor.py new file mode 100644 index 000000000..d71c62453 --- /dev/null +++ b/object_detection/models/faster_rcnn_resnet_v1_feature_extractor.py @@ -0,0 +1,235 @@ +# 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. +# ============================================================================== + +"""Resnet V1 Faster R-CNN implementation. + +See "Deep Residual Learning for Image Recognition" by He et al., 2015. +https://arxiv.org/abs/1512.03385 + +Note: this implementation assumes that the classification checkpoint used +to finetune this model is trained using the same configuration as that of +the MSRA provided checkpoints +(see https://github.com/KaimingHe/deep-residual-networks), e.g., with +same preprocessing, batch norm scaling, etc. +""" +import tensorflow as tf + +from object_detection.meta_architectures import faster_rcnn_meta_arch +from nets import resnet_utils +from nets import resnet_v1 + +slim = tf.contrib.slim + + +class FasterRCNNResnetV1FeatureExtractor( + faster_rcnn_meta_arch.FasterRCNNFeatureExtractor): + """Faster R-CNN Resnet V1 feature extractor implementation.""" + + def __init__(self, + architecture, + resnet_model, + is_training, + first_stage_features_stride, + reuse_weights=None, + weight_decay=0.0): + """Constructor. + + Args: + architecture: Architecture name of the Resnet V1 model. + resnet_model: Definition of the Resnet V1 model. + is_training: See base class. + first_stage_features_stride: See base class. + reuse_weights: See base class. + weight_decay: See base class. + + Raises: + ValueError: If `first_stage_features_stride` is not 8 or 16. + """ + if first_stage_features_stride != 8 and first_stage_features_stride != 16: + raise ValueError('`first_stage_features_stride` must be 8 or 16.') + self._architecture = architecture + self._resnet_model = resnet_model + super(FasterRCNNResnetV1FeatureExtractor, self).__init__( + is_training, first_stage_features_stride, reuse_weights, weight_decay) + + def preprocess(self, resized_inputs): + """Faster R-CNN Resnet V1 preprocessing. + + VGG style channel mean subtraction as described here: + https://gist.github.com/ksimonyan/211839e770f7b538e2d8#file-readme-md + + Args: + resized_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. + + """ + channel_means = [123.68, 116.779, 103.939] + return resized_inputs - [[channel_means]] + + def _extract_proposal_features(self, preprocessed_inputs, scope): + """Extracts first stage RPN features. + + Args: + preprocessed_inputs: A [batch, height, width, channels] float32 tensor + representing a batch of images. + scope: A scope name. + + Returns: + rpn_feature_map: A tensor with shape [batch, height, width, depth] + Raises: + InvalidArgumentError: If the spatial size of `preprocessed_inputs` + (height or width) is less than 33. + ValueError: If the created network is missing the required activation. + """ + if len(preprocessed_inputs.get_shape().as_list()) != 4: + raise ValueError('`preprocessed_inputs` must be 4 dimensional, got a ' + 'tensor of shape %s' % preprocessed_inputs.get_shape()) + shape_assert = tf.Assert( + tf.logical_and( + tf.greater_equal(tf.shape(preprocessed_inputs)[1], 33), + tf.greater_equal(tf.shape(preprocessed_inputs)[2], 33)), + ['image size must at least be 33 in both height and width.']) + + with tf.control_dependencies([shape_assert]): + # Disables batchnorm for fine-tuning with smaller batch sizes. + # TODO: Figure out if it is needed when image batch size is bigger. + with slim.arg_scope( + resnet_utils.resnet_arg_scope( + batch_norm_epsilon=1e-5, + batch_norm_scale=True, + weight_decay=self._weight_decay)): + with tf.variable_scope( + self._architecture, reuse=self._reuse_weights) as var_scope: + _, activations = self._resnet_model( + preprocessed_inputs, + num_classes=None, + is_training=False, + global_pool=False, + output_stride=self._first_stage_features_stride, + scope=var_scope) + + handle = scope + '/%s/block3' % self._architecture + return activations[handle] + + def _extract_box_classifier_features(self, proposal_feature_maps, scope): + """Extracts second stage box classifier features. + + Args: + proposal_feature_maps: A 4-D float tensor with shape + [batch_size * self.max_num_proposals, crop_height, crop_width, depth] + representing the feature map cropped to each proposal. + scope: A scope name (unused). + + Returns: + proposal_classifier_features: A 4-D float tensor with shape + [batch_size * self.max_num_proposals, height, width, depth] + representing box classifier features for each proposal. + """ + with tf.variable_scope(self._architecture, reuse=self._reuse_weights): + with slim.arg_scope( + resnet_utils.resnet_arg_scope( + batch_norm_epsilon=1e-5, + batch_norm_scale=True, + weight_decay=self._weight_decay)): + with slim.arg_scope([slim.batch_norm], is_training=False): + blocks = [ + resnet_utils.Block('block4', resnet_v1.bottleneck, [{ + 'depth': 2048, + 'depth_bottleneck': 512, + 'stride': 1 + }] * 3) + ] + proposal_classifier_features = resnet_utils.stack_blocks_dense( + proposal_feature_maps, blocks) + return proposal_classifier_features + + +class FasterRCNNResnet50FeatureExtractor(FasterRCNNResnetV1FeatureExtractor): + """Faster R-CNN Resnet 50 feature extractor implementation.""" + + def __init__(self, + is_training, + first_stage_features_stride, + reuse_weights=None, + weight_decay=0.0): + """Constructor. + + Args: + is_training: See base class. + first_stage_features_stride: See base class. + reuse_weights: See base class. + weight_decay: See base class. + + Raises: + ValueError: If `first_stage_features_stride` is not 8 or 16, + or if `architecture` is not supported. + """ + super(FasterRCNNResnet50FeatureExtractor, self).__init__( + 'resnet_v1_50', resnet_v1.resnet_v1_50, is_training, + first_stage_features_stride, reuse_weights, weight_decay) + + +class FasterRCNNResnet101FeatureExtractor(FasterRCNNResnetV1FeatureExtractor): + """Faster R-CNN Resnet 101 feature extractor implementation.""" + + def __init__(self, + is_training, + first_stage_features_stride, + reuse_weights=None, + weight_decay=0.0): + """Constructor. + + Args: + is_training: See base class. + first_stage_features_stride: See base class. + reuse_weights: See base class. + weight_decay: See base class. + + Raises: + ValueError: If `first_stage_features_stride` is not 8 or 16, + or if `architecture` is not supported. + """ + super(FasterRCNNResnet101FeatureExtractor, self).__init__( + 'resnet_v1_101', resnet_v1.resnet_v1_101, is_training, + first_stage_features_stride, reuse_weights, weight_decay) + + +class FasterRCNNResnet152FeatureExtractor(FasterRCNNResnetV1FeatureExtractor): + """Faster R-CNN Resnet 152 feature extractor implementation.""" + + def __init__(self, + is_training, + first_stage_features_stride, + reuse_weights=None, + weight_decay=0.0): + """Constructor. + + Args: + is_training: See base class. + first_stage_features_stride: See base class. + reuse_weights: See base class. + weight_decay: See base class. + + Raises: + ValueError: If `first_stage_features_stride` is not 8 or 16, + or if `architecture` is not supported. + """ + super(FasterRCNNResnet152FeatureExtractor, self).__init__( + 'resnet_v1_152', resnet_v1.resnet_v1_152, is_training, + first_stage_features_stride, reuse_weights, weight_decay) diff --git a/object_detection/models/faster_rcnn_resnet_v1_feature_extractor_test.py b/object_detection/models/faster_rcnn_resnet_v1_feature_extractor_test.py new file mode 100644 index 000000000..57ec5793a --- /dev/null +++ b/object_detection/models/faster_rcnn_resnet_v1_feature_extractor_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 object_detection.models.faster_rcnn_resnet_v1_feature_extractor.""" + +import numpy as np +import tensorflow as tf + +from object_detection.models import faster_rcnn_resnet_v1_feature_extractor as faster_rcnn_resnet_v1 + + +class FasterRcnnResnetV1FeatureExtractorTest(tf.test.TestCase): + + def _build_feature_extractor(self, + first_stage_features_stride, + architecture='resnet_v1_101'): + feature_extractor_map = { + 'resnet_v1_50': + faster_rcnn_resnet_v1.FasterRCNNResnet50FeatureExtractor, + 'resnet_v1_101': + faster_rcnn_resnet_v1.FasterRCNNResnet101FeatureExtractor, + 'resnet_v1_152': + faster_rcnn_resnet_v1.FasterRCNNResnet152FeatureExtractor + } + return feature_extractor_map[architecture]( + is_training=False, + first_stage_features_stride=first_stage_features_stride, + reuse_weights=None, + weight_decay=0.0) + + def test_extract_proposal_features_returns_expected_size(self): + for architecture in ['resnet_v1_50', 'resnet_v1_101', 'resnet_v1_152']: + feature_extractor = self._build_feature_extractor( + first_stage_features_stride=16, architecture=architecture) + preprocessed_inputs = tf.random_uniform( + [4, 224, 224, 3], maxval=255, dtype=tf.float32) + rpn_feature_map = feature_extractor.extract_proposal_features( + preprocessed_inputs, scope='TestScope') + features_shape = tf.shape(rpn_feature_map) + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + features_shape_out = sess.run(features_shape) + self.assertAllEqual(features_shape_out, [4, 14, 14, 1024]) + + def test_extract_proposal_features_stride_eight(self): + feature_extractor = self._build_feature_extractor( + first_stage_features_stride=8) + preprocessed_inputs = tf.random_uniform( + [4, 224, 224, 3], maxval=255, dtype=tf.float32) + rpn_feature_map = feature_extractor.extract_proposal_features( + preprocessed_inputs, scope='TestScope') + features_shape = tf.shape(rpn_feature_map) + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + features_shape_out = sess.run(features_shape) + self.assertAllEqual(features_shape_out, [4, 28, 28, 1024]) + + def test_extract_proposal_features_half_size_input(self): + feature_extractor = self._build_feature_extractor( + first_stage_features_stride=16) + preprocessed_inputs = tf.random_uniform( + [1, 112, 112, 3], maxval=255, dtype=tf.float32) + rpn_feature_map = feature_extractor.extract_proposal_features( + preprocessed_inputs, scope='TestScope') + features_shape = tf.shape(rpn_feature_map) + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + features_shape_out = sess.run(features_shape) + self.assertAllEqual(features_shape_out, [1, 7, 7, 1024]) + + def test_extract_proposal_features_dies_on_invalid_stride(self): + with self.assertRaises(ValueError): + self._build_feature_extractor(first_stage_features_stride=99) + + def test_extract_proposal_features_dies_on_very_small_images(self): + feature_extractor = self._build_feature_extractor( + first_stage_features_stride=16) + preprocessed_inputs = tf.placeholder(tf.float32, (4, None, None, 3)) + rpn_feature_map = feature_extractor.extract_proposal_features( + preprocessed_inputs, scope='TestScope') + features_shape = tf.shape(rpn_feature_map) + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + with self.assertRaises(tf.errors.InvalidArgumentError): + sess.run( + features_shape, + feed_dict={preprocessed_inputs: np.random.rand(4, 32, 32, 3)}) + + def test_extract_proposal_features_dies_with_incorrect_rank_inputs(self): + feature_extractor = self._build_feature_extractor( + first_stage_features_stride=16) + preprocessed_inputs = tf.random_uniform( + [224, 224, 3], maxval=255, dtype=tf.float32) + with self.assertRaises(ValueError): + feature_extractor.extract_proposal_features( + preprocessed_inputs, scope='TestScope') + + def test_extract_box_classifier_features_returns_expected_size(self): + feature_extractor = self._build_feature_extractor( + first_stage_features_stride=16) + proposal_feature_maps = tf.random_uniform( + [3, 7, 7, 1024], maxval=255, dtype=tf.float32) + proposal_classifier_features = ( + feature_extractor.extract_box_classifier_features( + proposal_feature_maps, scope='TestScope')) + features_shape = tf.shape(proposal_classifier_features) + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + features_shape_out = sess.run(features_shape) + self.assertAllEqual(features_shape_out, [3, 7, 7, 2048]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/models/feature_map_generators.py b/object_detection/models/feature_map_generators.py new file mode 100644 index 000000000..44e7dd0a3 --- /dev/null +++ b/object_detection/models/feature_map_generators.py @@ -0,0 +1,179 @@ +# 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 generate a list of feature maps based on image features. + +Provides several feature map generators that can be used to build object +detection feature extractors. + +Object detection feature extractors usually are built by stacking two components +- A base feature extractor such as Inception V3 and a feature map generator. +Feature map generators build on the base feature extractors and produce a list +of final feature maps. +""" +import collections +import tensorflow as tf +from object_detection.utils import ops +slim = tf.contrib.slim + + +def get_depth_fn(depth_multiplier, min_depth): + """Builds a callable to compute depth (output channels) of conv filters. + + Args: + depth_multiplier: a multiplier for the nominal depth. + min_depth: a lower bound on the depth of filters. + + Returns: + A callable that takes in a nominal depth and returns the depth to use. + """ + def multiply_depth(depth): + new_depth = int(depth * depth_multiplier) + return max(new_depth, min_depth) + return multiply_depth + + +def multi_resolution_feature_maps(feature_map_layout, depth_multiplier, + min_depth, insert_1x1_conv, image_features): + """Generates multi resolution feature maps from input image features. + + Generates multi-scale feature maps for detection as in the SSD papers by + Liu et al: https://arxiv.org/pdf/1512.02325v2.pdf, See Sec 2.1. + + More specifically, it performs the following two tasks: + 1) If a layer name is provided in the configuration, returns that layer as a + feature map. + 2) If a layer name is left as an empty string, constructs a new feature map + based on the spatial shape and depth configuration. Note that the current + implementation only supports generating new layers using convolution of + stride 2 resulting in a spatial resolution reduction by a factor of 2. + + An example of the configuration for Inception V3: + { + 'from_layer': ['Mixed_5d', 'Mixed_6e', 'Mixed_7c', '', '', ''], + 'layer_depth': [-1, -1, -1, 512, 256, 128], + 'anchor_strides': [16, 32, 64, -1, -1, -1] + } + + Args: + feature_map_layout: Dictionary of specifications for the feature map + layouts in the following format (Inception V2/V3 respectively): + { + 'from_layer': ['Mixed_3c', 'Mixed_4c', 'Mixed_5c', '', '', ''], + 'layer_depth': [-1, -1, -1, 512, 256, 128], + 'anchor_strides': [16, 32, 64, -1, -1, -1] + } + or + { + 'from_layer': ['Mixed_5d', 'Mixed_6e', 'Mixed_7c', '', '', '', ''], + 'layer_depth': [-1, -1, -1, 512, 256, 128], + 'anchor_strides': [16, 32, 64, -1, -1, -1] + } + If 'from_layer' is specified, the specified feature map is directly used + as a box predictor layer, and the layer_depth is directly infered from the + feature map (instead of using the provided 'layer_depth' parameter). In + this case, our convention is to set 'layer_depth' to -1 for clarity. + Otherwise, if 'from_layer' is an empty string, then the box predictor + layer will be built from the previous layer using convolution operations. + Note that the current implementation only supports generating new layers + using convolutions of stride 2 (resulting in a spatial resolution + reduction by a factor of 2), and will be extended to a more flexible + design. Finally, the optional 'anchor_strides' can be used to specify the + anchor stride at each layer where 'from_layer' is specified. Our + convention is to set 'anchor_strides' to -1 whenever at the positions that + 'from_layer' is an empty string, and anchor strides at these layers will + be inferred from the previous layer's anchor strides and the current + layer's stride length. In the case where 'anchor_strides' is not + specified, the anchor strides will default to the image width and height + divided by the number of anchors. + depth_multiplier: Depth multiplier for convolutional layers. + min_depth: Minimum depth for convolutional layers. + insert_1x1_conv: A boolean indicating whether an additional 1x1 convolution + should be inserted before shrinking the feature map. + image_features: A dictionary of handles to activation tensors from the + base feature extractor. + + Returns: + feature_maps: an OrderedDict mapping keys (feature map names) to + tensors where each tensor has shape [batch, height_i, width_i, depth_i]. + + Raises: + ValueError: if the number entries in 'from_layer' and + 'layer_depth' do not match. + ValueError: if the generated layer does not have the same resolution + as specified. + """ + depth_fn = get_depth_fn(depth_multiplier, min_depth) + + feature_map_keys = [] + feature_maps = [] + base_from_layer = '' + feature_map_strides = None + use_depthwise = False + if 'anchor_strides' in feature_map_layout: + feature_map_strides = (feature_map_layout['anchor_strides']) + if 'use_depthwise' in feature_map_layout: + use_depthwise = feature_map_layout['use_depthwise'] + for index, (from_layer, layer_depth) in enumerate( + zip(feature_map_layout['from_layer'], feature_map_layout['layer_depth'])): + if from_layer: + feature_map = image_features[from_layer] + base_from_layer = from_layer + feature_map_keys.append(from_layer) + else: + pre_layer = feature_maps[-1] + intermediate_layer = pre_layer + if insert_1x1_conv: + layer_name = '{}_1_Conv2d_{}_1x1_{}'.format( + base_from_layer, index, depth_fn(layer_depth / 2)) + intermediate_layer = slim.conv2d( + pre_layer, + depth_fn(layer_depth / 2), [1, 1], + padding='SAME', + stride=1, + scope=layer_name) + stride = 2 + layer_name = '{}_2_Conv2d_{}_3x3_s2_{}'.format( + base_from_layer, index, depth_fn(layer_depth)) + if use_depthwise: + feature_map = slim.separable_conv2d( + ops.pad_to_multiple(intermediate_layer, stride), + None, [3, 3], + depth_multiplier=1, + padding='SAME', + stride=stride, + scope=layer_name + '_depthwise') + feature_map = slim.conv2d( + feature_map, + depth_fn(layer_depth), [1, 1], + padding='SAME', + stride=1, + scope=layer_name) + else: + feature_map = slim.conv2d( + ops.pad_to_multiple(intermediate_layer, stride), + depth_fn(layer_depth), [3, 3], + padding='SAME', + stride=stride, + scope=layer_name) + + if (index > 0 and feature_map_strides and + feature_map_strides[index - 1] > 0): + feature_map_strides[index] = ( + stride * feature_map_strides[index - 1]) + feature_map_keys.append(layer_name) + feature_maps.append(feature_map) + return collections.OrderedDict( + [(x, y) for (x, y) in zip(feature_map_keys, feature_maps)]) diff --git a/object_detection/models/feature_map_generators_test.py b/object_detection/models/feature_map_generators_test.py new file mode 100644 index 000000000..690723db1 --- /dev/null +++ b/object_detection/models/feature_map_generators_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 feature map generators.""" + +import tensorflow as tf + +from object_detection.models import feature_map_generators + +INCEPTION_V2_LAYOUT = { + 'from_layer': ['Mixed_3c', 'Mixed_4c', 'Mixed_5c', '', '', ''], + 'layer_depth': [-1, -1, -1, 512, 256, 256], + 'anchor_strides': [16, 32, 64, -1, -1, -1], + 'layer_target_norm': [20.0, -1, -1, -1, -1, -1], +} + +INCEPTION_V3_LAYOUT = { + 'from_layer': ['Mixed_5d', 'Mixed_6e', 'Mixed_7c', '', '', ''], + 'layer_depth': [-1, -1, -1, 512, 256, 128], + 'anchor_strides': [16, 32, 64, -1, -1, -1], + 'aspect_ratios': [1.0, 2.0, 1.0/2, 3.0, 1.0/3] +} + + +# TODO: add tests with different anchor strides. +class MultiResolutionFeatureMapGeneratorTest(tf.test.TestCase): + + def test_get_expected_feature_map_shapes_with_inception_v2(self): + image_features = { + 'Mixed_3c': tf.random_uniform([4, 28, 28, 256], dtype=tf.float32), + 'Mixed_4c': tf.random_uniform([4, 14, 14, 576], dtype=tf.float32), + 'Mixed_5c': tf.random_uniform([4, 7, 7, 1024], dtype=tf.float32) + } + feature_maps = feature_map_generators.multi_resolution_feature_maps( + feature_map_layout=INCEPTION_V2_LAYOUT, + depth_multiplier=1, + min_depth=32, + insert_1x1_conv=True, + image_features=image_features) + + expected_feature_map_shapes = { + 'Mixed_3c': (4, 28, 28, 256), + 'Mixed_4c': (4, 14, 14, 576), + 'Mixed_5c': (4, 7, 7, 1024), + 'Mixed_5c_2_Conv2d_3_3x3_s2_512': (4, 4, 4, 512), + 'Mixed_5c_2_Conv2d_4_3x3_s2_256': (4, 2, 2, 256), + 'Mixed_5c_2_Conv2d_5_3x3_s2_256': (4, 1, 1, 256)} + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + out_feature_maps = sess.run(feature_maps) + out_feature_map_shapes = dict( + (key, value.shape) for key, value in out_feature_maps.iteritems()) + self.assertDictEqual(out_feature_map_shapes, expected_feature_map_shapes) + + def test_get_expected_feature_map_shapes_with_inception_v3(self): + image_features = { + 'Mixed_5d': tf.random_uniform([4, 35, 35, 256], dtype=tf.float32), + 'Mixed_6e': tf.random_uniform([4, 17, 17, 576], dtype=tf.float32), + 'Mixed_7c': tf.random_uniform([4, 8, 8, 1024], dtype=tf.float32) + } + + feature_maps = feature_map_generators.multi_resolution_feature_maps( + feature_map_layout=INCEPTION_V3_LAYOUT, + depth_multiplier=1, + min_depth=32, + insert_1x1_conv=True, + image_features=image_features) + + expected_feature_map_shapes = { + 'Mixed_5d': (4, 35, 35, 256), + 'Mixed_6e': (4, 17, 17, 576), + 'Mixed_7c': (4, 8, 8, 1024), + 'Mixed_7c_2_Conv2d_3_3x3_s2_512': (4, 4, 4, 512), + 'Mixed_7c_2_Conv2d_4_3x3_s2_256': (4, 2, 2, 256), + 'Mixed_7c_2_Conv2d_5_3x3_s2_128': (4, 1, 1, 128)} + + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + out_feature_maps = sess.run(feature_maps) + out_feature_map_shapes = dict( + (key, value.shape) for key, value in out_feature_maps.iteritems()) + self.assertDictEqual(out_feature_map_shapes, expected_feature_map_shapes) + + +class GetDepthFunctionTest(tf.test.TestCase): + + def test_return_min_depth_when_multiplier_is_small(self): + depth_fn = feature_map_generators.get_depth_fn(depth_multiplier=0.5, + min_depth=16) + self.assertEqual(depth_fn(16), 16) + + def test_return_correct_depth_with_multiplier(self): + depth_fn = feature_map_generators.get_depth_fn(depth_multiplier=0.5, + min_depth=16) + self.assertEqual(depth_fn(64), 32) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/models/ssd_feature_extractor_test.py b/object_detection/models/ssd_feature_extractor_test.py new file mode 100644 index 000000000..434a4978f --- /dev/null +++ b/object_detection/models/ssd_feature_extractor_test.py @@ -0,0 +1,96 @@ +# 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 test class SSDFeatureExtractors.""" + +from abc import abstractmethod + +import numpy as np +import tensorflow as tf + + +class SsdFeatureExtractorTestBase(object): + + def _validate_features_shape(self, + feature_extractor, + preprocessed_inputs, + expected_feature_map_shapes): + """Checks the extracted features are of correct shape. + + Args: + feature_extractor: The feature extractor to test. + preprocessed_inputs: A [batch, height, width, 3] tensor to extract + features with. + expected_feature_map_shapes: The expected shape of the extracted features. + """ + feature_maps = feature_extractor.extract_features(preprocessed_inputs) + feature_map_shapes = [tf.shape(feature_map) for feature_map in feature_maps] + init_op = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init_op) + feature_map_shapes_out = sess.run(feature_map_shapes) + for shape_out, exp_shape_out in zip( + feature_map_shapes_out, expected_feature_map_shapes): + self.assertAllEqual(shape_out, exp_shape_out) + + @abstractmethod + def _create_feature_extractor(self, depth_multiplier): + """Constructs a new feature extractor. + + Args: + depth_multiplier: float depth multiplier for feature extractor + Returns: + an ssd_meta_arch.SSDFeatureExtractor object. + """ + pass + + def check_extract_features_returns_correct_shape( + self, + image_height, + image_width, + depth_multiplier, + expected_feature_map_shapes_out): + feature_extractor = self._create_feature_extractor(depth_multiplier) + preprocessed_inputs = tf.random_uniform( + [4, image_height, image_width, 3], dtype=tf.float32) + self._validate_features_shape( + feature_extractor, preprocessed_inputs, expected_feature_map_shapes_out) + + def check_extract_features_raises_error_with_invalid_image_size( + self, + image_height, + image_width, + depth_multiplier): + feature_extractor = self._create_feature_extractor(depth_multiplier) + preprocessed_inputs = tf.placeholder(tf.float32, (4, None, None, 3)) + feature_maps = feature_extractor.extract_features(preprocessed_inputs) + test_preprocessed_image = np.random.rand(4, image_height, image_width, 3) + with self.test_session() as sess: + sess.run(tf.global_variables_initializer()) + with self.assertRaises(tf.errors.InvalidArgumentError): + sess.run(feature_maps, + feed_dict={preprocessed_inputs: test_preprocessed_image}) + + def check_feature_extractor_variables_under_scope(self, + depth_multiplier, + scope_name): + g = tf.Graph() + with g.as_default(): + feature_extractor = self._create_feature_extractor(depth_multiplier) + preprocessed_inputs = tf.placeholder(tf.float32, (4, None, None, 3)) + feature_extractor.extract_features(preprocessed_inputs) + variables = g.get_collection(tf.GraphKeys.GLOBAL_VARIABLES) + for variable in variables: + self.assertTrue(variable.name.startswith(scope_name)) diff --git a/object_detection/models/ssd_inception_v2_feature_extractor.py b/object_detection/models/ssd_inception_v2_feature_extractor.py new file mode 100644 index 000000000..2791f4aa0 --- /dev/null +++ b/object_detection/models/ssd_inception_v2_feature_extractor.py @@ -0,0 +1,99 @@ +# 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. +# ============================================================================== + +"""SSDFeatureExtractor for InceptionV2 features.""" +import tensorflow as tf + +from object_detection.meta_architectures import ssd_meta_arch +from object_detection.models import feature_map_generators +from nets import inception_v2 + +slim = tf.contrib.slim + + +class SSDInceptionV2FeatureExtractor(ssd_meta_arch.SSDFeatureExtractor): + """SSD Feature Extractor using InceptionV2 features.""" + + def __init__(self, + depth_multiplier, + min_depth, + conv_hyperparams, + reuse_weights=None): + """InceptionV2 Feature Extractor for SSD Models. + + Args: + depth_multiplier: float depth multiplier for feature extractor. + min_depth: minimum feature extractor depth. + conv_hyperparams: tf slim arg_scope for conv2d and separable_conv2d ops. + reuse_weights: Whether to reuse variables. Default is None. + """ + super(SSDInceptionV2FeatureExtractor, self).__init__( + depth_multiplier, min_depth, conv_hyperparams, reuse_weights) + + def preprocess(self, resized_inputs): + """SSD preprocessing. + + Maps pixel values to the range [-1, 1]. + + Args: + resized_inputs: a [batch, height, width, channels] float tensor + representing a batch of images. + + Returns: + preprocessed_inputs: a [batch, height, width, channels] float tensor + representing a batch of images. + """ + return (2.0 / 255.0) * resized_inputs - 1.0 + + def extract_features(self, preprocessed_inputs): + """Extract features from preprocessed inputs. + + Args: + preprocessed_inputs: a [batch, height, width, channels] float tensor + representing a batch of images. + + Returns: + feature_maps: a list of tensors where the ith tensor has shape + [batch, height_i, width_i, depth_i] + """ + preprocessed_inputs.get_shape().assert_has_rank(4) + shape_assert = tf.Assert( + tf.logical_and(tf.greater_equal(tf.shape(preprocessed_inputs)[1], 33), + tf.greater_equal(tf.shape(preprocessed_inputs)[2], 33)), + ['image size must at least be 33 in both height and width.']) + + feature_map_layout = { + 'from_layer': ['Mixed_4c', 'Mixed_5c', '', '', '', ''], + 'layer_depth': [-1, -1, 512, 256, 256, 128], + } + + with tf.control_dependencies([shape_assert]): + with slim.arg_scope(self._conv_hyperparams): + with tf.variable_scope('InceptionV2', + reuse=self._reuse_weights) as scope: + _, image_features = inception_v2.inception_v2_base( + preprocessed_inputs, + final_endpoint='Mixed_5c', + min_depth=self._min_depth, + depth_multiplier=self._depth_multiplier, + scope=scope) + feature_maps = feature_map_generators.multi_resolution_feature_maps( + feature_map_layout=feature_map_layout, + depth_multiplier=self._depth_multiplier, + min_depth=self._min_depth, + insert_1x1_conv=True, + image_features=image_features) + + return feature_maps.values() diff --git a/object_detection/models/ssd_inception_v2_feature_extractor_test.py b/object_detection/models/ssd_inception_v2_feature_extractor_test.py new file mode 100644 index 000000000..9be9ded6d --- /dev/null +++ b/object_detection/models/ssd_inception_v2_feature_extractor_test.py @@ -0,0 +1,95 @@ +# 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.ssd_inception_v2_feature_extractor.""" +import numpy as np +import tensorflow as tf + +from object_detection.models import ssd_feature_extractor_test +from object_detection.models import ssd_inception_v2_feature_extractor + + +class SsdInceptionV2FeatureExtractorTest( + ssd_feature_extractor_test.SsdFeatureExtractorTestBase, + tf.test.TestCase): + + def _create_feature_extractor(self, depth_multiplier): + """Constructs a SsdInceptionV2FeatureExtractor. + + Args: + depth_multiplier: float depth multiplier for feature extractor + Returns: + an ssd_inception_v2_feature_extractor.SsdInceptionV2FeatureExtractor. + """ + min_depth = 32 + conv_hyperparams = {} + return ssd_inception_v2_feature_extractor.SSDInceptionV2FeatureExtractor( + depth_multiplier, min_depth, conv_hyperparams) + + def test_extract_features_returns_correct_shapes_128(self): + image_height = 128 + image_width = 128 + depth_multiplier = 1.0 + expected_feature_map_shape = [(4, 8, 8, 576), (4, 4, 4, 1024), + (4, 2, 2, 512), (4, 1, 1, 256), + (4, 1, 1, 256), (4, 1, 1, 128)] + self.check_extract_features_returns_correct_shape( + image_height, image_width, depth_multiplier, expected_feature_map_shape) + + def test_extract_features_returns_correct_shapes_299(self): + image_height = 299 + image_width = 299 + depth_multiplier = 1.0 + expected_feature_map_shape = [(4, 19, 19, 576), (4, 10, 10, 1024), + (4, 5, 5, 512), (4, 3, 3, 256), + (4, 2, 2, 256), (4, 1, 1, 128)] + self.check_extract_features_returns_correct_shape( + image_height, image_width, depth_multiplier, expected_feature_map_shape) + + def test_extract_features_returns_correct_shapes_enforcing_min_depth(self): + image_height = 299 + image_width = 299 + depth_multiplier = 0.5**12 + expected_feature_map_shape = [(4, 19, 19, 128), (4, 10, 10, 128), + (4, 5, 5, 32), (4, 3, 3, 32), + (4, 2, 2, 32), (4, 1, 1, 32)] + self.check_extract_features_returns_correct_shape( + image_height, image_width, depth_multiplier, expected_feature_map_shape) + + def test_extract_features_raises_error_with_invalid_image_size(self): + image_height = 32 + image_width = 32 + depth_multiplier = 1.0 + self.check_extract_features_raises_error_with_invalid_image_size( + image_height, image_width, depth_multiplier) + + def test_preprocess_returns_correct_value_range(self): + image_height = 128 + image_width = 128 + depth_multiplier = 1 + test_image = np.random.rand(4, image_height, image_width, 3) + feature_extractor = self._create_feature_extractor(depth_multiplier) + preprocessed_image = feature_extractor.preprocess(test_image) + self.assertTrue(np.all(np.less_equal(np.abs(preprocessed_image), 1.0))) + + def test_variables_only_created_in_scope(self): + depth_multiplier = 1 + scope_name = 'InceptionV2' + self.check_feature_extractor_variables_under_scope(depth_multiplier, + scope_name) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/models/ssd_mobilenet_v1_feature_extractor.py b/object_detection/models/ssd_mobilenet_v1_feature_extractor.py new file mode 100644 index 000000000..fa4360c44 --- /dev/null +++ b/object_detection/models/ssd_mobilenet_v1_feature_extractor.py @@ -0,0 +1,101 @@ +# 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. +# ============================================================================== + +"""SSDFeatureExtractor for MobilenetV1 features.""" + +import tensorflow as tf + +from object_detection.meta_architectures import ssd_meta_arch +from object_detection.models import feature_map_generators +from nets import mobilenet_v1 + +slim = tf.contrib.slim + + +class SSDMobileNetV1FeatureExtractor(ssd_meta_arch.SSDFeatureExtractor): + """SSD Feature Extractor using MobilenetV1 features.""" + + def __init__(self, + depth_multiplier, + min_depth, + conv_hyperparams, + reuse_weights=None): + """MobileNetV1 Feature Extractor for SSD Models. + + Args: + depth_multiplier: float depth multiplier for feature extractor. + min_depth: minimum feature extractor depth. + conv_hyperparams: tf slim arg_scope for conv2d and separable_conv2d ops. + reuse_weights: Whether to reuse variables. Default is None. + """ + super(SSDMobileNetV1FeatureExtractor, self).__init__( + depth_multiplier, min_depth, conv_hyperparams, reuse_weights) + + def preprocess(self, resized_inputs): + """SSD preprocessing. + + Maps pixel values to the range [-1, 1]. + + Args: + resized_inputs: a [batch, height, width, channels] float tensor + representing a batch of images. + + Returns: + preprocessed_inputs: a [batch, height, width, channels] float tensor + representing a batch of images. + """ + return (2.0 / 255.0) * resized_inputs - 1.0 + + def extract_features(self, preprocessed_inputs): + """Extract features from preprocessed inputs. + + Args: + preprocessed_inputs: a [batch, height, width, channels] float tensor + representing a batch of images. + + Returns: + feature_maps: a list of tensors where the ith tensor has shape + [batch, height_i, width_i, depth_i] + """ + preprocessed_inputs.get_shape().assert_has_rank(4) + shape_assert = tf.Assert( + tf.logical_and(tf.greater_equal(tf.shape(preprocessed_inputs)[1], 33), + tf.greater_equal(tf.shape(preprocessed_inputs)[2], 33)), + ['image size must at least be 33 in both height and width.']) + + feature_map_layout = { + 'from_layer': ['Conv2d_11_pointwise', 'Conv2d_13_pointwise', '', '', + '', ''], + 'layer_depth': [-1, -1, 512, 256, 256, 128], + } + + with tf.control_dependencies([shape_assert]): + with slim.arg_scope(self._conv_hyperparams): + with tf.variable_scope('MobilenetV1', + reuse=self._reuse_weights) as scope: + _, image_features = mobilenet_v1.mobilenet_v1_base( + preprocessed_inputs, + final_endpoint='Conv2d_13_pointwise', + min_depth=self._min_depth, + depth_multiplier=self._depth_multiplier, + scope=scope) + feature_maps = feature_map_generators.multi_resolution_feature_maps( + feature_map_layout=feature_map_layout, + depth_multiplier=self._depth_multiplier, + min_depth=self._min_depth, + insert_1x1_conv=True, + image_features=image_features) + + return feature_maps.values() diff --git a/object_detection/models/ssd_mobilenet_v1_feature_extractor_test.py b/object_detection/models/ssd_mobilenet_v1_feature_extractor_test.py new file mode 100644 index 000000000..49cd734ab --- /dev/null +++ b/object_detection/models/ssd_mobilenet_v1_feature_extractor_test.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. +# ============================================================================== + +"""Tests for ssd_mobilenet_v1_feature_extractor.""" +import numpy as np +import tensorflow as tf + +from object_detection.models import ssd_feature_extractor_test +from object_detection.models import ssd_mobilenet_v1_feature_extractor + + +class SsdMobilenetV1FeatureExtractorTest( + ssd_feature_extractor_test.SsdFeatureExtractorTestBase, tf.test.TestCase): + + def _create_feature_extractor(self, depth_multiplier): + """Constructs a new feature extractor. + + Args: + depth_multiplier: float depth multiplier for feature extractor + Returns: + an ssd_meta_arch.SSDFeatureExtractor object. + """ + min_depth = 32 + conv_hyperparams = {} + return ssd_mobilenet_v1_feature_extractor.SSDMobileNetV1FeatureExtractor( + depth_multiplier, min_depth, conv_hyperparams) + + def test_extract_features_returns_correct_shapes_128(self): + image_height = 128 + image_width = 128 + depth_multiplier = 1.0 + expected_feature_map_shape = [(4, 8, 8, 512), (4, 4, 4, 1024), + (4, 2, 2, 512), (4, 1, 1, 256), + (4, 1, 1, 256), (4, 1, 1, 128)] + self.check_extract_features_returns_correct_shape( + image_height, image_width, depth_multiplier, expected_feature_map_shape) + + def test_extract_features_returns_correct_shapes_299(self): + image_height = 299 + image_width = 299 + depth_multiplier = 1.0 + expected_feature_map_shape = [(4, 19, 19, 512), (4, 10, 10, 1024), + (4, 5, 5, 512), (4, 3, 3, 256), + (4, 2, 2, 256), (4, 1, 1, 128)] + self.check_extract_features_returns_correct_shape( + image_height, image_width, depth_multiplier, expected_feature_map_shape) + + def test_extract_features_returns_correct_shapes_enforcing_min_depth(self): + image_height = 299 + image_width = 299 + depth_multiplier = 0.5**12 + expected_feature_map_shape = [(4, 19, 19, 32), (4, 10, 10, 32), + (4, 5, 5, 32), (4, 3, 3, 32), + (4, 2, 2, 32), (4, 1, 1, 32)] + self.check_extract_features_returns_correct_shape( + image_height, image_width, depth_multiplier, expected_feature_map_shape) + + def test_extract_features_raises_error_with_invalid_image_size(self): + image_height = 32 + image_width = 32 + depth_multiplier = 1.0 + self.check_extract_features_raises_error_with_invalid_image_size( + image_height, image_width, depth_multiplier) + + def test_preprocess_returns_correct_value_range(self): + image_height = 128 + image_width = 128 + depth_multiplier = 1 + test_image = np.random.rand(4, image_height, image_width, 3) + feature_extractor = self._create_feature_extractor(depth_multiplier) + preprocessed_image = feature_extractor.preprocess(test_image) + self.assertTrue(np.all(np.less_equal(np.abs(preprocessed_image), 1.0))) + + def test_variables_only_created_in_scope(self): + depth_multiplier = 1 + scope_name = 'MobilenetV1' + self.check_feature_extractor_variables_under_scope(depth_multiplier, + scope_name) + + +if __name__ == '__main__': + tf.test.main() diff --git a/object_detection/object_detection.blueprint b/object_detection/object_detection.blueprint new file mode 100644 index 000000000..9d6bbffea --- /dev/null +++ b/object_detection/object_detection.blueprint @@ -0,0 +1,56 @@ +include "devtools/blueprint/ncl/blueprint_file.ncl"; +include "releasetools/rapid/ncl/rapid_config.ncl"; + +blueprint_file = ::blueprint::BlueprintFile( + project_name = "open_tf_object_detection", + project_grouping = ["Search", "Search Features", "Image Search", "Visual Search"], + mdb_groups = ["vale-project"], + + tech_lead = ["jonathanhuang", "kpmurphy"], + + dev_mailing_list = "object-detection-reviews@google.com", + + buganizer_component_ids = [163596], + + owned_code_depotpaths = [ + "//depot/google3/third_party/tensorflow_models/object_detection/...", + ], + buildable_units = [ + ::blueprint::BuildableUnit( + name = "open_tf_object_detection.fastbuild", + enable_continuous_build = true, + enable_release = false, + continuous_build_email = ::blueprint::ContinuousBuildEmailInfo( + build_cop_email_addrs = ["vale-project+tap@google.com"]), + build_patterns = [ + "third_party/tensorflow_models/object_detection/...", + ], + build_flags = [ + "--compilation_mode=fastbuild", + ], + test_patterns = [ + "third_party/tensorflow_models/object_detection/...", + ], + enable_coverage = true, + + ), + ::blueprint::BuildableUnit( + name = "open_tf_object_detection.opt", + enable_continuous_build = true, + enable_release = false, + continuous_build_email = ::blueprint::ContinuousBuildEmailInfo( + build_cop_email_addrs = ["vale-project+tap@google.com"]), + build_patterns = [ + "third_party/tensorflow_models/object_detection/...", + "image/understanding/object_detection/...", + ], + build_flags = [ + "--compilation_mode=opt", + ], + test_patterns = [ + "third_party/tensorflow_models/object_detection/...", + "image/understanding/object_detection/...", + ], + ), + ], +); diff --git a/object_detection/object_detection_tutorial.ipynb b/object_detection/object_detection_tutorial.ipynb new file mode 100644 index 000000000..331e210cf --- /dev/null +++ b/object_detection/object_detection_tutorial.ipynb @@ -0,0 +1,263 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Object Detection Demo\n", + "Welcome to the object detection inference walkthrough! 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": {}, + "source": [ + "# Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true, + "scrolled": true + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import os\n", + "import sys\n", + "import tensorflow as tf\n", + "\n", + "from collections import defaultdict\n", + "from io import StringIO\n", + "from matplotlib import pyplot as plt\n", + "from PIL import Image" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Env setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# This is needed to display the images.\n", + "%matplotlib inline\n", + "\n", + "# This is needed since the notebook is stored in the object_detection folder.\n", + "sys.path.append(\"..\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Object detection imports\n", + "Here are the imports from the object detection module." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from utils import label_map_util\n", + "\n", + "from utils import visualization_utils as vis_util" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Model preparation " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Path to frozen detection graph. This is the actual model that is used for the object detection.\n", + "PATH_TO_CKPT = os.path.join('test_ckpt', 'ssd_inception_v2.pb')\n", + "\n", + "# List of the strings that is used to add correct label for each box.\n", + "PATH_TO_LABELS = os.path.join('data', 'mscoco_label_map.pbtxt')\n", + "\n", + "NUM_CLASSES = 90" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load a (frozen) Tensorflow model into memory." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "detection_graph = tf.Graph()\n", + "with detection_graph.as_default():\n", + " od_graph_def = tf.GraphDef()\n", + " with tf.gfile.GFile(PATH_TO_CKPT, 'rb') as fid:\n", + " serialized_graph = fid.read()\n", + " od_graph_def.ParseFromString(serialized_graph)\n", + " tf.import_graph_def(od_graph_def, name='')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "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": null, + "metadata": {}, + "outputs": [], + "source": [ + "label_map = label_map_util.load_labelmap(PATH_TO_LABELS)\n", + "categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=NUM_CLASSES, use_display_name=True)\n", + "category_index = label_map_util.create_category_index(categories)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Helper code" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def load_image_into_numpy_array(image):\n", + " (im_width, im_height) = image.size\n", + " return np.array(image.getdata()).reshape(\n", + " (im_height, im_width, 3)).astype(np.uint8)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Detection" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# For the sake of simplicity we will use only 2 images:\n", + "# image1.jpg\n", + "# image2.jpg\n", + "# 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 = 'test_images'\n", + "TEST_IMAGE_PATHS = [ os.path.join(PATH_TO_TEST_IMAGES_DIR, 'image{}.jpg'.format(i)) for i in range(1, 3) ]\n", + "\n", + "# Size, in inches, of the output images.\n", + "IMAGE_SIZE = (12, 8)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "with detection_graph.as_default():\n", + " with tf.Session(graph=detection_graph) as sess:\n", + " for image_path in TEST_IMAGE_PATHS:\n", + " image = Image.open(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 = load_image_into_numpy_array(image)\n", + " # Expand dimensions since the model expects images to have shape: [1, None, None, 3]\n", + " image_np_expanded = np.expand_dims(image_np, axis=0)\n", + " image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')\n", + " # Each box represents a part of the image where a particular object was detected.\n", + " boxes = detection_graph.get_tensor_by_name('detection_boxes:0')\n", + " # Each score represent how level of confidence for each of the objects.\n", + " # Score is shown on the result image, together with the class label.\n", + " scores = detection_graph.get_tensor_by_name('detection_scores:0')\n", + " classes = detection_graph.get_tensor_by_name('detection_classes:0')\n", + " num_detections = detection_graph.get_tensor_by_name('num_detections:0')\n", + " # Actual detection.\n", + " (boxes, scores, classes, num_detections) = sess.run(\n", + " [boxes, scores, classes, num_detections],\n", + " feed_dict={image_tensor: image_np_expanded})\n", + " # Visualization of the results of a detection.\n", + " vis_util.visualize_boxes_and_labels_on_image_array(\n", + " image_np,\n", + " np.squeeze(boxes),\n", + " np.squeeze(classes).astype(np.int32),\n", + " np.squeeze(scores),\n", + " category_index,\n", + " use_normalized_coordinates=True)\n", + " plt.figure(figsize=IMAGE_SIZE)\n", + " plt.imshow(image_np)" + ] + } + ], + "metadata": { + "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.4.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/object_detection/protos/BUILD b/object_detection/protos/BUILD new file mode 100644 index 000000000..7ab70ca0f --- /dev/null +++ b/object_detection/protos/BUILD @@ -0,0 +1,329 @@ +# Tensorflow Object Detection API: Configuration protos. + +package( + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) + +proto_library( + name = "argmax_matcher_proto", + srcs = ["argmax_matcher.proto"], +) + +py_proto_library( + name = "argmax_matcher_py_pb2", + api_version = 2, + deps = [":argmax_matcher_proto"], +) + +proto_library( + name = "bipartite_matcher_proto", + srcs = ["bipartite_matcher.proto"], +) + +py_proto_library( + name = "bipartite_matcher_py_pb2", + api_version = 2, + deps = [":bipartite_matcher_proto"], +) + +proto_library( + name = "matcher_proto", + srcs = ["matcher.proto"], + deps = [ + ":argmax_matcher_proto", + ":bipartite_matcher_proto", + ], +) + +py_proto_library( + name = "matcher_py_pb2", + api_version = 2, + deps = [":matcher_proto"], +) + +proto_library( + name = "faster_rcnn_box_coder_proto", + srcs = ["faster_rcnn_box_coder.proto"], +) + +py_proto_library( + name = "faster_rcnn_box_coder_py_pb2", + api_version = 2, + deps = [":faster_rcnn_box_coder_proto"], +) + +proto_library( + name = "mean_stddev_box_coder_proto", + srcs = ["mean_stddev_box_coder.proto"], +) + +py_proto_library( + name = "mean_stddev_box_coder_py_pb2", + api_version = 2, + deps = [":mean_stddev_box_coder_proto"], +) + +proto_library( + name = "square_box_coder_proto", + srcs = ["square_box_coder.proto"], +) + +py_proto_library( + name = "square_box_coder_py_pb2", + api_version = 2, + deps = [":square_box_coder_proto"], +) + +proto_library( + name = "box_coder_proto", + srcs = ["box_coder.proto"], + deps = [ + ":faster_rcnn_box_coder_proto", + ":mean_stddev_box_coder_proto", + ":square_box_coder_proto", + ], +) + +py_proto_library( + name = "box_coder_py_pb2", + api_version = 2, + deps = [":box_coder_proto"], +) + +proto_library( + name = "grid_anchor_generator_proto", + srcs = ["grid_anchor_generator.proto"], +) + +py_proto_library( + name = "grid_anchor_generator_py_pb2", + api_version = 2, + deps = [":grid_anchor_generator_proto"], +) + +proto_library( + name = "ssd_anchor_generator_proto", + srcs = ["ssd_anchor_generator.proto"], +) + +py_proto_library( + name = "ssd_anchor_generator_py_pb2", + api_version = 2, + deps = [":ssd_anchor_generator_proto"], +) + +proto_library( + name = "anchor_generator_proto", + srcs = ["anchor_generator.proto"], + deps = [ + ":grid_anchor_generator_proto", + ":ssd_anchor_generator_proto", + ], +) + +py_proto_library( + name = "anchor_generator_py_pb2", + api_version = 2, + deps = [":anchor_generator_proto"], +) + +proto_library( + name = "input_reader_proto", + srcs = ["input_reader.proto"], +) + +py_proto_library( + name = "input_reader_py_pb2", + api_version = 2, + deps = [":input_reader_proto"], +) + +proto_library( + name = "losses_proto", + srcs = ["losses.proto"], +) + +py_proto_library( + name = "losses_py_pb2", + api_version = 2, + deps = [":losses_proto"], +) + +proto_library( + name = "optimizer_proto", + srcs = ["optimizer.proto"], +) + +py_proto_library( + name = "optimizer_py_pb2", + api_version = 2, + deps = [":optimizer_proto"], +) + +proto_library( + name = "post_processing_proto", + srcs = ["post_processing.proto"], +) + +py_proto_library( + name = "post_processing_py_pb2", + api_version = 2, + deps = [":post_processing_proto"], +) + +proto_library( + name = "hyperparams_proto", + srcs = ["hyperparams.proto"], +) + +py_proto_library( + name = "hyperparams_py_pb2", + api_version = 2, + deps = [":hyperparams_proto"], +) + +proto_library( + name = "box_predictor_proto", + srcs = ["box_predictor.proto"], + deps = [":hyperparams_proto"], +) + +py_proto_library( + name = "box_predictor_py_pb2", + api_version = 2, + deps = [":box_predictor_proto"], +) + +proto_library( + name = "region_similarity_calculator_proto", + srcs = ["region_similarity_calculator.proto"], + deps = [], +) + +py_proto_library( + name = "region_similarity_calculator_py_pb2", + api_version = 2, + deps = [":region_similarity_calculator_proto"], +) + +proto_library( + name = "preprocessor_proto", + srcs = ["preprocessor.proto"], +) + +py_proto_library( + name = "preprocessor_py_pb2", + api_version = 2, + deps = [":preprocessor_proto"], +) + +proto_library( + name = "train_proto", + srcs = ["train.proto"], + deps = [ + ":optimizer_proto", + ":preprocessor_proto", + ], +) + +py_proto_library( + name = "train_py_pb2", + api_version = 2, + deps = [":train_proto"], +) + +proto_library( + name = "eval_proto", + srcs = ["eval.proto"], +) + +py_proto_library( + name = "eval_py_pb2", + api_version = 2, + deps = [":eval_proto"], +) + +proto_library( + name = "image_resizer_proto", + srcs = ["image_resizer.proto"], +) + +py_proto_library( + name = "image_resizer_py_pb2", + api_version = 2, + deps = [":image_resizer_proto"], +) + +proto_library( + name = "faster_rcnn_proto", + srcs = ["faster_rcnn.proto"], + deps = [ + ":box_predictor_proto", + "//object_detection/protos:anchor_generator_proto", + "//object_detection/protos:hyperparams_proto", + "//object_detection/protos:image_resizer_proto", + "//object_detection/protos:losses_proto", + "//object_detection/protos:post_processing_proto", + ], +) + +proto_library( + name = "ssd_proto", + srcs = ["ssd.proto"], + deps = [ + ":anchor_generator_proto", + ":box_coder_proto", + ":box_predictor_proto", + ":hyperparams_proto", + ":image_resizer_proto", + ":losses_proto", + ":matcher_proto", + ":post_processing_proto", + ":region_similarity_calculator_proto", + ], +) + +proto_library( + name = "model_proto", + srcs = ["model.proto"], + deps = [ + ":faster_rcnn_proto", + ":ssd_proto", + ], +) + +py_proto_library( + name = "model_py_pb2", + api_version = 2, + deps = [":model_proto"], +) + +proto_library( + name = "pipeline_proto", + srcs = ["pipeline.proto"], + deps = [ + ":eval_proto", + ":input_reader_proto", + ":model_proto", + ":train_proto", + ], +) + +py_proto_library( + name = "pipeline_py_pb2", + api_version = 2, + deps = [":pipeline_proto"], +) + +proto_library( + name = "string_int_label_map_proto", + srcs = ["string_int_label_map.proto"], +) + +py_proto_library( + name = "string_int_label_map_py_pb2", + api_version = 2, + deps = [":string_int_label_map_proto"], +) diff --git a/object_detection/protos/__init__.py b/object_detection/protos/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/object_detection/protos/anchor_generator.proto b/object_detection/protos/anchor_generator.proto new file mode 100644 index 000000000..4b7b1d62e --- /dev/null +++ b/object_detection/protos/anchor_generator.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +package object_detection.protos; + +import "object_detection/protos/grid_anchor_generator.proto"; +import "object_detection/protos/ssd_anchor_generator.proto"; + +// Configuration proto for the anchor generator to use in the object detection +// pipeline. See core/anchor_generator.py for details. +message AnchorGenerator { + oneof anchor_generator_oneof { + GridAnchorGenerator grid_anchor_generator = 1; + SsdAnchorGenerator ssd_anchor_generator = 2; + } +} diff --git a/object_detection/protos/argmax_matcher.proto b/object_detection/protos/argmax_matcher.proto new file mode 100644 index 000000000..88c503182 --- /dev/null +++ b/object_detection/protos/argmax_matcher.proto @@ -0,0 +1,25 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Configuration proto for ArgMaxMatcher. See +// matchers/argmax_matcher.py for details. +message ArgMaxMatcher { + // Threshold for positive matches. + optional float matched_threshold = 1 [default = 0.5]; + + // Threshold for negative matches. + optional float unmatched_threshold = 2 [default = 0.5]; + + // Whether to construct ArgMaxMatcher without thresholds. + optional bool ignore_thresholds = 3 [default = false]; + + // If True then negative matches are the ones below the unmatched_threshold, + // whereas ignored matches are in between the matched and umatched + // threshold. If False, then negative matches are in between the matched + // and unmatched threshold, and everything lower than unmatched is ignored. + optional bool negatives_lower_than_unmatched = 4 [default = true]; + + // Whether to ensure each row is matched to at least one column. + optional bool force_match_for_each_row = 5 [default = false]; +} diff --git a/object_detection/protos/bipartite_matcher.proto b/object_detection/protos/bipartite_matcher.proto new file mode 100644 index 000000000..7e5a9e5c1 --- /dev/null +++ b/object_detection/protos/bipartite_matcher.proto @@ -0,0 +1,8 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Configuration proto for bipartite matcher. See +// matchers/bipartite_matcher.py for details. +message BipartiteMatcher { +} diff --git a/object_detection/protos/box_coder.proto b/object_detection/protos/box_coder.proto new file mode 100644 index 000000000..6b37e8f19 --- /dev/null +++ b/object_detection/protos/box_coder.proto @@ -0,0 +1,17 @@ +syntax = "proto2"; + +package object_detection.protos; + +import "object_detection/protos/faster_rcnn_box_coder.proto"; +import "object_detection/protos/mean_stddev_box_coder.proto"; +import "object_detection/protos/square_box_coder.proto"; + +// Configuration proto for the box coder to be used in the object detection +// pipeline. See core/box_coder.py for details. +message BoxCoder { + oneof box_coder_oneof { + FasterRcnnBoxCoder faster_rcnn_box_coder = 1; + MeanStddevBoxCoder mean_stddev_box_coder = 2; + SquareBoxCoder square_box_coder = 3; + } +} diff --git a/object_detection/protos/box_predictor.proto b/object_detection/protos/box_predictor.proto new file mode 100644 index 000000000..96c501c0d --- /dev/null +++ b/object_detection/protos/box_predictor.proto @@ -0,0 +1,99 @@ +syntax = "proto2"; + +package object_detection.protos; + +import "object_detection/protos/hyperparams.proto"; + + +// Configuration proto for box predictor. See core/box_predictor.py for details. +message BoxPredictor { + oneof box_predictor_oneof { + ConvolutionalBoxPredictor convolutional_box_predictor = 1; + MaskRCNNBoxPredictor mask_rcnn_box_predictor = 2; + RfcnBoxPredictor rfcn_box_predictor = 3; + } +} + +// Configuration proto for Convolutional box predictor. +message ConvolutionalBoxPredictor { + // Hyperparameters for convolution ops used in the box predictor. + optional Hyperparams conv_hyperparams = 1; + + // Minumum feature depth prior to predicting box encodings and class + // predictions. + optional int32 min_depth = 2 [default = 0]; + + // 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. + optional int32 max_depth = 3 [default = 0]; + + // Number of the additional conv layers before the predictor. + optional int32 num_layers_before_predictor = 4 [default = 0]; + + // Whether to use dropout for class prediction. + optional bool use_dropout = 5 [default = true]; + + // Keep probability for dropout + optional float dropout_keep_probability = 6 [default = 0.8]; + + // Size of final convolution kernel. If the spatial resolution of the feature + // map is smaller than the kernel size, then the kernel size is set to + // min(feature_width, feature_height). + optional int32 kernel_size = 7 [default = 1]; + + // Size of the encoding for boxes. + optional int32 box_code_size = 8 [default = 4]; + + // Whether to apply sigmoid to the output of class predictions. + // TODO: Do we need this since we have a post processing module.? + optional bool apply_sigmoid_to_scores = 9 [default = false]; +} + +message MaskRCNNBoxPredictor { + // Hyperparameters for fully connected ops used in the box predictor. + optional Hyperparams fc_hyperparams = 1; + + // Whether to use dropout op prior to the both box and class predictions. + optional bool use_dropout = 2 [default= false]; + + // Keep probability for dropout. This is only used if use_dropout is true. + optional float dropout_keep_probability = 3 [default = 0.5]; + + // Size of the encoding for the boxes. + optional int32 box_code_size = 4 [default = 4]; + + // Hyperparameters for convolution ops used in the box predictor. + optional Hyperparams conv_hyperparams = 5; + + // Whether to predict instance masks inside detection boxes. + optional bool predict_instance_masks = 6 [default = false]; + + // The depth for the first conv2d_transpose op applied to the + // image_features in the mask prediciton branch + optional int32 mask_prediction_conv_depth = 7 [default = 256]; + + // Whether to predict keypoints inside detection boxes. + optional bool predict_keypoints = 8 [default = false]; +} + +message RfcnBoxPredictor { + // Hyperparameters for convolution ops used in the box predictor. + optional Hyperparams conv_hyperparams = 1; + + // Bin sizes for RFCN crops. + optional int32 num_spatial_bins_height = 2 [default = 3]; + + optional int32 num_spatial_bins_width = 3 [default = 3]; + + // Target depth to reduce the input image features to. + optional int32 depth = 4 [default=1024]; + + // Size of the encoding for the boxes. + optional int32 box_code_size = 5 [default = 4]; + + // Size to resize the rfcn crops to. + optional int32 crop_height = 6 [default= 12]; + + optional int32 crop_width = 7 [default=12]; +} diff --git a/object_detection/protos/eval.proto b/object_detection/protos/eval.proto new file mode 100644 index 000000000..081b60de1 --- /dev/null +++ b/object_detection/protos/eval.proto @@ -0,0 +1,47 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Message for configuring DetectionModel evaluation jobs (eval.py). +message EvalConfig { + // Number of visualization images to generate. + optional uint32 num_visualizations = 1 [default=10]; + + // Number of examples to process of evaluation. + optional uint32 num_examples = 2 [default=5000]; + + // How often to run evaluation. + optional uint32 eval_interval_secs = 3 [default=300]; + + // Maximum number of times to run evaluation. If set to 0, will run forever. + optional uint32 max_evals = 4 [default=0]; + + // Whether the TensorFlow graph used for evaluation should be saved to disk. + optional bool save_graph = 5 [default=false]; + + // Path to directory to store visualizations in. If empty, visualization + // images are not exported (only shown on Tensorboard). + optional string visualization_export_dir = 6 [default=""]; + + // BNS name of the TensorFlow master. + optional string eval_master = 7 [default=""]; + + // Type of metrics to use for evaluation. Currently supports only Pascal VOC + // detection metrics. + optional string metrics_set = 8 [default="pascal_voc_metrics"]; + + // Path to export detections to COCO compatible JSON format. + optional string export_path = 9 [default='']; + + // Option to not read groundtruth labels and only export detections to + // COCO-compatible JSON file. + optional bool ignore_groundtruth = 10 [default=false]; + + // Use exponential moving averages of variables for evaluation. + // TODO: When this is false make sure the model is constructed + // without moving averages in restore_fn. + optional bool use_moving_averages = 11 [default=false]; + + // Whether to evaluate instance masks. + optional bool eval_instance_masks = 12 [default=false]; +} diff --git a/object_detection/protos/faster_rcnn.proto b/object_detection/protos/faster_rcnn.proto new file mode 100644 index 000000000..e2fd5d666 --- /dev/null +++ b/object_detection/protos/faster_rcnn.proto @@ -0,0 +1,131 @@ +syntax = "proto2"; + +package object_detection.protos; + +import "object_detection/protos/anchor_generator.proto"; +import "object_detection/protos/box_predictor.proto"; +import "object_detection/protos/hyperparams.proto"; +import "object_detection/protos/image_resizer.proto"; +import "object_detection/protos/losses.proto"; +import "object_detection/protos/post_processing.proto"; + +// Configuration for Faster R-CNN models. +// See meta_architectures/faster_rcnn_meta_arch.py and models/model_builder.py +// +// Naming conventions: +// Faster R-CNN models have two stages: a first stage region proposal network +// (or RPN) and a second stage box classifier. We thus use the prefixes +// `first_stage_` and `second_stage_` to indicate the stage to which each +// parameter pertains when relevant. +message FasterRcnn { + + // Whether to construct only the Region Proposal Network (RPN). + optional bool first_stage_only = 1 [default=false]; + + // Number of classes to predict. + optional int32 num_classes = 3; + + // Image resizer for preprocessing the input image. + optional ImageResizer image_resizer = 4; + + // Feature extractor config. + optional FasterRcnnFeatureExtractor feature_extractor = 5; + + + // (First stage) region proposal network (RPN) parameters. + + // Anchor generator to compute RPN anchors. + optional AnchorGenerator first_stage_anchor_generator = 6; + + // Atrous rate for the convolution op applied to the + // `first_stage_features_to_crop` tensor to obtain box predictions. + optional int32 first_stage_atrous_rate = 7 [default=1]; + + // Hyperparameters for the convolutional RPN box predictor. + optional Hyperparams first_stage_box_predictor_conv_hyperparams = 8; + + // Kernel size to use for the convolution op just prior to RPN box + // predictions. + optional int32 first_stage_box_predictor_kernel_size = 9 [default=3]; + + // Output depth for the convolution op just prior to RPN box predictions. + optional int32 first_stage_box_predictor_depth = 10 [default=512]; + + // The batch size to use for computing the first stage objectness and + // location losses. + optional int32 first_stage_minibatch_size = 11 [default=256]; + + // Fraction of positive examples per image for the RPN. + optional float first_stage_positive_balance_fraction = 12 [default=0.5]; + + // Non max suppression score threshold applied to first stage RPN proposals. + optional float first_stage_nms_score_threshold = 13 [default=0.0]; + + // Non max suppression IOU threshold applied to first stage RPN proposals. + optional float first_stage_nms_iou_threshold = 14 [default=0.7]; + + // Maximum number of RPN proposals retained after first stage postprocessing. + optional int32 first_stage_max_proposals = 15 [default=300]; + + // First stage RPN localization loss weight. + optional float first_stage_localization_loss_weight = 16 [default=1.0]; + + // First stage RPN objectness loss weight. + optional float first_stage_objectness_loss_weight = 17 [default=1.0]; + + + // Per-region cropping parameters. + // Note that if a R-FCN model is constructed the per region cropping + // parameters below are ignored. + + // Output size (width and height are set to be the same) of the initial + // bilinear interpolation based cropping during ROI pooling. + optional int32 initial_crop_size = 18; + + // Kernel size of the max pool op on the cropped feature map during + // ROI pooling. + optional int32 maxpool_kernel_size = 19; + + // Stride of the max pool op on the cropped feature map during ROI pooling. + optional int32 maxpool_stride = 20; + + + // (Second stage) box classifier parameters + + // Hyperparameters for the second stage box predictor. If box predictor type + // is set to rfcn_box_predictor, a R-FCN model is constructed, otherwise a + // Faster R-CNN model is constructed. + optional BoxPredictor second_stage_box_predictor = 21; + + // The batch size per image used for computing the classification and refined + // location loss of the box classifier. + // Note that this field is ignored if `hard_example_miner` is configured. + optional int32 second_stage_batch_size = 22 [default=64]; + + // Fraction of positive examples to use per image for the box classifier. + optional float second_stage_balance_fraction = 23 [default=0.25]; + + // Post processing to apply on the second stage box classifier predictions. + // Note: the `score_converter` provided to the FasterRCNNMetaArch constructor + // is taken from this `second_stage_post_processing` proto. + optional PostProcessing second_stage_post_processing = 24; + + // Second stage refined localization loss weight. + optional float second_stage_localization_loss_weight = 25 [default=1.0]; + + // Second stage classification loss weight + optional float second_stage_classification_loss_weight = 26 [default=1.0]; + + // If not left to default, applies hard example mining. + optional HardExampleMiner hard_example_miner = 27; +} + + +message FasterRcnnFeatureExtractor { + // Type of Faster R-CNN model (e.g., 'faster_rcnn_resnet101'; + // See models/model_builder.py for expected types). + optional string type = 1; + + // Output stride of extracted RPN feature map. + optional int32 first_stage_features_stride = 2 [default=16]; +} diff --git a/object_detection/protos/faster_rcnn_box_coder.proto b/object_detection/protos/faster_rcnn_box_coder.proto new file mode 100644 index 000000000..512a20a15 --- /dev/null +++ b/object_detection/protos/faster_rcnn_box_coder.proto @@ -0,0 +1,17 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Configuration proto for FasterRCNNBoxCoder. See +// box_coders/faster_rcnn_box_coder.py for details. +message FasterRcnnBoxCoder { + // Scale factor for anchor encoded box center. + optional float y_scale = 1 [default = 10.0]; + optional float x_scale = 2 [default = 10.0]; + + // Scale factor for anchor encoded box height. + optional float height_scale = 3 [default = 5.0]; + + // Scale factor for anchor encoded box width. + optional float width_scale = 4 [default = 5.0]; +} diff --git a/object_detection/protos/grid_anchor_generator.proto b/object_detection/protos/grid_anchor_generator.proto new file mode 100644 index 000000000..85168f8f5 --- /dev/null +++ b/object_detection/protos/grid_anchor_generator.proto @@ -0,0 +1,34 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Configuration proto for GridAnchorGenerator. See +// anchor_generators/grid_anchor_generator.py for details. +message GridAnchorGenerator { + // Anchor height in pixels. + optional int32 height = 1 [default = 256]; + + // Anchor width in pixels. + optional int32 width = 2 [default = 256]; + + // Anchor stride in height dimension in pixels. + optional int32 height_stride = 3 [default = 16]; + + // Anchor stride in width dimension in pixels. + optional int32 width_stride = 4 [default = 16]; + + // Anchor height offset in pixels. + optional int32 height_offset = 5 [default = 0]; + + // Anchor width offset in pixels. + optional int32 width_offset = 6 [default = 0]; + + // At any given location, len(scales) * len(aspect_ratios) anchors are + // generated with all possible combinations of scales and aspect ratios. + + // List of scales for the anchors. + repeated float scales = 7; + + // List of aspect ratios for the anchors. + repeated float aspect_ratios = 8; +} diff --git a/object_detection/protos/hyperparams.proto b/object_detection/protos/hyperparams.proto new file mode 100644 index 000000000..b8b9972e6 --- /dev/null +++ b/object_detection/protos/hyperparams.proto @@ -0,0 +1,103 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Configuration proto for the convolution op hyperparameters to use in the +// object detection pipeline. +message Hyperparams { + + // Operations affected by hyperparameters. + enum Op { + // Convolution, Separable Convolution, Convolution transpose. + CONV = 1; + + // Fully connected + FC = 2; + } + optional Op op = 1 [default = CONV]; + + // Regularizer for the weights of the convolution op. + optional Regularizer regularizer = 2; + + // Initializer for the weights of the convolution op. + optional Initializer initializer = 3; + + // Type of activation to apply after convolution. + enum Activation { + // Use None (no activation) + NONE = 0; + + // Use tf.nn.relu + RELU = 1; + + // Use tf.nn.relu6 + RELU_6 = 2; + } + optional Activation activation = 4 [default = RELU]; + + // BatchNorm hyperparameters. If this parameter is NOT set then BatchNorm is + // not applied! + optional BatchNorm batch_norm = 5; +} + +// Proto with one-of field for regularizers. +message Regularizer { + oneof regularizer_oneof { + L1Regularizer l1_regularizer = 1; + L2Regularizer l2_regularizer = 2; + } +} + +// Configuration proto for L1 Regularizer. +// See https://www.tensorflow.org/api_docs/python/tf/contrib/layers/l1_regularizer +message L1Regularizer { + optional float weight = 1 [default = 1.0]; +} + +// Configuration proto for L2 Regularizer. +// See https://www.tensorflow.org/api_docs/python/tf/contrib/layers/l2_regularizer +message L2Regularizer { + optional float weight = 1 [default = 1.0]; +} + +// Proto with one-of field for initializers. +message Initializer { + oneof initializer_oneof { + TruncatedNormalInitializer truncated_normal_initializer = 1; + VarianceScalingInitializer variance_scaling_initializer = 2; + } +} + +// Configuration proto for truncated normal initializer. See +// https://www.tensorflow.org/api_docs/python/tf/truncated_normal_initializer +message TruncatedNormalInitializer { + optional float mean = 1 [default = 0.0]; + optional float stddev = 2 [default = 1.0]; +} + +// Configuration proto for variance scaling initializer. See +// https://www.tensorflow.org/api_docs/python/tf/contrib/layers/ +// variance_scaling_initializer +message VarianceScalingInitializer { + optional float factor = 1 [default = 2.0]; + optional bool uniform = 2 [default = false]; + enum Mode { + FAN_IN = 0; + FAN_OUT = 1; + FAN_AVG = 2; + } + optional Mode mode = 3 [default = FAN_IN]; +} + +// Configuration proto for batch norm to apply after convolution op. See +// https://www.tensorflow.org/api_docs/python/tf/contrib/layers/batch_norm +message BatchNorm { + optional float decay = 1 [default = 0.999]; + optional bool center = 2 [default = true]; + optional bool scale = 3 [default = false]; + optional float epsilon = 4 [default = 0.001]; + // Whether to train the batch norm variables. If this is set to false during + // training, the current value of the batch_norm variables are used for + // forward pass but they are never updated. + optional bool train = 5 [default = true]; +} diff --git a/object_detection/protos/image_resizer.proto b/object_detection/protos/image_resizer.proto new file mode 100644 index 000000000..4618add72 --- /dev/null +++ b/object_detection/protos/image_resizer.proto @@ -0,0 +1,32 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Configuration proto for image resizing operations. +// See builders/image_resizer_builder.py for details. +message ImageResizer { + oneof image_resizer_oneof { + KeepAspectRatioResizer keep_aspect_ratio_resizer = 1; + FixedShapeResizer fixed_shape_resizer = 2; + } +} + + +// Configuration proto for image resizer that keeps aspect ratio. +message KeepAspectRatioResizer { + // Desired size of the smaller image dimension in pixels. + optional int32 min_dimension = 1 [default = 600]; + + // Desired size of the larger image dimension in pixels. + optional int32 max_dimension = 2 [default = 1024]; +} + + +// Configuration proto for image resizer that resizes to a fixed shape. +message FixedShapeResizer { + // Desired height of image in pixels. + optional int32 height = 1 [default = 300]; + + // Desired width of image in pixels. + optional int32 width = 2 [default = 300]; +} diff --git a/object_detection/protos/input_reader.proto b/object_detection/protos/input_reader.proto new file mode 100644 index 000000000..8956b009e --- /dev/null +++ b/object_detection/protos/input_reader.proto @@ -0,0 +1,60 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Configuration proto for defining input readers that generate Object Detection +// Examples from input sources. Input readers are expected to generate a +// dictionary of tensors, with the following fields populated: +// +// 'image': an [image_height, image_width, channels] image tensor that detection +// will be run on. +// 'groundtruth_classes': a [num_boxes] int32 tensor storing the class +// labels of detected boxes in the image. +// 'groundtruth_boxes': a [num_boxes, 4] float tensor storing the coordinates of +// detected boxes in the image. +// 'groundtruth_instance_masks': (Optional), a [num_boxes, image_height, +// image_width] float tensor storing binary mask of the objects in boxes. + +message InputReader { + // Path to StringIntLabelMap pbtxt file specifying the mapping from string + // labels to integer ids. + optional string label_map_path = 1 [default=""]; + + // Whether data should be processed in the order they are read in, or + // shuffled randomly. + optional bool shuffle = 2 [default=true]; + + // Maximum number of records to keep in reader queue. + optional uint32 queue_capacity = 3 [default=2000]; + + // Minimum number of records to keep in reader queue. A large value is needed + // to generate a good random shuffle. + optional uint32 min_after_dequeue = 4 [default=1000]; + + // The number of times a data source is read. If set to zero, the data source + // will be reused indefinitely. + optional uint32 num_epochs = 5 [default=0]; + + // Number of reader instances to create. + optional uint32 num_readers = 6 [default=8]; + + // Whether to load groundtruth instance masks. + optional bool load_instance_masks = 7 [default = false]; + + oneof input_reader { + TFRecordInputReader tf_record_input_reader = 8; + ExternalInputReader external_input_reader = 9; + } +} + +// An input reader that reads TF Example protos from local TFRecord files. +message TFRecordInputReader { + // Path to TFRecordFile. + optional string input_path = 1 [default=""]; +} + +// An externally defined input reader. Users may define an extension to this +// proto to interface their own input readers. +message ExternalInputReader { + extensions 1 to 999; +} diff --git a/object_detection/protos/losses.proto b/object_detection/protos/losses.proto new file mode 100644 index 000000000..acd32b1fc --- /dev/null +++ b/object_detection/protos/losses.proto @@ -0,0 +1,116 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Message for configuring the localization loss, classification loss and hard +// example miner used for training object detection models. See core/losses.py +// for details +message Loss { + // Localization loss to use. + optional LocalizationLoss localization_loss = 1; + + // Classification loss to use. + optional ClassificationLoss classification_loss = 2; + + // If not left to default, applies hard example mining. + optional HardExampleMiner hard_example_miner = 3; + + // Classification loss weight. + optional float classification_weight = 4 [default=1.0]; + + // Localization loss weight. + optional float localization_weight = 5 [default=1.0]; +} + +// Configuration for bounding box localization loss function. +message LocalizationLoss { + oneof localization_loss { + WeightedL2LocalizationLoss weighted_l2 = 1; + WeightedSmoothL1LocalizationLoss weighted_smooth_l1 = 2; + WeightedIOULocalizationLoss weighted_iou = 3; + } +} + +// L2 location loss: 0.5 * ||weight * (a - b)|| ^ 2 +message WeightedL2LocalizationLoss { + // Output loss per anchor. + optional bool anchorwise_output = 1 [default=false]; +} + +// SmoothL1 (Huber) location loss: .5 * x ^ 2 if |x| < 1 else |x| - .5 +message WeightedSmoothL1LocalizationLoss { + // Output loss per anchor. + optional bool anchorwise_output = 1 [default=false]; +} + +// Intersection over union location loss: 1 - IOU +message WeightedIOULocalizationLoss { +} + +// Configuration for class prediction loss function. +message ClassificationLoss { + oneof classification_loss { + WeightedSigmoidClassificationLoss weighted_sigmoid = 1; + WeightedSoftmaxClassificationLoss weighted_softmax = 2; + BootstrappedSigmoidClassificationLoss bootstrapped_sigmoid = 3; + } +} + +// Classification loss using a sigmoid function over class predictions. +message WeightedSigmoidClassificationLoss { + // Output loss per anchor. + optional bool anchorwise_output = 1 [default=false]; +} + +// Classification loss using a softmax function over class predictions. +message WeightedSoftmaxClassificationLoss { + // Output loss per anchor. + optional bool anchorwise_output = 1 [default=false]; +} + +// Classification loss using a sigmoid function over the class prediction with +// the highest prediction score. +message BootstrappedSigmoidClassificationLoss { + // Interpolation weight between 0 and 1. + optional float alpha = 1; + + // Whether hard boot strapping should be used or not. If true, will only use + // one class favored by model. Othewise, will use all predicted class + // probabilities. + optional bool hard_bootstrap = 2 [default=false]; + + // Output loss per anchor. + optional bool anchorwise_output = 3 [default=false]; +} + +// Configuation for hard example miner. +message HardExampleMiner { + // Maximum number of hard examples to be selected per image (prior to + // enforcing max negative to positive ratio constraint). If set to 0, + // all examples obtained after NMS are considered. + optional int32 num_hard_examples = 1 [default=64]; + + // Minimum intersection over union for an example to be discarded during NMS. + optional float iou_threshold = 2 [default=0.7]; + + // Whether to use classification losses ('cls', default), localization losses + // ('loc') or both losses ('both'). In the case of 'both', cls_loss_weight and + // loc_loss_weight are used to compute weighted sum of the two losses. + enum LossType { + BOTH = 0; + CLASSIFICATION = 1; + LOCALIZATION = 2; + } + optional LossType loss_type = 3 [default=BOTH]; + + // Maximum number of negatives to retain for each positive anchor. If + // num_negatives_per_positive is 0 no prespecified negative:positive ratio is + // enforced. + optional int32 max_negatives_per_positive = 4 [default=0]; + + // Minimum number of negative anchors to sample for a given image. Setting + // this to a positive number samples negatives in an image without any + // positive anchors and thus not bias the model towards having at least one + // detection per image. + optional int32 min_negatives_per_image = 5 [default=0]; +} diff --git a/object_detection/protos/matcher.proto b/object_detection/protos/matcher.proto new file mode 100644 index 000000000..b47de56c0 --- /dev/null +++ b/object_detection/protos/matcher.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; + +package object_detection.protos; + +import "object_detection/protos/argmax_matcher.proto"; +import "object_detection/protos/bipartite_matcher.proto"; + +// Configuration proto for the matcher to be used in the object detection +// pipeline. See core/matcher.py for details. +message Matcher { + oneof matcher_oneof { + ArgMaxMatcher argmax_matcher = 1; + BipartiteMatcher bipartite_matcher = 2; + } +} diff --git a/object_detection/protos/mean_stddev_box_coder.proto b/object_detection/protos/mean_stddev_box_coder.proto new file mode 100644 index 000000000..597c70cdb --- /dev/null +++ b/object_detection/protos/mean_stddev_box_coder.proto @@ -0,0 +1,8 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Configuration proto for MeanStddevBoxCoder. See +// box_coders/mean_stddev_box_coder.py for details. +message MeanStddevBoxCoder { +} diff --git a/object_detection/protos/model.proto b/object_detection/protos/model.proto new file mode 100644 index 000000000..b699c17b5 --- /dev/null +++ b/object_detection/protos/model.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + +package object_detection.protos; + +import "object_detection/protos/faster_rcnn.proto"; +import "object_detection/protos/ssd.proto"; + +// Top level configuration for DetectionModels. +message DetectionModel { + oneof model { + FasterRcnn faster_rcnn = 1; + Ssd ssd = 2; + } +} diff --git a/object_detection/protos/optimizer.proto b/object_detection/protos/optimizer.proto new file mode 100644 index 000000000..6ea9f1935 --- /dev/null +++ b/object_detection/protos/optimizer.proto @@ -0,0 +1,73 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Messages for configuring the optimizing strategy for training object +// detection models. + +// Top level optimizer message. +message Optimizer { + oneof optimizer { + RMSPropOptimizer rms_prop_optimizer = 1; + MomentumOptimizer momentum_optimizer = 2; + AdamOptimizer adam_optimizer = 3; + } + optional bool use_moving_average = 4 [default=true]; + optional float moving_average_decay = 5 [default=0.9999]; +} + +// Configuration message for the RMSPropOptimizer +// See: https://www.tensorflow.org/api_docs/python/tf/train/RMSPropOptimizer +message RMSPropOptimizer { + optional LearningRate learning_rate = 1; + optional float momentum_optimizer_value = 2 [default=0.9]; + optional float decay = 3 [default=0.9]; + optional float epsilon = 4 [default=1.0]; +} + +// Configuration message for the MomentumOptimizer +// See: https://www.tensorflow.org/api_docs/python/tf/train/MomentumOptimizer +message MomentumOptimizer { + optional LearningRate learning_rate = 1; + optional float momentum_optimizer_value = 2 [default=0.9]; +} + +// Configuration message for the AdamOptimizer +// See: https://www.tensorflow.org/api_docs/python/tf/train/AdamOptimizer +message AdamOptimizer { + optional LearningRate learning_rate = 1; +} + +// Configuration message for optimizer learning rate. +message LearningRate { + oneof learning_rate { + ConstantLearningRate constant_learning_rate = 1; + ExponentialDecayLearningRate exponential_decay_learning_rate = 2; + ManualStepLearningRate manual_step_learning_rate = 3; + } +} + +// Configuration message for a constant learning rate. +message ConstantLearningRate { + optional float learning_rate = 1 [default=0.002]; +} + +// Configuration message for an exponentially decaying learning rate. +// See https://www.tensorflow.org/versions/master/api_docs/python/train/ \ +// decaying_the_learning_rate#exponential_decay +message ExponentialDecayLearningRate { + optional float initial_learning_rate = 1 [default=0.002]; + optional uint32 decay_steps = 2 [default=4000000]; + optional float decay_factor = 3 [default=0.95]; + optional bool staircase = 4 [default=true]; +} + +// Configuration message for a manually defined learning rate schedule. +message ManualStepLearningRate { + optional float initial_learning_rate = 1 [default=0.002]; + message LearningRateSchedule { + optional uint32 step = 1; + optional float learning_rate = 2 [default=0.002]; + } + repeated LearningRateSchedule schedule = 2; +} diff --git a/object_detection/protos/pipeline.proto b/object_detection/protos/pipeline.proto new file mode 100644 index 000000000..67f4e5449 --- /dev/null +++ b/object_detection/protos/pipeline.proto @@ -0,0 +1,18 @@ +syntax = "proto2"; + +package object_detection.protos; + +import "object_detection/protos/eval.proto"; +import "object_detection/protos/input_reader.proto"; +import "object_detection/protos/model.proto"; +import "object_detection/protos/train.proto"; + +// Convenience message for configuring a training and eval pipeline. Allows all +// of the pipeline parameters to be configured from one file. +message TrainEvalPipelineConfig { + optional DetectionModel model = 1; + optional TrainConfig train_config = 2; + optional InputReader train_input_reader = 3; + optional EvalConfig eval_config = 4; + optional InputReader eval_input_reader = 5; +} diff --git a/object_detection/protos/post_processing.proto b/object_detection/protos/post_processing.proto new file mode 100644 index 000000000..736ac579d --- /dev/null +++ b/object_detection/protos/post_processing.proto @@ -0,0 +1,42 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Configuration proto for non-max-suppression operation on a batch of +// detections. +message BatchNonMaxSuppression { + // Scalar threshold for score (low scoring boxes are removed). + optional float score_threshold = 1 [default = 0.0]; + + // Scalar threshold for IOU (boxes that have high IOU overlap + // with previously selected boxes are removed). + optional float iou_threshold = 2 [default = 0.6]; + + // Maximum number of detections to retain per class. + optional int32 max_detections_per_class = 3 [default = 100]; + + // Maximum number of detections to retain across all classes. + optional int32 max_total_detections = 5 [default = 100]; +} + +// Configuration proto for post-processing predicted boxes and +// scores. +message PostProcessing { + // Non max suppression parameters. + optional BatchNonMaxSuppression batch_non_max_suppression = 1; + + // Enum to specify how to convert the detection scores. + enum ScoreConverter { + // Input scores equals output scores. + IDENTITY = 0; + + // Applies a sigmoid on input scores. + SIGMOID = 1; + + // Applies a softmax on input scores + SOFTMAX = 2; + } + + // Score converter to use. + optional ScoreConverter score_converter = 2 [default = IDENTITY]; +} diff --git a/object_detection/protos/preprocessor.proto b/object_detection/protos/preprocessor.proto new file mode 100644 index 000000000..0cb338c8d --- /dev/null +++ b/object_detection/protos/preprocessor.proto @@ -0,0 +1,326 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Message for defining a preprocessing operation on input data. +// See: //object_detection/core/preprocessor.py +message PreprocessingStep { + oneof preprocessing_step { + NormalizeImage normalize_image = 1; + RandomHorizontalFlip random_horizontal_flip = 2; + RandomPixelValueScale random_pixel_value_scale = 3; + RandomImageScale random_image_scale = 4; + RandomRGBtoGray random_rgb_to_gray = 5; + RandomAdjustBrightness random_adjust_brightness = 6; + RandomAdjustContrast random_adjust_contrast = 7; + RandomAdjustHue random_adjust_hue = 8; + RandomAdjustSaturation random_adjust_saturation = 9; + RandomDistortColor random_distort_color = 10; + RandomJitterBoxes random_jitter_boxes = 11; + RandomCropImage random_crop_image = 12; + RandomPadImage random_pad_image = 13; + RandomCropPadImage random_crop_pad_image = 14; + RandomCropToAspectRatio random_crop_to_aspect_ratio = 15; + RandomBlackPatches random_black_patches = 16; + RandomResizeMethod random_resize_method = 17; + ScaleBoxesToPixelCoordinates scale_boxes_to_pixel_coordinates = 18; + ResizeImage resize_image = 19; + SubtractChannelMean subtract_channel_mean = 20; + SSDRandomCrop ssd_random_crop = 21; + SSDRandomCropPad ssd_random_crop_pad = 22; + SSDRandomCropFixedAspectRatio ssd_random_crop_fixed_aspect_ratio = 23; + } +} + +// Normalizes pixel values in an image. +// For every channel in the image, moves the pixel values from the range +// [original_minval, original_maxval] to [target_minval, target_maxval]. +message NormalizeImage { + optional float original_minval = 1; + optional float original_maxval = 2; + optional float target_minval = 3 [default=0]; + optional float target_maxval = 4 [default=1]; +} + +// Randomly horizontally mirrors the image and detections 50% of the time. +message RandomHorizontalFlip { +} + +// Randomly scales the values of all pixels in the image by some constant value +// between [minval, maxval], then clip the value to a range between [0, 1.0]. +message RandomPixelValueScale { + optional float minval = 1 [default=0.9]; + optional float maxval = 2 [default=1.1]; +} + +// Randomly enlarges or shrinks image (keeping aspect ratio). +message RandomImageScale { + optional float min_scale_ratio = 1 [default=0.5]; + optional float max_scale_ratio = 2 [default=2.0]; +} + +// Randomly convert entire image to grey scale. +message RandomRGBtoGray { + optional float probability = 1 [default=0.1]; +} + +// Randomly changes image brightness by up to max_delta. Image outputs will be +// saturated between 0 and 1. +message RandomAdjustBrightness { + optional float max_delta=1 [default=0.2]; +} + +// Randomly scales contract by a value between [min_delta, max_delta]. +message RandomAdjustContrast { + optional float min_delta = 1 [default=0.8]; + optional float max_delta = 2 [default=1.25]; +} + +// Randomly alters hue by a value of up to max_delta. +message RandomAdjustHue { + optional float max_delta = 1 [default=0.02]; +} + +// Randomly changes saturation by a value between [min_delta, max_delta]. +message RandomAdjustSaturation { + optional float min_delta = 1 [default=0.8]; + optional float max_delta = 2 [default=1.25]; +} + +// Performs a random color distortion. color_orderings should either be 0 or 1. +message RandomDistortColor { + optional int32 color_ordering = 1; +} + +// Randomly jitters corners of boxes in the image determined by ratio. +// ie. If a box is [100, 200] and ratio is 0.02, the corners can move by [1, 4]. +message RandomJitterBoxes { + optional float ratio = 1 [default=0.05]; +} + +// Randomly crops the image and bounding boxes. +message RandomCropImage { + // Cropped image must cover at least one box by this fraction. + optional float min_object_covered = 1 [default=1.0]; + + // Aspect ratio bounds of cropped image. + optional float min_aspect_ratio = 2 [default=0.75]; + optional float max_aspect_ratio = 3 [default=1.33]; + + // Allowed area ratio of cropped image to original image. + optional float min_area = 4 [default=0.1]; + optional float max_area = 5 [default=1.0]; + + // Minimum overlap threshold of cropped boxes to keep in new image. If the + // ratio between a cropped bounding box and the original is less than this + // value, it is removed from the new image. + optional float overlap_thresh = 6 [default=0.3]; + + // Probability of keeping the original image. + optional float random_coef = 7 [default=0.0]; +} + +// Randomly adds padding to the image. +message RandomPadImage { + // Minimum dimensions for padded image. If unset, will use original image + // dimension as a lower bound. + optional float min_image_height = 1; + optional float min_image_width = 2; + + // Maximum dimensions for padded image. If unset, will use double the original + // image dimension as a lower bound. + optional float max_image_height = 3; + optional float max_image_width = 4; + + // Color of the padding. If unset, will pad using average color of the input + // image. + repeated float pad_color = 5; +} + +// Randomly crops an image followed by a random pad. +message RandomCropPadImage { + // Cropping operation must cover at least one box by this fraction. + optional float min_object_covered = 1 [default=1.0]; + + // Aspect ratio bounds of image after cropping operation. + optional float min_aspect_ratio = 2 [default=0.75]; + optional float max_aspect_ratio = 3 [default=1.33]; + + // Allowed area ratio of image after cropping operation. + optional float min_area = 4 [default=0.1]; + optional float max_area = 5 [default=1.0]; + + // Minimum overlap threshold of cropped boxes to keep in new image. If the + // ratio between a cropped bounding box and the original is less than this + // value, it is removed from the new image. + optional float overlap_thresh = 6 [default=0.3]; + + // Probability of keeping the original image during the crop operation. + optional float random_coef = 7 [default=0.0]; + + // Maximum dimensions for padded image. If unset, will use double the original + // image dimension as a lower bound. Both of the following fields should be + // length 2. + repeated float min_padded_size_ratio = 8; + repeated float max_padded_size_ratio = 9; + + // Color of the padding. If unset, will pad using average color of the input + // image. + repeated float pad_color = 10; +} + +// Randomly crops an iamge to a given aspect ratio. +message RandomCropToAspectRatio { + // Aspect ratio. + optional float aspect_ratio = 1 [default=1.0]; + + // Minimum overlap threshold of cropped boxes to keep in new image. If the + // ratio between a cropped bounding box and the original is less than this + // value, it is removed from the new image. + optional float overlap_thresh = 2 [default=0.3]; +} + +// Randomly adds black square patches to an image. +message RandomBlackPatches { + // The maximum number of black patches to add. + optional int32 max_black_patches = 1 [default=10]; + + // The probability of a black patch being added to an image. + optional float probability = 2 [default=0.5]; + + // Ratio between the dimension of the black patch to the minimum dimension of + // the image (patch_width = patch_height = min(image_height, image_width)). + optional float size_to_image_ratio = 3 [default=0.1]; +} + +// Randomly resizes the image up to [target_height, target_width]. +message RandomResizeMethod { + optional float target_height = 1; + optional float target_width = 2; +} + +// Scales boxes from normalized coordinates to pixel coordinates. +message ScaleBoxesToPixelCoordinates { +} + +// Resizes images to [new_height, new_width]. +message ResizeImage { + optional int32 new_height = 1; + optional int32 new_width = 2; + enum Method { + AREA=1; + BICUBIC=2; + BILINEAR=3; + NEAREST_NEIGHBOR=4; + } + optional Method method = 3 [default=BILINEAR]; +} + +// Normalizes an image by subtracting a mean from each channel. +message SubtractChannelMean { + // The mean to subtract from each channel. Should be of same dimension of + // channels in the input image. + repeated float means = 1; +} + +message SSDRandomCropOperation { + // Cropped image must cover at least this fraction of one original bounding + // box. + optional float min_object_covered = 1; + + // The aspect ratio of the cropped image must be within the range of + // [min_aspect_ratio, max_aspect_ratio]. + optional float min_aspect_ratio = 2; + optional float max_aspect_ratio = 3; + + // The area of the cropped image must be within the range of + // [min_area, max_area]. + optional float min_area = 4; + optional float max_area = 5; + + // Cropped box area ratio must be above this threhold to be kept. + optional float overlap_thresh = 6; + + // Probability a crop operation is skipped. + optional float random_coef = 7; +} + +// Randomly crops a image according to: +// Liu et al., SSD: Single shot multibox detector. +// This preprocessing step defines multiple SSDRandomCropOperations. Only one +// operation (chosen at random) is actually performed on an image. +message SSDRandomCrop { + repeated SSDRandomCropOperation operations = 1; +} + +message SSDRandomCropPadOperation { + // Cropped image must cover at least this fraction of one original bounding + // box. + optional float min_object_covered = 1; + + // The aspect ratio of the cropped image must be within the range of + // [min_aspect_ratio, max_aspect_ratio]. + optional float min_aspect_ratio = 2; + optional float max_aspect_ratio = 3; + + // The area of the cropped image must be within the range of + // [min_area, max_area]. + optional float min_area = 4; + optional float max_area = 5; + + // Cropped box area ratio must be above this threhold to be kept. + optional float overlap_thresh = 6; + + // Probability a crop operation is skipped. + optional float random_coef = 7; + + // Min ratio of padded image height and width to the input image's height and + // width. Two entries per operation. + repeated float min_padded_size_ratio = 8; + + // Max ratio of padded image height and width to the input image's height and + // width. Two entries per operation. + repeated float max_padded_size_ratio = 9; + + // Padding color. + optional float pad_color_r = 10; + optional float pad_color_g = 11; + optional float pad_color_b = 12; +} + +// Randomly crops and pads an image according to: +// Liu et al., SSD: Single shot multibox detector. +// This preprocessing step defines multiple SSDRandomCropPadOperations. Only one +// operation (chosen at random) is actually performed on an image. +message SSDRandomCropPad { + repeated SSDRandomCropPadOperation operations = 1; +} + +message SSDRandomCropFixedAspectRatioOperation { + // Cropped image must cover at least this fraction of one original bounding + // box. + optional float min_object_covered = 1; + + // The area of the cropped image must be within the range of + // [min_area, max_area]. + optional float min_area = 4; + optional float max_area = 5; + + // Cropped box area ratio must be above this threhold to be kept. + optional float overlap_thresh = 6; + + // Probability a crop operation is skipped. + optional float random_coef = 7; +} + +// Randomly crops a image to a fixed aspect ratio according to: +// Liu et al., SSD: Single shot multibox detector. +// Multiple SSDRandomCropFixedAspectRatioOperations are defined by this +// preprocessing step. Only one operation (chosen at random) is actually +// performed on an image. +message SSDRandomCropFixedAspectRatio { + repeated SSDRandomCropFixedAspectRatioOperation operations = 1; + + // Aspect ratio to crop to. This value is used for all crop operations. + optional float aspect_ratio = 2 [default=1.0]; +} diff --git a/object_detection/protos/region_similarity_calculator.proto b/object_detection/protos/region_similarity_calculator.proto new file mode 100644 index 000000000..e82424e2e --- /dev/null +++ b/object_detection/protos/region_similarity_calculator.proto @@ -0,0 +1,25 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Configuration proto for region similarity calculators. See +// core/region_similarity_calculator.py for details. +message RegionSimilarityCalculator { + oneof region_similarity { + NegSqDistSimilarity neg_sq_dist_similarity = 1; + IouSimilarity iou_similarity = 2; + IoaSimilarity ioa_similarity = 3; + } +} + +// Configuration for negative squared distance similarity calculator. +message NegSqDistSimilarity { +} + +// Configuration for intersection-over-union (IOU) similarity calculator. +message IouSimilarity { +} + +// Configuration for intersection-over-area (IOA) similarity calculator. +message IoaSimilarity { +} diff --git a/object_detection/protos/square_box_coder.proto b/object_detection/protos/square_box_coder.proto new file mode 100644 index 000000000..41575eb42 --- /dev/null +++ b/object_detection/protos/square_box_coder.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Configuration proto for SquareBoxCoder. See +// box_coders/square_box_coder.py for details. +message SquareBoxCoder { + // Scale factor for anchor encoded box center. + optional float y_scale = 1 [default = 10.0]; + optional float x_scale = 2 [default = 10.0]; + + // Scale factor for anchor encoded box length. + optional float length_scale = 3 [default = 5.0]; +} diff --git a/object_detection/protos/ssd.proto b/object_detection/protos/ssd.proto new file mode 100644 index 000000000..9eb78c662 --- /dev/null +++ b/object_detection/protos/ssd.proto @@ -0,0 +1,65 @@ +syntax = "proto2"; +package object_detection.protos; + +import "object_detection/protos/anchor_generator.proto"; +import "object_detection/protos/box_coder.proto"; +import "object_detection/protos/box_predictor.proto"; +import "object_detection/protos/hyperparams.proto"; +import "object_detection/protos/image_resizer.proto"; +import "object_detection/protos/matcher.proto"; +import "object_detection/protos/losses.proto"; +import "object_detection/protos/post_processing.proto"; +import "object_detection/protos/region_similarity_calculator.proto"; + +// Configuration for Single Shot Detection (SSD) models. +message Ssd { + + // Number of classes to predict. + optional int32 num_classes = 1; + + // Image resizer for preprocessing the input image. + optional ImageResizer image_resizer = 2; + + // Feature extractor config. + optional SsdFeatureExtractor feature_extractor = 3; + + // Box coder to encode the boxes. + optional BoxCoder box_coder = 4; + + // Matcher to match groundtruth with anchors. + optional Matcher matcher = 5; + + // Region similarity calculator to compute similarity of boxes. + optional RegionSimilarityCalculator similarity_calculator = 6; + + // Box predictor to attach to the features. + optional BoxPredictor box_predictor = 7; + + // Anchor generator to compute anchors. + optional AnchorGenerator anchor_generator = 8; + + // Post processing to apply on the predictions. + optional PostProcessing post_processing = 9; + + // Whether to normalize the loss by number of groundtruth boxes that match to + // the anchors. + optional bool normalize_loss_by_num_matches = 10 [default=true]; + + // Loss configuration for training. + optional Loss loss = 11; +} + + +message SsdFeatureExtractor { + // Type of ssd feature extractor. + optional string type = 1; + + // The factor to alter the depth of the channels in the feature extractor. + optional float depth_multiplier = 2 [default=1.0]; + + // Minimum number of the channels in the feature extractor. + optional int32 min_depth = 3 [default=16]; + + // Hyperparameters for the feature extractor. + optional Hyperparams conv_hyperparams = 4; +} diff --git a/object_detection/protos/ssd_anchor_generator.proto b/object_detection/protos/ssd_anchor_generator.proto new file mode 100644 index 000000000..15654ace4 --- /dev/null +++ b/object_detection/protos/ssd_anchor_generator.proto @@ -0,0 +1,25 @@ +syntax = "proto2"; + +package object_detection.protos; + +// Configuration proto for SSD anchor generator described in +// https://arxiv.org/abs/1512.02325. See +// anchor_generators/multiple_grid_anchor_generator.py for details. +message SsdAnchorGenerator { + // Number of grid layers to create anchors for. + optional int32 num_layers = 1 [default = 6]; + + // Scale of anchors corresponding to finest resolution. + optional float min_scale = 2 [default = 0.2]; + + // Scale of anchors corresponding to coarsest resolution + optional float max_scale = 3 [default = 0.95]; + + // Aspect ratios for anchors at each grid point. + repeated float aspect_ratios = 4; + + // Whether to use the following aspect ratio and scale combination for the + // layer with the finest resolution : (scale=0.1, aspect_ratio=1.0), + // (scale=min_scale, aspect_ration=2.0), (scale=min_scale, aspect_ratio=0.5). + optional bool reduce_boxes_in_lowest_layer = 5 [default = true]; +} diff --git a/object_detection/protos/string_int_label_map.proto b/object_detection/protos/string_int_label_map.proto new file mode 100644 index 000000000..0894183bb --- /dev/null +++ b/object_detection/protos/string_int_label_map.proto @@ -0,0 +1,24 @@ +// Message to store the mapping from class label strings to class id. Datasets +// use string labels to represent classes while the object detection framework +// works with class ids. This message maps them so they can be converted back +// and forth as needed. +syntax = "proto2"; + +package object_detection.protos; + +message StringIntLabelMapItem { + // String name. The most common practice is to set this to a MID or synsets + // id. + optional string name = 1; + + // Integer id that maps to the string name above. Label ids should start from + // 1. + optional int32 id = 2; + + // Human readable string label. + optional string display_name = 3; +}; + +message StringIntLabelMap { + repeated StringIntLabelMapItem item = 1; +}; diff --git a/object_detection/protos/train.proto b/object_detection/protos/train.proto new file mode 100644 index 000000000..4f070082a --- /dev/null +++ b/object_detection/protos/train.proto @@ -0,0 +1,64 @@ +syntax = "proto2"; + +package object_detection.protos; + +import "object_detection/protos/optimizer.proto"; +import "object_detection/protos/preprocessor.proto"; + +// Message for configuring DetectionModel training jobs (train.py). +message TrainConfig { + // Input queue batch size. + optional uint32 batch_size = 1 [default=32]; + + // Data augmentation options. + repeated PreprocessingStep data_augmentation_options = 2; + + // Whether to synchronize replicas during training. + optional bool sync_replicas = 3 [default=false]; + + // How frequently to keep checkpoints. + optional uint32 keep_checkpoint_every_n_hours = 4 [default=1000]; + + // Optimizer used to train the DetectionModel. + optional Optimizer optimizer = 5; + + // If greater than 0, clips gradients by this value. + optional float gradient_clipping_by_norm = 6 [default=0.0]; + + // Checkpoint to restore variables from. Typically used to load feature + // extractor variables trained outside of object detection. + optional string fine_tune_checkpoint = 7 [default=""]; + + // Specifies if the finetune checkpoint is from an object detection model. + // If from an object detection model, the model being trained should have + // the same parameters with the exception of the num_classes parameter. + // If false, it assumes the checkpoint was a object classification model. + optional bool from_detection_checkpoint = 8 [default=false]; + + // Number of steps to train the DetectionModel for. If 0, will train the model + // indefinitely. + optional uint32 num_steps = 9 [default=0]; + + // Number of training steps between replica startup. + // This flag must be set to 0 if sync_replicas is set to true. + optional float startup_delay_steps = 10 [default=15]; + + // If greater than 0, multiplies the gradient of bias variables by this + // amount. + optional float bias_grad_multiplier = 11 [default=0]; + + // Variables that should not be updated during training. + repeated string freeze_variables = 12; + + // Number of replicas to aggregate before making parameter updates. + optional int32 replicas_to_aggregate = 13 [default=1]; + + // Maximum number of elements to store within a queue. + optional int32 batch_queue_capacity = 14 [default=600]; + + // Number of threads to use for batching. + optional int32 num_batch_queue_threads = 15 [default=8]; + + // Maximum capacity of the queue used to prefetch assembled batches. + optional int32 prefetch_queue_capacity = 16 [default=10]; +} diff --git a/object_detection/samples/cloud/cloud.yml b/object_detection/samples/cloud/cloud.yml new file mode 100644 index 000000000..495876a12 --- /dev/null +++ b/object_detection/samples/cloud/cloud.yml @@ -0,0 +1,11 @@ +trainingInput: + runtimeVersion: "1.0" + scaleTier: CUSTOM + masterType: standard_gpu + workerCount: 5 + workerType: standard_gpu + parameterServerCount: 3 + parameterServerType: standard + + + diff --git a/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_pets.config b/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_pets.config new file mode 100644 index 000000000..a09133374 --- /dev/null +++ b/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_pets.config @@ -0,0 +1,136 @@ +# Faster R-CNN with Inception Resnet v2, Atrous version; +# Configured for Oxford-IIT Pets Dataset. +# Users should configure the fine_tune_checkpoint field in the train config as +# well as the label_map_path and input_path fields in the train_input_reader and +# eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that +# should be configured. + +model { + faster_rcnn { + num_classes: 37 + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 600 + max_dimension: 1024 + } + } + feature_extractor { + type: 'faster_rcnn_inception_resnet_v2' + first_stage_features_stride: 8 + } + 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: 8 + width_stride: 8 + } + } + first_stage_atrous_rate: 2 + 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 + } + } + } + } + } + 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: 1 + optimizer { + momentum_optimizer: { + learning_rate: { + manual_step_learning_rate { + initial_learning_rate: 0.0003 + schedule { + step: 0 + learning_rate: .0003 + } + schedule { + step: 900000 + learning_rate: .00003 + } + schedule { + step: 1200000 + learning_rate: .000003 + } + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + gradient_clipping_by_norm: 10.0 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/model.ckpt" + from_detection_checkpoint: true + data_augmentation_options { + random_horizontal_flip { + } + } +} + +train_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pet_train.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" +} + +eval_config: { + num_examples: 2000 +} + +eval_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pet_val.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" +} diff --git a/object_detection/samples/configs/faster_rcnn_resnet101_pets.config b/object_detection/samples/configs/faster_rcnn_resnet101_pets.config new file mode 100644 index 000000000..b90304c2e --- /dev/null +++ b/object_detection/samples/configs/faster_rcnn_resnet101_pets.config @@ -0,0 +1,134 @@ +# Faster R-CNN with Resnet-101 (v1) configured for the Oxford-IIT Pet Dataset. +# Users should configure the fine_tune_checkpoint field in the train config as +# well as the label_map_path and input_path fields in the train_input_reader and +# eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that +# should be configured. + +model { + faster_rcnn { + num_classes: 37 + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 600 + max_dimension: 1024 + } + } + feature_extractor { + type: 'faster_rcnn_resnet101' + first_stage_features_stride: 16 + } + 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: 300 + } + score_converter: SOFTMAX + } + second_stage_localization_loss_weight: 2.0 + second_stage_classification_loss_weight: 1.0 + } +} + +train_config: { + batch_size: 1 + optimizer { + momentum_optimizer: { + learning_rate: { + manual_step_learning_rate { + initial_learning_rate: 0.0003 + schedule { + step: 0 + learning_rate: .0003 + } + schedule { + step: 900000 + learning_rate: .00003 + } + schedule { + step: 1200000 + learning_rate: .000003 + } + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + gradient_clipping_by_norm: 10.0 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/model.ckpt" + from_detection_checkpoint: true + data_augmentation_options { + random_horizontal_flip { + } + } +} + +train_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pet_train.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" +} + +eval_config: { + num_examples: 2000 +} + +eval_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pet_val.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" +} diff --git a/object_detection/samples/configs/faster_rcnn_resnet101_voc07.config b/object_detection/samples/configs/faster_rcnn_resnet101_voc07.config new file mode 100644 index 000000000..622194b8e --- /dev/null +++ b/object_detection/samples/configs/faster_rcnn_resnet101_voc07.config @@ -0,0 +1,135 @@ +# Faster R-CNN with Resnet-101 (v1), configured for Pascal VOC Dataset. +# Users should configure the fine_tune_checkpoint field in the train config as +# well as the label_map_path and input_path fields in the train_input_reader and +# eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that +# should be configured. + +model { + faster_rcnn { + num_classes: 20 + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 600 + max_dimension: 1024 + } + } + feature_extractor { + type: 'faster_rcnn_resnet101' + first_stage_features_stride: 16 + } + 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: 300 + } + score_converter: SOFTMAX + } + second_stage_localization_loss_weight: 2.0 + second_stage_classification_loss_weight: 1.0 + } +} + +train_config: { + batch_size: 1 + optimizer { + momentum_optimizer: { + learning_rate: { + manual_step_learning_rate { + initial_learning_rate: 0.0001 + schedule { + step: 0 + learning_rate: .0001 + } + schedule { + step: 500000 + learning_rate: .00001 + } + schedule { + step: 700000 + learning_rate: .000001 + } + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + gradient_clipping_by_norm: 10.0 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/model.ckpt" + from_detection_checkpoint: true + num_steps: 800000 + data_augmentation_options { + random_horizontal_flip { + } + } +} + +train_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pascal_voc_train.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pascal_voc_label_map.pbtxt" +} + +eval_config: { + num_examples: 4952 +} + +eval_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pascal_voc_val.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pascal_voc_label_map.pbtxt" +} diff --git a/object_detection/samples/configs/faster_rcnn_resnet152_pets.config b/object_detection/samples/configs/faster_rcnn_resnet152_pets.config new file mode 100644 index 000000000..128380b9c --- /dev/null +++ b/object_detection/samples/configs/faster_rcnn_resnet152_pets.config @@ -0,0 +1,134 @@ +# Faster R-CNN with Resnet-152 (v1), configured for Oxford-IIT Pets Dataset. +# Users should configure the fine_tune_checkpoint field in the train config as +# well as the label_map_path and input_path fields in the train_input_reader and +# eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that +# should be configured. + +model { + faster_rcnn { + num_classes: 37 + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 600 + max_dimension: 1024 + } + } + feature_extractor { + type: 'faster_rcnn_resnet152' + first_stage_features_stride: 16 + } + 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: 300 + } + score_converter: SOFTMAX + } + second_stage_localization_loss_weight: 2.0 + second_stage_classification_loss_weight: 1.0 + } +} + +train_config: { + batch_size: 1 + optimizer { + momentum_optimizer: { + learning_rate: { + manual_step_learning_rate { + initial_learning_rate: 0.0003 + schedule { + step: 0 + learning_rate: .0003 + } + schedule { + step: 900000 + learning_rate: .00003 + } + schedule { + step: 1200000 + learning_rate: .000003 + } + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + gradient_clipping_by_norm: 10.0 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/model.ckpt" + from_detection_checkpoint: true + data_augmentation_options { + random_horizontal_flip { + } + } +} + +train_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pet_train.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" +} + +eval_config: { + num_examples: 2000 +} + +eval_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pet_val.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" +} diff --git a/object_detection/samples/configs/faster_rcnn_resnet50_pets.config b/object_detection/samples/configs/faster_rcnn_resnet50_pets.config new file mode 100644 index 000000000..5e929301a --- /dev/null +++ b/object_detection/samples/configs/faster_rcnn_resnet50_pets.config @@ -0,0 +1,134 @@ +# Faster R-CNN with Resnet-50 (v1), configured for Oxford-IIT Pets Dataset. +# Users should configure the fine_tune_checkpoint field in the train config as +# well as the label_map_path and input_path fields in the train_input_reader and +# eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that +# should be configured. + +model { + faster_rcnn { + num_classes: 37 + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 600 + max_dimension: 1024 + } + } + feature_extractor { + type: 'faster_rcnn_resnet50' + first_stage_features_stride: 16 + } + 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: 300 + } + score_converter: SOFTMAX + } + second_stage_localization_loss_weight: 2.0 + second_stage_classification_loss_weight: 1.0 + } +} + +train_config: { + batch_size: 1 + optimizer { + momentum_optimizer: { + learning_rate: { + manual_step_learning_rate { + initial_learning_rate: 0.0003 + schedule { + step: 0 + learning_rate: .0003 + } + schedule { + step: 900000 + learning_rate: .00003 + } + schedule { + step: 1200000 + learning_rate: .000003 + } + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + gradient_clipping_by_norm: 10.0 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/model.ckpt" + from_detection_checkpoint: true + data_augmentation_options { + random_horizontal_flip { + } + } +} + +train_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pet_train.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" +} + +eval_config: { + num_examples: 2000 +} + +eval_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pet_val.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" +} diff --git a/object_detection/samples/configs/rfcn_resnet101_pets.config b/object_detection/samples/configs/rfcn_resnet101_pets.config new file mode 100644 index 000000000..2b9df17ef --- /dev/null +++ b/object_detection/samples/configs/rfcn_resnet101_pets.config @@ -0,0 +1,131 @@ +# R-FCN with Resnet-101 (v1), configured for Oxford-IIT Pets Dataset. +# Users should configure the fine_tune_checkpoint field in the train config as +# well as the label_map_path and input_path fields in the train_input_reader and +# eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that +# should be configured. + +model { + faster_rcnn { + num_classes: 37 + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 600 + max_dimension: 1024 + } + } + feature_extractor { + type: 'faster_rcnn_resnet101' + first_stage_features_stride: 16 + } + 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 + second_stage_box_predictor { + rfcn_box_predictor { + conv_hyperparams { + op: CONV + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.01 + } + } + } + crop_height: 18 + crop_width: 18 + num_spatial_bins_height: 3 + num_spatial_bins_width: 3 + } + } + 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 + } +} + +train_config: { + batch_size: 1 + optimizer { + momentum_optimizer: { + learning_rate: { + manual_step_learning_rate { + initial_learning_rate: 0.0003 + schedule { + step: 0 + learning_rate: .0003 + } + schedule { + step: 900000 + learning_rate: .00003 + } + schedule { + step: 1200000 + learning_rate: .000003 + } + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + gradient_clipping_by_norm: 10.0 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/model.ckpt" + from_detection_checkpoint: true + data_augmentation_options { + random_horizontal_flip { + } + } +} + +train_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pet_train.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" +} + +eval_config: { + num_examples: 2000 +} + +eval_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pet_val.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" +} \ No newline at end of file diff --git a/object_detection/samples/configs/ssd_inception_v2_pets.config b/object_detection/samples/configs/ssd_inception_v2_pets.config new file mode 100644 index 000000000..801701ed5 --- /dev/null +++ b/object_detection/samples/configs/ssd_inception_v2_pets.config @@ -0,0 +1,180 @@ +# SSD with Inception v2 configured for Oxford-IIT Pets Dataset. +# Users should configure the fine_tune_checkpoint field in the train config as +# well as the label_map_path and input_path fields in the train_input_reader and +# eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that +# should be configured. + +model { + ssd { + num_classes: 37 + 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 + } + } + similarity_calculator { + iou_similarity { + } + } + 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 + reduce_boxes_in_lowest_layer: true + } + } + 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: 3 + box_code_size: 4 + apply_sigmoid_to_scores: false + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + } + } + } + feature_extractor { + type: 'ssd_inception_v2' + 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.9997, + epsilon: 0.001, + } + } + } + loss { + classification_loss { + weighted_sigmoid { + anchorwise_output: true + } + } + localization_loss { + weighted_smooth_l1 { + anchorwise_output: true + } + } + hard_example_miner { + num_hard_examples: 3000 + iou_threshold: 0.99 + loss_type: CLASSIFICATION + max_negatives_per_positive: 3 + min_negatives_per_image: 0 + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: 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: { + batch_size: 32 + optimizer { + rms_prop_optimizer: { + learning_rate: { + exponential_decay_learning_rate { + initial_learning_rate: 0.004 + decay_steps: 800720 + decay_factor: 0.95 + } + } + momentum_optimizer_value: 0.9 + decay: 0.9 + epsilon: 1.0 + } + } + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/model.ckpt" + from_detection_checkpoint: true + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + ssd_random_crop { + } + } +} + +train_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pet_train.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" +} + +eval_config: { + num_examples: 2000 +} + +eval_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pet_val.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" +} diff --git a/object_detection/samples/configs/ssd_mobilenet_v1_pets.config b/object_detection/samples/configs/ssd_mobilenet_v1_pets.config new file mode 100644 index 000000000..e8b0516f2 --- /dev/null +++ b/object_detection/samples/configs/ssd_mobilenet_v1_pets.config @@ -0,0 +1,186 @@ +# SSD with Mobilenet v1, configured for Oxford-IIT Pets Dataset. +# Users should configure the fine_tune_checkpoint field in the train config as +# well as the label_map_path and input_path fields in the train_input_reader and +# eval_input_reader. Search for "PATH_TO_BE_CONFIGURED" to find the fields that +# should be configured. + +model { + ssd { + num_classes: 37 + 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 + } + } + similarity_calculator { + iou_similarity { + } + } + 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 + 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.9997, + epsilon: 0.001, + } + } + } + } + feature_extractor { + type: 'ssd_mobilenet_v1' + 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.9997, + epsilon: 0.001, + } + } + } + loss { + classification_loss { + weighted_sigmoid { + anchorwise_output: true + } + } + localization_loss { + weighted_smooth_l1 { + anchorwise_output: true + } + } + hard_example_miner { + num_hard_examples: 3000 + iou_threshold: 0.99 + loss_type: CLASSIFICATION + max_negatives_per_positive: 3 + min_negatives_per_image: 0 + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: 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: { + batch_size: 32 + optimizer { + rms_prop_optimizer: { + learning_rate: { + exponential_decay_learning_rate { + initial_learning_rate: 0.004 + decay_steps: 800720 + decay_factor: 0.95 + } + } + momentum_optimizer_value: 0.9 + decay: 0.9 + epsilon: 1.0 + } + } + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/model.ckpt" + from_detection_checkpoint: true + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + ssd_random_crop { + } + } +} + +train_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pet_train.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" +} + +eval_config: { + num_examples: 2000 +} + +eval_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/pet_val.record" + } + label_map_path: "PATH_TO_BE_CONFIGURED/pet_label_map.pbtxt" +} diff --git a/object_detection/test_images/image1.jpg b/object_detection/test_images/image1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8b20d8af3e195be7f4c212e31102cada9248dcde GIT binary patch literal 129862 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

    8rW|*Bt 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%0Dc_u$+g$b=*Z>EDuQ&9cQRQfrS>R{lV!T>n+{B~ zj~>3$_H!${EGw_SjdvsSHpgu;yNk6s{8=V#$tT}bEYXG?t4$RUS@Fn~hQJij`FQ-+Op=B%7{{OPnUtYv+tlK1ncIx<0f{rWAz58f<07?Wx8G}ZuggtintEPzcZh5# zM_G0X^WoDVO> z-Dgd9b56KQTt`P)H9d04F2AzKFm#Je2>F_t0^H>P@J*+XTTL^?8O`aO{=Cd|g8td5 z+gZr?+9LRMbeM>79=1011Ny_?y0jUjTkJ?Q=G0nW{o+UcC;GC3QolwFEIh=-)s?@l zY}Sv&kq}sHH@hw%$_eLy!dzWR)sylr>Y{#XdV6}z{qfjTm6d#%V;R#L?OXe953!{q zInAVv{<_G!fgiFKB26|~n(dyp1W&EW_s8QFNvB^jQbRE*aM}-~lx8Du%;mHp;_jN0 zzWBY&e^3z{#QU=EmE9A&TiRqf3PrbF?8R*M;>mA;gF%Ya-`VTh3_(068q(qxi@!jGQ z)YR4OBnlXokG`%>dt+u=Qps)bFE%@H?WPeIKxx2s-#1dIr&CyZa1Yg+>+Q{jd1$VV z703AGQtdzN4RuXq-dWY>sFSFVwZ#C}zBcN0?E=Eqh`uh(F~utmNQ(4V%{5Zt8Q!)U zmw_4abx`sJ;Xc6Cw9+`O36c=tY@lzTuu7kl<9A(=CaqfFU*y%M`;t19Vjp#Cl)?c0 zIXLiO(s0y`V!+CKNB#BM;mYfm!I|TEjA>&=AksmvgN6)5ATw8=z}h*LH0{m3Kf+fe z?luuldA9xG(X)qR!UYXEGVi9|9DoaT!p^Y}ZwZx~1%2^zKASx-ei$+xLG(NyR3^HK z7y?Z<-;HgSpEf6KPt*DE53LM0qD&C@@Qm4qy8%e0b@j^X7{y8Hy9S(Hy5B=IhP2T{ zeplR~y-lDfq{O4mg2!e^4_RZ)wD}M~0|gYn?O?Y*6N-z#lJDoG#pLPO3p#QkeD9oK`9z4x!oGP<t)H-TDh&MiWl+#!cW@=m&p$*) z!b+aU_iGrK-G>yJ@&{*cjgnlqdI00)tZ~~#A@C5+>U}Ru}4SK<>RJ6N4Q{# z@#xobpwY!p6HHX!EY~QMhTIdAbmVQThoiF`j5eLLdHd945jo|#jgY;#r&&_k>kcocJDQwLhzgGNd)VyV@srAb?5;I%q;)UniXO(-G@* z7JM+`_tptjSNU9>7+yI*OVmJ_76p|5TD(fO#3e}894ZX*=8&EQk|ESqebS&CQQJf- zSKtSa7C)m+QgME(ivgKVBf*l4mEw+`d=5)s<64jc!I*5_*Dr0go=>HmKtLH1X%pRBEj5VJIa8*ZofA)k8g+H|{CW+7t8#1=<)b>vaNNl17%IufY>6YwNYfmn z6pEwRAa`x0Ii8iWh%r=w^sufRJ&+b-omM?ajDK{uZ zZ|9WoOCk*96q0?8%R!uVd$R##X?sCV`TJSIJ^gR#cg525TNo=naba+6;w0CA=f%ZL z)Jie;#>otVB&-sjjfx9m9wcGC*}wLiVCk|pe`{5{Z`@qF6OR%XdIk5tcZ;hij%XFQ zGh$4)6}Q{{ouBL;aTbhq?$wiLv-M$_fJFkIH?vEb{!9jlW zx2>YdY{X~pG6&A7(>H&|y7>L!&?P}nvYloT)N1s@Wp}b$U197KQkZsDYG{dg=@gI? zRJOeS3t~f07&T?PxuVXNlBn`*&AM5wnEmawSDknp!3f1DGOWZqfzM85Lgu3JosLUD1pV9yM{z z(6w48%JAu58Id_66q_rw(fLSxWu2fu?ca4qPYYk4&49ovu{b*qb;i^M?*v&A+z}K0 z!qxKoz=@ykS?(G6x7R1o9Iu;a8IZprZVF&izn?~@yNO?PM4glC zv=kkn7-eOXD|Nqbrs=hrdWI`H!~htg61Lhd>tRSP^=6I88!`;?X=(OY_&98XC|LKw zVtl(?koy#n;F9)CirZ#N%@2~DvEPQDgf8wR1WeAi?-aIgN{tZiRQ}}gm8TiD^z%HRklYo-{;ubtH^ft+Br(o3B(% zTP3O-Q13JQLR4eK>vHp_&eKA26jf;L=DUk}o`RD;!b}Gcw*RA3|DpXS^}4_Mhn6J& zIGuOSF|zxTtenOUXQzKyjF5k$i#}ju@gveUbhGzS1%=5|$~|>8?RXP~Ld6ie>nzmxIy zor^hJ`|x*FAX3W7{|#ijc+oup#S4%hX(GH25njE&5rxxFn9!XJn8gpWN0eoMPb~NG z2iNQA2NVN6iuzVYX)iALV}LBip9>ae`u{b zA+Akv7sG!~G5?{BC`z#Ql6FZMbU7mx|Jw+bC++D6&Hn~OTK5d>^(W$D{E7TEl4m7- z2tk#$A5#8Y^oUZ`G~>mq^x0cdQ##Xd%^j|?QVNWhm;P>lt)M1IUrduzIyJ_{XM4>| z#$m*g_GiAoe!wi}h^C>ehm);q#yO@G#kIs|yhDuM{AB3b6=3?BdU?|@TUW}lDYjjz(7k*;#dVZM|fDk?1sFFv2hli2$$hg4j z)D_0|_CTU@p5JthR_C62;rXrLZBziRl)+1fVO&g_(&(G(Dw`Yj2s+E6Sb52m;@1fs z(240%nhFDI%iVE%Nez6cgkm|tYRLkdJdMKf=M|%}r6rL*+IhV6XL0Li8t-$GtH$>0 zT{c_LHT%VP!n!T)c;hAP^4eqO-;guzqN^vF(;R`q=?ru~s6FK7_Fi~+&MBji)vxf7 z(B6HEi*vfWS7E&=O2{(M)?fQTmPg{k9ZWmkIAS0F|461cD3ZyfBKGH)f9}6Ugas0} z)Ecw5K_|E)NIeFqA1`%c)?4dC`r_U5UB76(Xi&d^z#?b~p<+eO3FRiq$-Ut*h5X?x z7*5!x6la_?K5rj)}&{5tDO#}X4`Kd8vXeIrwE zsq(L+ZLtfQQ#Y9eE6s?GiraVAz}IGPU&Z!& z`3|!Z)W!IC(Z{07OBXQ^V!G{ZK;<|sE5u6~Y;9tm{(X^FL#QVQLa{*Z{zQ!V*@PWpvR~t57 zOtrcJ&LXi9?MTWqXk(b1?xhQn8QW+eNg&R%TaiPpBWA6VIH{!H8C|OFJ8SYAv_03y zj?bS)Uw)DS1B$)w@YtO-(CKe(D;&zGQ!(8~N+~+=Ql}uIdw*)AjbZ_qcc}HxaS0s9 z4DsPoLswe%Hcq@=N~Q3o=(N_4g4b!A?os(?)%g+337&7OWG>K0cGA5IOc}o%tXb4{ zd$TROf1gu2#Y=rx*1@X7p~e?*D_X!w-x;!+BSv(fyK*VuMZTUWHv7F;*-Bpf z2X8c`qOuDxpMx3awOpi9^b7y_=S8tEwy0nFKC)W5EDgkWOW0y~9Zvh%3Q^%w7(I?i zql#$4iauLXXl7mb!<6a^m_J(8!!_DB(AL2YH@BtZ-gz|x2%!xPl;$>Mi=;G;NS4DY z?88m}O}Y6RnH{(lAg;4yt@Vo>jcBpdFvhLP2XabL2l5vk^^&X(lWEvL=nWYI?I=^@ye-Ac%n|scF&LH!^lxdF`w&ZD3Mtuu zSum1o{SP3n&d?Pu=p$5&koS?=ZXLGRa*m+XM~=7E1#WfwWa|ohnJIue-)vdQj9iP> z?$R0a;p8nBgJpHgOfFL zxsnnp)-kx8G&3bNzQK01q|m=x(1<4Y#IQmJLGM$0E8YR`_w+3|dT^Xd-#%5k9GGaE zsj@_*o+y9Yl9SXimtn(Jq)(cEbbU(nBwFDgFnRT-i)p7@Spg;=cth@~j(?nCTrhPz zH$;|r@~3s2(V2>mFK2p~5a+s=*goT1Lba`~PzI0)5EXJqDdRP`$g^6J2wKs{@J2NM z-E73{lw>6v0FAr_b%0is?p0EKORNXs)PgFq*?w7*!?+@9jY|-50!((ShnsMEvS(ev#wWJvVsq&L_uX@cuQMnXI8n_o^M?U89lO(;5 zvR;zb#oeF;#)sG`d&O0^Q-mm23`In=Lri>MQhz_C6p@rw+^U5n`d6b^MD< zuEz!9yEA3 zl$+8gZm5S1aspH?lb1}(-~7ThP*9MEE%I~sj6Nv|?#Wzw%7h?41~sLHHcblcg9kH6 z8@HcKIQ0f--_AOQ=ay_ROS@3LSx9aWd-0K-FNfg!=^I5Hs!=Wf`qI9#*k`v}X4f@e z;0!^fv9Wv?&@R;m)cT(S8oXY4L47CVMl>MV~!&ah{jcTM`d> zNs*w!mwH$625C{s@Q}1^qG1~6-$D&5jKY+ho52P1oOfKVjQ;5ceDaua>pK+w~ zyTB&W7RRfp2gh@h+sp2_$=qO2o%2vGCQz#reglgdlza!m*_WQ z`&3sKip2S|rJ3tz+FgZe2>0+IM>mAvLp!FpM(|421q&3x8s=HNP}3#xlu99}Tox#_ zugAFkHpw9mpXP742qxX1Wr0I;japs~Y$^^{9d3`o_7N-k*!wE$ttrwLQS;(oItS6d z+8Ra4IvGvo1%*`1r(-FZ9T08VVUn-1vkzUq&fe#W>l+W9>-U@ATcm`Lf&s(5dPWrMRf{6w`v5b@3Sr;}xTj&l9@tFosid{nsJZXRxS}iW02R-1R!^DL@a~>-F6>V7(pz8k zY8Jhow*=t(p4q9l)@h70lgIVxh8~2!9_jgll7lD!-@kpj#Qn=BruVl-)2sceV`(x% zr3PJeWE7CmPSDh#KX zNvk8<0PzR@;Qnc)Lh3}exOFcuR7=i{ca$Wi#=uK}zC)O09Ix-|4?mso$aj`kah$OV zf^xV9BH+}=09KX+PMFQ7MS@QH8&#s|>>Tf0nte)=xbF)>-LY5=s47Cz$ z%@eVf+Y$7z9Ny1qW)8*Pf4*5RGf9Mgg)Bt zqn!snvGkYR=fY?{)UNZIY-$$R;>2sQ3njM-ZqoPl)9}xg*66a_o2~E#cf-$&3Blgh zt{m?_Y(2Ty#m{hAGbuYxe_${5SNIsV;(MlU03@}U>ep-GU(#{cw^NQ;9^8PNYPng& zs7ZF|yxOMGXIy2=vdHzjR(zvlP7(Pe`)T-@q7@oOFxXD1((e_UVvf7_HjN|WIz9mH z8P1_vQvXWI&OM(YKLb8rPBf9m_sUt4LXT}UQ>6@%sJc~h@?#|(Dpf^7fp#zJ=^VDF ztS-|^pD_DLTn^6DqEDCGW&=*xxP4phpObF&;n8CXJ#zt>IXx-%=75i#i^X4P$^Ev>?pII;UaWasWV-Kzd1P5xgZEJP*L-fKHm zZM*l{aGtP3wsQ&F^g>|gWIu}TSN6J!_N+Xvd!|((AEWqpsON+S5Y(U2IL`}OHyt4f z1M=JDFZo_Ra|yVyU-pqH(J*dbUGuIfRF7QRX8wr}jn)lGb>RSY#6d)AyHji#20lX_ z3F#2em08F0Mlb*j)ry319xhXQ&Sw4)Sx>81_20<*i#|pFz}fO(bd_&TX?hiOWYH2w ziy6X8|9#d@;q7O3DqeDl&ILrLA|9|p|GTVC;@aSVD^thC`&lhiT0~2swb33FGk2Tu zx@w@8Rl3t#4>YIYWsA!3PI|eylV=i`c!nKm-qb_C&{)U<$VT=U6gpx z{BI*e;&!Xk*7H`9q>)Zb5wm5ga7}6b29b?#&=wbiT>ATG<-;33=i0>u{YjnA-|M)x z5p1_aUr5*-z5imHVdGNd;#<;H%|fet<_j&)tFsbupzV({@a)9y_k>bY&$t@q)2-6D zcVW?6JNfJQ-thb|S0Qvql9>DZcsXIxidj@^WGu4KV*xr2nzdpE&%$UdoMar^?)HD) z47(=15&6rHQpJKE85DggR!5ww(Dt*1L!b?`-);}$fD5H0?cC`Oq#Z4{gbL(e<4l_G zEl^1_Qt+?Nu(TcT{&RFYv6gxZYY^=B)~3l{Jx+o_SFUWoL0qzAy}p<@3Oxn}(b}2m z=#13ztvIyx;|`MJkDdgBGBoi*7?MPQYLrg9#KsR2BUqj{=Po(QTobzq62w;nqy_tG?u@Mis{@1NS-ie=n zsd6lOw_dZJRMQclW`bxTH{Z107fJ@$9}2WqND>K(Zm8Nuu4_wx+S@eNzr`93X_@CN z!gi&T3SQQRICr4#=7%QL7w>O9FRTaS!eQ`A-OhQ2j?^Pd&!()m@v)y>W7}x#FSAPV zvSB8kk$qq5j>y~ROdvUGcp2GWL{*1|kF;#vd}GIVjv>aECJ0)?;dYJvFJ zulM?>-Hvv?XOrOQ<~9R=l~S-GAWhv#U6dfaDBGg@*4_zm6w0JgIw@(5HCC&1oTh9$ z+0k-XTmv3#p`+}<4wy0}l%#|k_L~xUBGifCEw`Nf1C#-o`4jttB24&)tsd{tcI&9* zS1W+xvPn#n?6gS4n4M10;PlV|FD)4l(wLMQ>I4tRZRx;)T- z<#ff>EDPO}vOPCth`vydfpMw{9G17TFdw^%n&_E+)h{meTR+Y}(trYEvv>B`Z7uWd z^$mO1S1q<<u#QUh_GNEv?YBSC+prvX;`@naHT*2&G7By(c!+V(60TTCMxlSEg0q_S@vm zn=9plt6yO(12Zd2D?ZrdlA|oE+r(?YXt#Q5QSE-*AUStao#OzUKMe3s9_K4|SBkJM zKiS5$qleVWe7U+czGo^yggS%(v~N9~bPcrwtZJjp1Ulh5lOy7BWt9xKYH)pyj}!AZ zUigxh_IaQW?D*bc0Sr(VAHP?nAGnf>xonhpoHJHMw2fL?vsYD5q6!DQMCFXx(IO#1S@>{Hf6qxSlqjZwSDfKQ=HYdB#ARO#G!^{XHIL4 z-f{Nh{uqp8y~ithmksg<6;hc?1y_cuKfc~DjRawx9ynqsz?GRZcL>aEiJS@Z-8p6? zn%EgC?qi-6-D}48+R#jXA4G|7Km(YL!P<*x;^?>ak$*1q`qVKWg&~kZNt(`X{+}kC zsB>Du!E%ep?qP~@YeR7SYDJ{Vm=aiDw$f}^rk&QPCSF5}h_)>z-EF?hUf$amol-W$ z68mh;-nP6!HNLTUH+!b`Ah}AD+&UKRov0>KB$fvTqP)dQweI4s+^L2x$hYrx=$aHN zFS+EzXqyq&4m0;xUcw9uZ*5XWF`~2nbRh2AC zYx1(4-p?BHn$0#U*O;(ckf4wd8W<#@s_a5eXPcM}$RN zwI3hqH=Cc`37jy=C1Vd$Ew;X#efO0R1xu$47f)R5DbQa(EG~qVdVHnz2rpIAj*^v= zi<~6NQOdt&ye{$|ere%M0gc72xY#Ugw^b0OO)l1i-isbcE@Y>B8w1u}r*aD})f4nl ze{0-VPp+qT632iDiaWkzB0n=idMrDvuTG@+(0S8TEW~FxYcpj9jdz}1koapOA+gF9 zz3t^(X>`7q=zBl*%h%!g+0_DpLhfOT(A@}I)&a729u;VOx8sw9G`2i zewxY)49wcSsob4&r7ZWG%4LVLlpnEf_*0XLLbG%fh-Z{~9a=Qd*5jsvcI5pprmGbthl2-%#hs za-gyV=C__i%U^vWdyceE1cjK#aB%xAegbGjE0KH&JCiOI_-V-IqFBx_8`BlIA%3-4 zUY+U86Lm0;*g92E1mq!8x1_v$36( z;NrhKw`RHaU8oWo=76jCDrxEq`#q`U%G>A;xGaJUj@Jhw>JkE~!d?xDh1HXV0{Ix( zsJRM-5S?jg_d@m9T9I#}tSAI?uPV6TRy2n9ePsXhD`t)ZT$hJ$p(*QYFcsrD={ z^u^=H&RuOvUG+bgysoEy>AupNSP$eD7xLtg$Zd45BxhkPeK)s%*V5e7Dwyaf(5$vu zIWqy(#;*8Y^QE=0!L22ywLsOa zqM*&~#Sl*KCH4X2bL5|YXrwoE(X{j^Bc~W#S*8`{l%3Dj zoPT6O*=;LuYBKr@+y}~GeJRtNd=OOhDiPp{knjnHmPYhYeXKyvNCIZNBz7#k`6f&L zLP}8J@UC3{2xej%OXOAkJu^R7bhfR$nr=1Yee#@ZvOieWblFR7;%|O*@lRVB$!^-1 z_W}sX^#`LPpz=;FK3rHS$r$GSv&y5BymxWKPFNXV@#Y)-^YGs$q8EB`9=8i!1Upas z`ptSv5HX!gwZLw^kSkN+fInhVAw4Xh{!d6+;5#_vwQvt<%Z2f6?e-I%HrmpWSc5WU zqe9H(*hL}x+ZGo~4s2-#{^5_?@+Tw##dEMa&m7mac|gtZX@NlsMTbu+g9<- z^Np_Rkei$xk*VL=%F1H%%A8m8rDQrRdu)gz$hX?dqkC{C@0y zYrf95R)AcOG*{#4v~)LS3-^24^CXIr>$UZZ~r;JKs8Y!*Uv7!b7IG0yNsX3%|3q zwIQ6Ohh1$D^v~l7l;}y#E_{kdinW>l1y$q2a}}OY=!%Vf?+>0eLm!mk-*8d=H#@to zW5Q|6(y3kb-B}hyOR$m@_bZm68tO2TuQ@k2Zg?o0Kik)EHT9_PP zz-g7e9z|sSPmQcss*07uAWu_c&YBI?;_@4CxFo})1T41Lv(QHwY#>?gZSLxCUwTA5 z&eT37>8YJ^z>urd>b}E?fzVaQ139jMY@dFvfvoSL)0geE-=;d;)TT{Pi+DAAmir>5kZf3fq(SeP_PtFn2K#Gfl z7{T6%O*){XV-Zjm9wkv8r`X-X)I`AbK~H7wTe^$_&46zx1=9B0_jXJ@FN^->cbb)2 zA$Ce^gflL8Ywdg38G3hGQJLSl$_sA!Qgl@cN*UHZx)>qaYHb36F_s+8i1eaGLW}#dDgP&{4p>v(8a_<;@kfnF`jp%7M z7jALSaB=eS>9N~%-|*=#keNuFRKmtr#)&Qgg3&Eqx{txQwQC*I{5Fg;5q(Xk!Q6Z(HcFl&)sOGA&+6)6Rz}KELN~YnA!4@T+8L^(Qs0 zXoV{4;XWFFG_+XmWJ-;Z{4ehiMn_L5WI^cKOws{Oq>Q0Q-*Pane@a%{3{D}+cs(h$ zg2~|>8x!0T^(Du?0p6+0rV$HZ5ZLy}?_bS%T#&JUZKvY9d*QA1RxD_F^fcqyDW`I0 z!LO@fzYL%kFHYYYQ7sKy+%Jt?5r7DNhKDykxq{IawHa+P*61wx9WQwwwDk2}HuhE# zD!~7Z)YOaIE?;ml7Fi-6y2#EiQ&rr-7+OQ$(|hfhdH*mz-@obx!SeK8@<@X@ zlM8164boJP!sx;qdNx;3aa*IN^V5$sXrZ93Sh^PhMu9t0L};#aNM_VWlPc*>pnnhq z7>TO`CBm`aY>=plDe{T@y?>{IYE{eYZ4TR3NxcL)Z$8F$%iPxV-gd&5Ve(s&QD1lB zuzdWex|t3AYftUl9cg7s8<^pHNw)vcKD&H|#w$pm*Mf33+_&KDjYf3a*Y!d~{&3lPkjoh=)qRYYQEV z{|Fv4%{Lh<;f;`ICmAZx;G&uV{Ak%>>?lGTEQJF^d3OO9nHn&uM~2%6*#%@_NH>ox z9?obWeCyf zlMevh0d4`a|Ik{dPrYHO zAj?0rpk$yWTpb~~_Q*SLa(g$}q1*224k}j$inno2?S1j4Zsco0$pd`8+Z7Kk8$VBq z`3B)Obgt-5eR4jmnWW|)R3d1pO6I)1oKtM?s9>JWlL%?@S;d~P^u(Cc}CXD^@%F#_t-a(J|f(41k37sZ{Z|cH&hdS>c z^$6BGz>ax`HKcfq6yw9Zsf?)DD_z%}$FMJ>Ul)vf7P{AKd$*4S4G6_=@AORJRJmpU z(9G5^D=vrtv!#Y2U@STLX5cD<2o;s`0Z5F{-n0!MNZeQc?sOoZbF{y4DG_r})pjY5 zfzn1Uxtlyy_{_<+CLXJkVOjpf89zhgh{ll}JyTcrdmz^-6;mp*%rcp@56A${%uJkQ z2q3|jAvJ&elOpYGJG0tU&|LU8S9(02a(ZpfBgIOZYi)C1+&l>#t;%(_1ub}Ew#i2) ze$BF`f<;>dFLtXB(KUtrK$5o2J-+y3-pD2;ZFyZ}X-ww{McVgS)Icfdn*Sa#Y+l;0C4%MLTdu{9ID z&Io3-y|LoP-Y1w+0}W6+eCUH1UJ>k$`%Qo+e=#%PbVzx4Zd}?m`wi34h)YX#Op%eA z>-~N^%(urhIO?FRrUPUOaZfF_mIHRh^fEoZvAz8jLH;&EdH)c}+p_24LN z1C}tnyf$E_!#YxuAE*oCY_9GZ|9=2vL7TowKe2By5@~Ecd@20&s*4F;Cx%x4$YrY!rwwpGot?CiS90|HZxMv?u^>aYfbZ-%9 zaqBuQ{2WN_9QO1%G=-y_&ZF0Ps&jP*9`H-QQ#GiEV7qZy*4JEJ@&fYmxCU$FGB)1o5Vy z@cYGHGPbkTHD4_0@ju<0%+AfHq0R^%;=eS0IDf%dziB_(gTW8r&xHQ~5A^>43|rpZ z{kKZjAlJs_w1vQtq&UIcr-T{JeDmYai60eyAnG0v@b8U&B}}&74%Fv|Oz~x;ZW`(- z6<=gz@=z1c&E|ts^3eibUCTIwHGp0UVGcW<+=OKZ9Rqb z0`pJ0j(C}htk5`RBpe(O!RLyb2p4DF$^ysUY2x378j#Rl`dKb7(m>zYw)qOBi1|ny z=LfBNwDYW{ZV~p;=8{g=yY17?`t6}rWhyOQUj0_b=v>N$NfD0OIjVC&j8B!`yp!WU z?bYxP;Qs)Eth`xw;*ae~bxTCJ(=^RAhni$%+zCAw@vk26@B9@9Sn&>@r+h}Z_!+Bd zUK6{uzlg;+65d?i$B!gNs7TmAV#J(|J?qr=w4-$?E29ZkN>4`Z6=dfF*UDpA>%xthC0cFbU6A(Q$Fh}{sS8FrEV>bz}kr5W;g_g}qH zohoTtEd~#IYRT#vwV{=9c+3K+I6nQVyw^b!FOmY_8ryFTZ(dOHco?LA2fT*__TcuX z%?*XiCUfnm?Zd_o(DtS_sQDA&F4;iTkg>2$bvBsGPt|>Z^*@?n;xq_E6xyp_* zI*xw|w-?%*{#}V1x#G8B(XEhxVg!#-T(8H!jQ$G!n|>4OzZiUPZFxSMq^h*gY1YPB zPkWH|vnwlrIKdsO;a`b=@LNqQ#@aT&q5o-(JEj0M`B{9hk0TP*k z`=Np09=NOz*w^-u{h4h%7vo(ASNMUc=(hUDg=Ep-(Dhb%A^T%1F}RJgKvpCwsOS>B z%r1AAVll3iWTd03>7ssn-^uyxa$;pSgp{e?EvL%b`%P)1J^ujWZ-^7cUMoH$ig@Kq z+Z%afk~5WMXu$-2b?cu4J{x!|;wQl+(!6cqEk9MYp7!M=hWgQ5W6V7X86SmwxA0PT zx%gS3NiMS2?lk0CZjVM_%m^OCp!UUmnfoUjfA~{2-f84&H(kH!p|9s}4|4UCc+Z*O z;Zf97{;-su@=CNU-<`h`^sH7d6_|K+fW%_pp;B<7=M}0_bl);v)$V-#@qhLZ_;2u= z#<70Tei-o{wqH%tn@Q6&8{6lW+QvJHQKe{sJZH;6j@x+ZbG!R9#U#HJc0>k7l|Nti zlD%*Cvvq%jQ~bCiQnts-)NijY{gk$!i>D+0VHY3z6m`?a^PK#0%Uqq^s6U^ysjshh zyENhKE*}ZNSejHKqZI0Bv`XnWW$O0W_mgLJjD_#hy&~9Ly7^&`xW#RvseF__GUIkI z2%<93ZU!(9`&~fwujx%$`P)d$hex^tGW5<)+R(Pr@_|_3FFC0UtiTn|`%R81rRD>2 zktXe!cxD_HHi3#(c`_Va~{3 zC_g^cLg}A)2RQWtuZYUF`~&M!HMl2nP&3C$q^vvXV;jkt^N9Vy^rbPfHhJBiq<*!! zH2Z%m(;VcQ!T5vYuZQ0Xd@Fb3uZVsuvC=e~Ex&8OyqE~%&rJPjx*?>IAQLCf-47#* z1guG9OmolHzFV9A3t#^L1pA9x@dlMA#EY{IgJRb!s4kxVPNGNrvbH$=>-NCScxQ+I z0N~@lf;zvC2ZuEu+1tdHdQ;qf>s0%Fvq;brg^E730dFRhFz`{yA z>9^ZoE{EvBIU^$+eeYV4V<3FP86B&}J`R7uM1Ny{jGh^v#d~_zYTnQq-$_IYao3(^(4Zcgn)R>K36F7ExeAH z)}=RPC39iQ#ctOUw$)Hs*_(g|B>q+NukAhm00lYyp8o)6Z7>ThLSGeX8v}itL?NPy zSda{o{ng|iI`LG$;H^KjWZ$#@0PMeip?GUp`!|R@PT$$~s|(_IVPX5pfbE6PTKr1Y z{wQj8npBrVOVtjZBL!_P;AM*8`5VU^uc7y^8yk(pLa(yJq~Be7zKM7F=y77R7}2Vz zsJ7bD%X@F-qPO0D(fk1a0D`oB&3_s78!rm@qV``3>sRt^h70!JZs7F&@cBVLhQ6SJ zSgs+C;?_i(IS?48M+~mMppK%ynx74L&PjCG_1C<3;W7UJrD-#dn_(m@c_WOT-Em){ zU+_%7_$pQB!*2;{KMDRXnYAq*!qH#C^UF6jZU|FmPg0oWSo72#mC=XC%AMhg_hh>4 zy&k*2xs!#*y0c14eOG_Gf7Ja#k=+#Sh>|*eDj_7>zjq04J9MreR{gZUW}PF$5csit zd2M%YW=QTn%E62b$_WS89V;W^Py7^5_DA@2p=tggwej7)wlx+Cw%SaArHB!nx}I0k zx}}Z6LM~F0vhzow?Wrx~dU%>;87SB%p&cp4XUGA1@=hzmwQu+-KfoO+v~5RFw7LGx z*EG3iw7Q8)GTb8!LgO569Ot$J)!@h#8?u%+r?Y=*UP(kIh)d3GY^|<$-V+JNE5K;q7O_9x1W7o5Q{&vb~DZ z?X9k*wYXMTrc?7q$2>4St7Z*00hD-${06FWcGMwhJsCl+;xV29$6~aR%m@HuoTPDS3O!ZG8$Tx%pJ_lO6kX=Alv z&T<-t#k16=uro+AoPBD_$0pwA)E`QT^-Ufa{NL?sK&7^VZj%gijCJdtD?eWG?}U6W zX8Ml5;;lN$7#J+_-CiOpF*pITcsc1pjF~c&BCm5&NEx!*PCvU|_ zS%BVX`klSxVe#f$T&sY8;3U%}yGN6Ee!QB|QkvYAD6>qeumF&F#wn9&@Vs~mNIge< z)kA)&2ha55^`{M0cJ49|J7b*Ga!EZ4=MrryIXDMt_B;_nSQtQHGF0_maZPP$6nw-H zo}!`}+(3||0D9ona?OjHDe7EVmjtw6xty7u>};ndM$-q`E- zQOMT7r54C#Nm)riazBUOn{R6K6CKLq(2rU#HQ0g1Fmu#*6;SKye)I(MoDS7ovL@=~ z>1?2n3Ny!ABC?If)+8R7Ju0*6tun8chdpu!6POq|utDql(_UGn{>?r)wAJlyF0~yhNTIrlNl-g-s^F2x z?g16=`c=h-l?vI|*uiZGVnjCag-}mZf!?da-V)-bgTShH4Qv5Z61To8jJlLg zcSdjrAXE}xNg!gq3FH#y5EiMlwZJ65Y&j7y>y{ z>rQPmSGiE?Fg>eOxlG*OL^sW`Z25Un)F>3;bpm1XI`kv2y)fxFF)n8ulhYLwMYx!; zox{_SLrZcRYCD!b*o=3B^c|>M#8c)lJvgc+DPv-+$7sgUib)zrY#<;G?b@FzCCS{= z`)Y&0=s6_P2NSk8XCwGcP>Ry>;2e>hb?H(=ZK=lFJheDppwVQFTXEd7_7TcKSvWt! zdK#Jy7TH@8H*SR1Yic(Z(g^2L4l{$rD(heYBep>}139GP+Zu5?D5JJaDODIBg%jCW z+@OkR*BSorO2xOS5{dU%f=VJ;Z;ne1`HLDdiSc2BJSX1V}VlAWpgx%?+=*GG(!p7 z6&!XEOrd3Nha(jnQx_)>lhc~FXADc01dwyo^rk|0V{R<0yp6C!^PUT0sr{OGT*e7K z$2F=XjwQn50CVbVI_^U)$&nD?@#rd)tgJZ^PjNh45sY$jc&FQ3th-EuzuxquxrS32 z`FDfwNfSj1?Q|f4$GugPi3O{e9irZ1;CEBk>r`gFMfs!$?{xyN!+5NlcMv;+%|mf( z2LAvs&bxEMk@u=&qttHgXgbPbK#|7W^gXKA+qe9lX49OGDvjB?7GhYGkU9V=2(AMu zXxl6XHc`uPQfSd@*T~=09yq47yMQWr9OKrrmgeGC3&0q`^{LiRF$P>@j1DT47%5z^ zCuAe$&pl0ef9mXOG+W zN%-IKGfUN>@Q$zW+HD+bntqWz>~`0X+?l+&5(H*l$j!9l1fd;B6~KPde-6G9_;2>j zhxVTM`S7n(@khg79e66v$_;By((bMsL%xOGqccNp467Wm%6@IQR!kgj75YW+N8vAr zJ||mha_iT3ksB#vSbogP0o=Jc$Ti-#?4j`Zp)0Iw7zYG2$&bqzudU%u6yprnEW>7a z3K8X%DN%3R@=8!rX*k*q)0|};?i{>#SI~1ICILObe ze$QVWbRXI);j}*!JUQWgb4RtF?rU|k@eZ8!w;;ylOBmb&6yqa3tLu3G0A(MB`2PTq z=CHp>kZ1A~N&6^#JdL(RtZO*O%eHg_^c6XugdB00%5!v6o8{${@jUWLC(NI@8{M?l z+_tv2xM6s|B*Uo1PRm=U)8$ROTH3o?=YJ#N-`fNJ86x9A(e(cS+QY+fY8Gbr&7!f< zu5M(4bQvy>ZmjVfcgW97*O30r-w6B#@k92W(mpVJ75FuE;h%>73DK`0({(*(Pnrv_ z66$wv9%c)1`{4`=5em)*<11ioP+RosU)fv00}6F*Se$)g> zpYZ+XZLcfseQc8GuWR4(!}g*5ue<~CPvZ8a`!o2z#eOI7hk*21M1C>VY5bqYfHxDzW#QW>qg_4xH+84SZw5A^ zpq?}gIQ|^_*V)heC;T@A1=aOjlg?y}{{ZZ@D*nk|4is+w+ttVWvm)p66tfQrc&`_g z)5221MjVSz_L#W2Y0WuD*rzE={CX=TqOf83uPUXAm08ttzx_LRw?v;kl3tqb^>;rz zzApSX@Q=k`kNQvS_27>H*=c$$yF=nXiW;_^rG206@kcJ}O+BVi1cphM%#lMJBzR%A zQJ)_8+o;+2Tftu+zh>P>!MeHe$Bw*79G(%D*TeHlnuxiAD6KA%Xwagqu|TX@fs8?t z0_`XDz<$Y}4#Q=2om?DckDR0WW|6;SkB3hm?=`(&<^*o@kdQwST{tfeyhOpcDPif# zFj2Dh_HJ>UXUyLtx^5~iHuw}(?-b0d@h1;W$*Ph~xAs2EO<60eTJ7JvZ>^6|_+tgt zkHc>cSl>UHaSnxP9Iv}Mb%jAb{N}dI>T<59jPu^Ibj3|J!rM=`k(nTcV@=&ks(=sa zR$Efsp*CYZ$>zVODN~$i#Yt+Czp43^Nv~+y+U4l*sx~)Fcc=ZS;{GB26-q1hW&|AL zj+Jg4+&7qG`9ba8s?BaI*pY8!$ZX^uxu^Z1S%_}^J*p{VMb6Mh4-_5kf}0E3`IpZFt(#0CxW{{X^8;qE`~B@KTp)%{KX0Kw1<8~zB}@dJ#1#YN%n{eR+T z4Sy_@$(HB;(D;MJ7kAd4Ch?WtldP;WMez&8yWrpRIR4zcIUb(grn%^KFA(^L!qQ8q zc!~uP;q2hqo~P#B-Fe`ioYQ=Fu3E2;ym_Q*7YQ1##LpBpys^WHQI4^7#_oqFw`$7N zJVUIb+iF_xhtbg5J*;hQ5G!)u!dnNXKU(~EAD5?^;wL1V=acE(w3W|FoFd@0chl2f z$Tdhc9T!}>y}yzNwp5MQ7~6P4ag<+HC#P;}ztHt0?ctcg1`^I|0HI}0m=wT=sppQI;QP7Uq=DUpxOz{=wrE4~w73QUbpTe~!&dRKKM+e_51t$xs7W43>W+U~u3 zHiK(+86kPj#FwBdxylkdj=gAai5e7kcUN8x@e8%R)yy*L5zQf$+hO~z=On8R{Y7#g z622PvgTi{f#q7F0&F$-4tRlxvv6AV^9tiPZEZBjAZ1e}yaQ{m_c7`3 zTo#eyZw$_E<+<^yiBZ`sqLR$vz~O@qgy(=gt8c>EzJq3?O0m}+ZkGXOk;o5|oD@6} z+lu&XULO^TtIb;O%~?3KlzvGoF7|$hy@tc!=|Ounr&o39-Rq&~zq9x45v%+j_|K$x zve!?z)JC7H$8oyOR#TJ&1qU1hoHw<5r;fkiqrV)!BzW6gy^~$D(KP)~xr0s-2ik{$ zv4%c;^e3fwXTn`W!%gvjM81Pc4YFxrmOnHq$~>Y<6&|=aBavL^j&;2r_+EJ5MDVcF z>{W`{>8*0bAUnv~7>votJfB+n8dKEF>+_94V_kXL9lg813-!<<39RC1q87;Kk@Hugd^pp5 zW3Bu<(Y!O_i)&Ozzi;f>=9zcPxRzx&`>e~H6W+b(V=HC26+A^`;@;_M^xyv7w#$2Z zv&>ZSQ^b2HJ-68}&!Y7`2jXw-Y4Nw?$HQ+H*u|*HX+74Q+I`Kn(+?~AJ)Gq}T#lIq ze;n7to-@;aB6xmra}~zugO4^{D&)JYdHGiZ=GsT4eS@QXKJccSb3T`-YZnP^dwA&Z zU1b^J^d)}i&O!CA1IC}SJX+NHuZr|P6ia7vx9e@A*~2mjR@05s$bRrpGDZ(Lsl#zD zO@qdJO4U8trEA5z{{WS`^f$%jwQ5pPhs2exw|4%%XO(;&_=m1*PWHO4nP=o%$dbo$ z$IF@^ISi!oNF(#E>q_{up$~~MYu+pICaI{;eCsdTEzEJOQ-G?kKV$NZxl#Jpjrjin zLhvt#H9Ng7Yu950p(E5Z>#yBT{;G4({{TAR?fiRdZ1+~C#57EvSDez9c$Yl59S(bw zUiBQRIkj0;p(#c-R#vsT-8T2@qFs-eRWWpGT5{RlUtKS%>AuJ6KgW;SyI=T^@QTO6 zJ|5Got+cyKNZ^VaV2iVnz$fM;pPM}meC_c^_J-GV?*&cplTg=VRJedz&!)gPkq+R@ znH(L*1m}wKl=zi%Y2oQ@^}CHg-RZEz*7h?(6;9;_GJEar4Pxrj>Q}owK?eC2VxYwK zXKZD#GwY1>ua1UIjQabQKBI4$>#A?6*K2LE`JSCjWiDCJmW}Se4SSxUr~G5pwQmRu zRh7Iy7M2V;RmHqYsc!51MD%7SsOeTU{{V>AnvM3IHmC6d_E_wIXk}}HLracHjYk0I zrFnJUzp4B`xsOiO{7nSv9GP_JZFd!B9F|j@4*9I@G6;U#HleL}j!2=qybTmPGlCBU zuTjN!ERGn6%2Au;-ERDyuCM3VDpvNf<#tkiFWb#@>i+-}(dF^Y_nC94TYBB-n{j)m>*rC7l?e^1gmuSXo7<&zR?AfmM@=qt zyS2WWyKiqb&^@In$x?HA_IFG0cJl6hQTsCf(OQM=)|ud6A86V#>H1?_+-de+A(7&a z-c?>9X9SmWPDgBvbgyIhKl@quBgBcN{5SDJ+}U1gR#V>V6^8T4l>Q{{V@E0CkUP z!+T>L>);DN9(adH@dS1n+bg{Auh~TKhHa#BQP19QT-U33YvFdaFPZT-P4Q8=xwNuh zwQ1K_9zu>lMjdw!$BrusIps{Q)UoYGGQTVDE9jk-?b81M@aD0n?RBFkb$-^{S8vfD za(o{Bqr4aUKKv@xtS;Dyw1Y)(Q<%ues#510|0X134#6>s*P)Vv*k zFM)sI9Psp-UF;UxR*2eZx-(mC21#U?kTMQN2YUR_@h^|{dzo%8SHwnGri?YZWX51+ zZa^IeJ^R%y1LG9wt30}Pxu`m^Rg%CKXw|=nupK_9j%(eHYoBt-LMqEmI$N&a@D6&l z+J)tG+tGho`JbcOAN&=s_QceDMK$fdwQas!BiTNVrr8SgDT9zteocLIrvCuJQT`w4 zpA9@M;@=whO`B2FK$F>@__G_SRD5d7eRZDu0DYA1KFqZ;SDmXBu-*oSWNc z_kY0H-B76r%6#t2U2XVVq5F%X{7vu=h~D1ULh+`RcP;Iz+*-pG;(3xp-5FOoAl99= z_`7#YS&~+a>~)R96+hiLJRf6UgLm53iEs7oAH*IC@tpcrucl8K8l}9XPj3?Bk1Uge zj>D%XiuE53{?{KH{tjtcq#p~saXdG=>r3|iO8V)inn#cj&H*@A&++sgovYiW#u;3! zs>Q}m{%gx!H~b9aQoOz7z17tHdA|6c;Jq_ni^CTlCAIr}tU$DeQ0}C1cLDc*8r_cK zRwdddARH1tnXkZoH}-(>-K)prm_8?JQeSD4?$j@kI%_W_vbMYR+~dU5sT7j6_wu*l>iTpfoY~x6 zNvdhGE}?TJ!p(keVK5;$&iC8FABS4L`iF&YVTV>4gwbA3Ri!Xo?u`=|`F5V4t$4?W zJarxBhi|Rl>J!Z*cBR@ga8xcv4)gq9L(;b_^c_0xW2$&k`qIwg;uV%nEv3E^0Ub*;r+_>WoY%amDf*8ZJ~$Pt74>~2E6uG*G~N`ev$sc-xaje;JrWKW#*5q zCaYrw-PCt?knV_(lV_^VN}wpT8&!9K(UoRf@t z*W-VIzAOAf@Ybmv&9{wid^3K~?OUBb@a<*bNQCpjAav(7?_aa0?FaE{)8jRkp{x92 zj`K~5cqhBG*L*_8*5OgIVaZ>Sr06>581G-3X1*Qf7~UeDCkKr75~-}DoZD_r@_f;1 z>FJ_}dikHCdbf{!Rj=8dH(c?Z<>k3>#tWOU z%2;$b^%dj48b9Ep8fWY$;IbH736OhOT;JEp_SJOYW z2km$82jS1`@uhr5_;cXT4C^=EGPjb`MTHe(@+aCvjCdq@XD6poUyiq54}4kU9}!)6 zgX8|Ku3pat^TB5={m7f`Hp4qd9DoTpI0qO9HR@#^AJgHEB&V5YIfvFIO3o>BbMq#g zb(E!e?(DnV$2a2&d5&K^76y~1r*!3`R<^n;YqqvM5VmuT;8wJTfbabyE@Q>~7`(per_}8XspSZz258o z5-n8RY3#jPGXNlDkGeQ3@6xPXcwfMtG<#bcy&`mq*8&KHhCew0#xvWV-Rm3TKf!Mg z_>TQG4-DQ%r=`5A*A`%WtRw11KKmY(`V|g5qOozOK6ognd;7}WF7I~!Pt5$u1BLSH~GDx9# z+xI~^2DI=00N}I#0Jba5ajy7M!aFTW@>v@8#%mus)ny)R~_+QN#O5@zB=&sgKcrB&33ldi{xHM zeuT>dWNs|oLG(Q0y^I!fkIZoHt`e$}jI6caPfzn*nC9n3vW-P1t-ABt-_mF5@5A5t zEXT&ZYxZP`JXv99rg+}VQENH%AF}z7>Nccf&nchWwoW#aoQ~qYMe$$lC-Fn!hlGA0 z_K#*KgbN;@AG=+mvnopWq{({&_AY;uA=SN`GTn$8%^?;n~vm1oHvV&avT z-uJ$muByt$b?Ztplwr-Tmg{BseBS!p`jhrC{{VuF-u}eCCf2+|;Qs)Gc5`St@P=!x zCd%kWUiB0wn9{f3j{ssk51;E_sNb?@{1tchaQ&*G(X`DPEA1Cu)NYNW+D@w;#*R?A zBp!|l;1F?N&#kYDtThh`Pj5D%v14K72onLZ$wtp}*oyS8*@xrCq2XItykFs#y0^Qr zzQwxCOc^^kse$OouHn0uQ{{*wEh6yTHHxz;$ZD=KYbZo6>Q^gJx)hz z{CDy1?T7Ju;%CHd1L6mcW=&3gBFsl&Be5Basq+<182kEHS>X@bfi=6CZtUzKig|L$ z6t=*kN&WQ%_4GC4;&`^4U8zCZ+qM2rPnpvS@~FAqi2FHh$NqUv7==hF0lgF4ln zuAx1fORQh$3HvSD0#oi#2TU%u^T-(ibO1!z< zxW+zGLFfqJ8efUNJ@}#H^B$eztxYfN(pGmzNaK|57zLywlB1v;R}nvg>@6XiQ_}1q z5=|b}kzf(HAmM>-`LD_|o-D;?n2NON)ss&}6=b6HTPwfEaEv8Ve6Ch;dj5wOag_|i%!!L0dh{dEV~X+bj6Vale}uNSFj&ZSM7)I~zPd>K#ezgR zU;ySdDl!PKqIJ&#d_T}^tggI4<2_#98E(JPqqM}Iyv9ngBaZbSjXVIJBhaq(uZCL0 zEvP8Xq*}C?hE@QER$kmFD%+eQ)<1wesvOO}TVk z7QTAvspB37_&ei|6KG;a$ z-u4uGh-B11vM%1rNnJVl*$4-3B}O)rkb2f{#vcv%m*M{afwcX5UAMK07KK(L2Bqdi zvJsOX&4Nko+PHrU{8rWVN#VAQjl3wk=G%rFRQ_iNwSD~zPG_6N`C>l$>kJ;ku|ZNy8p0X%j4pdNUy81V;-Od&3BJW3`jDoVmg zj`rt{dwnW+rO#n zDx8y4dS3T^x~r|V@7L}gK;n|tPYlTgz5b1$L1QD! zl3Q zR#zB4LF&%Fp84xnkHl1yTU$HDxYPvJgh6<*?S%$8-RMtYTzA9I23<&+j)|_@*+MD^flV} zm*RJeuP<$WBx|1%d`JHP2?mg=H0y~q7il5M8IN&1gUR&uubng*bxXZk3_{Zzi3HNH zIT0TJ08#B;rFE)m(%a49sbP}o7F7m9TIPY`&ASg`Q3YRN9S9*wI^zilP=$UL- zKV8%{FAhGJvKxCnMkbP5gxYY*Mt5)rKBm7i;MSvyOpZG(UO2AVeA}??@}}>&58>Xe z+KF$`XTErDW=O*r(0%V=>0Ok#n;C~{op`BPdEWN>tTPmWr5iQex=EgH{n3}5M1`_9!eMfaDVQO0YL*T3MW9{{{5;tRhI_=eX0*vD@i zwzHHQW@b&n8~dlX<6n{*kBY85KY2a1-Td)eNX9#eBD*Te4=cL|3-yx9c`NpJyp?wci*6T_>A?1{Rp#~UN1iZAeg6QChXir;r8z;XS=#>qJ3s3G0CD=| zuYbWwego;>2Q`0*_E$}Dccoa#buNY`QE@a;fL){9KxR;K6FF=UE62ZPzuPb3_xu!Z z;0Cp!d^z#whja~Y3;As?=9o3L)Ygq5^6sEQcSEsCgPu!dSLXYCXYi$$hIJo?-XYW? zxU#sAi}>VBfJn#Q?~Vw?c<;cr_@D5%;xwKp_+9ZLEixN>w3JI7(ethvCM_45xx|Qn zx_CA0;H%Qa;c7z?xg}+1-_Yi-n@QrMPL-tk#k)Op(@vIm)p{P=@l#Xy{ovn;ULf!+ z{xj7~nr-V^%Wod7Hr7bxC1pdq1T%cVj&gCHE1`$QUlr`MyZJSr8Kvdkv1qVeN2`~( z3jpLc3t^le;_55SwJ(i-6+SflS6lx88~B+nZY})DZdol7EsE!HTqhj2BQ@wjuWq6m z&XH>b@OdOkkX!~U=yBVg)#2v2oNgX*rA6OQzow^F8jcBKCoWgpTYUC?Ha(B_NByL{ zTl+7?s9fl4{iSiLLL{En<=9)?5V%nubG5U{K9%|x`zZWlKeR`Hh27?_rJoVlGN!p_ zr#bSi*-5`AP<>U|GkQ-sf|;kjvIdvw?GyE~&iG_KXt{2y z60bp|)!(8%`isJM@;F%!IOGFbRv!-D$PS{Mz_M{{RKB_^*58x%@?}Hl5;M z2-=2-Y42#Z7xK0T?--WC?Qp|xIThL1f5E_3T4t>rJ}meSpV`u0`EPY$tgAe66Vw6* z<{q8vs&!kJDtdy(VBfH%liHTCPv6F!(0P zBnBuG8Sh^zf5Ak5;HnnivA>C7_&K!#%X8_()?HjXvL0jio)h zXP$dj*0JJkBg6KMd#-BMws6L!o=dBRKmZvhlSx9N?sR)^NGma|{G~8sg-(8N9 z_rf=l=_K12Ti1*83qWz_ENF0~&JLvIc|o43**6OQ;%%H!Nt zwx|C91#A76An}Yo7SNYc)ve{X5=E<8s;oX?8Q6CaK^@IpT}Z3&fX_MzdE^M(|Lp6*x;qa8}~vospB*k($wiPP@5P zCL#@D_^F~4>n@^A0 zeyc2z?@MhK)=QaN{hS~;KGkvdwKj`6s$y_7J=&F`uic~BVbNzs+axT+=M_9!D@J#` zW4CS%aDNNG;GiF|uf|JRF8mwu*F=rt2xhg_CHo}FyP(@95B~sOyj%VX0sjC62R{on zZw>fI_IdcDqc!~Uw7R|Ujc%=kOhPJ>4l<;MU=DB%LscUE9w{#HnKwYO(0rn)z)hT;&5C;QCi} z9k!Q!BmV$q-NQD+i3}2l!S})9p+DwFOl1kA;L%R*7_JCrkmDQy(z2gUiDqUeI6ZOB zDwo6`jXwxJ7(LIiSdsv*7*akZuDmF!fAGsOFJ?o+V#DQcvKS7M*llu}D1e1^rTAF){lJ8Rj z8P!UMjDy>7sn$)dO9ICk`D+6crk4v`Y_Y59~9q2lI3NR(k4=2)<(?Mw^cez?Q=0@B`MmRa-Q`Yn40!84O zo$O=bgLB*3q+3{!1eHM`^-;m3?Da_1Ud0)%L$Uqw&Uogfo<+Hb$qRrwb5g#YZ6;J9 z$uvHm6-uB`G0&}1v(*#tbs)DAKn~_OBk-qvuqQbrp5v+asS@AIToLm1V@VyVqlege z=m?>t)}vR9My1Wfr-=AoI^v_6;$eW%k9oz|0g9t1nKAMTC?Uo37%H0)|F zvlHYuAOZM)jXq6Q*X3}78OA}ZDfIi>NPOsW>{|ySph0>h2{Nz>dHH+OrlA`S?$<6!JjGUI3xIm& ztStMHOO8E#t1<~Bh%z4Ispp!WE9DBiut#z!^1gtRyEg3SWI(vc0~kK_MnI9=GsK6J zpT?nwP~Q0Rx}J0GRvuWVP-7}Eq?~b@g%=&l9%EKRFpy`iG0ti=bWydA53NaMbfEb- zT+`>YSqA(eVmKgino9dHenmF9k9ZFM0K_se*1k&kohq-yoqB%Dt4PNlfGMxBEqpg@ z{#=qthAcCaoO9Z~J@~tBBKVo&OIW$at1>P-{Gbo#U(er2hMx}L?f(EjU;dFJ@BBSa z>-C%dRsR5`BgFpzX5aWE6`#U?+rvWrv^;m?<<9 zjnr|!j9};MKiczo9v*2^3|iwnb>gYN?I-ZpUbpyvr+8}S>U~c{@g=o|;<^adCuq!4 z1GwdZ&jbTnpSDvWZx1Suy4OSWuI@LhfT`hDAz`X1u1r3ur?jc0uDu?Iu<)6V6Nzw7 z7fsW;sc5y!)^7ImO*{1)`y!0k_<#a)_J}%vxn9VC%fo|_&=J?^UP1dF#;;-G9l-wp z$IKj$_bb`sSkxyhb_aZ?4Aj=~62oDW(-2t>eB{?WyLk92(M(?X24u6yU)kJ6aX#}j~@@y>sS zv`WSwZ`@W_(o~&_X>qfM+3eykEKqbO8wK4eJNE+ zF5<_N-<~KXMc+~g@2;8_%s>;zH1X&62cOoKGN^D{aL-M>>UG-K2N?J5L$E$u>O$q? zmvCGFIxR_VV}F^KETH5b^n~nuu;6w-&YcXH7}$VksP9jqE%R(R-NEJOXi`rH(wzE? zkOGRw@as@AMDMso_gjy8k9=c0<37hfr4~%CeG6w;mm((ILFby53$|o9d}EMnHI@ja z$yUi7yHxSKNg(ouK3r!MSd}R~4OlM~c^S|3t5Mz~eB@({b687q>V9E@P6bStz_15# z<2#7KtlSbW+UjeYfLTajdXNa>g!0Mi0X=cvvm?6z2jt*$ikd6PTbFze!@Wa=kzCg= zmocyIR8k-N<3I0Kq$9mmd@ayJe0XM^ugX@&;g zfaDsWZWIhFasA&)Be_yGoZ){eJ(?Ff?rE&(4i}8|@69Q?j~jmPrDvOg$IG7NijD4{ zJPrq~B~6DnLqbcfoPY`Spxq3B&wzbLHAKlPDP|mcdeg(1lt<;`9ESds`4SFmmfGbO zGBew?NVkPJ1Rrv1FBczsX#jL0mwn1*amT+joKT$FJ`?`{gPv|)Z}=m3kHArn_(;4w z5B?0HujQ)0sDJo5ug(7ehdTKCbcg;%4-e%?{{X0zHT=9(HDZ%&|Izqg_QLp6q<9m> z+J2*NCDpv1G`#VCuXhITH|=t-i7w+3sT@QV3NGAaf)6ATd{XnqzACUyGR2`utRroU z91!9duyObElHBvfd%x|!;GY%i{yOkAzK5pjcY0;r*Nc}^n#7h!3GrRm-fZDm4a6xU zI5p&-6Mg|npm_H8OZZviD=Ymj@(9+_!dtQ=S$5&aJSpAD$4-^`?D1F#<2{XfiOPJ^ z)mn8*1zMucLbUBdU%jH18N{pIdKz`gH8No`c}8h<-cq zR+<*>B)a!>@x+@p9+;OT-`9mhsHZw}gJGxYHtCqIZ=(qk2{JIqIwd;E%=2 zKM2e5Dqk3QQ^cAcn;=BL&~A#!3JwYsIVarqu0vF{@#dGU+S&Ni!1ouH^2s8{dug$n zM(U&!gPuN>+~^()_<`{IL%jHbp<6_kl0ZC|q`OlJtMyxeK3`&S-n{zMCzaBKsNz(U zX(crob8Bbst4cp}OS2p&PI78mYpPmXP4?H*@m4Ya0A(K+K%XAGDXZAa9B*-`+(P!( z54lu*o=!(Rla9Xi(Rg!H@h+KlGDlGjm{e|4Fk1wki*I(uTgm-c-4k>Xn) z+Y7w+mX>;C{B>%K13EVOww3;4&0ub_^~ z#@o%2irq>E3FU~uT`^)z9SC`bktI7t8(4wwDkP7CZ-}VisVfzwWrDcKcVcJ zjkEZx;wGJCuUkylwr*sC=GoE2F%`ptF@{`q&2>7}uY$DwKHoyIjt!zkmda>{nG~B& z;rDtR{#EAM_kunx{1ern_>bfN01xYWR+kQ!FZR51X!0SEBv2HFT(1Ok^{#yW&|V$6 zn%Bejaok!x#qF)Nz2vMTf+I65Vc%~QorPPIZySbD1XM~P_^N`cWKDj+Q#gH5G` zF**herl24#9TEnO+ayPg(Fh1g4mNTKNN?oG-?#5S*s*x`JokNG*Qw24)${XG4q3wc z9BR%SoyaGfDd9C^bgyY+l;{9Oj;cPKB-~65utaSJjpH;j|LloTlLaT5eC}3UHC)0C zA0+&;rU?dUn2Y~JerD+#nQ}q1ovrbKwHhx^Ws7*BSa$QIg?|z>NEy~}%CNZU+F>Vk zo+j zTZ%Ta>m+W~(`bW;8=dhrXX3wjyb@oapJYwZcd&UTy-?rOG_8gGv}w~S(DQCOU_jBl z5;Hu7x6e@8T+tJWfOQ-t_$FFZ{Jlu;t+jn*nRla&vKA7sL!6nOUAtdYJ3Q*YuH*FK zhR~~2@j72%31Ba6b+l+(M9Rj7}76;^lL&AcV2<*nDJbV?__Zf`qu5YEn0 znWCp-<$$Qjc@p=*(>)Yx2MHJr$7}NsaoDdZ++e*(cv2z0<)@VLT<@gy#-C99AH>CG zR*J^%LckL8JXu0&rc_(8qyM$f+o$xumYcart{qa$3nra{7ulopW$1}+1O4wWv)%?>((cKtbyjN3UfM9{y^_cp1=09}BZ6Zcxb642PY=`~er)sG6R6%7@n z8br{A58-ZTTwPTeN~ra^pc{am1o98a`-v(Xyku8-I-OW}O`@7=OM^raJOIRoZLPBW znXD?fMrtC@(?Q%mY+U5l3X1Xhe9j?)CMn|Flld?w3}|XP3pE^9lL`p(DRff^7i?1G z7SRq3u#V7fo1FC?LQMw^z{EQcgrqLvOWPt<Lg-Pi= z?Qb6{uf$TfZY``$?YqN2fsUy}aFZT*!OMDB!d79QOZC3Ls}Ahhw1vmQaDNUESHD+q z=`?^Gm0Z?K+wrjy-LC)&J@DKrKi+0s&||s&cvn{EQKG~$I1x-X!^Py^rm!ezUR5&@ zm4L}8@=k;qj9%blYwyzdGpxTK~sk6H>n0VafDmcQ*uKJ5^GqmhH03^`Ca1Ls050O z`#W*i9CU>&f2CCo(g2yTl9*y(+%EzPB-qj)_@?szCxXH6N~-TUNm1KGt<49 zrd5P0qzG4$50)qy{9bco88eWL<36>L2QvZt9pO;qQo6ywBh7|euHkbFdnk?*(nMy( z>J3{Wk&B}@GSmEa`+syu-VF${`cHtcfSpv0ov5FlM8O7i#VhreuW?9{AwCP1ajY(E z3%%>bsCCd32rJ4MqswC2`j4)5AhZF*d=+2)qRIHM5tJ9kE^u~N_aSsvw9Kdnr=G5szUKcZ$?NC37$!peVhT70O7cr57+8B+m0?uy6b zuh}^3u!))MRsGzZ4K-)Q zzVsHW^+REcIa-kq*E1oJIC*1+S#({sbuIoTLL=H>o*Zy)Zo1|bUbT5 ztf$U+0WDiVdhky}X5{7Aopc%L{d>cbqgz#f;pfpw6Sd8+C=wlnS7V=T5=3oWy=Fdn z%lc4-Ab;Ke-bj1>)%biE6JBQrqIts{(d;S@jSaMs`)jplOzg2OJm4FE_bQ8O2V^D! zp7%{J&F=VRbv^vsFQRFC7yg!8HgGv=Kqv#Mp#s<^Dc(?jjZpZ&LVxFxz^!Rn zUGZC2kJW;%zFg7gT-C;WgA74znXhC$LCzCAjVd&)S~gYehd+~~%PQ8->owVSxX5qm`vE<9ruvuN82Zjz7IFcMwGl zC7Xb#3yTH9RQ-u{!wJ{gKYJ3NqukUUuU!}@&k(Ye^!|$RmJOloMAIJ2^Z6i}-CUWp z9f!Eq3|xT9nU0_qQ&!rDpEnP`JuVl+1ibwE^yR|`;j4dLuq=`H5^WR1hke_$#fs2D zjd;o&kk|2z^?`L2w$6A>JqRdI+aE=#WiEJisq^bJG{hSPyQdgFuf@E79 zjWuWoiSFDD`U7CqQ0F*y6q`s7%LshZWXu#K=mT;P?~pPu9f6{2;!NE*zow}YiPhap zSFF7kOOZ+R)X*N4;z%FFf)tm7x#4JT_CC9ahj+_tix)XXsRyvfvmwS}=9!;@%{xP& zN*H;&`mSxF3fzfDn3eI1z~W4jy0s3ltiUANclF~iQ*zRaAT~z1U+U$oca%>h&>eoI zYMD=OpH_H8;_FwbT7z)XoH|g!HhLxZTf?YMXFolZ9OfO$Oaxgj&3@ z>!5~q9lO0l<~|m+>QG|Eh>tnBs;^g%z#YGJ6uAw9yI4#Z8eEz&?+Q}_X@!XEry>H& zEfCpXMX}#n`hlyVsO}o~LCd4n{ux9x+G|&}pIvax&CgY`-aQlz0HN2z>=Gw{S^8+75DF*rc?fj)6d&Z9&sRK}HX9}KLR{D-l8lS_w*L@}P zXf>7>5{Rl1N{bWCCN%ai@OX_d`Z&=5n-WfWHx z{n3%twBK8tXn2%ubT_jS@;TcJMc52LO}Mfuk9_C$Y@SQ9oITD{?9`kh%`LiqFo{B+ zg3IkBlO^jK19rX;`eaLOSi7$D-1=)hB*alq{*SJsfWzHRyC526FezbY1Xy3v9(~Ag zK{He;-g>xhR>OP-YY5U>JNA|L}B72c^Su2YWRpm0buKH z%DoqiE@~O-L9WYaU6H{@4W5iP?lrkzmdcGp)AvImh-MM}ToGrffPbjj8L#m`%xm^& z@P!%&uU1M@>@Wy!XnB~!!WBdX7IpuI8lAKBE>G(iy$$Us&nn}BB#0R6h z8=7IpS$KZDQab1hNvp%7{GFbAE~eEy4&N8`x>qdMIyAcOAQJ)CKduo1C82_Yz6Nz? ze)*X9Zaw)>7ci8B&NjHdpJkC2w%!$%PBbxI=Fx8+HZ2o^xVbj%2Cg4(!EqGfgD?u> zT~VYd^}~YEQ`=!^KTucd{Y5#|ZiQ-Dv?${$9?%0lDo>&1$#_$<1j@W4vC})!$Ufqw3vi4`ahz9FFmve(k5h;>>mE zWge zsBOC`QMYv6&W;QBec=1F?Z0!bh}nCB;~uXki-ym4J7ItGgGErZ5a7+6o;X1N94j@t z^U2_)w7k2(xR)cu#)9U@Cx!ldfHB%sIrPigv}89Y$G zn)&@t1q(tEO*;?lv+D1n{-etwv|x^a$VC0o1bg{Zz1kl@!r>uw^mr{u1rcS%U=%Ls zY4Bz_f#C<_o(4!q!&|k$csv~6BXoyM+d)%5h`Nb}Re15f;sL4T`$B&26*qpj1Z6-lOA?Vljub z{_0HaDG^BqJC5S{b15%ZMCdRLQ;}jfjs#`7EdapRo1JcD^dTfYKc;?4WS5j+>X^H> zlp}Q>^mQb$N2B-e{eUZ$Hvww9iN8eSf@#C*Gllb-Gl)f1 zY6l)eU@Y(w$Qb%7*#fc+x&}-<UDF)ZY_aPxH$5MRmK_}9 z2QrS&OJrg+s{jU=l${BaJtYxCI%7I7{k_kj>^I!H)O3<-UdtxkzV))ee7h@PIopLR z_oiiP2IlA=mgRPArQTg_;Uc2i#hBV=M*gRs@ZXPmRq;G@iTfjKU`}VsGhOYfQBr;9Z5Qo$o-CU8 zH2}MI_&%KfjquHIp|uwFF?k0)_!EGtWAXH3;+I%PC0HSNyRy}sDDOOpaK7sAdT9ir zOAK8<13r~>;ErFe_A#0gMefuc|3_zdlyH7~%8hG;IHRX_=oQJGdHYfFwBT&*9|Ergp}n-70EIwzH~^kis6 zq1R>~MKAoV&?qSHeFa;xU9zDKec#d15B-`+Li&UYP{OwAcK&&x2Fiw(S@hn_%hMgG z`4xc1$L-;FC{Sek`k^(!`P9lRCgZ-i3wTjq0}u^N09^`GwRSEVD+a1}=q79txHlAwYHb{SzFITgWuQ z<>c~aOz=fy$3+-D6-e`@s2nLz`D04h+QflYHN!+F3%UpioQ(r?J3nP z@(sjb6qyBv-SwdAc?4aFx=TNRUp>MW68GC+aXl)yQz=0M{d4>Po-oeA_*%n^(WF>n zykjY)N8dfI!9VC^0^67$#aAZ;RRSO}2A*YtGb5S8pL$&5GKF~m4p=IGlW8&VuBcy-*Ou4UduI9jJjhj$7h@NX&Lhu@%uKTyRrv6$n`5bT!0{$usL# zMYjK5_l)g4%PkNOdSV}NdjI?$b*Okv<}6aYpPwmFDPq1jtS5wYdYdBLCB$3XFyaXi z!#=CmcqYlOq(;|+8!3+X{m4f5&CXPDs^`|3CqP@dmI2lUK*-mO4ycAlS5RfTLv+%` z3a?XNiHtEi1Ai6tXAYe+UV7EAKVY=Lo`B;FID^H4 zSBaj^>2MtsAkzt~KuW65yw`nkNivbV=)Uq{X3Vh^1zPVu{xGvY_%*cRHAy+#O1Vua zaxC-^^;_WvKZx@_2wxR4ht@J3csRCVoSSkOU>9h2oPXvD2(392uojXPH1p`ynnA5Wj}I5>PW@F}&oSF5sr5Pv61Tr3)G3cCLlZa5~7 zc@-Aos*AY`y+ei1YyIsMGOyaO ze>@1f@jN3XyVl@Mv9j+Buisdp$Vj6*m=$mzc|%Wpd=CTy$8;%;r1+e}rVc@Fho>We zQ~DE=QyA4qR`Z=2gcSCGBEKzm-Ww_+o|(_>*0*`d+b1Wk8&`|lWTu2)4v>k^kr8u>y=LabD0oj$FjBv4?S}AFhYW{5e8J}*)OaIo*A6mssI8Y*=%@m7{%CZsf0ChSJz#bJ+#g~bx3Kx#UIud zWZ}OT)4d7|)6Sgzt`Q0a*mJ;N1Rwl5X~w*>;0+Z~JnP_=xIxwuVkXp|{Oi!Xw}+a( z>0Q5J-Ga^a*l(OW{f|yg(y&CAZRz*Xcws6&u*L}bb<*ba$@xgbm4K3|LXIe#**7}ufVkfLeniZ3;Ow^fDvF|3S>b10^XdR#P^ZE>PjxDwW5j;)L8GW~4qJn1Zj zFL58H1V#oeBJ-5-n+{>oQM?f(#NZNfj4yoNaIlF@9tL%4hPi{U! zH4&H4#dAJWR9bBZBnYoKD>Rb9pCx`F`LbCn;u`VSaqPB+U^rfwSM8eDX0tNohTSlt zrWjm{{m`-eBKq&8Iv;A_Jbo~oZ49x%wNo48&m8Harj^wj(EWS&tuhmBRoL`Av%?Aj znFv^4Uze|k+x9?%nk8XtttW>i;0wm-y8Yo9VesShr^1pNTymJs{qaIf&~_-$2o-XG zInjp=?sjFgJftX9<7<;@s%n=pje*`XiV=^GLQoGG!mP=0G*lFQs!YBVewC8haYgNE zsT-Lg(lXP`rav+3(@O^CKiBga!u@BdL9}`1H(ajQ-sGLcR{X#6B?q?ToM)>3oNUB3=Zc|q!}u|-C3{ID5Cm0P-kOVx zFYQ0N3z&JugJ!Ga^17j|tgJ=vV^Sl(2~`Ty9wQ%jv>ndnPH%>(*83qJYA`tq%oyao zBFfO(tq$2CwkuwP1$7QEo zp<093nazoIHBheRAHm~(_5pD1vH_&WOkT~Y!dh5KA)*b0bF095_5jD>dapt4Qs`HL zF^RMAc&1OjHiy4%`I*A?X|E~!d_qW-B9y%-CEoMsO{I-$T$rX#yrf)&R%iKp5MJElGGa60n-?tA7+^J0G%zBhe!+NfQYjDmlYY`4zVn!27IOn^s+g7*D5I_-=&e+r->_A8-aYUv4Hw*eP+( zxLAk%K|wlYIxMUlAKeV1P(2TLUN^jx62IbCTg9)@lD&`WUD{VqU)?TF2v_f+EkJdG zg2JS}*Ea5*nM@-thfg7@lVZ;~4JavIA2V{S5}=Qwwoh!|bLh%A6M{t#dO)etfe&tv^s zKV3~z&1V!adn!V)`daifzEc@Ne+fw6~_|ydTU4CE(k+GxP6^1hwcC+ys&c0O+P<~{QA^y(JQ*~hu!YCJ>)Uzn!;9y!>Bt%cPxBXd_x|5Pul8`pR? z{(aPPCun!#JbFtpl9pmXRLR7{)tNpbn_a`+;1xlSe>ROexIR5Q;91ZltXc)&sQ05c z0=~+M&fR3$Va~+dhrHghq(QC=CNBRVI})wOd3!ldkEX!Th7|RR2bGtn{$1YVbhRJf zuK<3|lQZ8bcKIkGn0pQJ(K>abv@yYdUmcL|!lmC2-Ng5T{q_fk&aWz-2+g`%5kld> zy7%_P;BL~gY_j&Cf~pWFwnDrUL5pusdbKy^Hp;>s`0~1bx}^Jm*3G&nH1D_yPRq%B z|FVi)7<8au2A}$ER-as;c9Gp{6=VcIuB3Q03g|qa^j$W8$8o?ki7$%m_f~3wg7TvQ zAkp9V2JVZZ#%qGyXBz|dDo?(jXwqUP&k)WCg7ld@_!4zt1E6>*K@nI7an)$W^!5CM z5dHJaA5$(#AG6iE!76i=YZ`17!UFRpj&{itn6g!GZBFb4SKV;IeqSdJ&od~u>aa~y z5N47vZD>BlkKugOzIEdU>nVvCy;$=^wO>PhXQ6v2xXugg_X9gIjCs?QPV!Q%Wl#XJ z0l|!I+8CO7U#)t;psdd1tC1Gt#qu^S-B#;%wvA9|qAH%mFtnL)VhRybA5*g>nZ)Od zMJ*LD`$0;1TFpmaU#MXVdEI0(m3n?v>qI+zP%#AkK|ajd1plM<^C337SZydU zQ~Yj1PdUxoB$QroAQqTc_*wrP96V(bLmu_qBP47hs7xE?ZapqzN$P9Jtkw~C(rrjYaNt>XrEKW4=UH_8TUeT2XVmIaKSU+{-x_@fji?q z{c`v9`#zs(>~iKgo0klh7dK;0FdEsIKP#W2l7sgyrffg1k2U(G_y5YzBV*ckZ?RA; zI<*uoP_NimqUkdBqN52T&LsT7g2r&K^VLHQ=0AU!i`og$7G(!%dsNvI!G09*vdn(u zy)@uKLF9jQBI~b8?7v=Zt~@_PKI{?C{#)%gCn&KbF-Lt+Jv&{PquXW;38Q`*DlheY zGALZffr%7eng+C0K zhSZWz%R7DTfnR<{dxluQr^SHUbP=v)TaMKe2#kEz-y}pqSYf@k#@~?RQ8aG;n`ZS7 zu%vZPO=~_&h@-E%ZsiX|btV3sUsbc_r!aR7ICy)4keXr1MDN{$wmV`c{WkBSXoB+J zh+f!_p|vL8(wzDCyls`x2pOW@w{Bg3LvT=iiudHzaTIo9Zota+Bz4!D#Ld;Mw9m!a zS$!7ki3XfV#PJHon`+;P5OBksW*@xr|0LTf z4Qb=T>qRekX3JYy#ax0OEVH!HEN6Bb{$5mxq^Me6BXn%|Rr?wjN%qRQ?+P?pR=|1> zbA_w`ZJ$<-?swj9bes8Co0gk{k(+ArJkY#7P~U%dz)zWUvmrP?MVJ6CyrON(hb%Wtk6a}sV{R`so4 zjQ!Xhe|7V!T#AIzsp=0vWqp)(igG^8APg!*(?_UWpLj>lBE8$= zTN@Wfaa_pHAW7+VAA87pb_*@}vPwmFp`&tQt8J(&hN(=oBNO@EwdlU{Wf_Q;x=YKR z2fx&G)44{?H@Y5)Hu~6=fPw_@Rr!lL$i=41lkp#HgM@!sK@T^j$QpeEo=cSduQLf1 zT(-CeMbVwTZcK-AG}Bbgep=>iB0=>o>n?6Q)ciwZ<*j$LxxAx&cLI%K5^8b@U{&o2 zWI)6XhP@AG%4sTuTTr2BcT^-e72_h?P*S&z1qSbW2*wh zr1Hy4vIhuZ?~f34BGYDakm4VkRXygg;~U+&lCGSl;i9nXsPI0x!%JbMtDanCPrd`? zG$Fc>?oRWIDvG$gS2j4*!e$)Ivv$AP^TqqKjYX7u`etcP>}zd~sE6Cee`X4F7j*%THj=omU-1?&Z2+D!00V{;l*3!l_Jr^ukco5Jzn|ITADB5 z<4wEcDxl9RKtHrEUSMDSKD<}0c}30XU_g8;jjo4xHF+XEi^Wq}>Fi$BljSWq(|>eK z3N48FBq0qJTG`Y4$c|Im$h5PM?;Ip?{E;?b`W#Nfi*ZQ>Uc%&0Fa*;%iDq@%wl(p4 z?(qG;5i0*;L0q%n{Fz9PhRH|3J^B9anAr!{)!sZ)CM0H3b58JDWbnJ@urJinwwBxYXXfttnw0k@UN9n3pXP;g z4LMM1W14LScjL$gIQBJy;c-vfoS@kFjt&RW$V+uCu(~Z)Zuv zV5jmSpoD4@O)#^+<^LpeD{lmkMz5pIdrnm;^xZ<&$BM_*o|us?f3r9d+bOv7==_)2 zm7W*lGq9%z38T&IcV>GkW@oe@(qnVtc{hEMGoqgP29+%q=H5E*{Fkz;|AgNrXLfk8oL(wuwM&ZKvl^?-MRqL}$_U85eC15&+<5iYCzsL{tejvd zeKoHz*Zsb>o~`~mb_DVaJ?C0iY*t}a_;Jw7b+3nozjm9g#Cgt&Wua}MbqN_KJA@G{yo0slcTTGx zi#3**x(ctM62}dxpo3wuOqUlct=Uw)gleW*Dl#(5AEx=D{!`VHxe!Jc@6P=A5bKqp zPC;gxseZe&X>`His)NQwcrAH4-fngvY4*ijCt~U!lm4M_OhGlYzlXgO94#b|i)OsH zJnUf}?A7=NhcTt>HSS?Lb z8^-%nY#MfqS6eJ{`5`)EmL&9dJJf=m#j5FHVS_`<`4S?k;zX4Qo1N(=@#xc=RBh|b5{s@tStpR4ibj7wLl2uP0R}OpUO3(a7%}E zkP>+)!FhB}H%DE6*4g&foBmYk3xkhWLujy z5;_PiS)D#ESU`6cMCC9=OV(`1j&>;$H-2BAw$a=g9{*YB#U%2z4K0D+8;T@wZ*t4@ z*ajG>Z;EGg(Rkvq0IN{uq6CFslveKR`nta_F9w-{o>t6(seT<%-C_F6Bz`3fU=XCt zlRWkDRs`&lUzaIWdr_0^)Mo@k?N~rnd~+i}j$~mG4klhiF>$YquPq+&7lCws@%a$i zJ{j*RW@F->>!9Q&-pgNeFkC|nOKBi2_}IR$P>HQDGd|B8UO&voP$L0)cBuGO;p}1_$`=*@(NSPy? zs@-{uZYo6h;;HgqNFDfEP3UDwfP)G%ZE#1;(nmF4`BUeuEBtWp=5`dWI`c#L&4TOh zpet0&643pT7Hj?oh@1u#f9A5|7sFhw$TR5UFj4DA;vQjow`bir_S(|gNMD(A8Fs$Y z&D(e}1kX+?5bXUzEf;2^#x0!Y1fQ@M_~UQMR~ll)G{B~b3VS6{B(^)<-TsR}J?)a4 z;|*q3aI^&hgcTxrr!(>DE2dPfO%z1WHLT{dk-geBbz3t)+@tX4I7eEUXP%B!$iOm8 zJQn;Ye~Ra{X)x$r!uVGBRVp)W>-n3Kmr~NHdAaxd`V9#Op!=~&uE&;TChIaC(S#Op zRkiNPXUCs9P!fN`DzxfkiL$>$Vka|U<5yU8=CbbhglD6AbjMq6oobNt3%xOb=_wAyFP#hJi@aF(Wh$L}u zil02Eeifcq$fogf(Oe4Wcbk&~kiy0W*#Mxp*&Vk>1AtqX~I(Fs%lGkhJleDx> zixMpkbdW`jrLk0CnEToYjZNdbH=#Q6>*t`gr*A3pmlE%)F}WwUS(`L-Rp#alNRZn! zf;(!daNeLsI63njuFhx3e%$LS+;02_`VzPG6{Uu=5iP+%Ta)tTmLT5lT+?^$%^Jc} zCc1$Of#~AIlcOV=k&3)uNJy!WD4y9mUsq@U(sC2-Mb%rNmAo_tbt(ZM4sISiqNc0K z15(5&lD3EI@-`R{J}k@tXFhpJt=a}p+@9F;AKq8Z+RM{Dk!Suq>1oQ={+_|Y8l-YI z?7)<`&#KtPRd-;Q_VadXXs|Dr^=%W?=gLfY&Os{2(q1U5HWaSsSL){7=it(mqNDIP zu>a+z(02-2?m|!Y-n~#)p7Lae*La}17X<5Hb#S$9S}K8L1M`@D9Lv1m-;&od@)=MNxP#3n;L z#nVJE#`nnh?g9=wYRX~T4Oogn%;sY3s&kTG#1G|5G9pY#c8A;_ZI=~p9Mrs$37FA< zN)=6<%(zdj?6#XF@SI>q)+?G2GCP%j?G8IkuDcy~HV$y`eCKBygDxza!Yt%k?vR*ugcZ{MwJ&}YhcB)t+&r23eHlpebj}%MW zEvMOdGXs@`WjH(GCTF=d!q?YQ-2(rkySm>LP|cx(u~N{vA(=h6+YqxdMi~g@K9!l{ zbO4#$qNpPa6S7dxD5<%^O{odE1kiA}j4jMZa<<@^8vB%{dBruJ`zCoCA=L_37ej@sXIgYim!Eff{Nh=}{V~kx;K2YjN{`_BkgaBSD@bS<=^zYi{cSEvep3SjQ5BMsDMTen}Kn9N8;J)?GEz&{AYc9w@` zA!v8M3k%OYS%K<#K8NM*{!LdtGS;Y_gh{-h`cN|UPpsqM00Hw!eY7gkq~1{P$TW>T z%YShfZA1;DJsRJIC^Ru*zrddKg=xJG2-5-jvE16fo<-b#naJu0)sWP!dOL5>k;p4*o{{5$2|#cdEhT3P&-W6>)(A2n`Kn4dUMz!w%v>a#Q` z-I}aWQnwI#o2WZx{ilal&E@)-O``M7)bSZGv~Ci=M$OXeR@B>Q1fud0qw2sXR2kt8 zNT>`8oG$}-#vq=^Sc7Z+J#pPB`{DDW;gb}uZyRuhM#0v7BGi_Xs*klP6jq7~enG z`s18Xi)=e3(6q8YQ_Q1(?BTVwo`7!WEF0sRn`3-XTtWpBlz}KHn|dN&rr)PnJ+*Q8 z=jaBh!dYWRW^lc%eo*(x^A(|T*bwLXywVVTOyRnPmG!7mz|k7|Yd>_LqZ35-yco;N4-!1pHWHhIfqS-EU9WL|}58aoCWO?JSnN+h@ery#< z{scq$5*H5NZJ){seJ$$^3oe_U=JHRZRAnn4aTohgeLyuu?>o&B^o;uQ3)n-U-+}fN zh?>z=&(!pyFn{+G2;TZgs2>G~QHHwt?_y|zzp0&gAH>UPWgDw&N*0M~7r+1cYsDTd zO=~}z?iD;pd#dxkl}aemS!fb`aTW$i9NoKrE)X5p5%LQA{^F?N&TA5&`>{xwNXvtv z_szcNQWIXDH(sRBNlu7ceT>S+u(*!KPlR^^mc$Kg)C*^EWQ~xxEBZ)%%IA=cO{cu3 zZT#;&R_4yeqQTtP!~UhD6wWIFqNW0O0>z?D%21iwEtCgu4poF>TLg>Rb(QKPDALd3 zQOyz5BkZH3?grcyFsxdqCb?!)gK5<3`gytNs`rHCKWHK5QDICjqZN}Xeh#VHfw-!sObhlpC)S8!6Q2rj6S5dC zJASIfoK`EHK~`RU?0k{yMykAFW><>-0Kr3$fX0b4W3(fW@r{zEVQ7%o`rkLymG)Uc zbUU|@H2J(qY5@Cwou!WB>26~K-B0@+?ujxgqEqwQUXy9CM)~c;3kR;XJ5|H_jDam% zlbT3(DqfEw9W9lSys%L2UKTi&{j9v7B^L29{*|#VNFtNZE1_i)XfZJ%k?h|e?CAZ~ z88#GnzLS{Ed0^p3@Jvt|^!5pxzy-F1H2I79npO-!^kx)J=5R-XIx&j_*X->LSR<#V zLBKL@>#HPD?{H_{dB(p{oZlB?;6S88UI~m@FP0zp*;Z=bL4V}muZrZI`dcZoFcD$% z7j8lMEC^39(*K*I^jlE%Hcpx6ge`2pT5|x47#v*rWaZg*-0xH}!S)^R6@Ehob zvAy5SF7c|s!G!V1dw(wwD}S)Fo<61o;z)1MqNCk~%j4Nr-7LyH=W2eVW$1GFD+;c? zPKC4ZI3s>*V$GnYvh4YIH#c2Ub`r0k#P6+*(;MnzEi%;LVxZe@KX-N+kal}Xy|dE} zV0t@qVvW^ROAxqoJjgeajtA`hKZbvNwvwK;WGQY6kOH9ysxs^7X`gk{=g;VCtE4}@ zaWHxj;EyV(3Ca5D;l=aXY3U^LqFqhn=xLant1DX>Btgy;!+W-{_LzxxOWV~A^l-#M zY-02`Cga*_IkA_Hqo1EWt%AjgZ7&%{uSHES>pK^sG#_NBxMs>Le395>(FinNNKzIl zdgjMxv38;xq9+q)jRueQ;U|p~o}1Y>_F~nLbvgdHyvG8TS>Q(!HcG8x@q?(4qSHPqvJ=OCF8)SH zFrV07R~&X*3s!=~c2a~2=`^UXe)WWGwFtZciBO5;NFy>n@k?XV#H+^Cle_AVntRh^ zB=}`!Su9-DC5od0l8W?55AX^4K7qqxqL`K(hpWX7rgzKQcS2P?PUe>V8cR0voXP!d z2y(hEB&>`eRho>-j{Wry=Jpu$C7^WUlzv`9m{!WtuK2}W4!M@3;KKddL|#$EThkw3 zNIOSUlNK&Z`g?K1)$y;Z>OX9L+x_vmKaSq>(72r4qwXIt=$X3GE>DZ2MU8_i!(llvNdD?DuJhXKROd z?mItDzG(a(UDmbfgu(z$@eb7lUCVJ-3M*-~0`Mb70KHr-)&7A2DBRw$kA5~<)9+w6 zM*aK(#%GcG^4;Xu^v3v2O$C)>Qgbu@qYupX+*Qoo=;QKaI^-$$D$Sh7j ze9*Y>MB(HB-*AwQKIPIq$O;X>HB(LIxw$`m1SoqzixnSzX;o(0_Ge;>1Tj-q49-5c zsGYu?Xi=rMA6lu5k5%@5e_C>_ZkOY&-b(jekeh(CL`t&|&qR$LAc=9Ei!%3(cBmg% z-t?Bsqi)H8%qjWmPya{JSvWM=wP74zP+A0}O9ez)y1}ALKyrji2_vOzQ&L($Kw3J+ zn8X-4x20z0%M$x%? zsgIpCXU#Z@FDNyMYd#)kBCb~w1m4me{MYpk;f*nOfgYlb^RVMOHukTr!!x~3s zt2Oi95U@@#%5TeQXV?G zl(+sSGr=#2d!iYdEB$ZLdXv!gm>VBpP!E?g_*r<2@fsh8KHPW3Hm>6%o%XC6DyUgW z*m1OYXHHchW*N$IbahI4xe09ms>ty>sEf!CU4d(Jw^MS*Ic|;w>07kk7X!6 z_FK;DHituWO?5GObKDv_7Zwq9k2(2_S0%!dkvBsM-Ltt-?!tL-*9spzT@^2MIrAkA z5os0joGtW#%&7z6HBlO;AxEC1`Pt;^4z}N~Ub0uaIFf*j4?hU3?F@?-v-MR3O5G^M zdDMAa+RHo>gg8wc61uI6(&!cSo*b@~Zsq5BUDp27ycthh$b}xb4#=@576%`BUme!2 zkxRu3(`_A$MhMt!@1AKzZ~4GbC;-p6sPw7fQzg$ARijtYz8ju?6&n!v5>9hvn-{S zWL_RYni@}*x)z9gnK*g;&vfM(b0vKdPDLlN^|Cs-rE{%8f7@s)SzI<+G0ix)V{hDn z+plQ9FahG$EXDu&vVD7$w|Z_FFte8yeBqDlGB)h$$Hz%23U5+IQBdtQciKco|EEVi zCb!f<_D*Y>8}TIZva>SMgzmCL(!8+)OKNbSF`w=;1(Ec&OCK)Dow494C&8i=ZA6F_ zVrQ;An&6rg?M|U~HalxPCx>Qgn-7gfi&&}8_a6~3zFs<*sW0$B`r-xL`{-Za><_A_ zT+BGS>6emXii4JiloYv`qNVs!-j3y7sXVTfV0+E|UNEV{gk{|%Doj1x8_!=j374ZD z7nc@cUna1j)%6sn(yHM=wc~PONlhzfBsl6ad)iDZ-k5B=_|$C)_E%ALnNpv{8nA0k ztsNF15RzwD?`S~RDQm7|KGi12y<^#qyR(X-TC9%_D&NUOyZIsD^-tv(0#lVvg1iz0 z5v-F*D~(g87)Y&ZD1u7-9fEmFN%Ka)v&0|Rn^}m>=yxDv*X5zk;zn*#Y1@wGIpE@2EdXzV)Ki}t4 zvHk`i01fN9Gpy^Cc@zoabDI59GjYvS7EX6BgH`BDVBA__;FXaLqfFor=_Z+2m0I&B zX5*HjCM!&Z_l%Nuh6ty^16VLIbna8&XdUBKwtd=_&cGeLs7-EyU`NTzx>e*^UoY(s zkK;fY*N?Y1dRmRaeEzyVwazGQ2w!Q4)OPTZ!xC+a2HN>m}{Of+- zV&x9qwIilJ){_to&l*%%@!|Jj7%?wR%w5jzHO`KoFj|dDyx@(BGYnFI`&879 z#JMtehW)VGD25hl-I{8GqEek0muOw8Sm5r|`kq+PA1KODcI>*}8C2yy4sUhLaaOBa zFi@+PqWq?i(~SKd>Y{1N$$phhSw~!}k`ce>E_+YmEVh7FPW&Lt(4}c=JKygL- ztbhv_hI=W0_YDbVAAt4zx1d2kg^U$@KS!878{a;!Z=39u+Qf{{gpV#&m%6#lFV(UMGppCopDQ?2D1}lHz6)Hb`g3ffQ`cX zZ#2y;#9#M;r0vSG$BZd{SXYY<>dFr7x%sN`xPL_cj*s4ZC7kc%avgI;1;4(#6``oT z(%=S-Qv>)#_(m|xDf;y9>{wg==9=~|pEAw1Cb+^E6Y$EDQib0F66iz>>dFy77(uLa z4ol8`> zux^`&bqn;}P4|B%?f$2sgE6Z=Dg0hRwvO?yLSvILQqY7_k-TLv%{(lvx;OvVc_TNx~T17xvUZC)P9_n>{K!^|r%gO<2IO|l)0)ReLM6_E(#fi8g z(WOqa2_}B`mmD9{K3wDBoX`~tHuVbeDj!#>o%q;0BZN8VvRSmKOqMHo(bFOm0s zDBKlRr1WA1Ko^>h9w8{3uYo1#$yq=+X0q^3?fAh6Z&8+~dvMOXxV+RhsH*Z~w{vby ziy+|Uhls(F@{`wQMO7`cqc&}u{s&WCFveJaMXR!&ikGgB4WEWeTi#tRXI7**yyh?P z_j(;Vi!vE6K|gM>TwH2#47SVru|g1ZUQ`|F922b1gMx!Wd31V=-k*wVazLNnVT?a3 zf4KEB%auANG9mI&oP6$-{^V{*8n?o${`(l<0v#o0~vjwkoV%R<@`tW?LJ!_>0j7yD= zeNZzwMuLP(=g50pjOEw*)C=3r&nl?_!1=gh_0rZ{Sg36@pybGPH?4L z8sRG=lrAzGwjf4gZ~S#ziL5wlRdpl2H18%HJJOpMz&+PmjrJtX##RM*^6ERZ#xwBX zo53C9r#mZ9E7j5xRNK<@u`p@-t>n0hmoS@*R z_2`AADz}3tY^KM2p+QrXPD}(5W1_&x zyzbI9w=QI-&5^gbp440C9$=kM;f|t|7q}94l`s-#yP%}6O_OZ9U#i7E4{hFCs-F!; zQIFk{`J)ZpYpgV>KK*^wkWJ5gu9E1SJGG)8cP$0!=9z?|ze6M|Ur8$v52N>i4aYWN z*B@5czWL;6&A6W7vy7gs@g%DS$n~v?ncq?#yXM90$2pa&-<^FILrDs0* zL0-?br??4mEr;6oH0W+HE*W23j4my>MpUDKTx5#+tGd!F4#!G`5)wjDcPGB*w)*$H ztHtRnh5H6tO+uvBhgH^g$)Y>AHkg%6;eovwMgOP6w@Rje`h7anzfX=@VrIS;6zv8* zL^>79-|d-*WVc&t-;eN$3sq;ino^WaE7mqJraLI8Ul@Vdi4+>s1rPt3y_niGbicb7 z_&%bJ;2u=_`tq%-MmuN2n&b@klu=LeOCc|HU*T)i<9rIm8s(2{jas9!Vl1n6ped86Qs1}(>Aha+Qsq29S60A6UsLZ2ABl!?UT27NZYyT3 z$4e&1X=Os9$0_~$ck`R$yv(N40%z8uw8<;b*U#VH6@98t{9V0%ceM%F7HY$|F^2Az zcMtb5r!pe|MU1gqNoUYcTQ!9S+6##?`a3sUmM4|E_We8c&?bvqS^_c0FzAj{eOmp2 z`;)AUT-_`tv=|gCC}kFmqjXIpl^Z^#ti2?S~!b^{_JK*<&u=A`%X% zr$&bEPr97%23(nP6oLj+>QGT`>)PN@i;0PBSA6=nM<;zcU-VC4A6XWJbQuecRbf4W zODEf(jaT+N^d#bCc}#ci&2p$f#ESk* zA@M^|ib+fD@O0$r>fZth+6zB#=5xh&Z-+rp-ChQI_u|9K+d$UQehD8ZlcF&v zfUanLeSK>nJ7<0NYbu|0ye}kB_&WNNaF*vO$mhr7t=!a|%%%OW zc+DKE1Jf1h{O4c)tL-K2?+U#$Om=1XX{nm&KrE z^olIxa^i;uT4pvfek`|;Twl+@j&6ITmuPzLndIp>h-rS=y<8;kvvJ;WIUjr|KCdx*W?b_bmflWuLriVV(`*{@~?% zOU8^0nKCSKl_1np&Ghb|K)MydQP)4r&Yz{L20~ev*R>h7-`#&(MI|m|=Q>aGm*%6> z=#mS#wxv^DohoCSV9-#T1+^zDl`NR{8dzH1C4{O9G-1~!zu$JG0P38IfY=!0Ft$d6 zxTN0)3GtSD(KXSUvo>K0Q&gM3)4rRWtEZadF1pE!0X=0;tY#q*LjCM_Z+EUt0WzJG zG!NU8&7$i4-u@ZQYog&!+_8>b$(tcIhHDm1Uh6wJNI39Ah5F}+(%P=%gnk@K$I^)s(;C((;7LTW)#Mqty_%=231SmNaI+N*iFf`h^dSnt;GE$>Zc zi%)SnqMmkm-A(|FWVz(a)tQfqI#MVx?Y672Qc34&0d4h8Lyj9_EJrd2MW^&o3-mBbt3qRMfsd0}&+pzW^ zp`L8Ed5>I4l{7cwUt{$##n&|)ue&9*+ld%w|B#--wAjtlE3#4U>Rtoooo1P%K!^GX z^E^46Xa^_fu?s(A%BpYVi+S#y*#j9A>H@G z98z|-{2q6Ioc44+A)V*JlGbb@7KFjGc;oXPSZ*3qg>Js==Lk$ljR)~}X~86)&hC%a zvxS%H@f(*^HI<-pcyU&+>A69lGOJFlGP_WXVTUWuj7oATYW=RUo7~x;GBP>i7{9o% z+x~mVs(Kvh%7OB%Hp|grE=zTj+{W133e=TW(~v1cfCv3twZ|fC-J=Zpz+L`M24Z2IdNX9ZA`Pe zuo*0o_`P=A`M&chj&mVmsM}qD{HNWUZuhfm!6?dZS(hgS*;Jld5Hpe!k`Mub`%DPC z1UI{`;N`6T;nEJASgV0_-#^`(|dykpt zd~dVRmYypy#M?9d>>tr!BZzC$R+**;aX79SD6uuCq{f!Pz^anHEWed)oFqEq#wh#p z{z1gt_m^i3XY|1m7^@1QlTVYQ@*$fPGTGRn*L+%+*07WA(1g_d?8QY}%cu(tEkHCFMEj0m+{(@rQra1ZV6= zf`v}$gG#$29!C5lQXk+6QJ4sNi1{f>0_Dmk#4ck&zPDB(5Ef4mgCJg|hwUFxg={qM zps73Gtz+6{Q$jvh2KC=|&7{*mGee5Ai)E*rOLz6|2v4@U2@2C0Z1p(QS5!l>>U$@5uB)u&vFNn0LAXhbVNy!tfU#vzJl#ax?CFF^53 z89pS!T`Y6W;8l`NJy8NZDAFYDb&pAFZvhE5bkTQx(YV+#%1cmrSTAJTeIm%;?+~vB zW;fC({?LzzHCQrtCk8>K^7qY#$TD4&scU zNO}K1J0y@*!I58EPajXA2^C{tZ<1HppnCuWP}MFT6G6sNyDPAniR5hkofaZ~!f%Qn z?R}{Sw)kWD`svNAC%$bt!JOnBOduJof+Kyq#Gx3{nd1x2I^3N0K1UTl4-P&a`RP6( zGANM!l}N5K;J}5iniOyRWto;D*K^+Jk;aE6BkXEX{{0j+8HAFPwo=(`H|Pk*3;tlV zXJ`{}B6H2oa%|MYDMZ+n7PvW`@v6gF7O4f=47(TfvAAznUN*xU;8$E2o55lgjE)NH zOX@OJngl)I z&zszTM9%t@z$K;@FVbk0da1-{Wnp0H7(6OkjO&|dLE^eeuXMq!oYb!ML&ECCKtuJc zc7fLAKC3f!k?VDS)IPs|-);?dFyKfZII>Hzqi7k(n{vmTkj8dszgpzfkps#w%^)7C zD?1>04#!{AG$9tewuC9y{y%ewl~=97A=gDpi{vr~-+ryGzLIsvYV2hy>aGOo0(%UA zU}|?EXuu&QBr)dT!_U)@}Q~`$7FDWJ>oudK=bq)%7dOHKl6c$h{fB z;Fjktk zh4p)I805yId!0FQE^n7v9-P3mUhOSfasO~3d#;?g(otWamvc1Oc1a|J0aHi)hmO57MSX1H^ z%m4u@jY+BO_cMrL96Om8`=WSx=L-Yvpo0Df)na6BMeh979YmYi@2hc}Sd6?IFj<<@XCV%5xKgBynmgaZ#izw&UVf1r62`mu(g` z-uzIe|1?rJx_uSfE3r8TKhj;faq*+*+-zm%W3-oz(W@&{D23)sOwP5nf*jrsRNtqID}o>3 z{?Eb7&P07{u_67MrQ4FAQFT!HHEs5wV_u>EKIo$oqz}Ei)V)OcNqpzY74STIejsf+ zt~9C6d+i)HHKdJIyZLcWEi5_YzKo3)G9;#L0!9kaKl;KZM=Vw zdjLUmpYwZ*0b0VXb(?a+56@D+af9*Do{zZNRk!pN;M&vJ)on#go4C$y4gW&2XKuJ3 zY?_+>mV6XX`e`L_0^ZMwweBG znqV*MV*Ljv@=`{HVc8c=1Sx$5ff=-%5;?#@(0S5L?zaKvuR$1_KK}C@a?uvS)kabG zZnI{9?Ba_Ud9~-{^T67%^Q9PNzNyFKAYPo>E|9I7ZEm4H^_c*Dq*c3JIVb%8%t`&_ z;dnB{m@?<5`f9otCw1dGwpIT9P^mE_Tb4J?uQwiT&E(7i*;gNtjvT#p0`Iba<0!4K z8u5Q_81hT?lSYLq=;3<`H+>sh?Rz<-E1QM>nfl+Jj7oO-cMb@<{?x8s@)X)SjPGNU zUu`;KxVD3j_&71)we=d)%soTCkY%tXkYiyZ3QHu{yg%HVMkOPXefu@*zE(Hxf~=`u z24S>Us8>m{I&K*o-mV<;hSG7NOMt_$JZ%mO5hTmZ;U&y(VUhnT5&ty%2~?@AYHQP` zTmPE{Q|Ngs=jjSnqH@$aLj(H6%C&!e3oYbd$-%zIWX;`zi{i}i33BpPL;v@U@qQq; z=P+3QMyCNsje!`|esT?_UWt!=I#ZZ1${;btSu|c9Opgh3+R|*n1^m+gG}O~H8$n0A zEf12K(2wSK&X1~{K>`=7ueOo+v)zv;%Fgp6W&164vvc0+n^H@0Qq-o}X=qDCTSS!z zi(mBfUa9TD-4LhHD?#B`Lu0$@j*ob|;vT5$*b+AR(nHW70XhQDNp@}{tE_f^~4D_epud32WG3`G z13cqsbGI_DVCpxiAS#@N>4c(`2+USC$tp)Aj26Y6+uQUM=k=?jtiKJDWc@73i#>9l zOXx-{R4}GQf^2619rq&(*H~wIGGPPix{Aq>IpDx^uQj*L7VMARTy2#UBk+oVo0PQ!_ zs7KLATqQ1;k0IT6J+3eQ@~jgKFm zU07pJ(@~p$O22lBdiESR-T*K1M0@a5!G4YYypujd5{8JzD0{TUE=4vt(Eia@q8?CN z^Cm`FXq)x!V*JzZO%*ylACu?aC5BWw;%sF}9G5b$L`3gh+m1SVH@A(!rIJ1mu)j20 zE6DbC-fdCB)b^>+fB0<^WzAR@Yo2QY9CUJH`KXZ|I`@yL4d+lF>6fQBTKep)wQv$& z2K7CYEKFWmhrZmKy=oKP-RfqA?WZ3nuDB4iuz_t|x(-)==jl1>b236h8>rg7-HjIE zk}}5!xiRkNU`orKNfRtCqH5nxZ8%{5xjfi=Y6j%jy>Jh>AaJ_U7mMB(zkFHzrEo#? zQA0=9qmDkc9+TKL=?oxy8c0ZnF^0=7E&a-zxYTG<1i+WiuoN@wc^X=3$luxsw+O+{ zHG#tN?hofy2|G>WEQA5wwmnZJR@Wr!|J2O37I!Dr3UU%)IX>#ING03ou1VKaVsFhG zCy2A8?OpZch=kdw7(NEvp^=3(oGTHO z{>afY2aGOcrsqA_Q}W()!I?mnSer_Ym(lRXYkGyPJUzC)FP?hbo$)_8W3VwZm2j^wBouaUIxM!L6eNksO zK%>#IO}_Z5Z%lOSRS)7$+Yd&j%F`V(Y}4y18;h%8elRPW#kfhpH@_J_#lDq`J`VA# zZlHzzevLM$d6o_M1xiMI^WJ8av|*hUPl*NH{7{srG}~??*b5l-A=A!R%-P$1CcoY! zibL*cO0>Q@{dyPzSzn?8cgT1z2x`C?RqD)A3~EFkBJ(!p#MvX5*~7w4sn(Q;V56d6 z7FioGjvZhMoZiWmLWcLkz1dmqZ^-=!ZekCt6lPJ})7~z%_3i}C7b4Ak`%iKHG{O0sM@whEWaQs<+S$VG_z*tH##1VAvc>Cd|HRm7Eh_3$i zeVfgI_ct+we4F!cuL)&9n4SQ%@nxD(*sijhprhVR<}K-thGEyr#*g=LGJg+p#fJ42 zO?{G7b^M!S3OS7vpzc?fDz_SpAvw@$DHV(yIAbI5AJM)hUeLc}aTVPSCaD8VSEbl@ z`_a~tGmXTZ2u7=5G!t=ratnDgf}MF{1DtPu5|#qlu1vHLEQ#XqtYTEG^Ab3=Hm(r8 zY2eFqOfg9lDEvo+AUrsVfQ{-9BxFH7=wfKGsX;{k_sWax#O_kb^VHLT@QLUk^oN#n zCX)xH5c@eR?+CNA$s{*|;pA4y=GD4Ek`-mQwMxQF6(7NqPI#5;(FuOp9i{B5s_sXF z*C^^ymE|n1-xra;q>)@VIcEUbj95tzP4RN&BGOG5;lv~PM|6`YA~`vFs7&HS2T=UL z%H*UWaR0>y-n7a&vm~d$3BlS63u1s57tu&f{5@oN693Y{m+uP^Ny@fhA9!W0?KjO0 zC!!N|Eh5y0JMdmUMbVuioqy`6i68_`JyOkQ&YE>+uChk|`o}()FE@sW9B8?NU(UK; z)1$ExW8D7Us4o_hf=^|_odWvw*NPhm)n_oMcgnNH)Q0HH$>h!K9=Fy@uGp!9U~wD* z)3&A_85MU+8~k##MZZXLh{8FOv5;e&`U$3=Q~cfcz_??}t!OY1HDlY6D;Kqr7z_S{ zBH2N5H)P5Z`aw%{bA5h1$pN&m&7jz)j@>&;*+Hho!3MYqOzd((1XBzFGNm^XEL<}5JVx*a z{4QpU>9hDl$?*R|Al6(AIOnY{+g|Cb^XyE`TD;kK@`}zq>gJY(%iejf+@F zx6QgmVReYK*WzweObbWFoO6>&DFw>D+{{ww`w*pw$(#2cyK2wXyt1bv{ZDiaRTy?h zyMT{{L-@;3(^=vM!SX0^D3NnLm3^{A_Is;SRB7Uxk zNmkSUX#7~26{zg$9TLcurowppNG8&Csa;tRqg+bg+pja384RQE1jqUt-Z?-?bprO9 ze4@IWV>Pc|`*6?Bs+D^_37{*|BPa8}Q;n9ox^OQs(`PM#Ekoqebci1zNDw72IpLpz zWpLoKend?$Z@omzW5mDZl-A0##xh(DNDeu-JAXE=c*+ot6(r>Z{&I62tL3NhrQst*hhZ58{xU6Wn?;c-OGue69Tal6_%Ig?4G zGBvoF&)o5a!_VvMRg@IPjzxQ13p!-JJrPr<9&l*!&sgz*{1NLL?30?h9RSIw+*ffa zgA0a$6!gjV0<{+`m;UyuMJ_-1uh7TXB{wiGXlmhttCqB5!D13Xi)nK)d~-Zl3a#$C z9}f>oEUOe&xFt8oq)o2xT65y06hB`oK$9s;5q96TKLgI33BbUmXMzr3JwmysX&~Y5FPkf;UHb4x0A5X(-SwbxQC`4 zM*AKTi!diHVvzK$;}4B+pUbE}?N^>w(-j%FPHe^n zjafLT(@w#ZE7RXoevdTP92q^IWNRxWnm;g>YgSyN%5byz=Sp=}#Xs`5F{qCs0P4aCIASr8ox3I9%6Y;$a~&iS4XkD? z8zAJITAnokK8HNWk}K7oN1C}g9vAD4gwCC*Fo3A8MQ~wd)_;Z1_$vz=zm$k3rL6b8 z(Ab+MUWEI&&)~6NFsO)iR?%<~AA0ssGxi8;zu*Rvb#}ian`)VVL^Ud*E>dQ$?A4~+5y1CxvK++3kerV5)BY6iBd9xI90b?gRHo&#teC<5$S5aD7WZN73u{Yrsb zk_QS{CtUT)_fg!6J>r!6Z6}-G@V+D8sHaV}bY4wq2Yw^1vs2fNzh49)LrB9!5G_yo zwC%@MsIE4MEPEqIpBo-&OYgO&yUFl0H?XY7ncB0q7z=1gJ!S*i!Ak9N>umyJR$90d zd3HtK|>d*J2_F+GeF=}{G5M!O0GV~cU z%a%`XG2h+ZtAQ_);ob#aq;#uP_eE9(|LptfDzuiP-6ec;mJ1Ml9r5DTYQv4=EqNox z%ebb|CYqCGRJ87rhoT<)a`W`v>FFU6tQrtY?Ya=63f|LUz0W`$25*7yKhYUS7O~zoH>H%E(hjoDZjy&vYPvg zkRkJMe~YXbA0Qv^o_jCfs&&MgnIi8B#D4NxrM!;rFYUowMUg6%jWVA5Hzv(o_k%zG z&OK85DrvrG2V@LM9OrR@-Rq2( zD&H`2%BjkPu@{NLO}xt(TfBBxU}OPi-W~6Ph>QE9zVO(ec-{wQv?nWY))w^g6bOgH zhCr`wJCZBrD|)qul!Cm+78K4^WR$ssnQ^JEn7ubR2xdzCb_TNv36X1P4STDPOScmA zauOT<96+0j%$~M(BR(qXq#D(lDVJZcop71I-zZX5D$TxaJGb=0pZrE-_E+# zdBfB>+c4s@Q<(L(I^?5T{8K}U0a}&a=?8KQQ9*-V~IVK1*dYdoNWuxW&{MGI5UAc?lrTcWv)~kT z8WOs&@6r4FIe)u*vo4Twk5c-e-du$Lg1!3;qp-;ai1nHTGcmvTT4`|BqEAAAc%xSS zG{Jx1$og~phcewiF+fD?<@QkFcl5gDgvb#%s?-&$LSSwnygw;Y3gtcsk%YG_wN~?F zDE^uj`3z>ybVyn~xJRv<^7fb^+hEY0CFt9RQML~$he|PMe`kqE`QF^8DS2ibOTjR2 zSbt-v5-XLW)mPbp@+}-G=5?36VGH=~S%dfamp2C2Qnbd*>vsGwwIBIX-%D;TY)7`76?^B#-dAeimF#Fp9TmVtZAQNq$ShDzNE@V4kA4qIOIBz|0W8H%vpurq) zWQ^eDg10neV-sl%<1D2vj%;T?y&erEnh1o~Y>l$@;CM?AegUQ39GPun_>P8s4{ zC(+zmm+Ry94Kx|;8V@rmo7%caF}?F8yRkLq()E%n{q6sYC!`#%Pndv8mDi|KUnpIe z)5BDg*lxuo&`dpnI<;ZcRc0x5C!f|vD)>lygt;n)OX|{YIkHD`)K0c(?JMfJ7x{0E z6OhK2uVKO{9X|%J-o$|joQ1o2Qnzth3v~AW_kMTYj}Txdvm0Vxe%@ z=Xa?KIcTSy`o7JOt!vuEKKX^ESw$<(@>QC%$5?N)$QnFGPUiF947TIxQ>#cgp6GTo zcEyUV)55V!m-ZuOO~PV+9!Sa2J#6ZHD3Bwa(s5+thNHm~Qb9w`CyUpJt8A>D4d*HS zDJp$Et1n$w`O;LmdX}CN1b%g_E3>OXjxUyi{t{&;%p6%XO;n%_ek=*hfGt>Gi?pLt zJc|iKM_2|1-gixJiVZs!38P{f^B2`lFsm->1D#mO_PvlN0%e=Db)j&)`rS7Q9T$RC z*VdS{93H}Wu<&)Uzh}|gUl$+$5%F`MNMQjB0Zb6#-Xapcmd66kV6MROfVEa8y-C-z zELq29ylY3q;)0B-^Q(2qaBtFI%5aK;cbAg2he@yr{?fmNm;1q}d~k~hvQoce_W&e=2$(C4z(#M9~vE+((x8Y%DikUS3y{uBDo?$xWpf} zUu$JaZYD%J*uLAWUv^~kVh%TD8rCzI@2pHZu{sdEtN9WAU6*LL)0RZ%W}Q~ZGl ztv^hU#Gec9hd2d2Ydk%?Q8h_r`Gbx@4{)jAC~wK9F#7G8WfMNYPv`G#yVGR8GpPoz zOFAl~=Z3b9t#t)HEzgHV_-L4{>Dt|KSx0n6aFAB1-8DlJUei|fi)6_5M!YL(atx`Ti5)89+S_arz&vz}Ps-t*}L6JgQASsR`?Zm%z&DevU2!X+1& z-i>poxhm-Y6cA*Ky|kU9_#(Hs^)+Tp*7>dVKGQrM%zS4kzt36azB0ouHriF?tK?uA zaS}tb1;6omuiA@>-`DqWg7<%AhJ&fRKW(bc^4y|y*EkeRUtA&6Qd--vt?(7Qwf+IM zzW3^Z$;0c41$$bqn`T%6t(mtWcg`!q`4!*{o?g1m^?%Z=A7D81n9@sdDmsXW_J!Do zM8CIX8KkzN6zL^Djvc-FEf7Y{KI!}}aWKi4!)H3ldq`L0k*~wE(W(uDZ4HKxGd5c> z3)aTQY;hIc_pm4nZsvtqely;oNgbz=7G9Tub)1c^5{cHN<~Z1o`-?zNTa2|rE=Mt^Y;fr{(`tGqh!OeR~sb)zka0n{aHiLMmTAIum*^Y z`1`5Fn2l_kJXii4kF{e~&f#C0#){4T+(f@02SDiHi|NhkW{kXKJT*=jO+Dsu zDN4GPYIH9d5YBoy&bi9M^z3hg3y7hxWis@~L%-bzR%T1dtUN~d!Jh+A4Ona$=bOgZ zgXdxFp7hy|hZPcw87I@H?mP}ySA7MZ0wlcHehJ|h&}>@3)~o;3`QmjD)-8L?z}D0TKAS+t;w$@G$dyp zeNlX6Dwa$UNpvHwYgIhtKwFzo+!*IziqQU6$Uj)#&wO4M^CtaqVd{pPp0%O~fA!n~ zG%0mW_0c-_%Jddol{3QKt?g6F+YB=VZ4u#XrMx_H7C&MkU}BrOI^hl)Cvs$=&lAsOxi*|CET7c7qgRRkP&3Cp}hW8(zv%O0==AOCMEd z6F1k!hW1f%E{oyLhva)D=|j?1p4dMlS`zWyf;z*-bJ!_ZBX(!{wjSfv*lWIlj%{E% zG+OaK9E#If`1;I6O-+8bzRtBqNR878^~*v;@?QXKagdH6*}Zlaypg`wWX{{#L>k|4 zH&tVO-In3Unn9c2$8>Q^WJYKqD5@#C1Bh8uaqA@+2W)stx*zb|I|EK~|x z>|v%NQa92j4=yz}NdH(rHOl?D?o&!R=SQX7owaH;*GIYEn6Iu@MX#T^!{QVIwK!^> z$p0pYWPFwt#DIO|gDqQ|k|KXQJZo2hbUggFaXcpgRMTF93n)sjiFlCEd@r5HR>((Y zz~*P+$P`C=w-*a;W3^)a`vFJhjl;9f?<^U&9e&Inth>4j-OX5kqMFogSANSW&sv8? z6zn`n(L>y!5fV}M;hH~}qK=GOCDmA&sw*3Fz@q^}UsR~juxr60#gEU^1|+j3>|44L zd!bfpShjV-u(xpH;HPlD}aoWO4U%aJl}li-CZ(DWyn4qqcX5@v(ux>)QllN^ zIvOrc_RyOs< zaq{Q%=)Jd1nqIU`Sw{77?{CHc%sU+rliibNSqF@0F@-l?e~J34`=ofAtpnD33WOT} z{5I-#tRC32BJb7dJW!~mP(an{^W|0Wth9H!3S{(cm>9Ugm4r(gl76N=D)M?h8}e-v z4j1OUGE6%$Yj&3vziSyHD6UI)ZBs|9&x_D|bwG)-`A1}>FUpNJ9A6XG@CM4(-3nzI zHP#resw)M?4;g6}>H7t<{^y#SRjG^K$;`!fUFDoSuWybH7fa!(A{RXRdc7y1_j@AO z0lg1b0EFdiO1b5gm+2>))Q3*$?IiD7*bn@iCGV#LmuKV&=XI4hGo_fRo_O+S$lib2 z&b3U4iN1d|rXBEjssNGh6_7O-shm3jv0up}Q!kQ4a^oUOob=x1J-2jQ)PH@R->AF& zwV`*)QNg}KC(@wI9Fm~^^G5Sxp#5r!pFpMjhH>l#v-!hTj=r9^itnCw$W)sHqk-Nv zF3B2`FJ8WS(f*!HX|MX2FI-Zai+o}CBCs+LS_5dg5QAPd%|BzeyYCVBvBe#$hNE9Y zMNC;gJ8nxrMR~X3wBcU^ig8|@^&g+@3#A&{n%gN33BIpof5uk-#j_zTIbxO&jAk=g zl5jhWIl=)qgM_>mrRLLOos$dRP)Hz&+?G8v?OP{`YBi=lvKh76>23)UDI7}TW0Ny; z?L$AZ5i98P_%c^8Nn3ON(71|<5=}D+{^gR&6KGqk-5meZ74~@xyA-8!RBFcNZ)F*$ z?9s>Zk}wAGX8betaq{n6A${wj6ido9UC;y|o~`Tc9M71)iFzYAV^TJYD7XEJHBoiU z-HqLuR4o}j82s!x<0D^Llm)$?mPVE!(~)Gqn@H#a%I{&}p_=b?A39-if_~8MVF*;m zO2GZ~&BzL;UJgjrlJ^DQ&)RvoF^U>@(+Y)`PW`%9- z;QsywJdde1M{h+v`q|=W%PtW|jjm-~(?st;aoXai>)PlMLEAjHHjBX~oDFtx3lL_P z(x|9tj}XnborO8I(L5b7JxFK5V$=eA*h}4)72b(}Tx0WUet@3*81mz+gL(OIN9phz z-lu_$7`q1bTx8nUPYbh9Xu_+gUWSGpEH(+hx|;BG5=^0deEEUs7b;|WNZpAkioset z7T8>@;ssjB`?@bsRJ0^y^;L_X512jF7PrCky3Bg`J~$X~oGErX4%XstGE6P;y5KUPPox`gDo3mYo<#ql&g(HIFkYlo6g>D9m= z^ohS}e3Tz6r+j;!#0#GoF2q7wbw9khs~N%Kp`6j{EN6eWsvtajvZ5y_v97}-GuZUW z6X@wsV(U2r@uIW7LXM`%meb9QwvQpx2q(Nq_^kP}$m7O<)XSvO;J6^im{#egE9=~h z=YKaO$WCw`{=}Gv^IV^e>MD~k;A^Ho3QUCh?Y%z|=zsJsK1dyv`8)&f9k2!mt1#UB zzX1e3r^FL#2XMUM;lw*?uQvjJpLg9Ti+u56xIQ+mP3QT2rCgekc&pO?Z@e7OAFS4l z>FXkdz}8IVju{-w^tx1#&%Qi4l2yOaf^`k5ahiQR#`KCcY8$sf;L|LT21CbM-Y%Inzvn3ip{}Z zBSDUYHf|EWbwJD!l~hW(BqqW~Q9HzVPCd{5`EG#jWUVbX?@cZ|fZ^95%I>HCI_J+L zoR@K3tKZ7T{8J8kK%^XxrJ4nX$pob%qZ4R)S7=S5dvq(=9hq<&o4>7vr1Rci$lDB3 zit1BRzSS<58dMOx_w|1yeT73)?fW+>h_tliq#LBA6{H2E8>A#hcTKvxq(!?0K3Lg_ggmnQ)vZQ%z(dy*v06dXTyTX?M ztvw4~q?R3d@9^JEj03gFSFG7>ge({blK8#5(_HNMQ+rEiEN(pBLa7zCe4?_4vF?<| zOd@PN02t&}e)Z35j8fiFNofA&8<>y8as?;qBvqq(%4@GyNx7P#TJ(W-5vrIxdj33a zls{df6XJ=BGK_Jeeq=?s78Effu98M8pA(pmRB9Tx9441pH+!cf1X{*7 zIq92Z)g=r1>hbH$RGHLxyPQ)O_^-IJqD?%~PJ5OAKP-e+NLQCJv`wAii<#(9=*-llPT3d{vX}1le&r*Ki7Ma2rL7r@|~q-qgUfS>co7&b9S=Cd=Dr=pd?+Zmsxp>!|Ze z$S$mdCGF!+R8p;DA}jUzdR;n2GOK%V8Dx{C*LUbznUAx`jnOrg5Kpf86W~D0a)o)5 zAVqd=OjkMWg6pS3#`+bsN3F*(E5v?vt{%wetlIed90cWZ_lhcF`45W~qorc@A`3P` zYIGQO{;_gcc{KcOV6_#oS|>idLc1y_JcNXM|Bf8S(k)6#{?P_GiHuAQWwpp;iF6iu z9U>H{TYRmAT|0tgzvf7Mgr|?cWsE6ObkHdzo*&Rl7B4!hSBEul|jR7`+Hr4`F&ba1I5FC05|k9`u}d!d5}^MufGUv<0Y zwJ~v3j~+m4NRp%`&i;@(;2KZUEzXfr z&WViHw(V>AQgkG2dG9)8QkKX#y6 z?cZzD^&gf(Yq()>E+hNvs>`=Kec&VV$4Ng?gTAkDs#w zC4H^PkGsmP;O>=Cn{~;b41I+>-6W&AaxHt+39aq@tXml0JTfF*L@{~FJe{gfJ~|1J1Q(lJLg*f+G2RCrK9#5zOf-06 z%^6>TN2Imno?vp&ob5wyu-G4YV2@&mqi3l&HC38YpO>#S$IfZXGU zcBsH}c6fqC-%RCySg!*x;WBO6?t5}R{T7h0Ii+g~>paE=( zPy+Y%uOq^F&_+d4AQlV<={4)jhNMH!Wx^mx;;_d`D1wj64!L@)-i20ZEgeMF?}2PJFf8(XG*#pd=+GWG30oDe^Z ztvxpKp~#0o#T70B*ZjNNZYfwVO0DrL`Taq`yl*i=!*T_>rhTtB=X~*j>3kYI1Lmm^ zF4!*WdvLze-zoC3z;{)f=0Jjet<5Z*%kB)!mzCqy`>gzjm3B_}$du<#xnmz^oR~K9 z2In0;{g;yb5pnsQ)6Y%9F0raSEm5Sz!w2Od<5u^$p_7ZBl?8@izSVy$%vG7R1mEG5ps^qFgBd(??f`?+jEhF2 z2JanuLjsOX&R2u!!l?`pqw!7cY$g-0>p> z;~x+u(0ea@;-Qh$^dQ-fyZ{7FOO@en$LKd4`{dH+EKp$x%a2R^^1$#@b1Ij}!=FIi z_9N$0L0C%oBDY58-%mexmA@3g{&Dy8)Ao@V@&Ds@(Za>y!-6zA-Eg}{^O=y1aWL?g zGde$`X9Wu^%PZH!&;JOj+@R7|qtfHZ`U-0Bf_X zB*etNNB>Q658h9k!)g79>r7O80L!Q>X&pY@Za_mbNk3&hKgmusLfITk+>a_J8|6MDYLA#+a ziwWoXL#{L_2V5M6z=|~Q6b!ClIpx3th z)&}bl81H{gzqPh@Z8aM#2nB?J=+GXB6g^bf_ErpS@Eu9gC6IfgfgkA7z?AaHPC#L+ z`X<@vlh9c9@`-%YtH$AGbOzrDROqH~dncx0FHXDSdnw!oUrkTq`vDH?u?1;HW=P}U zF5C-=|5lBXTl;Q96t7*1$26C7liJYmrd9Je-?=Dv`)pOGGQF30od@81)72U7;RIiE&gY}r*ymGb#uC+%n6jT`s%!9tFcIbM>JB=VHpl2m!&sM5(ZueDqyc(9~z(DDqb_FgG;F0T?U?s(4_ zOo0Rp!JiEWg2K0v&)cDLG>1#nFP&sZg+94U}eF1Az2w#+ifr22ikz+y^tAZqfS z{c(zs8#R7!wvKYPT=1r{sTkW1!mEQVsHe7FA5xsFO!E95z}oaw88b5=fww^AmU)I5 zSz1v6_XZB_DfNeKKQ5FPc!MRXz|&tdJY(8>TVua%MPwNgzWu#xKj00C2DJj`T*K+;nh%q$gSoB3^wZs_$tX36K8|8k3-7=E0@=8ljI?va>-pj zzQRe%8Xc3s+D=vU1W?FrmjZ~Dk;FHkAlXD7<{62XLdG+-%A1UFh8ffHTASJ?8K?qtb5 z&TcRQjHP6)g^$bL^(H`xxXK8ffkkZpaH2qu0u(&e2)M>kDV2pc5rK0 zdAb2FmcBlA#wFzj3RBGE|H$Gyfr!uqbtr*h$%4nX-6{=?#( zOSGTAvrS2>){$t?4IkV2DPry?tuOuplW-QvrJHFQ?AXJOl2w^t7v5Wzn$3>k&4TeGVtVCY?77e_Xw*95`Fa zzji?ahBpA){ZX!9aGNO!fn(|WHiO;;{#OdlPwgbWq%A#L?Y);aoG2!B@kv>&e8gwk zfy4+Mw%hzF#;(g5HkqF@m0UXteceICkcmGfHt{-TI=AVD^*rE+=hyZ(i7yS{0C02( zwMmVRquQHQTG4vdjyX$Mzd{Ok5>omibeCEL44ysw56ewTdEZlx<}$Z`y8wVtld{A@ zVM0Ovb;0L-^S)FT!R)PR7+(fwe{lN_kP z3S@g}=szr)qflAoexkP&0MAJlufJ7{TIg%K%zVp-CPfN4Vd1k~f$@%1lEZb;xEyjj zie50yqJM$DVSE9%Wazl6?q02|DUYILGI9Cg=$yeq(c^QF<4y`+N0|rVzpJkkWsX6f21(qvX!zfe`&zgiw|L$A56n=+Hn;a^>Fhq3Jo`LlOwMY;DaQ?k$G-OWa z&(2Nx4rgX#h^YONW56)Sk_1gzf&wVc=9vQNRN9)7jmn2e>7BYE4IZ=oHVDI*b9!PP za!M);>ol{J%S$gl({W+}Xj@Lx$=PQl!BI1N@UM?XKAGUnFt^&AdxGOJYhk|5$=sTv zPv?ezKgJ!-<%4gaMd{9Up(m@@CLM9Z<~q9d3=d^9LSiQ~mhAHuep+sR>J7*99CjBh zlGF!sK=r66*^8k(^u{S8nHwvaJ3wd5GQ+qf$I`nM??Ai`|_I%&iC zj+w^wDSEwlX*;x^*Oz&*>k9LEvB+cO^C=1J@_87$Vr}{kLEfJOWUoh6U(p-J9y8L; zHeOXMG;Rx-bJ>#TFnuKsw5jlzkzA@dvLu)GyvDDFvKEXby6V14^^;xLzY(J0@Ut5C zl@4lZ+!rY8lLDk^Puk@y6;ca0hpJ9cx3_?AO?n&Pt7n~%>kf{mESxf;;9@~&GPD%(UF=!T}j$QL@D8;=&PqQJwdy*ILaEE2Lk^)k{L^-5FY zJ}>!hn(V3NQ1DN`|HHcaivs*!Oi1on08K$K;mi@3+Hnhb5!kI>@Bic?U;d)vU(7qTfeJQKt-1I+4(!n&+*FJQbgPC*=T?d-lCW`sQu}aGm;U+rz=PU~*&O1IyVdws^<^6sV`r`M$ zkmIq-t+k--{q}qF;$?_@ie3+r*Mx1R)Te5|iMSXy)tbySR~4t?sl&KFt^o4p^Vy%7JD(LS!k5)HfP zDLA9NA6lZ;3$25M&p@PxKg9lefm84rOD242rkDZbDi>qdE15uzQ5NT-P+I1&J<~i51?ETecQE;Nz{+s;(;YhY} zeSI2Uzt1nrmV$QbA@wFT1)-NP_uu#RmgYNKaeqrD03#?jok6TO;RjIlR?KgPP~Qn6gke?9#H zqA=w$RlQ31lG;S@a<5soP>yz?YNVo-iHp-uV7lQj{APlY_46M<{ufQH#_GCy-I3=| z)Vp9CzdmKwfFR_F#|=nQY4P&+X>cPOEvB7|`8s^}OgA7e62bfC5X$?oeS*ibMj5&0 zrcT7hn8QKcl}Q#-10df>D8OR<@QIE=NNjsvJd}0e%1#9=OoKlq`xoe{eew8LH*r4u ze4YRIrXGIh2Hlg62d<1`N&U0{9bbNb_m|tD=1wQ=y$J_7qIt|MbMS3TF`V1Ans&MJ zk9qoy*YTTXMy4&4Ps-xn?1cy?!d9C_Nhr)r*Xw(zn#GXkui$ZG6&gm_7sJ#BM|I8E zxI!PcnB2y|q}SRe#Gk6CrzV#c^Fr3$@c$9;AM6MxE-20!sMxe;s8DS){1E=iId|sz z?N;5vCQ)40rITZ`tfO^~qe~cq9eXdUiX9VnY&cQ*Mb_D2tUn6={i%BQTf`grWU*pv zBE6p3*U`$(8d4f`kTY6;<8KSG(OI5@Z^2w;uhB!319$i>l$qM0Cn7P@g)ojoT?`Xe zL1e#hj`J{2e^@(9+#pt8tR$MD1Mb*(G2bVMG`5UWcr5oNE3Ca-rxg(z@eJhfx$MMuUli;9 zfqP`6aJtEF$yn-$)3l58e%GwIFK%woffH1pjLMgR~5p;oukZK z4Vib}hnZ!LwVyu6|D>lpv}*Fi=b@5yBw1_v@RVDk{94;LsNHi9-rLg@Th?j(W`)pZ zbSqSuk}p%(^keg3SDex)L;SYT1CP{Hx@k!IdNls>ZE;mKllZvUe^_9X(J=pqQx%9B zCbY;mZ2V;9&!#1Y?8$AZ;p!=CZ!~!M&;MC*;AGcay`4vp^la^DPttDmU7~fB%)_q^ zLLaJ3dz@0u%H>aY@UAr1yRx-CIc{zb)Sdp@f$n9p!n(pb^Hu`Fu!=4}&wzl>V@Gm@ z#`8Ww5tT$_K$rY?;$!rYaOsP*KZ{hQenf-&K%Ead9R`N@co3R0>N~R|YvB7lqm*~Q zBE~+_(yuN{(1UGfM}FY_cBaNaw#i2MUqQqRZdyR0&GoE|lX41rj{mSMs!Ugnf7B0i zc^f6WyZ3+8n^Vt~9l}X|zA65e{r$RupGQtlLe^#upZQT!O|^!v(&HT^>mzQtp7Xy) zNZ;yt0Lz!X!QDh(kz)oip+x4yHI?-!?2czkt3_7H+p$1<{K?d#mknou{ed2B!_}T? zecxY`j@Sb!BIKmPZbEA4++LpWpKr()OX&SmAki#zNWa@$3u*P0b#A)-Y8G*%RW7FV z_d5X}Mo$pzn#6kWte>H;HHZ7SYPPG3PqEg>iQFZGf1Nwrj{Nob9jSrhX}QQ#x)1Ri zn#TIKXeAy&9~%B^2(|%^;OL5Q*pcIs$?x1qsZ@Au)`v)pqwueqQjrGwo|)f&qZdz4 zvh%cZd>IT=4nE2Pq9=A$ZLkT7uhPY*mai$NmQkal=lcpoS$o~XmBGL)osFiVih2`N z|BXZXve_DWIwW;s7@2rgeSNw4!@cHGY4=X1XWPWwOeMZw!uM9M*sa`d%lsdP-NYyK zAJ&wedp(7XRLFQuTtEQPiukI}!mf+Z$KW5y!-3k4{5cgHovnX1`1wr_%*g24OgDe{ zxX%4tfH4?*j+r-if?C(QUDGC93>PQ(65Ic1DguKS`f@Q}Ztb`G8hq=6>ts72cr4>$+(#i#d^UG4ZAVsu5(5Vz*o61Q_G+ z`9rnhiK9qIK+q^f#&N`KMZVKzzIlmc0WnU|?XSJBcjuegYqAqt%$4zdlpJPAQ2H+fek5omo2nc7ChX(X zAvRHW3u!(9Rm?f@KJxFMYPYv5m~i#Q=8MWPWv6(xjxcs(x1@heW?@tQd0CY=H^TTK z*2ic?&056SEUf5CI+XSgCTC?y<2k~7Bl{&=R8vM!)i}`~&M`0JihGmhJ~1jP>Xh&a z>>Q`3Hl4Wj*1OPK%Wcb+Dj4K%x}%sRxWLxZay1$Puuu829pWUhha?lo0utJ?*!!Yw zuca@`TP1r!fuk4^Linqn`k!Q!l(m)0-^S^lblJ8mj;ar@#g17Mna$~4GJuMsZ(xRrTK`6L`MR1mgPzIyHv0CSHI!9gOj@tRja0BKCge-ym}325RMNYv;0#}_1w9w zNJ*hj+jSWoq+cEEwi6_=xJp;`VWQJ^I~wqQn#}R^lEdDaW=WQG3A}40DicWX#Vp(Z z^RF>$yhG#W!$*!*n~KA(f+Yg7EW(ROA>sqkPVv~qhpe4w5LfZ@Hyt1E89OlH!#S*$ zcI^qjZJ5Yj=B|_I2tV*(G_s98Nvd#GyF=3xIZqpu< z?aR_Eu%nZW@a%5YE|BK(s4x!*{746{xS!*jj8>G>o#FXD7tB|A*akHh(;uhHdk;2a zjLDnM*J>?t*y~9?$BMx2*?kUqM?ss;pf%(kfqgB6oz@o4Y&{14;5e7i8^s|(vLvFR zO6ZYL<@$X<)O0)g+od!M?|5fE_j%C z#E!&C%6tzdBK=_x5LlVm!F9P`^ggRK|Ahf(1W$~`8nb;*EzK5;q&aHWRRpeHY|>$L zmDwarz{lI;_7y8ZSuO^U&)a5^{tNY>ToA;49x~-Ew#9_b4bnJ?Pcb^>fv_ zyQNuZndNNm8jHgaaCv@}eEUkE**S3Jj*G2p*PToQn)_UQ@Q<9g@<_%h9(6pF!)RLs zn8P_9DUgRvvL<(M)LUY@=1rEiIM`nSzO?7Isx+Q49jqY9SxPJL)VgW*5TmJ-WAN3V zhAsOidCc!Ez^r-Yh3f=w{<7sHHisoeEaBE$X3K~nb-z@4&`;mIC`i?6z@d6G?UW?s zAFWzCzFz+R^&O0|k;fz+d6a07d-ar&hdoK*~OYXYr>aQApuB|Az zMh@pbH8LPuVcazcUr}2uNf&uByrT&nOP0J8^@J(KpDT!nw^ZiLr(>}&C>plUU(ShV zP_F3DETy}2u)4n(Q?rMU5sKk)T})j2lKUN+(@jxyVp@Os-xT}rIn z_BI0-P;NUrR5*Q0LqE)CgF;clC0%xlTJ15w`Hl-SM~P}+PI<%w!V(-@*zxZk<#Iid6?E>>%y+)_k+eJg7JR$&jj`MeH~`2B z0-?#yj2%EWN$e{&Gh5WCD*L%dj+?79I8>^R|- zS>LSDcG=ul)YmS1KqQ~C@cowZq}{CviE+{xU_7fAV2V*gHDVi_u^-=;x&+^_GJdjm zx=0p0HkE8dld%j5KhGK36rH;~PP1(yNI9{~D*INPXRW%OHpjqo1jK}WwBDSfQh?|c9z6Z9-Y6cw`-x5Wj?V==Dc*Br3Tcjhg^b>#6;E>h-t`&Ml_p7^%rx0nAZ#!l%C2fu0(-S^u>%L z&tx)x9Si;S{!d5Yg3kx6#5}N+FbwdWgQg(3u9cN;LTOh9iy?aZ!~58eWdRYxMXa-p zZdMDx`br0w5geAsu1fxdH6w`27FZiDI884G8WR7M+ z#`5wavu-;)1?_&Gv+VVYn{4FkY3r*2z+4N8- ziyb-(zUWk&};)c4zI ztxS%B0R0a2QUj7F*GgOW&)y@l%BNcc{fdP~9t*vAI{d9Jo~gUXepROHn-N>+Cq_l% zXF8qBmzyMKa!ojD>a*yssByV;+V$jXw@vrrJ|rZX)fnhx>}k6hvhf@AY1Ph6-F=C_ zJ>Fhe!n{HYBvl*~LXFl(2|`I;Y^tPGo1iS;_eLLaGWb1X7}`*!`}v!Gw7UyA+vIb$ zDt=vs89O5{M&)q(ZDNB-OC7d|T|YM=3i#leDrBfb6fDAB1BM+E2JE(ivCn(6Y zlQ~uqTb#66vL=i*aaZ{HAeIt8JyG7h+7VbyZ%28hQ&abb-l^hnf{%T-EW4n3+)}@+ zZc#T@Gk7*$d#~F~UEK)0`DpSe_P;gSxD3N^Rtk}@fj%+B*l<7Y;i`QK1~5twd1fA? zmDV@l=gDOJy@+ABd5l$O_+C@(lv{OF3wSSl)ZZ%Gf=HjhfS{#_nTRs~kp>RxMGLyF zfO;xHCQ?d!tlf&)kP8a~`=O5YANsJk0*Y!iK@VMx$wCPwcS|Q?E?K+EUd98NU`jLL zynMq1wIvJ=)Nr&ByyR7iib9I-YzH8Q;zZ%apCbAoExC@=^{MR%gKaSML5H zfkZj{P!Sa9k*q%wKqewb;aKFX{7~}UDb0MShn{kahUN8ch+^<0vTbZI6hjyeuk|M% zkYg-u>p*N4B33VS5&T)TQO-QQmJp-Ua!w2=GEK<$Qa<2mfX8MPDgl%1ijaE5Z$jJ; z$zgNMp18@rMv3c2jD<4(DWsy2j*hEqWRDka6E~arx=l?o&McVHIE0XW-e<0f@59LM z?`WN&tr?#vu@(WMy#4&}kO|iq3lLDF(Wm*NcDDLfGN--U&d>Fxd8_mWIDZXyzQ&jm zMWe;*Hc)=Ua&5Rn>U8%6)qIV04QNMu`nC%eO8*VocludjI9jh04fQcfjwBQ=vd64A z0z}gz04=e9kqz&Ws~_@Zl2;)d6?%sqE_~uF2hE8D*$YH(;x6^^px>i_JDlDRjDs+p z1u(%h*|HU>dUtoT-x%xN66Q5x^*u#@YJV7b`NOfxC(l%) zLVhtyWKtvFa5Bx>Xe!=QdNK8!NE0`vLrZMDGQO0%)eg?3lUSohOjfO9aUDxW z_Y{6wiF{oiKI!qwP={aJFHtu`Lu|8~?S~=oa<225AeKKO8Qz%bm z@FWF0T*`1T^f_t+171o&&jv|i0oYd%lA$sb_4X)OF8Nei>ZfA)s#12u$xSstaAs~8 zDw66tr5pA)%2%!#0vAW{>@21D^pl7FvEWS~g0>I$TFhz$a?HB2wIOzeIUgtfoC;7# zQ#IEKDFtUR$FX5?r8`OFROjpnC%ZWesI&;8G3Eg6;I>8~D8&uCZtLZozS)IHZh2tL|JL4MM7yUSWs>5)H%kl z^1Gn&-l-IpZboVc?)>hOS|1&%5addGF>;sAgJoga`cp2IvfG)sovby@Au+OHS#7HJ zHpMa!(%p*Oz20vjfza8B_cLp|Z z9*tS@Jq^AJfLt5{cQy%rS)&HXqC$%y-dVLdQOu8)HUIL$B@|dQ^SvN2Zw_2^dj6jN z4p_De`vz2|q}*(j&5nR`m7BuC?b{{Xf_0qw$)9J~X0L{JS^-}_n|d>n#(^SnI`nqb zHHbHWmjI0F^*7p9{w;1|`M17mYz&>ID04k;aDyeuV}^9`dp(N4@#d{LPCE-zZq#ZT=%Z9l{w(GUgU@jgb)(Qso3Dh2}jJY*bEmtHr9{dwN8{nMDk2Qk>l0}*Cl!M5rrICf!oa9}$rO4Rawn7-s`j3MbF{S9g@(nfX<`X{2|ed~`LK#V4)57Mv2JNz~Gq zg>X7vzQ*!=zy0WQZ+gdRg8^OvXbg+tmIafO0;RCo>^Dhye2N)&?lQhD*vk{X;?aB& zFBYTa_RY}LORU(ok+(UxV^Yv84W)0&U@Bj{HwVenRB*!vNyddxAZxYYDxmFl3>r+) z!!ZVayaJ$cFy!6oSR0~#Px~bv1!l1)-p9o~;Q4WnFGDT{-&5U9{WuUc!bg5oC!|({ zc)KnD*fapi?T@j`0AqH~@GXok0)WK3z)(^*OaH_A+J7NPW8(oX5|UZbz+rjI2D=Rm zo-{>ln*BbKzjZQ|+D)Jj)Nf8)o@6duCVTl}Y&P$X%f_Vtx(tLc#jwF69+|}C9>f3t zl_Xr{4t{{PY|xI_9wof>uOMbZ@$i&LpOx3*f(|!fSlHq#MxoNlS!o~}Kj7q~2Wd)D z8IVS{lQQp)8|GuVnuck+Fuh(ny)@gHmWvq(LH+oJWb)zaKdinO)Z7Q{=yiPid0<1# zdkpzS^p)iQvGD~GGw$idFovFh_C4$3kHNVwE_pczZRqAOGyOS;eYLU5d3($nslZe;)obG2 z1*Lfl-Geu4WTd|%qlo@#V!GVnS{PM|NXjDt2%RQ}#1lMRJJ!Zb(JgPkrJB-+E4$VV zb4lrEg0xMu>+Lfh^5f7gV{+Jw5plF|jZB|R(| zLN~zOE)q~W~xtP%+ zT*=H@pK||j2beWkNK=aNdh)NwECd8ifvbwjZwOtrZ0QcBnRfw#?+GAePMCy_hGVw? zLf;zJ*0hYQ3+#mG0eoFwVk|kxqfnI|q`A9eLuX86KO^3m#Ctc-X}tER6%3h@exKAr z$Z*m-aSsnw#2iZrU^!odF{={$dbCMh0t*>k!u?Cr#}HHGVnKVZn(tI&x}DO4lu4A);^sE<6tXmJnaRcbk1 zbxfV3#XRt&qbXSxXqCib&s(D2q}}}_t80rE&GOJD=K_jleEz53p)pF992K+;c!4Tf z8qVu&OM+1?x`HRWbr@Hdp8;Qlv*nWs+h&tS{UY@E8yxshEaxDH9XgMp4YV1aj|j#fTe|-w=Uhw}pB0ig<_~8cni3 z29}z0jh6H4i*w#nN=^uu)o|AIv^bd+%+F<-Xv*bj4_jq9xlwXXwDVr+B;S~|7h>r6 zUdG+4$ZmtVuMaVOqE2a{e;-*l8^bv&_Df8r8&0i#iWS3oE$<$LM(!+$0as3 zTA#8$dv!mY9%%Co^cfU^i7J(S`Z)ebhSEYRXtlI1dB-+nKN1j}wGKy}mZ$lSgT;dr zD^HU>r3^EQki2hJzDQOuid0+MNsGAAX4Yz|>NZr0zExDtGYqfhWUIX?+&!C492=!@ zR>K#}_g+j-Tr+Fg4<2Xps}xpWG6)_+a*Rllphf(WMch!FxNrib!cKkF(o?i_{~u`u zpC2fXOV6~}l}5fMnu6b&NcW?G4#lkPG0fW0K+Ithlll=nFxhIaDhyxb)8Egi z1ds>A6}&C)=cJd+pw!cJjXn0^FW659VqHF8%Tc4P?%j@V?zSSl<(Bh*$#NlXI~-ak z>)oI#QEp7HvCe-j3I*6_oQH2nCL;Im0BG_YC@2zh!i%vWL_m_fGf`=X{*iN3>ENtp zx>zoD`n2$P`IEv);O@*H9+ECfBYtXNpxy;n|U|NKzixID*#jb`z56|rLSHahEc3HIAuLZYXM&TEPcNe!U zRw64$0BwMidqm6j(kP$qU|SI<7)js9cK@tltx%T7*?O3T>$#mH`J}6us|`fW4Ws0U zwrsbA7u%Gf=}~8h{6RUQo|jzufBQ8AC4p>z*8~RQ4w!Jn@m_V>VuF}~L6koRnq?}duz&q3Na}I-8Y7}# zB8eKds@cz~S{Rkj7SB_it!6ZQUngzPxmj=km*~{rV=Z7oHyMwSYZj`glfRv)NiyyC zRa;L<6Klxd?`?NF^i>}V_CoxJHCo&R!1NSf4t6TXiW!a{0jLBRSFk9wm;`k^d~t;Q z86|iJp(+P_vztx)siD@+dAA*1q|K`YbX9ooBYN9E#<+%2ur&F>*vNR!25*NR3s z-*z!MTJ@nw+6!?pU}3x{3VCrvMQfbr#uxgRue&V3g`w1o*xlow^Fav7b^Jqfzin~2 ziQ?91<8p>(!ayhi;;NOAX#vfKB=RAcboOV_OWy<1Zp!?tsXL*P|Me`&Cd~3v@Fxt1 z(|(&z^K6Gbx55JB&}@CDk=^3{XHAQ9SD7`w?h|8vc$;tmy>*`>sva{Zlz#Zp$8fau ztPy127-LC%g4VWT&#yrZ2g~OeXJ~=5)YOnBd}Xq>`KkoDSUT^SzOGv0Ok{IBqi2+S z>@WT8;J*z&$k&FFub;9IQ zs()4Py0S>_slVj_s3p0{Dg>4iZ}1((Z^em90a>$athLMt+q z?QZ7Ye^K{k!LBH6Bo9p3pZq!)Ns{~>HLz3r0daL5`6ncA^iImM>6W@em12Z?syUZJ z?sv0C2dA4P;%2y!d=;&Lz*&siE``}4$j7$jq9y5^2yhxIp+@j;PF;2F*l$(%(#6{h z*E-EKzl&W`UJJm7PfU zdI5z{u(N|_C11$eAp>7&1}1S$8neA0j3%kS?_NT)+_SKkLbYBHDG9`O(HC*Y^Ca=} zeECh4vX|8({IyxB&Uzx%s07;g6@>$B#POd~h`<#4U~QSbocO0se;PA7fpbl5`u?S{oXmHB1cl+ewKmBJ4$iMkIWwmUuakL1Xqi7hyyRc+%lZMp1@S z_~iSzu8z`N_c9n_xWYrZ>=TE6T$_kTvJE@eRyTTZEBrq!^86Pkd8+)bOhhr~2c(gn zvRZG$k`83INOpt86QM4YgoHbqm2C+rE%`NN)sN?8P8OxRk@nQIx@s3m`8rw!#{*XG zqF%Hw%jWGMbe?_AE0t^lK_80JLJYc2LspnaI+?_CgmGymnz&_}gXVRWy(o?lW-61d z1#Z6qX8KiEPv~MB_^8JrXV^}DfbXKErQ*>C-zFAsQk&>22GVgq8s1>cQ~P2r6U*8h z2J%kNv^~GzpCq)qiltQcFaBsjEar0Ed)1{)!M?kalzUVEbi)wcJX-s zHAF^3Y0d#L-gkX!c;I>4Sy>1gdQq-#U=jK_S8(&RS~t-mQ4?4nyB$Kdzbzzop!@Y% z6Qs>)8$5gR+qeDO8*6`;WJQA(Bt1abWEEY9(n&pcVlXu-v_7A%pKp<4vQFTj)KKJ8 zF`Em-WACrb4V@;A3F$`JpC|wE`jb~g7o>7M@psA=tL?@n>blOje6b{HbyIc2R)SM$ zqY}+MQ^;ORp~er!!q3=urLn9OO$Sb9<()XHTxS5ih!xzE{aIE^^1b&;o0veoonJ>~ zWSnJ>VA`3Z2qy?l(j@cHtKC5Q>l%}`_BzGA|Hsr>1~m2lVILJF6zMLNl9q0e?rsK* zuF>67kS^&4=^V|FmX>aiF6kUOVC?to|2%J=mwU6FopYZ%zTfNmT+FM?9#rg>P;F1- zjCfVvti|Xa2d+R<ab3p}vk081e0ibKT_7ttVPci>9_vB(y=wsYGVTJ?uWymmdckg2Cc3 zdWA}S?vwQXN6wFjKX%_vAO0Rm;<%(i>Hb-IGQpr)sY3$AeYo(x2u1Y?g)2=t_A{&m zvR=BFY74AQOrZ_O9UK`60anvww{AYTo z$a!4;3y)In6HFuRmVk6sSD!6!FcWpEzFq7Z?&pfuPbWMU%(jKon~aaqu77=+HHN<# z;%)zMc{Vws>+j!c^=r3ck_{kNU8c4&eUQ{Cb;olJq({9WlVH}$K<^5^kM@XtuP6A2 z7ZQ;)7+&!|{R#ybHfWMRZ2%dKw<++lp4WVly2}#Oez_gTc-w#h&5{f24+b=fQ^wKE z!bZGm7S#&kI}CWPN(t-1)m~1`+25N_*~saUWUrJ=*?!43Nxrbu|c^ z(p1Fa`&ug@iKarGal)HGD!;gRTSm6H!VJ+KoKodCWhIlvvG=NgoQ1ipBxgghJo@+4 zwD4OJmzm~&^;F5ETx0boH^YvNBNjfr9~GSg;-E&|p;?S2_&mc7Zdz@OKbtoW5<^%? zgcSg(<>%E!4FJF}Acy>ex9rde&5y8K2#tuE#OOS>*Mbe#HEaFuQg#_EibVU-CyTBo z?q&Mp3!PzmE_ysCrtb&$ek~mPcoqiaE#mSkF6-E}<(F#&f2PoN_P)MhJHUiuSWT7t z`D%6qMojh9`Z z&*f@iSvqSI9doz#=W^?{4IWlkcd^zcR@WC=tAjzz;oye;BEiWEU#J|7XW|inlmpCH z_F_9{9&a=Q6B8!Rs&NW28v-^1R-H%QHw_m|mTI03oOpdY5xJps8vA#2pYqVj9S%z@ z<&}q(=!`w*l^c=LGwHa0xE8PHp0E#2e>7mMA?bE%suwjlI#F+_Zx2TtS+xErXg{Jr zryI*w&okhl$tVWW9r5^+B8@waURfm@TLDI$VEXT&&gLj^IE6Jqxt^T>r zkBh-g3Z;LvZf!Gv7`A5oDk!Whsh^{lDTj>lg@wO+*ge_!Ved0z9GQJXAqXb4yD8hX za|>5vADri>dA);`Wc2SxZX5ReW0K)xZCeEZ0L`#WSL#G0YY6FS-I#eA>|9njn3m2j zstt48>mtok?=D@UoJPV_zf7)nPQD9v_@tYEIHMbyUQX}2HFmP^@2x?rF!d=tDO?Jk zZ(L_$*olW5--5Dt1wUl7L&aac8)~PKlnVWewci=OtCQS_@eus0nyYw?mk2~iVrDR+ z93Eay;uXny6+pfsW^4m3;dF)68L#_aIq$73-&n3o;*{6HQ-<2Z2Y2jOsEvc8vdr)k z>OKJF?T*=ef(1?T$kn{kWAi=9HxpGCZA-VKzdJ(d&v;O!FM!)Xg+%I}v_RuDz4l6% zY9N>zo{lB$7sCc-d6g5rsFo1Z8TX<;gv3<^mJ!b1a990i^~zKV$it*Lb`rMI#7`H| zzF)tytJRTWDWEEl)DPB3hIgk&bDn+rtYnCb*{H-$Ims1 zMtK{|l_>GCk?-e`^ICeK2s%A$)|L>njbTqW7pG>!vk%x$9Yz3moxd*PpzmQ)@OXDm zYLWadj81K$RA}t0Z&!&BhYdd&vkoXJWkR+1xJASfo;DcoK))@w&eF6^Sh!gP5a?{W zqxf^yQn5gE0nJ>JUI#_D8e&YJ_h0?YO@Rf5xJ|=#h8{_pWVYj-of0%SICroQ-PEj) zx?jV~No&1D>!LFn#}7PR3-+eUHZmcM93(}n;IC<@T-Gtz{LN}&%ce1#@t{2e{f7ej zB%_pm$W(~Ls)JLM6`NSS#&7Y4LGz&kBCn@to18(|`lm1GTrP!V)CUQk`l|*o<{t*y zi5mLMv`)S`FQ%X(b7|R4zj8TRvI6e=V%-T_?kX1Hy+Uow^qVmqHYI>>cZ@0HnEUa? zuM&qzVlDhDS9_63VCuwN428GB-=>V0=J=}*Gi-}LOk%6@)X`=VOXqSG=2ofEOF0?* zG4!UQ@^MqtJn5xr#L#zUdIP9yH9qvIGT9ozsQMSe+@dqz=}Y{MAx9%9G0pS+2yG`$ zq{_niNM3E-^g|pavA;ZVK%jnNS_g;H^A3PA!$7)^{%Y{4&poO~osC~@;n3*ff?W^u zu_;$r;ursGROP>eeaeAbQz~xIO(0z&EvL-*xNom)k%Q6%vD)~v0(rsDbIMOz>~$+5 zWCc)(bTC$lwo=WarAvKvl-*#Pe&3L)uT~>c%Tqkt;LQ0G+HS-YVQsL>vrdXm=d$2% z>rA;Qc*Ow?7c&3I47;yt4tTTVVZ;aL*ARI_g#u{oQtv++UNf9s8y*2 z81e2kzmTL}#D~R~;4I#SP<1DZ{jer|^31=r9YeX@0G@VWwghx0@n4vWrK}zX$GW@x zg>I%GAc(sxocus+&G}2g*ZnGxvvawj0?~@BvC&n-M+%J{4dot?d-Vvf_+kn>OY1*f zm$0L?xsT0;xxuyPdzjhed`U3)3z;z4lZHHp>gYA6z$lJgNugk|sskvwZX<|j)wpBI z(I#0PJxOJ1F69cQ4N4bDo0r~M(#oq>GW`4Chq70lnfyVW@}pZ67eDhIn&x#53rx|! z^&0u}I)lG+;%JD=*V1>|cUlxexC1QMZIlFLT{f0fQ? zcP$=bz6jLOtQJ-;ws(aOG&!KQrmM`hCH5d?%C#AAmw}IM7sHD zb&axrrW3oy&ttDZ<#Mc@jo<)y{8sSkQCHdX`)x$He40qo{{AXVT*{d*Ubl1|m7pbu zKb1aFzk7M6-n`h!;c`M#6F&YmT6*kW@%FBcBmJBTP)VgZe>29_Oap1s@u)^lVo)N( zN=M8mN>Bb6&8KGpd|d{1V-^hdeHFn5-RFO94ml9am5S{VNzTL>_-#sz>!Cfn+Ggvk ztFqw6Q5iO(eX69~Z-=7Kpgu8Sx=1n1S0KvP!POG~O}B^*?ly(?xczp?H%Hy?;jwnX zyLh;M-9l$)O7V5x&ogXxNdGzt@)IL=UCTPgI%}PN(Zieb^8i$@MJJNXm%Cy`!Z4FC zxQ8-y7Vw~V>eWR)@HY3)`D+Ca(Py?v=NpLf%I{;5#6-(Q%V@U`GetTLo|D2;s5t2| zHBj$kT+R(0vFf=FuZ$lp_TP8EiIDC(x1&7C1}%fs>}jayM9CG+#jhJIKg-{W_SM+K zv!7{0wb}1PN3Z7u@=nvy5K&$ zoqB);$^{yOf+Qq}EKTccwTxm9uSkb_;F#wIgAV>fgEI|B=AYb&&6R^H4Z(d5JFS}W zp^`g4dxS{NuMU?)Qt5tW->rw)%&I4@Qq$f()Nl_% z(b+{H_0pNv&Kr_L(mZw^N8S^zV*eZIf6ziqB<|^XCE_^ZxkaUCKu>7J8o0H8`c8^u zyENEI#ES{^*moogWF}AZeC^Q=G+?Jb8(%z7`?+6~1;a#;knLg)yM9lXz{$DuS@;Qs zVMcX-K5FM~?SUG7Xm8C`+8_SJfM56`s1i5xH^n4gPRR5X@^3+dV5Bn&3vs4$*DxRQ z9v-N%M_A4zW2>-b%Xi*5&P zzO8{=h^h9_-bBuD*!{ZN`IV*ZW6c#-&nKW*qLy2gR+!K5 zU2>ruNOYT}tLy)qTwT3i(4CwpNMH8Qc4K^1{P5DN__m@=o_1os`i#2Gb!_sYdI_`) zalMkCI$L;hlG&n*rA2?v4HABmfd_6|n{m#%ctZkFM7_7db2so%c6a0G|IoZ#Y?Jj? ztlt(|1y$~}B`%NM4Y}eW7m?)81vho8byD#A?J4S`r9|3dDnqctAsO_zhE${eqwL~U z<$ISez4M51a*R_+5b!HfoN&~DP7*2AwkO%@8YnceGpb#igCI}f?YAeNSwC^mf*sm zu9cX~PIk-S?IhCfSXlR5x1UvGYCiW@=%oSOEj`zu&>}MZ0-Mdj7vF+-x9VcD({oDT zVRAF<=lr29mv6%r|**}eD$uo3r_!Y`7k@s zPtZ6_w$VWh=*tNcdXl@*mTJSPz<*EZZRt}_a$HdF`mNJ>%anO1E=1rm82RI|U%OtNQ0tr~TkEVnjzu3unQbqtS*EhVE3dh^#*teE4UHH7xlt>nuo^n152Q_JP?oBin{)p4tW7IZ4faFW}7^{YFO0u?O`WsdBz3#QvCk3@3A$p-5WwGrMhE7Sv~rMrwj zhmgRW<@NIg074~x*}^L2lZ5~mDd9SA)a>LC!~)N_jNDQ~M($3%*@Szlb;6vVFTxP? zRc7q>l<$%lN+D;|m8>}iCCWxt+v1J-Tg>=)Q^sqUf*Sy5_`aF`61b9Yr|A z1~amg&KUyW?^;{2X{nawV)%yi3mUObN;6?|9@;~UPB(&u++iCrQ`8)r)W&EsuGY8f21O;ZKt!<_`KB zqg^hfRe9QGCEr}BjuS6wgK{@nj z+~pUwzhpASK9Mukb3zq|<{IhS25!S6;k+N#<0G_2Q0%?_>912%++)>2GzBA4j zy*u4$@P*xrmk2(%LzQ30j(%ETU|}zQqE<`&^!|KFvY@E@ojt*Z*3<`GSc)l7qL~g7 zRAw{KFdPgQy z;_`B0vlpw!zUKuB8r{p=Nd+V84+x)%p{EtPy!MmPpB3Y0O3;`8mG8$@lz@@ro6)jE z>n=p+CLoUa8;JiNHE04urZ-7%3gYwM!Av%Y;Nd$hFs?9!#&!>mnInuld4sR5Igh<4 zSKk*)9lB)=_ov8RDn@PSd@qvyQorCupsUHbCakHUFm#hbH<m)HlzP+6zdG0Geabek#lrdP9dB|LBcTRaI5aN z{13%`b?O4G4gT7Ef!Vx&^jF(7+@$KH5%$#HuH#Egp7|C{ZS&P);#DJBTFE*0R?69B zOB^*1XB4IgfyS@TJm*CYLz32Gd%gLm|kiP}k2QafN zPoG%_pJ_kRd%)+>Dv!U-5%RI2^6D=pc~?5??v&wFTVNXGoH1~?Ck%|zL5RYwwi4d~ z|N5y@J(Dy))!_pj2?sd_PXLPO4a4M7tQ6914vY_9&{~)6^5Y*om0yc46@T4jm)HBD z!P#GxI~VtJt!iX0CT~9XQ$@v)3&-H7c6x#u!wM)sxa810kKEI=tR=-=>B9an1*Ecm ze$!n(4s6ZO+5w_6kb3Y?7_lC_qt;v$^+vdl9P7hy5}+%wTy{Z!LMa4m@neK6J-mRxa_g=20{y#O3_<6X|D zBU#{y6#$2qSS_(7snCLQ|9nHDZ>&_&0^R?(WwF>;NOr)aEx0OuC?UYy6TKPYGfeIv zAt)I3QW(KjVd!V+4{`m_aTSz<+KNQ?#kh~y2?4WK^>$5Ro@jJF6(CT zK1x8@iDxX3v2%PlWPNVL9b`wWe!?xfuF3K&=U7`>tK4VTmdrFX_jD!CmQFcYM*PA* z?wzDp&$bLmB!{|XKiHEH1v3Kc&pc|oseT&b>9gca?oRD_aCp2C(;L}UXZt(fuKcKg zl0XU0o5K5POk)MNJBNzQsF7AXaV@vz2I|`QmYr z(6RLp4Ujafu?CUntVMUm&)IW2xVP%!f@hmy9(^AOp6_|E=OSs8ZzyvR9g6@>}ft&z6(As z-D{1Ql>_yXdkzE^y^_>`)nvq*2DSFm^h&8T2(LwXa9_)oCn`tY6mP};ht{_9zprfm z1#d{^I9&L@UrCjICt}hHwhC48xZ)lf(pf#xx1FJf%GpHBBTCK#!+U94hK=E#vQ4PO zp4u!Nm1N%Jq#VdL-3?A>YUQ9l{#`6- zvs)H?wCV6`&f9jjR<7+wp>vV8%qNQNNQ#P@iSo>b;qtQ2A;;fVD=TfSMeSj?TVIg)8q2I?E{3Ep3bdxRT{8)5o z%LCNZYzGWs8`T;Xv{TKUB`)*~tl^mDss`7tiDtwH2+!DlTVXN(V`+(rK3I&Fy7vBl zYI>_rOF>{ky5F}xqjZ0>bT0I^k_@)^othNa?6cb1MHU6>ZRQU=AQ z&KB6j9bYuDmP3o}x1OfGI)YdA&()>Ssxidk?8@Y1Lb9y`oz7j&^vtCEqj7?HSUa&U z>%_KdmA_p z&4yNc{;G}lsQQhs`1ZwJ?@mvNUOAh|vmB@5X?RTE@$%i||cwiU*mgCfl{wZo+iRhLHNAt>rV9%#>u7(U`Wd%C*p~VvFS!##a=W49GO2 z53~ES>#hfqnaZuguUm|`o;5>@9hN7>St6#fC?%Oew4J-JBb7*|Y%Cl5%)^tF-r#M^ zGt*y}#<(_^47=Oy`$u`y5JSnmsd6HuHf3FmZ84wdFO2NQuY^r!_uum01{Foix%TxSm_|JK>>&ly9C@r;!dbGEd z^}cx7BbzlLAvpH=IObwEG4b{dQJER=Xz9h>Lt`ge({4j8$xTjDlb5Qx5T`s|owBvD z=|5>jwSiA-Y4La)v~72VZAwpnlt0I>h@AAqNQfIP+QO*a2p_CS8h>Ekhoohpq|Kgy zc0u3ACJ8?q#n8f=U6NRu@HlC~9L*?#^?hdA<{xP-oRTdwJ->*UUuLVQ;k3KA%>m&< z$o&mAamVwG9dZL~3aE+sTWfRgDpDj@OLNZYo%Tv3tB`)6g z>C!6yv!08pDmiNf4)(>Mb-%q0aGfu6y<^|x#ZC3+<<%@jR^{D&?M!sAam7g+Z+2v* z&#vC=Ka{;dnu|&DSA^K}?k3I25bB8Wau)eMDC81vIJ#3W5ahn~$=eXp{L4J5KZwgp z){34dL;Kqc(-J#oAHU)9^%XgECdJEFJl`TvI-m-J%Zpi!SYAhJD+SJ?U2^SGHomU? z>fz)f=XIk&BO|8T+pcT`)j;PG*>f8;kApnE&&AWAgBemqt>$BeCfX^BB4$NVuH_GT z#^1e`BLATkC?idaBL*yq^MqPQYfqsD$`_+&Tv-n>aiu=#tMR{YiW5S=^0=QEr&HqR zPl~pwRJBL2`@Vqt)Iqiq(5FyK4tHhfn6Upgrr`K#O?F~_YqGdg2L12>Jj>!TT9|R7 z8X8^tSj_cm?xv`vWLnRPk-@aTr*gOBx2S^g!wnrOPI<ygm8hR~gb{VoAyC_%7B=LEND?sn|9%(U+Ad5Uv;4@Qvs(5$?}{owtY~zzz&JX+ zSGrjB&p}F$4=xa)A{n)Qhyp}TA?FL=5hgl_dX(-L^_isN_lbIzmC}Ia{%5*#7p}#U zKp)eqn%IwpXf-y`NN>VNK6ory_5XLttM=e6@Ff=N`TYDE5R-l-+N}3PnhWVEp70n& zd(;nE50FUL0hUek56J5H)gy(i2J|=OuT@VQSF-_m7+PG%Owr1h?wGN@5}; zVYT0Rlz14r}8A0W5-U|v$a{1zTXFaHfS*Oo>!wP^k}EWu58SHz_#T)|(OH!jB<6Mtn2DAvd7vSosd@o>DTmICML3 z7Y~(d;v9wx>}OZnTva<1a0{=-IoBxN77#p_tOZ9*UM zjS!mNGu{+-dW`usA)Zvwl-BZh2~@e4I_yyN6_dgN_RI=!aM~bT`nKb?);OxpCaAs0 zPvTa8i~@J!6BSndIBA78<%mAZyC+>e*keCtSC_KANo%oo9ixpE#PrATGuMzbj(nn* zbwE=b4ipRqs50K)>r`UEltKXUR*UP$f0Ivd0 zV&TaqW;ZJ;Gu*SZU2QwUR9dEWGMvXWc;v{mYxs=&65c+_;nI(>hlo3)dANWZjJv{K zPTDXG{G_*(*GW11UA-v5)zlfKYAz~`*NJmRQyFY2uqV&&b_TLKL@Cx*?6${A!HyAB z9nlJkYS1@&qIE0a?XG#ncWEJw7?XR=Z|NnzqV~pqKm-G+=h(ktN*C&+j+6^ove5?r zl&|!|bf0%BJ09^X2MzBjzzzjyT?NbLm$k*-`HEL*G6d7tx-A+jjkBBTUk|%q=S+(6 z`Euf=cmGT%>on!BRx%mNfHkpsMD?@jbeAMc{W9Cu+%o^lt2U;_ZqQ_~KvNJW=|C5i z|9(z3gyyn4$Lf>5j<+v2sY5w6VXijdP)ArVIDf{ce51GKSiEE;nNxu z`%8II7x8$v0+Jw`t-7!b$z=&@KJ6Kw2;k%$X(buoJBWpBD|Fce0j~rA{ z%qS(sTUX#Dlav~PH24O(y8YNt@}Z_4vO}RW%iqk;h5H+y3w$&T>|*qsYoQ}tbM#k% znC%>}3P#7Ai3TPmwB+&cC%KCJapepXT6^n}i&~dAaNxKw$-zZ}6A_7o`c>tebbrpz zSNBgPz9q2Xa51UN^^ah3dkD_?OH>F41z^?HL~o1$OK!5fJ;R_OyJSzo@OTt>nPa%B z(8guE^4hSoB=?yGhHCgtP)!ycm-0yHJi=BQToG0nF;vl=LZlm#Sr<&(ngOkJT)QU7 zf+uM>y*{skUA6uF%h&zGL}`-W#3&;Z`>PdJ*t%NQX11zE1^4VcBt9IH9@7^LXwbjN z#qun0!C;pVpX0)3%-b7#jo#$XVE*)|7iemw*gdz~`FXX!^A%m@PtqynWQWr&w~4)Q z<~^MM&}Q4I*K4!Ff1(n@Zm*cYgnZHA9ZuiwUmOQ(l*({LyVxX>X3SQ+;-GdvC~qqb zJyGf0J*^|v*r0FGV2(VsjhwV^46A$xHa3|(WGbd%x0vU@v_boiN7 zG?7U58f$q!RX|-24|4;$D%Jd+zpJZZSi(XKcA=Z1)?MKF!bvw}T-}W|JB9ywy+%7Y zV3zrMFUGJ~T2OJSCHnSv1wS1n+;VoJwy+_8I}=ffW$O{#<+uV5!CH%Eing5;B8}JVaGzv%EQT{JM~|J;Udo|zG?4_Ju)X>u`rGv_XR*!1zbC`pnShwZ91|Ew zrd4^)`IQLbOq4(yxF+Q|!I!c@YSpHVg^j?F`v>y91P}CR5V%kWL$J-oxmTL*PtV8H zaqGP4Mb&}iS(F!=t@jOP6q&%lq*kLChQRxHLnzr`=wTe3jD% zzwl$dyHs6a_;#JFc_8sntF+C!{KT59_JB+{x#Umyh#PXplB=Hto7;(~6J7C({H^7r zku%l&3q$ervp*B`w3}T6?2*oair&H>#fQe?b34qG zi%KF&P{0mT%k9j3NGBO#@S=bes!(X`R)UjJ$tG#4d}sC{;C&6UxO2!C9=N2IUoRB{ z=&+wKIJ)Zoac(0c{Yg2{NakGE{}ogsj9+>b&>R_jAHkH>@1;vV6p*_R&9AiQym=X? z9(6EgkRWFuY1|zUk3Acc9tEim`Z9O=QE^TSdad;LBQN2>C9%DPDBy?zy1@S?xx{W9 zppV0ikhpyZ*&cItQQT4c2+-hS>@1UBzPH$DzLAW8lz)*=!&2Pa*ZbuGpflNNN?bIl zh2pJ!GCCaWfn|KPN+Ul`Zl2ua!GdlT7*~74UiB}&wgH@2|9;2^{dgArcA^79Qq9@4 zno8=<^L2Ql;M^vog^@H$Q~XKWQ{RzTP*EN2*;<#;WTw<=ms@d%QNO5(PbEtRtq9># zNz3hO>ulelM7*QMjIO03zXwVCKyTSyxc`YdFxCtMMwnPX;o`|Wg(H{S>LO}ev563$ z=DW1%g6ZYW<1vp|-Z}P*{~ojeB80A6;C9ouLVR#5H@v^*Cb=jC7Ri(`lqc6)scxV} z4OOD?M(%NGRa1zq zj%I+Xl`OpV3+yePc&@D+CWsuLrl5eIC03!e9uP5y%~(%wk`Au8uQ{p13*z|&B`Tqe zlNH#W2rYoXVJltH}14$nUt zScG2gIAKUC1A3^Ae3Z*audQ^NM_irG<-s^zs4RkN^1{yP2DksvPGD%Gf#rW}P= z7eNZd+_(CSUzq2X_{+UfZGji!)nhJViyQQK*DjddxKb$8>P2t$(}|p7iIb(EQL?QFBrJ9@f|F5?{ijyXv8j9tn28I!<6Z9}?~mvBv&=hppQ z|NF2V(mKn-aoyc5Iry26~38Qm0|AZO6mNsvJPOSuK^S;?`}$Ed%1M(qZ9IGA_u zzqTOqcn4Qrj>O4lG^UydW6{SP@;$o(J2K92YwgQ0f$KTu@BD6SPVmA*aQq2MW`5eJ z>sg2UJ;G+2y%S#@wdw=-=4jfjK3xv+-VC~GoTy1UxU!iNNSdz~gEP-`2FU`v0M2#g z?Lk-OE$FDs;5#Jy{E9j4?AB|#lb~*c6BUc%V-eXMjD=dJxc9gOacJTr5W~&W$nh-Q zGwHE*6WeCxvV9M&Sm?fOb<)5H+LJMRMH-^MR!{ixVW6K`x0s~$Cu+=>DM^9*RXFQ^kJ%2R4}U%6mjqnv1)M^ZRud>qq+LTUX* zyXNgIXrDsPeD$YSRLP++4YW`5v^~l?rGJOt#{{$fh2@PoV#xg57zrNgo&jW^Bg+X; zBk;OEyMgHNqI`9*ISyd>YPf+vqZUDMc~|H(eZ0Za9ccLr|GA{ZYSr_XWZiX9Bt3E75 z_lw$y>F}KM@h4E*LT_#C$ z!{|th^MQlOt)EI8&EZ))mus{}wIL<7j;-mc0QshF+wioQ{qJAtqbV1zH!>_dHhK`)q~Mogs&@(-1o z=@^5>6u+HlgSMIEC*Q+lLaTw1~U*xes|`y`QL7alEeL zHWwtHd%I=#6EgKZpgBYg{xJx|QQChZ590`m{+Yf&|HE8D>%oSZfnW%$ZZDtoax>M|nx%y3+-b~@0=(pc=Yx8%_ zYaLCsd?R@RlMR%5efr6v6-DWbXiIx)1qnO$4Jir$QbOgu+de*D}wdgXq5^t*1M z>Y4f~!Mf7c!&fE1K|DZI*AYdizuDU&a^HWvM)j+K&(l`)@`E%+ipS*mVB@OCp_I4# zi)zZtjB79A%QcrJmM2Vl+CU>_N714Sc;m|`|H#7=>YeKGt8zshNqSY0Y>ome&|>tY zO$S;X!(*M_Gk^H@%nhu+zQjUz|LQ-aLiBJExk0cE-%Q|sz)%Gg)sGe*Xli@>tETkX z^Ng>X2;Nv+^1O;``c7&!U|3VP3i#Vr>tgy~Hx}sW5PWZUgP-k;h@oJ2!4k)UdZ2{y zjg8#;==g3@k8 z;fw6Ya|#zuj_|>~qqej;uT~8A{RV2Ns2wDLmK)9$dD)5=nI{IQIUX6_($ZJrTPWwa zXq5@!*{X0WqfIm15wEIPf%`jK7x;$sy{Xnu83CknJx}*brWvbHjeI7YwaU-@HVIZf z4VPq@N^YAZh=h%u? z2up^CWPN>P+iD;e@jkXi!2%?=IZ=x&%dKuFQ>lI&Q$xZTv#Z@YFvRn~V7BGcQsB`& zT0o8i+Oz(TqxP@wsoo^WD?IX|$|%7sJ1SB9$ z^;VWlJc5O*s2jlOosDWsVsIf1>0p@K;QJ-i$U#jq?hyi*7yu+(a@)YGq&{kVB{%*Z zJuLugu>a7dAYLu9I^YHnO1>L!Zp44_nVQYZ+A(gQB%f zvams_o4m|Bsr^o0h39AqCiuyn_iP9|yyLDLp5Y8rK;o|k4lMEY5Kp|zAejYRNP8_v z+NOEVix93)y`uXDyT{)9HOV#Z{B~^#KC^_F$6Tw z=0a(7$60dxNnvG_#rQezW*Jnsm37CYU@Y+y`F^PvObp&CF2FBC-9^hFZuquU^;n5t z|BlUhS|Jcs{fN4EEmvWy+B8`OpzSLWGG^_nN{$obuj66w0Ao>Q5@GQ61s!tuKQt7m z#D0pv56VUCFbxvHOB55_5uFe;n%i$vtP|uoeMu#q)G-dBP$ggRH>rfx)8ZZc%GSA? zb<3{QPj$vtr=Blh&r_;qb@RBlV#=RrA+OH&rllCtjccnd;45l&HWQJsyVJYTSiCbX zKn<&Jh>E-3N6T(*cv2z434VYY5IiulUJlINcRkxZk=H?_GZzSr*GW}M7%QC_OR+EUM?!Cg3B_}%368rPp#=nb9$Sy*qUlb(|-y-rF}w_&?WP;0w(B~zP) zT9EUw;l?n#N0c%|#B9NHg2i~1sm@hcJQ=X=?0MR`TS0%!tzh;==oW2hlnxUCdJ+sm zd(P|rh@6vr3AUdiK}b(~Y2Z!ySNw%14z-HR9nG!EPmaV!IGBf^*(Y!(d_6$5Ptd_u zcvK-XlCSu<5;iG%91CLfd+-r@B??ttQxReg?iOVZf`o}YLKgLL4}@|AAHJkf{~mBxe)h*ogQb1Mep}geg1>brC7>;ax5=cQj{r}()k^ZQqp(Uh<1ygDGdhRA^nRCFsW$N^?><_u%n+{u-|PC#xN&UPBOyO#9& z(){upnbR5|{3*0XqZHzSN{f-8)9O=pABQy*Eq*lgwpaE|)^ZF0-1{VVz>`~wRvTMV z`tlY6sswT(CXOL>W!>GXPofgJ&!46!>T-j~$`De|U0`!yqy%dlycOa{WC|0AEe0*E zOocNNj5BDbh<->j#53v3ehf(QW@m!+ZXHd^lW&%7NqlBg4O+)X!Ntb z@?zCj?_||27h-tckWUU;y;dvh06^}RfWIya=#ZOcl+)eJ zATSn^C+88pF42K~*` zXPAccrN*fiY-u+yp|WNKCZtkvyh-e}iEsD@kjHBTV3332myb3myp(NdBz+fC*EUAx zy3myHa?~QF|2P$*t@5^*%YM%wyOm3D@l%VVW=BwRv+KFhgi_0mP;T+N`Uc%y2MHC? z4TH40DZ0iEajvrawv&7&)I{^$&lpJJxIqdhxg^`68w8%dIb!#o(D(F_g16Aq_P%U|zbOs%?P@5%`hYWl2R2`M9CyblC7})s7s! zMJyfm6b=8kBh*Bb*v$9ZP992|xdy;pU{na>a}^RF@%p)~8$XC|p{016j+n+%?@}%? zogdTal#`wFL*j&&2G8qXoKh~bs+bH00+Qc(a3~-me8p|Bmk{& zNzf-FW$YoA=KVDnHK$gLx3U(*x%AAW4=o-|s7j$i7GyCRXaLGTyX3t~z61`_VSywV zp|iJKc zumBhNA!Gs{A$i#ohY)2P?hnjt8EkiDC6{5$`}dOXiK90<3|)p5d9GUjdusY$H{ERS z0i4|o2YY14?Mh`uuFB1%v7-3GtoqG=venF$X4`VaiQj z+E~16S<&iP=RKlQ;uKS>kSXf*cIRK{KZ{2NGkN%Jy#vkIJ}2J-*tnVt2p?Ya1BGq- zg{_`vPF7zxbn?S=hG=mObNYFYK7X<_wkUg@nXv1B2r7a}p3&`!15}(v76U<_Pf%0k zE2WO7WywL{re`SyTWjSrPt_z(vwno$#cwd}mngo4+^WI@%{X~$HpKgPX^9nnbUW&( z!;NLf*k0u?bPKVNE{>m%D>deZ7X|$)z(c5bS1@R7KP@HI3$g|EC?~r{iYyd5 z)CLLiv0<@4q|%DJu^mFd+e|GrwtwM;cqUzq54QYt8^Tk&CJM2r6e&M}^_!Fb&|)=L zzgWE{~p?)eFfby-Mhb(?#SK;yRdr z<-gbYI{S2gU%RrQ5qaFy%Qe7ZgL}mA8B;9x`C~zexL0aRCd!Js)%cu4t1D!zEz$p7Q8^hJhT%ONoiHjZmmtkt{ zEsP-y0CGAP%lKZWM}l0GpG2)l=I8MVXdB|O;r%MhT9xE>GC)taQi$KX5QjMXT>l5q zU)sLK;^QAd*05yBzH09Lt>ngEv|j~{4{xSktb@id_6v4$#A~F-|9rRT2%FG{>BswS z=adn^<2mW|dqIZFp$Y$BdV<2P^=D*-KcHnQ!QD1P`xJeJLRIxg8SU=QyP6$bGqQez zAj5StpuEqq2R`12fT4B3*`R>lym?glT}k7)s21A$f}j~THb+$h_8L0{Q01DuMN04W z$F~;QU$D!W(!4wHKzv4Y*MH3eZEFEcMKwY9Z7UKw-Kyyl-3x3E>EXPellnal&yo@? zMQp?Ae1pe4I(_I9u*H5Ra*uxd$~d9Kn4_vMoE9xKi7U-FCi%I>gJ@h+Bti_uLeXWm9yj>31x#Nn~j(DUF~c4=2q9bfNbCFNq$-A z-x4cJ^X##}j4msmpFN@xwkvISFUvZ9b)yly{EBAPa@}4F3rI{u@X${A`t<6L?m=lW zvx#5no6j<6;tn`Ma;rM`n$GmtC!PnQp)3421LwRRV4QOV&1LM6*@qd)R#GaJKueAE z>oxPyV0@p)Sv5xUW)r7LUW2*r5v;V&1k0as?$?d@*AcSQ&Fj3%zO)~k+S?M2Kdme- z-&47-ao^Sw_DuDZi~?bVoIM8%lJiJ67#mVK3mi)KLnkhyR9ZY6>&3Vy=0jF@P$lhG ztLw4_MV-MgK-PK9*-(sgF{x)wK_@70*1u|OS^Bj1T&Zpt9$@k-$oJX*KeV;fsrWv? z^5TDJn8$t8Gl_=I;D75dE`#(lW3V< zKJGvrivMF4(c|*S-Aem;u;seU!nXJz?1ApN^v^tId&{V#CD~A_r&21;;DiGq` zdYI5VTU`|VV+ABet>o~i^96In3W;WGmv#*)P@EaJYb1yP*(b_CsQSfOOPZt@h`%BZ zHXldrQ}ucM$QW8Kwle(HCUs5KchLSFe9HP&nP1&`re5{w{{j0z1i#g#_-cPbzhd`@ zLE@3B+=N{&5xbzbxl@6jK{@MI;g?M}J1%V}aJfEMTwwMD)eT5^K`zaWx5KNYMQ!JL zD$Rk^HV+h+Q|Y>j#B{F<-P!LX2-(?``t$2n>Nzc0ouKws{=H34FG`>Hv1oEEOfmA$ z$vw|M%AktXT4rcSj3CMfVeDyH%a_30 z=qQY%De8%p-o?SEvZ!mVLky~LQF0ePo|O-km1u?bamb|3;m`%SjfhJr30IgWp_mEmB zM!A+n11f&dRf*3`4yLq<%{AN1mX>QIfEONH5(i^iGibNb>7Uvc8g-6ch8frbeZr1K zbH96i4OHBH(U#SI<$ndezuGL!z|4QpSoxjBiYkYn^7ENPPG3ox{44H`}6; zquVtulP&)Mm**WSbqx92G&tKzHQzOo&~BV&wK@^v%^gpYx{g~pG6+E0Jqf8{@gA8g zEMn#+82Z+`{Gv*QgTMxJfO1;eiQVae<0J-XM7>i#D9 zh4DAVw%!@|Iq=pm6WGgk4x!^+VgAVk5SAGU#$?*L#~hyZ$5yeqkPFNF#9Q5l;AMD?IsLv!&t?-md z`>pp1W7*uSgM<9)pwyO%{{VXY#QoQ+JikEj^j6RqWYspZb$nyDtvce{Or8cuyoo?w zO1l>8-#k}!ulySERfVUOr+8LID|S(KOXe2GcE?d!P4NE!#@fBuu|5!WlngSo8$bh- zl6V5NQjC4kTJpWlRx5o52m3v}+=g@X>=I3Cr0-@?8u zw}Mw+1X~1GQ8TWd;GxF@KJ`Hj#H}sWmY*cX2Vlz_Oaixc!4!NkNKL0dBO}LtDDgjy zu1p%&kNiP;9zCWjYm*Fk@4{CTbMSk^cUKRoE{e%L$IEefC@mo!vd89X?{xnF5?ZzI zmfA#%Cm_1Tx4%qcuH5QRF6jn~DZ>D%8NnW;=CY}WpdyS;3nkQDN?)ldfoyVbOniFqQj zbs;gZO_86*s+Jy|B(yWBR-~5qF#iDI7;8zIYy0+)gOwQDf%sQb@RLXIWzUMz>%{^T zOL69XqV7NteM@z$)RIOZ&oGU?=}{hP3+r2Zml4aRJn9ep@&JF6T^zBC*vZ}LbL(#o z_@}~G8Y16mULmo2MN|HevZzv{)7r57Y2q)08m@&Gh;`j|2_UkWo(D4nhQeklcvOaY>XD?*qVz>_-(3;wf*(Hiccycz{k{A&&MO9_1~;=YqHYG zF5ijmJiAXC=4-aw{~Hz;}{Oi2}}x}FC6HKBZAMuM*X9Z>?PSr9>>KU(h&{at;h;`T0w_C*{@ksoh6~h>r;n@Pq zb>W$<-a(L}QgNSZ+mBqbmfOqIRFLb8a&wC1^xYQqe8siU?_={h2I66l@u>Vo;jL&u z4abNiiam!m*3Gp*{${pQowP?kB{wChwsq%}eU3;D;4`)~Qcvr}IkhNufOQQ<+iz7+ zp-B8I&NTfSS(4@Lbj>Q}NW*9C8;WiogT6ks*Z5Dv+SGS#1>}t^5*)J3ml13PMj9ztXH~4^14`E-L&y_lVNQ0`qPG>mb2jB+{-HfJhK4@AJU*c7mD9iA7aop zZ8=N0F34hiF;`jqMIGRYw5yV&Wu-|JpTrv0GJ8G#WIJC?M*jf9MLwJ6NpB70j2L7( zWk=9fY`+pU8=HpmG)6E0*^$$d-_Tao#kJ9i36gZr-3qzK)~mmV)qn|V&mdoH;}yN7d_s9F3){r9#s(TzP@o^{P5%IdUPKdHLty4RjNryW+y4N4 zR%Wr_eNFA&!|k!b766pV^D!N`T7^!mHNDHV8FV)-JVj)-=I>LpvR%jJWm5aSYew5r z@anVq8oaQ%8-!6ry?(Xl!@x3y*nCT_+^Ak-w16rB^)-IiLB7(h1;2-M#I;!1?vq!z z-do=p$*NPWJ=Vb}Q;xSfVWN0y;_gjK>&KUE&KfBt3J{;+Y;>zo_-Dd%lz1nJ@^Y^C zXASRxT;`ji>lYV}WVN!(C=W8oJgGaJr?qKX&wVx5*(_H|V5x&4&*U-pXpEYAqbVtG zb6$T4-CM#Ag!We9j!dNsn&&m|h5FvR=EJS{;rzDa?Dq0`V;-0WwXQr#c?`*AHPjF) zj1ZBQ1p6AlWoPFi-p%ArGt6qaWAB>LMlanWGN}nBdHuia(cr~&7dJ_8qdS&qi62pu zT^@t*%fh}LwPeukEuFd-myepdR*KqC%9;WxC-2Dr0DJK3RW7wTwD$;nH6{r8S`rE$ z#-U;G)L$}+xudUIYnPbFxcGka!@fx&L%8t~Bp<`IM7|S-z?fpSP=gs%i7+D}>N^UsEW%#3SgpOvv}OoyJj{Z8+$pP6q`oFgsd;lP4mMAQHYlvN#ujx< zpE~i`n3KuKHKS>v>Jh+VlKHqnwJntVquiR1#@SF~Guu#-p|C-(#wA70h8 ztER^vpB?yYb=)!+9jdI+X}1J<+MJQOP83VD0iH2b>BFfU{_&BjcyCSeOgENM6hAh} zu3J03ahkxk)4Vkt(p?xsOe5uO<&gP+b;vwdRdu2Gaa~$T1Ch>JVo$$XtvYyk+7tb! zZ5nP0-dkk#>BmY=ntPim*;{iHQ1O39UGo;R4=@I{{Ux+Vzbpq zpExk!Mx*LFS4k&@d@lnax6|yTc0;w5L2P&H)YW;cv}L>d9I)K{@dQ$fwMIUjD<=w@ zCSNr;^f~zaW#cPyEp7A?Z5It2#dGsI`|j^rQ)-(0nqiYq%vT%p^V|Y4_;stV;cH1I zNf$|uP`pX@o1#WNKpkjp8XIkiYZbkoTjhT&w-n`0qWeSkuxms!NAVAd5(JAR&H%CK-e&dy5&oJv2&y6&DJHCietWbz0pSC|v0-bzckH#~sDytdaR-gq};BXFQx@pqIq{ z8&>-+o#b8aUF3O;Pd)l_E1YYq>wAVbno^_<7Wl*2i2#%Pi;jof)nln@`j-34YlMg( z$hTG4SJ%>sSAy2VRFgV{@h$G3B5b#gJ8{D+LgNSPQA_b+;v1tB(?GE=3bKN`$EeRW z$m$xOh`g~I-dxHgQ|3h{@cU=IELW)vmd|@}YD$daWDkS=(~5}KZQW>S=;^80zs1iF zTE=fAwU*=p6_Va8?azODeW%3z9`LQDo}G4BZaY%UlgGVt4dd+-KV`SkY}$6}u_F(_ zan`dRRMsu-g~Zd`Lo>)Sw&M#J>I-AFIaZ%HGiu#M^?2L2%=8^w#U3Au+B@r;dB|bB zq-W<*p5PuS(D?7cKPXLM;Bs^2&GRu8!D;ep4R<=~z9cT!BR*T&jGXrk-llygPKg!m zcTw1irDU0G4j7G?<#CRvfnvPVr8;|>4@m|HKXps01Nx0S{(#f)b z85G-D8RQB&s89(c*NOiC!98bzzh^hnbsLMPdAxPwBLr5~j>Hf6Nc9{LKm#Kqn)(aD zUKsJFn+%d`9yYnTY1k0HP!6k(GDjJ -Ua|+z6UTJIdeuwN>X}WkCZ9lUg$ocEz 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 Y0Q%Mbh=G$*_{W8bSq27 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