From d3c17d5e6bdb9dbaca19f3bf53aa2802105355fd Mon Sep 17 00:00:00 2001 From: Peng Lu Date: Thu, 2 Jun 2022 16:17:07 +0800 Subject: [PATCH] [Feature] Gesture recognition algorithm MTUT on NVGesture dataset (#1380) * add nvgesture dataset * fix nvgesture pipelines * update gesture datasets * add ModelSetEpochHook * nvgesture dataset support multi-GPU evalutation * add i3d+mtut model * add nvgesture i3d configs * webcam add hand detector * gesture recognition with bbox * add hand detector config * fix gesture recognizer init bug * webcam/gesture - recognizer runs successfully * delete unnecessary comment * fix lint error in gesture configs * add nvgesture category info * webcam/gesture - display gesture recognition result * add gesture recognition related docs * update gesture related comments * update light hand det model in demo doc * update gesture recognition configs and results * auto modify model-index.yml * stabilize ssa loss in mtut * add multi-input node comment * synchronize tools/webcam with master * add gesture task-name mapping * move gesture configs to configs/hand/ * fix a bug in demo (#1373) * update gesture datasets * add ModelSetEpochHook * nvgesture dataset support multi-GPU evalutation * add i3d+mtut model * add nvgesture i3d configs * webcam add hand detector * gesture recognition with bbox * add hand detector config * fix gesture recognizer init bug * webcam/gesture - recognizer runs successfully * delete unnecessary comment * fix lint error in gesture configs * add nvgesture category info * webcam/gesture - display gesture recognition result * add gesture recognition related docs * update gesture related comments * update light hand det model in demo doc * update gesture recognition configs and results * auto modify model-index.yml * stabilize ssa loss in mtut * add multi-input node comment * synchronize tools/webcam with master * add gesture task-name mapping * move gesture configs to configs/hand/ * solve conflict in mmdet_modelzoo.md * add gesture recogition into webcam * hand gesture inference config explanation * add gesture recognizer node in __init__.py * add gesture webcam readme * Adjust inference tracking min keypoints (#1398) * Adjust inference tracking min keypoints * Special case for min_keypoints <= 0 doesn't seem to be required * remove unnecessary transformer utils (#1405) * add nvgesture dataset * fix nvgesture pipelines * update gesture datasets * add ModelSetEpochHook * nvgesture dataset support multi-GPU evalutation * add i3d+mtut model * add nvgesture i3d configs * webcam add hand detector * gesture recognition with bbox * add hand detector config * fix gesture recognizer init bug * webcam/gesture - recognizer runs successfully * delete unnecessary comment * fix lint error in gesture configs * add nvgesture category info * webcam/gesture - display gesture recognition result * add gesture recognition related docs * update gesture related comments * update light hand det model in demo doc * update gesture recognition configs and results * auto modify model-index.yml * stabilize ssa loss in mtut * add multi-input node comment * synchronize tools/webcam with master * add gesture task-name mapping * move gesture configs to configs/hand/ * fix grammer errors in docs * fix a lint error in doc * update nvgesture evaluation * add introduction and assertion to TemporalPooling * generalize NVGestureRandomFlip * add unittests for gesture pipelines * delete duplicated config * add gesture inference unittest * add gesture dataset unittest * fix gesture inference unittest error * add backbone I3D unittest * add mtut head unittest * fix mtut head unittest error * add gesture recognizer unittest Co-authored-by: Yining Li Co-authored-by: Philipp Allgeuer <5592992+pallgeuer@users.noreply.github.com> --- .dev_scripts/github/update_model_index.py | 1 + configs/_base_/datasets/nvgesture.py | 42 ++ configs/hand/gesture_sview_rgbd_vid/README.md | 7 + .../gesture_sview_rgbd_vid/mtut/README.md | 8 + .../mtut/nvgesture/i3d_nvgesture.md | 60 +++ .../mtut/nvgesture/i3d_nvgesture.yml | 49 +++ .../nvgesture/i3d_nvgesture_224x224_fps30.py | 128 ++++++ .../i3d_nvgesture_bbox_112x112_fps15.py | 136 ++++++ .../i3d_nvgesture_bbox_112x112_fps15_rgb.py | 124 ++++++ .../i3d_nvgesture_bbox_224x224_fps30.py | 134 ++++++ demo/docs/mmdet_modelzoo.md | 1 + ...sdlite_mobilenetv2_scratch_600e_onehand.py | 240 ++++++++++ docs/en/papers/algorithms/mtut.md | 30 ++ docs/en/papers/backbones/i3d.md | 30 ++ docs/en/papers/datasets/nvgesture.md | 18 + docs/en/tasks/2d_hand_gesture.md | 60 +++ mmpose/apis/__init__.py | 31 +- mmpose/apis/inference.py | 56 +++ mmpose/core/utils/__init__.py | 6 +- mmpose/core/utils/model_util_hooks.py | 13 + mmpose/datasets/__init__.py | 4 +- mmpose/datasets/datasets/__init__.py | 3 +- mmpose/datasets/datasets/gesture/__init__.py | 4 + .../datasets/gesture/gesture_base_dataset.py | 86 ++++ .../datasets/gesture/nvgesture_dataset.py | 185 ++++++++ mmpose/datasets/pipelines/__init__.py | 3 +- .../datasets/pipelines/gesture_transform.py | 414 ++++++++++++++++++ mmpose/datasets/pipelines/loading.py | 78 ++++ mmpose/models/backbones/__init__.py | 3 +- mmpose/models/backbones/i3d.py | 215 +++++++++ mmpose/models/detectors/__init__.py | 3 +- mmpose/models/detectors/gesture_recognizer.py | 188 ++++++++ mmpose/models/heads/__init__.py | 3 +- mmpose/models/heads/mtut_head.py | 155 +++++++ model-index.yml | 1 + tests/data/nvgesture/bboxes.json | 344 +++++++++++++++ tests/data/nvgesture/sk_color.avi | Bin 0 -> 203906 bytes tests/data/nvgesture/sk_depth.avi | Bin 0 -> 43134 bytes tests/data/nvgesture/test_nvgesture.lst | 1 + tests/test_apis/test_inference.py | 23 + tests/test_backbones/test_i3d.py | 21 + tests/test_datasets/test_gesture_dataset.py | 63 +++ tests/test_models/test_gesture_forward.py | 58 +++ tests/test_models/test_gesture_head.py | 38 ++ .../test_pipelines/test_gesture_pipelines.py | 181 ++++++++ tools/webcam/configs/hand_gesture/README.md | 26 ++ .../hand_gesture/hand_bbox_with_gesture.py | 59 +++ tools/webcam/webcam_apis/nodes/__init__.py | 18 +- .../webcam_apis/nodes/frame_effect_node.py | 86 ++++ tools/webcam/webcam_apis/nodes/mmdet_node.py | 60 ++- tools/webcam/webcam_apis/nodes/mmpose_node.py | 178 +++++++- tools/webcam/webcam_apis/nodes/node.py | 48 ++ 52 files changed, 3692 insertions(+), 31 deletions(-) create mode 100644 configs/_base_/datasets/nvgesture.py create mode 100644 configs/hand/gesture_sview_rgbd_vid/README.md create mode 100644 configs/hand/gesture_sview_rgbd_vid/mtut/README.md create mode 100644 configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture.md create mode 100644 configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture.yml create mode 100644 configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_224x224_fps30.py create mode 100644 configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_112x112_fps15.py create mode 100644 configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_112x112_fps15_rgb.py create mode 100644 configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_224x224_fps30.py create mode 100644 demo/mmdetection_cfg/ssdlite_mobilenetv2_scratch_600e_onehand.py create mode 100644 docs/en/papers/algorithms/mtut.md create mode 100644 docs/en/papers/backbones/i3d.md create mode 100644 docs/en/papers/datasets/nvgesture.md create mode 100644 docs/en/tasks/2d_hand_gesture.md create mode 100644 mmpose/core/utils/model_util_hooks.py create mode 100644 mmpose/datasets/datasets/gesture/__init__.py create mode 100644 mmpose/datasets/datasets/gesture/gesture_base_dataset.py create mode 100644 mmpose/datasets/datasets/gesture/nvgesture_dataset.py create mode 100644 mmpose/datasets/pipelines/gesture_transform.py create mode 100644 mmpose/models/backbones/i3d.py create mode 100644 mmpose/models/detectors/gesture_recognizer.py create mode 100644 mmpose/models/heads/mtut_head.py create mode 100644 tests/data/nvgesture/bboxes.json create mode 100644 tests/data/nvgesture/sk_color.avi create mode 100644 tests/data/nvgesture/sk_depth.avi create mode 100644 tests/data/nvgesture/test_nvgesture.lst create mode 100644 tests/test_backbones/test_i3d.py create mode 100644 tests/test_datasets/test_gesture_dataset.py create mode 100644 tests/test_models/test_gesture_forward.py create mode 100644 tests/test_models/test_gesture_head.py create mode 100644 tests/test_pipelines/test_gesture_pipelines.py create mode 100644 tools/webcam/configs/hand_gesture/README.md create mode 100644 tools/webcam/configs/hand_gesture/hand_bbox_with_gesture.py diff --git a/.dev_scripts/github/update_model_index.py b/.dev_scripts/github/update_model_index.py index 3c24055060..f6721f7790 100755 --- a/.dev_scripts/github/update_model_index.py +++ b/.dev_scripts/github/update_model_index.py @@ -151,6 +151,7 @@ def parse_config_path(path): '3d_kpt_mview_rgb_img': '3D Keypoint', '3d_kpt_sview_rgb_vid': '3D Keypoint', '3d_mesh_sview_rgb_img': '3D Mesh', + 'gesture_sview_rgbd_vid': 'Gesture', None: None } task_readable = task2readable.get(task) diff --git a/configs/_base_/datasets/nvgesture.py b/configs/_base_/datasets/nvgesture.py new file mode 100644 index 0000000000..7d5a3df7b9 --- /dev/null +++ b/configs/_base_/datasets/nvgesture.py @@ -0,0 +1,42 @@ +dataset_info = dict( + dataset_name='nvgesture', + paper_info=dict( + author='Pavlo Molchanov and Xiaodong Yang and Shalini Gupta ' + 'and Kihwan Kim and Stephen Tyree and Jan Kautz', + title='Online Detection and Classification of Dynamic Hand Gestures ' + 'with Recurrent 3D Convolutional Neural Networks', + container='Proceedings of the IEEE Conference on ' + 'Computer Vision and Pattern Recognition', + year='2016', + homepage='https://research.nvidia.com/publication/2016-06_online-' + 'detection-and-classification-dynamic-hand-gestures-recurrent-3d', + ), + category_info={ + 0: 'five fingers move right', + 1: 'five fingers move left', + 2: 'five fingers move up', + 3: 'five fingers move down', + 4: 'two fingers move right', + 5: 'two fingers move left', + 6: 'two fingers move up', + 7: 'two fingers move down', + 8: 'click', + 9: 'beckoned', + 10: 'stretch hand', + 11: 'shake hand', + 12: 'one', + 13: 'two', + 14: 'three', + 15: 'lift up', + 16: 'press down', + 17: 'push', + 18: 'shrink', + 19: 'levorotation', + 20: 'dextrorotation', + 21: 'two fingers prod', + 22: 'grab', + 23: 'thumbs up', + 24: 'OK' + }, + flip_pairs=[(0, 1), (4, 5), (19, 20)], + fps=30) diff --git a/configs/hand/gesture_sview_rgbd_vid/README.md b/configs/hand/gesture_sview_rgbd_vid/README.md new file mode 100644 index 0000000000..fb5ce51f9a --- /dev/null +++ b/configs/hand/gesture_sview_rgbd_vid/README.md @@ -0,0 +1,7 @@ +# Gesture Recognition + +Gesture recognition aims to recognize the hand gestures in the video, such as thumbs up. + +## Data preparation + +Please follow [DATA Preparation](/docs/en/tasks/2d_hand_gesture.md) to prepare data. diff --git a/configs/hand/gesture_sview_rgbd_vid/mtut/README.md b/configs/hand/gesture_sview_rgbd_vid/mtut/README.md new file mode 100644 index 0000000000..80e0e8f0b0 --- /dev/null +++ b/configs/hand/gesture_sview_rgbd_vid/mtut/README.md @@ -0,0 +1,8 @@ +# Multi-modal Training and Uni-modal Testing (MTUT) for gesture recognition + +MTUT method uses multi-modal data in the training phase, such as RGB videos and depth videos. +For each modality, an I3D network is trained to conduct gesture recognition. The property +of spatial-temporal semantic alignment across multi-modal data is utilized to supervise the +learning, in order to improve the performance of each I3D network for a single modality. + +In the testing phase, uni-modal data, generally RGB video, is used. diff --git a/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture.md b/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture.md new file mode 100644 index 0000000000..297c6b95c6 --- /dev/null +++ b/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture.md @@ -0,0 +1,60 @@ + + +
+MTUT (CVPR'2019) + +```bibtex +@InProceedings{Abavisani_2019_CVPR, + author = {Abavisani, Mahdi and Joze, Hamid Reza Vaezi and Patel, Vishal M.}, + title = {Improving the Performance of Unimodal Dynamic Hand-Gesture Recognition With Multimodal Training}, + booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)}, + month = {June}, + year = {2019} +} +``` + +
+ + + +
+I3D (CVPR'2017) + +```bibtex +@InProceedings{Carreira_2017_CVPR, + author = {Carreira, Joao and Zisserman, Andrew}, + title = {Quo Vadis, Action Recognition? A New Model and the Kinetics Dataset}, + booktitle = {Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR)}, + month = {July}, + year = {2017} +} +``` + +
+ + + +
+NVGesture (CVPR'2016) + +```bibtex +@InProceedings{Molchanov_2016_CVPR, + author = {Molchanov, Pavlo and Yang, Xiaodong and Gupta, Shalini and Kim, Kihwan and Tyree, Stephen and Kautz, Jan}, + title = {Online Detection and Classification of Dynamic Hand Gestures With Recurrent 3D Convolutional Neural Network}, + booktitle = {Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR)}, + month = {June}, + year = {2016} +} +``` + +
+ +Results on NVGesture test set + +| Arch | Input Size | fps | bbox | AP_rgb | AP_depth | ckpt | log | +| :------------------------------------------------------ | :--------: | :-: | :-------: | :----: | :------: | :-----------------------------------------------------: | :----------------------------------------------------: | +| [I3D+MTUT](/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_112x112_fps15.py)$^\*$ | 112x112 | 15 | $\\surd$ | 0.725 | 0.730 | [ckpt](https://download.openmmlab.com/mmpose/gesture/mtut/i3d_nvgesture/i3d_nvgesture_bbox_112x112_fps15-363b5956_20220530.pth) | [log](https://download.openmmlab.com/mmpose/gesture/mtut/i3d_nvgesture/i3d_nvgesture_bbox_112x112_fps15-20220530.log.json) | +| [I3D+MTUT](/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_224x224_fps30.py) | 224x224 | 30 | $\\surd$ | 0.782 | 0.811 | [ckpt](https://download.openmmlab.com/mmpose/gesture/mtut/i3d_nvgesture/i3d_nvgesture_bbox_224x224_fps30-98a8f288_20220530.pthh) | [log](https://download.openmmlab.com/mmpose/gesture/mtut/i3d_nvgesture/i3d_nvgesture_bbox_224x224_fps30-20220530.log.json) | +| [I3D+MTUT](/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_224x224_fps30.py) | 224x224 | 30 | $\\times$ | 0.739 | 0.809 | [ckpt](https://download.openmmlab.com/mmpose/gesture/mtut/i3d_nvgesture/i3d_nvgesture_224x224_fps30-b7abf574_20220530.pth) | [log](https://download.openmmlab.com/mmpose/gesture/mtut/i3d_nvgesture/i3d_nvgesture_224x224_fps30-20220530.log.json) | + +$^\*$: MTUT supports multi-modal training and uni-modal testing. Model trained with this config can be used to recognize gestures in rgb videos with [inference config](/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_112x112_fps15_rgb.py). diff --git a/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture.yml b/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture.yml new file mode 100644 index 0000000000..26e6f58f78 --- /dev/null +++ b/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture.yml @@ -0,0 +1,49 @@ +Collections: +- Name: MTUT + Paper: + Title: Improving the Performance of Unimodal Dynamic Hand-Gesture Recognition + With Multimodal Training + URL: https://openaccess.thecvf.com/content_CVPR_2019/html/Abavisani_Improving_the_Performance_of_Unimodal_Dynamic_Hand-Gesture_Recognition_With_Multimodal_CVPR_2019_paper.html + README: https://github.com/open-mmlab/mmpose/blob/master/docs/en/papers/algorithms/mtut.md +Models: +- Config: configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_112x112_fps15.py + In Collection: MTUT + Metadata: + Architecture: &id001 + - MTUT + - I3D + Training Data: NVGesture + Name: mtut_i3d_nvgesture_bbox_112x112_fps15 + Results: + - Dataset: NVGesture + Metrics: + AP depth: 0.73 + AP rgb: 0.725 + Task: Hand Gesture + Weights: https://download.openmmlab.com/mmpose/gesture/mtut/i3d_nvgesture/i3d_nvgesture_bbox_112x112_fps15-363b5956_20220530.pth +- Config: configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_224x224_fps30.py + In Collection: MTUT + Metadata: + Architecture: *id001 + Training Data: NVGesture + Name: mtut_i3d_nvgesture_bbox_224x224_fps30 + Results: + - Dataset: NVGesture + Metrics: + AP depth: 0.811 + AP rgb: 0.782 + Task: Hand Gesture + Weights: https://download.openmmlab.com/mmpose/gesture/mtut/i3d_nvgesture/i3d_nvgesture_bbox_224x224_fps30-98a8f288_20220530.pthh +- Config: configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_224x224_fps30.py + In Collection: MTUT + Metadata: + Architecture: *id001 + Training Data: NVGesture + Name: mtut_i3d_nvgesture_224x224_fps30 + Results: + - Dataset: NVGesture + Metrics: + AP depth: 0.809 + AP rgb: 0.739 + Task: Hand Gesture + Weights: https://download.openmmlab.com/mmpose/gesture/mtut/i3d_nvgesture/i3d_nvgesture_224x224_fps30-b7abf574_20220530.pth diff --git a/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_224x224_fps30.py b/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_224x224_fps30.py new file mode 100644 index 0000000000..4c2e2b400f --- /dev/null +++ b/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_224x224_fps30.py @@ -0,0 +1,128 @@ +_base_ = [ + '../../../../_base_/default_runtime.py', + '../../../../_base_/datasets/nvgesture.py' +] + +checkpoint_config = dict(interval=5) +evaluation = dict(interval=5, metric='AP', save_best='AP_rgb') + +optimizer = dict( + type='SGD', + lr=1e-2, + momentum=0.9, +) +optimizer_config = dict(grad_clip=None) +# learning policy +lr_config = dict(policy='step', gamma=0.1, step=[30, 50]) +total_epochs = 75 +log_config = dict(interval=10) + +custom_hooks_config = [dict(type='ModelSetEpochHook')] + +model = dict( + type='GestureRecognizer', + modality=['rgb', 'depth'], + pretrained=dict( + rgb='https://github.com/hassony2/kinetics_i3d_pytorch/' + 'raw/master/model/model_rgb.pth', + depth='https://github.com/hassony2/kinetics_i3d_pytorch/' + 'raw/master/model/model_rgb.pth', + ), + backbone=dict( + rgb=dict( + type='I3D', + in_channels=3, + expansion=1, + ), + depth=dict( + type='I3D', + in_channels=1, + expansion=1, + ), + ), + cls_head=dict( + type='MultiModalSSAHead', + num_classes=25, + ), + train_cfg=dict( + beta=2, + lambda_=5e-3, + ssa_start_epoch=61, + ), + test_cfg=dict(), +) + +data_cfg = dict( + video_size=[320, 240], + modality=['rgb', 'depth'], +) + +train_pipeline = [ + dict(type='LoadVideoFromFile'), + dict(type='ModalWiseChannelProcess'), + dict(type='CropValidClip'), + dict(type='TemporalPooling', length=64, ref_fps=30), + dict(type='ResizeGivenShortEdge', length=256), + dict(type='RandomAlignedSpatialCrop', length=224), + dict(type='GestureRandomFlip'), + dict(type='MultiModalVideoToTensor'), + dict( + type='VideoNormalizeTensor', + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]), + dict( + type='Collect', keys=['video', 'label'], meta_keys=['fps', + 'modality']), +] + +val_pipeline = [ + dict(type='LoadVideoFromFile'), + dict(type='ModalWiseChannelProcess'), + dict(type='CropValidClip'), + dict(type='TemporalPooling', length=-1, ref_fps=30), + dict(type='ResizeGivenShortEdge', length=256), + dict(type='CenterSpatialCrop', length=224), + dict(type='MultiModalVideoToTensor'), + dict( + type='VideoNormalizeTensor', + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]), + dict( + type='Collect', keys=['video', 'label'], meta_keys=['fps', + 'modality']), +] + +test_pipeline = val_pipeline + +data_root = 'data/nvgesture' +data = dict( + samples_per_gpu=6, + workers_per_gpu=2, + val_dataloader=dict(samples_per_gpu=6), + test_dataloader=dict(samples_per_gpu=6), + train=dict( + type='NVGestureDataset', + ann_file=f'{data_root}/annotations/' + 'nvgesture_train_correct_cvpr2016_v2.lst', + vid_prefix=f'{data_root}/', + data_cfg=data_cfg, + pipeline=train_pipeline, + dataset_info={{_base_.dataset_info}}), + val=dict( + type='NVGestureDataset', + ann_file=f'{data_root}/annotations/' + 'nvgesture_test_correct_cvpr2016_v2.lst', + vid_prefix=f'{data_root}/', + data_cfg=data_cfg, + pipeline=val_pipeline, + test_mode=True, + dataset_info={{_base_.dataset_info}}), + test=dict( + type='NVGestureDataset', + ann_file=f'{data_root}/annotations/' + 'nvgesture_test_correct_cvpr2016_v2.lst', + vid_prefix=f'{data_root}/', + data_cfg=data_cfg, + pipeline=test_pipeline, + test_mode=True, + dataset_info={{_base_.dataset_info}})) diff --git a/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_112x112_fps15.py b/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_112x112_fps15.py new file mode 100644 index 0000000000..ae2fe2e960 --- /dev/null +++ b/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_112x112_fps15.py @@ -0,0 +1,136 @@ +_base_ = [ + '../../../../_base_/default_runtime.py', + '../../../../_base_/datasets/nvgesture.py' +] + +checkpoint_config = dict(interval=5) +evaluation = dict(interval=5, metric='AP', save_best='AP_rgb') + +optimizer = dict( + type='SGD', + lr=1e-1, + momentum=0.9, +) +optimizer_config = dict(grad_clip=None) +# learning policy +lr_config = dict(policy='step', gamma=0.1, step=[30, 60, 90, 110]) +total_epochs = 130 +log_config = dict(interval=10) + +custom_hooks_config = [dict(type='ModelSetEpochHook')] + +model = dict( + type='GestureRecognizer', + modality=['rgb', 'depth'], + pretrained=dict( + rgb='https://github.com/hassony2/kinetics_i3d_pytorch/' + 'raw/master/model/model_rgb.pth', + depth='https://github.com/hassony2/kinetics_i3d_pytorch/' + 'raw/master/model/model_rgb.pth', + ), + backbone=dict( + rgb=dict( + type='I3D', + in_channels=3, + expansion=1, + ), + depth=dict( + type='I3D', + in_channels=1, + expansion=1, + ), + ), + cls_head=dict( + type='MultiModalSSAHead', + num_classes=25, + avg_pool_kernel=(1, 2, 2), + ), + train_cfg=dict( + beta=2, + lambda_=1e-3, + ssa_start_epoch=111, + ), + test_cfg=dict(), +) + +data_root = 'data/nvgesture' +data_cfg = dict( + video_size=[320, 240], + modality=['rgb', 'depth'], + bbox_file=f'{data_root}/annotations/bboxes.json', +) + +train_pipeline = [ + dict(type='LoadVideoFromFile'), + dict(type='ModalWiseChannelProcess'), + dict(type='CropValidClip'), + dict(type='TemporalPooling', length=16, ref_fps=15), + dict(type='MultiFrameBBoxMerge'), + dict( + type='ResizedCropByBBox', + size=112, + scale=(0.8, 1.25), + ratio=(0.75, 1.33), + shift=0.3), + dict(type='GestureRandomFlip'), + dict(type='VideoColorJitter', brightness=0.4, contrast=0.3), + dict(type='MultiModalVideoToTensor'), + dict( + type='VideoNormalizeTensor', + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]), + dict( + type='Collect', keys=['video', 'label'], meta_keys=['fps', + 'modality']), +] + +val_pipeline = [ + dict(type='LoadVideoFromFile'), + dict(type='ModalWiseChannelProcess'), + dict(type='CropValidClip'), + dict(type='TemporalPooling', length=-1, ref_fps=15), + dict(type='MultiFrameBBoxMerge'), + dict(type='ResizedCropByBBox', size=112), + dict(type='MultiModalVideoToTensor'), + dict( + type='VideoNormalizeTensor', + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]), + dict( + type='Collect', keys=['video', 'label'], meta_keys=['fps', + 'modality']), +] + +test_pipeline = val_pipeline + +data = dict( + samples_per_gpu=6, + workers_per_gpu=2, + val_dataloader=dict(samples_per_gpu=6), + test_dataloader=dict(samples_per_gpu=6), + train=dict( + type='NVGestureDataset', + ann_file=f'{data_root}/annotations/' + 'nvgesture_train_correct_cvpr2016_v2.lst', + vid_prefix=f'{data_root}/', + data_cfg=data_cfg, + pipeline=train_pipeline, + dataset_info={{_base_.dataset_info}}), + val=dict( + type='NVGestureDataset', + ann_file=f'{data_root}/annotations/' + 'nvgesture_test_correct_cvpr2016_v2.lst', + vid_prefix=f'{data_root}/', + data_cfg=data_cfg, + pipeline=val_pipeline, + test_mode=True, + dataset_info={{_base_.dataset_info}}), + test=dict( + type='NVGestureDataset', + ann_file=f'{data_root}/annotations/' + 'nvgesture_test_correct_cvpr2016_v2.lst', + vid_prefix=f'{data_root}/', + data_cfg=data_cfg, + pipeline=test_pipeline, + test_mode=True, + dataset_info={{_base_.dataset_info}})) diff --git a/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_112x112_fps15_rgb.py b/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_112x112_fps15_rgb.py new file mode 100644 index 0000000000..9777dda67e --- /dev/null +++ b/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_112x112_fps15_rgb.py @@ -0,0 +1,124 @@ +# Copyright (c) OpenMMLab. All rights reserved. +_base_ = [ + '../../../../_base_/default_runtime.py', + '../../../../_base_/datasets/nvgesture.py' +] + +checkpoint_config = dict(interval=5) +evaluation = dict(interval=5, metric='AP', save_best='AP_rgb') + +optimizer = dict( + type='SGD', + lr=1e-1, + momentum=0.9, +) +optimizer_config = dict(grad_clip=None) +# learning policy +lr_config = dict(policy='step', gamma=0.1, step=[30, 60, 90, 110]) +total_epochs = 130 +log_config = dict(interval=10) + +custom_hooks_config = [dict(type='ModelSetEpochHook')] + +model = dict( + type='GestureRecognizer', + modality=['rgb'], + backbone=dict(rgb=dict( + type='I3D', + in_channels=3, + expansion=1, + ), ), + cls_head=dict( + type='MultiModalSSAHead', + num_classes=25, + avg_pool_kernel=(1, 2, 2), + ), + train_cfg=dict( + beta=2, + lambda_=1e-3, + ssa_start_epoch=111, + ), + test_cfg=dict(), +) + +data_root = 'data/nvgesture' +data_cfg = dict( + video_size=[320, 240], + modality=['rgb'], + bbox_file=f'{data_root}/annotations/bboxes.json', +) + +train_pipeline = [ + dict(type='LoadVideoFromFile'), + dict(type='ModalWiseChannelProcess'), + dict(type='CropValidClip'), + dict(type='TemporalPooling', length=16, ref_fps=15), + dict(type='MultiFrameBBoxMerge'), + dict( + type='ResizedCropByBBox', + size=112, + scale=(0.8, 1.25), + ratio=(0.75, 1.33), + shift=0.3), + dict(type='GestureRandomFlip'), + dict(type='VideoColorJitter', brightness=0.4, contrast=0.3), + dict(type='MultiModalVideoToTensor'), + dict( + type='VideoNormalizeTensor', + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]), + dict( + type='Collect', keys=['video', 'label'], meta_keys=['fps', + 'modality']), +] + +val_pipeline = [ + dict(type='LoadVideoFromFile'), + dict(type='ModalWiseChannelProcess'), + dict(type='CropValidClip'), + dict(type='TemporalPooling', length=-1, ref_fps=15), + dict(type='MultiFrameBBoxMerge'), + dict(type='ResizedCropByBBox', size=112), + dict(type='MultiModalVideoToTensor'), + dict( + type='VideoNormalizeTensor', + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]), + dict( + type='Collect', keys=['video', 'label'], meta_keys=['fps', + 'modality']), +] + +test_pipeline = val_pipeline + +data = dict( + samples_per_gpu=6, + workers_per_gpu=2, + val_dataloader=dict(samples_per_gpu=6), + test_dataloader=dict(samples_per_gpu=6), + train=dict( + type='NVGestureDataset', + ann_file=f'{data_root}/annotations/' + 'nvgesture_train_correct_cvpr2016_v2.lst', + vid_prefix=f'{data_root}/', + data_cfg=data_cfg, + pipeline=train_pipeline, + dataset_info={{_base_.dataset_info}}), + val=dict( + type='NVGestureDataset', + ann_file=f'{data_root}/annotations/' + 'nvgesture_test_correct_cvpr2016_v2.lst', + vid_prefix=f'{data_root}/', + data_cfg=data_cfg, + pipeline=val_pipeline, + test_mode=True, + dataset_info={{_base_.dataset_info}}), + test=dict( + type='NVGestureDataset', + ann_file=f'{data_root}/annotations/' + 'nvgesture_test_correct_cvpr2016_v2.lst', + vid_prefix=f'{data_root}/', + data_cfg=data_cfg, + pipeline=test_pipeline, + test_mode=True, + dataset_info={{_base_.dataset_info}})) diff --git a/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_224x224_fps30.py b/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_224x224_fps30.py new file mode 100644 index 0000000000..8a00d1e9f4 --- /dev/null +++ b/configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture_bbox_224x224_fps30.py @@ -0,0 +1,134 @@ +_base_ = [ + '../../../../_base_/default_runtime.py', + '../../../../_base_/datasets/nvgesture.py' +] + +checkpoint_config = dict(interval=5) +evaluation = dict(interval=5, metric='AP', save_best='AP_rgb') + +optimizer = dict( + type='SGD', + lr=1e-2, + momentum=0.9, +) +optimizer_config = dict(grad_clip=None) +# learning policy +lr_config = dict(policy='step', gamma=0.1, step=[30, 50]) +total_epochs = 75 +log_config = dict(interval=10) + +custom_hooks_config = [dict(type='ModelSetEpochHook')] + +model = dict( + type='GestureRecognizer', + modality=['rgb', 'depth'], + pretrained=dict( + rgb='https://github.com/hassony2/kinetics_i3d_pytorch/' + 'raw/master/model/model_rgb.pth', + depth='https://github.com/hassony2/kinetics_i3d_pytorch/' + 'raw/master/model/model_rgb.pth', + ), + backbone=dict( + rgb=dict( + type='I3D', + in_channels=3, + expansion=1, + ), + depth=dict( + type='I3D', + in_channels=1, + expansion=1, + ), + ), + cls_head=dict( + type='MultiModalSSAHead', + num_classes=25, + ), + train_cfg=dict( + beta=2, + lambda_=5e-3, + ssa_start_epoch=61, + ), + test_cfg=dict(), +) + +data_root = 'data/nvgesture' +data_cfg = dict( + video_size=[320, 240], + modality=['rgb', 'depth'], + bbox_file=f'{data_root}/annotations/bboxes.json', +) + +train_pipeline = [ + dict(type='LoadVideoFromFile'), + dict(type='ModalWiseChannelProcess'), + dict(type='CropValidClip'), + dict(type='TemporalPooling', length=64, ref_fps=30), + dict(type='MultiFrameBBoxMerge'), + dict( + type='ResizedCropByBBox', + size=224, + scale=(0.8, 1.25), + ratio=(0.75, 1.33), + shift=0.3), + dict(type='GestureRandomFlip'), + dict(type='MultiModalVideoToTensor'), + dict( + type='VideoNormalizeTensor', + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]), + dict( + type='Collect', keys=['video', 'label'], meta_keys=['fps', + 'modality']), +] + +val_pipeline = [ + dict(type='LoadVideoFromFile'), + dict(type='ModalWiseChannelProcess'), + dict(type='CropValidClip'), + dict(type='TemporalPooling', length=-1, ref_fps=30), + dict(type='MultiFrameBBoxMerge'), + dict(type='ResizedCropByBBox', size=224), + dict(type='MultiModalVideoToTensor'), + dict( + type='VideoNormalizeTensor', + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]), + dict( + type='Collect', keys=['video', 'label'], meta_keys=['fps', + 'modality']), +] + +test_pipeline = val_pipeline + +data = dict( + samples_per_gpu=6, + workers_per_gpu=2, + val_dataloader=dict(samples_per_gpu=6), + test_dataloader=dict(samples_per_gpu=6), + train=dict( + type='NVGestureDataset', + ann_file=f'{data_root}/annotations/' + 'nvgesture_train_correct_cvpr2016_v2.lst', + vid_prefix=f'{data_root}/', + data_cfg=data_cfg, + pipeline=train_pipeline, + dataset_info={{_base_.dataset_info}}), + val=dict( + type='NVGestureDataset', + ann_file=f'{data_root}/annotations/' + 'nvgesture_test_correct_cvpr2016_v2.lst', + vid_prefix=f'{data_root}/', + data_cfg=data_cfg, + pipeline=val_pipeline, + test_mode=True, + dataset_info={{_base_.dataset_info}}), + test=dict( + type='NVGestureDataset', + ann_file=f'{data_root}/annotations/' + 'nvgesture_test_correct_cvpr2016_v2.lst', + vid_prefix=f'{data_root}/', + data_cfg=data_cfg, + pipeline=test_pipeline, + test_mode=True, + dataset_info={{_base_.dataset_info}})) diff --git a/demo/docs/mmdet_modelzoo.md b/demo/docs/mmdet_modelzoo.md index a1d9a025aa..ae6b24df28 100644 --- a/demo/docs/mmdet_modelzoo.md +++ b/demo/docs/mmdet_modelzoo.md @@ -14,6 +14,7 @@ For hand bounding box detection, we simply train our hand box models on onehand1 | Arch | Box AP | ckpt | log | | :---------------------------------------------------------------- | :----: | :---------------------------------------------------------------: | :--------------------------------------------------------------: | | [Cascade_R-CNN X-101-64x4d-FPN-1class](/demo/mmdetection_cfg/cascade_rcnn_x101_64x4d_fpn_1class.py) | 0.817 | [ckpt](https://download.openmmlab.com/mmpose/mmdet_pretrained/cascade_rcnn_x101_64x4d_fpn_20e_onehand10k-dac19597_20201030.pth) | [log](https://download.openmmlab.com/mmpose/mmdet_pretrained/cascade_rcnn_x101_64x4d_fpn_20e_onehand10k_20201030.log.json) | +| [ssdlite_mobilenetv2-1class](/demo/mmdetection_cfg/ssdlite_mobilenetv2_scratch_600e_onehand.py) | 0.779 | [ckpt](https://download.openmmlab.com/mmpose/mmdet_pretrained/ssdlite_mobilenetv2_scratch_600e_onehand-4f9f8686_20220523.pth) | [log](https://download.openmmlab.com/mmpose/mmdet_pretrainedssdlite_mobilenetv2_scratch_600e_onehand_20220523.log.json) | ### Animal Bounding Box Detection Models diff --git a/demo/mmdetection_cfg/ssdlite_mobilenetv2_scratch_600e_onehand.py b/demo/mmdetection_cfg/ssdlite_mobilenetv2_scratch_600e_onehand.py new file mode 100644 index 0000000000..b7e37964cf --- /dev/null +++ b/demo/mmdetection_cfg/ssdlite_mobilenetv2_scratch_600e_onehand.py @@ -0,0 +1,240 @@ +# ========================================================= +# from 'mmdetection/configs/_base_/default_runtime.py' +# ========================================================= +checkpoint_config = dict(interval=1) +# yapf:disable +log_config = dict( + interval=50, + hooks=[ + dict(type='TextLoggerHook'), + # dict(type='TensorboardLoggerHook') + ]) +# yapf:enable +custom_hooks = [dict(type='NumClassCheckHook')] +# ========================================================= + +# ========================================================= +# from 'mmdetection/configs/_base_/datasets/coco_detection.py' +# ========================================================= +# dataset settings +dataset_type = 'CocoDataset' +data_root = 'data/coco/' +img_norm_cfg = dict( + mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) +train_pipeline = [ + dict(type='LoadImageFromFile'), + dict(type='LoadAnnotations', with_bbox=True), + dict(type='Resize', img_scale=(1333, 800), keep_ratio=True), + dict(type='RandomFlip', flip_ratio=0.5), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size_divisor=32), + dict(type='DefaultFormatBundle'), + dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']), +] +test_pipeline = [ + dict(type='LoadImageFromFile'), + dict( + type='MultiScaleFlipAug', + img_scale=(1333, 800), + flip=False, + transforms=[ + dict(type='Resize', keep_ratio=True), + dict(type='RandomFlip'), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size_divisor=32), + dict(type='ImageToTensor', keys=['img']), + dict(type='Collect', keys=['img']), + ]) +] +data = dict( + samples_per_gpu=2, + workers_per_gpu=2, + train=dict( + type=dataset_type, + ann_file=data_root + 'annotations/instances_train2017.json', + img_prefix=data_root + 'train2017/', + pipeline=train_pipeline), + val=dict( + type=dataset_type, + ann_file=data_root + 'annotations/instances_val2017.json', + img_prefix=data_root + 'val2017/', + pipeline=test_pipeline), + test=dict( + type=dataset_type, + ann_file=data_root + 'annotations/instances_val2017.json', + img_prefix=data_root + 'val2017/', + pipeline=test_pipeline)) +evaluation = dict(interval=1, metric='bbox') +# ========================================================= + +model = dict( + type='SingleStageDetector', + backbone=dict( + type='MobileNetV2', + out_indices=(4, 7), + norm_cfg=dict(type='BN', eps=0.001, momentum=0.03), + init_cfg=dict(type='TruncNormal', layer='Conv2d', std=0.03)), + neck=dict( + type='SSDNeck', + in_channels=(96, 1280), + out_channels=(96, 1280, 512, 256, 256, 128), + level_strides=(2, 2, 2, 2), + level_paddings=(1, 1, 1, 1), + l2_norm_scale=None, + use_depthwise=True, + norm_cfg=dict(type='BN', eps=0.001, momentum=0.03), + act_cfg=dict(type='ReLU6'), + init_cfg=dict(type='TruncNormal', layer='Conv2d', std=0.03)), + bbox_head=dict( + type='SSDHead', + in_channels=(96, 1280, 512, 256, 256, 128), + num_classes=1, + use_depthwise=True, + norm_cfg=dict(type='BN', eps=0.001, momentum=0.03), + act_cfg=dict(type='ReLU6'), + init_cfg=dict(type='Normal', layer='Conv2d', std=0.001), + + # set anchor size manually instead of using the predefined + # SSD300 setting. + anchor_generator=dict( + type='SSDAnchorGenerator', + scale_major=False, + strides=[16, 32, 64, 107, 160, 320], + ratios=[[2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3]], + min_sizes=[48, 100, 150, 202, 253, 304], + max_sizes=[100, 150, 202, 253, 304, 320]), + bbox_coder=dict( + type='DeltaXYWHBBoxCoder', + target_means=[.0, .0, .0, .0], + target_stds=[0.1, 0.1, 0.2, 0.2])), + # model training and testing settings + train_cfg=dict( + assigner=dict( + type='MaxIoUAssigner', + pos_iou_thr=0.5, + neg_iou_thr=0.5, + min_pos_iou=0., + ignore_iof_thr=-1, + gt_max_assign_all=False), + smoothl1_beta=1., + allowed_border=-1, + pos_weight=-1, + neg_pos_ratio=3, + debug=False), + test_cfg=dict( + nms_pre=1000, + nms=dict(type='nms', iou_threshold=0.45), + min_bbox_size=0, + score_thr=0.02, + max_per_img=200)) +cudnn_benchmark = True + +# dataset settings +file_client_args = dict( + backend='petrel', + path_mapping=dict({ + '.data/onehand10k/': + 'openmmlab:s3://openmmlab/datasets/pose/OneHand10K/', + 'data/onehand10k/': + 'openmmlab:s3://openmmlab/datasets/pose/OneHand10K/' + })) + +dataset_type = 'CocoDataset' +data_root = 'data/onehand10k/' +classes = ('hand', ) +img_norm_cfg = dict( + mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) +train_pipeline = [ + dict(type='LoadImageFromFile', file_client_args=file_client_args), + dict(type='LoadAnnotations', with_bbox=True), + dict( + type='Expand', + mean=img_norm_cfg['mean'], + to_rgb=img_norm_cfg['to_rgb'], + ratio_range=(1, 4)), + dict( + type='MinIoURandomCrop', + min_ious=(0.1, 0.3, 0.5, 0.7, 0.9), + min_crop_size=0.3), + dict(type='Resize', img_scale=(320, 320), keep_ratio=False), + dict(type='RandomFlip', flip_ratio=0.5), + dict( + type='PhotoMetricDistortion', + brightness_delta=32, + contrast_range=(0.5, 1.5), + saturation_range=(0.5, 1.5), + hue_delta=18), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size_divisor=320), + dict(type='DefaultFormatBundle'), + dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']), +] +test_pipeline = [ + dict(type='LoadImageFromFile', file_client_args=file_client_args), + dict( + type='MultiScaleFlipAug', + img_scale=(320, 320), + flip=False, + transforms=[ + dict(type='Resize', keep_ratio=False), + dict(type='Normalize', **img_norm_cfg), + dict(type='Pad', size_divisor=320), + dict(type='ImageToTensor', keys=['img']), + dict(type='Collect', keys=['img']), + ]) +] +data = dict( + samples_per_gpu=24, + workers_per_gpu=4, + train=dict( + _delete_=True, + type='RepeatDataset', # use RepeatDataset to speed up training + times=5, + dataset=dict( + type=dataset_type, + ann_file=data_root + 'annotations/onehand10k_train.json', + img_prefix=data_root, + classes=classes, + pipeline=train_pipeline)), + val=dict( + ann_file=data_root + 'annotations/onehand10k_test.json', + img_prefix=data_root, + classes=classes, + pipeline=test_pipeline), + test=dict( + ann_file=data_root + 'annotations/onehand10k_test.json', + img_prefix=data_root, + classes=classes, + pipeline=test_pipeline)) + +# optimizer +optimizer = dict(type='SGD', lr=0.015, momentum=0.9, weight_decay=4.0e-5) +optimizer_config = dict(grad_clip=None) + +# learning policy +lr_config = dict( + policy='CosineAnnealing', + warmup='linear', + warmup_iters=500, + warmup_ratio=0.001, + min_lr=0) +runner = dict(type='EpochBasedRunner', max_epochs=120) + +# Avoid evaluation and saving weights too frequently +evaluation = dict(interval=5, metric='bbox') +checkpoint_config = dict(interval=5) +custom_hooks = [ + dict(type='NumClassCheckHook'), + dict(type='CheckInvalidLossHook', interval=50, priority='VERY_LOW') +] + +log_config = dict(interval=5) + +# NOTE: `auto_scale_lr` is for automatically scaling LR, +# USER SHOULD NOT CHANGE ITS VALUES. +# base_batch_size = (8 GPUs) x (24 samples per GPU) +auto_scale_lr = dict(base_batch_size=192) + +load_from = 'https://download.openmmlab.com/mmdetection/' +'v2.0/ssd/ssdlite_mobilenetv2_scratch_600e_coco/' +'ssdlite_mobilenetv2_scratch_600e_coco_20210629_110627-974d9307.pth' diff --git a/docs/en/papers/algorithms/mtut.md b/docs/en/papers/algorithms/mtut.md new file mode 100644 index 0000000000..7cfefeef2f --- /dev/null +++ b/docs/en/papers/algorithms/mtut.md @@ -0,0 +1,30 @@ +# Improving the Performance of Unimodal Dynamic Hand-Gesture Recognition with Multimodal Training + + + +
+MTUT (CVPR'2019) + +```bibtex +@InProceedings{Abavisani_2019_CVPR, +author = {Abavisani, Mahdi and Joze, Hamid Reza Vaezi and Patel, Vishal M.}, +title = {Improving the Performance of Unimodal Dynamic Hand-Gesture Recognition With Multimodal Training}, +booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)}, +month = {June}, +year = {2019} +} +``` + +
+ +## Abstract + + + +We present an efficient approach for leveraging the knowledge from multiple modalities in training unimodal 3D convolutional neural networks (3D-CNNs) for the task of dynamic hand gesture recognition. Instead of explicitly combining multimodal information, which is commonplace in many state-of-the-art methods, we propose a different framework in which we embed the knowledge of multiple modalities in individual networks so that each unimodal network can achieve an improved performance. In particular, we dedicate separate networks per available modality and enforce them to collaborate and learn to develop networks with common semantics and better representations. We introduce a "spatiotemporal semantic alignment" loss (SSA) to align the content of the features from different networks. In addition, we regularize this loss with our proposed "focal regularization parameter" to avoid negative knowledge transfer. Experimental results show that our framework improves the test time recognition accuracy of unimodal networks, and provides the state-of-the-art performance on various dynamic hand gesture recognition datasets. + + + +
+ +
diff --git a/docs/en/papers/backbones/i3d.md b/docs/en/papers/backbones/i3d.md new file mode 100644 index 0000000000..b1a51d1400 --- /dev/null +++ b/docs/en/papers/backbones/i3d.md @@ -0,0 +1,30 @@ +# Quo Vadis, Action Recognition? A New Model and the Kinetics Dataset + + + +
+I3D (CVPR'2017) + +```bibtex +@InProceedings{Carreira_2017_CVPR, + author = {Carreira, Joao and Zisserman, Andrew}, + title = {Quo Vadis, Action Recognition? A New Model and the Kinetics Dataset}, + booktitle = {Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR)}, + month = {July}, + year = {2017} +} +``` + +
+ +## Abstract + + + +The paucity of videos in current action classification datasets (UCF-101 and HMDB-51) has made it difficult to identify good video architectures, as most methods obtain similar performance on existing small-scale benchmarks. This paper re-evaluates state-of-the-art architectures in light of the new Kinetics Human Action Video dataset. Kinetics has two orders of magnitude more data, with 400 human action classes and over 400 clips per class, and is collected from realistic, challenging YouTube videos. We provide an analysis on how current architectures fare on the task of action classification on this dataset and how much performance improves on the smaller benchmark datasets after pre-training on Kinetics. We also introduce a new Two-Stream Inflated 3D ConvNet (I3D) that is based on 2D ConvNet inflation: filters and pooling kernels of very deep image classification ConvNets are expanded into 3D, making it possible to learn seamless spatio-temporal feature extractors from video while leveraging successful ImageNet architecture designs and even their parameters. We show that, after pre-training on Kinetics, I3D models considerably improve upon the state-of-the-art in action classification, reaching 80.2% on HMDB-51 and 97.9% on UCF-101. + + + +
+ +
diff --git a/docs/en/papers/datasets/nvgesture.md b/docs/en/papers/datasets/nvgesture.md new file mode 100644 index 0000000000..772cda9483 --- /dev/null +++ b/docs/en/papers/datasets/nvgesture.md @@ -0,0 +1,18 @@ +# Online Detection and Classification of Dynamic Hand Gestures With Recurrent 3D Convolutional Neural Network + + + +
+NVGesture (CVPR'2016) + +```bibtex +@InProceedings{Molchanov_2016_CVPR, + author = {Molchanov, Pavlo and Yang, Xiaodong and Gupta, Shalini and Kim, Kihwan and Tyree, Stephen and Kautz, Jan}, + title = {Online Detection and Classification of Dynamic Hand Gestures With Recurrent 3D Convolutional Neural Network}, + booktitle = {Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR)}, + month = {June}, + year = {2016} +} +``` + +
diff --git a/docs/en/tasks/2d_hand_gesture.md b/docs/en/tasks/2d_hand_gesture.md new file mode 100644 index 0000000000..6161b401f7 --- /dev/null +++ b/docs/en/tasks/2d_hand_gesture.md @@ -0,0 +1,60 @@ +# 2D Hand Keypoint Datasets + +It is recommended to symlink the dataset root to `$MMPOSE/data`. +If your folder structure is different, you may need to change the corresponding paths in config files. + +MMPose supported datasets: + +- [NVGesture](#nvgesture) \[ [Homepage](https://www.v7labs.com/open-datasets/nvgesture) \] + +## NVGesture + + + +
+OneHand10K (CVPR'2016) + +```bibtex +@inproceedings{molchanov2016online, + title={Online detection and classification of dynamic hand gestures with recurrent 3d convolutional neural network}, + author={Molchanov, Pavlo and Yang, Xiaodong and Gupta, Shalini and Kim, Kihwan and Tyree, Stephen and Kautz, Jan}, + booktitle={Proceedings of the IEEE conference on computer vision and pattern recognition}, + pages={4207--4215}, + year={2016} +} +``` + +
+ +For [NVGesture](https://www.v7labs.com/open-datasets/nvgesture) data and annotation, please download from [NVGesture Dataset](https://drive.google.com/drive/folders/0ByhYoRYACz9cMUk0QkRRMHM3enc?resourcekey=0-cJe9M3PZy2qCbfGmgpFrHQ&usp=sharing). +Extract them under {MMPose}/data, and make them look like this: + +```text +mmpose +├── mmpose +├── docs +├── tests +├── tools +├── configs +`── data + │── nvgesture + |── annotations + | |── nvgesture_train_correct_cvpr2016_v2.lst + | |── nvgesture_test_correct_cvpr2016_v2.lst + | ... + `── Video_data + |── class_01 + | |── subject1_r0 + | | |── sk_color.avi + | | |── sk_depth.avi + | | ... + | |── subject1_r1 + | |── subject2_r0 + | ... + |── class_02 + |── class_03 + ... + +``` + +The hand bounding box is computed by the hand detection model described in [det model zoo](/demo/docs/mmdet_modelzoo.md). The detected bounding box can be downloaded from [GoogleDrive](https://drive.google.com/drive/folders/1AGOeX0iHhaigBVRicjetieNRC7Zctuz4?usp=sharing). It is recommended to place it at `data/nvgesture/annotations/bboxes.json`. diff --git a/mmpose/apis/__init__.py b/mmpose/apis/__init__.py index 772341fb02..2a7f70fbec 100644 --- a/mmpose/apis/__init__.py +++ b/mmpose/apis/__init__.py @@ -1,7 +1,8 @@ # Copyright (c) OpenMMLab. All rights reserved. from .inference import (collect_multi_frames, inference_bottom_up_pose_model, - inference_top_down_pose_model, init_pose_model, - process_mmdet_results, vis_pose_result) + inference_gesture_model, inference_top_down_pose_model, + init_pose_model, process_mmdet_results, + vis_pose_result) from .inference_3d import (extract_pose_sequence, inference_interhand_3d_model, inference_mesh_model, inference_pose_lifter_model, vis_3d_mesh_result, vis_3d_pose_result) @@ -10,11 +11,23 @@ from .train import init_random_seed, train_model __all__ = [ - 'train_model', 'init_pose_model', 'inference_top_down_pose_model', - 'inference_bottom_up_pose_model', 'multi_gpu_test', 'single_gpu_test', - 'vis_pose_result', 'get_track_id', 'vis_pose_tracking_result', - 'inference_pose_lifter_model', 'vis_3d_pose_result', - 'inference_interhand_3d_model', 'extract_pose_sequence', - 'inference_mesh_model', 'vis_3d_mesh_result', 'process_mmdet_results', - 'init_random_seed', 'collect_multi_frames' + 'train_model', + 'init_pose_model', + 'inference_top_down_pose_model', + 'inference_bottom_up_pose_model', + 'multi_gpu_test', + 'single_gpu_test', + 'vis_pose_result', + 'get_track_id', + 'vis_pose_tracking_result', + 'inference_pose_lifter_model', + 'vis_3d_pose_result', + 'inference_interhand_3d_model', + 'extract_pose_sequence', + 'inference_mesh_model', + 'vis_3d_mesh_result', + 'process_mmdet_results', + 'init_random_seed', + 'collect_multi_frames', + 'inference_gesture_model', ] diff --git a/mmpose/apis/inference.py b/mmpose/apis/inference.py index 6d7f4dacaf..20c6d0d007 100644 --- a/mmpose/apis/inference.py +++ b/mmpose/apis/inference.py @@ -2,6 +2,7 @@ import copy import os import warnings +from collections import defaultdict import mmcv import numpy as np @@ -800,6 +801,61 @@ def vis_pose_result(model, return img +def inference_gesture_model( + model, + videos_or_paths, + bboxes=None, + dataset_info=None, +): + + cfg = model.cfg + device = next(model.parameters()).device + if device.type == 'cpu': + device = -1 + + # build the data pipeline + test_pipeline = Compose(cfg.test_pipeline) + _pipeline_gpu_speedup(test_pipeline, next(model.parameters()).device) + + # data preprocessing + data = defaultdict(list) + data['label'] = -1 + + if not isinstance(videos_or_paths, (tuple, list)): + videos_or_paths = [videos_or_paths] + if isinstance(videos_or_paths[0], str): + data['video_file'] = videos_or_paths + else: + data['video'] = videos_or_paths + + if bboxes is not None: + data['bbox'] = bboxes + + if isinstance(dataset_info, dict): + data['modality'] = dataset_info.get('modality', ['rgb']) + data['fps'] = dataset_info.get('fps', None) + if not isinstance(data['fps'], (tuple, list)): + data['fps'] = [data['fps']] + + data = test_pipeline(data) + batch_data = collate([data], samples_per_gpu=1) + batch_data = scatter(batch_data, [device])[0] + + # inference + with torch.no_grad(): + output = model.forward(return_loss=False, **batch_data) + scores = [] + for modal, logit in output['logits'].items(): + while logit.ndim > 2: + logit = logit.mean(dim=2) + score = torch.softmax(logit, dim=1) + scores.append(score) + score = torch.stack(scores, dim=2).mean(dim=2) + pred_score, pred_label = torch.max(score, dim=1) + + return pred_label, pred_score + + def process_mmdet_results(mmdet_results, cat_id=1): """Process mmdet results, and return a list of bboxes. diff --git a/mmpose/core/utils/__init__.py b/mmpose/core/utils/__init__.py index d059d21422..512e7680bc 100644 --- a/mmpose/core/utils/__init__.py +++ b/mmpose/core/utils/__init__.py @@ -1,5 +1,9 @@ # Copyright (c) OpenMMLab. All rights reserved. from .dist_utils import allreduce_grads, sync_random_seed +from .model_util_hooks import ModelSetEpochHook from .regularizations import WeightNormClipHook -__all__ = ['allreduce_grads', 'WeightNormClipHook', 'sync_random_seed'] +__all__ = [ + 'allreduce_grads', 'WeightNormClipHook', 'sync_random_seed', + 'ModelSetEpochHook' +] diff --git a/mmpose/core/utils/model_util_hooks.py b/mmpose/core/utils/model_util_hooks.py new file mode 100644 index 0000000000..d308a8a57a --- /dev/null +++ b/mmpose/core/utils/model_util_hooks.py @@ -0,0 +1,13 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from mmcv.runner import HOOKS, Hook + + +@HOOKS.register_module() +class ModelSetEpochHook(Hook): + """The hook that tells model the current epoch in training.""" + + def __init__(self): + pass + + def before_epoch(self, runner): + runner.model.module.set_train_epoch(runner.epoch + 1) diff --git a/mmpose/datasets/__init__.py b/mmpose/datasets/__init__.py index 650d7e9480..fd58f3ea6d 100644 --- a/mmpose/datasets/__init__.py +++ b/mmpose/datasets/__init__.py @@ -21,7 +21,7 @@ TopDownOCHumanDataset, TopDownOneHand10KDataset, TopDownPanopticDataset, TopDownPoseTrack18Dataset, TopDownPoseTrack18VideoDataset, Body3DMviewDirectPanopticDataset, Body3DMviewDirectShelfDataset, - Body3DMviewDirectCampusDataset) + Body3DMviewDirectCampusDataset, NVGestureDataset) __all__ = [ 'TopDownCocoDataset', 'BottomUpCocoDataset', 'BottomUpMhpDataset', @@ -42,5 +42,5 @@ 'TopDownPoseTrack18VideoDataset', 'build_dataloader', 'build_dataset', 'Compose', 'DistributedSampler', 'DATASETS', 'PIPELINES', 'DatasetInfo', 'Body3DMviewDirectPanopticDataset', 'Body3DMviewDirectShelfDataset', - 'Body3DMviewDirectCampusDataset' + 'Body3DMviewDirectCampusDataset', 'NVGestureDataset' ] diff --git a/mmpose/datasets/datasets/__init__.py b/mmpose/datasets/datasets/__init__.py index 603f840206..f44fc8e198 100644 --- a/mmpose/datasets/datasets/__init__.py +++ b/mmpose/datasets/datasets/__init__.py @@ -13,6 +13,7 @@ from .face import (Face300WDataset, FaceAFLWDataset, FaceCocoWholeBodyDataset, FaceCOFWDataset, FaceWFLWDataset) from .fashion import DeepFashionDataset +from .gesture import NVGestureDataset from .hand import (FreiHandDataset, HandCocoWholeBodyDataset, InterHand2DDataset, InterHand3DDataset, OneHand10KDataset, PanopticDataset) @@ -44,5 +45,5 @@ 'AnimalATRWDataset', 'AnimalPoseDataset', 'TopDownH36MDataset', 'TopDownHalpeDataset', 'TopDownPoseTrack18VideoDataset', 'Body3DMviewDirectPanopticDataset', 'Body3DMviewDirectShelfDataset', - 'Body3DMviewDirectCampusDataset' + 'Body3DMviewDirectCampusDataset', 'NVGestureDataset' ] diff --git a/mmpose/datasets/datasets/gesture/__init__.py b/mmpose/datasets/datasets/gesture/__init__.py new file mode 100644 index 0000000000..22c85afd7c --- /dev/null +++ b/mmpose/datasets/datasets/gesture/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) OpenMMLab. All rights reserved. +from .nvgesture_dataset import NVGestureDataset + +__all__ = ['NVGestureDataset'] diff --git a/mmpose/datasets/datasets/gesture/gesture_base_dataset.py b/mmpose/datasets/datasets/gesture/gesture_base_dataset.py new file mode 100644 index 0000000000..e81d972ece --- /dev/null +++ b/mmpose/datasets/datasets/gesture/gesture_base_dataset.py @@ -0,0 +1,86 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import copy +from abc import ABCMeta, abstractmethod + +import numpy as np +from torch.utils.data import Dataset + +from mmpose.datasets.pipelines import Compose + + +class GestureBaseDataset(Dataset, metaclass=ABCMeta): + """Base class for gesture recognition datasets with Multi-Modal video as + the input. + + All gesture datasets should subclass it. + All subclasses should overwrite: + Methods:`_get_single`, 'evaluate' + + Args: + ann_file (str): Path to the annotation file. + vid_prefix (str): Path to a directory where videos are held. + data_cfg (dict): config + pipeline (list[dict | callable]): A sequence of data transforms. + dataset_info (DatasetInfo): A class containing all dataset info. + test_mode (bool): Store True when building test or + validation dataset. Default: False. + """ + + def __init__(self, + ann_file, + vid_prefix, + data_cfg, + pipeline, + dataset_info=None, + test_mode=False): + + self.video_info = {} + self.ann_info = {} + + self.ann_file = ann_file + self.vid_prefix = vid_prefix + self.pipeline = pipeline + self.test_mode = test_mode + + self.ann_info['video_size'] = np.array(data_cfg['video_size']) + self.ann_info['flip_pairs'] = dataset_info.flip_pairs + self.modality = data_cfg['modality'] + if isinstance(self.modality, (list, tuple)): + self.modality = self.modality + else: + self.modality = (self.modality, ) + self.bbox_file = data_cfg.get('bbox_file', None) + self.dataset_name = dataset_info.dataset_name + self.pipeline = Compose(self.pipeline) + + @abstractmethod + def _get_single(self, idx): + """Get anno for a single video.""" + raise NotImplementedError + + @abstractmethod + def evaluate(self, results, *args, **kwargs): + """Evaluate recognition results.""" + + def prepare_train_vid(self, idx): + """Prepare video for training given the index.""" + results = copy.deepcopy(self._get_single(idx)) + results['ann_info'] = self.ann_info + return self.pipeline(results) + + def prepare_test_vid(self, idx): + """Prepare video for testing given the index.""" + results = copy.deepcopy(self._get_single(idx)) + results['ann_info'] = self.ann_info + return self.pipeline(results) + + def __len__(self): + """Get dataset length.""" + return len(self.vid_ids) + + def __getitem__(self, idx): + """Get the sample for either training or testing given index.""" + if self.test_mode: + return self.prepare_test_vid(idx) + + return self.prepare_train_vid(idx) diff --git a/mmpose/datasets/datasets/gesture/nvgesture_dataset.py b/mmpose/datasets/datasets/gesture/nvgesture_dataset.py new file mode 100644 index 0000000000..83f5e0df06 --- /dev/null +++ b/mmpose/datasets/datasets/gesture/nvgesture_dataset.py @@ -0,0 +1,185 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import os +import os.path as osp +import tempfile +import warnings +from collections import defaultdict + +import json_tricks as json +import numpy as np +from mmcv import Config + +from ...builder import DATASETS +from .gesture_base_dataset import GestureBaseDataset + + +@DATASETS.register_module() +class NVGestureDataset(GestureBaseDataset): + """NVGesture dataset for gesture recognition. + + "Online Detection and Classification of Dynamic Hand Gestures + With Recurrent 3D Convolutional Neural Network", + Conference on Computer Vision and Pattern Recognition (CVPR) 2016. + + The dataset loads raw videos and apply specified transforms + to return a dict containing the image tensors and other information. + + Args: + ann_file (str): Path to the annotation file. + vid_prefix (str): Path to a directory where videos are held. + data_cfg (dict): config + pipeline (list[dict | callable]): A sequence of data transforms. + dataset_info (DatasetInfo): A class containing all dataset info. + test_mode (bool): Store True when building test or + validation dataset. Default: False. + """ + + def __init__(self, + ann_file, + vid_prefix, + data_cfg, + pipeline, + dataset_info=None, + test_mode=False): + + if dataset_info is None: + warnings.warn( + 'dataset_info is missing. ' + 'Check https://github.com/open-mmlab/mmpose/pull/663 ' + 'for details.', DeprecationWarning) + cfg = Config.fromfile('configs/_base_/datasets/nvgesture.py') + dataset_info = cfg._cfg_dict['dataset_info'] + + super().__init__( + ann_file, + vid_prefix, + data_cfg, + pipeline, + dataset_info=dataset_info, + test_mode=test_mode) + + self.db = self._get_db() + self.vid_ids = list(range(len(self.db))) + print(f'=> load {len(self.db)} samples') + + def _get_db(self): + """Load dataset.""" + db = [] + with open(self.ann_file, 'r') as f: + samples = f.readlines() + + use_bbox = bool(self.bbox_file) + if use_bbox: + with open(self.bbox_file, 'r') as f: + bboxes = json.load(f) + + for sample in samples: + sample = sample.strip().split() + sample = { + item.split(':', 1)[0]: item.split(':', 1)[1] + for item in sample + } + path = sample['path'][2:] + for key in ('depth', 'color'): + fname, start, end = sample[key].split(':') + sample[key] = { + 'path': os.path.join(path, fname + '.avi'), + 'valid_frames': (eval(start), eval(end)) + } + sample['flow'] = { + 'path': sample['color']['path'].replace('color', 'flow'), + 'valid_frames': sample['color']['valid_frames'] + } + sample['rgb'] = sample['color'] + sample['label'] = eval(sample['label']) - 1 + + if use_bbox: + sample['bbox'] = bboxes[path] + + del sample['path'], sample['duo_left'], sample['color'] + db.append(sample) + + return db + + def _get_single(self, idx): + """Get anno for a single video.""" + anno = defaultdict(list) + sample = self.db[self.vid_ids[idx]] + + anno['label'] = sample['label'] + anno['modality'] = self.modality + if 'bbox' in sample: + anno['bbox'] = sample['bbox'] + + for modal in self.modality: + anno['video_file'].append( + os.path.join(self.vid_prefix, sample[modal]['path'])) + anno['valid_frames'].append(sample[modal]['valid_frames']) + + return anno + + def evaluate(self, results, res_folder=None, metric='AP', **kwargs): + """Evaluate nvgesture recognition results. The gesture prediction + results will be saved in ``${res_folder}/result_gesture.json``. + + Note: + - batch_size: N + - heatmap length: L + + Args: + results (dict): Testing results containing the following + items: + - logits (dict[str, torch.tensor[N,25,L]]): For each item, \ + the key represents the modality of input video, while \ + the value represents the prediction of gesture. Three \ + dimensions represent batch, category and temporal \ + length, respectively. + - label (np.ndarray[N]): [center[0], center[1], scale[0], \ + scale[1],area, score] + res_folder (str, optional): The folder to save the testing + results. If not specified, a temp folder will be created. + Default: None. + metric (str | list[str]): Metric to be performed. + Options: 'AP'. + + Returns: + dict: Evaluation results for evaluation metric. + """ + metrics = metric if isinstance(metric, list) else [metric] + allowed_metrics = ['AP'] + for metric in metrics: + if metric not in allowed_metrics: + raise KeyError(f'metric {metric} is not supported') + + if res_folder is not None: + tmp_folder = None + res_file = osp.join(res_folder, 'result_gesture.json') + else: + tmp_folder = tempfile.TemporaryDirectory() + res_file = osp.join(tmp_folder.name, 'result_gesture.json') + + predictions = defaultdict(list) + label = [] + for result in results: + label.append(result['label'].cpu().numpy()) + for modal in result['logits']: + logit = result['logits'][modal].mean(dim=2) + pred = logit.argmax(dim=1).cpu().numpy() + predictions[modal].append(pred) + + label = np.concatenate(label, axis=0) + for modal in predictions: + predictions[modal] = np.concatenate(predictions[modal], axis=0) + + with open(res_file, 'w') as f: + json.dump(predictions, f, indent=4) + + results = dict() + if 'AP' in metrics: + APs = [] + for modal in predictions: + results[f'AP_{modal}'] = (predictions[modal] == label).mean() + APs.append(results[f'AP_{modal}']) + results['AP_mean'] = sum(APs) / len(APs) + + return results diff --git a/mmpose/datasets/pipelines/__init__.py b/mmpose/datasets/pipelines/__init__.py index cf06db1c9d..e619b339f6 100644 --- a/mmpose/datasets/pipelines/__init__.py +++ b/mmpose/datasets/pipelines/__init__.py @@ -1,7 +1,8 @@ # Copyright (c) OpenMMLab. All rights reserved. from .bottom_up_transform import * # noqa +from .gesture_transform import * # noqa from .hand_transform import * # noqa -from .loading import LoadImageFromFile # noqa +from .loading import * # noqa from .mesh_transform import * # noqa from .pose3d_transform import * # noqa from .shared_transform import * # noqa diff --git a/mmpose/datasets/pipelines/gesture_transform.py b/mmpose/datasets/pipelines/gesture_transform.py new file mode 100644 index 0000000000..28a3e568cc --- /dev/null +++ b/mmpose/datasets/pipelines/gesture_transform.py @@ -0,0 +1,414 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import mmcv +import numpy as np +import torch + +from mmpose.core import bbox_xywh2xyxy, bbox_xyxy2xywh +from mmpose.datasets.builder import PIPELINES + + +@PIPELINES.register_module() +class CropValidClip: + """Generate the clip from complete video with valid frames. + + Required keys: 'video', 'modality', 'valid_frames', 'num_frames'. + + Modified keys: 'video', 'valid_frames', 'num_frames'. + """ + + def __init__(self): + pass + + def __call__(self, results): + """Crop the valid part from the video.""" + if 'valid_frames' not in results: + results['valid_frames'] = [[0, n - 1] + for n in results['num_frames']] + lengths = [(end - start) for start, end in results['valid_frames']] + length = min(lengths) + for i, modal in enumerate(results['modality']): + start = results['valid_frames'][i][0] + results['video'][i] = results['video'][i][start:start + length] + results['num_frames'] = length + del results['valid_frames'] + if 'bbox' in results: + results['bbox'] = results['bbox'][start:start + length] + return results + + +@PIPELINES.register_module() +class TemporalPooling: + """Pick frames according to either stride or reference fps. + + Required keys: 'video', 'modality', 'num_frames', 'fps'. + + Modified keys: 'video', 'num_frames'. + + Args: + length (int): output video length. If unset, the entire video will + be pooled. + stride (int): temporal pooling stride. If unset, the stride will be + computed with video fps and `ref_fps`. If both `stride` and + `ref_fps` are unset, the stride will be 1. + ref_fps (int): expected fps of output video. If unset, the video will + be pooling with `stride`. + """ + + def __init__(self, length: int = -1, stride: int = -1, ref_fps: int = -1): + self.length = length + if stride == -1 and ref_fps == -1: + stride = 1 + elif stride != -1 and ref_fps != -1: + raise ValueError('`stride` and `ref_fps` can not be assigned ' + 'simultaneously, as they might conflict.') + self.stride = stride + self.ref_fps = ref_fps + + def __call__(self, results): + """Implement data aumentation with random temporal crop.""" + + if self.ref_fps > 0 and 'fps' in results: + assert len(set(results['fps'])) == 1, 'Videos of different ' + 'modality have different rate. May be misaligned after pooling.' + stride = results['fps'][0] // self.ref_fps + if stride < 1: + raise ValueError(f'`ref_fps` must be smaller than video ' + f"fps {results['fps'][0]}") + else: + stride = self.stride + + if self.length < 0: + length = results['num_frames'] + num_frames = (results['num_frames'] - 1) // stride + 1 + else: + length = (self.length - 1) * stride + 1 + num_frames = self.length + + diff = length - results['num_frames'] + start = np.random.randint(max(1 - diff, 1)) + + for i, modal in enumerate(results['modality']): + video = results['video'][i] + if diff > 0: + video = np.pad(video, ((diff // 2, diff - (diff // 2)), + *(((0, 0), ) * (video.ndim - 1))), + 'edge') + results['video'][i] = video[start:start + length:stride] + assert results['video'][i].shape[0] == num_frames + + results['num_frames'] = num_frames + if 'bbox' in results: + results['bbox'] = results['bbox'][start:start + length:stride] + return results + + +@PIPELINES.register_module() +class ResizeGivenShortEdge: + """Resize the video to make its short edge have given length. + + Required keys: 'video', 'modality', 'width', 'height'. + + Modified keys: 'video', 'width', 'height'. + """ + + def __init__(self, length: int = 256): + self.length = length + + def __call__(self, results): + """Implement data processing with resize given short edge.""" + for i, modal in enumerate(results['modality']): + width, height = results['width'][i], results['height'][i] + video = results['video'][i].transpose(1, 2, 3, 0) + num_frames = video.shape[-1] + video = video.reshape(height, width, -1) + if width < height: + width, height = self.length, int(self.length * height / width) + else: + width, height = int(self.length * width / height), self.length + video = mmcv.imresize(video, + (width, + height)).reshape(height, width, -1, + num_frames) + results['video'][i] = video.transpose(3, 0, 1, 2) + results['width'][i], results['height'][i] = width, height + return results + + +@PIPELINES.register_module() +class MultiFrameBBoxMerge: + """Compute the union of bboxes in selected frames. + + Required keys: 'bbox'. + + Modified keys: 'bbox'. + """ + + def __init__(self): + pass + + def __call__(self, results): + if 'bbox' not in results: + return results + + bboxes = list(filter(lambda x: len(x), results['bbox'])) + if len(bboxes) == 0: + bbox_xyxy = np.array( + (0, 0, results['width'][0] - 1, results['height'][0] - 1)) + else: + bboxes_xyxy = np.stack([b[0]['bbox'] for b in bboxes]) + bbox_xyxy = np.array(( + bboxes_xyxy[:, 0].min(), + bboxes_xyxy[:, 1].min(), + bboxes_xyxy[:, 2].max(), + bboxes_xyxy[:, 3].max(), + )) + results['bbox'] = bbox_xyxy + return results + + +@PIPELINES.register_module() +class ResizedCropByBBox: + """Spatial crop for spatially aligned videos by bounding box. + + Required keys: 'video', 'modality', 'width', 'height', 'bbox'. + + Modified keys: 'video', 'width', 'height'. + """ + + def __init__(self, size, scale=(1, 1), ratio=(1, 1), shift=0): + self.size = size if isinstance(size, (tuple, list)) else (size, size) + self.scale = scale + self.ratio = ratio + self.shift = shift + + def __call__(self, results): + bbox_xywh = bbox_xyxy2xywh(results['bbox'][None, :])[0] + length = bbox_xywh[2:].max() + length = length * np.random.uniform(*self.scale) + x = bbox_xywh[0] + np.random.uniform(-self.shift, self.shift) * length + y = bbox_xywh[1] + np.random.uniform(-self.shift, self.shift) * length + w, h = length, length * np.random.uniform(*self.ratio) + + bbox_xyxy = bbox_xywh2xyxy(np.array([[x, y, w, h]]))[0] + bbox_xyxy = bbox_xyxy.clip(min=0) + bbox_xyxy[2] = min(bbox_xyxy[2], results['width'][0]) + bbox_xyxy[3] = min(bbox_xyxy[3], results['height'][0]) + bbox_xyxy = bbox_xyxy.astype(np.int32) + + for i in range(len(results['video'])): + video = results['video'][i].transpose(1, 2, 3, 0) + num_frames = video.shape[-1] + video = video.reshape(video.shape[0], video.shape[1], -1) + video = mmcv.imcrop(video, bbox_xyxy) + video = mmcv.imresize(video, self.size) + + results['video'][i] = video.reshape(video.shape[0], video.shape[1], + -1, num_frames) + results['video'][i] = results['video'][i].transpose(3, 0, 1, 2) + results['width'][i], results['height'][i] = video.shape[ + 1], video.shape[0] + + return results + + +@PIPELINES.register_module() +class GestureRandomFlip: + """Data augmentation by randomly horizontal flip the video. The label will + be alternated simultaneously. + + Required keys: 'video', 'label', 'ann_info'. + + Modified keys: 'video', 'label'. + """ + + def __init__(self, prob=0.5): + self.flip_prob = prob + + def __call__(self, results): + flip = np.random.rand() < self.flip_prob + if flip: + for i in range(len(results['video'])): + results['video'][i] = results['video'][i][:, :, ::-1, :] + for flip_pairs in results['ann_info']['flip_pairs']: + if results['label'] in flip_pairs: + results['label'] = sum(flip_pairs) - results['label'] + break + + results['flipped'] = flip + return results + + +@PIPELINES.register_module() +class VideoColorJitter: + """Data augmentation with random color transformations. + + Required keys: 'video', 'modality'. + + Modified keys: 'video'. + """ + + def __init__(self, brightness=0, contrast=0): + self.brightness = brightness + self.contrast = contrast + + def __call__(self, results): + for i, modal in enumerate(results['modality']): + if modal == 'rgb': + video = results['video'][i] + bright = np.random.uniform( + max(0, 1 - self.brightness), 1 + self.brightness) + contrast = np.random.uniform( + max(0, 1 - self.contrast), 1 + self.contrast) + video = mmcv.adjust_brightness(video.astype(np.int32), bright) + num_frames = video.shape[0] + video = video.astype(np.uint8).reshape(-1, video.shape[2], 3) + video = mmcv.adjust_contrast(video, contrast).reshape( + num_frames, -1, video.shape[1], 3) + results['video'][i] = video + return results + + +@PIPELINES.register_module() +class RandomAlignedSpatialCrop: + """Data augmentation with random spatial crop for spatially aligned videos. + + Required keys: 'video', 'modality', 'width', 'height'. + + Modified keys: 'video', 'width', 'height'. + """ + + def __init__(self, length: int = 224): + self.length = length + + def __call__(self, results): + """Implement data augmentation with random spatial crop.""" + assert len(set(results['height'])) == 1, \ + f"the heights {results['height']} are not identical." + assert len(set(results['width'])) == 1, \ + f"the widths {results['width']} are not identical." + height, width = results['height'][0], results['width'][0] + for i, modal in enumerate(results['modality']): + video = results['video'][i].transpose(1, 2, 3, 0) + num_frames = video.shape[-1] + video = video.reshape(height, width, -1) + start_h, start_w = np.random.randint( + height - self.length + 1), np.random.randint(width - + self.length + 1) + video = mmcv.imcrop( + video, + np.array((start_w, start_h, start_w + self.length - 1, + start_h + self.length - 1))) + results['video'][i] = video.reshape(self.length, self.length, -1, + num_frames).transpose( + 3, 0, 1, 2) + results['width'][i], results['height'][ + i] = self.length, self.length + return results + + +@PIPELINES.register_module() +class CenterSpatialCrop: + """Data processing by crop the center region of a video. + + Required keys: 'video', 'modality', 'width', 'height'. + + Modified keys: 'video', 'width', 'height'. + """ + + def __init__(self, length: int = 224): + self.length = length + + def __call__(self, results): + """Implement data processing with center crop.""" + for i, modal in enumerate(results['modality']): + height, width = results['height'][i], results['width'][i] + video = results['video'][i].transpose(1, 2, 3, 0) + num_frames = video.shape[-1] + video = video.reshape(height, width, -1) + start_h, start_w = (height - self.length) // 2, (width - + self.length) // 2 + video = mmcv.imcrop( + video, + np.array((start_w, start_h, start_w + self.length - 1, + start_h + self.length - 1))) + results['video'][i] = video.reshape(self.length, self.length, -1, + num_frames).transpose( + 3, 0, 1, 2) + results['width'][i], results['height'][ + i] = self.length, self.length + return results + + +@PIPELINES.register_module() +class ModalWiseChannelProcess: + """Video channel processing according to modality. + + Required keys: 'video', 'modality'. + + Modified keys: 'video'. + """ + + def __init__(self): + pass + + def __call__(self, results): + """Implement channel processing for video array.""" + for i, modal in enumerate(results['modality']): + if modal == 'rgb': + results['video'][i] = results['video'][i][..., ::-1] + elif modal == 'depth': + if results['video'][i].ndim == 4: + results['video'][i] = results['video'][i][..., :1] + elif results['video'][i].ndim == 3: + results['video'][i] = results['video'][i][..., None] + elif modal == 'flow': + results['video'][i] = results['video'][i][..., :2] + else: + raise ValueError(f'modality {modal} is invalid.') + return results + + +@PIPELINES.register_module() +class MultiModalVideoToTensor: + """Data processing by converting video arrays to pytorch tensors. + + Required keys: 'video', 'modality'. + + Modified keys: 'video'. + """ + + def __init__(self): + pass + + def __call__(self, results): + """Implement data processing similar to ToTensor.""" + for i, modal in enumerate(results['modality']): + video = results['video'][i].transpose(3, 0, 1, 2) + results['video'][i] = torch.tensor( + np.ascontiguousarray(video), dtype=torch.float) / 255.0 + return results + + +@PIPELINES.register_module() +class VideoNormalizeTensor: + """Data processing by normalizing video tensors with mean and std. + + Required keys: 'video', 'modality'. + + Modified keys: 'video'. + """ + + def __init__(self, mean, std): + self.mean = torch.tensor(mean) + self.std = torch.tensor(std) + + def __call__(self, results): + """Implement data normalization.""" + for i, modal in enumerate(results['modality']): + if modal == 'rgb': + video = results['video'][i] + dim = video.ndim - 1 + video = video - self.mean.view(3, *((1, ) * dim)) + video = video / self.std.view(3, *((1, ) * dim)) + results['video'][i] = video + return results diff --git a/mmpose/datasets/pipelines/loading.py b/mmpose/datasets/pipelines/loading.py index d6374274ad..a19d220cc9 100644 --- a/mmpose/datasets/pipelines/loading.py +++ b/mmpose/datasets/pipelines/loading.py @@ -99,3 +99,81 @@ def __repr__(self): f"color_type='{self.color_type}', " f'file_client_args={self.file_client_args})') return repr_str + + +@PIPELINES.register_module() +class LoadVideoFromFile: + """Loading video(s) from file. + + Required key: "video_file". + + Added key: "video". + + Args: + to_float32 (bool): Whether to convert the loaded video to a float32 + numpy array. If set to False, the loaded video is an uint8 array. + Defaults to False. + file_client_args (dict): Arguments to instantiate a FileClient. + See :class:`mmcv.fileio.FileClient` for details. + Defaults to ``dict(backend='disk')``. + """ + + def __init__(self, + to_float32=False, + file_client_args=dict(backend='disk')): + self.to_float32 = to_float32 + self.file_client_args = file_client_args.copy() + self.file_client = None + + def _read_video(self, path): + container = mmcv.VideoReader(path) + sample = dict( + height=int(container.height), + width=int(container.width), + fps=int(container.fps), + num_frames=int(container.frame_cnt), + video=[]) + for _ in range(container.frame_cnt): + sample['video'].append(container.read()) + sample['video'] = np.stack(sample['video'], axis=0) + return sample + + def __call__(self, results): + """Loading video(s) from file.""" + if self.file_client is None: + self.file_client = mmcv.FileClient(**self.file_client_args) + + video_file = results.get('video_file', None) + + if isinstance(video_file, (list, tuple)): + # Load videos from a list of paths + for path in video_file: + video = self._read_video(path) + for key in video: + results[key].append(video[key]) + elif video_file is not None: + # Load single video from path + results.update(self._read_video(video_file)) + else: + if 'video' not in results: + # If `video_file`` is not in results, check the `video` exists + # and format the image. This for compatibility when the image + # is manually set outside the pipeline. + raise KeyError('Either `video_file` or `video` should exist ' + 'in results.') + if isinstance(results['video'], (list, tuple)): + assert isinstance(results['video'][0], np.ndarray) + else: + assert isinstance(results['video'], np.ndarray) + results['video'] = [results['video']] + + results['num_frames'] = [v.shape[0] for v in results['video']] + results['height'] = [v.shape[1] for v in results['video']] + results['width'] = [v.shape[2] for v in results['video']] + return results + + def __repr__(self): + repr_str = (f'{self.__class__.__name__}(' + f'to_float32={self.to_float32}, ' + f'file_client_args={self.file_client_args})') + return repr_str diff --git a/mmpose/models/backbones/__init__.py b/mmpose/models/backbones/__init__.py index cb2498560a..09745d443c 100644 --- a/mmpose/models/backbones/__init__.py +++ b/mmpose/models/backbones/__init__.py @@ -5,6 +5,7 @@ from .hourglass_ae import HourglassAENet from .hrformer import HRFormer from .hrnet import HRNet +from .i3d import I3D from .litehrnet import LiteHRNet from .mobilenet_v2 import MobileNetV2 from .mobilenet_v3 import MobileNetV3 @@ -33,5 +34,5 @@ 'SEResNet', 'SEResNeXt', 'ShuffleNetV1', 'ShuffleNetV2', 'CPM', 'RSN', 'MSPN', 'ResNeSt', 'VGG', 'TCN', 'ViPNAS_ResNet', 'ViPNAS_MobileNetV3', 'LiteHRNet', 'V2VNet', 'HRFormer', 'PyramidVisionTransformer', - 'PyramidVisionTransformerV2', 'SwinTransformer' + 'PyramidVisionTransformerV2', 'SwinTransformer', 'I3D' ] diff --git a/mmpose/models/backbones/i3d.py b/mmpose/models/backbones/i3d.py new file mode 100644 index 0000000000..64f330abac --- /dev/null +++ b/mmpose/models/backbones/i3d.py @@ -0,0 +1,215 @@ +# Copyright (c) OpenMMLab. All rights reserved. +# Code is modified from `Third-party pytorch implementation of i3d +# `. + +import torch +import torch.nn as nn + +from ..builder import BACKBONES +from .base_backbone import BaseBackbone + + +class Conv3dBlock(nn.Module): + """Basic 3d convolution block for I3D. + + Args: + in_channels (int): Input channels of this block. + out_channels (int): Output channels of this block. + expansion (float): The multiplier of in_channels and out_channels. + Default: 1. + kernel_size (tuple[int]): kernel size of the 3d convolution layer. + Default: (1, 1, 1). + stride (tuple[int]): stride of the block. Default: (1, 1, 1) + padding (tuple[int]): padding of the input tensor. Default: (0, 0, 0) + use_bias (bool): whether to enable bias in 3d convolution layer. + Default: False + use_bn (bool): whether to use Batch Normalization after 3d convolution + layer. Default: True + use_relu (bool): whether to use ReLU after Batch Normalization layer. + Default: True + """ + + def __init__(self, + in_channels, + out_channels, + expansion=1.0, + kernel_size=(1, 1, 1), + stride=(1, 1, 1), + padding=(0, 0, 0), + use_bias=False, + use_bn=True, + use_relu=True): + super().__init__() + + in_channels = int(in_channels * expansion) + out_channels = int(out_channels * expansion) + + self.conv3d = nn.Conv3d( + in_channels, + out_channels, + kernel_size, + padding=padding, + stride=stride, + bias=use_bias) + + self.use_bn = use_bn + self.use_relu = use_relu + + if self.use_bn: + self.batch3d = nn.BatchNorm3d(out_channels) + + if self.use_relu: + self.activation = nn.ReLU(inplace=True) + + def forward(self, x): + """Forward function.""" + out = self.conv3d(x) + if self.use_bn: + out = self.batch3d(out) + if self.use_relu: + out = self.activation(out) + return out + + +class Mixed(nn.Module): + """Inception block for I3D. + + Args: + in_channels (int): Input channels of this block. + out_channels (int): Output channels of this block. + expansion (float): The multiplier of in_channels and out_channels. + Default: 1. + """ + + def __init__(self, in_channels, out_channels, expansion=1.0): + super(Mixed, self).__init__() + # Branch 0 + self.branch_0 = Conv3dBlock( + in_channels, out_channels[0], expansion, kernel_size=(1, 1, 1)) + + # Branch 1 + branch_1_conv1 = Conv3dBlock( + in_channels, out_channels[1], expansion, kernel_size=(1, 1, 1)) + branch_1_conv2 = Conv3dBlock( + out_channels[1], + out_channels[2], + expansion, + kernel_size=(3, 3, 3), + padding=(1, 1, 1)) + self.branch_1 = nn.Sequential(branch_1_conv1, branch_1_conv2) + + # Branch 2 + branch_2_conv1 = Conv3dBlock( + in_channels, out_channels[3], expansion, kernel_size=(1, 1, 1)) + branch_2_conv2 = Conv3dBlock( + out_channels[3], + out_channels[4], + expansion, + kernel_size=(3, 3, 3), + padding=(1, 1, 1)) + self.branch_2 = nn.Sequential(branch_2_conv1, branch_2_conv2) + + # Branch3 + branch_3_pool = nn.MaxPool3d( + kernel_size=(3, 3, 3), + stride=(1, 1, 1), + padding=(1, 1, 1), + ceil_mode=True) + branch_3_conv2 = Conv3dBlock( + in_channels, out_channels[5], expansion, kernel_size=(1, 1, 1)) + self.branch_3 = nn.Sequential(branch_3_pool, branch_3_conv2) + + def forward(self, x): + """Forward function.""" + out_0 = self.branch_0(x) + out_1 = self.branch_1(x) + out_2 = self.branch_2(x) + out_3 = self.branch_3(x) + out = torch.cat((out_0, out_1, out_2, out_3), 1) + return out + + +@BACKBONES.register_module() +class I3D(BaseBackbone): + """I3D backbone. + + Please refer to the `paper `__ for + details. + + Args: + in_channels (int): Input channels of the backbone, which is decided + on the input modality. + expansion (float): The multiplier of in_channels and out_channels. + Default: 1. + """ + + def __init__(self, in_channels=3, expansion=1.0): + super(I3D, self).__init__() + + # expansion must be an integer multiple of 1/8 + expansion = round(8 * expansion) / 8.0 + + # xut Layer + self.conv3d_1a_7x7 = Conv3dBlock( + out_channels=64, + in_channels=in_channels / expansion, + expansion=expansion, + kernel_size=(7, 7, 7), + stride=(2, 2, 2), + padding=(2, 3, 3)) + self.maxPool3d_2a_3x3 = nn.MaxPool3d( + kernel_size=(1, 3, 3), stride=(1, 2, 2), padding=(0, 1, 1)) + + # Layer 2 + self.conv3d_2b_1x1 = Conv3dBlock( + out_channels=64, + in_channels=64, + expansion=expansion, + kernel_size=(1, 1, 1)) + self.conv3d_2c_3x3 = Conv3dBlock( + out_channels=192, + in_channels=64, + expansion=expansion, + kernel_size=(3, 3, 3), + padding=(1, 1, 1)) + self.maxPool3d_3a_3x3 = nn.MaxPool3d( + kernel_size=(1, 3, 3), stride=(1, 2, 2), padding=(0, 1, 1)) + + # Mixed_3b + self.mixed_3b = Mixed(192, [64, 96, 128, 16, 32, 32], expansion) + self.mixed_3c = Mixed(256, [128, 128, 192, 32, 96, 64], expansion) + self.maxPool3d_4a_3x3 = nn.MaxPool3d( + kernel_size=(3, 3, 3), stride=(2, 2, 2), padding=(1, 1, 1)) + + # Mixed 4 + self.mixed_4b = Mixed(480, [192, 96, 208, 16, 48, 64], expansion) + self.mixed_4c = Mixed(512, [160, 112, 224, 24, 64, 64], expansion) + self.mixed_4d = Mixed(512, [128, 128, 256, 24, 64, 64], expansion) + self.mixed_4e = Mixed(512, [112, 144, 288, 32, 64, 64], expansion) + self.mixed_4f = Mixed(528, [256, 160, 320, 32, 128, 128], expansion) + + self.maxPool3d_5a_2x2 = nn.MaxPool3d( + kernel_size=(2, 2, 2), stride=(2, 2, 2), padding=(0, 0, 0)) + + # Mixed 5 + self.mixed_5b = Mixed(832, [256, 160, 320, 32, 128, 128], expansion) + self.mixed_5c = Mixed(832, [384, 192, 384, 48, 128, 128], expansion) + + def forward(self, x): + out = self.conv3d_1a_7x7(x) + out = self.maxPool3d_2a_3x3(out) + out = self.conv3d_2b_1x1(out) + out = self.conv3d_2c_3x3(out) + out = self.maxPool3d_3a_3x3(out) + out = self.mixed_3b(out) + out = self.mixed_3c(out) + out = self.maxPool3d_4a_3x3(out) + out = self.mixed_4b(out) + out = self.mixed_4c(out) + out = self.mixed_4d(out) + out = self.mixed_4e(out) + out = self.mixed_4f(out) + out = self.maxPool3d_5a_2x2(out) + out = self.mixed_5b(out) + out = self.mixed_5c(out) + return out diff --git a/mmpose/models/detectors/__init__.py b/mmpose/models/detectors/__init__.py index 66e575e2f2..d94d8b8aab 100644 --- a/mmpose/models/detectors/__init__.py +++ b/mmpose/models/detectors/__init__.py @@ -1,5 +1,6 @@ # Copyright (c) OpenMMLab. All rights reserved. from .associative_embedding import AssociativeEmbedding +from .gesture_recognizer import GestureRecognizer from .interhand_3d import Interhand3D from .mesh import ParametricMesh from .multi_task import MultiTask @@ -12,5 +13,5 @@ __all__ = [ 'TopDown', 'AssociativeEmbedding', 'ParametricMesh', 'MultiTask', 'PoseLifter', 'Interhand3D', 'PoseWarper', 'DetectAndRegress', - 'VoxelCenterDetector', 'VoxelSinglePose' + 'VoxelCenterDetector', 'VoxelSinglePose', 'GestureRecognizer' ] diff --git a/mmpose/models/detectors/gesture_recognizer.py b/mmpose/models/detectors/gesture_recognizer.py new file mode 100644 index 0000000000..f99cd04f30 --- /dev/null +++ b/mmpose/models/detectors/gesture_recognizer.py @@ -0,0 +1,188 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import warnings + +import torch.nn as nn + +from .. import builder +from ..builder import POSENETS +from .base import BasePose + +try: + from mmcv.runner import auto_fp16 +except ImportError: + warnings.warn('auto_fp16 from mmpose will be deprecated from v0.15.0' + 'Please install mmcv>=1.1.4') + from mmpose.core import auto_fp16 + + +@POSENETS.register_module() +class GestureRecognizer(BasePose): + """Hand gesture recognizer. + + Args: + backbone (dict): Backbone modules to extract feature. + neck (dict): Neck Modules to process feature. + cls_head (dict): Classification head to process feature. + train_cfg (dict): Config for training. Default: None. + test_cfg (dict): Config for testing. Default: None. + modality (str or list or tuple): Data modality. Default: None. + pretrained (str): Path to the pretrained models. + """ + + def __init__(self, + backbone, + neck=None, + cls_head=None, + train_cfg=None, + test_cfg=None, + modality='rgb', + pretrained=None): + super().__init__() + + self.train_cfg = train_cfg + self.test_cfg = test_cfg + + if isinstance(modality, (tuple, list)): + self.modality = modality + else: + self.modality = (modality, ) + backbone = {modality: backbone} + pretrained = {modality: pretrained} + + # build backbone + self.backbone = nn.Module() + for modal in self.modality: + setattr(self.backbone, modal, + builder.build_backbone(backbone[modal])) + + # build neck + if neck is not None: + self.neck = builder.build_neck(neck) + + # build head + cls_head['train_cfg'] = train_cfg + cls_head['test_cfg'] = test_cfg + cls_head['modality'] = self.modality + self.cls_head = builder.build_head(cls_head) + + self.pretrained = dict() if pretrained is None else pretrained + self.init_weights() + + def init_weights(self, pretrained=None): + """Weight initialization for model.""" + if pretrained is not None: + self.pretrained = pretrained + for modal in self.modality: + getattr(self.backbone, + modal).init_weights(self.pretrained.get(modal, None)) + if hasattr(self, 'neck'): + self.neck.init_weights() + if hasattr(self, 'cls_head'): + self.cls_head.init_weights() + + @auto_fp16(apply_to=('video', )) + def forward(self, + video, + label=None, + img_metas=None, + return_loss=True, + **kwargs): + """Calls either forward_train or forward_test depending on whether + return_loss=True. Note this setting will change the expected inputs. + + Note: + - batch_size: N + - num_vid_channel: C (Default: 3) + - video height: vidH + - video width: vidW + - video length: vidL + + Args: + video (list[torch.Tensor[NxCxvidLxvidHxvidW]]): Input videos. + label (torch.Tensor[N]): Category label of videos. + img_metas (list(dict)): Information about data. + By default this includes: + - "fps: video frame rate + - "modality": modality of input videos + return_loss (bool): Option to `return loss`. `return loss=True` + for training, `return loss=False` for validation & test. + + Returns: + dict|tuple: if `return loss` is true, then return losses. \ + Otherwise, return predicted gestures for clips with \ + a certain length. \ + . + """ + if not isinstance(img_metas, (tuple, list)): + img_metas = [img_metas.data] + if return_loss: + return self.forward_train(video, label, img_metas[0], **kwargs) + return self.forward_test(video, label, img_metas[0], **kwargs) + + def _feed_forward(self, video, img_metas): + """Feed videos into network to compute feature maps and logits. + + Note: + - batch_size: N + - num_vid_channel: C (Default: 3) + - video height: vidH + - video width: vidW + - video length: vidL + + Args: + video (list[torch.Tensor[NxCxvidLxvidHxvidW]]): Input videos. + img_metas (list(dict)): Information about data. + By default this includes: + - "fps: video frame rate + - "modality": modality of input videos + + Returns: + tuple[Tensor, Tensor]: output logit and feature map. + """ + fmaps = [] + for i, modal in enumerate(img_metas['modality']): + fmaps.append(getattr(self.backbone, modal)(video[i])) + + if hasattr(self, 'neck'): + fmaps = [self.neck(fmap) for fmap in fmaps] + + if hasattr(self, 'cls_head'): + logits = self.cls_head(fmaps, img_metas) + else: + return None, fmaps + + return logits, fmaps + + def forward_train(self, video, label, img_metas, **kwargs): + """Defines the computation performed at every call when training.""" + logits, fmaps = self._feed_forward(video, img_metas) + + # if return loss + losses = dict() + if hasattr(self, 'cls_head'): + cls_losses = self.cls_head.get_loss(logits, label, fmaps=fmaps) + losses.update(cls_losses) + cls_accuracy = self.cls_head.get_accuracy(logits, label, img_metas) + losses.update(cls_accuracy) + + return losses + + def forward_test(self, video, label, img_metas, **kwargs): + """Defines the computation performed at every call when testing.""" + results = dict(logits=dict()) + logits, _ = self._feed_forward(video, img_metas) + for i, modal in enumerate(img_metas['modality']): + results['logits'][modal] = logits[i] + results['label'] = label + return results + + def set_train_epoch(self, epoch: int): + """set the training epoch of heads to support customized behaviour.""" + if hasattr(self, 'cls_head'): + self.cls_head.set_train_epoch(epoch) + + def forward_dummy(self, video): + raise NotImplementedError + + def show_result(self, video, result, **kwargs): + raise NotImplementedError diff --git a/mmpose/models/heads/__init__.py b/mmpose/models/heads/__init__.py index a98e91140e..459c20b8bd 100644 --- a/mmpose/models/heads/__init__.py +++ b/mmpose/models/heads/__init__.py @@ -6,6 +6,7 @@ from .deeppose_regression_head import DeepposeRegressionHead from .hmr_head import HMRMeshHead from .interhand_3d_head import Interhand3DHead +from .mtut_head import MultiModalSSAHead from .temporal_regression_head import TemporalRegressionHead from .topdown_heatmap_base_head import TopdownHeatmapBaseHead from .topdown_heatmap_multi_stage_head import (TopdownHeatmapMSMUHead, @@ -20,5 +21,5 @@ 'AEHigherResolutionHead', 'AESimpleHead', 'AEMultiStageHead', 'DeepposeRegressionHead', 'TemporalRegressionHead', 'Interhand3DHead', 'HMRMeshHead', 'DeconvHead', 'ViPNASHeatmapSimpleHead', 'CuboidCenterHead', - 'CuboidPoseHead' + 'CuboidPoseHead', 'MultiModalSSAHead' ] diff --git a/mmpose/models/heads/mtut_head.py b/mmpose/models/heads/mtut_head.py new file mode 100644 index 0000000000..4931d6d436 --- /dev/null +++ b/mmpose/models/heads/mtut_head.py @@ -0,0 +1,155 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import itertools + +import torch +import torch.nn as nn +from mmcv.cnn import xavier_init + +from ..builder import HEADS + + +@HEADS.register_module() +class MultiModalSSAHead(nn.Module): + """Sparial-temporal Semantic Alignment Head proposed in "Improving the + performance of unimodal dynamic hand-gesture recognition with multimodal + training", + + Please refer to the `paper `__ for + details. + + Args: + num_classes (int): number of classes. + modality (list[str]): modalities of input videos for backbone. + in_channels (int): number of channels of feature maps. Default: 1024 + avg_pool_kernel (tuple[int]): kernel size of pooling layer. + Default: (1, 7, 7) + dropout_prob (float): probablity to use dropout on input feature map. + Default: 0 + train_cfg (dict): training config. + test_cfg (dict): testing config. + """ + + def __init__(self, + num_classes, + modality, + in_channels=1024, + avg_pool_kernel=(1, 7, 7), + dropout_prob=0.0, + train_cfg=None, + test_cfg=None, + **kwargs): + super().__init__() + + self.modality = modality + self.train_cfg = train_cfg + self.test_cfg = test_cfg + + # build sub modules + self.avg_pool = nn.AvgPool3d(avg_pool_kernel, (1, 1, 1)) + self.dropout = nn.Dropout(dropout_prob) + self.output_conv = nn.Module() + for modal in self.modality: + conv3d = nn.Conv3d(in_channels, num_classes, (1, 1, 1)) + setattr(self.output_conv, modal, conv3d) + self.loss = nn.CrossEntropyLoss(reduction='none') + + # parameters for ssa loss + self.beta = self.train_cfg.get('beta', 2.0) + self.lambda_ = self.train_cfg.get('lambda_', 5e-3) + self.start_epoch = self.train_cfg.get('ssa_start_epoch', 1e6) + self._train_epoch = 0 + + def init_weights(self): + """Initialize model weights.""" + for m in self.output_conv.modules(): + if isinstance(m, nn.Conv3d): + xavier_init(m) + + def set_train_epoch(self, epoch: int): + """set the epoch to control the activation of SSA loss.""" + self._train_epoch = epoch + + def forward(self, x, img_metas): + """Forward function.""" + logits = [] + for i, modal in enumerate(img_metas['modality']): + out = self.avg_pool(x[i]) + out = self.dropout(out) + out = getattr(self.output_conv, modal)(out) + out = out.mean(3).mean(3) + logits.append(out) + return logits + + @staticmethod + def _compute_corr(fmap): + """compute the self-correlation matrix of feature map.""" + fmap = fmap.view(fmap.size(0), fmap.size(1), -1) + fmap = nn.functional.normalize(fmap, dim=2, eps=1e-8) + corr = torch.bmm(fmap.permute(0, 2, 1), fmap) + return corr.view(corr.size(0), -1) + + def get_loss(self, logits, label, fmaps=None): + """Compute the Cross Entropy loss and SSA loss. + + Note: + - batch_size: N + - number of classes: nC + - feature map channel: C + - feature map height: H + - feature map width: W + - feature map length: L + - logit length: Lg + + Args: + logits (list[NxnCxLg]): predicted logits for each modality. + label (list(dict)): Category label. + fmaps (list[torch.Tensor[NxCxLxHxW]]): feature maps for each + modality. + + Returns: + dict[str, torch.tensor]: computed losses. + """ + losses = {} + ce_loss = [self.loss(logit.mean(dim=2), label) for logit in logits] + + if self._train_epoch >= self.start_epoch: + ssa_loss = [] + corrs = [self._compute_corr(fmap) for fmap in fmaps] + for idx1, idx2 in itertools.combinations(range(len(fmaps)), 2): + for i, j in ((idx1, idx2), (idx2, idx1)): + rho = (ce_loss[i] - ce_loss[j]).clamp(min=0) + rho = (torch.exp(self.beta * rho) - 1).detach() + ssa = corrs[i] - corrs[j].detach() + ssa = rho * ssa.pow(2).mean(dim=1).pow(0.5) + ssa_loss.append((ssa.mean() * self.lambda_).clamp(max=10)) + losses['ssa_loss'] = sum(ssa_loss) + ce_loss = [loss.mean() for loss in ce_loss] + losses['ce_loss'] = sum(ce_loss) + + return losses + + def get_accuracy(self, logits, label, img_metas): + """Compute the accuracy of predicted gesture. + + Note: + - batch_size: N + - number of classes: nC + - logit length: L + + Args: + logits (list[NxnCxL]): predicted logits for each modality. + label (list(dict)): Category label. + img_metas (list(dict)): Information about data. + By default this includes: + - "fps: video frame rate + - "modality": modality of input videos + + Returns: + dict[str, torch.tensor]: computed accuracy for each modality. + """ + results = {} + for i, modal in enumerate(img_metas['modality']): + logit = logits[i].mean(dim=2) + acc = (logit.argmax(dim=1) == label).float().mean() + results[f'acc_{modal}'] = acc.item() + return results diff --git a/model-index.yml b/model-index.yml index b3bcd91e3c..500bac9e30 100644 --- a/model-index.yml +++ b/model-index.yml @@ -134,6 +134,7 @@ Import: - configs/hand/2d_kpt_sview_rgb_img/topdown_heatmap/rhd2d/mobilenetv2_rhd2d.yml - configs/hand/2d_kpt_sview_rgb_img/topdown_heatmap/rhd2d/resnet_rhd2d.yml - configs/hand/3d_kpt_sview_rgb_img/internet/interhand3d/internet_interhand3d.yml +- configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/i3d_nvgesture.yml - configs/wholebody/2d_kpt_sview_rgb_img/associative_embedding/coco-wholebody/higherhrnet_coco-wholebody.yml - configs/wholebody/2d_kpt_sview_rgb_img/associative_embedding/coco-wholebody/hrnet_coco-wholebody.yml - configs/wholebody/2d_kpt_sview_rgb_img/topdown_heatmap/coco-wholebody/hrnet_coco-wholebody.yml diff --git a/tests/data/nvgesture/bboxes.json b/tests/data/nvgesture/bboxes.json new file mode 100644 index 0000000000..f6c1c06692 --- /dev/null +++ b/tests/data/nvgesture/bboxes.json @@ -0,0 +1,344 @@ +{ + "": [ + [ + { + "bbox": { + "__ndarray__": [ + 88.10102081298828, + 96.01033020019531, + 198.7417449951172, + 221.09027099609375, + 0.9996840953826904 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 96.0768814086914, + 98.94043731689453, + 205.73648071289062, + 231.98422241210938, + 0.9979396462440491 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 112.53093719482422, + 100.84430694580078, + 225.46414184570312, + 218.37539672851562, + 0.9948186278343201 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 140.26339721679688, + 100.93962097167969, + 239.01219177246094, + 213.0036163330078, + 0.9963332414627075 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 162.32077026367188, + 93.03462982177734, + 246.34483337402344, + 211.05172729492188, + 0.9872151613235474 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 169.93429565429688, + 92.26083374023438, + 275.7470703125, + 212.19854736328125, + 0.9966674447059631 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 177.5313720703125, + 105.4382095336914, + 316.52239990234375, + 208.23284912109375, + 0.9904665350914001 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 177.9888916015625, + 114.24292755126953, + 313.8994445800781, + 205.98202514648438, + 0.9988754391670227 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 177.15512084960938, + 121.3445816040039, + 306.1671142578125, + 205.3945770263672, + 0.9995057582855225 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 173.60049438476562, + 124.11859130859375, + 303.1548767089844, + 209.4412841796875, + 0.9997778534889221 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 167.17393493652344, + 126.2450180053711, + 297.64093017578125, + 211.53280639648438, + 0.9993647933006287 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 160.3257598876953, + 126.101318359375, + 289.13494873046875, + 214.8901824951172, + 0.9971745014190674 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 159.0267791748047, + 123.04993438720703, + 280.7857360839844, + 214.73959350585938, + 0.9962193369865417 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 149.41571044921875, + 118.96903991699219, + 254.2805938720703, + 214.06747436523438, + 0.9996248483657837 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 137.837890625, + 112.58697509765625, + 218.49127197265625, + 217.65696716308594, + 0.9997895359992981 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 128.7360382080078, + 106.2151107788086, + 207.93055725097656, + 220.00509643554688, + 0.9997987151145935 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 118.36389923095703, + 103.3556137084961, + 202.43719482421875, + 220.83615112304688, + 0.9996999502182007 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 99.54222106933594, + 99.98068237304688, + 196.3734130859375, + 227.61508178710938, + 0.999809205532074 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 87.78035736083984, + 97.02134704589844, + 193.37466430664062, + 232.29702758789062, + 0.9998823404312134 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ], + [ + { + "bbox": { + "__ndarray__": [ + 85.43366241455078, + 96.68350219726562, + 192.14898681640625, + 229.33612060546875, + 0.9999115467071533 + ], + "dtype": "float32", + "shape": [ + 5 + ] + } + } + ] + ] +} \ No newline at end of file diff --git a/tests/data/nvgesture/sk_color.avi b/tests/data/nvgesture/sk_color.avi new file mode 100644 index 0000000000000000000000000000000000000000..781f348b54026ddb1d2939ffb35ab584379ef375 GIT binary patch literal 203906 zcmeFYbyOVB`|mk81PSgAL4vyzB)Gc{?lQOqNPu9$-6goY4i;R3yAJN|F3ab8@9*y2 zv-j+t{de!D&UAI1>Yk@gzo+`?)9-qXs*I%MH)0rosHP0Lyo{RqF$Mr&W#(*W>~3wv z3jhGP0^TbF!uJXQKt}=qu>aEn0HFR`4?yI-asJ=?{^#94u@cY$02fzhyZ7xsvD~fA zToh!LrT*vh{)t2S-#h+^{eckxczZAZb&d$s`9Co%=-xa2+5T%hC;&d-Kd}N~{_E8L zv6t0Ql*4}?8}`5Vu(Fz&{*QzIHvU!MUj_bE;9mv)Rp4I*{#D>#1^!jwUj_bE;9mv) zRp4I*{?v$uQySl$0`an=9pi~Pr@`N!RnQIu4YQIk|hdvD7dyIXMa zGIO&rv$3)Q{!#1x|8BDUufzS@`>z83D)9d+1^&qp=x{Iqdk1&xf1FfGs&|KW;q4WG z@t!A4-}3|;E8l+-#oIDK9Ds<3h=hoQ{GWn~g8F|JG&D2}42<_06%`E)9UTJ$6ASab z0|N^S8yg!R9}kb1n1qs&{;d~K3;;mGK)sj$AtMYl94r((0ss*T@P9qS`Dg6>)m9gWj z5Ia9X;ln~zAXXp~VgMBTSrI#_tt6o+P$r@#%_0%Iph-%m{6f*;{X!FHk)STkFKx-C zx>&UvQ2mw)k#PgHBgMbcqeCZ#%RT^@;u3b~MS5jb(uvjr{Z)9DG>6xj-XKPAV>F zl0528Mks7@cpC;)zt9;(Ditw&MRch6K!0>KzX8)BqdOWdvd{@fpY`q22;_X8sITdJTV<}3BFU5~c$!9&d(qkqn}tpk zLvy}QR!Uezi$F8hFmxDL*;)1jH3bM)1zJgq1G%PH zVxX8C(2hh!U|aMGwEZ{}d!H&Q2av(WOc(9*wr;5HsPujc^XL%(y!_77Cw=;4`7`m`ZJ8@ zG`fiAQoBEDjw+36{uPY}&9bEO1}3)Qz6z?WvaHOGZGfrmk5Wk!iqX9`VmGr3wM`*? z$K$~P0bKc>uJ{GsHvZh6v4^Ew0q?-o%BL`_WF{@pN5izp_|~i93lqK;0-~PCGEk-qPTP$!JK$&|7<*8rrA-W9j z*C0K17?qf!RhQ;EIt`MDg4r*`8Ic%Tk$yyf#P?gCTmq2-n;e=J(Yi|n&ibpU4Y?}C zT!A4ug8T=PX>#!jIk%fBsQ4U+4ZyZs$1POEenxVKLw^K?$3@Y7XLhQwqf}m@(7)1W z%Gv1CC<)LqE`N{&Ay-qF2P1DuS4q1UZa}+Py>X?uor#@myseT@8`kzg+X-YVAXvLnJPYv*m*J=Xm}w`UimhE9p1{asb z{Z4-A8Z&)J>E;_CBKh}|(MjysIKQ(w^G6Gs^$39&*yg=)0k}>HaRpnjUy1BDel7E$ zkuS&#chGB@t<&iS?#yiAN(oCsOES7t%&-_UAiUJ4G;hwYA3^d`IoYdXG~?=zo+FB} zQ;yU}8@Zu(FkQvJn!p&FLM%>?X?To!S*o-+A+T{fF?iCN`9RAIdOF3gbc!O-pYtDT zARnd^t-Wb;V5lDQV9_J60A*r*IRJF*4HOy?w9H~-UJ=%zuJ>dr_92C$2oX?0=kJTc zE`L6_u-Cjl7amtJqpYb4uq;X=cV#g~eXS6-`tTWIqdo*j*g!dhXj>uMAuY}%-2mX4 zC`hcX8F!DwQ*!$VWH5IO+Z`oOcM;^X@}u!>m!ZBCu6StQ9XWi4f@~Iglr|F~K#1q$ zM(+TRA*@~)y}FS5Qt?)`Ddkt0=dm8i6u>OxOH`EDx7<5S*~z&0_(my^`gSn5aH9&= zu%2V;c|g(k^QgcvhwOn+C?2B;{o}wQ8GQ z9zPy4>MUM6zR~!z*w;e=NVQBPHO(yq!zD?(!3$h;m%T8Zs6iZjgLsv*QKqF$Hzh|<3qyUjrXcR11auwzn zc!y#?4qAzoldRFSMkvPk(5gkK=GH6Z;9ws?+Q7rg=!$=PpVTD(0MJZfa8YbiQl-p4#urL6sfU^=sMwKhJy6Z>!G8xQnn2IICD z3Qc~~WhFy(O3%68v@apkok4q5rg<)DyWmP|icv?3QL0!3v35)AH3^ASHoQmK)st!! z&=yumB3JohYL$3@Gzs51g}y~$<5COpVzm-VsUc0XWduuQfd??H$|@fA1g?^HI1ya) z2G9fc6?Gp{sEn&)m`_!-z_LG3&H!o$sjNQ})EsV=jJHcGQXkev#_5-5L&H!7Q#p`9 zl)Wccj%Ppr48$6u$om>_D3&caPxmlf37xuJ`iGHgsp(Ux5QLVa$@8XZp;3GZ}Z_L z=9D2j9pXTiH277~m|}!-!9Es5#2J7Flrg&FESPM9v3H7kXiksTc+iUhGT<`TIvl(F zsnDbD*i#ao=Hd_>DkzBb^V2_22zva9^gsuuVghW7cHnV_a4zc)2$S#f1h&VMwYaba1!8QSDmh}&H2-meZw{n(V zi?8P-PT3CP*YXZiSz7Ehi1c^0Q}#p@PFJ2gicm$4CKc@U)|u>_E%2iyc)wgS*Rr)7D;o{kf{7S~2M2 zjO8zLh?5~4Swjir>vS^h#M>7-n$2CYrsXypp$!h%tpMkoF!7^{UPzQMgv%F2X+sp5 zza&a+gUv9GlC=biOLl#7dPKWCR*!DCXY?XE;;3Sht1o%xh7-aaJUyGM#|@0FacqCm zs?;TcB!Hpd%2wr4%F7}jO9npuLVTbJ4zrY5yWkjZhpcm!UZqW8P5l-O;k@9*2+z}` zwQbhimZQBtAmY@)XMh3OatbRqj|IU)-if)#j+fPsjdvdbr^o)X)*_^TeIqHOC3IWM z>*O|hh9jTZ4x-p*cf=FgC#4K7kM01L;WGJ>M`gP>A+foi=bw}%Y10TF+g0;(FOKfW@4n@s3!-Hj2@hsqRZ-G z6$@5|40Xdib(~j#*g7bK`fCZCjX9?j;cT`_o@7)i2 zGL2-hzJnPE2W{%nG>)}9wN=)E`v3y;JX=r?at$#o^bzib)!!wc5L2#Q5;ypQXQN8_ zur{xHzUJ&w99aa{iZ5KKcc+pF%kB7_tAp~ zYm%EEiDA2gk}7Ip?ZPmg=!YUD2hD0sI~rY{e@?lT6>-R+SNxn-y2?{fx;eJ`1w5pp zb+jRNvH5PB-%rKmDXCH`J|bv$XFQ&|*|oWDy5}S>N-%$|_i`eFT?Da0+J7!6$Ojz? zs%6HXl@9$NKqQ46x)4z$7uL^6Es%2%aQq#*kbbAdA5JG&9~L%k#v19s(xYJpcW{}1 zu}UfMA*Kl@(a&)e6 zpRihe>)ckk<@-9~g}|6F*`-2;+x(fc9y+kCX5M-nwOlGpi9|t>VS22yz5kEBAjkLm z7oLaBAui3_u()#EaV6N!?XR&lcdx%yOj6^bWe$kDKOd>8YGw^&O@|kj#8f|dTNftC zp;9|GTcDVakWJ0QKPPD3HR?=hv^d%MSrDJy*8G@uV*<9arx4@od#)WC__mzlXq?^% zE?B{fcrfdOjX5`)t|m#dYj42`Td|LnO_$Fqz*86C0YkyUolM|~*%W3^lGkpt!}uR- zw{+5}gB9weg7T*BImq5a=KG_9w?}62bgz<0_zUh>2qYJy$t!MP7ZDOpWeH_>p2@k{ zwo54cd0*lyi)+N=r$!egXonjXrL}$2;$?H&@XCH*erw8>Gtp`r?eY!QG>Bad%5&Q; zWni+KnUwz&MoISOrT&rA=MQ+mR^9BxcXIeFq0-k}zMO#T-i{AG;)`k-O1Z0c=naR) z@~9$Z8E?>{(1mZV?B3B!D<>D+M0oF+rj-13t?JJv2}zr{mSu-+m#D(xv@0Vg1OdN* zX(}$bZ`><$P=4X`#Rodx85q-<_4%`NB=fPYkMgbyrGc91AK+22#kT`(#y0}WSy zgT5C&>hohy>$!D*lgVh7ut8VEPW|Xs!s0rF$-i8n>pvk^Auf+5jsCTZTo)5C3<}1D zyuTmx=2VE_mF#tuW)+rpi^4f4Ytiral>X|`<#)97+*!Xv4XB@)UT9o!Jpw{sqUQa0 zN+oMKJ@6fsP+9sEQYDeaXQgZ2;5d4<>ou*L3_B7P0=`8tkdjFhozFctm)2g0Jeqb&N1SkX69bunKF6vs>HQdqG3rdOgE3W{@&;e{4k;p)*}{X@m?4CU?Jl+EX}! z?k(l~c!_?6{dwaek=s7l{&|%~2#mkVxzEt}J;>+OQG9M{!$-!8u7}K<0)*2Y1~s}z z4mI|&`?nF&;dSl61>Fw-cn*GH^#3EweW-4=@bWi*Rv}*Wx$$yVu}r?`S3@ea}suh+2GdHkfLbwjK|+J4JtQGIV;UWd}MlY^DNxYb{(AJUD- zriUPj^%~eYhP|rb!?hZxI{uV+|0oz{k0t|fu2+Y5`cO+nNg;=UB+*VvYgDR_*rRLd z%sfm`Ee|0R_~@Q}c~?>>dDZF1L+tke?5iQ8!Pu>*MBH7IGIlo{w^OQ={o$KQ*~KqI zsy8y8(nTD@b>rojVBf#;I<{S|YJZBd3XhUjNQs}5xwV|6G_lW0kcq2uiu=cg8;HXW zt(*R8ElAG~ydIfYdX8^{?|P~2V;0dZbg!@U+cIU6(n8UfGNmKotYqu45#5?(>))M# zSQ}Uj`zhEVauT0J8vq@TZjkG|2(0{c8qSrxe;mekCA-mD*k9Ta!FVs1R_@vZS#Ece z>@Q`~rm08p(B)sGu-fiGWOINgWs(>VGP<1iqc zm}Do6`of06pdCmF)!c#aqyo1RzLM;IPD$8rU=%{h_wV*!PrmGc^D5@w00m`v%S|EO zBFq$I+aW;dRMS3T+~lpFGfk+KA8r>vv3Hz}7ip;&I}H-tXJr0n8tDe^o+g zikoTHZ%XWW>UGZ+Y_KA1R25uLML=BH9y#A}w>*%STPvIem~827a_z=Z>_TR)jG$bq zJV*drU2+atmhnF~VsO{?TjuTWVR8;z)}M!2FjTXJo^}-$N2=K3{S{(msXnM zABdN*ecAMGeEo-PN_3=@64;h3X7ug;GXqmr9Jo&gL-g@?GdzD`3 z01&fj4gQ0QC!~^I&PQLj?_^sAlK3h+*C;W{y(XSV!hjJm%t?@L!dS5g@3g3cn}S<} zXpA8vSs|MWV4qNB5<&Z5_A7=W={?~_H89N&LY+45+p3};#LWGM;n6E8)h*|4R*ub> z$!BW$)WS1Bl^5K>%1QF~+B<{%R?2x#hp?U;w|x$ks^klT+_PAiilyZ0dTB}tjc51V zt|wiJ5gIvWD^r<$0W4qdefe5Z8S`0=hr`ka`FFjKmq89ri|d!aOhuZY%sG2tQS0nV zl?*x=?JL6tuE6wTO6Ds1Yij*3gDdFjoDo~58N3kiie>M2<~DG0+^E6qYiTFF_3tvH zrmQR2#$Crv>jNfR|9#~C5|W>DA_&UDwe1?8RL-OMK0EtD;wM5TB@S+6$m;+kvDeW$ z+Xe2Bl6lPGCBD7a4~>u(mFkae5)VIRxL^(akF)$*XihQy3gI>>leO&k=RWW`gDa_k zi!L+(_;wOG-Azxl+EUX$TU_TEoUc$o`i#>rl`5XNjoZ7Y&rfrla@Z4pb%{wA3I47( zj@zG#-W#xQHe$il^*Z!inBORvip#lC_Rlxkk1e(`+*2Pczt~~VQX5tVO|i<5V#PGu zH(0|3>T44ro4NASO=~Zbds8#pZaO01)Y(_g+on=cfs8dl#|sk23!U@gi&MMzir<)R z9V@Xgwqx`bzw0A4R`O{VCn+#w$L5nSL!5dTN7WD3f?BN5)3Ku_4rYtO-q)5MYx(z? zqrb$m3|Q*Ld=ZvI^Zo(jw(N)n&E))%xbF=>V#Bu0*M%!({P(WuTQSfBfw7>KD=c?m zBtNx1zKhcgTS{m2GlLOTw^1y6K)-w}JK({7j!6V1m2&yFc!ex(U?BU$=b4Fq!%w;~ zk$SH0>mU$mwVCCHxCnW4jZAgth9w+J!m4V!s!ETP1HoopUkBV%=!a}%>XpOIqH-Vg z&5M4=0Hu<-nQR;)KGnUdpM{u-I#k3d3N!V3>3IaqCj2oQ-V96$IP3uodi)NXXH@JO zk(JgwsNMLZ=OLyZ;v5g@aKi{dcY}!t-(8RFU{-|ix)*3UV&$DAb@4YqSzJS_0*Vhr zC%P*}`s1^)CtX67)*C>}cA3c@j!5YNW0>L5oC#*TyUi2m+IQ?ci;0@pr5oI$t+1r@ zEy7w7P8Wy7m-KZdmBZ~mWd7x^(X7}-EZT56i@=9#wf5ft*;$KZ;E4GzS1H{q>ffP| z5GI!U&-_-Om!|Zk2Llwtgc9ReS9fFvT3Z$*X;B$PD0H2YnJRm&6Zpv)i`*nQ%<4|H z=V8L9oa<`45v&W8gOw;wo6-2RZE#*Xcnj=)j<_n>otT!}34&K)d~unCb45o`F^B6k zYArdJxQSEld@T1D3W=SdPuu=gah@0oKdsj#Pz_CwqPc793c4ABNRh2Xw?PhbEQ(itckBKkdNI<>+9O!)zY`0jT#SuNP3I0QM4%K9@Zf@c+IfT zqQJf{ExvyQ6~e51{IBIbY|*4BJ5NoijQjsKm|Hd)x({I_-PKso4J)C8ggua>R?=&e z3?gQ^wj_slz(swHRJ)18sMFyH_`5RAe`#yj`E5e;;yAEln^BlyF(n$B=U&{`OVw4T z_dKM&7te@@CimU^ zyU(nR(77lXXR9`0c%bv+u!Y(%t!73!^1)2~E{xPoU`aS;J5HrE^D~>5Z2;KJ`uq41 z_(SCCzGipSuZZc0U@PBxdtw9Rr2hA$cC2+QAgpB<`wGM4W8G|oykxIugM8t z!-g_*ZsH;4%*_Dd@dhqtSp=wdBv%b$-^YWW! z$3IEL96Ki8re+0R8k$7sxXZLYCf(6~ZXaBC>2b(QeOOuU5+D7ODp8KGRUpz4P|OR7 z8eAOEr~TPXni~eG^gxa`PamSujW7Y?MEIVn{py;4C}Tr~%z*NBh(z%vLc{`6-xmKN zWn$m|u=xK#%D}@Tz$3i#GXEemNJt+(e0Z<_*}WGO6qNtg_@{+}`p(R}vovUEA3uJ? ze1~RmaBv6+$lghr^8Zhy%sV0TU!)8SECMV70u<6a>75}Nvf^@=Qtdba|GNCj@%1zm)c&nZt6Ny?)aaa|3PJ-VPWB5VBb-le>k0gP#HD~ z6;VuBr&uiG8vh(I%5y6AuE`B-)e94GIGq1?T1IR+@k-2_$x7j!kNM4L^%aU{Bn_Gl z8JdoW9M!6p6;FHaI1Cyv7oNvSZ6skO;U^hr5mn7|;!^vi^j z8Vcy`B$o{AGMUjvN%>T3lo&eQ^7R12h+ezCD!wMwC?RmjE*v$mT6R5RA>RwCf_b&O z=hu3Ln%PZ*!_;8th4mlg*y@u@nNcPnVFb}6x5R06IQ`H!Pq~N@uR?)CA`al(y?QD{@{DExaDzS zmC-5E!e;y^8vq#oIRC6S{)9b|#vqJA!GiPW!R}q?vXagCBr+Zk7ryTQu8};h$W$!5 zdS`cykLGW<&)2T-yvtnT(zbq#7Qcx%ia{G-Ti-^o`eqXEwy{Sj0`g^b|50(|%tKq; zI4?#BU%=>Md1UfL{{oyMPXR-=KijA5-qPT zhpz%{_m|M7S?zv+80BO5QUm_ED%N)4m}5(J4h}X{`Eh<| z{3G7n`U^GJOxhN1M{w>PG^_B8Xd<`3%t%^vCW;i7mVYtc?hHmhTksl`|4=iig z47@*K82bBE2*eRkUJ9M*q*KJ?;*}P~4a{MemMlcV{SD28Uw{nqBjBa;kpCdUCf(7} z3w5DW$=eSG7DV>Giu!1=%xUzeeee{os2KMm?g0SEmS~5#I(qbyI0P@E6C(;}TYZD? zx*EYk`9X7L1jFWDraxU<8@q~rQ6EEJrhQHtrBWm^9+Da6ILza$$z*HlW9WQeAX*xm z^nEF5lcWA-m<^MMvdUwtwpQ{juc)>dSx@JJ`Q{auuT-blq>EAB6%Zw{p_={}uT&mt ztv?5IqmCEgElwWw!^^-BsI5mA#&o?{K};yjX_^JS$9$gr8#+W?_Ii{mMpH zI_cv(D&y8sB4BrW+*IkDb^yx~Tm9D&RCf5HV)18KVJ6~5Dp~DtmZ5Ul4&U=O{iE!q z{!eOm7d>$)p!AlU@&!XWmHx!N^^kYda<_gLQAMa*bLG!6ep~uMh7z0F=SD&&71#^h zE_94vCXBI_C{|PR{6gN1^C%Fs*|YQeUy{r+TH~(nDmTHhTb8pDA1NOwsm5EquU``b zq7Nk}Ehnah!z$B$ANio27Ml5wrtw&#O&se{>FW7>xUzEL`A|6U<=fPSI|cz0?$cLr z$Ihgt0uD%Zn$9|jKs=}T{1+9u-Rj-5G8>hQt$z)iF_26?5)Z#SBS$8`M*!G90=DmerE+7<#Iknmy#<(VGF=}k!cFz6GMjC9MxjlF#TtCN>``} z48B5cP=-i}llXGrU+NW#97Q;i2hJ7#u9PA5kT&~hO7L#r;~WSHBy?%Pz5@yTx2_?W zG6SpN;GGzi#p6!{_cb3ZnM!=nT7l*=4B-dfnds4SYc8{w9f5_B7ekdv1tV7WnH2>a z6;Lf%Tz(<|eGYGjj-x_1{G6bHJ9iBtk(JbK%AV>efEcWG3OFN_F!Z4Z#l)ZLKO7!bs_mCxiJ8JpEg?)mf-HAt5D&c<^J`H%(y%qppQ4vJ?c@t=bn z+gNVNtqO*4iuF~}utczFkl~TV7@@g%Wg+o_F!;2H#^~hlOc^$LW-+>Yk1J~iIn4W1 zs?W{u9o5Awl!k9H3QpTcKicsh2E`Djq8(KFahdepOcRt+@fMrK-Z{0Zr5&}bz(vjP zHgQy>@YTCk#L52hl;q;85Ul7uzb<8)=1-d(C5~x}&eh~+Kb<>-Q9i*#s&MezV*Vv@ zz7pxuv*)-1fNO7)ZB-bBcOvGQWe{~*z-0DD;4;Z}50a}ZeQc5vO-$z{m8*Dp17H)F zU2n%c5bAAjm^903wb1HQX~rrq^TztvN#7Ok^cQ{dro7KzPt^$ErA1Jte4T3lm>Z>p zD*ppRqtq9;4croF{#j8c%1qsCx(QR`^-N%okLPMnyY&63gkQcRa&yTL>%uH{a9vxH zxa&$%vbnvzfd|nv!Jwp}dX)b=;Qe_de8FXBaJHD6k!2=7~3?DJRE80=)Mpgnw6w8W(NJntP>@928UWi5_T~ zMHQ13x{a4Wj`25BxlQSoK}n;C6y{hWBDyz^3t$p8(~r&c{;J#f0V}7z5CY&;oO%C^ zM8&9-1IXzCA}QFv0stIp0Rb~#;oeEm9CrLj-9#H*MBA#$q@!(C!Xp{!#sk1oOi^a_ z*9QZ4t3P*Mg)N~YyLM;xLW8#Y$Q8Yp8v(BDw)y+i#c8q+@I`}m z!h3{9zB_mY1+!DO#Co}YpXr-f$k{u{j^1fw8%^*Ncxkfl%Bh6~Sj zx$kh<2(ZrNAW$^28yi!T_cEZJ)z#iMi)e=Wsq&E|dS_p+E4n}ra>4sFGG;GfmvpwG zsv4Ijn$v3%D4X*ixT!gZ4?s2R#bbR-CHTATSegADr-s7`e>Wn+W^#OZq@kmby3x76 z!a*a}WB;z`BmdpSDh9_(2T1;kNM6P{iaAKQW!#60?4sMEm^OI8p_8^$l1-!pkNz<~ zprs>e*cr)al8cM8R*xX%-BO?Vkn!5TJ^H)DGL0a189gyOT|G=toZ)bA{7O7RE*(+OXkku{T! z`bdd1z~S$+5xur$$(6$x*WSwWXvk1PkZ z>s8clz4%wzce*kKS1qonOHut9oWdgzO+*=|4;l#|u-zz{g`qK`c$92XZO&}sVT?jVWIEF>{x<2BU-1h&1&#Fd2G`h3XUCJ zNbKx3dY$0Mh{X;qqq1};%J}yp-x1Ei$jpM%#ap*@XGU@H@cMIJUGC%}lcd6070&Uf z(oz|Gh=MH)kit6)G~Y7XNfKU3)cCpXSk18M#>^Pf&>^)Q<+Yr{_l<#HkN))Zwv98~ zX4WH4kw5fP{twhvt&x5Cj^d6l31DmrmpS($N9vy`KGad+)!y=kGt|)3d zcm<5on!8axu*>4Utr9Rj*4U?RPhvMYg4n%8tuD6|OvZh7+Mrr-Zo5q9G-Bj8yp@ql zoj!DL1Xhw(8`#F}W+PE4+%_8&B<(8iVj!^fXfz>b0d>f}?igOURsFJ-aG$!V?rVOT zZ2!__5W-^{?V{{DC z*Eo`O>7H?ZRoS6*z!g5+`+1QD+1xtAiaX2-y_f8$V`)u|h#}Tn8XN2D%1qv8)e+$3 zt0awS+biM6ESLMFX)rf3fVqXNn2K=tb3=F-0(8>rJF_ItJ5jKSf*MD~`#j67|2VkD zSatu+-B*?9kf*Bxx;GwVeYuj=Slhw-ncl{Tzaup#Hfar7e_V$2%II5Gb5YSy@xlIv zm6a*Xs>HK{`@Fy<5vV>EZ`N9izcyLvIta~IX!S8zwIQ#QRA4|GBsWNn;7k-{TkUW? zkiTp@A-gp?JNxuxuZ#e8Y5$ldE(v39{mu*WdNDtMUv*Df&)4SA$l?;5``Obn;yh}7 z(9W)FuSK`|_@s*56VFbaAF8g)iVim%bIt167Yy@eWgL4)*Y(GzDGI^rddFiuUVTRLJAFXCzt`=eeKy`y480(yWXA z4n3+uWHD6@M!m9=Q|l6&<(5w54Yj5^q8hD8Ig@q2ZN5gMlVTi6wn; zh58rD^+oqTvstCZxxcZ!ynh<58dx9C2jg;$Vbr5b{uY_psskD^gYW|C1dHvtO;O^6 zWf;6GRC$wmlVqGXWx&*!JV26q)8&2{ef{Kv=kYJ^B!Y>o9)Hr&O(#Qs#bGLhAvbR zOk+a$-eGXQ7P}+RRQ{FVk{<%QC-pl%7})sg&U9B96motTUnr}yG&G5LBjGp`JDhb; zYZ=1XFxkJy*|0S3z8k44=h2$NO@(}%7A=pG(y-*lyHUfl6OD{E{MnZ&wf8}oSkly1 z%D-~;V$J>4xoPf;!Lq2o7f7lJO2c6)s-G#LMY{RUbk56=^C3HJ-{FzvN-yknCp4ZrAuh zT6feQR6=5lC;PJMOVd*gho>>8Q8!f9=6DR}<<**zYF@7=C+AIC<*+zFtWlq?RlD*| zdd)zEF<_W29EoWZql+|9PWT~WHK1S*;wz_6{H!(dNBiIyhy?^-Qx4F?hgHrD!A-I$ zvEf2obu?|cI3t;yNNvLdfV4GoYU;YnB|H837k9!c;70Kz%UJxfl2qookB?@ShtkV} z-|X@en60yXzLJpR(7~FYwh`lDMH(xZ;UTG=t zuw8FM3#UD!DYY}+sc`FBOS2pJTpXg8t+LA2#`357K4eJRwGxLigmHi^T$1MFfBT=Gt@{8@v z^TDjEvr@{OI;%dr0yP>`Bgpi6a>nzN(H-=NkNh58I$1st?k!G5(xvZ29xb_!G58|krb7hqSBN3FtGg@Ad3DmY zHe`KqaR_5)1+g_1v8Nl?%cM4+eLCbB`9`-bvMjqtG$BeD$=cJWI`FZnt4JB3uD_}Sm85DwjhL3pMt|?kb$@UkU3}Vy^ zljB1I5?2`I3!abLYku2Rkwx%nis_5@8H-x-ThuG|OmmH6KCZo~K9e)kHDeP;ai3uI z!h@f}4epdle2s-Z>G6C-D~elLavK;&-;16E%IB+9H>pJp5aWHCt#qByMf?-4+;eDu zVqfL|Sx21;>C?FL%OCw(NrflxNs3h)UWn0WBI?TfzZMQ#MpGO88wzYePNWIIjOI00 z&EE~^j2NaBNX)5#cVbM>6>!($O0Jfh64GJ>3dVwE>SUUOpdI*2BedA&zaM=}Orf0; zq4}FOq0G6zUsB2naR+i_Ny#wiF&uZd%@GF7(o1PitnX!f2)0vrmD&GBQse}m!Oozd zCUi=#!vVA9=%NTqt4||3J5$3dLDdKZ3cDrG_~M}a0SzmUr9Hm^2)tuQm;Pe~h@sUs?ZQ1qiWO8VotIcp%T{IGS7}B5fPJ~C6KB0oQsu%t zWj|M%HgQpcE(7@%X(_$Kl}w4Hc-8Q!S*>l#(ws3oo-{^JvvC}*u6$@-m( zkyPHfl6qg%%8CA5X8jaPU|>O?wcPkr;@xk`9Z=LQEIE2NDV#9vCj%}-(8!J+TX}1 zOW)4?g_>iAd$6l$??5H&oQ+I0NGNe(KSA|v1(y;OE)%Jb*eOzWhU*S>GZ4uolR3#M zQKo+@Z1hRmX;gfq+=BFeqq&kY9L>^Dw*aDxgsQ zw&7=*t!0zS!IyHxTN1%`WCrsdcN-5{GOK*m;+Q`!-k01DdT07$PJHp*9>lCbRQXoJ zUC~#cd6u;HD$m;8(@!>y@4UYz>{%)j%9Obq@Yqo9!n*U8xiG$jF4LI7Ebyhw#xm6* z)T_Z|O5~GybQ}@6ogT@!1J2)r%+C`2w21Owk;!c)5^Z1y-a!m87^f~h*XF7i6moih zXNiBAfP4f7xM>$fEDp?nwEHfH8KT7?;54Rh+n3o+t1_>)EMIy32Kcs5*K+E9J<%{q(odx?^w{n=!T%GGFdwTjvo>C zoAzL9%k=qZp3JADrj^ybUl74*YklT=fV(wf$D`t9PEh&1C>`qOcUeQWa%`s$pY8J< zrzso&PN3zpfr-|Z9qt__K9+q)Iw>OMfVK2>G^3><)8YNTk_};=k9QHoSjN^1FwNt4nXp>=qGKL$mVo!MX;IN8#kV;bB!6@sWoC4tO-1Whra;@XVc* zkK(3B0(^0&_t2*@Jl-i>kCcf5Do;|jU$39+VV=kRKWMwlrnuU$QPYjPyF+kyhhV|o z-JQmr06{`<*Wm8%?(WjKLvVKuWcqpc$d{?wRZ}&8fG@0u)%Sf|=k=Cz!*n00HO2g%%-tox48=85NB-=9-h6ekziY90G6QQdf3HX)LM5H&sOiqV>`^1_}UWQsg0iY(j z%-6E(#nvmjYTRF%j>j-!PUeAc(`U4;g@5hQ-5IXH^Q-&;P5s%D_q@nPwbcM z@1YU+BL7p^9*s=Am8u)W`)8nE+D|N39i9hY(5YR{p;YxH^%u8;KR{;xi73%2Xk#1l z`0T>CxGnB`rpF%_^T}{NlHTrb8tl~>1Lv7cnTPi`0xlItRH_agC=yIZYX%UIMM!mY ze`E5PdBA7$IA;|4$W+aQX^ZEMv;uP%&6g9GpUIMF)fQDDcq>$X*?^D zoNts@2JWhuRSmRQz#3br*M?hQqM&N}AsX&gLGM{yJ1VW^z3g7B$QqezNZ$Ccd}#Xv zs#xdW6;p|L%ZDlG>9T(S;R>}f{{{O3+mBNIC0&I82*zC#QaF-db$G9}$lh+n?AP@I zVGhq7A81QU8V^45+mpCqHdOa+#M14>Km3E8h7-L2#K-M@ZtXP3SBCyY9eFd3SKPGU zwm2;&^KV{Cf9uhpY4|tr->LcVe0zVs|4`y3tZm)a^eYs|i$;qKzK3e+Y~wSI@>gT+ zcUPD;Eh7ticmkll@3}=@4;6gsQBF;sNy{vqvwYo#nP!g2Lpxr;9l0NL1U+BARum{2 zv-mQ-q}E+QJlV^gMpgG3!Ewfsex*Kd&TG5+gd&;EJ*cnBV2JeD9^n&i7?DM}NnO`6 zNF*`Ozudq)Xa2dDB^`bygmwq>!FV<`90T*ay~6;uKuBSGg%x+x4c_60vV((PNWm4m z0i)WdwbE$;=75eTbg1pyZzlr0Iyj)W5*!TS#gM!*lUo{(jM1FL%V^1k|CJbvZOf)) zij{Rib}NWjYi)z;gXXdwb>Z@c&#mLA(SE!!HPVcDr$iZbe}+2v;EKt4SBpB`JgT%} z%eNt6U_-W=E3MwDqUD(V71e?$TbidL|Mt$mjijC-qF;mAPJEyplok}`j z1Zg*!rUS54u9YVN*(2{!4pHft;`bZPeauTQ9@i{Bxb&p_AQ4Z#D3r^fEf}s8`fJAN zKfw0gx$lD*0jw@6z(2v<^}bJkU1L;dMGd6BaF`4jMK;$T=k<#_diK2xBd@35Ovdf{ zh}sXStV|m!CCZ#8iEB8fw~i;)X!1Z+QYq0|NE3az(`DU)}nN5fa8EG zHeZNZf-C33dW+juc`zit4%pYSVq5F&v#l_Eh1oCgV%7OF5{LB5KqT7pNSe`6X&hFAU%zz9x|A0f8D*;lp{01ouH~^7pwVijQRj4yN+#l( z?%|G!hLmys>)3>`Zb@c&t$1Lfr>oM-!g6Ea5`5~^e3fdVkM!V*hx@web5U4YQe#eP zaO_Wo$G6sF_m%L~ocGp;UdHWtLv;BQnm;hkbl9l|v!|TPyuf*$0fL0G;$)4xXwlsK zXVhO*9%e59OwM2{R6`+jFA8PNa@U0(?Ece!t)=szpp{L^;w@pwd7?xaabH`U?kLelDk5Z!wdTxm_OtO3T6|*GFN(>e3 zDLloCXREIiqJJ%vK>2-@;q-u9W3+W6&iXqWgIy-wL?!c<6^g@$Ek9FU2 zTw4FG7mPyIPwJ(GM*33U`B1$K6?x@VhOOnb22h_kq~K;g9~m)DCvZ>wzbWlKj+}3h zp^QHvYiglZ>%~4+th{6TEGIRhDub}!I6L|Mi>&vPlg{l0d=gN6gx+jmA9 zwVc|u5y2?37dx%7L9TAqcN*gRUx+p~HQm-}HX@G}=9V|Ar}y{Vp2tiJIxZmjzlE9o zxleUJFJzg_)*FXK{yp$SI@!cMeZQEd@$Pv#B7cI)KHHVg*F2pwjIEIpKg=bCP~fYl zcXOTyzj}t(eR*!XV<}h3QdGbABDO!bHQMdtslE7^?to-#<`!=2*=MjelUH{2@k(W(I3p{Bi}>rPHgYr09kC5VzGoDXl4Y|dZEMQbkRure}cEyu;E z$}p`}C_~`NXkRVm=kWAP?Mv0jxe%$OE!OuO^Oc>yx$Ga_y$_TI=Y~b)j+NyvU3Wdc zI|kSF7p8N<(<-M}EcN|^7NWO51L@dgaZ#NLb}OeMLye!01D{6L;6CgbZM4k4;_T?7 zouquvVan4Pn+WsdQJKH%K%Xqn{v#L<)P{kWw+;TxEWp&x`$#{8=i^cKUAT0jqD?U-ymQmGVyY zj<=|D&R7i4{TwSh_L1`sAah45$9ItKzVZ1F5T7)>C~;p2w-ELbA6fK?>V@1#pMG6T$T2qeF7X=n!- z0)^6T9V}u~ZAi@*f0RR5t{QLD!>nGqwKG5(pKkk?+{#L8us3lFZu|o*)uVmKdSflK zp#V+`j?{Z`Fit$(Ep5TI{vb5)wQpQWs(53J)F(e3TJkUy*8eOzbkgZA#ohSHzP@UZ z8Apwxx`dXcw@f$R!Xjt}>XeLc}1`xmCEfjSs99u7GC9iU{naMOsb#R%i8sd#7 zWlr2m-RCPuX{Yxi$%B}q9^;l@rhz8P#s+G&w3YoL?%x{VKbiPQ^Z&CnCo5n}m6Rq(;*J3V4B8BJ6*au>VUXVzN^}i{*bYRdvA%O6afWxB>qJ>^&HX z`Tsph6cVV0(pc78ypZ6c1jO@vg!fTV9yMb_SHoZ8Ne`jeNTMJSLVSY&aOy?QrG!_* z!(tSSO(PJpM9*93ghB8jK{{iM5n@30HBm80RqH`o==h@rHkj1W$e?tXG)#&!hrbjn zrF@m{IhDgA-PdId=KPW<4n=6_W@S;+P%*Q80JB`Dcu$*>GNy@>90R;#W+RJj`o~i4XymddL4gE zE&5ncH|ovxAY|Z9{jve;#J)&KXrMT!_*Ypu8EYwo!E2k=Yt40X8I9)x#VLF%SSDm0qXjU(5b5cnfHZfg>8 zCyU+iRlb#ghUZ21;q(k5oZc|bbiM4Sb!qfhg%9brF7N?H=o_z6kYWoDTYG;s%*Yiv zz(BNlQ|h~i`e=;B;aA~w+i`KQPmJ~4xZz*rJ+&oOw%L-(Nk*Ha{2Un?Q;UR5{?c`UU~af@qeE^cH%!IBI$TIp(c2d7O9MCo zhr)UL;b~5g&83i1&=nu|{<5H$Uy!bL45*s<)+k>fqq)OiXV+O0z7QL;LI!KeixCwb zELWQ%bCzg3-B>pNB**$me^NPGH~sRtoR`p&z1oP~QB5mWM;gDd56m@F)B|pbjJfOu z5zyL!Pa?Bp^Z`{)*eaM@`czUy6zdqlL>S@_s8}IfVu=vHAi#nQT$K{{3}Fjj1c-)X zLhn&H3Ye$`H&Ia}Q7It83e2UROGUt2?4jXSiUZ6G)Ox5bK~VeJ+eI$HuLgu8M{0@f zKU`}(gt%_Gmq(6;2g@7+dxvtV97j?bW)6GU@1@rD-_-P;Pnc_c7^@CH4ja4b`-=pY zVf(mlsj`{h(~{>kczp1s6*}noEj=uLTw)9o`Kk4+4DCHM3X2MqGRe6txSNgLc1oWh z8d)D+Rv@EPKd1TEdc@k5m=QF|Bay+^?;7DC1jup?A4n)1)sa=?jjfQahSFERDYz2SHin62VK1lC}%yPu{{YhCE44J>HpzG zlc9aFx=#5)opUP^OTlTs)~35he_^GaRxv0$+mbkB_V|7#7W1oRh+nbpmO}}^@ClN8 zrOyG?&%pasW9qQfK}JcZ5jn;foVPK~uDKG;mr zov0M<8(iIIp zMEUk(_YwsMup@~HyEaOi-1x>FFR2{PzS5XGl;}ZQ1N`l{0z-jjI^=COMW5YJB17Uy zr0Xc#eCR&&dsO~7X{lvKqBQ1qLXla2eGc9&XW(Yu#Na3b=F&vQq`StW-H@>?Qw%A~ zbE|@;$xiWGnAq}z*~bdf^wSTsic)VT->mzRqMDp4tt(jqdr+ zc0vF2g}U-y#@8%|_c%f}lOmEJglKGdT_ zFIKi;-P3M+E_dP2H33=e-*YmpM`c!|RvGkFDJ8Sx2F!uPMvnL^?X@03oqEzZ%hKH6 zT#`5?qTE^{;WE8+idHMrfq2}}^=GF#n6U<4w%n{i2xikIa*yJIO)o;ZVx`LCl##BX znm+t60(r^O6x~pOL%a@N9Kq!L|4f#;>Wh;clUILw?CphFVWMf>xb>h}j#2X&Cy7g@dkv59uNBn0!go!1+`X1*Nj z6Zs8<8rZpN853uzGmKCMdnWFJzgQ<(o@}OkMPeDCtWuOE)dN%jt(uXb_i3@z0+ z4!$4roM2-Y;7M9O`dp%xZB^~kfgAd}!ARxI+-gc)1Up47FX(LB)N)sW)Kq_uf)x5{2Hq-hSHI%OgCWWm7RX)W>BQ8`4_ue{B0 zsJL_CtG|H;^^QK5FW<4oaekI0DyMUWW2#I5ix}(fhv}5RxnfNKuu|lz&AeQj9*s;r z1>2!0+X}fMNXqlTDWfdhJSMs&4yoi5lo8m|Rh0qfAQ z@~c23-;xG&@JT2|>Xz9Hc5rCNWR)?wgrUO%GZ>m;-?zZ;P&U}~-YCe0ZysAp4B?0W zClno&6&(`s$QABNcBmB=w7ZOCmGH0oJT3eDIt&%=arcxaoGz~{)R7N{2+xIFY^Q68 zk?f@;0slx1Ld|hZA$j>!r;ijgo0iy z)DUXAZ1f)?-OSX27x!j-v5d_do{A|C{a5%#(gYB+6Xzk(@mvdMQ=Bq`dn_+0)2XNd zV<)kaI1@BS=&Ex601DAeJ*_?RnYC%#7Towz^{2IKb#+jAtuv?3{1z5WHWSi1Ig>`) zb|Dv@N?0V*w9uq0w}(eZ2>S(uY$8|LTT`^ zck`MG8m@SV)gZ1)J&p9swhezJNx*$fNgv=;|4iQB3V#HWpxpH>C;bB~T-dbZZ5oi^ z9`oeB=?KTkc6JLj>63B;k9^ei<9|Nb8vg@4tAS_ITz@>qeh@kWh`F1J9{TBC&$>|) z5p35&Wuv!>^T=$qvalBIwrbtHm%}G%0BQ zMCNRqeyCy7Jfx`B`7R;RKfzZVI>){zD-7-JHRdPuP@0=Yt{*PB=%?uC1$2hS-n&LywyVjwFzmZb17% zjqGj4A#egILz_sMzsGwp&n-QS;>c#ABKraLKlL!uyjdBvxU|u*)%L<8h+-!%ys%@>RUaz4xVp*0GiQf|ZsyB(v5rTI1sD zMC+FVxjwiqE-TPxUsO~iO7UVzh`LQ?Q`W4sg?^A9orj)gRJ~!ZJZ`-;;J>xbPTfcg zs2Ft}VLQlIcg)-wFl2)WOTP+BQUYFeT^@ zUQ3!Un+K6hoB>SgdHVKW(>TR^_K6G?!&A^dT_~3$!Y;!>2;F_DHB#6c1o;7tUjkw)M+n zgYkJfc&A1nkKrd>(sG;h3=lxY%lQH0_Aig@DXffft&HGo=~YP%tx3sf@ZekQ(1oZ@ z=F`LSM9FX~$#!#n+OFjYuggl{l%b_3s4b%&M^dOM&ShX;Yn+@^XDwo<0r8w)Kb5-B zsp2eDmW8!|rV{{F?yGa2y9D7VTjiSD5a6;qQk;nY+VY)c<&F0+6#M|?Kc}_lrA=}j z9k(}d7mhKye#ZL9S*3Q`MvmT0tATFq`wHi-=hO$rN`RSx5K$tD@mB9Gy~^G;!uf-D zD#N)%!R^(W)7iQms3n+b53`Cl`_&aB5Rk1Qo&3%cP{3JB@h2F<8KuZFh4!+dpkEs}s#+A}|9_QPY z$Q2x=T%e7V07zx*^=>Rhr$d`vTLuH>aNP0<-Fl$M%(8YvLIDqZP4aQLKdGU*%9Sg7 zi8QPZI+CAGw(r?a1E8gq{EP{)K72M-b!)z@XR>Y$-U^*4W`>-*P_Tx`HiscKZ0M=W z)|W)@JD(NW_iG=`UyrKKH9zv5XR0_Ft9SOzeVp84i{KFGN@Ff(K8q~DW^m1WmSe>c zd}3>{LG8~iDRevj6$)u{%mTP!?&6uo#}yPtk>7CHD^I*4i~=a!*s@M%6_Fy8>i zb1}Ffc>FBM4Cff7sQxcIv<^In*oj}ISHA1y>>u;B$F;M#@KOa!b!$s2q$v}ys3(3j z{^Wfgmk(1r^!&)HNo>t1^{X|vteu-=#2=fQo+F?JO6V9N0J^yF{429nx9YVVUMy1b z^aDl43~6cXXEq9RoxLIw2hk&!sYWS)S(gYcc)Sp>>%m3O!eB2bErcnQ@B-iKGN6BN zhy<6UkE1uG&T4ybw}`#(U3p211>%h51#4J z73|QCGOAUdDWSVYyS`h@oo}Z(dlM3SshaL^X=iYC$MQb_aFgcOAS#sibX1V}@CXfG zR07xwcCLs|>VUdqol@MAf$b*-%`J)@ldi`+_&;4HYZ#C4C!=Am_J5462(n6q<}hf% z@0;t)_qhnmPUV0q?)_UcDc4#~Y$<^3&27d0J>|xg)6HWiTgo9Yc-k|Xjbnyq=mIc@ zM`x~9X&4U7wdSDHH@^LtcB&ngcI(vO$RYH|p|wzf#V)OoGCJpUay@kNAU1lIbaKR& z6H`9=z`oM%IglYaJR&fkt7Iq;K5}$55B#zOEjvho+i=2Dy{u;)ydY(uO^Y2uPv}z@ z-eG~@*}M2Vx#mKK9scz`K?^@hzRw~mKZ{SRjQyewuOQc{u0sKB59uaozq{6 z@N=w|ac4eNd-Cw^ggrX4etjZBk1na<#t>qUlV zip;F+6nbQ;T{x0O&Xa$IvE#TuxL{^-X{zq!@W5x|#{pA5x+qclK=I)y-A(aFDHa8ruF^ zJ#5HVx)qO;@*c*n9O;9!)nAQk(km4!T&nS|&7>jXTI%pEz#TlVKS>NeX6u9}uGhYx!X?;%t?g_|YU;}$T4VGl7Spt_^aDy>^`Ao)`kS2sp`(U?A#O6I zc8@8X`M6m`yVGXneK*UyX|DCQQeY?l-o%2#y|lX0DBs9?;+h!l8OkH>ZeIIin`Kd@ z_4=qXjc(H)YlD98J41z7>}jT{ve zObJOp&QFXv_LrY^GeXxG{qNG#zVZN8d~8ZqmxjLEW;Lo3hA!?_C7N&9$a@ri3*pn_ z0ynbfAXt853zl^703w2x$c-U`U|=|Ksh;Hm!3nJ%QDm&(Ump1Ye182M4#M)0hoo`xiC9sg@+IZ} z0E64BPh)dO=)10zRFi?LuNATJu)^EKjfNTqqb-U+rTHH)LjK~w*@J*|;w`FtR_Yny zY_~^=Ya*Z8z~+__j-IH{>ch)r#?CD&=d*#)zZv`2i4Qszr^n0uI-keT7DD((n}y6D zWy>tBB8oUDLM#i4q|B5gL^s_v=DkQkz#iCPgZwH#G?+Y_ZoPuojOsS)Q2NaI3t)G? zg!1@?K^Qi(zhLiUTk1bR&`a3BxL;_uT<-@=f*WCgyR*ThF0~`iTXdxNP0Y-~+ zklbf4ZL2%6J&=sMmIXRoo*g_2OeZCdk80`9WTiv@;+4dqG zQB64>aj)-^g`auU*MTGhtU@$Y(;)oRG}5fNxYeTep^qInv`KeR0q4f`numHxq&ufA z^7u#Pmeu)#n(VErg-y+=d8ssMy>6Uo(`sIg#)tg+5==i6MNt+g^U%HQ!%r+5}m7;k)y~{sVLyKl&l4={rhIP2NNJ zkiE#vJ&@CnBShQmvwr3Y7fT6{pIvLK?|m8EubH@m4e_XN>CTcq^ayte!158A`I?~b zNk2>RqvZJ{VDfdpJ!ap)2&y_wLY*dUd8-C)x7~kPs(pVlw=5A@4+7ET`iJXSCfQd! zn!k}{dXo+>s<}}k-9+e9(ca?jTfbzw3a#S_Q+;hj`e91teig`##~y>|S3ty{7hXU=cTDj2C5LRP?m(xn2_!uY z#<|Eifmq(61{op%RAlv#MzP6dz5!!EmZl7782+CHMfn+89|Z*{GSSplMAHjYhxlrB!)-#iRNjT#Tv8uns7RZjDXD9*|j9jErnnmeUN{)qZbch2Dn8GvEL zGQkYaiCig@{b5jx$*cwkzbtI78iZ~Rl|G1QZg&%pxIX^$pX(=;7nQnM_Ik5+VOE{$-&I6tV8A+5o2}DJ4silxYOb2(MGqJ!)p(k^l`Zt3; zaDsd~j*Oxp!$Rj*jr8d2ORX|CpTpH(G}^L>TE&|#!z-|z6VY+ZQnEHW58$uR_jyKO zbe?!A^sq>R0@g(ohROr<9Y~L(!XC6@gqdsh6~nX*{xaWd$k=nrGjaX6F@OFZDxm@q zsY2=^?Yh5Qe;M5}+`QD&3MqzqsIS zf_(Z62(F@fU_q9{h&AW<zpNc4Z3tJK-2BD}pF>k#ncwO~NAmlVjb?hLk%AOn5I6FgNkB!_PT#=fsD^ z$RN?NV8OTH1ZZvpxr6j6f3kTIn&BCMHm&)H)c8V_KCprAiYQB|m9#wLl z{P*iIap85l_wRM2xQM^pz4r&W+)0w^%KYYr5e=hHLOhUKhBM@6I!CAJ9&NRG&(;`! z>M!xyFMEuDKwg^q8Y|1<8k8aFh?2dcv{=frYYDqtFE68TIkIAH?4`f*@5!hcWLm^< zVudo(E$j)AeFA;F1KuAD+#8uzC5iNt=}as(jk8OnMD@=-MXdue`%IP6-I1PYrm^H) zUUQdXosJT9IWurEE4&?7!LIWPV%7`)00?t%m~$+DN~0+9bbU0`L9!jq=QoVR%pHk) z_B-JAWJct%#+c5a)dA};Ox>7MU}o%8cNy0%Y_u2bb9bw5P|w>E+RjJjt~ME_`6^Yv?4J2B~v_ZF@-ERoUE`KMjJH|r3dP?44(9#Ofh3TRIV zay8rS$B=aV6KP)>Hng)6CUO#FoxNqb_@XnV7WgC+Rsqc6Afoa5P4wbb%M)vppdFTz zD^;-Vz@6FnGGK{>zHgpA-I9k;c7wdW{xzEEgYEip`xUNL@Y*9DwaVdi{MFq{7G%4&yf;$X_?c0{ zH|+N9nCICdiq=chG1oaKYpbYi1lw5?>q_Cg<V6%;Bz zOIXvdNl9dt24i!as5$uYo{*-DvVweWbZUif8qfK``ziB8_j+2Xvx3JL1JB|a(<<)K z5F6#3DoBRsEi$GS?=;(j8`fLb#BVFd(dob`V2zS!gX|t#Szys8??wdg$%+(HCn66U z6cZL38N%V)xCw6&qSJ&TO63FN3ydWg`m^MR$x$5b6(t6v5CfF4Ua)`WLzf`{x;NfE z+IyhcGRIPy63_kvj9vGe7>LcP=eJ~IZ#q=dx4d$#N7^&sYsgX!qv}#lChiS(32&Wte=jo`x zExWRuM(J}LLOXfbTxx5lm|5?H(;~DGYnaxCz8)QdR%LfKb-Q@f9-U@LE@n9_a+%iXZkrb7Uw*O39FN0x>xn^5~7|^jwis^Thi?eotYbpA;fn`;ql*Now z4#N}|xJ_BTo^yDtoB3<~Ghyr3h6&tRq&3FixeZdJxkLjGqf8P(;(3|YpT=gwCkAg) zJ~-N#;f&y;_@)qjS`C>4O8MISiqeEWjZv53+mq{1Ima}cbs&@ZCgH%3p&UF*e+UW( zu}N@`*^-*%`Z6E^FJdi5^-FAlxyk;%hv9`?~bd{!~*!m$_pG4QB> z)ip1!uIq_MXAZ;+H#rfwJS0lz`c4I%m!SOt&?=6@pzFc&Rzsf^?326q>QQHzGc%~T zpCl@7n_#jgy3^6a-qqQngtqRr6)AJ{_RBG-RV})w%)RlmOo`ck?SgGoS{GVvz$Am0 z%hcPMFK8?bX*1KMrGvpk-=8IaCY+Rcu}ef$Vd&Y$_#rBu*vT=qomT%y z)8|=uyMB1D8T=AygMb=9-TSnsU9hq9d?EiKYs^{fM{?+{ZxFEP=KTP-2dpt_CZ@O$l`mUYHgUfe)7q?{f-&CB6Yo)&rg3}D(mO0qFM1-*nHvj@CQ^5 zdZ~=x|FVvHs9D%81*ZT}3P^d_T4F1RISYLNBmqmpT3@~XXr%y1kp|441 zmIl~ugjA}buNYgTo;&{5ZC&!Ws02reOc_~H$cepZZ5JsFZRqn0G^^X}*hu@D zqG;9@T5yVWq79*uaLm|}9*CHSYPeoBWtQSIv}Ch*PpNkP^^&vcx`^iXS`T{m^oVMow z03X8j-PaYQ6XMO-Q@2-tW#zQ)c3T8^JDZm{S3f6>mRON|+>I8wlt_%&b28rIc!Z=i zr$?LAW5~KLr$3av13pwnb5Tjksy1x&Ja+UzpON&2H#AniG}fE;b2sW^=r8GCvatu# zs>3heEkk?o&7G7||0cUfA5zebN&qe+WhVm$&jDy@iFucCPQQ2F|Ayqygk#)to1%TH zL>8@+oFQ<27NJE4+`R^5CAbFs-Tw^NB)!iS!IUd-G>DBX5x`gArs4gr32`TNG@fa% zcO%ms)Dy<-mfF7T!*hX?NMIFoB=qg&z}8{iz^!c&d;@~8pGW;ca^2q=@!;`gC{C_q z1qvLNCgY;LGiI9`T6ymrVF&ZKu|0Fss0B2~F1EIu7I}@uYVkzFG)R}BKT2LRmv>59 zZa+72&=v__@vvN0XcfItL?ek zV;?4}Zx)%t*9Aevh!_dVXRLtMD@0ibr16}bOXzcK&+t>0?? zzv?$=a1#&)7ViHg%jW{_@?mr2*Tc=v z`@eQrg3EkRF#ivMqhN;yFR^sN`oa-ZpO7yO(?1VhVtM11xMvnz@V{qp!h+Q(io-U_ z_c+mWk&q)HDwudQp~<4ST)H^<|6|{JY}WYTAu=#BFgY!(^59I?NPYAAeW<42nzlj2d837xDI&|QMeVBzf=9fOThDo_sHjaJ@_y|NM zv;P2`nO_Jj_e^_TomRQ)_w`X*i*L^Df3@0hFZ*e!N%gq$4hI^TswfnvK`B!hp;8vC zOLh1_q)Ne%Z?X#@AwvTy%x3ytO>yY0*oK2bnF~K@1ngVTB%6ANm^4l@D^c zOcLepqj<9rLy2vFdh5E+|K{|e$tjqJ6Hc3xL5`_*(MUprq=e$@fh3p%xSZB6tmVRxRfJ|qlqpVFz}!-z{<%BoU}8t| zOWwiaYf0K(o@qIV zwKZK&tR9=p78S>)H@i19jVrCRDp-5zd&blsZE9Y0WweusxG)Pk+{kP|p6^&?U%@p8 zry4W-#lPre8R1@9x;}H$y^@yAB%lwsSGVEl=Wel2Yy7>}ynY^ad(hR;l4wVN5Nw5i z5WO}nBq+@$wR?-SX;mC+Cfnv>YT4gM$}{jMV^;vgN&>QCaSBk}^V;`AbD1|Et`bDr zMV#_5b3d1NtX!l@UaC>kwuV;Oj8uuNM^_ScmUcxNkdX9&EKQNU+EPp-#pJ&w`yAWs zzo#(vUK3xIy=wmjBYC;C*!TX#BdR<`%?vU!`v-s+Oa_>!*IT2>QHsyOSd9fz+kCTw z8JmXR{co2dEPOr+MjJUF{Co$UxE-BnaL6SBp?Wg_`@c9APJ)hnwOkXeiNKtip&c~l ze=TB&t0tQgmI>v2m++Hbtesud_4rz? zCUD8q66SJAj;43#m_4?UeT2=G{);kKo-sxOtl*Y==m_t9R}D*4M);7$C=@-E6%U04 ze@m#Q9nSTwXXp|VVWD}hnfMy)FUKJfpKSS8yAWK;u3x_(NTF&P`&v7Z=~&e8B-YRg zS|kd7mnXdxskS6qOjxi^P?wd~RurB}zG9afC)}fQbYsmQGHhV8{o~rToy+;9Oq%!m zijZN5l)Pknw&+{vWI=T*;ZzOn#Io1i7zv7G#+p9RTX63+PhSu9uwB0DR{fd7d zyod6(S#{hjL=`&JDy&o2t(C<;D$A)erWf~Xa;ye_5I@S;jF>9`>c-$7K!x{CY#;H& zAfI#Otdzb+wd@Xc16m3?ESSm$54@2xnFbyZEeY=c_Q-7C5Fp(FS{LEydeP|Q`J8Gn zd3jJF&*FAp6eB{A#3VX=*Cg#X3yXOoW#Wt%_Pj3ANEy z6uf+u?)<1Tq<_1JbbC(VmD5qyf*qWTV>(0|(S~fBoWcmzA_4*vw5A{YoZsm>&3)wA;pD2EFeofW0JulaI2=doq3#>Bou21c5=$U)q0wpT%bCK>1hSnuAd3-|FvE zuho7xq6~1I3s^RV=;B`JVVL$tj>j%_t0o=NODfAJ_8wfb2}QCP%cHuhkO1C#=%x)< z1f=_qAGW`(U=!-5Hl()Tx^0Cwh#c5Qc0!QltJ^m`1k;GeUt^HyCEWR9lv<-F;OCZa z0DjsUAUf&qo|yf}iEmkYnyqB_l&O^DcfHU!qM2@_M_Uana1f0C8jy!9k9-g+?=_o+a zdgqwJA2e2;Ort!4ih3z4BxCZZ{v10Rl#|Nl6Ffx7u-kzNFy*=B=)11@2~RdAm#O`YykpDG3OCZ;26`N_F`x11yWVJA(Z0VYeJd~E-a!4oRAPRT8x+*)({Q|TvzM;5%H zLHdQNB{%kZY`fl(lf%zn?KcL(j9Wy%l2*{X9n<)tvM(I&5&7B6$93)9TokJDyr6f} zAX$%38y$q2V1O$p4DOvh%Cet*NM`pK4>0Vl>(lN=Y4hUHuKwZfY_fsH7&kNA4scjrCOpsxM6)!5AEOP%|I z?1!=|;)x}P=CaQ|mivEU#bGw+L$xv^-d9FP&62HHk_gnX3pq{|jP>6-Dox;-HF$7R zF>s4bRHdH}yiybz`wOH>uuF$14yPIoJQm_Y76muv2}-b`9SNAif`4VpMHDM5ay{U| zqQy$bcp`WHV|xq%X_NVY`BMeCu>Tr;_JrX;8J6Z&p?Z7@MzBFSp(;;*yv9pZFA>~@ zdGXZw-pycMc8}<(?)x*Hc>g&`U97QhwJD2O1e``8GS-yydrSNYPLHrSh&-%<>Z2h# zTlSrIecXss6kQUAhZ2>!rFRFSk{lA!hbfqWzdg7Ux%&kFIKJ3Dh2?w{>lUZ>LDt_# zz$T5OLUcIq$5t6&C3NiK*ijN^^3k;+^i|%nZDRChOclh2HyEWRW5GhEcquvI$3dYQ&r}k;HKf3_n^Jv z)&ERFwR}{!Is0N&2VzV=Ot8SpRc2LhJ|wnYC~$i_MVx>uoAt%;xi=pOE6nif1mqW( zTF;1FWBwSXH&B=1>R8QhtH>I`3^}-RB}y=dJ|N(=mGlw&j*rnQW!r(sh-fkh0|v*~ zfZa803g~Zuem3ym-T2^E_dSFX@H_Dr=U?u9c~FqQv1yW@MYZ$fOJLx!`?grVp=0a( zJ~bfRROZ6Hvd2?RaIc>Vr)jPuHQesKZKjp?w8P)b{<3U1=iw9bMQa(vMrKieMY$** zA9pt4^p>6+P_A@cG*NoDotn$q`jiz1_0tT6jGweaimN#f4^Mxc+~6&~*~{2h(i&No znOTG(XU8q{AHZs{%9gb{{rE{|`EQU2_mtHKj}5|A(eixXpL3&LDutwdJOgc2OAx23 z<=BoTznn^9chVy-vdTDu_G0VqJz<1kYFn1n@}IPaGH(XBFZNIc>8H{*qT z>asde>u)W6;%cqFQvWQ6R*}}Za+$xX2IrX-DT0t!@N#`GauC|5LgMEcel&|9vHsnc(&PVo+!P4iK@7h839?^!Dq>UIozJ|;f+^nL&!FG zdlGJ#>|2#@}JH=6d;e}D>s6zhT&}v$78Z1*!|_b z$kSM2kq*Uod$FGUN>$b8%`*lLh76sl##u>Z2Xg+atX1ReLfV6Z!|*yd!&>v&5-C3Z z*cKk=!yzI*7o_s+h;oyKRp&awh-XT9(6U5-ZU_(=~ahr{YSRNl4c{qv7d*g zaze~vUmwzBE@gy$NT|twk-hcGr2Hu3Q?P{7d26ZNcR7jmA5$_4pGlqY?IrUj^jxVN zadhbn3*DK3v(tK@G1ibb@psIhkjc4lncoD0)-{Uf4kEi%{+<+lpK^6i{s*`eDx>6v zrsQ@bfdcoZuYzC@f=NJzP{i0LqNVWYqUyn+>BRsX?zu;17;;3Eq%CNg4sj&+zI;PA zI(#h=*^cDG>5>xXg?;Ar0+!}t;q)@a{&itErgPoBx`g$xFCF-ARdRDjyM8^x2MO)U z7LPG8d$n6#;-LnLl5Mi@B&*+}-2#NZ>|_XQ{p1g=nf{9R#Y)4hJoIW|j<M=~?VvCMt`l$Msz{Kq>%M0^y zAug1*nl3~&P3qFkgEK9<&Zx!Qm|1@JJg2*(&h3bkNQ2ucPwVO_k2vSi_TbH|qqUVg z3aJDC|AV%-ifSur7;S?)#ogWAtw?cqr$q|{cZz$96Fj)PySq!V;wkP{pv9pl{r-Pk zIcJP>@5{+Ul8213AF_9{_F8kzNv?C+cbjq(C3l9LJILnSZoJf^&x$cY1mVW;f)3;V z2rr97Kh=FXkNIti*HlKAymn#+O+uWP75)XhB#O#f`>8+KODEJmflo3s_T;Zpcs2$P zb$V^!UZ%^h6e-jYJeyyol?Pd0~h+b2BWz(MPOUEi^RQ&vT?o@Fp_-sogy)6Hfgt7mX`c8yw$K~DP zi5XnBRmYg(?4iE45!El+%rb2oyWR_Tj^*`!tXGD81N?T?o}7=L-xOvp?ELEBe~`AU zc@fh#+re~hUV!I2=xQlh%Hs<{u7!jg{VWzIhOqosd>3jsD--G?Bnq^wuhnL|B)(Oj zOwWpB2}wlyZ2k<`heB&0hwjhiXH8^H{}J8gmZB7a91)Syr0>MLHla7Q7*sI90s^LO z%YC}v)8|k{kN~$-S4T}I#3fK>O(rbF#f2o>%Wi=~vGJF}?b?QVrYs#uk>1<8;*t(@ zD6tRn?gdF7pJ<)z(T}!#55z?O3pkrl{;UP@^7;$Nmsd3Yp^KS%-D>}gq|8bJ#n#q( z_G@f`XaiJ)!Du~IIrFo!hUq6Dn-C|tv!dh8%r$>^8^+;@PcfY>-`G;8-E$fQ77mY_ zYT8+}d1uN=R^-Lr1&r;`yy@9d^3Rho59#Cx$Q}jZexR*nNgIyWr#+nbxx&|SJ!-PB zV((&ZyHecd*OA^*&s_dBqKTkOk)w5VETEcu6)?YMEnm{4kVjSKV8ak%gG35RhqR5E zx9c|952V@P%I)Oy{iH3^aHrtl=Sp(hNPBSTBv2a73XBpQ=mtgiI{fAUPua{^LxyFr z?EJe<$ueeQ&^Eb6Pe^afq7cORs~fF9L+xuo7Wlt*F3n3V;xSVk&J@YD}_hsLG@h zP(Q}%I&RpmITH}gT0&iP7wVr92GN9gyqTAO-G`L^jMzFzM8w&N=uO>Qvo?&FabaB& zaTmbXJn0#SbC%wlz(-F?bqosGBA#MEDVZT>d9+k37+Z)EgAAcHr+Xp%xHd1-z1ZK? zNKQ*>3kb#Hruqop?F=nR(FR$?%k?F9tDe((m~6av|HH-AjH&jDmQ<7c({zvSF{@VW zk-!TSryqFK^5QKok@})IA*a#7OZzcs0j>c{wmy?FFTqj5Utjx2ir_OcG-Ll^13)iOi#{TjZ* zILwIFg=U#FTVTkOpPbAaNm=-E{;)@ka&(06qECD1?0rtkB`2AFOOWM^+f|^)t`ZH& zolgkQ7?Hr8{C}`B(Jj#L`4xX(z z=2)^u?v0Xxsc1q(Lz7Cp_q#B)!-rISe)x{=fZIMg9z_d>FR>vF0OYo>58=U~&X(>D z_>CTuy1k`23%_q%_fEI?_6S@0v{2i)roTkHeNO4wBtgh{XDLsy=+-;gX;Z2)o6~~* zrk(3NH8^;++3E046(ugY%^ya>R0%#7dG+VMi%opt;JuA-A#3ChxrDzBK z{6`bGi+?DNvQ^Wv{F>~FU;+zIE5EQ`Y6V=LU=NKe zDaw42ytUy%whtfOOta79J7H?~6QVGpIH)~#AM>xiIIkYg{1=86aDEh}te8GSt}04) zd9l)E-CjR3G|~=-kFYYc@MJW4{jM~~&N*KEmFJ!GDIHBt1UP%Icf>8g=w4OY5cm)_ zN{Q>-C&qcE`Ms3OwbJBu%H!!EjmNF0>f-bx&*{(m&VC&MdDE+unU3pv#CF#WYrauX zdx9!&I&z$hy;l|+4p}J*E|r2mVSkAcDiPEa7l{j@LT(mD$6HZ>h1ioXL#XqEYdV!n zJK_14oBwI!pl^|x2asVF)EUy?CI%dT*e^cP-B7*Eb;u+zWfg*UJR!p1*3gm!@1cMd zI4KQ2*y4)Huw1yDl5QRMJ-6PmAY~Btx^daWRZ8m8pXwE~p|NS-a5tFMI>Cv~+8Fj& z{u+WkC)WI!+QzZHa{2@j*XnPzn-0^5CnPyL9WpH~6{MNJhkx)8N?{Of$MGAlD zE9Av(2d7V&6dgf0K$zuIjDwC6_G%>Sc4G(AW4?IulZeEo!#yv}U}W z8c#&f({)}G4eer{$_x14oZ>4V1ZwUjAG}VtwSa6oV%$!FP#d-cL1Y%l`$7P2x%2!l zfc^~)Zu6+qMpBG}3dr8te|OOnv63?{pt9S%Ji9ez-mD_nx~cE)F2kokbktWUHsNtI z#yZuR@b0Bs(~@P9A@i`aKAGbnHmF`W(NBPolo>r2Tb!7>vqe!$?fqk>{75@t>*}W( z@m_M6<-WPf-z|#q!J{jyW11Q>V(%U^<2tr?fQDM!v1CEGxLFg#(0QfR*0nsjT@}}E zIP-^^jriAY&4BG6!eNMJcVn0ft&NFGVSLVvgK)tdUKjlj!9vF1IvFo7>;|X|?n@hM zo`p{Oww%h0sZ@2yD-Opy8o|zNU+!&%KHGzJFNjM1l7xo3-NL zU$Zl^t+*pt)Z9V}GO#Tyb6S85&f0YAw%ri!!MP;by&rC$1^J+HT{04ZGU{VQQ!*9}86L^1ds_@pvc?z2x{ z7kdZVM85TZ{9WbAcPOeva)CMUq?WvYX7Gf%lul97FniesL(w-}J4hy!N^x2`EDDu< z77O{HJ#zw|^vF!wWv?gN^yO9YMslIj`1-2+cC{)xmJ>R*Rt)xcc9lQQ=Y}D}GjtRQs`BVg~GRsR)^L4x3(AKiZ z+nYQL@qXi|F{!eswz#Xbq0K&lo!9?Eyw7V@-^00f{7cc)n>vShersjHE(rHJa`Vgg z$qjd1p4&^F6~yV|B2N=gD8iZ@6byp_`m4FDoi0|K)OaVK32voE=gZK74XKOnlL@<) zct}DjnE}2W@lP=ZE3G_>6=@=q7k>1u&({+JB#u?ZkG&GZS?+ogs!o2n$WBibMvCq9 zW!qbehv59dAQ5WEAncajdqq_Yv05k-Hlz5HoB^;dL^6imq{1!4g~n$r2Xci%VNmsN zd2sK>Mv&~w+r4qv7lH|WG2#?zVu!2I^Ju&4^jtsJM^_zSWihauexTdsnf(TJsS$av zqxS_m5AnW5{&@guSMzMAeD|`{_#WI>Ve7r&{E1T}!<%E@0Rp`Wjw;VTnt$yCd#>KB z;1-`_u0AcPDjThN{jN0uml%^1wca7&=~2+3X1qw5BJ-<4qCMqSsEo3qW{J>&CWj0< zV-IA%8WjD0XiZ0X#BmPJ)V^O2HW(RoqZq+)p#T&0>4c-V6F|RQ@bjd_5Wv4^Qrx+b z56f3WkB(>WlcUMzD!7|;@#)sJm5F5Yw|W&tT4-z5L#iKqXHIQy&hB+ESFT)6?(I2} zX^rDoLP5hn>Dp=&Yl?)!%b{;m+v3WNQ=h}i+>lguY-A}T+D#(Y@;Qm7Z&BQpIx6X) zQY-fa_6N)<2SNdn8F4*`;%mBmU|R$cDR2sn^S3K;ez_G ziY$G1pejD+=D2@hUgyU{%FeQQg*|EOn! z0aQlmAaHR!M*};NcnZ0ay(xRO0keAdQ;c0z9d)f=CXrN;L5zYhE*nlpPJ^5wJ3C@7 zn(@AJ0xgdF2eY-y*b(u-U`B=<()!W{B!V)>0bMnE*#b1clt@6-t2kg zk;EvL6mv-%JYVj0(ITrNP?RP|`C=^bS}-Kw`YphWumLDPWx&!ZqDeC!I*Cw%4FtGY zwbrLJ;5b7^3IMh$S`lrMktm=V=>FG#CDTZ>gBYxTl}o5h)=IQ=ggc(lbZ(6T%yN&s%muT5d><X;JUK`Z9&r!ku->RksGrPh&W$osDAu6*z>c zBw!_muFSq`hx@k}|$5un2$0O^UcriQy_9wRL;eiQ(*golvcl6r4 zvv!TEvmHj{j$q8U65qIbGySDx;wGiAA+w~XE*fk-3oVq7S&F8@$!m8~`@K-1S}-L1 z;;=9*Y5l;qbFYE1kI^*!k4C$-vH;^AnW)ZG)u*I3%y%1+2)?Tj)5uCWff-i{S;No3 zrPxj%L7-)=dr3CI;467>*pqxY)5(9pF&qI}&B4uY1M;#NN`okj*36iJ&$hOHAp|1e z6*R9m5IE(t*E(;h>kbvSmPqY4(7LLeK%yt4mbU+*28dK2Zy8+xpL=>AN_5+rOGPQ0 zdR{BUh}+iV@eZm~yEvam(1`=8#jWl5ef7fVhgi$2EjJcih!7LkTh-n{9@sdfMu%5P z9plm{S(ThE^@_fLW#xZ}flQvg(6MF=7tKBG9?_>CAXN#8LaGT9N6?WqLz9&x=&Kg|a3zfqn_o1vc+ z!F*&xJJ-X&h_(u`g&-ptTU9wElC)T-k7EtkGV6~c;+*YQX&~0sHX+>O0aiZZH26bx zY8iBD+!VHmo+!iup!}p(82!h5-BF{nHO2RF0y;d zj5-Q2jHSb9WHo9TC!j@r5oJk2_`?2Gu?QnicM0{2owew(`Fp=Qs_M+e@3dFt9m939 zXND?x#bj z1Np98#1_A{i`{^09{GK;6mkkLK!RH;^If0i&Rm3IC0!6lr#ef{oYE>I5_Pqos~mq3 zaf}KX#4%9)@`f9h39@48h_swvFa=54kX})=PpJ!ZUTn-Aj2)q$jLBDVdb-nZ9z$hm zMd7z&TVZf(nR6DeRIYxb!N){Qd&a@!tCy|FpBSe2m>*R_OmLf?Ql0`7A5S zM^ixJ40GaTvoKm>p>AyNaY3s(;N2&&ewD-EoE*(OE6UkGc)>N&bQbQxO1b7wARZ`#iBFJO|v{EvXJ=2{B>^4r4XMxfi$U%-jddYl)29p@jq6XQio zwMs?PxT#8;wu(ACZLYR5BS<-vOatdN9Q{;KIN$ZYz6DJ*_EQ+${ z^L2!C{hqNAv{9J(R%jE~$FI=q#JXYOrs=N~^cEkj=kcdSlyjLC+a`}3*Wt?4{lL37 zaNrV4^r73unUb<}ZG$t30{cLeH)~BW!+XUiE`Lnp1o)n7EIS*=M5TIzChXb=PWr)Y#L4{=*%r5n$P+JmpcAsWQS z781Pf6AC;G&2a#j$thCq^PvBx#)$q;Q)3nX3pEA{2M>dQ2tY#q=R*4r&Gk=e_MgZ9 z>`a42M*6>;Y5yH_bv38K4S_1tqOZB28RSi#{TOl|7xx{|DZAR0!=6}=E_5ZD~+eMGe3avi&p0U6Bv8UX+vG-^f1wF zW}pgI#n73{fR$ct4FxY(Vl=u-16twd2*FFAXkEkSJr2V$P@tTc1N4amH&_yJE*74O z76z6LzMrj}78!0?)_Px(j`gsEZu{gp-$`J$@<~(Jh5L+ z(+a!MhquzZ&ERv>>~gvg%x597RVK<9Q)6wK4jL^JVWbO(iS=g%>2B5{&xXCdL;S`P_BzxTbwpydh-?Yl| zb7~bn<~>?y%uMwFwJd0JUn~+s*5yiLt`9eLUONA%d%S&U-W?jCl|)$iIYi#EzL{2e zll5!NS$12#wzlgbd)7Be>+sE5AZ>{2a~Aw2+|_3}C@OY^BIXlII0a;8DEy~Oi&vOU z=lgXHj~-i{|U(b7clxjp%xaD!FACK zs2ouyU4Y_Zp?0c9hiy&aO3Wop6$-NA)O()P1M~xMwBvYemFhO9CLLoaWXF9!M-Sh9 zL-qS*&;s%kFi&4HKhPO`hMHL77%!ES7R@T^sB6GjT+Fe^a>#1~1~LY?lG1Y-bHRN8 zu%H?;5g2+hsGwMeev)+Kt|+o9ErptoEwu(MHQ=QAV2D@>(P^YN@z((O^RVHpl94Q^ zwbgjRbW#X+jdTE6MAW(bcldG&g$Ot8Jlx-&t1H|f)@G+Z6}!N%%O>{#SJ-~ID~bI_ z5B@ItL*YG2O;S6>n>@>)!3`(B$up54L5HiGO&Cm37mQxIUu|C=Rq-xzCFKTJ-HelAWdF#b>z-s8$noZlr{+m7TNGsQ9Ak(%0GOz%cBkj!T}u?%>oR{0`A1&n@S@ z#=-BWmhtE2^+CaRF~OAhH-S0hS-CcRP-2WafOn$YoesE8iN_we4F$$b(Or;S6pYEW z7k$-L54qK)(nsLzE6m_5&2Ki?MTc??AVXxwKQWi#<<$9d?!15D#PVyKZ3OPMCsgz3 zlR`&VWZe@MZ52RB*<*FnmoyfY8xcdM(~6@d>oN(Q>ZIBg+}x>SS3=9Roh#PE-;V`% z1!O;gCOckOgNEia>rwpWUYeF5TilVBD3==}C z0s9p%*yTe=2?;{|ry`|Js>HC)`q0zMV9dJ@sHQ){gROs*K(IuqU^}Y{5PVW#cK-v5 zWvNq3kRue$59jrGO+jHX;tXxMF%X0Y>HvOaIH}dNWd+H9XQrg`Gx%O{I1s>EcH;y5 z6qzl=wSAOJm$RwQHeHL#LormojWW#M-B<|~rgd(*>Zdr0bL3T&axdIVl%;vlaVxBu zp0_yHBi{6idjU+6$bQn^8UWPpOsNZOKB8s2bwjreTbx!P*B^c1QhK~pUGmhqLa1}k zhN5D!Wty%3pwtj!Q*L$!pJwyFAUb-D_bH|1@Lh zJKGj<`U^Y@WXxTxJwL*%N%S7QjlJbtIz70P$(ke8w zjriY}BVt$+oE0M_#x4ofs;P@V#u4O9b}FCp(5Jb)(lTjW8S|1)JAO8wJbf`Ks4D^P zS&K{N%EgG9H5~8>&$_T%QXFEL%=6L0phNN$kq?n}F?C|lR*?D++ z_Q2p^%&tm{Dvj;Qs>MFm*Y4cE3?mZG{5Wm*Ye|Qa74rm#ikopVbi%(1>u~g>Gm$CGwd{i=s%ppv)D1ar&{14C% zshzYbIx&AkJ?GzNxl9bxLq+(MkI0J55q%6uErHT7e3kutSepdNw&ONiU)!$oW#1<9!Yy<>7YH1i890o$C29uRX1s zRGR4vHPZ{gCvc^H-T)Z$@0r?macv5j)#2n?TsU6v!Rju%#oA7D#a)}r>uGOgGO6uI zNC-cab7d(sHI*wDlvV`iVmN40FTb8=nur&qanKwd4kvVZBAR^Jueq>VJ>31;;OnBs zu`Pc;5I|dqsEK}lTRC(p#hGBN;<_12Vsf#GaNXwixFl>%MUvgGF0R(sdvD~C=}G(R zmL}0@&5$-S%bT=0ey%1>arw&mo%Hn>ikwO53L{kNXZtl#3du2UW%|ox=^%@Xmw$ZX z9hZT>=`Sre^iiWP-coe6DRE%sZ#~axl)4HSRw|@PY72W0x%aQZY*1LNwcBgYLM+KZ z#BM~?jq97_fi>!~dp|wpG84O;j>p`AH&9cGRuxObtYa&9bN?1BZ8Y|u z(rZ@v!#ZcgoB2kcuNJdaKR(o4a99v7!F1vLRKtq5psvi2X7E}65sT&Dz22EnVptUwcErn>vJlt zn*MD0qBvVp9>~+S+f>ykBZ{$a{#%p&yWHFRXl>rsB#Q$!OZv_a-e{1T=|@|-63-mV zpSb3zDygp1>@GUCHuDX!S87aw0n(jTD2Vg0H55I?bG0-+@d0Bb0-gb!3>>n3E0rmFDwFd@-+FGfOql}!d8ar=8<=l3APha z4H_op59ar2qu$R^!xlZQ9)E-}blIHJd~J835te)P9l|3ULd*@eH48QXq|UrIlW6-; zWOZ$57b9CJVo7cCOS+Wah5PXJkpONZ|b!Tl0$(m zhBI)Sp5_=o@)0AvucT4CN-T)5VNGBN&=Spfj25$z{xRM;pZfY+AHo?L7ttndyYxqWlG6NVs$Tk9vu2{Lddn_Se2mGR=S+wOlpm z%Tre%PWo0q@Up(cCpB;Haty6rdr?X!Fq};ADvpJ`DXYizQ+i%lEj5gC7;C&ePPRE5 zF`cm7qJ(R-%(~J%ta|05o%j--gIUT+`2!qPoLrth-9X*(zvIr_qO&+Bb{ssGl1`nc zCC4^fZ!QZ6=YxCyG1abCt(^UE3I+JKE^4oy}T>HC}Skb+Q#$ zWSZ^J7W)=gy9}KYLamn5_Rj-$n1zHNK|*o{aGo8*fccKj@23>Gfr>`Eqe<$lb;OTS zZ=|m=+9equbt$QIgh^7Ir{=*0ZGP7x=Qj*(WxPVClGMNPv3t5-#Y2BzH92) z5Zh=R9e^Zq3sDL|rV=vKEbO}iQ9GA8sn+cplkfcbKvS|`J@+phj?b8& zt?0Q1Mhq=a2I5!wdVvOgnNq|_ zBN-0KQAj`L_T2n&fk&$y?!lcG*XSHu|DIptbMxK1(6}r;^u}I;gfOVH=@cP>kR-zd zX3$uGKF@qo zQ1U6B0a36E$G3fVZ#rjX=BBqQhJK2=^jq95AC$#{EKGF8b@e87tRiITLz|=HCUjJ8 zXz5wy6KTe>&9!yTc1uu$*q+|H>aB6pfxk95F?*PYE46@S5bz1&Lya5?k0pl+(?|uq z8?D!XjXx%bM`gnqqzmW6_f|r#O+=UVs4DD)=hdp$N`>Iz@!_KhW{k*i6Xmc`&fYb@ zplH=LehC@YYmRyhg!nkF14tvA zZ%q0Weq>ZxP;H+**E*@A8QnLuQsFlK%!pDHtEJTb+DE3g+|kyfP`F>W2#Pjh>^cg! zMD0N1qkQLOVBqizJIshQt+OvGyvs55NQ_M{6e^L+|%9i*MWtOE=oM+>lp#IoA zxyjHDdK-iK*?K1wm6b-Y47M=*%#7A4%`)t*XO&(=aDF%X1?}G3}i@ns5W}cU$j6LSnJ;i z>ado3R2#V`zdKlO**@mAGHRIYWg7Sq)OEc3`wzKD>ctnG?nhBOk!5fpFTO4*J+Qa5 zOVRGP+YxVX)s!1qt5S8Vpsd~_x@xKC1^LKy!R4bQ&1$03QIp7%h=7?hS(MG_R>fE_ zfI|EXu{6!$l#_osTtLwR8)OFmi7!;_!`VA{Tt6(ov}Qwp_*r5traC%?x;&rJnnk_3=) zq%V7C2^)T!er_C7IaENrKwKd+VZV1XE?7MKsUFbkuQ@&S!*-qZGsJH?H?QK3ZQZ2F z*~XDoGVUj}xA<7XyXn>gNBx5oT{SLcTN5Yas$*K9o%fQ&$h4r2xW+EGC;zYa!$KUh zK6`b+z|A&{tNTrt3nSO7f4$EKVz(OJ7}cui)*dy#*R*s_un5>jd?dbSgQF%2%hO5t zyuS0bHGth%@JDUtYtb0RD|oE%l+L@UzDd(-dGoDg($g|_i9yut5)+Vgr8KG z7};MpoEnP>6`7_JPUW6>qaac;IhjDr*FAPi z3(@;_Y7d=M?J@Q9KODD`bS;-5zs5NSE?!2x$fY4?Xa(ZAh71lReL18?Fg>-L0fWcD z8~aMZWZZ1y_)tZH%gq`RqcJ=8*_R|n!pxBe{@cf3hQ4q>iS~&d#2TriY?LooX+w9H zv>FuVEw7BXWWTWb6r?VZ%mw_!M!$CmGyG)(VZN)BIbr4(bRFl4_SDC%o)4goJLBmZ zICld1Gc1leL8cKCl%!g_1f0s^UHBz5qIc#LpqLs!W=#<3)%?bg<&UC)^g+LQQ%9R( ziz_H!FIL<)t#<{uaY_(NuAZi2v~Y5PYFnG*3Ousnompw=JsXgJA&dMx-!TO?8P!&I zPGkP+ou8DH5WVkSnaN`i^VzZ}4~AI4SL|ApvwcJpefN5NC4RT@wl{OieS1+C&6HNq zDvwtkW4YU~d9+CNOIgS4Ow5g{@H(*J*K`#V*c*J>m}FV13OtEHa}}N ziC|iB&R@W@HbCae+|Qe|CVP)1nJsncGGO4z)Irh!!meXk;12gYoA;I!kYSz6s7?I=_`=3}2?TMjG`_;6e90 z?#<3lwW5N+w8=GU#&0Bl4!XgO8zv7-|ait7aih9_^CfgT5vM7VU>MK!+Y`# zm_j6LsL{=28oTo0^de6O=`Ct8Gcu?y-4iAco6XBx`#?9tCN@9p(bo7|xO6fjtN*_Iajeon)^hE7D_+2^b z&lb&16`aO&pEf@z3%pVJeptg7aJ$CO{zR4@X+T*k3K z5yW^;%yVBjPm7=G=*BwXyz?=Ei0heaY(rcHUh>2K0x&#@y_I`A(s&WKl8Lr%f-8iW zuGqULQu0vs>@|*>w6P31$j^!rswzKHE^`oAQ=YeXfV0dFzu?|g@VtF*LX}I#H)ept zk^u+)z!^gk)i;yL)u|Dvn~wPimNntz$O?1O7f(-;Vz+gujTY3M;1WOxS0&@DM`)$s zd0u--i%>ISR;`Tty07+jkg`2qnx%_s_;$~Wn?iqJ z+CsrQkyWX!-XYif+AobG>ylIvtxjVd2^`cLaIQ~!vdScDU(p2g4&-8$>ViJC7jAff zGtP}l;lW8=;yV1Y&tIMK%-I|=Felv1V$dd&=x9bK2k|7=U{O!!;O)?hSDUJq&|wVY+MycvYmjw3c9*KGq=&09LF-Rp(HcL@ z8@%4!NU<(f$Mf;k8U235mcDY%1kb=A2JcK&#@E(74uZb`bk(7y%e|jE8d(z*fu*lW zO){fJ0#&vpfjmWk_u3O;N{l=Y^bO(o%+*NI8BF2F&xwz$%Hyi>pd)x7D%E1cd{%HL z3)zAXpk>Kx{={)6B6W-pkW{&E!B$mpu*OPxV9@4Au1l$>Hvq)F(%xG3bQe?&J~wf< zA`@-h>%4D4%z3?1L|wP@|ufm{)~48{Ki1Pw?7ZQ=`aKl3-* zZYW8jilCCVf->p_sz7@8xaN4w=IYmO#ET7VG1RU%!II%!L4(`7NL_5^R6 zzYVG>lKu;@2#_jEe>=!~AUXSzb<$TE@%k6wUUhV6TSA`tGNKbY$1hxbeH3Vr@MS77 zs+&R(!t=Q$%k1v3Q25@*MMH6oP8S!EiG|3YV_Sf!&_(oC?peH+@NKXP;vX5itr?ft z%t{_zGN>0hURz1KtOq>{GfoIFlJKYvR8;u4Be0ke;JYJC=csDCBaCzqs*rUd00X8( zR+QLJra@9xH#Wq1nWg6uZGAM?&Nrur4Q}W;k39-s4dY<1lJ+CHiwG$KvX!2!#r2FA zb!JNo8(eL9)h#G#qo~VI$btu?maG=ZSx?hJj3*7kJ*GvcA3q*V7ZfG1l?3jaX+gT- z5Wi&WPrz`xdh;|lb>7?Ht0&FUf$~2`0l5J>rSIAItSm-)fT$G_h%7rmz~i_xmZQ z-*DarXH=?V^h^&O`aLS(NbC+1M6oiWr#lwL^G0Y%(-!l4u%&$CCt8(hfz%4Hc`Yxn z-BDbJXiz1u75h+w$YUJa5m$eqI_cx_3V1!%g({q8j2SN5hE~c&=5Zbi_)>*R_CvlD zl~oyP-H~&TI?$%xUw1tyAgrs64zHDNun1i?mre-iW$Anf(4L^bYTH3ks}@*5O>-mO z>+t&%q|kS&Nmt_kA%yhq5Z|h}W7Tfd#QBoI%rv^RgGJSGq-+zk9!ze5thsz=RK4_d zjca=Tr_v0+k-P+|n%NNT(9gILV<(LP94!4*W%#f3MG9yiB8=G3kQ1Q;)6izt31s^1 z?(PhN8m;^g_@2;`eax6n>P~pqJgf^za<7kCZchwj#O(fQ9$kL{5k0|{KZ|q^Io%cD zv-VD#;E!}43kd18uUVI75|cGGC1ctzi@BV_1tx0jyMEzP5@}PT@~LWDAX!DXDqy19 zF@8eaKL;H4YUVA2PajK3L(gDbci=ur{P-HYHomjW#F ztKTLEt+G)w$@99rFLw*FM|WBl>mG!NbfwNEXbYg<^|IxHC9VOo3Q^{~p4=g^y2 z8Qt;J^S$=rR}nr7OY-#=(taNuiWD_n5nrva40$Up>d}2nQWn|ecDr-%f6A>{6Zk}r zf!jFJDEE82-r_2ejG#R`D|_7W^VMyCWp>c0C`23w*m4;3-KvZxNN)C zJfV!e1pzmr!7q=#&iM(HyymaZGYw{VyA_^X+gw=^yK`3i{^Mu%bZ11F|C2y=5C`b< zi%P8Xh%(eEjVHH9I9ztCo6{CUzUe)td4sN@*@&%D8vT5c|^x|ru0)N1a~a_ z&Os@=7nUU^(7l(5LVKX6xj0je2C6j!tD} zf;kM|sR$8;e)$qE_(fat7)8Z^V2f{+R6tzeiecPV>GaDND)^G;3#i|TwJjxhJ&VXx z;cWq!BNQV!lz-R$H8pkU{|`4c%l|ig7?j)ke*cplreFV*|(yx;Ptp>H!Tm-s%0 zdlColTSZC8x|O-zpXXnS)XrXZ`5>Gr?MY2!Y0>pWLr?Y{>TxZ+nzfJ4qiZ{}c0VCf zxmTi6bQsy&Z&XeI_&o>g@s0e8LOFM11gKJ)=Mjt~$Op}3akOOFzgy3nFS0$8qe5NB z4_8H_r5X+I$Es%t>Es%M5iVda5D&9PDVHK5Wr1R0swgukjwl1LL~|KSaPU&dLbUb< zDyZ-3N6eAsu#>{^p)A-(jnr^z1IRX=^q45S(LT9<0a(BRmz__tZ@}VMsjObZGI46UYWHRbqUDBjE62UC zKa?hVV#&Kbv8%3MKqYmr74MnjBMX}#tAYW%u~cvAf(Ani>#bUL?$B01fEZLTGWhtB zexqjO!s}erv1*+noZUZ$dWz@QTE*%|_P*$QLhPU+cbiQ&7)KtaJq~tDBQsB-$Y7H* zqy1RbNP5fvfMLCdc2i-lc3Y;HffU=Gymo8)%B-gJe9lf8uVn;rs!pU~Uq{rY`qqm* z<)w{W9JiD5Vb8KwEnOdKdhU#>rUZ+~>txR@k7^j|HV=6>qtk^>7f&2`xpNE{c53fD zoJmbW+8i#>5yhr7pa7n6RDZEl!0*+q7>i3tntxpLt2ynXvK z75pvP9`TDG_x`hU#8JQVQyAuEzeJ3|Plm!by*nJGpbGJYpML=jgY=AepvJXu?F9)A6&$)HFjK`9?KoUJN~xe&E2RR+!?EDRLjaa2VP?$4M@TN+KF z>W`&DAY#FFnWsiD0{l3SFwh98lEkJ$Jq|UUhljyqUD5S~+1szGz>_hlMgKJXI{D3{ z&38p4m&Mk|vApLJ?kfT8UjQMCP7Uam>_$+BJ7x4*Vk}UVHqcuD;+fNv)mi|dfm!nU z*-t_DtMO44UlJ@Sx8W~NkX6bb(tIvrZW(qMv`33bes8;*JVIQYw{$X;wC9yD$M-t5 zv8E$};8X5>c5kDq0~g8uLv4bHJ@OJ2KCe4kdT+ioFxWM(f;6EfT;299dGOIO0JtG2 zC;l5!Q1t1)n6TxR6rt0qoIOyQ!J41pwAWkP+$Me`2e+bwVPJ(@^8VVrN?Z3$^~ftV zt7X<-K&*HpWxCq_3se%cGj&o|D-Xi>iru&8256h{&uimhx;-C_Oy(-)EOqr%E=sDi z_1EOSqq^|QR3MnaaD126I-OB(EuOMPnJA18>fq3 zf3jZYZBE(QsiN$R1nHKHRyvv+=t`J+C{oc)mGCyS-B)AvMa?|1l$SFdR*$?c0UPYsIzB@4d1 zeT757V%GmamXCQ_8+rOohd^nk>m=cHapK9tj*=1gu6Fkfw}Xjxnz;zLX;^Bi4|}+- zI>@e?U#sXT;j3`M-x9m_UEnVO|95s6Lv$yy-OvywrthxeX6jTEYvk9c=JZ_nU4lbH zU3%dzGTAgm#*u4}ryQ&V!j3;|Z4K1%dXsmHx)S{&w-Vx3X!ZipK-`#`PilbwLEBly z#1$^=8rMQ`8??Cl;1t*5?oixyaMu!{mX4KJ%=n#??iB;UVfwlkbnQ(8dsR1%f7Tb%Tn0-4l)=a>iq1xHybqF3N9A7t0h}kH5&IZvV z0kt{k)*D4e-jukzQv^8NEyYR5txcGzu4}7E_N{mkg4$_*Qu9iCd-z}(3UbJUos0FX z|3ZO;Yk?XHS~MBa-G>^Jxj0j7M$6qYIkE@dpSfo}Y90F(lDHqLT?v&`R(;^JtGe7W zh);NV*<+pjGD!URJ-0#g(<;OcZdL6NlbErGza>sPt|=6GZ&9C+_r;xV)!HSujTG|1 z?C^VyavX>`E@?9X@e{qnumvBKi&`RzUL-V&j#ni}t&3Tj4tLF(ls72D-c6&Begs80?6 zx2GLTQF8#VW$cT>8S}Hmb=`gI4fRtjV7)Rl!>{|=m%ZXvlc7l+gf%~eEnA)7tA9?F zFLeTnzN{SpCi-O805E=tSVd~6O-!kl2Bp1f1(PJj8>wnQFKidDGY}?KHPanB9=I(- zhXl|ftuM6SA^Yjq51G%H9sP+!<8(XW5RaIIl{iI~e%M3on1c$MIDd1{Wx}}%)-hlVpQhe)sQX>M zw=y*6pr?o$_nJ%j-iOb{;&qj35DhVic`ih{ADLhUR#GDySmvN6WT~OZ&!KwYqE~w6 z9u?=vaSdE}B;ZUR0Ay*?x!X~l{C3Krh!fY1d##Wt& zgAEoahKW3t1AweOBtcNNW1^b*FddzFS|Aa*IRnFDXb(}r%V3_dHzL+|Xx*`Y{xA$1 zifZzXm94w2Xu;*Bl3D9HSq;vgJFHYSiv+yTXJ_wD9qU(u13CPTPOhf8eyTRmE1m}n z(qV%^_SJ!h71Tg4Y4H@pw2grM`Zj_-K<8{Kss1RXilMnP3lFv3h8~f#i+$_Osl^Fb zQFD#u_xiC_H0hCfU)I-%oVW2|*`ky_1Fwb0@Tjji9mFXu1sjx(4>>UPHjIAp%i%j? znb`gKs%bjZL4cCMO~OU#Kjlsk4!bV*Wf4I#L(JivAuh>$SMyxAUF^|FEua1!Po}sq z=*g$r_IiqCm1WGua^XAL#@%An=n?v4#D^5rhz6P3=A2cYzfh-erxwW(0|&ykxlH;G z={cMStx`+dD1=6f!gxkLclyV^J@@owO9r)&NkSpK&jrru#BFLu2Y@OvAZ<Pt{=aJ_VR&&kJ)3z>wF#SXq7ihq-%Hs_1Oc1xvnmUmWwYOquH zVxymxb4Qx6_@I`ds-|6L8hlq-DFRSa)oV@^6~T}Q!Vuxbv6r=$LJ1{8Ax}bKQ$~aA zlM&rSN{7%OOby~Ec(D&lRXEKaA{;+Aei#@meGrmgmDh|8Q{s{1xQ-~-{^#=gGjnoK zs_|GGCGP30ld!aAjn^iKJBOP6PQ&~$gK7jY_I=Svn`En?gor( z;D+Z>@?cB#t_hFsP9ewWR)PYfY!ws_8vCQ-=~VE)fri%w7%S4&i;vK{wUm^Uly8XF z4=KU;!qmmZI|{<$IHnyGAy(cl!_^$C{i|8jN6npIHMOL^y^7b4Q}FFEom?QT~?LzC}(fPfGXZh2OhD0c7uhN&y?bZic4G0K;sh!hYuJi5L7;fL5S z-Nl*(o6amh(X}Wi)J{@CtQA$qVwJ%JVY%K#oSrAQW(Wkv<&a`+wI7qR^t^-$e7jh< zB2F=zC+Op`-EjPm0IZev>LNH~0oU*R`1wgwQSAs}X64F2mU7 z_a*+`S$0|J%yJUO7AT86tv@Q2Um(&IN{k6>R(GT&!iFPKY`|V}4%IKUpv(vlH6)VP z!I(#c!njReRuen|rzK}C2|_l7z^5$TAgJY^#lc-C;Y|tdFh{TOA`U(}?F|M{Jhr&jA>n(cRv9zzoc-6M zPea+WA8-#_z|}pYGp-XAo(2V}Wt0oR_>Jr`JB1fgz0Hi1q30j2v~Z)+!j`IQhpt41 zb~}X+aZIRj526OE4)0gGfYs`%?~6u*IL4<|#QJAMqX_|K(C3w9!{eDFz$MMn&!VYg zd&IgP4+bQpvTDp-A6w&Fhu!Co(}k7r+IOft0c@C$4JVEMXDc6^&P;i^JAJhn@2Gi{ zz<|=h71xS+$wwm{XHpV8* z-b#p1#~zlG!yxWGY{uAF#l~~{Rrv?_p{?zj?(1Wx#$P`UGJRYuJ~H?gCG5t|7C%d+ z%~Up86EiPoMYT_(In?{uc;Y!!4{t&B;wGn=YpBRxW3h3472#p@Y2g@M{O*eA2*|UP z>%)?WHO;zkUyw@p_^H%Fry5oltc1Gj93CtT$JKh?xgX?wOgr2dL7TMoYuF`neLg=) z?xi7udz#tJ`KO6(b4pzkVoHks_SBPoPy+)RTZ`%{!d4(pD-?W09+b%_3DKpUFZNf1l+{P`z0{C)Gb?KX%I`rTaXe&0hf&`T6n}zW zv_PYY=06rCRvx(?sX^5hIy~-MnT-g-cwyOEJD|mJXA06FulBC2bn_-Xy`K$2pUDPe zc}tEGy`KY2k41&bVR66|#Ry#(2~~D^A3lj-qD^41eWTC}w4aBAtl+Z@-P})Vn>KvA9!n0bR%`srj(+EYS-#l$lvO;o3e{u z7_zS}x)g=+Li~htS&)wh2nstjq@}}-qd%*T`KQQ>u zo5NAuv$q*e3b&MG3HH+F>+q`&bq%wyQsiLg_nS$Mo3?`lu3oa6T?D%RGnt7qBIA^ZS6m8TV7pwi6Ik*r!I2q_Y4+FgqT7j?+T{bkIktIXZO0J z=V1$ckc2w-P|6QaKrMd)zXwo+$BN3U({Zd;dT3a%yelf|eKhLiI3iA%p!A$3=S;)j zLP}Ka%B!m#Qt${&ZZQpXS~}@ExV~mKZ#GDo*Wm4^&Zl*^Sq~*u1Umi3XM@eZ@({RH zGm3-R0kF&z@czsr>ducSe!If=rSGrk-s%3x!%$w5sfDv?pcwFHHIX%kKf_&UgeO>@ zYPp36;dh)o6X{{35z9#y;$a(XMB5l4RF-jh8wq4#tce00xNa~9LFx@I zz5$9WJ0J`*io!f19RgGAA+W&um@B|$xnuGQ;pb%WUnp7A-e>5i%5C)Hr*XT6G0+%k zpNyR|2<;cy0mlljY0qy+rFgQIy12p>C>E>XiaC3!QJmAU|H3MR7G1h*K}d^nts9v- zNlyB{&=!gZ5Vh-kx9-*+E1sd!HZDqedFi%wpe@oXWO^W-aXCD}cGPs`0xmamO0+Gk zJssy@-veq#84_J)4WRvn8l5XgzlyBTk&1(@?ry)=R$sr*PZLd>C!nU?{AS(#629@) zLd{Y>?Ry2W@<$FHS9$qeoZk@NwAxfE-8n9KDEJvS7JCd^c4$pG9OQBLcWK3FqbBes zyr}FlUFl}J*@brJzxgxP#x>LA0LM{@#luGOOYa+ArfIB9(rY$N!96ia9)F<}fzq5r zPcdOF6ei=5`iTx2Z(n}h7o&ty(Eo+v=-vJ>N0Mj#zCr={Ff+r!^c0IIEZE`|6B9~A zmR_?C-Q zIA#mrtDH(_QbSFV6m&E(Z%gSy_i<6VV!`Qhl_n{hu`Uq4S)Jpe=yH~I{YBE<>h8-$?Ju|ddtXudtQ5PTEAx6 z-FvTQ=07mHGOGB-gpR7MtJK_L&pke9TUJ1PhkxaKB%?p$%u-*t3^~QtyEPdK2?ldo zHqUOtPalnIZD*5mSzoUWMCx#C^i=r|);q$Uc610I9Ku5{vw5AOl_QRh=O6P0j#5^T60U~Vm4oPdK+nNYInomU#5z8Of?U zDKhh~U7x~Cfuv$tSNUelp9TP{Z_Xh$q@Wze)x#XKwsqelM>vXR#|cFAvNml+4EQ` ziz{gj#aZGN!Dq|lXEjNj0GTY9XDZ6JP|ZQ@`tRDzHaYts2}iqxGq|rJls}(qzGk*^ zru(aG`!{H7+y{449H8r}W9x9eFm5DE6%TIQvZ!RX7VTZd;k*iC~3`HVlbX)#SK%hRKVZ+E$^+ramuOYW^teQTVH-_1{l@IBRRn$q6t)aQ7sm?j^1(f+YHGZfH7o+ zdq_sXb&Oo+b>01Xitw?n#0uGs2Jf;uxF68cU5@NmMm2@PhVfSExtSU>ii6xL;yYmm zR_ois3z3MTnRObO7}cvNPz_Py4NoDdd2)0~H)uxn3dr@zIHr+@ay~8!`k6-{*VI`F z59J|&EH4pg%%dr%&7gpTURfTF5OW+~^@830f|}$5dq&nNyi!_Lh5B%_JLylW5)M^G`JkI5Km zc?+~6Ge$l7bHAlH-H6}9`Swj(k>=IEbTe~y4nC5QIzjl(p}WvhsVHfqLZ#@>HgU1r zOht|?>A?x#FeI8zmDoK|b|apbYQPJeWQmNNm!Mz|1vi{chk8@~j>q^Smx<9ace)#F zN{_3GhPiKN-@MUW&JfB1#{<`*8YeN5mgYgBh@JC2ad|WjY4xK||~>a9yih z&u3>~Odge$$!zktkID`*b^Q9<^~>gQU+A|acTM~B$l47w+?<<6ZR3mkTbs8QkPxnj zrP&%icW6sld`epR-g?!Dm zEp~?m;HzcVaHB=EtL`j~nk;$R6!4o$T`MD%36_dKjm@I3zf)x5&ng5 zRj$g_-68qM&tWz7_dG3=XgilR9ak1F?y`SQHDt)q6s;)_{f!P9ik*BSvLoGQ&^A?o zC%r4@fNYNl_+Zs9)9?~?EOx>9;TImzk-S+fN(DUY^FK<;RI)Hbky66<8O6Sf;Z>vD zhy+W7GIpQ^!($;N$ze2GX>6-PXjJt|Tx3zk4&0+Wl6eDq++|o8h(6NeMKJj#s$_mI zENv6!chp}f%|md#j|a8Cqsn4iOrVW#&3ct12Mg;d^j2-Lq+Y7~F?&%Sl--9$#~SHP zfd1wffF*#ET5{Ntwq-jujtn(X?GnF|%461vBlw%L8vpRY0)~_EAM)2Mf7HVU!AW}e z-_`oRIY7Qw?v>XMI(F6_yKuacJk^9|mFACBL(?WQ_8bIVcxnWW=3Oz8kz)J%A|}3{ zt{bH}L{L>}YNUG^zQtx_x9N{v3le?ih~HiX+iXaSEv%sS^tm#i`Gb@cC+jbriJ=ki zjCf6FIZs)#h@FScl;ztCv$JE!+_`7b%FHtYl=qsq4d7xr^;hlK`h71PO8T`J;WJK9 zy=%Tfn0?}PHpw2j>nqTeZ{jbLMCm*My?ZjD6uXDzDbuKN0EYhI{v9oRHo6Iu8Fqj$ zqbBK|=S|@A0n|@H^Gu!Xm;>>t>I3Cdz>Sc(`E5a4wq6H4x_2o)MjllW~HBZY96eq zQ$B}0EI+wscuTpQH%FnHhhI~lLygzS8VIT_T!~h1CfJm$-tqE})C}tXq{}ccn6UAaQ(uMZchjuS@#J(DO z2-Kn;lokV8S}padmTMaflt?fYN-P7(3%MM=qq)bsHn-jp zuTBYMvOT|^jEx$^P**KDaK+S4`fB-XffNfT7>)qg1kv6geahZ3{+~zt`D<7x^&0Jw zUx2c9>SZHUf|`3cLLZ5NpZcU8#98a}?L@6d5$sm? zalmznRd(YcsEt^PoEO&Ad~4yk)@HeHjKTO>r8TgRi!F*I`m2cDK7_0Kq0&e^*!W+r z>cVuH(E3euP-*k1?c#uCi;0#cp*aD%k><2J_Hz@2ssfXA)Tj;?<+FY(&h-*=vX%0S zBq(R3h_#BwmblsGK;$|{GzLZOymxr4i&adXNjLq6sftS;cyZU+=JqmxwKnw&WbM;m z2S0UR4&59SDr8wS$Fh=m7(IH<2+nwO@d^(_)7Jkek`D#`iu6z~O&o`Q69V?{tIrggL>a)Swo<)_rY;ck3(+k1LebVN zV^j{Sg(=5^S#9m8tTj*vvVeb1)RZfPAZy_s%ZSuOlPoxV!QT7X$=ZtucwFLycH#%|;#rzl=k9N;*#aZbL!j~)cHLC6fUZ{h`#p%c{ zImH5&vCYUZo-l71sU(B@Wv=aGUvPm)E`ieO>X`58M7uAt@($XAot!9Smme;8T~0gi zl^j$mA)v#=XtqSJIiDW2L2o-V$ReA`phc*{D>wGK{0EP}Nv!p&j-5EZLr^n3AQ1ny zN0u2(M*rffzsaG!;p^3-K1>z1H_OlT%#JeoM$66oj=N5vl?i?WI9J%81_Q+Kt)QU!YIl0b+ zD@E&8YPUE99&dN}xZPSuetBBrzUn>AqOYe%ksw2AO8Y2>>M(HcXuAQwlPZu|DyM+vY8yI?Bw?0>7Sz#*g6Iur?SnGr6&UsoC#`vNH=$Ktp#istzW?^n+TFh z4k{H^sSzeDbs3E8M9G@lr{hpy*}4)OwX=|MjVv|&X6XpzXc_Uf)Z)$?%V5rf+NCZT z*`)k0TU9{y|Cz1&=iY)Sw*H4>3kD*?LV$vUxVQfM`oH2W$ov0bt6=|;Rj?4Y3L5I4 zqYDo6pLT<)me~ZgSaG9$Iy4LgET$+HJDf_83AXd6e|3%fsvI|8aUk-o0@ME|Ty-yOv$3V3 z2eL;w3WCa5$+uEOQx0z~@1sCdv{eb-Sejd|LK-m3=PsW{(#6ie2x>J0hkih=MiSXr z>I>jcWDW}ev@@F zprXbo<1<`J|yE}A*7bqUukQZ>dw+tyz&6X;x|9PLcI6I{sB~hX zrw0L1HtTK+;9lAJo0Kf10%^HK{RC zhzu!IL4=DE35G2n7AY9lL=4fXiX4*DSq=`uPzk*HMt!as_(^%&eq|e4(xL4TqS#^& z!G6~3)>vU&0bP~G=Fnu?u=-TbtiZfKGgPQI%yf+kn3TsPJ+pt7Z{1rED$SVm_5j+8 z|DlBfks<9VaZXH*pH=|40%NM!_KLrZ)Ny2o0tvRrIA>QW5y9tDod#h1*pT%<=Al^Mc|Jco`yPl zx=+(*(mk0?T&M_VoAP>rEl@1~VX0CwwAU1>er>UC2r|p)4ah1tyvR-{G3WeDc}&qr z7g(^XHe2Z}AzfGUDA8APGDv(xFjDeU@*;=$wT#f`;5%27+`y8ge(5TzV4QHduLi28 zNB;y{DqS!n%^T)SaIgpX1oVbwXIqse_cW8DB4aulGV;1Bi2&+grKLHv2YjH+c*}}4 zaG2CtI=6Kxl5IP@%jG>BM5c8V3ruK0;`6Vn|L6r#Y&}&K@@JrYxlCG^Us{~4T`r}e z*P%41p&ciA;Eig&;P>d>>H?>7eJWi{8-vhP_G$7^fU8T>H9SfEUp9ARRPj}I^YX76 z-ccz{JN=kC#ZS_6g!d?ezN+u%*bvW_OH}feI-M~#VK6#!)wVKwCndZx z(#V*|h8hMpI|{)tJ9Bvux)e7zb$LHj-}f>YnleN<6Xvod^2tg_2#ikTsv3SCCJf~? zBw;x?AGU+5NF?t=eg~kM8oHJZ=iK%h6NC(;c zBdJCo``y~5m30#2)n=_pAg-Hi{DRt3k`U}kJhY$ z--qa@8fxTRP)TfAHhc_M)fSQLK|=D(r!whu>6axHe&Llfzy659Hv=TOvz$RKEZu=R z*ky#N11Fsf6Nk1^$ID8e24haIG+`4#)CL0KsqHg@aye59?xxaQjb9E{MxM{`(QcZu zR}&PE3@sT>A$Z&}fIU;sXjG$iD`|{z@9|`s5nLJqu0~^q$hLCOng)m+i`o(tR|SK| zj>?Lv7ASJk@9H9kJ-DBo;aqqcGyY@SI(dEDWScUQp-7IkvwPS+=X%d1G#)Fo=r06p zx(+KHJ5FV1C^y7cGf|iP{>c&HsViJ&1*;;sl4FK;Wa_?c|z(Ym2R zx1DP?waf5$Hr9gRv&Ryon`yRir+%ktjJ$Y)=vA`=sJ7%iYgNBu2&b@g!i%xO*Z%~) zy6E7c1JqBfoy76!Ny`AY;0A7fl06`p_PU3P%BA8B_%NVgdP%M_Dj$O$W}pO2-Wb*+ag$0 zM9JIGwz3)^i9BZU6I>==|15{4r;e+ZC*tdkIIgNxJ-RahRWiq2OslKj`WWxswiBe0 z@##O3t&iard|_MiprFbreU?J$#4;tb#0#kok+ZCMC-{~FujFm{hA-1*+GF#sbySc( zM(c-xJ!XzL4HC@~$>dBDaonWY90?8xN9A8s?W$z_Y1o9T)=kEO1XikRRlOW+r5ue?3&6o-wVj6@1SI@)0jHs{oHTI6KesE` z$PDvUgpU*;VZw{7Y-4HDEtyilq%Gy8p|Eimv*F~a5h;hjkO!alH-wgf3*o7YInI-e z85XW0&#{8Gbph4zVu+wtk!rSoT$Mx~@XU2)!!wt%&Z;-p(wng?dd7X`R*_g-R}|D9 z|EPF)t@Y!}LpEG9r*QY-BNe}CPoL4ndtFERInzLP(qAZYu(a^=k2XY|Lzu-!eLlR6 zHMH8w*j%Bayvcy=xSyMgrXHkeb)9dsB;SsQzG~o(NMp&;ysbJS?MqId1oQ9~Q&;lxSO}XeY5zbeg9xmy zXG;1C*}{3icNXOGP#liWi^2#-5KHEefux_Iw%*sV~9d4sR4)=6ter( zH|%n!jp7H_na99o?`UVj`E+$N$k*)lF|~DEQib7nuyWfvPKbPUq+aCGJ&wXcPIOW$ z4g=27cldrqXIXcYq-f-6dip9cbWIduT!=plu1*aGgFKo#IPw$`PL#fwfB|xRht&@e znxMlI$T#+4nqn`L4psS73^ozvo@1nu#^ zv*fNS9vypr%I=36S=6_zxz*uakKFc)R+N2z)CS)st>B=c_iv}H{9SL0i_ zcN9hEh&xXqOdVcR>xQ4DL9_0CImapU4>&RP2?~b8KU5yJ+*(?4oeZrF{!}RSE$*dG zI2+PnrklBB#U!3BT<1n^L_|mzQ#xGOaq~LV>yI){e}#`Oj9=NFi5oQ;LP(HlfwMQi z;!DQ1oGdDBrHOZ7^7L41v*uXkpur$Iqa+2y>Th_)ng*Y9Zq&U5Fua@^UrIgC^2%QR zQ9>@UyOR^K-I1EHvi)v-N!+=VHfci=S2TMP-|H$LSMQ@>C@-5iAaR3a-m)+PYxps3 zbbQrNXPJ-GXJNNuO;1n6;5GS+wd*0G?Npy5NxOi%^QWJSCTnetw5$b!t<{F18R4UG z{PBxN+-~zdIz>g1Ks~ixWhndW_D1=@O#oec0sFOkcTqNn)ZP>SuH@hq&ar`BG=HpdB}5|C!6cyyf=$p9U{uMF(VP$RVURYzJG4M({r}&n|@> z0${+#hbE2W5unf{)v3Vb0o-Jb3pxUACfZvUSsOJZ8eTY^D-ucYs8adeZvA z(`$`sv1YGioX)r~tizA@u`RBavW4oIC1F?x;7ti3pgKGaN;0+MWXQ^XW{ZDy+>B{w z@&O~{Ii&ppPgMW_1_1e(%=6DOzrPFhCG#oG&8B2X^Y^f3kipq4ecDXSrqrQW9t)3h z?O50@-*-IPUD*?;2$6mKLndIBO_qJaO2#ZH zkQs1=$5(Z~1er-1L1*iTN5|UC2~F3{>o+`Oe@rh7r@8HMVVO=w3CJ{BNc$je!PalqI6 ziJg|Roc)r)K;=YB4G`6)TJ6BG$=!@PL);Ts5>p2MtfFu6m@FmcRO zfb549EeF->F*rW>!5T5^(&015HdBQW@K)j-*mvDm)lF#%94aL zlvz@U`z`_xB#be3?F7h?pSM2CFZz#{Di_PMEqlvH?tX%_$Wn2hBP=F*Vzb9GsPWkU zc(4wYyLSNQd*x65^`a$e<_lDTZH>v9czYI32|W@V`;lszm*pI_X`wbMxD^~uf@WmM z3166%sAeFWN;5MShLr@0_Ps>j2Z)n_5ds*>LPqS+zR7~62*Vy%jngzv%LEf zWdO9*eOj!)$GglAY~LVIxY&YpiGTGvsLzS7V%HotwdFq^$UHQghC9ERFV-Iod+Y`-M{QZN4l%FhkD8kZhoQ~&9~Ku|wSs1>@hCTTCO6(v z92bS;I4$If>t2)D#EkaWXG~?#1usNDy;V6hT&ES5I~Ft9#mB+n@ggFk&$++XbX^Oc zy+%ht9{j3~A0g!MoYNpi2q$Gn(O6M^o52?b0B}1YBNe+>Y^7S5w~*F=^CtD1VI1V| z^QbFeoBA5UND(#b1r!1AFIDFsNo}>6sNzR|Decw!XOEYjTd$p4bu)XoP2*#7|Ao?- zhE&7!9d)NtpJjY00LFaMvG1Ie+MU3kJ}0l)PLyXg!2kZ(VY_>{9_>F~v<_a5o~9zo zCZ4%+iH?n9tMCtkD{P*2`aSUM1HIPH(8wSs@+AREn(?a5DW+IQl1REddsK$chZL0T z#x3#Wwh{Qw3~6M2Wv<(&b9C9*X)O(m2Z(pdzTxQgvK95rH*U2}na`H9+TgzKu9Gnx zCpIk1AKP_{Z@fEG#rS4#vdGkA}>Nc2^un!F3_q1EhvtBk8QbT;l7mC%x<4weFS<#rfYk zdfWahiK(BWid=m~GF=g#lno>`s4>GWIxeY3l%)+@J)scfzL@u{AwOe+k}==A4( z#e+66|6sJZ`|%#v!qR~hT{ULwX}MLh`;V?fWIMvfDtlbggWrK@ETsBNn*dOxtn$MN z4b%1_5yTnqWK4!@0zYp6WfeXK(^)3ce*jVUVlc!ae6p5D3O_~I*436kNrDcJJcHaK z%7`H2;%#j@gxzT@jd_?nG>J#TW+XwHxJ%WHPIV(?9zz5AS852M(e|;~kbt#>c%3?N zl+nyo7KW}{%#$o8rM|h-0}cbvC|Pq!^ounk@w$$S9%p%Ru*Lb&6PE&ZghNY8EF<2j zC|qrFLwj$d9#zJ9;{LhLy6r9XStT zz^KLV-`UvW6t+g#&DQQSra?V#0?a|;nLzEL5bk2yO!^vdr%GI>n{;$@$HsZ@FzG=faINZ zBK*nhq#c(<-~A>(NF3R;d4yxsEj@MVvBes=QygJ2@yU0grfmC8V5dOh5{at%2uBmE zX6yHoaDERv8C?~YxPHmdHg>^-uKv3m7XLV9lJIkVcRN!`Tt$Ywq$GloQ2V3;Aa09w zCq|L=*RdA~wT;TVl1DC#N!*e{rM63lIn~$Vu&G@S*sv~ESN86D>(+!Bj-Jx-a{@x3 zku1TdAr6A3_cO<%vtXk%CbUL9pN+pzF-frj3F8WCbXcipcx}`hA^zCy zH<@-KJp^SHpLF?tLB^t<=Sb^iPVi3lNisdxDx(;O`|fAk7jG&%JA{43@e0PZ zHZOo*Ctd4sP4Dtn_HcxKs_U*X^Q4b`&&p=%XSwT$LbZa1y4gatLmoZZw*;q}va>$Wz!3s!GU$3|0a~2PlC751LJrK3`CrVeeeS zu4%eLItcsJkd*+LFPg@?*1kz51{lnlS~og@!gN|8sz$24^zSP8y#xJ9@Qi-paYJ*BH)ptQ{|P0&n0O5nGN zV1wWTD`qa-d-qn(hClVbHg;No#*ceWXq%<|$&BHd_@X|5nr`HaTUUO;u9r3mjn4Ni!g_LK?t>7-`Pya`>; zSKh6-F8LM9IXSVDt@!jR;|nZplB3`bZ5C%4Q+*x^?uf|ooRIGN;gZ#DmvQvppQ2L% zEQN$H$DB=FSzk(N6Hd+P^%pJtW2eHdg^yLhcb%4tV>>JDy;fRBV=Kr&_KUt`T2UPh zd2&}OSNpyyeK2?HO`zcDzBy~Eg7R&Jl>g-_R!G92RD~jP*U8$r4W}#9Mv1d9S;4ar z;7X8uKT<)uAV8D-HnA-J&&RI@f+~u}*%uXIGdBb|6wwj7hiag<^_8_4szdu#MRS61 zM%cPO$K&JmR`1tr{)bD+&-u6f;idleelq#DsZSSejORIn`%8q6b%)=MI*Jn)&8M$? znA(IK)l)F`_;?{HJb0F4uEfF1zfkDa&(_?3)FndOL$Uq0d#6nwjcx0O=nLI>>xYK~ zj2+(@J{7Qy+jX_-P0ZlamTYt5+^X!oIb2zR#&FbVsJ>oEfET(<{Cb+zSq{b2VU6VaTxJkYBCq4>p274e_l$ z42Re`XccI!45q41s~7wkIg@P>|)0< zvZG2w`wa*@_FGYI+rsu8@9tTL@a|U&kHlJ-C$IPH=*83E8AhrcRfqeVJFBCWC+J^g zyz7=UUFkd$Bh1Dvh2`qa63){37|{%3XKySWmmJ$|RKsLeUtFbNL4%d$3;B~&CMU*$ zqHCS&&@P@OXuqliFFI7c)%Tm)^NGeJpHV3$O69yn#*)Jv-v6uMH0jhq{P;*Jz3Fs-Md(>BwIQwYQO zt$AD}E3Pyb8V=h<_!nxG z8=o>{M*ygh-rs2>c)RVK3bS|JZo)_Nra(!|kR7wN&XON7a3xOq;LQ8UFK0j~5+@F6 z0O}jlOt2<|REDcYW*EQ^o+pVYIZQk(T*a!SZeH+>i5%$94_Oa71XPm_;XlggyIB zN0(cM^9c1ReOX}}*K*egtuNQOw@+2h)hvvi7y%RVo8#`>){-WkJr};rJ&eUoV{N_wd{RXb=>?TFwfWBbsl%IU(EShY2YeCviLcU+5ZKLB$52Pe*HoN0sR zx!P>-oOm%r5yxuEGFaOT_2AWSAWC6t(;j|O@Sh=;L<0@lQNfw}_>)V6SFfd$B_Whr z!GgT_`nK(vP5a9Y4gL;@ZK8jbxbC@D0Dg98w>9H3xJDD$Kcl;mT2$7Uh&uPkcK0W` z`#Qy>1V3Caxu5LY?WNHbvVre04>cqK{9 zcgR%i*sx!+v6(V+m!h80KS`}0OVuH7^y3X{+N33=rtrrs2LE>Di`WsQ9dKv9(}BX0 zntGa#N#E$>A5>PBZ8Zb8a!@0N-#BymE%aA>UV&j`nQXH`0jaHt7;atok)IVovGE7e z`(qR&xT2^IP0ad`IQTY{1k4AB_^GqNxUO22Jd}wM9%Tu-TD=-xp`(dmiNeGtPcxxh zUTQM|Y8S4vr0xM_fq6q}4J#(uHriCbzemMKJUJ<~LYGw4uuO;M5%q+JL8&X@I z(cGQ;m@tV-iyO*syTnv$n2m%L=8ygQ#nP71UWnR4UP{Xn@x_BUHTN;r@*X6djbe)ogH^iSJ#_Elf`gk2_MRcmn?8}_5wO=la) zXP0yO9(4VdyWFM7r9IYL8i|ST-_WRg^m(fwO@VZW|A-~~T1Yfdc=NY<7xURg7oKnl zrJb(=ue%M8P?XhLvIjRA%NNTf2K(sZ{r(fTM@yPotj$&XDD#-1@M-K8UkA$gA;Z0Y z%u@COs8n8KV2pwMMdjfG9D6JtdD93fS{Bf`^@o0YvZK_v`#50|;F{1!UI|08g;cuR zWPQkh?=01PVWmYx7&(;1wAVToon(Kr#B%YLPJ!ca)rf8@L^o&f16Q*0Fq080my)f# z;VSS75z(Nv`*LP$;JAFUymiT>7r5!X?!Y+dQd3beVFi%KzJT^xbF|E2Z(TTkj0ETM zPcsr5F0%Ko+!Fh?81&RI2cZ0RGJmI>d}-b&@iIWr!CT-9AuA9{VI-`|WU#H8#6EeV zquZNe*nZ9B*q^nGzd&s==M+D9FuQjp`Nc&0SxupG>_KcJ;>2;tl#K9w%j5D>zq^RR z+D>1)pu!O5Rsftz_x=us@5J8OQe8EwTywg-bR{!;)bAvX}SY{9**v1wnhrsBYJ4p=vOe>CVlQdE+&9k$LDV?N|KKt{fI|kqDxS)y6T`FRvYjJ*1VyY2T*iNxci~VxGSf_=S03CuD4B z`Px&C)wGdMum~xoQ-)B%{)HMB*{UzvPJW;Y0GyaSd$W)(J({ zIvW%dNVD*kl>|l!8FE&3CwRDbCh5PGzq)TKFYtaWWm?bX_8% z__~ZPY;@6Q)U#ba1z`pMLiLJ3aHW?;#lKL_EHR~PooesP2hm#^!ma_X6e+2tL@aL| zfvt^d%scCn{l}59ezY>*echZJ-VSrBr|)6GO!%k`a(t~qqAmgNkaR{=Z#^r-b_Xna zj|IX0BBV%R-_bp@f4YM_m3upQ9~#m+N%kA3A64j=Ro?27g*Pg?mUCAURJ03m-=~Me zHWoGL=3Z%-OvTge)&IdeB1=jyYQLs$=c^s;13i=+C;#G;d&Uwer<-s3CnHPbFI40w z*~CKrDAurAu4{)n`E-&aS3CFRGsKt|?VL@Tm;MES)x$C7$WpRp#`z=dPw(;RSE!F= zEPkzYd>*Tj+$BV1(=S#m2w|!ss|X6 zNe0;xsI^ChBbsZo(OcyN2UacNfr6=gOSOpqC6y@te_No6{}-tQ3j=8h zL_~msv;;y3rvHI3{V!4p4&^_H(|<`NEOv_jL)$xr*ZGDIm$7X(wryv}_6{1`Y`T*M zjT+mwZ8o-T+i0BrzTN-#UDwRf%$&?oa-6(*_mlfs_gb*na1y4jl>ZWcocS7kW|HtY zR9r!BbGUd31-I^d4Vu*RKc(=c|Hl9Y1p@;M1?HUo52W!_M&?3g5>Mr;5!a|V=%rN3*w2%-$dq%_{Nnt1xT-pX^LZ1?(92+#SdLjv)gc*Acl*=g-#+4Zj7{UIS!eMwbvBxnR5M^xvFhvF}e%;t0H@`I=+Tg zX$rJ05fCH^iR}G@mU{_`0-;%s0wI_KGY5lYwz!L&pWH&gpx~Kpcx`{Xe0%eo7@H7) z5-#SO&ur8>1f39FoKNvl0pP(vb-naf%Y$`hy97&23@`w} zI>A*4dHPU<)c_kQND>$n6HFpph;R%RI6Ec{Zpv0^Xrx6zen56U6apqu93>9~zY!__t!laVg3*QcDg#s=RF~O5=@<3+}8oeQ{_%b49d88P8LqQ zGPrE7Rx}-WrBT$e;4QPUW88w4{q;CFP)1P$IW-{^SP`$-fuvwP0HYFrCulwPt1Zi7 z&{Z}i_*~W$nXTS|*lLrQ8Hnv7+jU!vJ@Ws$s1L zQ7JW5#ThX^#N2k^2eLm3c9*+dyqc7>2nr49whNiK!(lNvF^j$YD^UK|D?S_&arJP1ZS6#J*gPnTao zGNx2iZ?qjmCYt7?-xzh`LJ=DjJ$EDWNRlG1%Zgx)KqRg~XNH6XlkY4El{E%g7gO9? zcRb63hR=|TDC8i+oK93Gi|P^I=q1dCOugKY^-sL`tMiU+1!YfOlJQL>5G$5y)~ z9L*M6NFoT%dworg%DsiA&;jE0v+T#wu`mO?4UJ!Bnp%IYRS3JBmP=>Z-Ran_)OJ7K ztz2Hf4xBZ+x;Cqgg$6*&wsc5r;vHpFSGa8v3#k=baIO@l&7bc}$;~wmoAiGMyg~y_ zW_LQ4BO#rclX$h4J4a}k=h)*mL}Kx;;Gz8!T!vN?fM|;Ct#po=8xAL=wQk<14WUXr z9AOQb&K&)$z3oc5Y4i;B=Abd#dMLh~V+dew$`gI-)p$gzYZ0gBw~DMWq9=G65@%G3 zb*u%ZGz55c7Y}_Eo1?ATlvdgaDap}vFt9{4YxFA!yFS(Y*m5% zx}^JRW%P#X_$q#QUPl75I}A@R?A`d{$Db6o^t__&HDw-BCF}#Ng+vsvvc&AyysR%8 zv=uTAkMV&cps-PmF=R*z1mcMCywE!lS4IQ(Ab#-q7ACW(IKG1 z+0jeck&6fM;KCLYki&zYF2o=m$bJjnq)`mDj8pLvSPB@OFpp8lK3!1$mL3CyVx^Q4 z>VgSpv96*=K!Yy&jFLA|_Q~Gf9_ij-u8mPc{{ss&ACk@biOB%VHSAPfWjSLfi9zVWE&v)36^LT}(;ue^c{#0TQBi>`9PPGkxL@rGE4xAJwEYIaa@Re?_ zZ49T%@6MUmlUp9F&AoUw8gNZWuA3qbcKH1*TC8OFpZ6&bYNb15CS&GGbu;Enij+xD zZ-2qhHC?KPh^Nj58uO1LFb9p8 zSOH#h?jJktL$m{Wefj=~0bgqgvm-esVwQlZ`G{sS8#I2iG1ZFZT4xJv)G;c-?m7an zZrJf8p@-y9M-f~45!ri|Bzd|hYTaBwjIMa1g!O`Eb_DF;d`>wz@H+_*+y|Mrhd}`& zt_DdIfdP!bAWv1nSb%}BDn(G9!=$C;lyBODUTQC37z}9 zsY|(+=X%QN=~JT_Nk>HVHAUl$V&oqhThmJzR@xLXZ>a96&H9VxW7N(U0bacA8FU&BjQ*HO4@;D1j3GYniJ4gMD||G}1_ef#!L1>@@jP zS8()~CaHMi%vhLK&yuJ?N8tvM%yb3DyO6oCv9&df%5?-Dk!8VN^ZTOG@}P1byiAqP zbg5kVx`#(i+{V~XgkB%-F~wDHLX%QBnX7FCdVx9w2EmZ2s zcakML4D`Gc3&g5U#6S;@03L6jv-qbNKuO!^{t1@6mPg7kj$x*A?(g9n?%?Ly=+(3_ zqFvIvCgbJaZM=t-$*jc;c?Z}fQCBKO9c16O9xb_C(wZW>&D7z zo&>pV|4qm`S?_L4L>k9a$#&PYXd@p;-GD`}RpD^P3E$v2RH0D7;UNNfAtCfiEdV48lxH8(@P<9Rs2S+b&^HpkhM7@hf5UDb2^hAqEk+f$}6EmjrSD zC?Ua`N~j=VF!YIkhw13d4`Uik4haU!`Ifd&plXdMyW}8ix;;{LG%z(4C{4ZFysEfe zqm3`8i^`jQzcvuxJfGJ$EZT-CKGC+^LDKq%$NRar8gJj`P0)JJ;Z84~ldAV4caWR& zy814@DLY6WS(Lf|vSH$C(t#w_yWA)!(kZU@F=#9aNnKJ7G(Rh`4-Gx{WnDt)D6ycj zZAgW&9HfuSmYi{XJl0@lE#xyuR&Szx5FhpNOLprF6)Q4zqC~VoivghMbO(Y0db?eP zk`2mtUR^fCB;^G4B7^3+U0nEJ3^AOS9BW)Rk5oo;LnU=dr6bK#rHANEgX3*mW*VA3&%c03DlAfS)m1#9*Q{LYxL;t>)7J;#2Mp@)>wS zOzJUaUT}0grEkEW<1MSLcETyFWG+E_&B}^C&m!(Zh%w8fl`BJZ56<=RzqS7Pz?>0* z?Y}T>OnT}eVB<6u#83SeJyl!Qzu^P9o#aNlqhYv8Q^R_;A9B2H#sSAEq*Xj;8PT%$ zSB7fYJaPUj6OReN3_{lKM39J7PfimCHW-$j-fot>8vJBQBM|IyDUdD%#RbtKn3Mwc z+5kz<5{TEy5&cXErsLOXFdg>!TSoKZSm(2MjP3&xuDlRfswPvAWwIhwM9GYqPJR}u zDY`BUYjdI_NSMdcu>Ze1A+ymq*eysHIe!N;3Aqf(Ip<%Zcl8Dy8;SmNm( z0n*~>9tpwX@9ECZg+|WFTC<(7tfFL?@3KaNQBxl=Uf{*$kCBMe23gi4HG&b2;^Bet z52`~%(SWKWF)C4@Ey?+5wAu@m6$M^4$r;fY8f!4SL9-5lWxS7egb&BQJfDt9QFeWl zjCn~}LctXg->9d)?Gw`In8jRoaj(b8pIEiVuKX9Dwg=|bLXPSe( zRLDO)>ZxWp(`xpi)t-x@uqhz;yu<5++59ZDvXfcT7sG%6onW1*4K;7)A>0WWjDvD-5G?=>o|sYBG&%TL5#iX z{iq;+EM>1P+#fsIQWV0189uh#-~aV@KO&Aw4s|sbI0cc`OhFd*^>72Xp?Gl|0P(Xq zPiU`v7Z{a(Bqn>?+}p{4^YU13@{AE4o+}uhf@uO11sZ(XIby;B!64ka#rZvvgrEReP_-E+T?S|6$_1Rm` zsZWc&d1Tq?1=ZyPgOevoYwMy@q5*dBKbUDVWrwy^g)n|A1G4V;aqZw>epQc%>omBIx`gxjJQ5sf z@|sf!Fvmu~P!m^1v$TbFejPZgSOSX#{tBbk>J#O`g_&8``0k7gCTBdLn$fXB;3G+5CzW2v<+U2gai-aLQZ+`yZjpac|!gE}!aOUK57>n(6 z{b(FoRFl8;bU)r+eiGQ$#ppnsc#vSI4j3L zGwZmSRA1r?;dY3R!E+~rL?g|scI|cGG3;&=@>nw_RmHByhW@#w?1$psaMTwAx8fB{ zj5~Wb0iPi>@+)?Ntk_GLevFN2P7J#KZ%WX&v{%!8?lUI}YU?tP3It+XXQNA<%!W_= zA}6HuD#CJ?zCEl(Q^7m2;XVue-N96U1^lLqQX+)b3syN@Bkmyi8ulE&dHeC ziunvW{i+M9PT$Pq_Kvf^GV5nszAvQyAE#AV?j@B@E4}5bR|X|_?d$^4yl>LBH!_=F zCNb*TtKEMS?JmBDR+dN5@0!H$Hb>di*~>hvY6wiG zn9VPkt?&n-$HU4*bl6UV6Nw|^>?gU`K`oDqINE3TQ~0VGWHaujv1@_ z&T)`Gk4$10WbA3KBUrn&nfU=4mS^}VhZo$m)jpeToL^0H9aD62 zlNt72&gl}Dw};Ym%9HX0-q1v*#S#R_ zL?g=1JPFoxq_gWd7(ymknx)|1jGg&g>~c_SM)Zu=0Yqm%<5uELh|aHndh8ZK;!&kd zmHq50iF(&j!;L(KJJ=`|lWzJC;u{Q$bmUPfuRb;Z5xNacbp$I#udFTAzrdbn8-*9K zrKhx3XiWW7aw<(?M~)4xt>D@vludX~>OObO(u5lodHQ^dmYdj)z{>0&3o^@PoBxp|MqxFV|4fF$gcGd^#n>V&t01k@=#VkDag++ zM}ha1c@ZrPR7k?dj$nUQb4^k&xa{LRN1c+H%_^LvU~Fm0yh%DAq2L1vIWNtAwtPw8 z{JGnKh*%TYFsqDeM3U;L&7I;{@Qu&c?G$*tnF{zsZE8`79y&>eE1U*2nk~N&r$ggOl`7+zTOAXVi}Ld234$67j{xxU zHh0=d+mcuQjrjg=}usn4ceZdS~5L0{*JW+#q& zcWGTRcu`Jn$w060hM+ecP`Fi83RxR2U(Ux`zxYg&(yRIfJw7$Vb7u4W_?Tth%Ot^C zphvcD^Lq$%v~cLriMG<-H43L@DtG8hp)1Tuny5bPIj3XwBdZ_uTK{Pxw0fFxlwA))nbg)h1k|Xrz8_Iz~9&9W<;R2HM>L zyft!L5f@9D7a23_7MvZC>f}&w>uZ@`%H4&kR8r^#)p18zS-bQ zXlNatz!bj5X6l_*!}k=O#I!Ptx>OQ6Wkc4PE8fE&x5(KEkW3DKr|SJwx1E|BOQuWI zE%{y9(=Q3@n$c<*;dtP=F}1D-6xsP71>8J>1L`r{;9v|~Z#nF*1?D779pbtP7$(|V z2*gZ-0SH|Q=Xd1r;0PsFI)uogW4|a5_2onK_Cz5YsRY?~aulbf`WYT!EKXe)C+IB| zsVz*;8skZ2a+ERrx+U*SQW<*_jLrv%*-q4xSYnI1+ibEc)Q-p+|Kyf7axA%bGxwR` zeTrzSytaK*{2pUjjm~o*oPEks*`Hz0#u!!qf7vK6Z!>#WCKYVlRJ| z4-GIMjf@W>?TS5SqUZR-c+r>veE7f>$V>Yw4m+L3O~!0>uFZYrO)bH$O6C;)Gt$G? z7g7;4LJ0-;W{`}FF*Qeq5Nr@O73*TbYPtHnH7_vS#D&dBsDi=HU>Am!C@oc_8o6=( zY3vIDjTE;|^_+tnP#vuV&aOT|0zdAVx#bF{$&tE`D{Rfw^F5XGYTW)zLU;SAxmO<` zloFIhpK@-+Z-en41f3`9MzP6w;`V{RS+Y#qnA+|dwII-ozyIVe(_0|TU%t9`Ymnrc!nS$nr;2_Eooj3e9%iB& z(X*w$b;YBg#ojOD>Mb%|U7kela*+f8LocECgBp_^wQZw3N_Py<>&TK;ADXb%czOvs zdZ?3mZ)1<_@ZRCVW9L(HWI9pYg65Ks51x{x;^YEi$P^Tk&Y!kFV)!$@?XNwi+V3%k zcFqlJ6_MKTc$tzi9_D&HjebejNtxJ)+f|frYS;Hx?_BN&!gtzv}2ljmR~hBDmVXXVmpQ z@E>$wN9Q5Sc_Qd5MX^(RCfDsr61a6kfp*X)O_QT2kAj7wE{*^rTzC39TF>TrYcXNh zLftT;N_>>u2Oi^|@E;Pvj;TMb!irF<$3z?Q?SJ8PS>5W%elzPj^xNl*eUVt&LevWd z>}Gm17*Wm;eZ+9Av5JN?4aV0y zVO|-WYZ#`4_~>hQO48!2`YxvodtgBUZ)G#i%lO6^elNMl4|8=V^@Vh6-OH#{716AF zsk@8v?zs~=FUFe5w<6j8AYZsP3?K31mUVC=bt-+a2TXM>mlv{VoD0DB?<5S3$s_Wa zG*FRKZ-@@g_39=4)F}DdZvSTn#KrX+ryvRsB}R3;Y5UKPJt094em2GQS04dd zO@ZG0*zx1f_h9==WOCmOX?nrr?iL5O)4i6g*ho12w2lr(fw{~$sUnYk2h1IDNn99v zEKM#1K%~4+j#CE@HKr?>9gqjCOV$%df$A(qjAN5XF1BlG#q`$oRP2M!Pi@iy$ICLQ zBjzzyltK9=u*k@bba4DLOs96Roa{9To9DhMt@|m^UY|puHpO+4ARDI>uB@(H=G;K=!o0A@j zc_4a&enXw~2a zX#H7;kqR0Toug~ZzFDz_bfoe>LAskR9%5K*Qe=HIMxdhbWil4EwdOAZ9eU4$BC~H? zX%vD=^oBA{V3o9Ezbhw9<|u5vk%D%(3sKMp974@st2`e6T-ryDjRqOrUd$#IjL3Xt zLY?}=(?R|GZ3;QlRu3Dv1MEo1T~ZB1Tayhc5cY_v%(rMsxsHxSW4+BZig|Bo;pImK zEfV>lGRL5y_~5ZX53!;_r zgagE=KWTobde7DkndjH@+O~uhWF;@rZU7b(7IU$flQsAgPS!t(KjNbGTktl~?UJq{ z3YZT3-h+P8Fdg&|?38NY0TbS%>F`VT@l$EgB;&e+j*1bRp>d=8$0|5o1@L3$ikq1T z&a24R6!Oe-?x;SuuvVnl6zSPu#cP=}iNG`OAw4;RsC33ANRtwUu0Yf|o_VHwg#gbZ z2_TFQu=sG;XeFZf5Q^w*6*&uHG8+nuN2qRe;ZP078wV7(UT$6*veiKRp<35t1ScU? zwY9_YA${_o$H7%ks!~x?`a-qktm6&vge60;B{?HeOm1RqV3I5e^+YG=NXFK(3Cbka z0Hr(q;y9b12s#m&dBBAtmtNF~$q_I_bUb9M*A2eboZX1P|K0;N$P{Gyx0DqO3T%T_gT9h-0d9PpP3i=+&*~ zxV6zcFy2>!u)J6YB|q!v&9T25%$CN;{>%d(f5%jRm1GLzRv=G6vxw%%10p`+9drac zi;-qI`|od!#W|Nk6h|>G#w3izos5>Y$DF!70;7;Bx3rgxAVe1GUc$afzzUJ><3Ers z4#%gMqb{tE*0;)>AXB-QLj!CY#3KfK$OndTMne_2?JP{WRb;tl7VsQh*oj>k16nin ziGPvZ&o?iS25)(N4NvAT%`b+3QkQ4yjj#@lbvGybGnAJ}TNj2Ltb_R+GFyHaeEQ^X zv06UYCj$nG7MNwPq{&3~uEc0$+T-=sbw3eYy#|R+4E!hv-Kw%0e}n!| zO%K~NZJi>`68)isWTBZ|+~r+)Dq@v_I# zef}?*^Iq|sXhIqAKZs%t9y0$5$_I(WM1jyCyw3X@f%&RU0^lOOVPiE7 zz_~n#f4w|Z+W()OPBs4zq!gHA`adYs|M5COg5^!H82_Vo0y~{x5n4c0|Qeq za8Qu2|2JStT>=Xx9&B~Wr<4q&>YJ0)K`}`}t}l88MV* zjR&X%+}krML8b7rjs+MaEjH3@acQiv>-2faWmBg71|LL?8=w*}pxjc%OU7vu+Nd}g zsCmr|FEWu~8NsHCD$jJx7Rr-wa5v92=BmhvBdH0%Io_h54puvLa04t@VRm^T9){0I z$w?;i2sd3XP$8p#M?~71i-yDqJ}!r$z?C4p5_fkh z?xLR-mq~78^Z0PgK{?L8S|+ePCqg6#si0GlUYYtWjBjE+u2Bh81^owcgM-bD#%gOH z)_)RE5;WsrTX+8ow0Ue|oLLPO*$viK(TkW)4 zt)Zyn;{k?K+uDb!A=*d0iJic~JogdUM31N4>BfYRp;BLe_uR-bf3#I%ZAB)sH=82I z^vJB^R!tz^39{R^%Gz7lk_V0tR^%KuzVr4ko1NEc#?ZDHHwS4Z9j>Ii{|M-3ij3~8; z^J(#%!cI?$KEytZ2JS$~@xu=>P0MK^x#vOu$>_;1SU9h6$jJH>C6jKiH%`+f_=%fq zKvi%Uwxn&SHqh)D+CfCKo{ddQC!~{XppG)QTG~b}BW5-NiwB1i8jMJVFD|O-1dE3* zs%2n{2MTGxT#4h*Aws$21V_H9m zxlZnjB2IiyNS^qU?GwcKEyJ;T8Wi%pP=sDoQf$zmW10i7qq5|gbgKWvka&48vQ|AV z@P3kpx8p~Hc@VB@mI2rD)Vu~Z8%oS|3;r3Nwn@3#MR(Nh9P8!x^{KvMFGM(@foUl< zPde2jwbKufwM!p(&&Huy$7QRv)S?=1S5EI$^zID#3Y9q|?_CqW|6s=))8>;dq>`yJ zYb;9rpx_FH0Pbu+(V%Y6p5CFN+CN6Y*4)%9QG^SYvp+jI6!ZI5{Wj$<-L+y#`^=zE#)L=yaXdt>|kVy zRGZZz8YETtD;AY}t<^TE4g-zyz%uzfCVTwNnbV<(8zoe*LK3(fCdjL(N;k>e{RlSy zjx~n`=P(KZg>E?u(JJh3oS%e7-+HNCo08*xd%ZRciX7E?_9{%^FV4=NIF$TPFV76< z?WHk7WmG{YPx{!lKT>!0vEMvJYx}Bd%@E&!Ssu)uqKCYHC4M!Dg8z@0N_W7L0~1q4 zuiD~pN*j1DAghl};RU0abpH)%7eG2C$RY67}{ zOsZEwFmC}2bTcMuS-5z#41zM)5tc8cw!ponMnH=QsZ>aXgbSW6Gh%=$ShD>JHfSajKB*&(7E}puuj$xagwayXRcz8 zS#yq;+irPbF}G5$`?02<27fm`o}2iNN2|0}WXWT2b*LGnGw#;5I_GwxEgXp^7Qf}9&!?fi&}IwDdKlboTuZN>q%TRa{bf!CnviYWv*mxi z>`F*lc-?-Z`y?yd)2#3$v(M!8f%PTr2H1WT5r(Unoeu-j4+#_4hV$>pdr?0KAMt%_mED8<%Bc^V72t5=ycg??&`%c@m)sdB^YuYMD&sy12!rgNl z?vjYp1(3|0h30k8S(o=z&&?fK%fn}!Gjg?ss*12yf-fc(HW3V!xnsh2b2?92hNkhi z2V3`tL4oFc1XJFddhsC$)bwe)NDr_RZ~#|Tb{fVQjUn6O}7<(l1>q} zkw?#E1TdOtnAMy@qi7mYc_{^=AWOT&s|vP&sp*b(6D<~0>i5P)&W z4ihaL!9Vj`Mzg4%-U?m$@Wvg|E8^cz{|TOUs9L~+C+`Z&@X2FGsUeY3dpTCh)f{-o zt|d6@VtVR|6opq}A8Otx^V>(V9$o#M!rD?xvCHQcfsh;8ycPWqpxkYno4ccv-d8OJ zhxI*|eiaV6Zk2)|>+t~HhC&|-_18}NYaPh{YrNvZUJ zi$t&6WPx|2m2pZ8{Mz(JS`Sn6L<33sWB&1#5Bz!@_H}Q?9wjvjBEE)6hRzq{>>Fd> zx}?WM5suM>EyD6nB!(|*@x`gbA3`faU)lC8c!;vDzM21VV1{>T5%pG0SEMqvIedmo zJOh1S6Au+%DBhl^<9+!eQ$^6B-R&WqeeBdy(4y#l%%GrP3zw>olDv4Iq<{?uruP5w zQmX}B`C?`v0Cpe8y~!LA)fFL9l5##+ss4{6*XuDu2MJ$Z}rjuGQ=!Am_A)U`~+U+)n+ zxP9)aYd@6V^Cv2y5S8DwPpk48a`K%&{^H{khk?3#4t!)T^HiBf4k|0;b&bfCw??kf zQAp)a32GFOv6)@&_1bO4)$tm-&t`V$Ve~-$qAX;{r&+)!RN);>NU*Mu3?l=^q_QMu zEFVA1$s%X?<~`NVB~Nfr!b&ssnSxEM*jqINSy0`nV+Um}k+Wc*NLFyuwL1{SPza=k zetF*358}oD4BX^5wQTu5z_K)}pJe@Q*HMRH_W zJyAhw|K$4DHwu0eIkwN_TyX0S8k&JvncAXxF20Ckiq~Bc!ZDl=-hx1IT-VmxVnSet zJ+mwGGjVUhNecHghQ#o2=l28n)}6-=)A)M2z&Erehi`&ADPDTUPIi%-r?rO;YL|Vt z-fd8i$Eeo)*8;T@di3%;>lcZ7lKyi>^U}&o#_g}%oIDLry!JhoRhM%3P}_LmoB#znWzOy!y;hd>>=_qLj0;pc-9GE zQUK)z{3c$uoMrPc790Q<-1Rm2I%pX{_Y#$h;*BPP0dc@bOtR7OTgU6znFc`wQdqAk%LIo?x*k5nfIZhv8}wg6{asLNVLg**p0!C3+ zD*l6T$d=o*29ywwOpRkh;{)?&<{@^kV8 zhA&oqv=MG=1oG&n&6}`QXsDjiDOWR`YmGoeThsCnrk#@cC*_oN<~fNUPt<;bA|PDU@g^H4(ALFW90U_?{%~~ zb91)3+8K3pgYNEEW+e-wPOlXv0%EfI05%)P;oU6t7%2lOTUr|kGjV<}cf4gm3YqQk z(ae*~9a|U7kzc4UxI^eVaJjx=9`R(Kran}`m}c3_nCrF5nhOY&d+!5o4aqME2cMSMaum5%%td#|- zDLbW%B+k2y(J$Rm0eD3U1ax7*iVK7X_aPo zkk-&KtA+>N)g#1iFuNV=-c1w2_s|EPHE=J9j#J%F##wsZvOQyV(}Zrqj=ivhi^+@n zJ;vshDLLQ0E^Y;;CX5PMv{PjLP?Gg<(%sOAY=Zw+IQ_yTqu*HnK1#wP-b z4Y|y5PYPYk9fB>q;ih{RNFxh#ZJov7^--r`K2_sO_M6D)W9M@>JCg(h`PuC-$ zvLgUgi8h1^R73VOMLO#f7g?q&n>iG}O%FTMS60MdL|sW$d@HW`yOacsv3y)}h)udk zZr~`Di=blYr8A`lk${Ud2u7j0U%5R>BI+mOTU}$^VQv)=-x>gVFEL_zatI`;Y_s)! z0?DsJ&WZEf;@oLZm$Mgg!#=u28atlmHD)29gN-TGL5=<=pL@O!i*p-^;wSR8~VAOq(i z@p*Y=Cb;BJq4iasWurHrGVy)lM_u&RbH+v3`{iTUph?7u0{Aw6F`XQK z7&JE?jlQjlL6@}JXD+>GLWNZ_QIL1|eEnfL80$7OurklPRYRL*F1YVfKnG6=^e=ptwKYZveVSrEKKC>sKTtr~pE%y1`z9WmP2Urmn|1fguVKIo z<5j3B%*XnPs<-*iVy`ukB*lMvu_I5vN-LM#bP+W+IefVo!}`+2(AT+VKf=*EWOuXP z44Iea8HXY{jp2hnf{IB>Xk@Xi;0qE}4 zPvozf{paD6Q5;=-E-O&7X~yO}m%QLhli2Ks+_fKJY`U$wOoRY1;YH$LwP?Ns7*SNhGx{j?+Xr(st$&fkA10AvNZdB9N zq|!ANRjGix$C+Q_TacCJteW*f1FH{#$2zhc7XvCVm+@06t3$44yQ0-GBV8s9-8NS= zOMR!d`+aN~Txa*7j7E0b(T9wbfnbNS_za(KhEkcQPIz94Wn~ugG*=YDh8vB4k2ho0 z@Zc)7&5IJG%I`(y1(vsFT-{^KsmrE8Q z@nvw(@XG*k+v*YGbwfe(^Y&v#)@*vHf(&AW zd^d^@eS5B5b95KRsCTfjHZI(sH$KT2aXpoysN`KNFTDnOaVd2}xrjH*NXz0(Ee3kA0u~y$)FI^-_be}skd3u+PB9PrDC=Y*v;+Gs z*fnAB1TDaCdJLCpmX&6Dc-Ze}P(27>@KEhK4F{SI7VW&R4{i@wexfM9i}*}qt51>= zek>3oB2u#=4ECAbuI+n%{BEs?o_9=P7%OVFntbm$eRGvSGfkIXJMo{4$dUMFdku-k zk@ZCYkLCs*FbTC{09Ejbo#>U#HmT$`yFz#sogN(tXi<;>iUpv8B!1Qj)^q*xD%N)TLdWNZRIliWlLW@^ z+4VDdYg&G$DAlM5ctYI+uLY`UN4Q@HgP{C5D)on7AvZ%PO3Wu~M8gn?|Cs+RH87G& z&_YLXZTVoXL!T%wFIqtjiHL%U*%~r9#)wiWh5YHnA!d!m;<v#ftqf!swo#h`3MpLqJjbn9U zMvITv!1T@NCwk*$X7f>xG^;n?6qp4IC2u*Zy^S|5AIdWtfg-K@%~rYczj%AApg6*| z(H3`icL^HY-QC>+0fOr=xVyUr0)*i19^8WjcXxM}WOu&rue0~n*;nUKT?H3ZbtI$;oFxX*ReeZ1=baKa|`fT#M}xRfgCXWAuJfx7wXu zm_>oBiPwT;w}sxg<8HrI6>}zj05=0M`vLU}&yG7@Xm+;Zx*2X0^jk*{*4Mw}X(tcF z-uq$u+eVU)4(=6$j?U|pyBZs(J3l5Yjoiz4b7aZ#&_i+#CHnUW=7}K7ebJ7uECsFY zoOx9)Y8wpS%d&0HO{JFy4ax`7ZY z5tuK%34bA8?NUSL_$waEo!^FJe#hvc4>}br{TlHAdxhZ{5U#eu3q2XY{{9MW%2()a zjuVKdfIy8(vwCE3A#Yjg-bVIJ<^dXY5wTb+bGz55YlaD%CDOasaUGi%DC6F9$5K@1 z9^k+u!|%R-tTYCcSq3{h?i6i96ZiD62{CNxytZw3>z0%!@P=}Bh44_w&|T#79w8x3 z1$ES1yKVD^< z>hk;k-d8?vD^i%Xj(#aar2XCW%=71b3UFp6%H3-iC#m9n1Z-7YCMVR#CSxnS+vAO4 zUm0Yd^$U0IN?xHkY_&+;2$7dN{1F_=AQvdYqPb->c=a*Hd&BC4PYF(P$bRBfcE%_5 zUOu|2q_AkzX`kVuW~8IyyB$N-23p#tg~Apq0B56Ky|yie3;48Lr&Q?Y&!2%!r{(R} zl*}QsVfnAh{p%+wvN5-$n3{T zrY|m_I==EYT9Bu?pQ^i0fk1Thp4SBjUrO|sFfnTGilR)j0(?y>+c==hhNr2?GEG@M z!@k#)r+Glv-Za=cwA%F39_>#@^=x^?1e`X5&CIg{w(8;MfO@3)f&)@riV9p=e)eVB z7LDD8Ylro{d>hX_(!E^X%!ytWCb`~^#xN>zDpl*T+NV|;tJ`HiCJbdXau5efVw34*iJeWMRBl#t2 zV+{~!)lxhE2M*)vG@ofo(}^)$crfcz+>QPD_he*V~;F zwb&K$&6!nDXDNIfx7@+kX!A!uwDubpc-cK-9F8NVn*6or!TLHTD|(wDiswNz@Y$zt#sw{)B7b^6W99TA1TS+xA`!gy{ZpD> zOFM+-n*nRJ>iwT|u23g77jpFawfEZ!~FuU#U1@Kb*8=f3V( zip>kuq?~hZCGC}jXmN3ph$7xVT|v*gGuk|`)9f`)4zq)0;gbYrJS{yGs*t5COpq=D zoA$-MARbL<}2vB8Rrh zdSDHOC8)V5S4RnhEW_nv8IoYJ*otM!`A2sDT+>jIbO~-fPTq)aNA`%WxNhs6(}G1k zafOR*P@rwSzwqAefpYy}D^iiZMa5C}!^o|{owws)NjUomjVVXxkqV<^Ys=$~E*(O>a5-}GJ`jx9s)9_W|3VDpqg0&fPLUnTGoe4^w+zaCQF=^xT&wk( zNB2jewPoze#sgvkits8Zq^Mdsmt`PB|99Bx_c3-{UKPWBai#5-SuK$b?~=a|#MKU8 zNn0lBBmY9YV`E>u33u*MwXhV~3=5Yy4o%Pgrf*FlU@9~WPUixVIEI`1 z3jr?}^t5srtT$X{lX7&UefcQ@fBWTY>E-S39g7kxBg!IwvNqw3S4G)Qf)encFm&(um@Y#(k90qz4DIdaT?Pk7N5C= zTHh81Ff5KHe|!(CJ|p4YIT-dbT65Sr3whix%zgb(#3!6TQ}+HB;z{M3hT-I+L4&a2 z0&Vd^ESgds-G)d16tl3bw)@VCz38I%;r^JNqtn06-vIKl%idpzdY_!nk!#6C6CJfi z`^g$}M|Hd5QG}4002FrWRqUW?o~twj}rAg4gzwo-e&rLsIbNVul-q- z02TI6ll8wnSwMad1`-Y)@Mrz^>;EkM{U1~q3>*R!EYkm`!v6WQ;IPHjUCgM0B$D!R zIN*CVT&bsn>o`B-;%z5i{g|oO1WD3pU7OD~NLhsR_Z9rtqXi8Q0}1mV4;R3fLH+Y+ zVX4EI<+}tWiDOGp)p2M{!zN#C<52fr|6iG~TR~g=38K^|u)(yY9H&9_6jmcuU{T^i z6XB0h34j61#5BO4KVP5*C27(qC1e~cy|`Ic zgJR=?j==N)i0ZHrd3B~BIlgkvjQ$5TbNw${N3!ZK5F_JZ+r#3Cq@7?%_kKMw=GcxY z>A1U?_1Y_0Pqe+Z(R} zw=Eb{bdFjgb}g=)v@RXl$@}NHu*9CfE#^T<2k|cp<n zD(8QsKbkIeJPJiNz>BXiU~Lww0)RLIObN_bz$TWIPnr+=&nRXeiNW3@V=ZCJq}GgE zS|w>c%_m7IK_RI!NAG;xCv^gEj*3^LghN>tto=P-)&Q&6HeK?xh#^{AtYZKA69rqv z)%k%CmR8e*d5m-8Kbh9JccZ(>;vFO4(n7fwP!*Ky1$paX`s8k;8&ea)T|8T5-h?JM zCa%NSo0AO5Y|fm?4uVESaF@pcGHjG_KABjHiyPNB96IW5a4J|jYm0>AoSe6gEZgl# zz^WHUC0Qb8MVc!p9|$<23qpSAz!Tic3XqfR`D{5O9mc$ z+(&nMyhf-)QdT*l98SH#IE!`RHqOfZo;3r97m_<)@vDiTCbxR-%i?(PaLxlz7CU## zZ=)fzPpc6Fo%1A7rtowB5owu%XIrSJ6>2g*g$ueGM~Eg@*Z9w7Zt@Tr?`Erp)v64E zOtXtQMF93+=z1`)5I+q&1Q=JQ)&66&ee?@?H0s1Z#>)i)=zl- zg&=a+k{OM15&^=01$o$AlK%M-DpTme<9u~p_*xs|$_&-C?oAD$AEXIF@nPq7kfiVn z+F*|~eg6*~Bgj>W_0@u<5FDhCFg^ez;JNNU zAdEHh!y|=uivnI4g20pp9W2D60akD(F$EzT18X)GWR#r3%jq5*R@X3zX}c~Q+ZWZj z)ap;M73->~T*~0Ow%;C+ju`Mi^CW!Is1|lRhfX4wm7IvLI!uO=YP{;&Px%7Cxaj*B zXw=`k0-x?$qHm%Z2I`^HvphW+bd~I&8J5JN;?cft+Q$|MoNSbj;kYtdm7uKg${sQL zQ*XbQWBpFBpCsoiv2PNuIT-^qS~2ceIKFxEIF@^oij=0JT>Dq8Pphd0qMCg-?I(vE znmFcoPzD6krfULbb}}K%;n4+*6`u~1v#hgyAfDE%UJ|G^2wF81E%aIG_URpRPn%uR z7uy8WY3=Bmo*jH>MdH=L?~ulMkslbYKmU(NYXjo3hHiTzn&s6czi^EKC3T>^Xgn#w z+E<-@3*%g$)M4qKtRt!VXF)Hb1$jN* zOb3aouSd^rg~@N$t~wKte7i|S218N}H8ykKfx+VZQkM6ZDf@QDW6G*kO5dtjWt_Y) z4G8;*I;V_pqB#1k|I&X;Iy9Rn3tc;&$tvm|D&_GCN78(0v)uI^{}E~Zghh zi!*6sWe0F-MzMSJA0UK5Z(_5*Y)FxgjPiLGc8Z1V%jb!JFd`H+luAe&ZX^_pSXvvs z+OH*Quu4)J6{7)xt-9LEHwuI~wNPLKe0>U6JtZ1122tw?NiMqr87 ziqBIA!`LMAy;>e5bdz=iUzhaud)iy3w?Wzvh7BH$@8~hN@Icjje|IInfb!hMixAr` zOt5KQQRTEY$+8*vAD%t%rX<5}J0W(08W=U?TvgUOMfnu_be+f&BUjuI@7NT?JH?tf z#px6_=|0;uG!(n8bNl_gll0dXk0VWaiF4m0|8Qg3`@q5l4oDHuX)SHs^w@7Q$uB;b z=(m-Ce1K>DAq%x-O?@XKr|Zc+sD7dYT8?SAnmx7-u(Tub?5c2?W@x;9Ci1K^??G;^ zS{gcLn5qiTnJ)in|G{>mzD>{;uPmiK|LF#=@xssW5Eih+WumhePuHcR#Pe7~{KnUC zD9>FbZR>E&x1_K!W1%^X9VgwlUg-Vq6~%kfe)ZFNBWKg)A076}*Vx%8Y~oOyRW7#t za2TsQa8zs-eE5u#)P7jDSM3wW=a1w7uwiI%srOJ;je5Hup?D(rCta`0|{B z7Y2o;TvC`^@HQT%#jgF2ak5eESUztP!*wc6KLNT(fDZE{p^p$nNxPQzDYN&Ie^317 z8oobwgQR8@xyMThhS|q$Z@?8y&DUZI+YWGrQ9r>Xzyixy>-@s7$fPBtheVkY!;Zt^ zmD)y%Afg9ED@5PT3?gSjk=#qg%6cVLFkl^RrZS`Rh2RX`S_}f`woz>{sGeCC*2TDi z)25^yT8CNR4f;deMt)5~263Hm^)1|~DzM! z1OON1geEQ5SK}6eoVkLA!(9Rq=yRfPGKZ{}Pj4%Ar%^oshn?}Fcl-tWGq4J*OSowER)&RWF!bj<=28DC z4m(KL%w6{)xsm_NyI5(g3byHv9$t!^xJ zgzh_K%3T_24tZCP*sJM7d*%>cIU9cD8Pu9a7A~N;i{@jRq|k3U-4eW>MXaVEL?3+$ zkPEpW_}A_M>B~cM<&0t|Y(gm(GTFcfkPdtaTG~iX+zV6{+b~_IkCp|@p7xR+i0Fxd z#+#bTBr6(z`H<;E;1%Dl#omxBwNKLhXM%xv&3D|sAvq!&-}y|sq=;XK=nA(MA;~LB zcd}H9Zpx}7`Dt(UZ1pc2&z{xpT~1MnbDv#?ixMg##DpM;R+|BpmW2fi^!x5rp5^)R zdm4fXk#onsd&DiFWu`%IoV4M3u1JF@NvfNoi-Lxx92;LKFYeqek^*r`a_CMV@f6J* z*`8G@ews375NRnSZdNEYN)v00^i)XWk5IaUNYtrA;uU~&wq%`iYBS}lTT`DBcCdRi(D960<%F^id-Oye^$Mk2n3q?crQ z-IM2)xBRZnDj7x1Vp74rMwpxjx}o*v_t(CBnvBjn8Bn!Pnm~)Gjq|ACTIs?#YaxTs zOV}@wqMC;)=4ce;Ly4j8}aniQP?IRjzwj4=94rgiM_@2K~aHvW@U1%01M@z5lG&G z!o@J14ZvtHwj^A2or_g2{wu~khS}&7B&g-T9=LAmotDE-!&V-eFIJ^sYXLgUZGY}O z?R(RUIoD)Ydcfo`<`;Z-)*L#~uUxs0{;Eq?;0C>YC&lgC15jhQ&8L04y>nC>k(8w( z;ubBzcSDi4Sh6jnOV^On-3TcZX278pOIsZq%V#ue#Z?Qpw6PV(9D+`$aK_BeIes}X z=#kBVotKxVO-fTplcC8SG(5s;kK`sOHfiV?&yC=ig`^Nj&4EJj=smhS`aHNs4k4@Kyr zT9CLw=s$3bHS0mLeM%jNYBe9885Ck*%|v&w?Q<=ldX(7z#DL*3Z6xYVRw7R9^a zweYO1j9jZ}zQ0Y*-%%}M?y{QG&gwbmRpX{RD6+3c9Xo{FNHQEAe=&OmtE_p&oIESH zYm8V%PNOt9He0Yxt@qlIj|$;gUE42|J&3p$&Zwd#p{TaB3Va?9F0d+3b{3oNj5hHL zugkC>_fhoIVAh@_+d7~1t+40Pdg@x;bB^cXEd#}uwB--?erff{xD$}{n-_(Sj*7r{ z(ZCpb=)lIWoAQGe#q@K$B=O+g*O&#MuJ_`kyk}`QH9w@QGv$WzN2}Zo2Wr2*B)-z+ zoyHVCgGVICbP0Ria`OE5Na)}im#d)@UBhLi{!2-xX;K=Q>kBaSWiN5}Gze?D^j8yK zv}R=3;#qnELNmRf*c7WgSCSnIKPaeE_(j~5_M%v1gMu}5Sf}a!zcS(WC>=|D- zc$nEJedKO;0=F`XWru<)a-jOuXvVkKs zy>R%o*~0Nqdps+o(3QF+epJd`yf%AhcN%kDve3*)bqxedtF3UoAnG+EIAsb$qs7C9 z)(;s6i@R%Mn{(22hM=fc@a(St;Kw#cH&ir>YvC6QqV2Prq)U^u@ZCXr4Q~=M_mWVy?4MpY<_8z{2t>R+d+`c-Q`b+pXbz*#Y01b;O+VXO8Vre|-D4YA3KAgSdL{nf~ z;??itCv(a}BE><0lH5vM|ICG+IC7CbWw7tf!p+PT+lvh?t2sa1G|b_va2r<;ZMN(_ zVzXn%9}2Q`C&pK#D^cCYGo3q{C)FPUH^|8)w)7bLj;~AIjLsSAUL2FF79Z^*(Cwwz z4d!ZL8n}rgRO-a4or(k3dn^-OG(c;XyrNI=tA!ziCU$*4&<`Q5R@TV8pdb#YAd1LT| zxPk>UvM&u;oRA%5K`X|8AyA3CJaZCKVr0enI{Q_sZmnf9Od{Ze){KWY8ybhl$f}%` zomG$bUeN$|0gIA@TjK8QzI|RK*USLc7%AAfKl*`jXt`UqX?*c~f5}AH#R_u1M0#Q> z+ITW=w+#6!cEavd#%OqM@%GXZf1m(wjsXetChpN2Zf~tBu?Nu<3F6xaZ^LuZ8xzN7 zU%L>e4$0;BE@WgcChN1=H&T)5&P7M|2wz^GdL)?=5SZ9yOqI4hJ$w0+IWe)M9<%`m zDjxI{HU?U#u@@d;6$c>ti73{;R?xkY=^$1*Bptjf_)tjylQO{Q$6tsHkH_E^PBCmCnDKmW?R=>PNiH@x;+#{_?Xr zN5zim`2?DD;rMA`$Mu$rI@ePM8#}@Wt8o78Gaq9oiI4yU@<;Hb(`KmPJE6HA59vzA z6RwN=V{B5M)lP!dC1c}RZLa$ut(_!Hx;fI9DP5+dGu#>l(e{RG0#4vnmiSuHKKHFP`m zR^ORhD#b7!cn(mD;!vSA;Z_HD1f zN87};-yHd*#_PAVGht*!&eU1K@yG`gF!aH2m};;fe4w2*Mr4zKQ6d3If5$+^)%*~P z-d~U_I_n>pG{T78w(@I&#Bgv%c$K3h)TFToWacOyl=@%G#I!zPw2-ZTyh-nP`!3Qr zo!-J5L9+POcpRX`3L9mSi+CrhZ#qjPs{)1FG4Rcr7DpIFkm|1GAYI~JaIWM*RYmc7 z1AdeCIolv1WE@=7v;=3gv?@H>zWvTnjFO7xs#T|yuX0LNaBmsULFA=;WaEq7P_~GJ zBEU@zz>9?z@6u(kempTu=g-o6niz4evq7_gILxmjEhV0-KTq!O(BQd}xAp5w;9Dav zAklMVzD&hl=%*^5auKFc-CLZMmu5LTWFrxFiWby!14M0t2B)*2@430v!8({s2to_Q z)(1j(5oiltdzj9u%9Acnd(YlA)R@B`_F|3P{gYKwF8L27#hXXzdyJD*q`ozzzO-&&Eu)x_Yp)Ryn^sEK$MK1m8BckmI|2IK?e>Xhao9&#Bq9F5 zv|%J5CbiPYN8V{5eiJ-4X0w|ckX9HvGt4Z?iL_VO+OAJ@)=(|KJK8-+&e9EE){)S> zokHeEsgrVymg$?i_t@q|^ZG6Etrq`JrKq|@j1Op{u!Q|-XYiHV!F=RTnFmSUP*>z* zZmUS*l1k$P>B$|5U$f{_Y+6JQl&G_AVvAcyqtBmO+;-b=?Ev(Ns>XA5^O45XZpxn~ z*U=Zk_SZwd;F3pt`K?Y3Cb?GfPWe1mZ%qss*0z0Fg*SIj*-Rd~zE%l&wT-r&`nl@rlggMABay6Zn#0c)v6FHa^+t1#aAuHJ-}rKzzdv<|ss4mX@ z))i+zZ8RmMK*3Lw7KRwt$R5?AemK|&mtR%RxD_HPv|0S%*ajwk?bl9YKNTn>k|RfoSnyQtQr&gO81hMLkZe0oowQA?qtaf z+$`Ku@U=OCyQ9L+S>{eu6g!vgm_8{j@Xr(Mc95*J<{hw{mI{!~L-ws2snyL0EbY-Q z$-AJ~2iU2tJAsy0I;y$|8Hr|9(FGPdzQ@fX8?5}4r3<+jVGgtoHnLy`$$cm3o zQ&gT%OYh*8-P)@yt70+2Kixh+Orq?`LUDg;IdS z+sQ>ZV(#S5Hq)M~jSL8`)0y2Lx87fkg+g=WnSgSPC)1!j*hQaev|I#6ZHgt!2z_^T0^d*`= z=g~f&CNv9bysn`#N$L$e=U90IsRMI2bKr~ilDXA{y{WB%Ku}dDQ+diBC|TYq-#^<0 zA|AB11u-)z+(Kvu-$_)ERGEaHdAhmn<3GxfAFV6S%UVw2;lywj&cxSzKG3IFd1`WI zhbXWVX6|A|M~n*b7ox;N`_84gZs;B`<%!7HK3n$Zvu zfBN8lwLtmOqQ(D1;sCF^chTuxheHH!vAe#atG}43;^A>4NS^xJgpn^o^Y?1+`blt8 zt0oM8JTv&rU)TNhS5&E~eY1&guH0R3mt)W%Y1l7RMc9?9P@-?k$41MVrUc(`0*eWm zi3n{6%k1^b097L_#Aq_i$Op?7z53%aD5xf?)lxXPQw^3;h(|*=A^AiU1-txBtr2Ew z-QVtbtq)LsQBbK%*75son!R?7-PnJ2xp^)}pCMAH)aA><=JLhuM8oR00uOw@Tk2Cjq@5PFyR8=Mx z8I!o#5ZtlSna|QQe!@BE7Zu8FnAEq}bkd&Wr@0zO#0@p&be5DgWiF86jF8&nux5z^XEHz zsuDIbfrM%XY*VX?C{^&U<9FDm$ zn~lQK9s{F684w=pFj9}?3?ls{b74wE5Fe-V_UHA>p1i7_TFIT%_)ONDd5jDln%^8J z8w={+*lo zi-al2ErgD5-jK&8S?j}2+biTDKW^#**DqW^-!6*0UcN=K!ghF1A0J;$ba9n8I!Qf0 z!|tk35rf%Ff*z7~C%3Z=mxL6qql(>T`S;e63m@O7U&AE78T+JR6N8PN2N!X=9rxNu ziPT?pYdHGN2UsJQkeG#|*GyS>EJ{x_9iR}R&NY)7r2DSvF|=kKWdy9aI4B?;%_dJ2 zWL7y=WTlB!wO#pE?NUrz-LL(?-64trc%y%T=uo+5jBB}#U6CCD$ll)0@1=XA>AI`W zH^Pt?b4qg#fh8v2KOKIfD(4qH7oOa#Z>1>P&o!fm$90pEvZT1a8R-n`4}+TW-&`5w zS5k~4%(<17<)|2teYt?j`aG^WEXZKzktC9KXrd)b;KP##qB+Id;k1HeceYtWVOKgc zTB`_hr6=jTppUgn0PXGdTn#DH$28@1YG%I~Ny3zz|6q_i zzBPzXxseH#m)59}vD~J<<7CeTK)_z0pDEjGw;LRxY5^aZ;8*g1c3j zxrMT9m5(wgKif`elb{e zkytG_MoMg=iC%nMCC|xSKU*`B#BacOoTR$r} zyqyb$V`K{-Gn_zZhDa3E$ z_=>Iy!#&mSzg_cIessn@PfS{0$I#f>$1YUo!a>(EB^^}^;f^#iI)8PHtthu-;Ytx& zVsH5~xCg#RD=$mdv}+qU-sn&fSo;LSrYUnK)Ms?PGW{JMfT)A*TrBK$3hI8TI>)9o z8N|XPC-A!47Q@H(xLR+RQv@66Bqc99lQKt+A^?6_u27^*!0GEsZGKFM%c)#{-ju#~ z!5-`!nQW`T$*|w zf+1#C1Q!W^YF<5=xv#4m#(JmJV=dZEkAoBg+D}VcYmatm_;kPyDlBA!I5=r+gEU58 zKZ6Vmt`62G_yzIu7w>G#wa!x)Xl@!t-0o~%KeDZ~R#(`rt}2qGLv$^`B$CNDT{>1j zoRt0Uy3kJeXjgbi(Ee!ME>g|n9}{59&ziJxTlH`*c=BT0EKH^++AhjHt|Q-6wryVfSEN&3u+ok2d^=Mjn{QvCLh`Pz&B!&9qEIbhF4 z2B}GqJ+l#dJ#ys+|Fe?Fda8L;Vzt`7c%4lF8``3lV=kWqS^{teQgB-{=m6{6b(V`G znD-aW4|-kVGV7nmYN|_#j;8VkthFx^lr9IYys3sYg-Xs*xjrRYqeY(P5jye4Loy4#LbyQa^v_-V3W$77|%&Bk8& zA}T0i11Ur@$)(x-dx>3Dwz#}14ZUpbpA<`X0dGRr^5l=*7@iMJ=OX*l$k(`4g#qKk z=2gaB2>YK=aSNZ=G#|6AAGL$*n{&$D=R;x!oLtH zDze>L?hdIF%`6XTC4bg9M}%Q+f>#{gKOD^b(YO>qwDz<|7g?8+{UlT=a(p4HCR-%i z6%rbn>#YOG_&@(BnjqawcrK?mFU(B#48#g}_`IKHw(X= z!};`M`Z*}BfuAhH;RWEl?tHq#ccK*(idS}1G&9fNbOtbJwl*kNx6$^5;A7hAHe}Iq z9o0N9>O^=n>jKAULBl4 zHQ1BbR-`J}ORv5qy$c`WmTgT~<&eMCPa0e}vJI1d=;DQwF9UbX))$q~8_TxX*-M#& z=VGYrxt&jNs@LkYw&@k|DPzXTb@PDjdSg@4r zRO0G@_RA#*2k0aAy4Gn(P{RjbC39|np2nT|ftPYkV<9=K#ogb~7g8Sz&|Q$gu7FG; zKzIFT?~qWG0N#ZKqb_ci6y$g)|eC zPJ_s#KvuStuZ)QfTQ+nvA!3q~q(ijNq*;X?pplZdFoI!P(9umH)REIn4Z}x5O6xnC zh`S+XFIt?^bI`DHjb3Rnvrt)){>}E$N$cA#slH2S(aZr`m=yE)y0FnWWKz4VC|;m_ zU32@j+*x>SBc9Bmr(O(k4);xWvdJ0wg|v_6-nfJYPSkNx;C52H%{57)oK}{FHB2Zk zj4Q^7%6AzbH4>Z>Sd!4dD7JNzJG5hJ2epFXF=6=D`^qntPqrU7$O5CIKrVC?h{F`yc0 zr3WlA^YR7b(zt@hTT#E08EAbo9C<(6EC^?PZW<0kW=eAVqo1*zg(o2I zP*~6Zo26?FBAaLH3Yl-jJhrti&c>$>$R*+fH_DMj*)B{%&X0^a{G2wOaf2oPwh_W; z0cYdI4%*DIpySMk?F73>f~?h*!%U*GXOFkpbO_Q*9=S>V>ghU8y0EPk1@ z$!As<1`nre2b^oc8N3&o+xDDr9;Vshg3HsbHtrp6w+p+U=jT%!)PK`pvklXHnY7vR zPn7w7jqEugPqGx%>SAX3Z0ECT*$aHEu5#U8u_&70oW1D2?7~k8+`NV(g`Es6i$7Df zn{Kg#>Ed=fN$#A)$jAAtrm*xYaHF8ZHgS(0CNTCY-%`UzRAc`_kOs8#7s87;Q^bPd zl8BkbB6lp)@*cm%wj6)6x4tp2j;@A~)g>TF9SrG!Nm$3yc(2k^DV~W*gw-Udfgux* zP)0b2LBY%?g#f(h5S1&D7{Er6F9?>yZi2vp*``pLt7Z&sZ!8OvLsr6C_ktn?bRkgT z_yFmJZA9zLTx8N-*i+=W8;k#IYlwHpO|oVx?hJXlyX8*%SE@gL~b=b4LVP#&G%AUDG`yH=m@ZJxbxoU)E> z_v^z`CZ1~lH{}5UgZ}+L0Y!p4N!y@HEv*ZM&D-Vyi>zo3E zI)I!FHofrpEbi-txupCjxx<;xq5N?llb)x1^Lw<~bq-VWK!IS139zb)r7(Y{v@qHy z66W$^PgGMn9!2P07gJsoQ0A3eEoFpqsfU5!6QT^7p}}I+`cJeSk(yB>2U-g@*NZWX z$Mnvn0NXKQ!2V^GG&3rzttvd;cyX>YO0-R*0%FzMVO=KdxKD2>oajCXb^U5^!ww)H zU(KqkhtOMzS)-ff>^MdW(<(0|S+>j8?W?F(JGY;I^kdrCpuvstD**1Vdz8#%ja1{a zb32k`!j(u`PDtuHd{JF|2bZ1Q`{tKI*SSL&;JmZ44c-ex&Jdl0zR8k>YR4z8B=a1b z<-1X~4bKt3LWqT=B*xUsgtc`K4TKR(jBt$1M+F*b<}hmEb4N5x>QLAUAH-l0gQa>Y zP_RSkO2qz?L}KGn*da9lRy6QoxaWhD3&F-r$ z7BbH$-pA^BGw<}Ew7a^x$=-0a(;$#N-R`l9z>n`WG;LY`7_X+fHI%)JjYvY3oH#j3w99_#LE6kkSxEtda-*6^IHT2q(o^AJ8;`yPQdz=$m zZsdy@W^L^w@mn#^5_Mb_7j!js*L>-T|sxAdCA!vvxLx5q)bTP^%nI6kC3F+|VcggXwYv7AaTWoMq z!^pIG{P}rPHLEjatWP#21aUatwXwbCoGfu{)qr913NyOSL6UruP9rjYI2))fEhJfe zEUt0J)+8LOKif`s9k)W|XdbWm8(eM>>L9)amZh>Dbpq$|8BtKQG~*0K#e^+mmx zcQgNQ7%RFkv#Mq38fnsby|D16!Y$`~&(-IKWfJhDkXePEOVa$dCvKTj> z4lTv}$i|)`6FEs^HU`{vA{3zZ18fk&^TFZO#|W-z+KWlw@#+W%70K~|ONNi}Pq^j- z#KWx`&PssuIyMA2ud7(&37f9p*!>&%vPv(0qr2&Z=-X-W<~idn7_j{VMvU<-ds zHp-=T#9T-`+X2BYOk5l&zMAM9y4SAMwOusoY``l+;Lq#2#WBZ=Yz*3-270iL+-b|y zdZAb7&8WWi%sM|lfrNT`SyNf(37y?%8VvD!WPNasayBU+Pyq3|iPiI66aFP5nW?A> z`5(p$k#(x_qxMgtRs(J-h(oU~SwB?dbe7+M5}5^I6gMP1OQjyn22;p5st8{2>cios z;z!(vhEa&n`H1~9by#0Id&?u4fZ*aoFAOy|dzcO`69Nsj<~nMT037!;6ov{03Y!#y zSP3i*@R}hJnFgZFM7cp|u0x8o=!9n-V+qd}>IB7sJru!vp&a)!%U%$|QzbdIGRNd~ z-}8=7zJBK9Ulkk_p(0v@IyKaMR2g8LLisKKpH$+d!(QTc{2TWxvxtcu=bZN4S@B

gkRHDQEv@84a1OqdA?2f&YU3Sc_b=TM*N=NV8_7r(K3|yd;XzxYY-^3@NchYz4 zDC5q%X2ihEK3Wi7kTuw&T=`%7SFnc6wf(rpbk7x;90jQ?x$8N{S`3{ZTVm`->e!G^ z%LA=)QPwlNqYA+`1RnBa71_)rrC(=Bq8JN!`GUiBA4dAdPW=_LYqD30xCvrH?&{w7 z+#p<-yQ(mO#pln8#n7=Z+4KEqZ$UtXAcV{AJXB~xO4cb1mZsw4f- z{%Ok7IW?9ZyDzj8dN+uY3c>qbzV?~w=l9m5%yDZO$5Kk%s9H1aFmVG3(hhatU0F;G z8fF^-=Ln)e3-|`~e1NhgQ!#uN1e;1IlHGTx;7S|{O?V0@V<41RC}&t>OuJZ}IMm)z z*nU3+tNbCF<3b2Ny1$;XVtw+&LD&0m8-=V}VOUwcm{b^hO6h^aRi2V@RuXI<}8~dE8W6m&u5?UsG~o4<~|QT)!K<%!3mdG;v_p z*twC8rOpw}gY(Sdk&)AP7$dwy`>icU5-y5$huoSm#dc(l*zI>gSvoLZq_UN%pS8|W79=Q?dTTh`5R&8*YX(3{DHe|09EDXI3AS?kC zpin_@Ca|=#w5CWiIWT}_i1A-a(K9VL2&UaMyb>oI>vT~P*7GM9J}CrD9z`bFVK{P% zC1WIvKu1dv{dNga#Ig&ZNa&fE(c7pQ{nTo&{?| z?=GyQHTs2=`_QWGV?q8?m-jk^X|qMIqy+WS{(DxOky!#hoR%uXQ(ASqfc>l0_rkt$ z!RJ$^B|m53uMGh2vW%9E#gGsF)^x^;ZXV(h;*dWjFf`Wnu6d#mTfyqULM>_e_55R& zOz==s6{uft(fV=ydrp;g(?b=;+DcfupK6tv%5XPc$ zEj?=!cru6Qb~~ytx}t5&WSLK7b1G1rKhs4g@?XHq`1<>WJTbKS1uE+U+zzqyy(|#7 zsmyOo;X<}Eb@W%vKYIImCu2X@cjhMuV_>^Yz02yns03rmyW%uzc`G#g5pq12w?jEo z?$I&n))=$@&8P5eBV6QFbD7{GxJdLPmAg9A{x6e7D8K;5!^bBC7{EaP3}6mpGVROU za6~Q)b|K;v=*|$R9YhNW(&$CE2%Gd~vFu#PY<$UzBF8sd1z+aPS!$B0+liboAzJZ- z;8B!6LY;S3PI2wDY@}khO`Nx>sJ9qClU^$C-xF5mw?@WOprnv0eyscM3n#00hKJX- z)y%^E-@u~vbL%arIb}zu<(EJ?a|UyGE)Um;th?^YQc;R#$wLg6Cu~CHslsw0D;5Sn zYRvfWfxR($>*kt;$h%g5B(`8jc|q?6t+>*2F@!2{BJ?^vE!}2nu_-^`rVq&iVw-yg z)585Da`48t8=l?bU9FV-`LU>+wIH|hMV=MX zah1sEjD@K#rnkTu~SrjxYs;Ji0x^|UwRh#dc64`f-+;?WUWPqjS z%E}6k-YU7^axSx;|fBgyL@!B(?4>p zZPV2(<|Uo%jwax7Bbu)*t~QO^e|>X~8HI@h2KagJOZG3sK8`CSvd2bqI@2j?tKV+3=A}1+zPWI3e3QUSJ0OtpgZfo#uTtM+AQJ zFNAlX09GaWyNRId1FGMjWvArS$rw-KQt2t9dW7Z!`?@fjg_Sz6~- zd6gNbi5dM(1w1g(C$>Q~7=LTD0h*=aC9ox28uUfyVe39oU>0v%0wcE-<#Cz!mz1PF z1@t5)WU2ly+TJlZ(zbut?090^n%K4`ww;M>+qP}n>|kO}Y}-y|;^f}GpXa}|yH#87 zyI;1e(%n`0kbLM|xz6)Ee#arHDNdoD$On;gH$Dw^0*i;*U!PB$kX%}#ST6fu!G!w7 zI_pZ2V{n;1W{f#RZU(M)XWDlGG#i<;MdXBxvJDi=)XW&R^xDhT;WCR)jfat zJNW(#IGO&=$qCanP`ZiQm;cTvTf@WwW>)VM(P1&V`F2I=VYb$^xH1M!_;umj0IEwr zH`(}vRI5i!&c^`Il9T^SqY{PPFm9mll$@zGq;L_uTZeJ2D*UP4>2(*bu}d*egt ztF-!{k+N-Ic)KNmy*=doEP<`nstu8E@F_3ax9XC-vVK9~IjC_ypru%L3&*;e}z& zEt@oY+633Fe~z|;MeHt#sInu{-56j4%$sqkOZ%{mhE0COFm(GHLLAiaRM~U3B}d^% zkQ)8DC-Sa3U==?ssm%{_Tq|!bV{fk`Y~!_UsJ*Px!K2(h5*D&PKL@^+`rF|7w(6wg z_F$~=O4?c(*bKFOusS^oGz1VVSH!d4TECPyu+=*saD`<14yb)~Pj0Xy3m#V%keA^1 zS0?C*6{YQso;#fv=|aoRu!scAl@ofanx#H8U66>>V3r zPO^OPV={dbL=nwnn8&Ra|hwil*YFzxpbZ!pjhxc;alX)fA-f<7jL0u?_aMA4cKX6 z|Lvwt@PdF_Dj!9y8)<-70fvgsssk0Gu;XD}{PLFC-<;gRv$AY#zg57=`vAPYn}Y4f zjCn>ks@>m5ok5r6YAX+x>0q4QX zoZAAq$(WqF3!=8WlUKq)m(`*A1#T;s;_HW(?rWp4V3FbG1#kwV96zU$V9 zu8F*^spDOX#T&`G(xR#gH*tLi?xf(gM!nazXQBhRz3Q@CIX!m!fg?`FoN5=e!>j2= z#QBxRC9A=y6dQsrg3om>8RxS1yfqejC7Hgs^R)7-e9fN{lVk%uOh=ubOp%N! zOu-g`ZdxaCZ)@jc_7bA?!7v}3bP%ek&2N@KSgn_wo=bWTaaFJtiMPa-(W5+Em$)~a zjN>&=@DgAw5|*~2bx15o7J(ixqN|Vo#8WXI*7uHcZ5i`}5It}y0W*E-TNzsiy3Qhh zGJ>s1jzwF1<4B8&8(dL^5pOwZV(*O|X)TMp`IQuNvw6hrSY-FN;kB(fS;gUw0V?1V z@W{nUCVM@L`jJ~MuxtN!i+uJ1{xAytLchCu81ucyu!DsT}i(;jNpE@|rmPekPr%iXAP4jJ*6B<3!RXZdnBk!=-c{d$zcBmCb# zLcL3P8(Ctlj0n(U<{2txp0++)0X|-SF~r+w8EgUo z7_7lSbBEueam8!YuoFq^YiSLHS)R(JK+9)nja1CbLeLB=XuaHB6u1XkK2QE#0Ay&O z+|=S1g402f&<+Sg!j-yX@oWXrh-iaM!#wF`@?bbQu>v2B%v-y^*ZV3RtnL12($cv| zTW`l@&H3TVO@5<8G+*FjTx1|{eE!TLjWTIW|nvH@nPEfom&q|u8tvo z<)d9!^F`OhS3^%0Nn2zqFsX5PING0cP?9%_!~P zuvdCN3k6dS3zV=9e%bo2@Fjs9jPzASyE4-z z86i5$<&zk7v1QkAgUzRYcr0fTnfDINE`2j7)0=}@T;iJaM3-Lu-^U$Uu9+tGcU?zi z@kboYGEXotQDhsE{X2&am7Fa^yIeXOWfi7e*u1Ylcc)IGwDtN2w@A|02MUdLtcLM> z#M^Be_!<<$qhr34L6mzJ|T}P!t=TZF_=Z!t&*CWiL+6lx|{bGnd zCQ=rq31-Q(ep-N}@perd?~22V4$Q?ZyrwL9E0QnzKDr*}W9dJW@Gxi0UklR*GlQpf zDT6w}hFguDxUf7Bqbn$hqyJvi|6ra}^P_VX7YC_pgW1Z5Q87Yq_>TWviIgK+?qa$9 zotLh9@@kh2d-_0h{FUGSTB~E%r8&G49i1;sD`H>oBE5d-|8v>+$ekyg8?w#5m$ zHBo$HTfV^#zV3rA|J)#%X8?WXYlIEyeH$p>DOuFjK?u;_c6XCZIxdcmaEi6EWOS$M z&WT#IJ~?3h`EsbaI(N2zH~D^Trpxu331ZfX%E!F%yo4Bj;o5aHD!a^rm_rw_V&$gUU92q6nN53`uG8u&qjorJ_eOEg4CC$$VHpHucgHe!FzC-kUede1SjdZGHaK zWOHoBF($&v^*2RM9_sU$Ie3MocvMI59X@qwD!w=?b;sSeH9U7umYg{~YGM3M?ulc7 zIGWxGWh1ihmRM1n76(>OYD1XxSvf?O+M4o*_mv{w@CW12ei7Ck9rnMN*29B4eg;0W z*Bmv<3G1l6^dmeSvJ&qa{W&AOh&f%zMZ0P&u+{jG`;Wq}ZK>%pH+6hdfo&6$AjU1H zcJxBKzVDrOzz{{!a-Wb5VjC3b;?pSA=E^KOGaCy({@7|$1)Oj&Otm`HFJ-g%_uOxz z^Npni6_3zreXNjnLR0CoL8R~PIJMW^uAFnV#~R(mt7^Ljr5?+Z2eJh=ci$u$!}lTA zrQF5&g|k3i?9GBZ<)1QVUs!w!#*+p|HBJ@SC32znj=#8=H692lmcF|#B z`guA!kpOG;Hz_r5D(zUROYEwMToj?L9D~CIa<)ll5|_D`FHp!V1tk>S1`Br%y+|Y{ zc^sV(ilY3hgt@A&FGf7Be>wdq$g5s{D$pdA-ig3A)kGx}xNLih@~_-ukjBE&69>yCXpjy%Cd#cJ95l zkJ}q9oWr`BfD>K|h`)Vd>>v+U?`FU?b1kL7hNLwwDHNgk=3j(W3IA^mhmACe85ng)Grlp*Vf zvLi5?O-z0DISiu13H3qHMl~R_e_g74`74@XDVzLtnqWYbmbZj#Z80zw7nt}UW|c?D zPJ>$5CY3P4Se!&FfHCSwuq3rK{telR53K8tn)$Tsa{NevIeCaR!N5C!>r5-X8!jzJ z!Mf&x>ig%pX~xTz{+~*dAF&g4Br%~^|ZU8I#- zV`CO4D@Mf=iOU7&(7-$rs`%IRgG{YTqbdPV?gqCIY-@hAQ6(p(lF7ftM-23Y{sEZ& zJan^?P0Z2zoku{(_i|X{zx`7+EM#zg&wFY_da={P(vW|uqAGc4#o^_mYxF37<7$yA zVWwKtUkUY=6f;YoPYP+4M#qa4E=sb9jHRNuv*S;L(C~EG->Dl{RVCLo{8u{?g5N0; zl0ZL6)V-$qsW-{4UvfCeTa{HVz9T0>)}hWi(r%!cE^MJcqB{{AeW5H1WHqlm%X~Ms zEJG9~@*NDUe%W_wi#BgoFYxOAsC6zS`m)@REqNebghK37$UGi5WGh5SbEDuRQ60C@r}EzbUP{|AmDM@UNch|H8Na zwPgL@+lW_#X?G2znBkvXK)jhG-BBc~jB+Kyb3k9UG=`GnQDfs&mNx z!ac3=O;kWk=I5dUp6{*Q$VQUpyo2;C^jiM0q6cqAvgQ{5j3#z?*| z{{Nuig0HjetE#JBiJ>xN76+pfrMCWYW1|zjQft}FrW42!CvK^=auzukh+FqBsYT9A zHaKddyn)6JVz$IuslYGM0|Q(3P(}M&DUjlH`s7XF;$VYEDnS+)$aXmnnPFd1(cH3T zL(*m6)>@hVx1j-2*h#gUQAC<=*HI5BI1AUd^lX&cy0{F@zipG}#RzZJ3a>6BZekh%nptmAjp?1+OD1HdVP2E`H53=dgA>Ed zy}hiqt#D3=nFB?;dvOst8d*{FFoRJhAvHXOUDP|m^a{;*bc=L{?wdV5sy#YzEV^e_ z4*m@_^16_K`CjEKXDi@y?$D8dW*?Lk=J=%sB@LnEo?`xhE~Luai6sUjb?|^`KCIwx z?BbSE1FnW>sDi)H6V&#;zo(Sw{ni*+56JG2{CR^aEsp@Qag`@Mps@h&gDfBB zdJx3MtMp?co`3{w1Gve0!^zy8?T4V-5uhi0Rr?4&eC`TI*~txQAvV6w4LO?~hxTDa zcAY@4eEAY11K*|_2=n*_%;q)HlqE+<-M?PS z$g_vx@4={kwK^KJpPHLCwZ1H<`hDAt$#`X2KDcJyZyszfD!cyM5w~p>`*hmN&W>V9 zbprA?%LHyqw*P$E0A9#%Ca3Ilo+zhIckq6ZaU%E0!z4Wzyz5=H^_Xmn3Qnhq+n}Sp z)dSQ#s0S$O9j=Add%I=Gh3vL!-Ev2>Z^fxp400m9n*P>9!SKQKW>(-8Z4)AfQmj0sp${4(rxC8C$v0{y>`<<(t!-GfWnLZBKK2QOh_8&}x#F zWxFVO){4jC`bBr`E8aK7@Y`Uxg5KKc{QZg7ar)4`O-Mx5i3#~yY47=D24s%k*5*(Z z8?_r#m>`Gr#D)@8ER7a@AT9Sa6kL{6;Ubo3rV;+6f;wJa4oh)0USi&}k!+sZhR~TW zd}uD6%szeIm1Vbsgy)H0{q2GOLdFeLku{onm919T{HlCr=!k2t8~9`_G0FMpu}drv zp7uW`6+*H5^U^f_-oEi|cXkQPA^Slf#KivfrtY@Y=r4BoCEJR*$);Vp8)8j`^az$LcQ=kuHDZRiHa$m<2{E1aa=6l&HD6s?sIP#Lm7VPmti(NA z(eUFxZ63c}MsTWRSQo8JQbeiq#F%^!D2KOapK|Sejvg6jPWeh{XABiY9VMJZoP+~A zsS!VPP<)ZXT#=M=4xLmnUW*gyEKzYMkk-kWNddxF=n5k^w$B!JA{#4pGAO*ge;#j9v|HBl9d#BzBk?K@WZJHTQoRh-O#xIRvK$Xh`pVbD(RB-XZg!~ zgRV^kDp0`x#1n0h)>cF#h{XT$I$5R2ojk#B6Oqz+N`7EvY#W=9w`)@ozHHpsvEJwe zy3RyOmfd*gulGB$O2|CQYwQ8IOI_ga0>NBz2tqIeA;V)%+<@o31F0xoAo{&?V?L`o z@r>U=g0bh){D8LFBN&8g2~xVYg?BiYB}9;MvT>y+W1oHwxo**YE^wQh6u5F${{iT|)PHh@<&s!@S8lvbcuhQ7?`#P;tY#Y$H+sNzPKHPC-YaO^a z>ALGonVH9g7$p!;lx@LYXuEHQ3ty6&6B*lbwKD?YT5&!b&j7@Y1)ln9+-j59tZV|^ zyw)ovUrpffXLMJ4lZ(#OPhFq}GBSI*o`>~P9|C2BkX+hd3Enn_1gbep9!mjmNd{^& zYiGD4E-GjIM1WrLv&KP%f3)&sm}3tzk#uUb;I~*D040ecpEAtStx9FCWTHSqU!+JT;R7h&>T7`UK{1gHZviOR-brK zhcZ40(;}9SAASPfK#(3OCM0h!v@4w`FSI?VO-(Y(W-spB=6ca8FA8SQ&rFae4eqC~ z{C4S)6&sR`$OJlVIt}WhDYlRs+@0snJL19oexyC-^bpOJMBy$ok>&*#pH1v5!%NlE zw{myaU*u(|T??6Zv+#>2tS`<%MFSQ83`C|oIEEI+&$b<-bpKvAIbq*X5Y1PQF$%WV z<(Q;I)3!ij0y6XrzJ&Og#P^C?MpTIn8VDsAOGU0&MhWfk2u`i{^V=T`ypUEUps6Ha z?}sm+b}m#>97;SJ(SCevl2bS`bkuD~Ccz$Ar{6k=9wOMY>XxrmbStvyqB@d<>{l9C zjJk1}y6(L@cbzVxX7Ain!NN#}%c(cvP#gqjsV<)2zbQZhv4%JW5)L;FfT( zSYnG8QR~0~*xVFdvGOf5gb`5j!qMYUNP;lCNFfg?0D38q%$XEmK_K5TO?_Zt2fR|V z0^+>r3!$}aos<{@Q2ha5Q`Z-1m27CYAkSwQ^y^6PUl8)q9@jO|~(c{;(F*}Sd z6MU5xX$RHqa}+-~ThB}Y2nvf?*H~9a)^xw?ot(aZS#Pc@!}7H6`k~_YEMzeAM4)Sv z>uniqx=ntJ)%S5}(3lPaehL;!ZBg|X4P`t215mBNN?Xf~+iTy~i5>eM4h{egYsVWxrJXKX)b?2`kB08OR?H@8Xf(YBlR z?-A$ODwilrS`*9KRy&j4^0U_HrA_R63t4opnmJmG#nwknW8O1WAN*nCr^~o`u^(Odlim79St{fMdeRCr!67WqU}< zKWwa@P&|Pz#3znkv-UoIGbXU(yG#6cb#I@Z-=H1Eu<*->&Gzz}962Oglv#a5L0Wg2 zZDOOirJ1e{pN-gVi40x3I!}IoUo%BNUp@i?bMa_>y}kl_3mz%+w9l13d5%WL*EHJg z_M(pxi?<`g_aLepCDr?Zs9u~ZUC7uQAfFK;!(3}4v{r0127wuQBU|atV_ysA)}gJaHuN%?Sfe!50SbLiS4m`uHHt0Gtgnnk~kUUi%rkdQ|&0cpFaYDV&{1N*+gR96~I#2a~`olb`c*rOkWlZG|;5Qo38Vok* z-##cHLpO?SP6b80LlnyQG(7mg7SJUq0^rME0pVKz`nYTz(luRavaWk}PBU%nY*H?& zZI$vanyLFaDeyhvAAg&@?+m#2xFK>*i9r!C3&Ay1zF0Gsp3`CxKVv#swTBz4RUXbdK=jzpYa;KaE-&B zUfCu6m(@N|mKN6*Z|X=UpAGg{-R%qOIXlJIiUcrQzP#JSwQ^<%cbO?&!Al^ z#9`NEfZ2S3!9>ot_obPEmK%&^xyywq92hPx#T6@t_fGzr*EFcpA*ceF(r@LTnj)z(q(s ze#-bNb8d>=uxokG`gk`B)VXLu$Zm|b?JU+qL^avC_LWZ!YWFNPGfX#6P^4(IGoN<% z$YxRANPk5+5)ou5!fTOJ)bTK#=i)BTGM1-+l$-R+da8UUz6}0*wr0uSIR!iC+$c;S zOwb@~x{8Wcmwu`^9I3Z*rkewxTA@$x^s%9@W(zNGkg!e84^gdZLgV9R99Q?r!C*3J zbzQbOpCTmr9Vi|K_t&=>1Zs-8g(B4Qq|b~W+XOQu4%#>Gn66d&^oy0OmGT7wC6 z7Uqcdw50n8wfF3%IS{Lc*Yxn~S~^Go4kbGC4{9$zouVFG@rLkq_<}Z42>-%-@+44d zy4B*_`6dyapnJx}3}xOsMRoSy?5sFgZ<|(9?YVgAtESp4llSdU154a;kjTg**(YeA zuVB8nq6Us%I{!z(CGcM|%f{=a^QH4dG0VWI^L2Rf>P zCt{2jA;CG?hG(A@f{Lo4aE>ZqH`)7$tN?1AP;~G>ShvDbox~nd7=~e#)$zhD+Oi%Q zNS;hpDU#fx%Z!C@a`e1m3uT{~#) zm1bqRiSP7S{i7Lt@OlzrKM{H!r(N_&HH>OwbODz+$K@Tbo|__-UGr|O4{R7MeTS8g ziBEM?d3C-GX7e%F+0U(AtuDvo_Z_p%H(q)4eCf*|VoRRC@zV3YwSx&65t-v^69#_? zM~m5b4xX}jU3b``B&ZzUBlo_d68DL*!(wl`!^-mJZ!S-#mOFiCC*64n@U)C@3(IwX zIfYPX5y&b??URYzU6A$<0FuXZBFO^mwcwdCQFRa+mB7%`2urPolcBE_TC2LzK zVynxy_EIk6+(OR95j1yA&v6ez3At~$0)`;`Jw7y$I4Xs<*va_LzR(_YJC`{s_Rj~U zKIw&$TZqBuT0N>Sx8KwbuYwfQhW$Yi1QQ3Yz@N#W;)TlBy4pX0)SY2Z&)=YFEM!CS zN7}o+?N)f>YC&^{fkMBj+bjFccekIfpr088kB#z+>mPvVV<${K==J9V8WL?`#>NP7 zbz+2DQOpJ<8H%u_2HM z&wGQR!!4#v8;U{CVn3hzh?rh@?KN!Vra-KRP4Aw8x^i;|@ot1P9Xp_!rl}K|`pEc# zY=69Xvbv(J;I78;`ZjPojn$hQ#vg8xD@&RcrS*M;`R?#FHd@BX8gD4y;IQ8i&BY`Uy<2 z{f(1sq3!9j$$J|uZSc%xk=Qp01ZW?Q~Ijj*Plf`buqd- zml&KAy0@v**tg8koi6k8ETQ$tDGPGP*|Edl&+^J7%6St&i``5TdbbW)w>uhZk_@>p z*B$9VrB;j>C9wKFpzJ!w0@#4OD_@;3vJV52R38w)NU0#Qxr6~%*Ig+Nh?in6-;CV6 zk>oM^4C$>6*p9qWg2YR&X^%PHB` zj<<+|cG!bToMI$T=v6?Ts?x-y>N|@NRW4bNjsT-Wsf@$TzQ;N0D}}=XhVLF>nb)Pa z#jtm96oOrWMp72;3#m~~fQE>Sr%5fjWXagrAN!{ghS!=uTW0Mudn|^(SY6+-)ARZy znS0Y6i6GI}YMS$IyCBbOYrX{iAMby>ZjmpG`?78R1ISw38%6s5wP?G)fnvJ#1Wd;{ z_Elj&8Eb!jMd=AnZf}oQTC?y`4CMLfs!78SZCM+~q4;d~fc*>6>1T_1IF$dq+n?LA zkKyjHHA5Mko!_qL<^n`ou5;>gh;h3mbRO~JsmJ$9gsG z1kq5Ret87YNGn}Oz$`2TXAtXc){SCVv*w*sx~GzZR`2-swv^H`%H=nBc1c(xbq@tl z-gJ)}C50$Tx70Z?pfN;pk?p4fW9igNW@r$g@8169QdL=MxAV*m&#gB4vZE)oGUvQR z&4{(B3&q3aI((aO2PMY?}*gdBMwXs~d3q1Cj# zSdwq8?d)1v<-JTWdK~l@ad@R|7c=6dUS&W*c!`>)6eN5(<>Cc#<@|wNth#aAMCt4$ zQfbNYXglX{-zywoNXl?l*EREnx3;j9?OYgTUQrSXJLrn=MBYI=3KA|ZKBBYLLg+rC z%hFlPsK%Lua2Z7jrTr^s;FfzfUBL}viW7R@Z;e@N;@_}IeHDqd@SSGq-a?A2I=JU# zb?|9S1Rk{U#G=#Y)!-Pqala6~eoyNHjt zCj-E|Yp)g1ed$-x(3~GJPmWtx4ZRn`NJ280#Q4;n>tV%kVK1|?_=(x7$BnXJXy73$ zZn@})^ySzLHRWE`jeZ2o#n|0v*yN}Gx_y{duCGc8AADglNiXPK&N9H6_Id(oM2r(I zV4c|8HcT(iG*o?h|1R43Ar_Ub6gVic5!iAGhUzMHz4dQt*qbP-vpO}+n6!i81kJoWB2au+YE;YGd==zilM?^({WgjbD28FWexifyIW0S$fK$+VIr z4K5Yg%|QgW(QcMPZ2SKE(c7yVPO_o~OZk|qZ^h7R{U)=-R}O^6q>mpQJCwjy~ZIQta) zZq{Ae-u`qP3B3eDQ{Yd`@ONNQEBg)6wSwLD!~>YIFa1{V^-F=512Rb07V36vp}@=o z&Nq9Se9xOjs7m9ceV8-7=u_7HdcEiBO8u8(Cu1xCMQ&P7c^MRNgTh*&LyX_3v*;Uu z=~@y~mw8&Bz7nq9Y~N_wvHP+{M$bwnw=RPxX4o#D?ipFreldv910*Z}i@x`d_XgiA z)7_$_74}|gP1GMUeoQ-anQlez;Hfr`dq%^I@(;?lTiVKUFDG7TgYSGegL<3BCXa%PiuB{^S-s6T;|!+0c|KZ(Q%WCP z1YMZZ_L~Jt9oyqBh?}HkR%}= zbWHrNq>6W`QYXd1VLjCl>yQK=I?vsw2|vvwudDEn!eh#bX= zx!A`1ZBE)C?T<^Vt8wMl1;EsVicjInJNFgaeIJfIoLeNV4l}Kp7f*>Pz+4(R)UY+i zv-2bM7V~yaWuDEx=&e(0ZW9L0mS2(YQE&aE>|LpSr)`2z4@9nSS!Xb%WFJgF$)7s9 z-23S&;B1bw`&+OU0X4DVy6vBn)nYl@W!*62Y1wtR)7}^r7#l|C;%U~tq}MUUsSk%^ zPguVn(31P^7Ox%I`a!Ve)!pwwd@$-QJ76!VBhW`Rg?!Tg@Ef3nA?1%Os6Vhg%L_{6 z)btwH5vn{^eYX-Cl0*;K5ZL_?J$jo>JHF+j6r`+<tCMC^&~mz;dC!UYi|2kpbr~G&NPfw1 zdfH^K@{Ftw+UxoBWp7LhGK^|X-}qjYksd(U5}w;LSg>J5qqO$ zfY`a)YFAxi9a+sdE}=Vb%ZY4Ecut@kIQo3AW|*>_f44^@Udy&RT(hZ0pVi!!(wBE# zZOdroI7n8V*2^|ePx8FAZ)>9LnsWEGH<>Xi99Q3=pv_k-_RfiOwO{XTRYXU_m%N$r zXG&dRUH`8$_x4QND|o04V(qfgJK+zH3+@8IyiavPxv&2ef$zBVU&gbtxj$CX1~Q}F zYPbX^<_n=4MbV@C@~mg6vBg;%0x~fio)Xb3JB#GQ39?Qao0(X_O@3zN>{LJ;Iksj| zkg2Z{x5#bQd0@2>uCUM{)nQeKAf6jD<~3 zvO$Q%99-v&Ot-K`iP~-vrg-Kbjg3qA>iLa}0W6b`%)_+>wbi2BGRaIQeIM&*?C=ZR zZD24qUg$D9UQ}t~hdotmttE_-ZL5R`E<-EXCx|L(sNk% zLFrLzfBpxHS``|OUHcf7u`V##tg$uPB8NO$Ontppnd)Z~bPx8^ZsP z+~Jpr zGNzs%yrNTaKt(?}f*b#N4+#nB9umMOLR1sKL-PdPn%7s{g7ZRUDqjD`gR$unpej<^3c~0m)u21m&C=VKaSYX$@sn#O0{a^ysqKLWsUHXU(9dL zdiv^(rze%Br=j>2=slj1l|H>$>nb4>dvym!whIE4$Ob6 z{Br*gZJY0WCa0R&@Ta}vj%@3HfXnRm(&l&OCOQ-R6&##>W&LWX)3Ri~&JBxolo$bA zDy)+;Z(~);K7%+eqFn;3YLRt&0BklShjlbVvZ^#c|I`N)g94$@c6-(O`kPl~? zm3~sd70W0aaxBu+9NwfljS{$S^~?vU^eHF0M!+HENPoDBzjBvyoPfGyx00p|Vq|Id zPFX1Jk?AZ3*$A6{7>DrN&`CJERPZ6a_<+I~5-3{3wm)$tSjS2GNMl+0WW9@emrQc< z)uM;|BD263_7Od*@@vB;qDYgpY$NDxOl|Xx$?_u_-#ytc`nq>V!U`u~3|(?3emY$< z=6z@Tu{PnMOCv?+M+W6il#Gl$!7qQ>F1D7^YSR4l1bC3#iiMC0NmmBW!|Gm9mGYO4 zytL!Mgco@yZ*HXNEE|gq-2#|`74_V`eCW!bE(S-b_m0;Lgpv(;y@3>M43 zxVq+6?!(bjAiS6@9P}9;;IJy9fzLRO@w)>q4|Z*@=6D!oQ>(%*; zwt_ZX>52~bP1ZPv6WF7N7}*)R%WT%!ei`~o_Df^}&-zkh=wji}f&9VIG^GLmtTQv4 zngmk-0ROE0^94YaGjccK;AUiD<^g>%1OC|rhylKVpd@IRZ~u9~!otGA!GWIteE~fX z5D@J#gbqPVD zDJT@czrHC5C`c$+DCqxqtH1#esE{mBq-f|O(8@++PC=|iNf@F7O)%t`#=&gP$tuP3 z`}cQZsuT;RA%j?yt?Vu);y;>G{?9HU1mwTJ!N26yzgj{eH;g|3PIQoY!C~=5-G68S`3AvqN@`|ieNo( z1eV5L$~bTDX(mz#3BmUPz;75~R;1u{-V$blb{X3276ff*qx5m! z5-PB<=_pgJ3@I=_NwY_cVjNTnTgw#`1PuH|v?CHyN_BP&f_H)(-{OaBA7!+rP1Q|# z=)B$;p0e(eHW-GaTEmJ11D(J{VA2~bl8$iI6Xl-nZ!=KICY0p^LJ85&XrHU%U{z96 zOKDtbNdW31BC4|rtNGmYz>2bEHmj*kdsdBA!dT}`!SogUpSf*m1X`(uKP*rbBakf< zN@5BO{hd|0gLPdWUw?J|}^P7bade(pEW`8>x|IDwy5H>EJMl5r3xbY{;ANC(4BoBxTc3ZAn^^D|toSu<4AkY*mNZs(`@&>Rks* zl1lo4i;T5oW;Fah&es0P*GI}5U~kf~`+8tZnH5pXzPhf%$=R)cL5MVD8`i;+K{;6} zfg22|JT2jstAv*0LzUpqD(7PY3Y7z)M{*UIo~}zg0m_RnO_r$}=6$_E;Pdw%DfeKd zz|@S~pLbd}R2enw5qiDag}lxjh=uCWx@d!gGP*-Lx;P{umWc9I1CcCXtsG^IEE3^X zhPY*s>}-fenT#lA+AJO#Byu6pa!LcfILm34`{1TH>TA5r-_n`#)cch^`nW$-s3{>f zD5IEgFY%9cbqCPYTkEV<0#8r?MK{$oXE(vE*^O2Ti_OQhBDkKyC};4O435weY=6BVCuUr}7S+s95s|u4Gw$uFBJP z-E1KPu%zN)oHJ(L@mIw|)3kG7Q8Z*?kz=$wW(2fDPmT&u%(k@9k1m8v%{K`_RrOEg zga1!IPh~Qr82wfm6?QOk05nEPcuee|$gqCxJOv7MDKVxGKgszP_58?OvX1|8@}-}O z`}*~|gWj>`B`WQYA({dF;12)sLd}QetBMPH;I8{QrrM%#n9FG;c)|oLLB_-K-?+T% z;$<5r5|Ge`PDT^4i%uUAI!qe1Is!^HXZNp#ycwa8kCmHIhKM~QNj_PoNvTyfLo%fZ z>|L3;AXTJF5mW)5$J2kPC^rcUVy&ZU_T&p{KDeXE1pOF;-G!2%rO4LVqIUhX?Y4oP zPRzkHH1Mbrjg2eiWmZZ)n=si&UEjseDwf$1X{~BF6I!VXtDOSJ`6Er|5wA3g$y8jU zR9gICKr)9)5x>KV-Mr+WXm}`IL{4fqgMPXes88tQSc1;*gVTXk{YTx8DmRtCDv?QI zUi{B0ZM^AFXI^16Rq~UFw1$*g`~j6v#xmXT<7$7h4m@<}a(a|C4qn;Bpof`FgiB2D zSP?{`Yz@=<2ZIqypc~h4v@GppRdmHd@o>v(S)n>0LcYV&R}bUB)3zuc_yRq|dj?;k!MrCxbCNZxRorc{5_EXoKg%O4Crp zxr~WU8M>08)W0WG(Zy6$orm%oRMeQ|!P?;+Xsfp^B&SvkSD|U30Thm@JR#xWLTzUC z4diTgRH1^q7$rmeRfnGnOtPe>a|l<8!JoI zC;-`)8y_t$FV|EdeU8jCieCx(+wxVI4gn*zZ1!0B2cSegp;M{=4$fi z!;#67pP724*n7d%=uaT|VVKq_+DMqP9E<~Tv)=4uNWQpP}_CclNm}bAaw${k^Rn*U> zi@7#3vi>C;8}9a+AN(vUvk6mwh?DF+1OBS0t7|r+U51Zi-Xc5sQ5w=qBYG%jS|wbD zzmi6qc2}muUyfUE5__MFQFMLkhQQ~{Bj375@ys%s zvspTv1A9lpY{T)KCU!LPm29(HD1ozS;HY-;ahYN0ea~Tr1~4S1VJ`k#?X* zM@a;VI=hsza&^$E4nH_S`){~v_R6QF)kB@$wDO;#ubg%)5jDnO_%Avz)HZZEE`;B>N|)3&?!Jxn<3gt zHBcv)FG(xo=(uVm(-ZS(gadSX)`iI3UdoHJcg11~PlhOMx{_`bAA=ZWgD_x{)ZY%i zfDZvyl*nKk66h?;ypTLoJ2Yq04s3=RgdAtyx{k!uUk+)9TF&CI@zVs@sm^4Q>BS6? z_4)18?i8@)vDGo#lTZp*r!GC=C56}{&dO*>yU^YqVJ54#VIB#U7woCuc*lQjbMBI~ zN(vT^t0h`C1xX7|Mp#>l%vp82%KARcj4?p3EqFUFGrVXr&Z$8h@Yhidj%Tg}9JxfAD01S;p23dc=ssJCP*R91+sunmma1trI&GD`iZsJ*3`sWMYoQp5f0}mGwlZm$aTIotEEjbl#f^c#@HUj}4Is!GS|b)^ zBCyu!tV5H`{l+Ry@7Dth{>`BgJ7CC;Wk=Fd!^+a;zJ!PizSk7kR|D5=)HvC_)sMYw z2c0jn{8vl;uO6wY9GQrQKuGWTVGL%2PWFf1Wj)tPXo6;4`k{PretdCEo~ts+VZz^$ z{$!bhoick~ZcXbAxDb+FcyzFMWFwn{C4O}eICT}GQ|g`%_9Cqc$y()h2K*L^C|>p1 zu*>UmnQ%r|$zcNXi?~oLMm=AS8-2qn zw^Ut&pb6FBf>=J#nj=lD>U%kO7G;=%JWm&775w!oziAVaN+A)zRk3hMX)VK=be^J$ z*xAbMGm|K<51z0PUP?r1_SYM>#_D`6O6p4)U2niVwF+5_ZrXLNMj>e?hik#`n-&w& z`!nAgwfKz*&MG<>QdS;O79awb&=&?mt&3028;GL)BvV%_+)B!q=Rq+n!27m6X0N|S zF}2P5+{^rAt|%zxWg8~PdqExpZJ*!)k05Uo1EJLa$9uFu%?Q8PQr&$d3?^Ug6Z=ce zOsw$%F6;Ux9&@TDKLey%zj)=1bU`d+o>6jHhRF(X6KW2|=C=HBw(_PP&@#4MRzgj! zkv6`Q0^c6$k32`^JPj327p~u1y2;_@``UG6T%|^_kZ4@dZ5M9Dc*9Az)0VXpzMVW@ z;(#kv%qU&3%^pZi4T=3?w*=)t;K55SaTL2l@bGu$FpauU?Lkl{6UwN+yEmR~2x2>O z1Us(DWEKARO6+LZs$OgJ!~y&foF?HG*g6!W({UGwNTw7^+yom1ac^}w+@ zn2M?ph`5R#Tv;t?dR_p6ph!BDN*|1jGqCPpYoOdz6LOPy?bJGegS}Yh&dC`bJ9|q4 z4=>(Q5}TXkho2P?>fWH_5`0TWzgQ-cS))&B$8V#-#Nkv!H5wTXlQ5 zMRne^=lB?Br<Kw=ZzmEv+*Bu_gSucx*Hp+AG+!O zBQ3_~7oS(&3d<{_=Zo9)$fbYjl%41P0Z!B`Uzll=u|sl^<-qas&uSvFc~aF|YUMK% zTmO}D1YVlr5-bg=l-PS6v9BM;b16RC$|IeEM{4i6D=&3RW{?a@PwsQ-Z>HVuA2c-o z09wiZ15b1$5KID7yj|zK&bgt#TfSH`t5v%eh+KM$z1Pe!N^7~$$rTSH`V|F=$Hu%(zKw(8&2~od1UGC1?}2neh#hPajRgXD zb;ECdlO-M)%Jbh$M}cD3(p4 zkFTp1&2HfDJ@>JlW*_fUCmN7bKH61!_LNl|e8nM&Q_D~>|H7ww9BPjwFBD>&BHQ{s z3lC2UeC9cKXa-%&c^SQ5EICnjHLD&-EpPXHVzO=-7P+N zQa8+#AX=^q03fq37<_063SSfS*8uchLgG!0zvfs8rQsDjr5dF$e8%shODhUSomcD% zM7cwqs0`NZQ{~l5508U3;vbg%tbF)GBjGQx1K7COk{C0T?YxiMr_0B0okHzKsUPkv zQeYQ$=4f@5v2Y6e#vgBG_r$ySW;%<aJH)CI9FOFIu%n^iVsGMJRqLNgFGg%bm6cXK@+anqJ-{v0R6SU1j~@WFCimE?E} z8IOC5j?GlabQ4Pu4a?H(R?17t6`vhjg;e4wbit;g0w=hQ@J@ZCO}Mno5nkyO^St_O zFH~qrBfhLC-dpCh)dueD@Rm z&?=KV{`hy{KLEYqlWx4rZYXXym}DwE^-o@;8c@b(G8VLbu@q}eV1WcHYElz=ao)8< zMO+rjXT+RdWj>29naoeI-yHM&=fVuGjxL0j;`YvN!*XA5{IAWzNs#ZQCguMD2E(75 z$Pg#}vm9R;10)wW^z%75!C-~4mEZ+Ik_Ti>3f!p3^5lB7nbhEEvdKSyYxgMLgRJ4r z?(SEX-9L{WE@|c(l?{`O!G(siImCWt43JUhD)NRA=9`#y4q3goVRCgT))kr;QsOCg zKpE1x;I>$|0BPUJx?UL_tS{uQf=0&=9-Ul?Tj=EZv2-w2l=Qpt(#7s^{WOn>1}j$b zTwGKG(|jHFtb-Thzut2A=AR(v+0z)d{VSGX?xm4J5*9; zC_{cN9_OEFbo?z*Pk4X??Y6}pVh>?RaE{{#!41Hu=_Zgfq&YM>If-;$j{q5qN3GPVq;nw#fHi5$d1p6GHo6Ud z?4V7HPGU+MP!XNU?13Dx4N*3#;~$_SJo3SbP3yd*y>DLDN2VGC9X{kA@wYg?ni)(v zjkovYm~EGFR919y%J|)-mX+V+L6>=1K1s{4^w-`eu{y2E?hh8cv?EJ0206kK z1rLEv2T-drR@K9hD{HKKaE$|Z^c$C=D~q*Yk=kltk^<@55pCR&AR66~)V0IDAqyxf zWj9%AUQd!NJsK-&FCc8EjlUGre;Nrk=@#5bu9w`=0ZpiRWU-P8L}klYtch)GmeaH7 zWPh}mmlTpjRQB>})8>roJTEq&*V6pM1R+&n)GmFgxhD)VyOP!C^zDkUT z3rz$pZ1An^$54?{2j=Ma4xF?HaYSL0Cw0)9mJu%~(p%}cL&ysl-0UZJV|f*SPRR&K zc9py`m!v@5{H*p?6M5}7P46qEQ)4TK!U$XNg`auLX*wK^N}{AMgBpmsSXt|Ic&0yMP4pf98DpJjqtAZTJE^mbH!GXJk1B*gT&`^2 zwYRKaLEv>(nNEKqj7r!ckQIyNAFZ{-?O8vZOEJpbEzydxYo?R&w2T{V@jYyoJ3M}) zN-3cw_Li6>qm1rn9O_R)BHu@@UVOiyScIan%a(*Uq=nLiOE3LgWi6S;?^mHY*7C}4 zqtrQh{O(%fWu@JjUrCCp(18bKdf*KYIVg>)t{j6KarYn z@EC&@tJq-rU|*{kG}O&uDcfye9`|}|P8);AK!KaTIwvZ25J3X(zBV!mYat)HE29-y zaK>+Vh-2r>7fBe+(xF)Dh_S_pd7#bFxo+JJ+5)>`bs*2*)8&MfKTNO(*mPpb5cHfP0g?-5EL}+=D_SJ$S{tvx=8+(of z+4;XG<#NH|N^yV{X&p-n2ct^%%mQ+3j5+U8Ku}ipBxG@a zB(!{ND39HI$hOd#*00IFw1IdkA?Ne$7nP?($!pi#qxx>FZ3 zG;Ww<9ko97ortuWbC2q4_fPQf$#K8$_U)FoUFKsbpM$V;)EYLVB;dZW>(Qurcf?-l z4;25gkz)A5EI%l2G>tfX2aXp;UM>)LnGAs)nYN{OBIC8R{b{{Ib>YmtLiY z<~#KKO%%J46p=r;R7FlEcSfZ>Z$F$>M!DA3TOLi7!z2+XyPs5)TI6ZPe)DtQX_O;J z+qcMi1G%#}ZA{1W_ETwVWfgxFE?)TS%i2ZFEGxaaJWzL;`Y2PHS{t0gq$n5T^!y}D z2BOEXJ2L=5aF}U7w5jt+y&h5J6Uuhr7N?uvHUv4m0z6h;xDaGO6g2@>0kYqe;?gdC zTgSjjwu9pqOoSNTGJDOT(!ZshYo0Zm>tjU=(&-dsWn{{+^diOB4f$+{YRhgUy1&h` zv1HrX1#dB>meX!RvFysBX_5J5_PH3uia81|ik&*sFa=DrnLJ-k6(%lH7)3o75U!6o z6A|4c7@9IrQm_XEyzj8|bY%!D=`yfGEB!EA*(-1_NGwTI2u&?yg7Ax3Z#mXQno&d_ zLso1=Z>qN0_ORYt5k)FY**UOiEH{erV@hXlc&e-yK+l#|$odKyZA+t?xZA8`qNsmt zBCU3%rD-*X+DHV%;;aGr22leu#!}S#A{=y_qjdKe`G&lf*Gnp$er}SlRE9N~@p<$G z2$%Z-!(3H5%~hN~@^Xu0t`=It#Qa1&N6Ya=Ffl7yYg%OZzL1VlL|&S<(IEnM4yYTw z(;};pL(s;mg)o7lz3UZ=*GAZeaEc*-5EdV_(GbS($WZjlNsybhzP+Iru`F5OICmv} zyOF;363}*r3!hhM4#7WY&ix-C;d^D)mJvpAc8!{~l@bgV+#F?wCTvdn@o}-J1KU@1 zEI2h%v`Dj~JS9@{tQ#gjtL%bXHJx=&yF3D=czG1|4Ih~c7UaYyonZp+_l(8gZIVfVQP&yG&pGE)_kh8SU&y-LFar`IPQwj+VC zrU%Q3VV2V(`iPcjNW~@?7$0eA4Gn2j=N1m!QD<^d`7(}D&p zTE}L$^j1M=RV5+F26cN#_hholn)RaUd8Hs!XrAa={Dttwa3OV0Wfe#|2~zql`v*xI zcb8di+ayC3Ub_@G%Sc6RzCzJnm9j953m3o6aiFm=1Fs!Ey1`S;7`gaJ z3T?Lgsl=SI7X5X^rRh7bL|yn$0o{NCwsmgl)x2}d2)t~!Cgrazv3GWbj|#$5tCG-; zgQ_GDAqs{%<<>-Od#enUT(K85(7K&H&p|`V3H!(IYG(=Szs##n$7t2cQj;>?L^L=M z+BE5|C^}{gl5~PWjp%L$D!Y8FG%Wrqfhnz~YUdYRDJRnoO_G<0cIw+KM${X}KfePKd-+45r{UxEb>L<^P0!t#WklY3l?@A)2>V ze1xJ5ci;IL6rbCgf6JRsU!EPCoizd$vUE;s_T{Ow=SlaV9z_8q%@Xy;S>wAz^?q^0 zc;t-AkGLx6*|Qsu#iaMoq@&wsepgVna`UdfB~w=Po{A){$g_%ugR1yjEpLuQSky@T zOQSH7ZFE0DWuz5HHo1{bmIk93Khbx5S5Hj$r>Y(b()kFxEG*f0G>kQbW>Q$AuywqY zi>aKNg(DTEU{apk9>m5<*laScMm@gXpyH?WfeiPu-MfIf)jmgglYMDzo1Mp zwfWJ4^j*9<0@55Kq7)NniW1%`?ww@yIv=SXKs&pHC!D3Yj{-3`O=+y+Ezb<1!Lp`^ zijkCRwD|FPpgFN{w^W^r8^_qLkpZ8OJHqNp=DLnfoLW$(M^LdeqB3_jPmbOOL-ZSK zyRwQVb;qg9ljVIH2W&gniJrls@_~8gi^`{R&bH1$Z5)2QkSj?|hxv_{j=H$gYbUg2 zXNTb)y;a`Y@uc?k?$Z>I_e%>xTE9H{%z@C z2BnrDnv~aTj^)WZBkoj?`!ii{ZAeWIZ#(!y4W`bOgkyzi#q|}0MziSpGMA>)<)-;V zxMLfK!xwy>NV7($b|b{^jMd&_AU4!3yCf=-ll|&qr()R`%js=tO<2|+5zd54SZC@A zF?34$<%jtqIPqDUJCWZBCxujAvb&GVs`*gYmn&Ww@WE0sGnylsFQJ^M-cXmN>~Ck> zAN&(Qp4UVf=qrrV?l$16^Cd$bDeJclonu=f@d-I1Nk6X=}Xh z0k0Fb(<_5)zzLJpn9L&Xs@GkrcV&Z%PP0i-mqUjU(0H&HT)rj>)yY@EZ;E}3O*uazLJ%Qk|I~ktwx-nF-$n?%x4{FL0L8C=!=ZxRXcf7;Dd`u}n>{f9(?f&CAO^#AQx8z`9nM{i*uDWsqL7>osPh7|enBd}#6r zKzJR@V9|{`Nvqy6VFgQJoTbp61xAEGQZ#v71qdWj8&Lo!^kFC?_(3!~gj8WnfG`dw zBzhugAd{9s9cjvMg}uI-4j>LuzCN0msTJpdXC7@7Ms^LLT3cJ~_t!IMXbNFv<2+8` zot%{|@cJ!Y4T?Wi%kV2`Du2~Q{~9QBtbk_{E}k{FS)^w|XB*vzjn+(cFm}k%re$!% z9YfZlJ5Hz)3&uR$hmO#b!XoNh9H$FXGQNNd^t6Qz{(!+I*_>2s537_|<2#ZOX)+y; zTKs&8as_LjU}fQmD{HZgVtz_)Qd1rs#_F`cw%1p#m-cznX=RqJcs#vF3_eZJd$Gz< z#JZFY*zo}$1ilx|7)a8Okfju5a<$-2R(e=s+Q7E!)5wC;fp5@5jYtw>j>5qZ9QjZ# z3YK-Sc#<<^)PNw;49pD1)R~M0cpFipI@tY~GKhRUcu3A7Fm^_U_5%q%7|RO2ai?5M zT7(>485&}*TwBZ)LN+a`z0fJRq%LS8T9HrM82?Yt4&jX>v!p2(pWfR33axEIw2#cx zupqz-R6--tKFW9-gsEsgL*VnBcGI2RjcT>!<*<;lMg5keKXN}&e;-(6H(g!)YYVsW z+(c$v7gh89?;q6@`t0V6DCt7qr0*7Ai6!gwR)#H7I+)@i>~R?6OjepQB%8cFL-q%x zZ}oH!*Y^d4+cuGH^z`s&j&zj)`b&LBz>X!YygIKWHy<#16x7nNezpD~#i!UMFONc3 zS(u((N#(Ab8P0K8@0+EY9Rn(vX<3$E_w8ut`AKOO5;NQeuGFyzWYG7ry&gOnnck=b z@+r9Zu3B+v@}^v%3!J+qCXoYIxi_+oymlO#ZYMh{z)GR*V=0+~u@~=QG*0$`Uel3qmrPIh_YJ09p zRMp7blhgeS2RO^oh0y|kNC=SiN;|6t1Adb#J%Z_@boV1dN}oNUk7yUT#$H9w(Vt5~ z2OdOSg{V^{xZ9(3?76*k4Duc&-7v$hq{gm8&;sadI1?Oo45$~!xQsdh zNNYx_{UN4r1bpRns+0^`66xY|gmn!#D z($1pzT(Q7;@au56p=v4CN3)HF-)m+@Yk*zwWW`I`+hHP|HO1m$f#r%(%PDg381!~g z1kwczMMm${0(Y?AS11}G!-AJEGh>s&DE{-M#=<;^RsgIPEiltL@yd<8%0?Q1lT`6x zS42{a4!$E@^+YxBa*}D<4tfMO=WCaL0FBDGIxQj;5v4YnMv>Y4tl zFx(k*JD#PXoU9CDR3>knC}ba*X83`)Q0zkCuLzR-G929%yw5Mg=1%peh2cT23EUo! z4*t1gHA-I5$__KCyiuTxyvNv}&FG1Eo}}6yI7_Em3|O3P$Sxbj*4zwRmq#GWGm;6j z<@6TS#rTXKb5w;^rTg!RCER{eR}MI7Ux!*I2V4K^Z!5W|X~;VE`K|QPt#|ZVDqsM~ zxiE>xg9`1Bfq`747w>+15tqwILccb5wnpca1s06<#2_~n))8eZB*t~?rIEuXln=6N#u z{GP_p)Ml{p(DCT9%F-=WwxnbS%(?8fnjhgF+I9Un*20l&F;hP5PG_;+D4#TK@ASPE zLx7EnS@Yoz!E=*YWpBN_;nr#gWx*m9^2M&}EY+sCPJyqiEOd!18a6?Y;2`r>7ldK! zvLx?A{ zu`#s@W9d4K7k05ccobCAFjY&BtpkRAs1j~ix9Y=RFM3=u*qT=dOVmS*lm~^Y zaf6=3zCy0hu$!hWa+C)y?L^Q^m_nkIM-a^7Lxxy?OL6>zMh5(WK*p`l)WdE9g`VHHiKf{)gpPN(nhE-LxaXkmpLQv{MK=(+qU8+A$8kpUDJ z5S3=111X&-nfjvLH|Ur=?P}j54-U+)7^6}e5?)+rnGKXIp4-LR!$;3zu{E5vbO+4s zdTeA|WZ?h#G%stj;Fck7a(CkRyKCv*tiL?X2h%IkG`n^g1L6l?LPgCNtsmzw<R4ks;&Yzq zp@Ps1#%PPu^+Kq&`Jg^eoOOa#~(KeM!n0b7VL z1C3~c5o|;UZ*4?JT7o&ftCVXaRRN@I!qd-?rfeeK>IPYY zf^+~3#nWt7d%m$e9L*26R~ofX|5c$^fZvA{(PZ)F(geSN(xsM{SYWZtuYa2*dUWJtT#(C&RB1Q`A5v(X{US@-=gk;y3>BR7ss=Yg=4Q!L#8OaqA70OmUJlpc>qu z=bA7}50T-xa@u!a=uWfQjHss=^TN$ohEk`RZ*CN_p^_mbZu-bcXJxf*L160QpvydO zv-+q__t2VOT{|S=^1>UnOHPSgr(&EcNrFJp>0%a=I44hpKHAqPc_3U`kn$0UukCx4 zc=&|SWv!}QKJ{f>$U^~u8W=sCJ;_d zk&kvI;zKSCg^*849UQnp8bJz!MhEXGju9kD82Gguk50G{eg!r->ckj(<{lyfl#EEK zY(%uU2}v2+-_~9n3>zE2_s4nsBxAC90i*mOhUi7R^V6!WFcM zBLiD=Eik@RulOW?rR==SdUE@(7QJ59!agJ3od!DFUAJ)=B|SOOl{NaFNT5H{sE%aI zeSJ}6lq+5t)?H&i6(@~e$yADDw4DG}898NHO_F1?CFuo|*)&a`Wy*WhRv7_oKam~? zecLIDuf@~$*wik|5zw}p)dFI3s&d8fZL#`7g1)<;n-6Uc-%{K5+RS@Vbc^ESj2fAs z?(L2_O!f~T%@9?%Hgwd`&p`Ui``GG1j1WlTJp+~fLoTK|ofYHzqAlWj;n3ZXjDYZ(oN93F!&RXNP8dM&7hnURR8qbZP05G{@fW0WT*B976k5kCZNCGw3F z3g#PDUIYQDn6f@2mKZx=DcP$C{CXMoR2k8Nk_jWQ0OJL1`iq;$cs=TyEb3{$t5|wT z7ijp|^)l&P*KN+oUFY~}N-T!S2Ci*{$@j0@RuBATE4N+M7No~4C9(_F_G^!I<6*bF zSPF%b9@FW5mbe&D#0eqYpE=<$`?Zy1S+y(tx|w9JsP1?cyyIu>kdmfkB-P>@xRn$? zhI?E!GZ(5S`M^E_lW4NahVsYMD|GDOEB&C_HFg}r3?BB9x1ykrNPe@@& z5M@H{Huw(^ur{twZ1zq^lJ(`dJ1eh!vtF$wVBHmiq>zgF zk@4k`XxrAkBBrY=brn;9N?iWqXo%p#uv~jWjiq<$l@7|Yz_BHnK2AOEb2&uJkJ;^= zE;qI8)a7d&aOr?O&Cp{*=wxmCA7EKbOhZw-d?)_Hpih&o<#w5Ig7DN@DA@y#Isr^i zOir<`->L^B#ojN^wA{Z`Z0PwZd{0=5I%_g1l!nsH5=MdCPYL2&urze%2fGeEy?Doq z4>WKWrB3T7<~3qibvCmN-jKA%eKP3-$z{J5`Z#r4eKm4Orwr`ko6t@D+i)S!wMAPu!O_jA~}>94!&r@ z+%;~zbT)*G^Do1C%W{#UOk8%?DRNqn`}3A4^N}c3>*iW+6rFh6$~4Uiq3A!- zCitPfhMC#51J`!G2gqRw;->hWJ-i)`6DG=yX3q6*c8mJxSQp8Sz0I6BoI8vgjwqIw zN-w(3QBoQ1HA72H6R1f}ttim@dWK$p zD&i8#+%X&x7;+5OUDV7CEV-vHGXOJ-{p~q#pCx(N8P~d8fH|V#psgVB-a0avbK zt&r_SP!JNXCCQq?$l{3Vv-$uw6;hRyJVv{jv8Tr7Xlo}IVPX&jXCld;WHy69zrWdO z^Q|_?kDNoJhC&nLj`e*-`YD46cBy9ZZvltp_%1@yX1BXhZgI;5UG@Hsv3aB5|B zd3}as%Z&TP1Uy(H`vaFRmV{tRamkxM)`r{fbm0EF667II#6&n0ki&G7q8iV?i@ zN}6^){B(^Fc0KEQdhZ6h?;TM~Zi^ZLIqC=k5=_lNS%0Og40jOe!D04B)xeVn@DI z(Vu3-h>kc5(sz8n%47?+ak9_a*K!ijDg1TY$C>nAa@R=zfCIyDm)G>}KL8)wQ;zOt zUVE7hd3w(VTz!3a!p#WEv$jOp5$x^d%xXKXSX<|G=-knh1DI>Yz*!j*u`+JTFN0*p ztbBSl!p%7t2FH)ZuK{SS@_TSsK+~ZKh&id^!D%KN#n3o(uEjFmj}@lfpixylwJF<9 zS@}^xtox5x^KO@o)+lGQuW30A@uWze`s* zC3~*$c%N6|MNdDKlc&ExaW(|nEv_<8_;Fz<==c)58&n3hNH8^+Td=4_=`ZRnRx%kysl37Msiv>6hz8{d&{uqHG!xf?w?>PVNdDvmX%^=ledzwHX0R) z&{rny#u5SGX&@3*3Wv0POK}y>vo+En3_cDL{4I9y-o@i1xAy`A(F1*fUV zGVnzF5PX^joun$%W?QdCF-Auu_+IpoFr$C8Mbn9C;(?thq4tC%z4N7FDLR;q-eP~> zE)KW#3HNrO={3AyX07R?tHQhvBsDO9#ih807;68m)xc(4Q9ZYny1~_zISKzY(_(IS zpD1sLM3Fr`I@`U2q*r2-huDf@9wJuG>-dpmuf|>fvn2XgI#FTR0`Mv)7ujQZHPA_3 zR>eB+In8C}a6Dvve(QJ0mD-ehd)gW2C=)-bx#tF&AADO#t0tRm@TDL{lCJM)Gb)nE z#$TKZ8TNXfDfS~S!l0IMG)*=M!puqCN(;Dp6Ci;@!sER`GT9)u)%wwU90lWs1(R#s z!4t;gW}9`I?}6gbuIJj{>$ppb5xRBppVm0-6lEi4xM-5)2J$)bK;Lily6c|f5lEjP^NGu{@%jD~gSHO0$%N(T6IZk| z+SS0`9IPey!zcIU7~N@qr@6@G?^m9_zlCic36E8!5*6(YUGAjOZHJ}Oe{D=`OkJ;! zopF?5`f?c@o|sh@ZBOaYPAQqE`HpmK8YneA5JCOg}Ekg^kr7gL4Q znnWo$d2$6S(`0$8QU{EgioelO)>M&c21ACx=+9I`1B@}CMTmo1jM3=)l=aDw+iuZB zZ@mL&)VD^3E8B!QbTo=YMgbn0uI&X1fkw0ne%LVLtL#<)eVn3tq=bz437yM>-PZXp z5ecV=-Wgac8;B?FbSv<-lc>10Fh8XUCx&TlHt_I?67S9k*?2(ev6`(5cb4KC^yKRlE|Ybz{?h2KwZA(RL-32B`!WvOq>=SQR!}O zQ!;CO2U$M83L&GDba)P4l3+%jnZ)a>Gx5>w+1dd(g2%ax*I_7mHw151vp#3tT1I{k zwrvX?!K}unG*5M_hA<`-H7a*4Z?AFEh=Q|xS2J24Y>%|M>W}kkLox-Ni$5OEjF^xU z=V4<+`!>sKyS-DbtTF_z+fULT6CRZ8u=(pclbJ#2yk7_TA5l`d&`9u@y-XYk73bae zoJRG43`f<2pNCGnshPU^zbd(;x5-?#jv9XL5Q=b;1`40QB3EZr%jW`Iw*e}1@L{}NmV49snE5wn2Fw7YrJi!s45K7$RoT<4bNzM^-83|ZH9 z-9z&l&#d#Clao`n`$=J7MMCVrFDR4iOn)-jL=i%N#_Ej#^anh~8y0)LO=d!(0EcJB zVy4Nx28DL$3Af?&#Z(IEZg;`KsYTecIfxeUu+`e+H&M_C_rt9=W(}t<%y^P%1J`h& zkp!`J(L&u6qLFktF||ik6O%nvS+AV6k%|hF4wf|JVsN=97QjP15KB|Cr%xl4p-mK- zK^SxlSdM2Qg@v*5PS6LS(Sl^PBwPVYhv;Z3f8Ll7=}lDjU;%n1oA_ehqyj}nyqs+> zFlDbU&fJ7b@A;yMk_%?O^$L(8vX|6+O3mc=tPw0FG%ONRu z#zbr+(-{l)Cs>nj_2$=>x5Fgr%kw{hW>mhh#MKlYc%`?W2xP#RrnBSi-DC8jyAYSF zX4Lm&!FA?TA}}W!Dw+=z)Prcg(FZlp`iG+ksTecDf~SX1M;pTL>alb9ZC(a5;?W7| zTHJU*ihzc7;bh%%QnEl*f#pRH*DeyhZkW@}-h%Iw6b#%*344~U#5AURF!jwj(oV0^ zil=3FG?%VdiJX&PVb|m{(@~Avsm;6`TZ9_J^DyA5j3elv0ZEy3Dl5e>nb3vN^UT65 zMI>C|srI=vUE_ld#N74)BH(R}6ta!fySQX%(-i5aDqsz%MxPyxsM^N1x+bS4u{V2w zf>fG<;ZTYIXm5Cc@9nAEVqe!r+wd42pR^$=c~og<~T| z^ItYD`-{Z|k0kJ1GzEb(vR5ytoFX#XDt~P{AU!!^WK%Hqr&I2_PPbGx6_#}9w?@cQ zZrLC0P*aa2lgjqj3AVTN)HT1$Y4EecLcM9eL3a6V3hBEDUTC3~v=z_Rd=3Ca*S&w~ zyG*t%(NQ*hPfq7o<3CieHsd@I;DIS^JEQw`a^aEKFlzmz;Z6|1!8-80I8g6h4E3ge z*0v3lAsNnb;Wn)JY{-4DgZ1D%G=sl~Gc&dH`ez!_dsqzQQ}PELsln{Dp56##YVQ*H zEI%G=u!zVsrWJE3l`;Q2fOFPD7QPlWn)kD!<*!hi%#$<4FQqkl{eJ*RLA6Dd?}lA4 z6EQojTabFal}r@dHdWwV<<(1`ZG#a1_DiU##6{H1nz8Nd$dbaVM_rNdwc+mhDan(# zRH00Jq+Mh5x@);tfTNwL<7=kI&;9lmW9?1NYD{#_$@&$!MCc5IVv{ixnDyj11~- zmWa$x;z2sl*vl!%DLbJsl`x1;Y5+|Gv8aYn{iSUd(d8*K&ufXj+TNiT_Ns47bPpRD1gcK18{X`HH5wYL#vEVT8k7FiV^XH^b}^; zQEEnH1-jI%t4w~&0sQS_>famJ3~7B#_DtR7ALA7hhb>1n<( zcZO28TavTvJEiw3?*%oh5@jZmT>PnUOunUJ;;--6Hhjk$^(=R4)@o^OhC!11MM@&> z)H%z|Q`AtQ0o@rejb-PA^CK9P_>(}DsQXgV)~B~~X_9liNj?7O1_kbu!C~W;`4nko zv}Y5wJkwR*L}g2iR^c!DNPk?MC)YE##d+rxLQdZlc^}1#X2rX$##Is)W%kAgbmW+! zmXUGv%*hX&mh0vRS%%ittor47T>58=spHCS)n(ky;DYZ+)L{p`_2PTX^Y+kioKN_H zEo2_a9h+k*GZHXj5*$GDE(|Ebbu_U|aH&D>;PUOH*akfhy>64aK+n!I2ORqi9H~?p zL(Jw$6KuW4rmB`H$;H&jZ^gou!vu#(u3H%eE#?-gm+POY+(+dc+}&R=&!daLwRBuV z@oS{&DSTc&;kBNXh9K0}j6|2f$3zIej#^ilT>aY+D|-v3+n~9XdXi z*PN2~1bV_C>$m*&Qy!-1?;w3$zec1VT1>^xM_e*t9=WAOhYUnKy$od#hVJOVjfVjj zbc2F$qbMmj&e6VKc7z!e*+Y;Sv{e>>r~J9nNSVGj+RHla7Ll=ph?oqx3od=`#PV#HZ6Uv;*ZD|WA z`@D^Vs-V;us^mz9YqIKZp+!d1V1(#eT=+F%LFx_C(nLby{?qxNrQb86^yrI!kKVw= zZGEvK5p9rt1W?#ffcq!CvA^SSCfLs`e9EoCy~qf2R$Y#Lbi}QtYUCE?lSKJ&Y7>74 zu1g)MH=kRnC2fV-bj%l*9`xHeE{q0v%NBe+cEu5}m5?9LGZXs(Zt&Ad`v=JIzX>Qf zT(3QOZnn!0b$yA<@(C<$ZWPEgV@4W{^}~ctXm-peUDs6Hw8DzM$p zA^ED4Z}TxJr6ATJ1(48M(Z4_i(AN^4^1KJO??jou1o@+B}$HA@Q-eI;To}N(*IUYmYzqo*L@JQDkY^TrdIr^4}a3@c%Ri zRrLR#gMxv91DDJF4+r(1jykZADaeW37!xaBG~qjiO6_d#&ueTrHZjV?0_Wh}Ivmyc z{t&7@lZO9kr-O!s0<%{Cw~I*`8bbukQDK5Xs)X;g*S!?8Y*;E{|9c3kIEAA;)c}HX zpbrf5AmL|0GJfGG&!dOn$tSbc)is@2JcKr)sUYrU*XM?2M3_X-YDNGmS_=1M%mgBH zj`WDaBthbpT_Xf3Nn$`I0YoEc%5czOWcxIjz@IwhS~6O3XhGld=%OJzs;Z&kD@lI{ zE7$ah?qsaw`~wt&JRJAb+Q0}@@iPHB4k?D`&(69_nMtNqYo_z)aWV#Xd!1?%SR9^9 z7N#0|4g6dIR~bGSdm;*|ekslYtxx=G)hn?`18~adg>^(&G(}mCgQuT?u>TMPUZat$ zVnZ20oF-dk3|i8~KK`)W9+5Aek8Erc`}W-~hv+c-V&LofT?2+Nj!VW}e&a|V!qmt< zJdZ7Sz{b(=)ZAcq#~x5#40?x<4XC_dzMfUu7^tKET>-y_oew*cH4|2FlBZ3|l+P0c zCF&z;goY4Eh7d%GQVxVCs4!8IUJ6|*ZX{5F$fK|Z#FPj20F}fsFlR`s$i!2A6$eL< zX`n%b86#5XAuMu$EdY-^?lm%-$P>`5yw8@%6 z6Yo8-#jU;?ONcwTt_j7&jsPE@@hg`7z)(>3h8z8N#bQGfU+x?k+9#6608}?OEG=J_ zfu8kXs5MD~hXFSo*ZFSy#EMwFt(VZe&J`z1JOMTvEsnn;bAy)W?ZmS`mZlYv45@!g zJ#WmvaaWrha_Hs*pUAcD9dIbfrkcs`Pn$ky z0&HN~e9d}%sh_hqog0}YC^9LEb^6GfU}_vnv+3myKkZUCln{UsPrj>%PRQgo=g*oS zEV4#?3tgC<5^>~0aS`d#3jYz8vJ5xnWBlku@%zWZK)gSVYUe7{V3kt~9Ftn*z}ba^ zBu05f#hw_7^K0cy<&hU z?DLsCBq|N=zc9Dl95IUn_qS@TASV#}!i?myF}RYBqRgnV9d!0c#(=LeN&tEy*d(wL zjAUL<^d}-z%wCqMbr4&)!P({PoDBQ`4?=V~Zcph;rYX3s5|{nND*ir7PZmIbEyCey z0>-tyCLcs92ypDV?q>d~bh%P{UmbsQB}PnB7Xm+5oWrlDLu2EY@i*duIdA%XG>#~* zkkRP*)Uf>0>|0js;vHf|-?S|46oV8V&Z}Kek(LKGQKG#w6h*ng5PKkdt9z$lwKLi8 z0y-waFc~+&a?bTvZxW03);H@)pPwlg!BT!)%qU*RvkWEyW~o&h<4t0Y=3D>*V;eT$MJwZsiz7Uu5)O@X)l7`)zK8H{lO{r%| zC~Hm$&M4*w_Yk3$HieVU;iK5x$@WgpA2-USj|bk7mvM9I>GD{H{T~VZ?yrho4nBd1 zUs!!m{{a+#oYIe3ZBTt{pUvQ5A-rJfb8J^^Kqh}-$>?v*wKk!Lo_r7tUb*2Cib&(y zOgyeIk7~9Is8|x186ko`f_GIhb^2xF;Gr9xl6C%Hw4GCMq;L49J5DCH zHL-0>Cbpf9F|mz_t%+^hww+9D+cswV_pd$Ks;%0iJ?^SL`ugqfeV*&SFA-rPHsC)1 zbIXPQy)Jw*<;(hfBE#RMy;jI!NSFXCOu%q4Bm-=}DrpoR87yu*J1jFgICKFl7>Hw$ zm9wPnH5ccQflc=#_{N;PsYFhT>#QLI3!>}V>53|#0ECMH{+cwf%u1V-C)MeIMYNFf z!Cd(EJ&(&6!O8a*Yn>GQjgN;{#wimgL?y<= z?j!dKQ<3A+x`K)VAzN0^9(T4A#fZxL4X}6Z)WZ7ZYEST~kin-tUsBh#ef%IJHff&M zkSL|PWa+6}EYCRKK0V!y82}mA+16{a%v0a!?g#_N31G}v+5CwrIMg&a99ud@DN@S* z_H3ljw8phoN(4^;@@s179jV*a^2|>^BXBW5Hsyl(cvfpY_O)=?HSl6VB~_=XdgY6+ zw)I+>Y(B&7MgR~o@oPPmw5o?l!&AJPNWk}82Kt=kLH+bk(5&@oLrYGuUQ);wP&I&C zt4eDpeTWS=-nFHl2pl**R9LH*tr3E8>;bdkYgvq*RoaHFyxKUYk&;dWl$^KN_|P1g z&{Rk2S*$@WRiLA!FP6NeIK`~u{84Z~rA0#d`WoN26zJl^K8487A~{A3vvYq`+t!j& zR`#ToB|S2{Qfw+)h2_dQL=cVYp3Pj_w$ti&&iRHpKQCBK-MsnK8!4Hpf|UBbbUivSyd5(NndJ=p7?Kjshi6Vi}ciyb3~r7X9w ziJ8m*PnskdHNX#VE;W8*UJ;BA7IX8v2z7w&EOdqe7F|0k8B)UU)uOnYYVFc-a6Ek8u551U!kWv>dsgID8Ik9{Ca=4%dK# z2F9=ka!}Ni;C`@VLYN>oaTLZMrGP&O?aa#6Q5b-w0w{6}gF@044d+Tpe<1|WUPD(K zU_dtp0-?ZkwLydm0AVgHRrl^%3w4mg>v1ysu0%E5=_cgk^PE@ffnn}T>H^%%CsUH2 zgSf`XpFU+u(qk`Ajew*T5v2*zqXE!J<5CAKY?60b`?j`57h1f;#|iS!aSonMC$khS zoT$67U)s;g2SsU9kUP_|8(AptgvI9z%k^*Mg)V-d!0k>D!xwHz#}cU<&>%~nxKbJ> z*s)*zD@ng^Y#3(deAc=-UiWv@BGA68WZ30q1DxJ{1Am(={Vj*@vgE6;3cL?Q>eY{m zDdBH^2_23oUm82E86_p&s%p&7!-!#2epP6*2&Qhtg`vrc24)i^cV_?i%_?=)bi>WF z5s5K>`SwzM%q3}-t^lD=1!QpQcI`^8a9so4;rDI6KB8gtDfa{q{ka5^TjoR3YTJs3 zf;2f-TL1|C?j|7_nT3qe*^vN7oH0hHn1aD&`xY>xcCt|9O)92n1cHVpOE~WiUd!l+ z@?Fh*At8+Iv_2e@AWl zwlWNm8Sdx1Oc^EU)mo6Ju!)>79?HIulLONG&$M;i+5GW>Ho~t^Kw%72wIP7s{m5D~Yb&U#DB_2>QDxf>P`IzA^Zrt_bH4Bh zv|m5d>Z^Xt^?k9Q+`eb3RQ((L>dT5?{ztaYHas0hZFDF6O4GH@Eo=7q!$9ywmEzlVel(EI-tj!-IhL^#;`ON zjwFha;J3_C;^Y7gq__CyLNZ69dJ=^7n4p0A7^<~ zZ0gQncTJ_#O@pO%V>XDJJxY(^=K}m;$@KUTL2Nr;I@f;vd9x9YHiSdla9=KP(?Q3& zGAlZsi{p}C+s^p!EzD^3b&{q@dCUjmGN(`4a)^2hZ?-{j044k%wIhfwML_00ppL3#wNFs^;D6$!f~{^B1XgMVGAWwtrejscm8 zQfp(WtBT&j-WcyZV8?}#3|=mz_~}Jf=i;((BQIHsJh^pHmn@vTEtWi9K|O;pMvWdN z2?%_D@lBYS?s}!+b$-;%`Ue=qh$%nv*yIkbuA;)Y3XYC$Km<#>ut{BIdQ1)p1rl(z zu_BG=2x-5j;z4_Ee906ZOl8Y8(?%R`G^>lQG%n8ahrR^@!?Ei2<{DcBmTmiA&c1ua zsQ2#P-Naq%D)qPX9-Qfq1x~-ck>W?pizwJ7&0WkOAw}N~%ShTYqP8Nq(vxpAzt|*Q zRY!&4(^v)CUO}&(?si=o;PGJys&5!24QH}UT4|YuTDjlVorlWaf1^Cq<~)1N_sQ_z zHj`wsDzG11#@Ywp$BeFQdB!j*@+m#iJUFD*@VXHUp3t#=uWWA(GeTra0SdpewBW#R zu#t&JdotzC&x^?5#85)BJ5Od;^CTR(=<}@eR5PU5hQwpF;Mmd-8tx^%b*_)T=p2Ag zR2=jEr?C@z!#q4bax}{*ZBAa!7m(Lah~Zw3m%CZap@W-}#4WDFM;TDZ#5_<`>{W|a z3;=axNHy4R!;k@ZZ=H4mrSu9Cotkd>(i>-M>#|{ zJl^c`6L-x2YB?NcHV7ucDf8XzCL`MDV>fx?+nOZK2>xBlAawX`KK$G`RIplefo|uy zM}8DBo^z)4d4}Vhq-;+lt-<(piMXZy@P#iJiS;hW@tF+M8B=JrSzS)|IxQKQjvG`9 z(#tobdvfzBCXFLTCqPSsb!!l4sI}cKEq8DW<|8*Y6DxD!Z0S5p*%}vJ+hS=O_Gj2w z_mDgB_;e>x*iOFD2l!M3~y_rHFi zi@x|sAfrPUO#ZfdjQIx;Q_%TIM`;X%mt26zPS#K7%2q1P-JrBaWGqJNuojP|I&HeL zS+klk2E9xL ze@AJ5P4KldBqOV&-DCdN4?KDod8U*l&l{AH2zkX>M^7YZlb6(Xu!(OaUolSoA3M85g|-PgR=!Ao4rkuz-#r{XzGKVSKA9&&~3P zs$n?T@O?$%0!bc2_gkdx^rN!wfwXdDO}zaa@QJU;y0Qq~7jdStHbS_%3&EXsmUHNI zg`$QGhRW~~U8TtRNO1@rOWo#t-c&8JK*F*|7S53vH-_u_-s4I7ilRz$98(Mb&)W`k z7Xj@%zV!AosS+~;c~_5rfa%D%qr216+I7Vb8%{i)mWkS=WK*1d3-jn#H?BAy6)&51 zcG;rgXW@EI?QNT4!=C6|4iHh5%nTx`q%s>+*5U_;lc2?JMgK#mDmmPQs{kz8n(E(( z{Dy;siUKMzT2RhG83cMsFuxjI0BG3@&=4g^ad8tuYR|ujzbbof4x{CZLH8(bF6Q%- z9`$fbS4VTt9(QYRU2|ADu(c;?@O*6I z?qibIv_MCuUGS*06)`a5z}%XH3I`9|>8kx){#^*VfvXioLzO30#+PnqhWH0*210rJ zvW3KZZnjA_x9cm!W>5#?&|P}^iD{WBeY9ZVav$lN?X2U#@NvLJTZ?NzHKznz+vk`* z?NJ(VM%;YNDnCUAUPm6jCh4xs#7c@oqmO`KCS7s3dd_v(Y|8EOE8#kr$#o-S6R=O_ zXQhca_4Y^k>B}gl!PfZ^+q&@!n@@;v7mG~9X@S10o9gOOzPc2$uKJzQzICrXv*zLY zL4q|}U$;6-P~y@{w1IzF?H@p`w=MbyOd8~`zxLWXq6{f^Q6~=I=h#|XTy4)tl&e_` zrj+7aOtP95%k0^zl}@hZ2IQjxtuDl(v9TIpv+bI$6(dM#c11tX`QvR7x7}YDoXJNN z%{OH7jQuz}6p-C}T;{v%EaFmXZEkh2_q|9f3!QiG#cyo+WrxBx^Op&qr1Qz)ALgH@ ztn5tJU)V|)(}Gjm{O?fnf+7QF2O0vl;;zG+k?bMiwN^J?Sa*fqTS(Fv_s=iSNqYm1327-K%vZlaC%V4&T1J1DQF^E zb_C@r8OuPH{)+z)3ws{_%6)yZTnYG`jk>pV-7Qk>5bR~ulwJg3$7`7qjBK-M@ zIsd@P$vH7ZO?RGI(Kfdry5<>Dx9!{t-Yp>0D#FmnAZV4lUNMPem3omP=-wjV%r*Sr zU~}e^uN!NZ>iaX`6980Ty{lBsyH`P3x76zWl1kjzWg|gk>`uE?lp|?#lG4#- zKHaa5T89{meyx9`U2mIgL03Q#PC?(ybDHt^kuK-&vP8^cM_(8@XL#FD0yP`+Z8-_o ziD1B!axCMSWnQUeJ}le};9FEOc`NwSa6mx(9bmlQ%Mv;M6MREKj=Qp|K6r7?Grl86 zB6_L%Sm&bd8?6CLNB`Xx_RG)OOWoTRjKYr!lUQ0G0fi#qRy7c6bV+@N5Q;MKZw`9s z@b06)h|Vixb$V8X$z0~;Q{y#3_s*IOxO z6Km5F-OXF3w;B`{xy3Br_|lzW~zwMtSak55Vxh#^6Wnwv*?fEX_=rngHoRiE+@H|RY4{h zZpmJox+&~`rQIa`mpSE6G^#GRA>>a0^&c=gVeINZA>h@gZeq^8%$l>vA2~RY}nVSreN!e5(m_RL8 zz5+#J0wZ1uSa~AF&|qnIaJ_s0cGqsDapsjDn$HBUYzKuG`@EK(2GH3KkO^Wi#E1efgyl*X-YJ|bHSlSS%f?2(;#2qSZVSKRP@g2d?y_3x?|cxV0} zqW>XZx?sdPr!Ifb=DjjcdFPFcGkZNdj%yG)JS$=s0_x#1d#}~G8h(~KGorB`?d-C= z6>OB}tZvvMj*apSa=$XE+ag+|_VTf#+yjP{Tk;Ck(8Y^ri$X5l05M!Uu0A2ecKPp922ESYD}a^#xwEWM_>C=pzDkliy>&S+H@EPYTe2+ZcNHw z#btH+Wv^nfT+}PSEixW6t)dG?3zg(0TCMY2vwF#Xj?*mUd5LNIaT@UR^1q3CZ1%?Z%*UF+PaO{@jcXE|AsO23V~fmx=jWE&kP#Z@ zz?M!)IUf?)cgeTvDSl-XP-Zg+@J^cxlE`&mE;(*GSIi&Ga+D{`eZkJDNlP($VDB*9 z30Y3#BcgBD%^*%}r)*-kN4)(K^J3Y|6HQDpQ!Y7!^KdOsKQENjn+YnaFEpxC5h+mT z!YW{62mZ2eKvXiP)!XWSHg>-d(S{I1NbwiOPDCJyIf0H`&6PB|D>BDDa1+MNRRuw& z-{!s{Dp$~rG3PJleftYlqpSQl2Y%H~q#Ych!XG$*3JH-AE3%n>EWB1w`aomf{OsDp z&fT1@lM?N*JXSS{-*8@pFtlstM?dN;Wf!)`uNneb00sL{=sdQ*kc3;GhMq{>pMrA| zLE30O3wR6bdmp&fRk){1>+!#Pn2KI8jRzxe<=uDhXV`osCTWvKw+x$+fC~J$EK8R@ z;|3E9D~_=H{v5D%4ND9PXtxcyVlL=>5K|$G9ih!@Z)o8&Ac(Wdm#f+!V?$s&1_nQk ze;KmU5aL&_H!)M}f#K^0758aKv4EG;5wS|-U0SXduX47}oZ6zsu6iK4jV;m6*;t6S zQH{yPQ0Yen(sTDA-sai5{0@ zkvGc#H^!i5Q@!vGnU_nelfy8qt9*~UVhipotxmmq*Ynq0_NAB2-wpPsO^1#l!QLgK z^>R9pa_x3{)IPhLlOOePmkY#k*|wAO7{_=zM(r){yi*sIltsrziICe`$>_~-SU`sA zePW)k^ojY^0D>k(H-&q;!?ZJ@OF;tUV*3-TAFrnsYy;zG}~^$E5U~8 z>Aw18*Y>>>R{G?4V2!0A{|C5J+E$;pi3=b)LQeY9sy3>ZtE%Rv_fp5OuB`Blp6C5! z^xc)UU3u}=%Xrv3>bTRCx703u$k-160bF5E?Q9R?3Tb0_(Pu~?p4nu{*WBQfk*MD@;n+L!} z*I~m&kr3)Qx4ZRAgol0>56RnbV6>@8p`N_dA!h)QTPw7~u$n;!l@5?EA_aqN6<;Q|n++wwE-8>o7P%e?PnQG zK^MJe7K7DMLbBwK*&2DWe*j?0Rn7$0xyNAR$}R*w_`!NeC&K|2hXVWC8kj-bE9ctI zzWbK-1*1JqpziE}&wm=d_4-9_-8K3xDS-*i+mU*9=v{*Ll#kiwqQVzlr-O#|W=<*u z@u!_eOpr9b3;UGZuQy}{t01_1fq|swR|EXjM9mfhHw}ksJ@{C}}lxOR9;~j6w*+gzI zu>$H7`kfjJ@{Lu+{-?hR^flm+(Eq1H57b}fpp*w9x8l(2gm3z0S)p#ply{B(pWxQ8 z`j&L1mmP(u_cO{kX_661Cp9V@8q}^(L_Q8IV4PG{QY;7@t2as01yxbFG{68=EKgPJ z=eJsvGqSK;VOX$TSaRxEXe>|m$3lNFWlH8aaxsIEc!uv>lAPba4YBNrg5#c2x(k_z zen%OKH5S^d4F=k7oh9Elu9wWwSpsthC9uH4uMT~Yy1qNfq=*;5$pnpeNPgEhdzkVh zp$%S7(<4<-Wc+R1VGfb2zHY)FGTHonh!=A~Ke8;ctz!j8d*kg&Xq)%yt?W?>|(2=eVLP0n{Pjpw1~U z1g;RJMj_G#As%vJdOuQ8@NgxBd_+-dKjv{#NSr7VuppN(Qb=JOaWqInz(MLBIf-8y zYLM1!)nGwS2~<}C5*I=_jPj(|`XNia?YDQ#f$(W5H*YWR{!^P0 z4pTIH0^JN8c@41YPS2NkpZH~Hjrse<+1-)2drmboTmH&UU$Z`IA;c>XElSTWhb&PH zpPd8FDHqH2mgddi<9`o+_Z-${g(SYrxv^d}qg!~}XR6`Z{SUBeF>$vZD8D>mwSM80 zxVEW|z|E>QYo?S`|1kVJVU0(P&k-iN%ASdfMz`WVsy~B+8N0_a8F_A5y^adRjqQ1= z?M5!!30qO1SIq` z>_f1!6hNTe))0e}4`s6=0B+;fjECV5ZK<2-y=&$?@6#Tm-FD0D_tfh~FAk7y0+s|d ztUgFMnyt^;kGQO@BY2KU#+3KXN_yIZd1Es%A5{Fu3lx_ zN-Y~dDA}ytVA%W^ZCZaGIyk{Z%Q-g`AZ!p;%ihbKZ9*vYa;tKvi`beaV9B<=x|JW- z7O!`@rl8CGI42_(Y%>Hh6^>lqrkgM;;$^S zP~}3`7;aYHA?*kA@U+>#nh&XL(lQhtzminXBiX89Z2b8b30{~iQ`hg3SG#124O^|3 zNkudvS_RDp*38IeAH3I!K*|i@9Sn?~2rw6!929Qj=_DhvBHOYX#=l!uyS+W^qBN}D* zX`ix>31lah*5glK?RaHh>(oyf*=YHUervL!_}7{24I(Xe*G5UK?hTaxF{7ZN%wHx; z#wef>yInb0(Tv+d>?W&(3%1TG;L}}sO8e<%G&yKuI9j){J$NOHo5ADHjxNHEE`$Pu zp0m9CB<+oGqOEiuKAt+YE;km44CxHK~NK!Y~;jjjjg6Bo#D~DJVZv1fjL&#EF1hJ#vg8;5^Mjj>In@hi*V3lSe%K`04?|i601)sWpW@L@2 z=uD7N6SOUxN9@&eJV_BsBt>Dvaq;-^%!VnRnM4mI2V<_YH71uzOIS7W-Nc*f@XL^h zHF7aKv#4a&2@hx&twO7Mq(yjn$yArcL8opw@I?Y+s^oNRbK&)z zL{z+ZONgx`quInMtCMk^=xTg#k(5GMqQ)eDphv$*UbF}`p&(9vdW_mW%xpvN5Fv{$ zj6>-MZlO|ML+kb`qs}JW>Dc=ZKs|*AjR}3rx2q2RtuDwPN4yyfLjo0xUxd&Q5+w*4 zeCPY+Hw0MbNHC~QLl{CMKm{ClZLN{96g2$m!s`+?8FbjUm9KJyab1sm=t+Dk(@Eu3nk$w~!u6`7F;ly1SreECv3)99Y?QL{ zVlRi{;&y0x{qg)mg9|DL3#PN=eOCWiN$|O{ra}&sF95#`N1oPH zK7CNA8&>?x47;cd9YpcX^dL{K@T7tLIdqo+OxtG=7aVP`uf2steU-I3m(FF0qV=7c z7=5i~@%@OH@%-IFfyLSTs?T&4KjIszU{e05uBXP0_DqKA`g&lcvl8GW_GRYca`)~= z#mz)Kk}kK=sECd-MoE9hzpL4Y4l9eE3ZP7PF?v})V_Hw&HlX)R>*)PALZwWCg8R&;FLfJ0)haC#8?l2B})kU3t{?mXEdV+ zHoBbP9&gZ>S3`MAK1ka3^LV=PC4-DGQU6VlrAV+O%aTClj#SOx%GpS8Mrr;SvJ$>V z>Mv+G8Q(+FEgKqv%)~^8{M^~{i0Q&R{|_+IZame>0}og-;H0&AsJ+!eZG~PwtWc<) zQ?$t0jc!Dvk0!GVuj7pJbyo|R?_RHI}RDFEgG>eOW3Lk7&|8)8Xu=zUeHzs$^z;WayE{r?&n21MGPfVM~ zEo_cli&KzDd;iW&F?HQ|%z>~SG3vA#X4CS{?+Rh-_ekWHP-f;lSpH*yv**wGWx6H- z#8B68EvV{-G2ae}G0D#6nY(lH(@{xV###jr2+ zTRPdQRKRpAPMeQH{j3AGm6jp&)OI=nZR&$>O+)3E`gO*O_Obcuta;sVvGvZNRC=pa zYf2-xmwd*wM{@-A33QWCtFtVo7OunHf(FX3RqGX2O_mLSrVF3@G@nwHH7;HzDH8H> zPsbL+(lhkVLtT&exM16S>?edKRNxLbZBISRf-%!S!2BWi2cG^%NK&jIueBQH!$V)) z-e}6DLutqSr)a>L)J1rYVi&K@!0<C#%>2FXv|~aGDDNTN{d0Rr z>S^sMxuRILEA(RN9Z3tl^M#3w=VPmr)ZMUHmc9eCDN(0%j~n$>vgk2e+*@Xq1gg)` zcFL!_$oAX~9(R_2+cxvylRb6U0&J7QbR8K%@NBx zFLkP$db}3Q=XCfO?{)PPd<#q>Q|y5dQ-Pu{xwYk~mZ$DrH+>gLW5$BUUF&=G?#uF- zjYsOismmAf&QNUy7(y2n&)nk|56RoTZT7B;B8U0m^SZSbK>sCHQi4gI> zw;^?6hp#qM9q>;5RsoinyzR7DWTv}hI8UA}OA@Ta(7&>v8da-rlW~weF z6h`Xp>CGJ-@|2H%sE)Jjt9b_jxASAJWA8$87v(u{2_=w#k}zkl{)7Iy*X9IK+N@w)6 z*=Y-Pi+}+JGGmi-$Nh+!!8qpYG8Z`o$$1UIl%%q|o$2C&lfIM?{!!uWM)5iZn<_72 zxRu|{UHVEQ(uvw0iW{p9zi6v5>!Ry&tzx9*FT?^h;WO5ujh5NIF~~s6rV&$E?HXMS zg_}5P*70rnH{X{G$v{%JU{iehCIku`rzaOCtX+Ocx_xMr-FFG^sA!KQI|XCo9rggH zqOBti-VedcD4(Ej{sC^15~1{2Z^1Vaj}=ZfaUUi*ING>?6+uR|n|0jBYc3~)+QTEK z8E?g9B`7OrEGEXgL;fJ@EOy$Np01!=KIcsA*vAJ|S7{%ZoXA7W8TBzhwl_Se*nUf#P9&YFzv5`WjwP zx{S!#!Abch&qa!KRT;%oMyqLOcGcLCB_=&>8Y8b9C@%Ieospw9;do#)G}1WHL+a4X z(Ym7>auA(pXSHTRkMUxmXgnAZ!58XrOMa!0wq>04V==W+mk*H=Q{KE~bm_Ui`)X@5 z^u(XGyz7@v2C3nnSQ1YKHmZv>WuK#Vkj zzXv?9Wy|O; zNiQG{&x^uuDyS4_4io7%L^a=M&HXmDo##4o8CKfYp1J}(Z;I|QLnToVfpFS|Hx^OJ z=dSIlJyYWR>4p8R(uAWsb7iGi?Ui{YmI{Ht1iok0uhGB!w&b!1%gooBWy`=P-LO7t~k$w!hdfTG--W6_eO!#Q8p;2+7GI;dowrRa82 z)l;Rjzqzn-G}7MXnO?l*#d_DzU_Ic8+kZ)YiwKM;Rock~yFl+Dve@5i<*!^RNTXmJ z#;*8HyGr}8b_Hg;s)8tKLMWKO9~~MpWpVw|&YzlGg8xMd+s;KKxOx2wm+twP`K$B+ zB9u@}jFFk8o{f`ZZD!uIsinuI$oP61y&%(5yE3og#udTN3tv=yEY=Lt%Z61jB^IA#P!+)6L&nHr(YON& z&qe;e!Wq%)nwNn=I#C#v{z||h@%E7iz+-Nk}QP!SF0t~tOu4>7?Mw#g)UaYqoJD2KF!R8J%d{S_1 zNPFgd(LT&x#<05GIEcEM6T+{hXEw#+ODC2vEZ^fKXTJ4=QRyGx9@lbQdhJtY zfhYB7eA+9H^XHv@=f?1?0Gm>Z%3uw6 zT)+m6ex$E+_Ydrvv*a$X%vJ9q_#o_=m8yqB#ep(4i(io{yM6NE(a+d>BV{0Jp^Blx zIh}Td8(2+62$2DfEKHykI((l|*o~~BMod6EQvF2p^(Nn6gT;Fx)YQf#Qnzw_{3^3A zt>|DS)pXW9$hyHwg{^_pBLS=r#4fHsO~NU+rxgFTcVKeR%m-Y#>bU+LAGi#3P@hw0 zYD@FNZgDcQD&wMZ)gU`)C>tLEzdg|294R^GLDoo~5yx7uh>D#12p*ZqWA!KiabXrt zWBNqO$*PAfL)9nsJRWYN(5fekJu$^1zEwi%gwOagK`Pt|N|J|5s0K^iY#72J*U@O) zej#XfSaLheDQE`!VLxhF_Gtp6Q38eoIzhirXt_OcKV%Qm+yV#yMW##m%k)fMz_e?6 z|8u%zHv?JG+#`X<1ykoKv#U)TTh7B(LPQTOGyy#7BT@qyvB|}z!Q5vf8Jx=LUe~h?9=CsYb^VnC>6Hu5smlhlG)}W4FSnaP3AHhDAe+lA8 zN>cYIEL@DxJIVbh6R=EYOA-+0t+Yx&hgQc&g=bL@JR?}M72?4f_AEPPh2re_nScZKSD!dk{#1L zUOI-}9BQ!s@SW@Zpu$U!0oDvj)m`;GOJFB3Y6t4_qq^tCVyn(8f4S0pib#eldkF?I zQEa{I7iv`WuhuD>D6^W#RAgXMh>hbvulxhxHtF&3UifMsIE1Ad%m!QKEyEHJNTKZ5_v;$Y0*%#cZ)!GxyHqb0R&RzH7xTc)MI zf{7KTa~2GLkhsS&LCyrZ46F<2b&|Dh+*S%W@3vQ~S-quz~2Z624mL!>o#}THJKjH(O#O-#bKJ5txoa=Wh z-sJla=T>$xL)y(PRz%9NkU#LHQJSPVft@*yn;%;3hq%yw0@$G^T$~ zhEj6Meqs4O?&am)FlTN))aR~CVA8^|n0qYYIks6q373rx=^h(z1gB(dF+qS)#JIz2 z_#c3~)kWHxH$vrTG&L}AC@p}Y_e85Q|Ld1iqjY;>e#ux;bU@Wu>bqhM2d=#O^$8QN zy$_o56Mqk-etM?PJfDK%TY7x_M504cuaFtBbxidZ`NPMthJwHS_k7Lo@JJaCCqII!XW17Edf|w5G!uFrCW;1u zI-g&TEBi{_*Eu5rxMqc?KZ<|s|4EG@Zif0&@3~L+l*!^E@|H<72D;$B+zAOujIS<$ zi!BXMp9~bPe+aOaUgAc7_DoJe;gmU=x#U^y#xnA6hnB*RFw#eay(}lNzK6#pViLqG zr;mBhEiP{i(iGuao=pIGvnj{M?l{HjuZJpK!3R!vH-44#5#O^Q7hT`>_*}oPD5xkK zx{V|$mjK3-xwU9+f~*`{OsbrzoQA;R6CV5<8OvfDe%e&oLv=i5M78JV46MvZEG&4r zfsEff4R+h}hwOBgj&v*f!Otsb{2vUQ_spVi9bYgdnKhR^sAJsCn|S-aGZy?Yja^#S zR$bmgW^cEj5_{Z{v5Cj!t#@xfTT@I>wD2dPZxdgYRA|ssC_pzM0i-CmF8D~IWoAqo zGpvjN=t3k(zDx3{Vt}yQ&p=qj!M=Kv(FA;OJ90ZL$uO8`6r8`FU(t@+Xk6rd=V^x?<(01qZw+ zs}O$~?7Ya&&#mICS2q~LAYm}Y@9kZD%l{Z_Ba${MPS2B(Sc_LS!1^}F}ul)c6ic)k&ICDr3YpOyQj zeW8~xHEF+ONkZK&kvl|sUs*% zb}7e8f}B*Q`0s+}prl|in z(y9x;3ww1NFshZc{y=@DsYL_p%7r90?Z!{n#lB>rG;-EoQEb@?_=Bjx3mxs1c-Dnitu#q!dcv<@Ymm#AQr%lYmvMKUb$Hj!82 zE5G8sa`;cC&#XZK4Z~ER85u?U_dm#!TXUR;EiJzfm1Xq*(*BtkNuVQUB2L++-MAk` z&ZN}X@l3c+FZ${jl~#YOk5hBCWjsPELR{=<$9W0k;);%+44t?NrX_alGLDIZbV)c^ zq(77litt@xhH{^>mDk&v0El9Y0WIz$@IZ@!EBPTN1?6z*tr@yzGZje`@Z1PZ ze>LF2d$zX5i=Jft*@P?G75C+r;lo|u!h{1dwXqq_t=QBaoku#Q zb6w%}CpekVx5kx~cihoT?L*Ft?4pGqhfP(|&Ci&aNw}(Cfu^>^pUFvnX`+vJtW%5A z^R5}HGqSdY@3Q40-|#I;8yKj@Ruw&q@Wzz=^le2-CjEH5iv|`|4geJpzQ68L4rG16v{NsHuAb7HZZ3Tm8kn91NBT3kkaN5{u2IaY3Y9Jfx}_>i)F?j?{)QgER! zZyFm%&cIdkV5m>jZbKn;R*_1_9Oqx_oTZt9#XFRn#PE*D7*;jD*Vz?@2zffRbWP-? zv%mTxP2)ft{Qkp*E$}XbNCv*Jx08g+e1Q2QWE5W4{eXT&;n(oC5(R*fOWA9OSZD#O zfRYuO;>TgPOw7_!D*$?L!nYLtGb5abp*q8+z%{8vykpVCNQMXd3i9%B39)=7hUAiy zSY#&RvjE-ZBHz-a*xT2zYEN`RcJ9=~iJ>#toFMA-NueH&R)XP*QclStyo#{CwDp>` zsk}AN=9=^2!}@Y+=p(l(&D_sqi#ma&*!;l)+>;jUI5aa21%5fj{%OYQp9}RSu=dbJ$h64780uW5Q6*(USyoW9$A z*|ocS?ce&W@2YyV7;s)!IdYJA;^r|JGHZV<^Y?W_x1xQ2VhOO8z{Zc@Qd8VUEL9?1 z7!&1mtS5Eg_9+X8xxs#Q4H-*pL{Uu@aF)4-nG+-@bV5Z`(2&XuS~foLe`Luf&eS_v z7Kkog(P>&@iC=IYiT}O!C88wUTjkFJD86~YILaCbVWf8jl{--%-67{^zCXkR;#O){ zAD>s_e|s-(;=Q>=dT>ZE1RlAK*{4RM`G!pT$*9>%eC`@6&kLbudJ(#76EMTqj5UxyNTOp$%H3toZ=^L0$HEHyaB8E%2Y;J5_6||=8SWPA zf3Zm_RXpe$6q6lV{{yR3n9#DdzP2g14ZWr&tWi0o&1t!O41M~{6)_@tS-)*)?OEEL zBy<`W3YjmBwXmGzCR=qK$b{zL5CPTp#Q3zS4rh-L-w=?yJZ(+9wJlZ8QrfKB$W3em zsn@)G;`UAWd6$>7-16g+iR2QPv9JN=48~GztAA5pAGakcBaxF&oZP;YlkN%*q~ZOB zS0h18Zu?^Ern<6a+WiVwvgvEKIkS4Aw3-Nu`s9-}9E+!`{cV#CnJ)2z>h3H){0^^11aWVJLofsQA(Ul0nNWi@}d zq!JK^A2KFFup$uh8o&c%F|@tFn3Kuw452n>y%-Ke4FSMKCbGW}_MZF4_7?KUK+f|8Bu!G`u+2}%QRL1&99)c|-%;{U`u$duGfHANSjsDcn%9c|-FPvIBr$gavA4#wV_vaJt z&r2>$taR$W8ouGpfnX_6h~HBFWrx9M$v`aa*Kwh+J4rsfhrOAo% zKd`yRY^i&ZmiN|EEui7U(d8N~-Y1fDIQ-?PQs0?lR6el8|LePo-P1&FPmSm?;N>xA z<20iDVud}m{rafF`4VHFm?&GK{Cw4+khoJOo8$${$8b{oq5e_7|4Cj+;dyG`z`Xe3 zJicDOsI|v{xINDLqtsjJP0WGE2zNJoKeske7UccH?{jI z>PSl*9S$)}6tIE; zX$r~KL|lIp4ZFhly3-(!O`=k%&p1;r%BZLaWY;^Ks8pyZB~# zgt~?RMO&kaGDt=KTNE5qj9C+$wxOzdk`yeUB3082RaKNG0I3L5OckYy1hOKI0T0)qe7fhB~jIu^ST*`k;%GAbIMju12}SdvAE8+%p17@qJR~ZCCJc4a*2cR&hys+dxBELwBVI;z|7_`<+0MGOY{K&M;R@yK z0O5KbVM3Vp_>bmsn<-;@f6IJr;;q;>ZZ3?K2(itI;u%?wqtS71$+w5BLv4HC7&SYn z2mrca^Z+W+Z)>gqvz|>yj%gvwW4)4793TMEDt9#>xp|4^@VCu0!KLJ0+o#D9s(0JZ z6*|is&@T+|9*kd0y5S>`#WYp?4rEU|?34nRMwR&Z4`{MjQN?H@B1i)Q(4crCI6$hf z0Y?%-2qi!avLaL%CSmG3SQM!`f-RvTNf4^xFiAiV+#pR12_)Pgg%L_C6>~I&7#3F& zg%k?vzVRrFiJ@>JKR*g8r{mcr3&MJ6V(0=l*tKBX;2}xJ)}|BGB2v}rg)!49ufjvF znSd@{bD!=cs)+Gup&!OjY%tB0xsmfjM$U)YWmnoz^iq>2DS z(9?UwPJ8h9ceb6-Q@<9UgO=~aOV?#WTTdD!Yu$^|nz_ zybiI}I`(**E?1Ik`p3OWp!aL#FxOL8Hk$9#8KXrh5l%}wh|t+|+JOjNqGVWw`0p4FB`KK4~mM6P4F|7d&ZZR9%J8#g_iuvmB$toA20b5 z0WE%Xj5aVDlL!cGd>dq3ux+2^nF%mO38IYc*9gnxXXK3S(B?R0CB$R|-&UNiK z(6Z+QYetqa!|_!1MrBaL;Rpi`lo7jyd(Z%tNtYl*=jBKC{^ny)&_WtLpdPW+dowA& z`Hq_`|7B?DD&NuE^~Y^pVF$|X!GME>b8>N))02IgB5cM9gCpRHx@|WPh-!%^VXMH8 z$)Lt4te}(Ym?y0&z-LpRHP(z{je5h=_xx-ivA}xvxZ#&Br{FdvyPpjvM*s4MyfUE{kHU}JWJ3N~&#+a$9DuISBN4A(TF$PP zvJ7Q@mpw5Z@8kMiCGyX3e9ztZo2*&1YO6ApKZnoUs}Bpde%meDS^J`k=iPqoVvd=r z`|!tK3sTVohZ;4XV}e1J3v1@{65L3ss%V356Xn4aGn(FZRVN#IP46JdX<;0ViQ_(r zJ_!MHMFY~qSqTY(Xu_FK`NM%s(G+xHb~a&lA@DbO?;gl&)i>PvmXcNFhpTnGZA7bi zW&lcLkBm!*T=5H-*#=is(e1EE;R+Ulh!6nBH538?ZXXQO2t$~j2K0j>9vp`NMiWP} z!Y4Eci3f)>2vK+-ydx{bf1pqCxMxgs`^O#!CxCK)hv85kOzs4Gx5WOVPv%9WDZY|Z zu%i8pmu@od@BBesiR$=I_*CtKy5B*9+@)Qd&`vUuaX&JLXcQu2LnipwfDNiDwEz)} zG|HSu%6P3A|1{;rm&UC``-rVb>v9Vp23J7ar}<$|(HU^xMTCe+h4D;p1PGj@%ZD+} zg^`t=mehw%X5&s5@6JVJx3y*N*T!#=u`<@DsJ>!bo)55 z7;IJ>)B#wK)J_Howbu979A@VMKJt@K*MX-s^_xsM*Ymc#%D*39IjlZj9D#d?;y?HJ zYJ_&vL}R&m(QGhUKieQ&-Dn+A2r3Rw zO{u`ePC6KK`Z-*J0UdX^ak8SN5-Xe)O~|c&xaXWFK<*8#N(c+M_;ElZsouNVaTNR? zNXOnEH+5`I@aD9X_*8{tvvQP_&RRv-$Cq&@fPU8|!lSSBT;J`ow87{s9UrSgrfU9O zHEkpsKb~%QYDBZ)P<$MFZ8k0Og82_b0j#Uq5wNy>ch^&GN6!Ft!IiQ#TIF!W*dI&y zx3^bArk3M2Q27NVjbNFgq!hYaK@`>7f9%6GY9R(UjcdgAPYk&*I}CKGepOH9hBkYB z9kCN=1)CWk0NC;9WgwR+R@(3_B z-a(-ndN4~Cdw$lDhTFhkkjLap#JaObnTR2ig25s))%UtJ5f#-}J%7ZxGvR7%r;k`A z_3gS;s5b>sy7VliMMe;%^ZgH{6G(fuw=9jyt+b5?@O3z$`r#@i##e&>rf`cbjz=BU zM-+gz?x#Pp5dza`d7J67YY(1v*jsN3H5WkfYBvlk#YRi_CUh&6BScCQGPE#~*&$G6 z*Sh5xmDE8>+tPMWyIcvqrb?rhW|a3G&2<_8r`i~ z(ZG}5$%zs{aZKH?wX^U^g=4{1*B~kg68sU{eWGDw@;(2x)6QE1G$-`bF@F7NCrxh~ zc-N=&B4yJ1&f}L<-plQ&`bTCv4DIT1e5L&~$XVjfeD!fU=TEoajwQcYp_nXgEXr*0 zEM^S42r9%p9?a$pPoa;(L$)nPGkmcx3AB4>m?r89u6c!#n4$51bkC#`3}sE0#jUx-&-QO!nvlt_C1_ z@V&0W0LHg53_b)LO9e`EX;~Qr$uM!STHEbKJI)?st)N$3X;@W{ye@-YS@BI^3t)jw zyA_u`9XEu&tU%vDAdxrzAw6czA6J!SO|RP8dGEWW@>NS3_DvwZMSEK1LgRle-9_WW zuH*QU20=+0#cu8KHl+lHZnk~Ae5iTz&*_S_IS$#Vp~TyYewi=Q$K91r^BG1N*F3Ml zM34NR84|ZcRy+-ts}MK%LG9TYO(wP(eofWTqd;#o-JM%$YB3%05e|L<1*}b1qNm)n z)RJH8KZd%h4zI=;{nvZ;9Cf`So5SfU|L$IR&Lkme|4;%Fq_i>7s%OQsc~UqUcwceP zh;nW?WyOhwZTb(5StASIJoea17n2!*5)_sI)D1e%9a&sX(0m4awzXgVKYDOJT)leZ z81DtMm2_M&hJUT|G1zY)WQu0oGAj^dPz8|KFIpdB!{AQC=N7QG{T(+nQYkUSQP3BE zLh}N3I?)hpjKBjBal}wVgm5HKq)mdsp*vYZK<)?(fjokeWgq~m$LO1QJpLh((S|8M zzUcKzVffUPd^u$wX)+n-cyUB2C04?6G@+Lq2`^N82E*NJZAwK zSJs!8A!Rf%%-Z~7e0WcSFJw4EVvZ`NB*Rfi2r_B0BRV>I z1C8FKdpfc)BOBHGv6XT0Hi_sntJy6GFX zhdyJ+dd8@wWIk<1<|83U)0_#xnLNoTGSmVzw+e={2ZFzUyNn|d4uJtfyA8ns{o!CS zGdtF!K*6B>B^ia8hewLhC}}QOYF25W9oAO;(Xuhj1e201+i#zEkW}1LauesG2)|?R ze?2zKFzeuvw2wX#qI#hX;FPBqUzF|nq-U~!kJcgK)T^EU7;BOCB_`uW>H$}2b`=!H z8~z8@y7sZxTbv}{?3B~RN%2X-YHXvGkBUWm^z#bFUa?Z58LmiqDvGD5t#`#h`S_^! zghk6BE(1Yx!GIK-uZ#NS3uqjy!ZmN|O5h3IK+&%dff(B7xSSm(hjg6SDc>%^CtVe1 zGYnSjdxBkdBs*CjyS(%s?CnM@y5^p{>&lW|DL=&BT-98UP;=Hj7O2wA6UUQ)KU2mv z>DoDAruLddMRdbhU3rFQY_Zr(fXjTMqV(rKc5h;@KQp#Q2E4W2n7`cMWAnyW91*}n z$X8)!vtISRI@HEcizV36^=RLT9`Cokx8au8v5{m2uunFVRo!%O8l?AVrga;s4iju2 za`~Gy$ti1eTjgK;&rVKy?vxi*AN4ps66T)r?k#-I0_99N=}*2hvQPPS#<7@6QXl-m zl-gPCeG;6YW4~iHmnU&;%$0O@abNtax?*~ZQ2c$rq4$=HY3_8THU96+m5M4*x{dE8 zcdi^u1*AZa5l%IIqvV!46Fqk;4sc$@EekEh6Yqb`;s7{@!-koSS$d<_KzL6teh(^R ziF$l2quTNCr10^JsCeF>45-kx5l3)T`I4g8rsn$!!h8OEoAoUVOV<5U*3HJ&KSo#N zqN~Y0Qc@W?o4sv;J1$<4!n{WotYg&Fc&lE`WY5;Jb5)0(#>}8>VYA(IxkPXRPv)(> zD9(pWcxVAU)xAhe?0N5eWv}E7_lygL8*>J1`2opLl_Z8cjz0SOeYf^%@|dj5WXfZgU(L(+_dZUOIDn{K3jVlu8Jb2~^YH5c;yhIAWv+ zV!g$1qoCSF1#~eGM^da0hJni&tO8WfR&n)=%D zc(c0gSY9vKNi9}%C_|Pz?gH(TCb4bm=uxtMEKDxvz|d6(Seu+9IgTzjmma?wGuW0? zT#>J2LVuR`+G?w)L}20@eot^E{-wgSXB`I;(ha?HMRhAI#i??FN^6TS?Rs* z9I^m*lpWgX!^a%x6rO({Sgiu!=TzcW-Hlqbm}u`xj3wF$m5VpMx>Z*-F2;2AGBcyg z$)mP2HNQ{^Z?Fy6^>s9YBZuCB6#;+BhZKSeYirpr8|0jy$< zRj)1&m{W;-**KvhyBvpEA&rLFw{_rCC$k}BjlE@%;LHCdp;PSG&~DlJ8M1Q|e)rBm zMDVCe(5yn+zJ+%EqBekl!WQy}=dxxeIsV$$S0hzV; z$}+VnA)Ka!5pEid_PsyTeP&~O*!FVAvJ<;CJo3>|McMcVOIGmvUKR+ovJ-6*=4M-V zNWAa~LxpI?ie(Gzz+(>D*BYD3;H>7hO(+obyJ)GZ1m2y$+%Bcvy?SPJ@UrDtM*qOM z9LcQ{HvEWJYVqW?r~Q({8`QvO;?W(U{s!_(QUOp6-PsC^6x2Mnvib+P^&{jWVSBrD#)V1SOe1|}mXKXXN?_Y6F zG5vGHpmpIR?JtksP(2dYj+O>6!v_p+wq!>x#Bal7b=eZU+To(dR>$b)c&6aIx2DTh zj;I8(HmG9@7@C1yIh%CsdJ6C6aaEtuu57U(|9HoT-y)niuYIFGcWO@vg~z_6HtJt- zBD^)9kpS0Q9u;mNB7u9lq>^EcVQEQacs|!?OHNbWskfNHvY=-2XiwGVci*&ZZ2d5V zHo9?aX%}M)dDx%T6{(x2`;J)VO2KhB+~YmWAOXV+h@=pL?L82V_ydx+hPE&WMkqm% zoWVVIN-+*5`Xp@gD0hS}H4*fyyl@puYE5YMw9KaJfQMZ3RGpKGL%2)U?(P5xmHmLkHot->j9Qq0vRz$^xt#KERtDCSZrlgw}WfaR9(U^YJ;s_i|>av+}Y8B zgJWeo!2S$!7?apK#0a)N(A==Mkl29g;8cT?d;6*Rt@%TX^~TNz-1BxmC?9zh|5;6% zsh-`PO`=}CwD@K7A{&lJ>Fpbl5!<t=?KqMVJh5HERHXF-6#Z7AZ_m@eAtA zl$*GTF3TUQoITD|E?dl=WZJ}D^y_EG*72Vxn4QpfT}WLFAKe_lnYcV#%U#@av}zko zp%RK#e(YtH!<&!%Je82mn2;3wM$4%9zO&5}*{BH1yHtqOz6NE8wE$>6(NdFQknpS?BHA=Sj?Zv{#Z4lw3}wk4@0v z{)t~Pa09t3)rLJv#}s1iCG|fGwtY0{d33288XmKw6q5|@jFnT0j#ejVPZJyCN*0oI zzB5BbhmpVCAh7wClRdz%tUf;Siq{s|6yB4gArJR-;>Skz-t_j8R79%5I@%+b-*{U0 zGKuKZ*7{1f$~*^CDVT(ToK`u}bQ1Kr31;N9 zmHj!~5baD)`Xu|TQ8we?kdULTXo4@!zI_sCt}ebI%1@h0&V&4r??b-%0dj-CzResB zoG^5`4a-6jhul2!TTshe7$mke1brig8jtKTLc0@=fzbC5>-iNjmW*{L2F1lv4@>kE zFuxk>(ViC&6ifw&%U1-J9%PmCYlorR#GqzzIwxky{YRuL528S=QlKQ1ues87x~D35 zFq?;h=ozy)j16MD6wop&*@V^}*rovB?9uO$?{NV}FSDJN8lL0e2hT^&J{Nae));X+ z1f>4tqkb9t+x{j^bp1ROR7d|sdFK7_Cf{=LK*}AHehEs-kmnGz{_2StL$Xm(9?nT; zB)|=0`!l7GdTXW}S2iLmFJa)GRZeKDVyX!91s*lZlhQ4+EQ*gL@j14W_a9jI8KVX^%h~Y1$otbo>QmyYD4Q6mAN z6S%)`|1SGOxGfuzjVOuum{NW`1r(IcT&DV=huhSp#J%<9QUy~&GJYI1f7%teA6aUd1#Z91!Eua;4opp z+U?%e8}4~LiN`HG-B$Md)6V_UQpzi<$v9Ry+)NBIO{oB-L_3`)X{53{pCU=8017FG z*xfv+?l!V7|G<}{&$Bvjd4(TRPitLyro;Nkc*@I{D%4Uw-x4N8R%so3k*k?T<m?AR@YK)0yzs4fff_W;dz_-w>C zJAbW46}6`TK+E}iY`ICrK!t#f2^D?MkI9Dh0O%b5ef_+9k2h`Z`(8pGbE2EVgFt8a zF!MX4cTC@x#Kj#nZ^RSgSb2B&vzGIJV434CwFZpoQ%QT{oftcZ)de=D`EGv>62ItX zjYB{3dq{MztNfimsZ~#~KC2SPVx?JD)eR2r*|$j5ukvXKFpvWk_60Wtc9MXS3%DQ^ zDDe8NvtQ|<@6)g))>>x2Rw=e`5x}@v_%;6w^=7-yw5m3gCblHSpRlN+36P9^zfK z2HyJgBcJ_h%?e{qYiiU%fhAlQ-na%GpheH9F}$=J1g6yXGQK>}b4beuVin`^t3~Kj z1eI4^bLojv?etQ*=_wG3(;>`iFe1csLTX~Qo$n8S2nB(i8)ECqa(&3eN5Q z>&rnVa_t5G9~g3fc?mQprlRJp^$qB2wfPiYiSg=w-@)GwM*#WA33;D%f4AK2Q+>x! zF8#`^Mw+%tt6-t?<{wk-JVgR{8=Kry(D^iPrxPS4{h=j}2UrCe=}=CIwxM4yS7D zChO`HcJPav88KSgR}7v}xcyc}RxX~Vi3DNgAK4!}dwp^~{0RdjbmxVes|9Z98tZoY8F9tW zw|=Qm9Zz#kmG{(%sgJn9Gd66j zA5T=5t1s&PTh*4#L8%MjRO)7*7=d(4i8R?#-?FE2L(7f;m3f(UC0}J~m0{>g$vU`J z&#U`x7=-5%Z9FLu|G*+#2ur>>J*r+q(mBDFcHzc}?UYx8Ci7~{injzxEH+2F|x_{(-qPkL*B9DppRGXjYRL z3KP9=j9SmKp0ECvW{qCgVADI(E=pu0X2%j6FU=ktm`86%5ul6S`2{>?pWX}VgdK!= zPh7R^FEP}P5ztmO9o#Pgn(AvPc5(yIk2d2YwqaxJN+J+?cbBjV7Xn6W`#8s?U-JsU zC#M;?A^g(OoaBK~Zp*jqIldFUOm$%(nP{D6*Y|taYP2oSBy(RJr4pwQ-&^$T9&RAY z6UdDLT<_FJM_=tg#Z#%>?vK{3us+fLJ=-$kFFPx(RtDMHx#Z9-oz49IXZ4|`V>2CB z;6#7XDYcZrN7YL+0nB4;zfundjiML6bP`-9^mDeyY67L*P}8r!j+Y2j^tCZTC6!la z>;daXk0=pO?FK7Yd3EtD@M_WVb+YDT?|paY*90-d0oKM^c*0-#op}#1xilSSLMPzUaY;6TNeu`kLEMPL*OlG7+dNyEw-yG? znA?zhMn{J_D8CDQO#EWU^Ck)_Ew1$yki^{j4+h40HnzVth@7_6s0YKGCW3`dMIkc|O^~yB z&GDJu(PQ|#{?YzgE$=xs_p-XLkD|C^8!9pChu}_F1oVcb+B90DO6YpmIrJ}~;)_H2 z{pnwjEYWIFGNQ#sPJy#v5)oiMC*ILtVm{u6RXVsCbF(wx%z_)h+Ub2vIKuyEGumzld6${j6{qtN z%7+u3j^aQK79-WRgCojdMxd+dt-X&AF&>Nu3Y*v49#mYHRoqDCF&IJ>PLydlSsyr$ zbTn%w)P6AfgI`+XOckppx{=88HKm1Ou#6SaWAzisP5Cvhln{sPXw+qNtU2?w?=ET{ zj26Uw2YZv6pZnh%SxbKW|DX{eK_KwIBJe-EQNbV~prOEFVE?CVKNzV0U_{7o|H<}) zgaSiFBZX!WCNolkK~D^1brdPYfNcat`(X|^8UGdCV@tXXYEq$CTw)&@G?6I!KN=D8 zzb~l&YbPrjDT@-6uo0OfdSGH>A?tw1UpC`~y<3bV<^NG5-ZSuSLbr`R7w8BvW2xz= zVJR}e&C?fXS2WYEceLYbKuluYiYG7xQX&tPL(? zz|jh^+m%kf>sEBcY(@K}nPC_({6&ysF$&CzXN(<1{2MW?uY0+ZRTt?N(|oZ|g0#&o z=;PAR4z3sfVxfIG6~ydz#cSdpdt?rpf$OxhKAkH>juv6udPoK~YLT11N7qMhxEWU` z(9_E@4m2j(h->J;Gi-Q6YU9eQ;q*RyShDlY@YWUNm*;9ILGok>Du8ibM0<*tm(Avx zH{}a{h(n$8X<{0^LV9Lb$bi<{AmWEpkPzCd>c?%GVGFaYOcvdWr@)Kgil<7N6-Dzz zgBj8XXb0N0fr()$(p#JBKn*e^+Cq128e-@~<1|H!;H~MyFBoe6-h|iPgckzX?|ItL zqZX}c>qLtP0NO^E)>!1O_WDDE1iBM%=dQk$;i&5tS>5!%HU^2r#3aWRz?WV_8+!fu z2#@?qUr~`kW+I=W4N;5ZTF@vGi>WJ~Zss{WXwYYs5$PwCF&ufMNKgN=dYwlnyIQTi33>biyObW8UrTO;bRQjRO-!k_B*;?7XE642mXO8%dM{y$}A2A+%tNvY~b6Dm&V-Uc9z}2;hVm?+>rE@_0vhMqANu88HrT@HW|M5Qjp!MX4tif*9z)5?qgdrhLy*BtR2qoA;p~(qOk~d^9FUg`{2BYf?Ql{s z8PfN%(|ga8ZTkoLljA=NZe1B0IHr{%?AGkn5DUP|^h2`=Z59PI_DlSQ(ISMhK2i~U7HE@?t z$u|MFwA_lSvlB8SbZ<6_fHB4ImOMBItlkz!eip<($||w^UHOM19wcilxNgvH|M=Dy zWocS_l%rL#4e$NQ30=hxH7<}H(v;wL*$Y=A+1kpjX#I$l`7W&-{Uv}T*GAz6l8KtJ zi6zf?5>yfbhXLI=%?T&H$(?hVTN{LR$MroSmq!R@1OSRL|? z&hj?81-ikPSYm{2xj~a@2sG_j)r)JIB#~x~bt^KEiJ>W)D?7i}XtkgtZ3@=t;1Clu zN1E2aYiqGKfGDz?npe$+W4$v4)<(ZX%^&b(HlerxEA*Qd)@Paxim+rkxNX%5E zar+jvqR6Tgq*1w%bI(i55dBV$36hl0zy?ME3S-Vn_YZ^wigbO1BR)6`Qf~>iWf@Cx zTl3LAK?11LL;a`=qQ#uN72kvw74p6Kh$4MB0dBcZwX|cgw)RlzEt< z62rVDp2+1Nu^ha~4a)1MNML;tcRgx0Ug}M$kA{^icAgR8*`>9tWeofgwbY|6jI zX|f#V>O@Cd0@>Mner&v|$IAHZ&r*Hfcv&gP8$@Y>db0(Ftv;jj_ZVgl$9Pw(!Ad z*Xij@X9|d(Ryo^jOgGJ1+GYyly)CPr0buKGjaHy5B)OwX&aB#i^^QFKLW;~h6wau_O|vl_aWB! z_~Zm@x@*ifcDahv3Qyf~uE)3%L|CJeZrQAByfzbt!SD;7-r10b)%R_UmF-vv)?{m; z3HVTh@HCB(c5NY8Ynt~q^k9Qq_}|yT8zA&I;JqPYdt3W!;e2-2Tl;GuZJS$5KeI*f z0o*6r9(fYHG&8NkPAUwdQNCid4Gb}M{warXpjw{oO!^zGc~bAFY04hkJn~uK-`B0V zii^s{><6U8xjs{RNR)$C`8Q3^I+l6cT(uvs8`4N@>|^NuL#n4HI5(X7C7v-S;o#o>?Mlgh;JYm|=dcxM%sp0`IRUwdMk9^{^eJ!KY6P$2 zto~QCt3%?6$Be_QJVa;YCqA!6)sbvv+&}ChH%iNjGJDJLRH$q3UGKiiRFoG1%|@62=;*7a2zk z;A)2%{nmyB#|-$hCZ3=P&Spo?-LCTMn;kDmN@dW)mGjqaNM3W&shq|uD#scWKd;W_ zX&wsRp*0b|t8UrjPEUvpNsTfsdJt=ht5y@d6WFZx$ZHdKhaaAYTRu9Thj+y5f9QV-3jBzHQJ+*-ThDT{R+2V&Faq_UUzxnmumYxE#^1OV*J{ z$%Cf1SQz+r@Qma#goNh!2+b5R? zLBLo6bTY2pwbzx8Ke?u10uXxf-20>~w@HBHM8LrN5n2ECPQ^E=W@MuZF!d9H~+E-PC<(@>9J}RIaYwjwBkQSH#S6h_&DgE3Q&$nAOs5{&(X3X$8|) zfLFJ4-7M^C=pR^7!VQbuvcj!@;n9j}Lp1Fc7bDu5w*H1XK?v5LwZNS%d?=o55&)_l zV(@pwrnQl#Y$$BN@3n!pnE=h0?Kew(*6%sG+S%PunXQzSgoJGtaWY=i!}W8zV&PvMu^C@0r_!(Y{_8Fg_SVs#v32+3#HyIU^UhQ}?{4+abj zMXs3yIO+Zn|4J&=SZUIH+yBZuf*~2@ZGU%!w|QAo9q4*4Wa> zF|8LX5AQ8WRSwJPl$(pgYCb?A@neUKSuN_%3x2;ysV=0c^g4^UvIK{%b4#<;RiANl zLR1P|vA8GP1Va)wzkr(;l3KA6c?Gaqo^9tJ7#3h-XL^ZMKx>CdE4P)IsYQP$#gX7-BtTc@$o+sg9Qk$k$Zltj^8#cYvRs)HFf>m)C zy7d0h5s{zw93_)Y<#nI`9>SE;xzIFSM}gSA!~KHFnsk2;A+q1I#)fm(h;J;vcVF$Ee@xznYIEwzZKV$^Igv!}eOoVbev%zwn zKXCr&6}lv*D?hW7#(h*ddsehN6vlO#qkKLCV(Pq>Jqfw*6N}=%Bmd` zi`yBOSAFr7ipqbT>6M)v>-81HALej5>8P~EqX-2KyU$@O#KwU&u@m@A`U`%c(0uF_ zmhj);{`NC* zI{!DR&|9a(E4SZ*z)^!D*W>VRz)U|4)l9|a_j3^_;&48^tufdJF#tseUUuhr6udUD zaZ{6YpnaC!aMOGs4l#|BW(68lxRN<#If##w2m4Grv-=8Q(}`<05pwSwr4y%k@#mR- z)+dep`b~k54UH()dBe!r$&IOJ*Ww7aib=6@Qixd|T>sEdwv>!0bm|-X_PwiPGC}wX z`v~Jv0*u+ww;g)idjpIl4L`uvc;?|qccgy+>AB@?{;k@QvLCeYw2x+45RqAr&n?G}BbHyM7cx;z!d}iJ` zK7Gq$_Uz^+kaFtFFO0LiJS$7yQepzegu~)g5y}rG4aiqM!m~efSG@GKqw2`Gxrt~CP_@R=rmL}Y zJV`oblVW%;+=sS&ITcJlUL@EIwNh6*eQRBQEIal1dsY?s@@s2GW5rky{?Qt;ZpE@5F1bMh<1g(SD6WtMQ~-u=UrRkI(Zf*qw^S?+2gfA_y=JFB3$qP4+7(4fIx zLgVi4ghqn9ySuv+AXww>?(S~E-GaNjyJY&#f9H9srfMGgxvS5q?%ika^{vH*1HTO$ z9y}W2LInp=2Po0rl5oOek_R6Z%Q)nkP?1;g7#5t%dw3eXs$^F&=vMGhgj@X0B7b_% zq+*c(ObTdyQ9X1M#7~WT(Hl_25V_2$l8`63h$eJYWk+c*D1|CrQ(S#%{|6zpsJPe` z{!=*~m+;a21WZr5kJBq3-SZa2vzBhywv_W66_&@cA9B(D$_5@765+i#ILgE0<3mZ%Z(dy^{cLQBl0}BZU(JkTH3a1qn-VS(%Qj1} zoAqRkE?3pxjVZKmMb(y^1oN_@>Tv|(s(%>2(>jGd zJs|lH2iCl)>3|ZFqEb7vP*FC@C-4(ku3C0R05)Lm%7;ULlAup5<%p z59|+wHhf-g#sn|Y?*&lSIz#nMIhgdR80cGdtKDaP8rhqo8Zy7aCK)gLZ~4qeMM68_ zZdoUvxsI)$TJd1kjopu`_gz7GzZG5UWj%QHM#5;j!%3WqPdEuS?phie#lKAe$8uxB z&$~;HqVnH=mXz%uoY(J-7BMJx6}J0}N_S)w=NSYmMbE!JrZiYj6qOf{&|bER{c>rcX*s|7ygDU_+NE%M zSTw;)MK2#dhN~X7`8_;0Q~5yx*pt8k6*5 z?XD#YDQ=_`*p|0qM)ngWgG$Ed%Au6Es`O9hEl{cJ!gf&6h&KV~G*f`IN9*|EUjMdL z8s`Ob3hVQJMMs^k>kP&&7uth~gbgyEEPTuYU_DK~lybF<5HfePX zv3fsjYdQI>VR}XUwIXM1k*vK%{LPkw5Up`rU}wT6k2zBw@Oefw+j9>Ts2vcw$0ssL6-g(NX5u$#Hl0P~JYy$)6hDbTO(A zD}x^3Aep|&L3(_>lmg`(_ygXq;;6dq*Lmd4q-lPo<4F^N;CnD@q7Ae9KX|y`bnc6| zFt4n6-+XRIV^oqjT1NPH7h5`~Yy&1+%enYFoRAI2-KDe>i-3AFe(IVy-tA$sYYAcW zQ%sAL&La)9Y{0rb1?UssK4efp5Zr`LSWz1;O?e3Y9J%mtq8n|w4$|?$1}m?98xGEC z8F}K!M7tgwqBPiJbwxI3fY3VCJ3cKeE;9mP*7<%ig* zmzUlJAAS8r@S?(F4oOnqF#Y>Gd<{u6)|1b-BA=UW-gR}H;q;*=@^b)U&j+J=V&x+M z6LT5qO{%0pD8K|Y>q^;sJaWhHSK|=zvcC|kALK~ch>C=mH2a)L9pEPC&xHH>J;C#q zraVG_M0mh&X4rv}6noGTLBC@@2tx7`u$?`9&uD4H)Vw#nv*(MOyVp@+{r-b>Qr_gF z?)^RFcfVgP+uEU@fL#vCa8!gvvO*-`UC2T}G%DXj1a4G~n_O;Wvt?PV8BoY>{;2sQ zMf?Z*G+PzoQl(R2PDz^Zld3?^r7$iltG6Ye{$d_UnI$-s}(JFyv zZM%w!j6gvoAujv4jKIt%2mAL&3F9tx?6~nIi;#m4($1`6Ra=)VT1Hb zz{~dYtc49)?n(Kx!{2x2$zL&XZWDRg9*<(jcibq3>84H3rqi7KV~FboNnb#wBUW&3A4? zvnX4$bbTKeoN!{8a7~)2Akx8gNiz*`wIDKdOmwsjMhK&}lT5U_vz3_*Q@kF@`z%c5 zYr%z^ia|y4KV=vAjpn@vtnlmK;M;etSY@ju7`+p<bChk0Q!fj=muKB)REe#D0 z-0^FuTK!&Dxu;(-;XanIc(aXR^F_Fglj|8c+2(wIvs=!s;*!4Stgdc*riYPC zfiN#IAW6~fu-$W8N9iW;cl!sCHIE+8FR1PN`Pg41@(+T^4t`|o=q0kFy6N=!yIzK; z-cK7v30e7N;D?vXWTU90wNbpd8v36xIWA;GScppk7{mqZ7qR+vfH!K zZFAQTQQvT_jFb8{8B=Kb4A4M(9-h^3#{!{ffn6lDtrTvgM5bP#ezpUj zVY*Q~O&A_!JV>1o@a7sG{e;}wv)TS_>6&)Sj*S*~X7dhHn^S(n11qLjvmE3_ZKa&N zV-H~IN!QEwyBN#&hWBnRqmt!}!a;o^G>tDl?!7{MpsQ^AGOM4a`T-o@Q}9z+uJ3M# zpVp0I=b%i|?z2XPd?yzfIZkn4FJ@Ko>sK#rX}=w4xDaUnSsCq0K*qana^J^T&u0?p z#L2%ChwW-=p4b>L7<> z9@;&-YB36OBRs6&T((~bxEjUp3}W55zDsB-X^`^X)xrsFbR7)jc^p~CD~bi1;oOI< zCFR?faT@L~uluxXf8np8Z+F9jeRRbp=QOm7lD*U@E>5n`r}Y4ur1di;%UfP6Cfh^$ z^@;x^LS|>5_5C(f`t{T$rt~?X?WRZQo*a>hhP=ezEG^=ht-ad+U_sFR*jYLjc*`?a zT9lge-AkJc-EDfjr^&0Z{Go>atTlT3m+x*-2%%3V$E~PwbmjYn|6>WAzINtkadp07 zk4bz3Q=cS`h}L7PsOe%>C?Dei7mcFairUGpf7K98eXzG|HBw{t#5wmr2vvX~tpe&@ z%%(6&`=MTWwgN(};oH>|P?QeUHhjK+r{oiK~3y}417q$vk7s}S^sW% zUNyK-&Cs4156tR*{%lcVl+zgDF?<{x9iBc_`h^FDgk>=hglago{P9%8J!!YPJQY6N zZ&7WDUmQpI$xQ!yPZ=4B2#Tn2uiA0*Z3EvknvdClDDV=HIruO@VyJ?-2qONRJouRF<$U67DemR`oIJEY6Xv+@&J3kSmlHKbsYTgk zCOYv)?ajT3jzHGy9Q0jnUoQvjd7*z-JuKixMLkF<0$9P5#RM#tv8tf1cA_ z9@U9N5<31toU(4wCEgp8*eNf6BlH2=3H|8av=1ov+lWYW%y8`aafIIJz?AN4^ytUd zC-hu&kGMYlovlU}9zmqcS}C-Rqw9X`kBqwNb?Z`ezAfvbqW1=q2#Mc8g*BV223kuo zEOsFA7zw>eJMVAgyGFyM1zR-ReRgPpKevXFNM6Vo-mmdt>;P8 zC?a1kO8p|P=^=45DL>#)D|dY9OopWPHG2Fq&n_ifJNWY@ znabg+Q{NHcg#$*=#K-I?Y-csz`M8v2xX)$o; zY@gMSz<5}IY2x`xUG-+&N9Fe$5EnU|+st?&(>4>i9Wf>+bKfepzT@LoZmR=FVnd|p zTn8h@lDg*s<+VK}D$0FDeH*p^C8CXc&w!fP#OYI_?Q_}$NoXgtPKK!Ywd50{bSn-9pdzhByt|UIEt;FUYGHdq{`03H`bc~HwU?{t!!zTJ6QXxhf2E1C7ZHs& zZ1WHxwy=)h9FH1(PwK7$nqf#9=!)tRUzTgp(ES}<9MP!xv$jLm#G2XK+Q{azi)e(F zB^orrz7_L&nXw#ik>ehbuQAF!rTBZR<&S@)Aj8J>gWjLfWmcxBhqv%E>r}S|=`B3m z#~6&8wP6~Sv`o-~SGEc8EB4r`oNWoNe^>6Bw;7^!5GS8@5FgOiAfC{0{!=;F9UTGB zOZ6v)C%?$jZgt5mm*mx;pRkIw#Oq?%^2RdhuF5}%Zwoost@@wIoD{nYmzmM9LQ{8n z&)!HveD$52-I-z7sBxu<{&{K!V@$;$Vw>y`s?Cf*aA3~G~ci5Y6zT6+BQXNg;Nq&a*;TlHmec69z(()^aR8}q6jB2#~J$!=@AZF95EA4&` zP11Dd>^JrDpky2$Uuw)9k$Z$YC7EPYCw1JTIP*v7{s_5)USh+*ZTWZb!^laThteqi zqmp#%(bB$ZgwCkIm3tqb<{2`T)GR0vl6>zftM0MYDn|KjJIcW4@{1f}BlaRj7S&JA zuXDrKZSQb>wYEzkWdQO(4^NYqTV)`}4$km*!j9zUHL%-w%*jrGdoU{6To8wXP#S&) zR&XhC(#HRPSWP;*|Ie&uG1wgW|G;W~`Hwde77FfvI?Pbu5c78!5p)(<3`H_W!$8)! zygpG(fRYj1b=@o$n^XKw{Y?-BIeBg^9;x`@fT#{MK3(e>3p^R`Gosqt3HbQ54SaUDTr18pO>H zDWqhLpjOlfihzNzs=`z6j}ymI3xJOVDoW-@QP&~m|AwKS%S50@#uR4JU@y)^t{U`} zfuTbe#2k=*R-1t`OS5#8p{^rA`zd2-QH$PP#gPd4ODRrX+M@3LN0Fhp#zwa))r@MX zTgK@AJ{nq-Ge$2))QQw6qc#UFS3Q75LtJihrQ$e+KO`q7B@t=~Pq6hdmni5dahGih zjNYKd#K_xgO?Sn7#Pyg{y~aKlkyXiCb2Na3*Ql*;uL|&0Wt6SV7m#GX#5KS#W;))R z-1@`H+Fa&Ax-W$l+AGg<9$XpjdJZ`$tqM|5hXzM7e%~Mk9OX$1&sP8*odG8=S>2|@ z=Lm6+P(P`@CEUn^4U)Gb!ZgXT}j1asbK{eC6wHwGH5SUeR+g4hQeSs z_jptI3PMLx#emoXSbRq_)6vIDlk6nlh@F8;DJIH`sun-9K?I1Xj}g%&8T< zs`2kBTLP_9MUG#u(b zmPrP<%zGfL1MQHgHQ$NkJ0|+L%x@fUeTg?}_`!NR9F5LNW^i6c=%XE(1{v;Qxqn~toWfN%p1XsNh)WNJA)9K&m=8PnmYo%>0d2^0+49_ zhSCgzZ#@i>a)dB?276Gd0;Yfkdt!eehuBhoqO}$>DGB?y)T9gz{G6qSTDlg*#L~vT zW6*83imt|+nmX4S;-0-Vl1Q5jXtD$i_t>|-_nqbgwvFDdW8cXMQKr0& zb(qm+n~pQq0Rr2EFcit}x?_I%5YLdT)v1##T7z|t1^t6^`HXo$c{K>tE zx8A`+f4au4u7Rlm1P|Dp#(m!V;oW1Y35^*hny@z6dNwe;F(AR!TcQ$+*7f3s=C-C- z?Jc?5Y*Sid4p+--ub?{TZxW*H)h$gQDJ#utYc-Xe723|5QZ~DrFyfSwkw0!}nP?0~ z6g6JzpC?hHv)vbhiS`R$|fW47(^~C@z`9|QhUsGVE-9;{)JtDteKQi%L_?hm>1Gd;765{5bYk5l(BwA&v#=G=E?KYH&+t0OE8yH?kwOQ$iy zf{ZY-c`#WZqz5tq#GOT&UO~u)qu1%{3w7j~iOm-hlmpPgy>a(>_y8e3p7EAbNyN5s z9+XWx;9X#Gd@Xj1d2(z!I2`ARHQbSG>a!K=%Ty;^mYqoLuF{yXa0)0^*Jf9o~e84AH#4-t^qxB`Lx=p*8 zL(7do{89=PB*)&rZ4@;mhZm3bpP-}=6Kk2q;daE9O9z4XjHZV_G}aK-e88_8ViR&E z7W3&qD&eAVFs@ns>+((Z+$Yl8rtd)+yk?ZpF7vAf0M^xm?YyH8=876|DEP`YRCo`J zW75JelLBQr48XW1eryXtAg5yQ*h(z@Mc{;UKzZ;1;YHr{TxkKdM7me!uUg|#+U7EK z0>7iW^^0*A;r6<#uf3KGW(ZX=!{ko<`jD@ME|VgU6%B5G*XR#t^*E%^J%f@c^1}&n zhuKPK>kQ-D0M*DYe}T8S5UqsgrJbvwJfQ2IqCn9`J|E?NYoE0x*}~Bz^*$lLhMJm= zyQ>m_Ub9?EN*d8E2QKa(#KdbtI)O4ifhtHu@7IEun6V5*lek^Qq}?LzB8d+h)T;t( zGOyw{CC38l+%E`*8Z-g9lCJp(;w6|!j&jtZzp3+}wWxC=sB=Zw(jCPx`vZ`=EE2Ku zp(N$w&crEfAx9R*rP605Md&OH{vHO#$cn)>;;VhHIMw-UmO3=~B~3(sp(Mpfv4fuMs+^trE<4a)CT{n48Dx@-2R};z1Ombdcch5-*!PA7 z;qWZt{ot4UEJykvsE2F9IqcS|Eu2#gM=V8C1Qq*EDE*V>| z;#SfhMTevFZWH52sYBTfagG#h)@HL3J`9S{b2MsW-jh{^!e2r;A2DP)p6L#okeGrF z0j^F5`=%~OB7~BsPUVtu;XJzz?A2t*?UO()a>4wN|4JdX|<~-+uFr;=5D)Rwx!cS-xo(QD=RTwiEKKB^xq4V zxx(q?x}N#|(v6E+{&8VVoUB3>_^b-zcoq=(jY<)#5UlBjl8p;UHuF`aCHM-I-xMM9 zA{>Q-l!L`ev><5HNpmB70+*ykM4a`NkX`ZTXE1Fo*qg4|o3W77-#F5$CchJ;zp=N~ zGzcT76W&#I$^%>!bM&oj7Jvt=>%4svq7`43`$n&ML5WXatm~7}btWn9qB={G_#vJh zdWXaBCx1SIURP|vd4&x?1{|XX?Dt_Tp7kovI1{X*?Z@@sVPtG2oE6{rRX$voQe|w(bZW z$=hR2l%oW>^lY%ngNn?j+G?K_xQql>B`y+Iq#ORVk3?EYe0!^m!xp^fmLX4rN9d>v zT5LS8*A8=4P1qCxkIczghy0GaP$P?*d2tft09Tpa64QB&<5TGN<#Zygd2-qSaZb)z zbeZdv?-Rw_=zaG~*SArS=Paeq^LEN3ab*7>E(u9Qq6K-%nMWWG?2~$8{jfT58*g8{ z33bhUofNeh#VKRc-3g;4v+z(B%!sr#u8a;Y7!&&{V?cay+byU%2Ah6LF|m>Bxohh1?b-0_XkB+4 zKcHPa*6SOVZ8Dp#N=;_dZ-QNx5`IQU-42zjv*`fglzKTAsA6J9jWl% zXb~IVj=N~#m=a$!97;{5E2Kk8e2U#%wqB-qC-k)Qgvx)_>h66E8gF>b4_@4!IVF)UPVh#RVLrQclxJuqB+4I~(OKs=ejn*n^J^h2gBU z=NzJ&u7-IPGtII{$10LTC0J(g;T&q{PB3yBeVcI+M=7i7viaRl2DRspElyZ+z5{JA z_d4t-q75z(^{zHd;nhNpT`ZEe{YkEZeuOG_zx&swg=uY4nUrZ!e%gcBB}ig z#Se{5ovcHnYxVCTokg{MRaNkBT_>|}SUqGZqJ5JVH_34%rV#Cdp}QfKN85K)p(?Pw za&_zCx%!C5!Yu8*{1$~jFN64~zew4pEiv%-LUVAs%2XjrgvQNvwV&R`%NG&5IJ6R` z9pXOq1gwV~n|;ggdMBcHWlmD&he)W*zAQ0F>OUL|ufU_(M)(bjrA2R96hniq)_BRD zHap-`IEgkn#kjaklbGM7qed3JQ}8yo^|2$%AIVB0PrKYp%8p2={OTxLyOe+G#6#@# z{5m1{+sp5;=|EkyjMEK~ZVsk%Z>D_1I7`8OPUQu@6*b zRHa*S=P?@k(Dj$lBsxpCF)@Iu<*vG35^m*Vy<=bHiL#EJ_ImkZ|D~7PgLL>U~^U(04{yFbbnzk-sC~Qad)Z;IZhMAz)%njU|nqk74 zE7R~j)uM~vx$B<3*Fnvr{-dW$Tqi5P6CI!c%haxX%t55E#^8d)nEq+Xa+mDyeaxNk z*w4V+H122TYFzKYh}-C?{v(Ul_{~c;%(zntRnzU`t#H&Y0x0e3iu>%!W(cfS8_jDJ z`||8HFWx3Eg*>Ali$f^#RqD$ZYo1*oX5P2}FJpceaO~Jk4#rKOBXrHQK4=Q&0Bwi} z)NOA2mG?ua%BnLV!JF8%YeTz#J$e0O6P9OH2UW*h`lyE`1%I)rp>5-6KRjP~ z>A?g6f~1|N@b&xOB9n9S-4R)=Re2$`2$ubWkJM2zpy6dj_Lmkyw)DnIiUeX_Io0<| z;Yo+O8kSd&1=EFF9e}a;uyjU(Ve1e%axswmWN$~xzG0-gZ$8NrC9G^(mUsS9YlEe% z&s}DJW_xkm>2!UBO*kGvIE{gXWz_lvTeA!T4k(2KMx)s>3~ER$+s@?<xU?sS4*&H{vXGg{UYHGumUG~6YIph|s+42OdFga*f_XufU%MqkL zQ2gz{2C=2TG2-V_D}rZ~nsrDzUHk2KCQ(los41CI&cqQMyL2TEiEI2~w{qn)l|($#s~?hkoAkW^%sY_7~=Yhz=gJ=0#J(#%d7JHULX zuaE?x1DFqWJIGd}9BNy(-|V=5!y7Q-e%ycFXr zoRmV0dCG}PYw7J9?2+Z;NZZ*cIWCkX4kqm;6%?u;6?Ft>vkEV$sCMsc7iTt;P-Ayb`!k&=m$@5`iWc0ht~H`2{Mv*ILfJE57^Ga(YimLHm?r zUGv5RQ%Rq55FYdv9|86k935fNfXv`=N{DffcvvyN>$72wZB*F3<>)bQhM~XuV>+f( zfqnbggt3$%-FmL4Q6*w|6}#hd35DR*%CpY9BC(0_Q$4t;b;E6bVu^aa6)%~Keso}p zb$k0*V)8;w+q%gjd58SHpY@OvF+c=w;YjPyp0mDrV_&&xqcW~RTYBQ$yA0D5-W;Lw zSAez}xdrdM8}H}A#nrjVsHAc_C(jsLGSMa~GEZ2j09}Fl=I)$Z>-K@i=cJ+!)pW^S zUEk%S6aV~1?rrDPFiBWiIEg7g+n>K5CohRs{HSkdO`M9uE;GErgnKg4JfPHSyMGY% zDQfQa8+q17Vo=%8?}e@-_tlz4@%v{W$??b)#|H`C#}?LLp6IbUstkOkijj0qKLNVg z$JPRB04+1W@T(Q_&*7#ulh0qea4K>%d0uh7^mv|wh#PgW+^TpLx$hxw4PBu zy>xrmiS%8E5<7VI2={H??y$4_!Z?OF$;X#1XM=H5&ZkQ$WC-i}T*A%js(sz= zu^Hj-jhHB1IoqpmFOPa8ujwM)~Si{7feQNLKOcl$B$aG5%KL)bs77St%B zK{i~4*}0fIgAxXZxl)MrXn}~BKKq|Xb3<#9Nm7pMx!7Q}!&nYxc8Kj+BW1b}BB@*` zHCyt_1GT4OyRV#as~7qMXF+}I zy{A>@)-xR#t;5ci>-)}(C_+^(cOpc8YzfP&qnK4gMM^71;ifsCdG}KOL5!!|XnCyp zD-UnZ?kP-^B;KwEgN7LOq_gDsNe;)A(cJ#B0G8wZukZf3CKbuHsBhLVHj3w~c74$;s}{sSX}) zEQ?w2AB0xt?U|Iw;?>Y_8cGbgsdEhC(?19}PwUk+9{qTEs@4S`i>{*c&#l$T>YtXg z_AI1b$@B=c9L2MkVlr+df1_$ z0BL`8_@I)o6ACPBUg(#;Wwm5e>g6rH&aR$*HT$WqMBzfAvlrI}lztB0QLBlQDa|!7 zAt~87(@C+Su+Uod2uCFVW2th%b4~G9g3<35L-cwFjQ1y7Zn8uj?Y@{^W+5Gy!FG|d zrjBD3+1&*(TCdZMin8UY5Ib$fs|H zTN!JC{U{9MRQDH$BF&Te=11KfHe>3vRnKssc4w93X@v0>;ni=m6*(+wL`OM5vn3_T zApzQj(Mb%-^fD^F);DV3qz<7S&frc6_@G#iC(js)F!_XHL5|iU>=ZtKPBt1j$HeMO zDT*D^n+*cCe>S0YVY>sX z>oB@>!lOD}JGZ4vwO_Gp4ehwgtkk)f)->gknosc3vw8 zN^58E?9q0slx9p63&WS!FA_b7W#H+@Ov%lS*{)o=!Jy$v0!{he!f9Ii+P5P)>Wr zVKFTrWTg;hs~!7!2UvMVma`iYWUobIB3X)!f?!6ig)Ph${ynFr^uMo%;_9>qE5DU^ zOcsQ=@eU4oMvif}&83e!=vu{WT^J}m_CDwf>IjQ^r@vE$FT`Ldd=z?WYJX3kMHLv{4Eedebn2d!d%@9FtOToB zJoBZ!e2+8oNJ2+x}g!lCMVfDPdH~qC|?O2Tdp_>HpdD-GEW(b0N zNv;Pfy`-VRHcs|iEZH#sj^_;8M&lJNwGnH#ri3WleT9Bfi^yx#X11W9kgMVsI`y>V z!K=SKYVzX~x$bqmWbNOL&hjgxa!U^ZaO{T_*(VOh1on|&{&+bl(kW!0qL8uO6*@R~ z#?;e;#7jF6{_an!`si79ch9og>ZOqIq;S@wAO1Z{wg;_z`O)*Qk^JlBa0IJDfOilLx~~rmoTzIx1E=mc`DQa zeoIo9cES=MRw#COTS|zLwfgL%xx0Ky!y%J%er>up9)wK=&B#-Y?25O%xi>mPpT>bA z$ar{fPfyjEUWZld1&jb^%0E%C65j-_eq_&l|+ULT}fz`pt#GW*^0cXmkjMa zYge`9aXku~W%=0-kqWIU1x*c0Ic#_^DYI;xT+y6^qZe~>-mDKo%y4wX7*+lJCy9R46flB;tWQaQgCB7I^3 zE`(NV;-#JHx%2m+I}&EnIFFe*vK9E6p!SuYr)A}L=H)nO8qRG>dUDilUC)A_W}1Ds ztPa##f!f(287kboSbL}JgD!~eVh%keQK&0eIu6k#gC6yG=G-5ROUarDb)~}@Cr`eO zlFO&C9Qb=z_vN}bf_txC_f^_O%1h(J&`?Wz=_ghnJ|8Hq);Z~nbs|d>N3Yi>>B}7> zO!@KR(6SheKKSUk=6M4EsQ@PwI#TRQCxX1r#G@ zPbZpBI)6UAD)qq)`?YQT^{PUI7PN!{IxcD@)x-xGpEd<$v1l*X(ieB`OI!G)kz_oC z5fafqE{5m5K^%XBI?f$(4^>2F+Y*iG7DZ4b6nmpUSmSmXDPG46%j_mF&SN6<>=4QH za_J{~O351io9B%zJdSmCyLWf2Sjr`+Ug(1W^A28ARnlMb`{0#Qf9$E9UKGOWbN^aAhvbU((Xw%!nmukUtSox_x1GDb36yC(D5FS z+J(PI*9va0ZnO>6rXYGH z(yD|!E~Z1P4CRR4JcHaLF2G7o01SkC!e2q+elI{R30%TlD4?*N|M-eVbwC(8~fh#v7H*s}ed7(e|6%c@!fuXI!%Ay*0LB z@YR&ZVTBel)-y33DJy4P%{{6#T>HHxfo^lJ8scz(QG%IjIE5qJS#{iE)vB=Yxkt2P zLlR89nFmmY+D$Q|P+!&S{P>#GLpM`fIKF0{rTKe!Bw*(!0UO+~+ea4Z7F^(2f!e?*(*rg+uT!#efk3 zD1-DP9OX0RTiK0OF^iJiLm$7{B@zU=)f%C^PRE7e79+2-Eaxlrgdz#CBJJ?wc&5_U zU`!ocX<8grYJxN3k<(~;OfF{|_AqS{w~vy=(|W1cPU=$$sPx3M3m z_g_zpJLcN`bZm0^ve?z@v09YQ6+HZi%ZoSd6~1TV(7n$sxHRb4`djY4DdlWZ=h?Of z6)wCE9Hw~jSny~vMGv#!&tcUscrA4;LkTQ>z53?lusi?dw(|5JeI0D_7SxZAOt1@IcDWhQqC4 zTYJ-YZ?U~uEldYi7K|r^n%NcZ$($4^?QNM4(q6rsKVjTq;cPanC{Fy zeQld1vfZGz;Wl(8_cM&@z6SYPAy8j2<~-oL!wrzD?lkiY+FgLTXJwtc?5e!NPSU)O zjOS}Ce9jjD)sl$1;SnzqR4+m?PvZS>?<^pxZ-&t0hB&C!^4#J?QzO5*mQ9?w5c593 zP*zG-m_S>D&H}Q6##weQpswK`1npQ~l@B$P7nt~Y4ItqCt7cXoibQXYB4A|*D<>;T zXAr!j3qhAW&G*BpWkO{lyKaQWF!0V;^Ok?I7`pCio#q zxJN>qv@6gYlE|GvshTdla)adjc>VO;l7EiVlbprzF7~>FxVB5t(*uZJ?87$}qsw$5 z{s*BGgOOUr5Grj&e+S4Lv}&O#&T{x{pF^feV2DCHU_b43#VcxwCkBOmCIqWvZw|?S zsMO}b{pNnHD0@Rzbi$^sDtJE~_@Jy7ft?YW;G+6qg(11d5YsJl-e0FjeO+}Ck~;`n zQ^PPj{0H$tROz+;^B=@=Rgt`*!&{@&>cGnMAoNAfAjLr7ee`1YX=((@lko784x{cAPdA62*Y_15uvrUXD#dCBy!N#>V_Ene>0({RCRE=uBB3O6qa;*%~{3 zVr^;bK~FeWO^i5I5+nwI56r)b>_s6YEO%JhaW+}!hgytA*P(yrDZtwV9vs4j;*h?o z^Ke}s`KeO!Ffaz-{ZVdq+zVF{IulK(^DU_R?Z4Jcpj4av5O__-=v+EH9;dMwk9u9M z+A`uA>c1lRmXOj_N9*9pGDA8_>S4|=K#PCW1PeYmw1Snber7YH;;#xN#TeVF2X83) zX27PoX(!`RVTGGGt*?f1{~f$(m7pvNcqq}%^;1sI?JydW;5qF3TWB0EhN24Lg)ms4 zL-OC+Is*OwXJ)qS|Gl;j)|URJ$79d44+J+l_y3>PIt*Bh3eL6v7nudiP5-C0E*cl;m{)hrirJ?$OJ1MPwgY1n z|9{11htl19Xfy0W_Yo~4h7?T^tqM57ic?c;aU2Bt@@7a{`aU~M?cLsGQ*2kfMiEm* z1877u5sRR)0&ECz>}{>UNJkqn$P5Ff{&lT(s3op50|sVt#5ljUS}~o-K(NDemw{oj zD)|e+Bctr=I36}DeR6TH@FlLMD4Q>>_$*)41>4ulC9vBu;cnP9;F_b||0aIGOb0Faj?cg9=x^|ITo2HHR|PFYO?x#v z-ph=R-X^TmZ!`G|le^cYQH2A2GbK)~`0usXz{=g;ac?<#BnHSb+6(8C=f57F4K{I z$cOsK0wehRkpj+A`uv$@TtwDL{7BwqC`db)A^He<$XHDUEl4x%Z_py9+W2dcFtyrb ztJ;w=9PkVwrOns_O5$ChbuA;4^~lIL3%JRVCa@suril}*tHa!?SvrG=+=z+sw~?iu zt1Qevh1_~yUOy%<$#jJpx^D^*C?pBE5*m9Q(f^8suXha@ zVku4Ry2Salv;D>Ee>HWML2Yeq8xCHIS8$giMGGXj6o=wkC{Wx9?oyyQ1c&18?rtp* zph$7opuyegmvd&mdC!kb^2|;$*-3V?_Ij>$-5vx52bGT_oiHuOiv#x-1T^bq0Y$h? zVS$5{!W?$bX6BD%k&10qZ?E}>{fx_Am?T$4XnwXxv?Dpc>nJ!XvFM2l^435IPW@f2 zii%O_Q&oZ;1Bs~D6Iumb+RLwIHX=1Ahe~gc{JArCJ<^n9pVA}=8ioh>2e`?HTcrmU zw2*@E$PM0aF`2{Q^u?(zLnOTG1VJ41$uFkrMW;2Ye z!Hz$88v1HX5PnmN0iTcE=~V8co^r4xtIgG*_|J51kMT32hbBt|^}OF#WdAUBx)FnF zu89bdDmE`~OGU=v_WUt&6bNos!;~t#^M*{H%{1G5%aTRFTT`X=jx|F0xRGHiZ8PnW zG_nYxINf8%P>ycHT8a2sn204sjPRFAj;)t{p~FTJ-`Jw)(#`8b6ssSi^?UJu%eKH#xl@?K-Sg&jdJ! zdKZ_+E?2A9EsaK;h4r#4ON~8gr$zQM8`R>39GO=y%S20G8se)GPy51CiTvZIc-Fek zTxQJqtc*VDgi%v*OsPMNJzaBpe#QjXb4uOI(nOv-QtzH44;r7!u@N+%_^G+Au@U@0 zOyt7xh*SE$nyL41r#6-;nZsNE(dV_4~ySwV4WVUpEB zkq&O58E!yQaqoOImo+scGGI4M`!w>mMeNob{d^#>SHZw z9n4#4cI^3+0q41V^Ff)w_p2Ce9ZVLJFv_S&$zM;aORa|K01ky|MVYZlF-sffGGc`z zQ2htFAG>P7N`bOZiWrU?9uuC5yj+zsZGjSDm;Z9JX0V>4{!4Dfx8OGiEag1xl$elc z$F}mf$l9cc`1L4G_zO@9V}b6n)2}xfdDr`&w#kfTjOfm5(%*|TIj5K$%VO~lnN-|0 zQjCurE9SP5=ji?8H|<-}CC1oCEI=zEx(ql&zb9-%=uJP_T^TxFVMG}Z22P>a(As_F zEN_fOc$4$`=mp3a!fk){(1Xny2se9-v<6wJ7o@)ZjC4hKJ6Q1@ZE>RQj&#t_7yE5- z(qD$Q5~fv&H=yk70#jQ@1F3-5yv(Dl8q0@X<5BHks!m#ucO#ico&hthJ#2fc0lRN;XtP7cB@8I7)z3JOZ3kht%D(*i>?G!S7mEiKEmZeLU9?nDBQxNmutVVEAU~a-<>WnLsnd}w$gM3d3yMx?- z;=Fn_wO>CuHgW1(3*&rs?5`{yICgXOKmQS&9$8=e8~sM-AFeE$TD*KrjW3UpPkz3T zINl|RPcu_l+ll9=qP+jh;&j1-n!(s;Vhr;2IFy$ez`Bu;~RY;(zQ#9b4c_oSNDN!k!nMw#f|h3rM3m z%eG5c3h8Lgsz)K$f*FhW3UOBH>0-Q%k-)nq)MfLh^e>A@(bMM_1#v$nf&`kBgn;}Q z-EB`pv!kV!`-E~VW~bo@-{~QARfH;Y5BlkHRWANt;VCIlc%2Uwau(+WNlT0BFwI8S z=05mQ^@3Dd#J9Owc@D@D5<>NUnBd2q_ye@&bE(hIy-uk9ow~kKWF(ymXnxWwgk2!s zV@Y5|7GooFojEn%HgdiK+#oGM*=bMWc-Ls##P5->C0z{3;q=+Qh@os=y8meZisi?s zzGN^0pq&lmQyqX89GJ}Yt=sv0BKIYxw9FUfB{Gwf`1q_9#w@Ck|KWpsV9}~J6=|!b z8vgghN*=^dS)>2FFPJ@?tR-hxp zO8)=Oejc=7>dE2L|@0ops;_>F!; zTv_>z=HjFyFj}F1G1@H=;9^yFLZ(HmcswGK9f3}n-|ic0p{*6{(U+#lBfi+`g-93x zf417^)OLT>IdwLUc}l*W-rvbDWgRz*k2#+i284iPl{);x2np&#ZQuO$j5|6f`cd4b zw;SEB_SllD54Gm}^tUVe^d-ZQ33gqbFISCYlcXgo)ufEd4^*>KAAaq2+k?%M75Q2` zpLz>mg$2U4A*0U!#<8C>4t5SQL`Ab=jKRMSF5YY^yMSOns(b!T^>AAx(WLSC$CN==Fd3q9cGw06W(V+KGJr*6QBwVl-=g%j{ z$t~SlorBqBoeNVk9Wn@8J>pP83c&zw_OX7OXQ_jIgVF8qzu4c&P?rdt{N0q3f1D#| zZBjEPb`a$G5l`I9SjUlEZP@WIApOXd^kV7TCPzq=y8%U@dGvWfl{ZJ}dZ&!01nu2? zwGoIlAq(VENHw&!c>Uuc1H1-H(5E`{U#{7C!U7SgqlnXuO0Y)6Fl*je^$I@xAeE)t zcL2=PqClJ0;Vmqd{k$)wlmmilrk_q|ez#N3zB}L_*HoW&IIqA7FH~tc99p{|ilMG> zxKC=RIcfdPnvoE6JzH8;EQ84Xs2VsOpF98lm=>vAXg`O-dO;b#>6ys-r&4Hlzob}G zdfet4kF01D<{|BwKYx$b>FWnqtTGVeVu_eA4{%3biJhnPPiys-67X*GDbV2} z!E1#h8u0I3mowkFW>DG|`3VZRJu>DO~CFNu}#hl6MSsQ}7%@>eOv0c3sT=5ke z{#AMM(aEj$p!H8_GFNH;kvPG-D;h?SrvyfO6xiqDI!U#`=E4OF4^KgB+hlIgn{Kud+~;mdIp&!Ej%=8+6E=MRGAGKWC_b(HfyNr2|5kF; zC*sVwJwm>u)VQ>&s0f?ZbtsK~WgQTY9T)FgC#_@T&AyTa%K9LOm{rHfUL7?~v$K5a z)|}pD7QV|KjG-R57Z-iK;P$U&wa;2-9X(0lF|qcMBRhRAODtBsz{_o7LdHRbh=}JO z$!hQ?St8vOM%FuQ+?)p!;(bWU1UVo0Grw;G^^;fQTdJW64F^q)iByYZ2=PCX)%OL~ zBqWn0q1&$UeG@P~nyzFu5;6*xx_De6ADQSYP5hoK{ApQ(JnnGX$d@h$_Z1(lsVPrB05WZ7PpGW=Ha1Xm}QT_v%LcytWCf`_-K;L-R{_gS!#Yz~mI zBcv-~vjN9kQ~l=ZZb)#K{w)=C-oJ*`q(I%xRZ2_K`KQgu7rw$pzw>5e8%=sj5JH3dI_ zeJ8E~6%q6NglXp*!$!YZ)f!w-=nXm&5A}2pr7GWwC4PPSc$k1{D3lVJKMhUYGLTD) z(+sB38=i9?ICq*Dq#%z;iTSk>2R)V0}a<^Q}Z%d<_P$~2Uabn z{fVhzZS>6tcPWr7`Xu727(t8ezw&L07HySZby58bm>h%}t-Q`I?!`+f;*bt(-QRk} zOuZ_($sfk=L7G;m+E^7>-8NVOQhPCQQ}NjbF=qt2Cto=+E}!me^UmwuktS{FI+^yL zx?`4@KJ(fMGfRuTD~!VV3y^9X?A`*48ri4x^1r|oM2c57=g7@6qqNXmKJwOBcAk~K zko^m=C{nIc?B(Apk9vg=-RlJw3yqqp!b(w5R=r*aJqw<4)L$+0EFa)B@p>58+)Li4@%RE7iw zc&UaDtSypVY`-#{61hC$NQW&VRnyc`5p^BaryYA}4DH zFd~qi^ZlJcM;!5vWmvEF`=Dj}pWm!}IBRK>^(JO0>Rz-jes1(w_iP2SdvDO!iv&~Z0hA^`N#%}u0>gb0lf!gYu zh$;1X@8}!9#F*vsk3L*J3zzlPZz?G`N@;YSxCdp_P`n$S7sNLa=xt0YD6~`Yw^N2F z#hXWsBh=W@C)nkBV&12$PvLa7rdOom7kF3)-Q|8YSs^d>JkE0)1r_);9Vh@ubQpB+ z%NfFYY?0z|Ih~xHeM4i5wi?ANa!=*7xKkrsm$mUk@Ryk72oxDRyiEmtKPL zA2o#)P4l%o4_`DLyhitTa0h*&+UQ(WPK3R%+zns8Ml%z|t`a5NyVNc!Sdn{{y+0ZK zi8cIQL8_8I287#tC4E-9%xO&PLqzh?ov%mK<(1SZ0X~$SOsQBHcPrt>3vv<+zt@Y8 z$k!m1XZnI>cO^6B9}Cs1uCDqEDArcQ^3(;fw3ED@=ons9sM^;1MM(+rS7l;$%Fi@T zEZCfFb{!mCO@*0nn+_S%M9Rh+0xJhTQj6%@a_U4Uf~QwM24<0J5cEYv2w{XDF_sE% zC3wI?XW|?>TzDK(&`0#jaL{Uj@%?QeobYgGromqtS_p)#Xr6}(6<1X&m)gS}=ti1f_DKwi> zHWC`6QW5@Bh}s8~Csx0%r5mKdl;|8S>SOiM)&V(`rU#EJD}6&P(A~H% z_oBZd8-rD|LM5@`#20-#4))m2w;vTygI1-Zq5&@K&|&mdtueCWY_j_2g42#G=OJ}v zab8Jl0Ob%w>m}v^X>;oo#&BpiDCuO*hVSw1YVn}7l1xAq#FH+bM5L0vWq22r;DLkM zvEzzO6TeD_9MqmCP8s{QAv&Dil%U&c-;;Y*BrN|5M+ibod5qaZY-b21Iklgd=yLzI z`8F$d>s>)rz$zr?O-Vo_S~9ilD@i9noIoiYci`U9>}+B0<_o5IXjz%6fx>g);Bgth z2+sj8%Y4sBEtk16o6(4+5wI+Mhi46TRyQ3;H2z0RxKL|Bv+!~?B8 zQ@2bG+m64J)3(_qWJ*Q{kLS0NCI>bJ3~0T$%BN}>k-fe-J)@%YrSv-?lIw&RD*U9B z>m+Ub9l@t$43Ww5YNXUkT`K-mOLy8D;*o~u@gy(cpZ)}H zls_?RO6fc=CKuCl@>5{m%O1p?cgY#2-FL^$k=MFZHB6;3Q6`t^tM54x$LnDbhMmjQ zTTnc4(49H<4bt}XoYI|w?5Kzo6YdFI&Z33ti;yKk!{hE{v|}STa?GcphTz{!OGSa`vi9Fr zL}}!HI)*M=o4u+%r^lh?206*lGTp7~>uXbRu!pNDMSqkHY;RB0DKsL%Hjowwk=VMm zYb?DU);!}mj;qk$itqjh!R`K2SXs5zDZx!b&+0>`%xbcgDw;afv?0tZv>aulNkFhM zk7$_~D()W>(5wW^oe$nc!lhW$TO5QIa*K))!`B#w_L|yyYu&*yxK?S zygnvqmee6xJkd9Mncc_G=tXUZOU()+W4c%u?RkRCh?d^R{tK7SLDP+)U>?i036@Tl={n0y^_u)D{!B~XHjNp`S2-hq2 z`nxj8*2bQL?G@#g&ZF(OMf{#66sz>CCS)q$LhX|I05(I74aH!Mc$FPeYYj4rcZV6- z`CBaf1t%o%wijS!98Hax#l+-ti)^GJvhv{LJRONrRxw!ni7BEi=<+=!OI}+d9}a=7 zZRJgfx6HOrKMsy7VcE;Q5m!#n7lLrZwqdvF0v8cbn#XWrc|nL2|8UHRzF}!gzD}&B zWI$mNqxKmO+cfS9TL^TVh?$XtowuO3!h7 zwu)?gKW-Azlt3*)TYj*EK}j@{nU#i#=8`xiz2~6XOO)h+>&9-3hn9(vDd<0fVYaa^ z9nawu8lM)Znf#6gRu65tUY*VY7E+aMny?eQ>nvLo%+_S%DkpHePG0z2elgxdB@qE4xHCh_3V4fp&qB%zHe_8f7T*b=PObEMZ!s>U}j;Z7=1pN9UxxX5EVK zLIU5ocCX4${((R!5>z>Q-(Q6fS2PhunP)kXPwKmX&P#3l)|$?i{KG30k2j3k3LBe0 za*4b8`CC|SU*2O5StN&)G0bP`t~(ZA2pXK3dYF;juzNj`kUw>oV)8whwz%wc4G3*A zS!|XT!>oS@$f&LMwP_~)V(|>ac4%FOl^8qt*czl3;wu6n63k2J=_5 z2WBISk=@M2s@?|YTL{0bS&F9b=aD{Zm4)*_fy;pk{S-XpA*gx=GAXa~&g8GOx zf)UW}WDr!ta?Icj9p9j>#m$Ksw{M=5AQ~A=w7amkCHfxG2aU z_s)GiFlA5?H)Q260Bgjzqp_|&CR0Stca$`QZAYNPjc;s?#;2Q$DfasTH3@4=Kv2GQQO;l{PWSWc=#ycQZ5T6&aI&#q1$>=6Y`N zU=u6C*X3j>ybV?QWh_f5%DjA&)%-jE%UCy^BU^v8ZQ!5qCc+fjKNa5TdZ#tWSm_F1 zZIO3+_#pz>=m>L zK7$;ed1__l3H<1bt1+VkT^Ld^Vdely+zl5wYA<)o%1X>6=T-{qBcn2E&FF(mQ%ux6pS|q^fgsj*gx*EuOYuOkNN3Hidp@2X2^b}yd&DTn0hpG_vt=2 z_7fLw1s-+U)a~Rk)1X?>8H<|D&gk`~~czC!%BWerCDUew$|F-{VG`TpI3EK@#j}Hpj zNDNx&HpFV6+vcl&j_XZxCFvqC2X0vv6;);aY)WD63!X%6`2{x!FG)yxC|I{e>WZ06 zI+M_7i2p#twT+eZasD|P+uPr)7{T){-Fjp#dm|1uTK25s9(v7IBy9qn4Tn3L*IIZ+heyqDM0!Bpd0Y=Zj%^S?(Wh5&zH$b zu@mx=Mg-9dZBTnL3%svAGHu&+E)=f86cOV@;+G_-!U%B?h3IcwjpGrmbz*v+r2 z#rV;@@yzA*Hc5?c{ncw|5mdD8y3iLhArtC;^aOMC5;^HEMXvb^0D3)N+fmg?@{6+}H_-p8j0X!+oPHcwS8HM9%Ur0x|8z8S1H-uCV_8qgi0 zHbD-99M9x!^C{FOaTCJLi9CCIZf8r(R^pb?!5VZTU^|%&>OgBF5r2(1VkRO5_|3t# zwvS$Yk>d*)y%UdJ3Ds4THAjEe()c_(MYwbrYRqPTyy%7ai5UC^oH@E21Z`<~rQ>n8 zj6Ixmo)?aQv+)RgF&I_ez1zqBy3|jB7Ox>dezP#Gg8;=OPC-rOE^-+ch+w}HQm-@} zKNGhdmcN(meP=C zGD^CyAP02SXBk zPr^`#HdsW%rIm1XE_Pkob)n+%UqEeLu8Ds0E#BEhbM#WqE$sSebl>b^FJCsNN+F+++|#@(fOrgS!&6?&x`xRnM{jGiGzZ!uNNpT zlY&%-%N2QvoC!e;xsk=GTEHt&i(1;nP0}byy#O>yv#}grg4QoFV^?ynyI1RGHg%oa zigDdT>U>hAQE?&Kb}X1$>Nj2{YH5N{V-x5S4vc5A&%s2Pb$|~_1KBnw@Y%Ub%#hpwntqn+|HgFc2^tEza_Ae z)5(%}yDgo{-1Y=^dTIr!XqGyaA1mH0ilK$$WITatHK>1>D(0TGi1kpVj&VCUCCqBO z_ccmcz3KK9n-2Z*N;AeyblVl;DvJ}ZJ?Ws6bYv9-xqM)5-^A$6BNi^(=O5Y7UAZs}gO5@H$K+jGn! z@i#3_?(~S$CFKbu{squow9B$=W)I&Y#`BZ@Nz-2z%Xu+}1t|dGiEwpyjpau9Uq^z3 zDgv*Y&$|S&x-5Y>rT*Wae@*LGa!@CszI$1F8#8vn)Z{ABx(WXMp8ws1JLnK)lWiKw zQ}>Ni&0v1r2;qdlXEb<4WO8n))mC{Hi$^H@(*fz@JdCl`D{CWtWJ0=TAg+2cqD(}d zlOy2rE$OJ9s@gA6^Kea!yCSw7iVc;R1Vg`LI<*mpH?Nc(I@w+fEr}RBS86cGRJLki zsDw>PJ#Q6!=ez|f>Rg&Z{-f&|#)hs%8z>;4jyOORYpOG+C><8*(^5i$+ zAEJO&F3OC<)k)A*q+fP;^%C#dl}a1F6`?@QXS!4*J^D2BrWE)NEr}kx&x+;`7vF%u zZtm&oCG!@~;YK2Y2S$G}M4`mtANoy0@|8e1t$%}WniSi1UvL`T(caX~ir5D{PI{e! zb$%q3y&g$2kJV2xF1Y_VbaSHRm9L#Wtjf;XZI10U6@KSdT(K`qFHxMbkygM^3bT|5 zJpyiU9Unnumm`@3wW}{Wcp2JEl!Rp26@R9qq$riyI?mwe@IO|gw>h%W84Kt&l((FL zbgdhLB9~x)>M^{HbEU={ZJeEXl*zPdF+018T$QY^`mY0{m*hAsEWhhg`cxUt%>BnyT|y^FM&gQd$)C$ ziI`Vag14kW$xNndg~2XZRo~YQw$!$C;{}9DmSVaF6@zt0g7lE==&(-^^JZsniYuHE zi>6@EAqjs$L%2l=6lwo{GCI5<3`+R`PZ$)Xbw2I<3y?{CQ-^j+op9R0^<=ukP+h6( z!nhE=5M%U}6T`e`E9L@g*v}B;FTsUZSY=NA1GpDTfR*1K}!Z zu4ioly>mX06xsT_$<=2maq}LaY<@}NVZV&qJkhM$y&B|oYFQA+mje}1)~%HaoBc(G zKNo$D(>4;_=9u&=bODvi+WPmeJm-E9UR5@k3+Gh-1?10h4+a(yo-1O-6Y89kZGF~3 zSNu#u7G>zJ2w#(%lOpFvh$C1eXos-aL-nuBc>8eOP-{WCgQI(`O5udkR8Vx)6ykBm zr6RX3!5=|^8Z*#w9YgR`rV;gS6(tbWb-whJG4E36S{h$OK zL!ZkP6&%v6{d(?$5a;4FwJEd(MSB!C|H<%{A^Hn&%-pu)Afa@vs}lUyWnOvPZ<3Gt zcyX&wZuMCU(W#!|JR49v#^qhVu=KxSe%2o6FMSkY>f&SaQGAkMM)-4gcWXN5T{GL< zv8G52@zxMWEnXtQT9+U4Y4iuFF*c1Ozt$)lTiwX7w#(?Pjbq+b&Yj<6L7;%gt%}=Xdg~${KKQ z$~h)}dx=Q=;7%SA$*ktNDEbTV@K{5666?zd_N2hklyC>Xr|ez2Jz5Kc_BU9(>*Clk zL8q@Xw;prY50Cp0C&=2D5D}=Ox4d)V30#w%-WXn2C4EyRlGPRr9R1ZO=RB>&R zXi^3KmBj8r-m&6+$ilzW{OdOS{{r3(bf{(L$L8D#ehNLm*6{_JfNv-(SG;WFLE(qR;yP5OMQk;T^|L(dFiok1Nq9 z-@O?7+Z5<8s^Gas#sbMueR9Vw(1fr$havWuzmN}y+^vC#{$IdtK%IX?G@^NYw1_7w zqahZW9?&j4NFU}S>-plb!fR4z^Z8>*!4{77alJOYXTcV-q1= z)_i?I)8XjmgI6&sLtLq$zKU2}NDwusflOxhGg-Y`JXQ(R>7*5szm1LX%+rPE?rIJ5or6ew5=k>2F-;9WfGr7pzP@xWo{=EGMU_ z2|j|FMMYd_>Hb%WY zcJ4phqi~S{mH(+f@*o4G82|TrV)c*#A^+K5%J0a40_Oj1AFf0O{9_&d|M%;89vSct GQT=}w&%D0? literal 0 HcmV?d00001 diff --git a/tests/data/nvgesture/sk_depth.avi b/tests/data/nvgesture/sk_depth.avi new file mode 100644 index 0000000000000000000000000000000000000000..c8c32e54d67b217065d3283ca212b5e1b38c1626 GIT binary patch literal 43134 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 U`3eNu$g;edUrq#pHoU6;1E*lc?*IS* literal 0 HcmV?d00001 diff --git a/tests/data/nvgesture/test_nvgesture.lst b/tests/data/nvgesture/test_nvgesture.lst new file mode 100644 index 0000000000..b5ca38e9ad --- /dev/null +++ b/tests/data/nvgesture/test_nvgesture.lst @@ -0,0 +1 @@ +path:./ depth:sk_depth:2:19 color:sk_color:1:18 duo_left:duo_left:169:262 label:5 \ No newline at end of file diff --git a/tests/test_apis/test_inference.py b/tests/test_apis/test_inference.py index a8b4005fed..b8088951d0 100644 --- a/tests/test_apis/test_inference.py +++ b/tests/test_apis/test_inference.py @@ -3,10 +3,12 @@ import os.path as osp from glob import glob +import json_tricks as json import mmcv import numpy as np from mmpose.apis import (collect_multi_frames, inference_bottom_up_pose_model, + inference_gesture_model, inference_top_down_pose_model, init_pose_model, process_mmdet_results, vis_pose_result) from mmpose.datasets import DatasetInfo @@ -306,3 +308,24 @@ def test_collect_multi_frames(): _ = collect_multi_frames(video, frame_id, indices, online=True) _ = collect_multi_frames(video, frame_id, indices, online=False) + + +def test_hand_gesture_demo(): + + # build the pose model from a config file and a checkpoint file + pose_model = init_pose_model( + 'configs/hand/gesture_sview_rgbd_vid/mtut/nvgesture/' + 'i3d_nvgesture_bbox_112x112_fps15.py', + None, + device='cpu') + + dataset_info = pose_model.cfg.data['test'].get('dataset_info', None) + video_files = [ + 'tests/data/nvgesture/sk_color.avi', + 'tests/data/nvgesture/sk_depth.avi' + ] + with open('tests/data/nvgesture/bboxes.json', 'r') as f: + bbox = next(iter(json.load(f).values())) + + pred_label, _ = inference_gesture_model(pose_model, video_files, bbox, + dataset_info) diff --git a/tests/test_backbones/test_i3d.py b/tests/test_backbones/test_i3d.py new file mode 100644 index 0000000000..8c6e68f745 --- /dev/null +++ b/tests/test_backbones/test_i3d.py @@ -0,0 +1,21 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import torch + +from mmpose.models.backbones import I3D + + +def test_i3d_backbone(): + """Test I3D backbone.""" + model = I3D() + model.train() + + vids = torch.randn(1, 3, 16, 112, 112) + feat = model(vids) + assert feat.shape == (1, 1024, 2, 3, 3) + + model = I3D(expansion=0.5) + model.train() + + vids = torch.randn(1, 3, 32, 224, 224) + feat = model(vids) + assert feat.shape == (1, 512, 4, 7, 7) diff --git a/tests/test_datasets/test_gesture_dataset.py b/tests/test_datasets/test_gesture_dataset.py new file mode 100644 index 0000000000..75d456d129 --- /dev/null +++ b/tests/test_datasets/test_gesture_dataset.py @@ -0,0 +1,63 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import copy + +import pytest +import torch +from mmcv import Config +from numpy.testing import assert_almost_equal + +from mmpose.datasets import DATASETS + + +def test_NVGesture_dataset(): + + dataset = 'NVGestureDataset' + dataset_info = Config.fromfile( + 'configs/_base_/datasets/nvgesture.py').dataset_info + + dataset_class = DATASETS.get(dataset) + + data_cfg = dict( + video_size=[320, 240], + modality=['rgb', 'depth'], + bbox_file='tests/data/nvgesture/bboxes.json', + ) + + # Test + data_cfg_copy = copy.deepcopy(data_cfg) + _ = dataset_class( + ann_file='tests/data/nvgesture/test_nvgesture.lst', + vid_prefix='tests/data/nvgesture/', + data_cfg=data_cfg_copy, + pipeline=[], + dataset_info=dataset_info, + test_mode=True) + + custom_dataset = dataset_class( + ann_file='tests/data/nvgesture/test_nvgesture.lst', + vid_prefix='tests/data/nvgesture/', + data_cfg=data_cfg_copy, + pipeline=[], + dataset_info=dataset_info, + test_mode=False) + + assert custom_dataset.dataset_name == 'nvgesture' + assert custom_dataset.test_mode is False + assert len(custom_dataset) == 1 + sample = custom_dataset[0] + + # make pseudo prediction for evaluation + sample['logits'] = { + modal: torch.zeros(1, 25, 1) + for modal in sample['modality'] + } + sample['logits']['rgb'][:, sample['label']] = 1 + sample['logits']['depth'][:, (sample['label'] + 1) % 25] = 1 + sample['label'] = torch.tensor([sample['label']]).long() + infos = custom_dataset.evaluate([sample], metric=['AP']) + assert_almost_equal(infos['AP_rgb'], 1.0) + assert_almost_equal(infos['AP_depth'], 0.0) + assert_almost_equal(infos['AP_mean'], 0.5) + + with pytest.raises(KeyError): + infos = custom_dataset.evaluate([sample], metric='mAP') diff --git a/tests/test_models/test_gesture_forward.py b/tests/test_models/test_gesture_forward.py new file mode 100644 index 0000000000..8c83278c0a --- /dev/null +++ b/tests/test_models/test_gesture_forward.py @@ -0,0 +1,58 @@ +# Copyright (c) OpenMMLab. All rights reserved. + +import torch +from addict import Dict + +from mmpose.models.detectors import GestureRecognizer + + +def test_gesture_recognizer_forward(): + model_cfg = dict( + type='GestureRecognizer', + pretrained=None, + modality=['rgb', 'depth'], + backbone=dict( + rgb=dict( + type='I3D', + in_channels=3, + expansion=0.25, + ), + depth=dict( + type='I3D', + in_channels=1, + expansion=0.25, + ), + ), + cls_head=dict( + type='MultiModalSSAHead', + num_classes=25, + avg_pool_kernel=(1, 2, 2), + in_channels=256), + train_cfg=dict( + beta=2, + lambda_=1e-3, + ssa_start_epoch=10, + ), + test_cfg=dict(), + ) + + detector = GestureRecognizer(model_cfg['backbone'], None, + model_cfg['cls_head'], model_cfg['train_cfg'], + model_cfg['test_cfg'], model_cfg['modality'], + model_cfg['pretrained']) + detector.set_train_epoch(11) + + video = [torch.randn(1, 3, 16, 112, 112), torch.randn(1, 1, 16, 112, 112)] + labels = torch.tensor([1]).long() + img_metas = Dict() + img_metas.data = dict(modality=['rgb', 'depth']) + + # Test forward train + losses = detector.forward(video, labels, img_metas, return_loss=True) + assert isinstance(losses, dict) + assert 'ssa_loss' in losses + + # Test forward test + with torch.no_grad(): + _ = detector.forward( + video, labels, img_metas=img_metas, return_loss=False) diff --git a/tests/test_models/test_gesture_head.py b/tests/test_models/test_gesture_head.py new file mode 100644 index 0000000000..b5a1339001 --- /dev/null +++ b/tests/test_models/test_gesture_head.py @@ -0,0 +1,38 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import numpy as np +import torch + +from mmpose.models import MultiModalSSAHead + + +def test_multi_modal_ssa_head(): + + # substantialize head + train_cfg = dict(ssa_start_epoch=10) + head = MultiModalSSAHead( + num_classes=25, modality=('rgb', 'depth'), train_cfg=train_cfg) + + head.set_train_epoch(11) + assert head._train_epoch == 11 + assert head._train_epoch > head.start_epoch + + # forward + img_metas = dict(modality=['rgb', 'depth']) + feats = [torch.randn(2, 1024, 7, 7, 7) for _ in img_metas['modality']] + labels = torch.randint(25, (2, )) + + logits = head(feats, img_metas) + assert logits[0].shape == (2, 25, 7) + + losses = head.get_loss(logits, labels, feats) + assert 'ce_loss' in losses + assert 'ssa_loss' in losses + assert (losses['ssa_loss'] == losses['ssa_loss']).all() # check nan + + logits[0][0, 1], logits[1][0, 1], labels[0] = 1e5, 1e5, 1 + logits[0][1, 4], logits[1][1, 8], labels[1] = 1e5, 1e5, 8 + accuracy = head.get_accuracy(logits, labels, img_metas) + assert 'acc_rgb' in accuracy + assert 'acc_depth' in accuracy + np.testing.assert_almost_equal(accuracy['acc_rgb'], 0.5) + np.testing.assert_almost_equal(accuracy['acc_depth'], 1.0) diff --git a/tests/test_pipelines/test_gesture_pipelines.py b/tests/test_pipelines/test_gesture_pipelines.py new file mode 100644 index 0000000000..34b7b06ece --- /dev/null +++ b/tests/test_pipelines/test_gesture_pipelines.py @@ -0,0 +1,181 @@ +# Copyright (c) OpenMMLab. All rights reserved. +import copy +import os.path as osp +from collections import defaultdict + +import json_tricks as json +import numpy as np +from numpy.testing import assert_array_almost_equal + +from mmpose.datasets.pipelines import (CenterSpatialCrop, CropValidClip, + GestureRandomFlip, LoadVideoFromFile, + ModalWiseChannelProcess, + MultiFrameBBoxMerge, + MultiModalVideoToTensor, + RandomAlignedSpatialCrop, + ResizedCropByBBox, ResizeGivenShortEdge, + TemporalPooling, VideoNormalizeTensor) + + +def _check_flip(origin_vid, result_vid): + """Check if the origin_video are flipped correctly.""" + l, h, w, c = origin_vid.shape + + for t in range(l): + for i in range(h): + for j in range(w): + for k in range(c): + if result_vid[t, i, j, k] != origin_vid[t, i, w - 1 - j, + k]: + return False + return True + + +def _check_num_frames(video_results, num_frame): + """Check if the video lengths match the given number of frames.""" + if video_results['num_frames'] != num_frame: + return False + if 'bbox' in video_results and len(video_results['bbox']) != num_frame: + return False + for video in video_results['video']: + if video.shape[0] != num_frame: + return False + return True + + +def _check_size(video_results, size): + """Check if the video sizes and size attributes match the given size.""" + for h in video_results['height']: + if h != size[0]: + return False + for w in video_results['width']: + if w != size[1]: + return False + for video in video_results['video']: + if video.shape[1] != size[0]: + return False + if video.shape[2] != size[1]: + return False + return True + + +def _check_normalize(origin_video, result_video, norm_cfg): + """Check if the origin_video are normalized correctly into result_video in + a given norm_cfg.""" + target_video = result_video.clone() + for i in range(3): + target_video[i] *= norm_cfg['std'][i] + target_video[i] += norm_cfg['mean'][i] + assert_array_almost_equal(origin_video, target_video, decimal=4) + + +def test_gesture_pipeline(): + # test loading + data_prefix = 'tests/data/nvgesture' + + results = defaultdict(list) + results['modality'] = ['rgb', 'depth'] + results['label'] = 4 + with open(osp.join(data_prefix, 'bboxes.json'), 'r') as f: + results['bbox'] = next(iter(json.load(f).values())) + results['ann_info'] = dict(flip_pairs=((0, 1), (4, 5), (19, 20))) + + results['video_file'] = [ + osp.join(data_prefix, 'sk_color.avi'), + osp.join(data_prefix, 'sk_depth.avi') + ] + transform = LoadVideoFromFile() + results = transform(copy.deepcopy(results)) + + assert results['video'][0].shape == (20, 240, 320, 3) + assert results['video'][1].shape == (20, 240, 320, 3) + + # test CropValidClip + results['valid_frames'] = ((2, 19), (1, 18)) + transform = CropValidClip() + results_valid = transform(copy.deepcopy(results)) + assert _check_num_frames(results_valid, 17) + assert (results_valid['video'][0] == results['video'][0][2:19]).all() + assert (results_valid['video'][1] == results['video'][1][1:18]).all() + + # test TemporalPooling + transform = TemporalPooling(ref_fps=15) + results_temp_pool = transform(copy.deepcopy(results_valid)) + assert _check_num_frames(results_temp_pool, 9) + + transform = TemporalPooling(length=10) + results_temp_pool = transform(copy.deepcopy(results_valid)) + assert _check_num_frames(results_temp_pool, 10) + del results_temp_pool + + # test ResizeGivenShortEdge + transform = ResizeGivenShortEdge(length=256) + results_resize = transform(copy.deepcopy(results_valid)) + assert _check_size(results_resize, (256, 341)) + del results_resize + + # test MultiFrameBBoxMerge + transform = MultiFrameBBoxMerge() + results_bbox_merge = transform(copy.deepcopy(results_valid)) + target_bbox = np.array([96.07688, 92.26083, 316.5224, + 231.98422]).astype(np.float32) + assert_array_almost_equal(results_bbox_merge['bbox'], target_bbox, 4) + + # test ResizedCropByBBox + transform = ResizedCropByBBox( + size=112, scale=(0.8, 1.2), ratio=(0.8, 1.2), shift=0.3) + results_resize_crop = transform(copy.deepcopy(results_bbox_merge)) + assert _check_size(results_resize_crop, (112, 112)) + del results_bbox_merge + + # test GestureRandomFlip + transform = GestureRandomFlip(prob=1.0) + results_flip = transform(copy.deepcopy(results_resize_crop)) + assert results_flip['label'] == 5 + assert _check_size(results_flip, (112, 112)) + assert _check_flip(results_flip['video'][0], + results_resize_crop['video'][0]) + assert _check_flip(results_flip['video'][1], + results_resize_crop['video'][1]) + del results_resize_crop + + # test RandomAlignedSpatialCrop & CenterSpatialCrop + transform = RandomAlignedSpatialCrop(length=112) + results_crop = transform(copy.deepcopy(results_valid)) + assert _check_size(results_crop, (112, 112)) + + transform = CenterSpatialCrop(length=112) + results_crop = transform(copy.deepcopy(results_valid)) + assert _check_size(results_crop, (112, 112)) + del results_crop + + # test ModalWiseChannelProcess + transform = ModalWiseChannelProcess() + results_modal_proc = transform(copy.deepcopy(results_valid)) + for i, modal in enumerate(results_modal_proc['modality']): + if modal == 'rgb': + assert_array_almost_equal( + results_modal_proc['video'][i][..., ::-1], + results_valid['video'][i]) + if modal == 'depth': + assert_array_almost_equal(results_modal_proc['video'][i], + results_valid['video'][i][..., :1]) + del results_valid + + # test MultiModalVideoToTensor + transform = MultiModalVideoToTensor() + results_tensor = transform(copy.deepcopy(results_modal_proc)) + for i, video in enumerate(results_tensor['video']): + assert video.max() <= 1.0 and video.min() >= 0.0 + assert video.shape[1:] == results_modal_proc['video'][i].shape[:-1] + assert video.shape[0] == results_modal_proc['video'][i].shape[-1] + del results_modal_proc + + # test VideoNormalizeTensor + norm_cfg = {} + norm_cfg['mean'] = [0.485, 0.456, 0.406] + norm_cfg['std'] = [0.229, 0.224, 0.225] + transform = VideoNormalizeTensor(**norm_cfg) + results_norm = transform(copy.deepcopy(results_tensor)) + _check_normalize(results_tensor['video'][0], results_norm['video'][0], + norm_cfg) diff --git a/tools/webcam/configs/hand_gesture/README.md b/tools/webcam/configs/hand_gesture/README.md new file mode 100644 index 0000000000..41ae6d33ab --- /dev/null +++ b/tools/webcam/configs/hand_gesture/README.md @@ -0,0 +1,26 @@ +# Hand bounding box with gesture label effects + +This demo provides hand gesture recognition visualization. The label of gesture is shown on the left-up corner of hand bounding box. + +Example TBA. + +## Instruction + +### Get started + +Launch the demo from the mmpose root directory: + +```shell +python tools/webcam/run_webcam.py --config tools/webcam/configs/hand_gesture/hand_bbox_with_gesture.py +``` + +### Hotkeys + +| Hotkey | Function | +| ------ | -------------------------------- | +| m | Show the monitoring information. | +| q | Exit. | + +### Configuration + +See the [README](/tools/webcam/configs/examples/README.md#configuration) for model configurations. diff --git a/tools/webcam/configs/hand_gesture/hand_bbox_with_gesture.py b/tools/webcam/configs/hand_gesture/hand_bbox_with_gesture.py new file mode 100644 index 0000000000..b8d9485432 --- /dev/null +++ b/tools/webcam/configs/hand_gesture/hand_bbox_with_gesture.py @@ -0,0 +1,59 @@ +# Copyright (c) OpenMMLab. All rights reserved. +runner = dict( + name='Gesture Recognition', + camera_id=0, + camera_fps=15, + synchronous=False, + buffer_sizes=dict(_input_=20, det_result=10), + nodes=[ + dict( + type='MultiFrameDetectorNode', + name='Detector', + model_config='demo/mmdetection_cfg/' + 'ssdlite_mobilenetv2_scratch_600e_onehand.py', + model_checkpoint='https://download.openmmlab.com/mmpose/' + 'mmdet_pretrained/' + 'ssdlite_mobilenetv2_scratch_600e_onehand-4f9f8686_20220523.pth', + device='cpu', + inference_frame='last', + input_buffer='_input_', + output_buffer='det_result'), + dict( + type='HandGestureRecognizerNode', + name='GestureRecognizer', + model_config='configs/hand/gesture_sview_rgbd_vid/mtut/' + 'nvgesture/i3d_nvgesture_bbox_112x112_fps15_rgb.py', + model_checkpoint='https://download.openmmlab.com/mmpose/' + 'gesture/mtut/i3d_nvgesture/' + 'i3d_nvgesture_bbox_112x112_fps15-363b5956_20220530.pth', + device='cpu', + input_buffer='det_result', + output_buffer='gesture', + fps=15, + score_thr=0.7), + dict( + type='ModelResultBindingNode', + name='ResultBinder', + frame_buffer='_frame_', + result_buffer='gesture', + output_buffer='frame'), + dict( + type='GestureVisualizerNode', + name='Visualizer', + enable_key='v', + frame_buffer='frame', + output_buffer='vis'), + dict( + type='MonitorNode', + name='Monitor', + enable_key='m', + enable=False, + frame_buffer='vis', + output_buffer='display'), + dict( + type='RecorderNode', + name='Recorder', + out_video_file='record.mp4', + frame_buffer='display', + output_buffer='_display_') + ]) diff --git a/tools/webcam/webcam_apis/nodes/__init__.py b/tools/webcam/webcam_apis/nodes/__init__.py index efdc63845e..3c8ee2db82 100644 --- a/tools/webcam/webcam_apis/nodes/__init__.py +++ b/tools/webcam/webcam_apis/nodes/__init__.py @@ -1,20 +1,22 @@ # Copyright (c) OpenMMLab. All rights reserved. from .builder import NODES from .faceswap_node import FaceSwapNode -from .frame_effect_node import (BackgroundNode, BugEyeNode, MoustacheNode, +from .frame_effect_node import (BackgroundNode, BugEyeNode, + GestureVisualizerNode, MoustacheNode, NoticeBoardNode, PoseVisualizerNode, SaiyanNode, SunglassesNode) from .helper_node import ModelResultBindingNode, MonitorNode, RecorderNode -from .mmdet_node import DetectorNode -from .mmpose_node import TopDownPoseEstimatorNode +from .mmdet_node import DetectorNode, MultiFrameDetectorNode +from .mmpose_node import HandGestureRecognizerNode, TopDownPoseEstimatorNode from .pose_tracker_node import PoseTrackerNode from .valentinemagic_node import ValentineMagicNode from .xdwendwen_node import XDwenDwenNode __all__ = [ - 'NODES', 'PoseVisualizerNode', 'DetectorNode', 'TopDownPoseEstimatorNode', - 'MonitorNode', 'BugEyeNode', 'SunglassesNode', 'ModelResultBindingNode', - 'NoticeBoardNode', 'RecorderNode', 'FaceSwapNode', 'MoustacheNode', - 'SaiyanNode', 'BackgroundNode', 'XDwenDwenNode', 'ValentineMagicNode', - 'PoseTrackerNode' + 'NODES', 'PoseVisualizerNode', 'DetectorNode', 'MultiFrameDetectorNode', + 'TopDownPoseEstimatorNode', 'MonitorNode', 'BugEyeNode', 'SunglassesNode', + 'ModelResultBindingNode', 'NoticeBoardNode', 'RecorderNode', + 'FaceSwapNode', 'MoustacheNode', 'SaiyanNode', 'BackgroundNode', + 'XDwenDwenNode', 'ValentineMagicNode', 'GestureVisualizerNode', + 'HandGestureRecognizerNode', 'PoseTrackerNode' ] diff --git a/tools/webcam/webcam_apis/nodes/frame_effect_node.py b/tools/webcam/webcam_apis/nodes/frame_effect_node.py index c248c3820a..8cd5fe15a6 100644 --- a/tools/webcam/webcam_apis/nodes/frame_effect_node.py +++ b/tools/webcam/webcam_apis/nodes/frame_effect_node.py @@ -915,3 +915,89 @@ def draw(self, frame_msg): self.num_frames * self.frame_period) return canvas + + +@NODES.register_module() +class GestureVisualizerNode(FrameDrawingNode): + """Draw the bbox and gesture recognition results. + + Args: + name (str, optional): The node name (also thread name). + frame_buffer (str): The name of the input buffer. + output_buffer (str|list): The name(s) of the output buffer(s). + enable_key (str|int, optional): Set a hot-key to toggle enable/disable + of the node. If an int value is given, it will be treated as an + ascii code of a key. Please note: + 1. If enable_key is set, the bypass method need to be + overridden to define the node behavior when disabled + 2. Some hot-key has been use for particular use. For example: + 'q', 'Q' and 27 are used for quit + Default: None + enable (bool): Default enable/disable status. Default: True. + bbox_color (str|tuple|dict): If a single color (a str like 'green' or + a tuple like (0, 255, 0)), it will used to draw the bbox. + Optionally, a dict can be given as a map from class labels to + colors. + """ + + default_bbox_color = { + 'hand': (148, 139, 255), + } + + def __init__(self, + name: str, + frame_buffer: str, + output_buffer: Union[str, List[str]], + enable_key: Optional[Union[str, int]] = None, + enable: bool = True, + bbox_color: Optional[Union[str, Tuple, Dict]] = None): + + super().__init__(name, frame_buffer, output_buffer, enable_key, enable) + + if bbox_color is None: + self.bbox_color = self.default_bbox_color + elif isinstance(bbox_color, dict): + self.bbox_color = {k: color_val(v) for k, v in bbox_color.items()} + else: + self.bbox_color = color_val(bbox_color) + + def draw(self, frame_msg): + canvas = frame_msg.get_image() + pose_results = frame_msg.get_pose_results() + + if not pose_results: + return canvas + + for pose_result in frame_msg.get_pose_results(): + + # Extract bboxes and poses + bbox_preds = [] + bbox_labels = [] + for pred in pose_result['preds']: + if 'bbox' in pred: + bbox_preds.append(pred['bbox']) + bbox_labels.append(pred.get('label', None)) + + # Get bbox colors + if isinstance(self.bbox_color, dict): + bbox_colors = [ + self.bbox_color.get(label, (0, 255, 0)) + for label in bbox_labels + ] + else: + bbox_labels = self.bbox_color + + # Draw bboxes + if bbox_preds: + bboxes = np.vstack(bbox_preds) + + imshow_bboxes( + canvas, + bboxes, + labels=bbox_labels, + colors=bbox_colors, + text_color='white', + font_scale=0.5, + show=False) + + return canvas diff --git a/tools/webcam/webcam_apis/nodes/mmdet_node.py b/tools/webcam/webcam_apis/nodes/mmdet_node.py index aaeeb15872..4e1b7e1efb 100644 --- a/tools/webcam/webcam_apis/nodes/mmdet_node.py +++ b/tools/webcam/webcam_apis/nodes/mmdet_node.py @@ -1,8 +1,10 @@ # Copyright (c) OpenMMLab. All rights reserved. from typing import List, Optional, Union +import numpy as np + from .builder import NODES -from .node import Node +from .node import MultiInputNode, Node try: from mmdet.apis import inference_detector, init_detector @@ -32,7 +34,6 @@ def __init__(self, self.model_config = model_config self.model_checkpoint = model_checkpoint self.device = device.lower() - # Init model self.model = init_detector( self.model_config, self.model_checkpoint, device=self.device) @@ -87,3 +88,58 @@ def _post_process(self, preds): result['preds'].extend(preds_i) return result + + +@NODES.register_module() +class MultiFrameDetectorNode(DetectorNode, MultiInputNode): + """Detect hand with one frame in a video clip. The length of clip is + decided on the frame rate and the inference speed of detector. + + Parameters: + inference_frame (str): indicate the frame selected in a clip to run + detect hand. Can be set to ('begin', 'mid', 'last'). + Default: 'mid'. + """ + + def __init__(self, + name: str, + model_config: str, + model_checkpoint: str, + input_buffer: str, + output_buffer: Union[str, List[str]], + inference_frame: str = 'mid', + enable_key: Optional[Union[str, int]] = None, + device: str = 'cuda:0'): + DetectorNode.__init__( + self, + name, + model_config, + model_checkpoint, + input_buffer, + output_buffer, + enable_key, + device=device) + self.inference_frame = inference_frame + + def process(self, input_msgs): + """Select frame and detect hand.""" + input_msg = input_msgs['input'] + if self.inference_frame == 'last': + key_frame = input_msg[-1] + elif self.inference_frame == 'mid': + key_frame = input_msg[len(input_msg) // 2] + elif self.inference_frame == 'begin': + key_frame = input_msg[0] + else: + raise ValueError(f'Invalid inference_frame {self.inference_frame}') + + img = key_frame.get_image() + + preds = inference_detector(self.model, img) + det_result = self._post_process(preds) + + imgs = [frame.get_image() for frame in input_msg] + key_frame.set_image(np.stack(imgs, axis=0)) + + key_frame.add_detection_result(det_result, tag=self.name) + return key_frame diff --git a/tools/webcam/webcam_apis/nodes/mmpose_node.py b/tools/webcam/webcam_apis/nodes/mmpose_node.py index 41000e0744..988bec41b2 100644 --- a/tools/webcam/webcam_apis/nodes/mmpose_node.py +++ b/tools/webcam/webcam_apis/nodes/mmpose_node.py @@ -1,12 +1,15 @@ # Copyright (c) OpenMMLab. All rights reserved. from dataclasses import dataclass -from typing import List, Optional, Union +from typing import Dict, List, Optional, Union -from mmpose.apis import (get_track_id, inference_top_down_pose_model, - init_pose_model) +import numpy as np + +from mmpose.apis import (get_track_id, inference_gesture_model, + inference_top_down_pose_model, init_pose_model) from mmpose.core import Smoother +from ..utils import Message from .builder import NODES -from .node import Node +from .node import MultiInputNode, Node @dataclass @@ -120,3 +123,170 @@ def process(self, input_msgs): input_msg.add_pose_result(pose_result, tag=self.name) return input_msg + + +@NODES.register_module() +class HandGestureRecognizerNode(TopDownPoseEstimatorNode, MultiInputNode): + + def __init__(self, + name: str, + model_config: str, + model_checkpoint: str, + input_buffer: str, + output_buffer: Union[str, List[str]], + enable_key: Optional[Union[str, int]] = None, + enable: bool = True, + device: str = 'cuda:0', + cls_ids: Optional[List] = None, + cls_names: Optional[List] = None, + bbox_thr: float = 0.5, + min_frame: int = 16, + fps: int = 30, + score_thr: float = 0.7): + TopDownPoseEstimatorNode.__init__( + self, + name=name, + model_config=model_config, + model_checkpoint=model_checkpoint, + input_buffer=input_buffer, + output_buffer=output_buffer, + enable_key=enable_key, + enable=enable, + device=device, + cls_ids=cls_ids, + cls_names=cls_names, + bbox_thr=bbox_thr) + + # item of _clip_buffer: (clip message, num of frames) + self._clip_buffer = [] + self.score_thr = score_thr + self.min_frame = min_frame + self.fps = fps + + @property + def totol_clip_length(self): + return sum([clip[1] for clip in self._clip_buffer]) + + def _add_clips(self, clips: List[Message]): + """Push the newly loaded clips from buffer, and discard old clips.""" + for clip in clips: + clip_length = clip.get_image().shape[0] + self._clip_buffer.append((clip, clip_length)) + total_length = 0 + for i in range(-2, -len(self._clip_buffer) - 1, -1): + total_length += self._clip_buffer[i][1] + if total_length >= self.min_frame: + self._clip_buffer = self._clip_buffer[i:] + break + + def _merge_clips(self): + """Concat the clips into a longer video, and gather bboxes.""" + videos = [clip[0].get_image() for clip in self._clip_buffer] + video = np.concatenate(videos) + det_results_lst = [ + clip[0].get_detection_results() for clip in self._clip_buffer + ] + bboxes = [ + self._process_clip_bbox(det_results) + for det_results in det_results_lst + ] + bboxes = list(filter(len, bboxes)) + return video, bboxes + + def _process_clip_bbox(self, det_results: List[Dict]): + """Filter and merge the bboxes of a video.""" + full_det_preds = [] + for det_result in det_results: + det_preds = det_result['preds'] + if self.cls_ids: + # Filter detection results by class ID + det_preds = [ + p for p in det_preds if p['cls_id'] in self.cls_ids + ] + elif self.cls_names: + # Filter detection results by class name + det_preds = [ + p for p in det_preds if p['label'] in self.cls_names + ] + if self.bbox_thr > 0: + det_preds = [ + p for p in det_preds if p['bbox'][-1] > self.bbox_thr + ] + full_det_preds.extend(det_preds) + + merged_bbox = self._merge_bbox(full_det_preds) + return merged_bbox + + def _merge_bbox(self, bboxes: List[Dict], ratio=0.5): + """Merge bboxes in a video to create a new bbox that covers the region + where hand moves in the video.""" + + if len(bboxes) <= 1: + return bboxes + + def compute_area(bbox): + area = abs(bbox['bbox'][2] - + bbox['bbox'][0]) * abs(bbox['bbox'][3] - + bbox['bbox'][1]) + return area + + bboxes.sort(key=lambda b: compute_area(b), reverse=True) + merged = False + for i in range(1, len(bboxes)): + small_area = compute_area(bboxes[i]) + x1 = max(bboxes[0]['bbox'][0], bboxes[i]['bbox'][0]) + y1 = max(bboxes[0]['bbox'][1], bboxes[i]['bbox'][1]) + x2 = min(bboxes[0]['bbox'][2], bboxes[i]['bbox'][2]) + y2 = min(bboxes[0]['bbox'][3], bboxes[i]['bbox'][3]) + area_ratio = (abs(x2 - x1) * abs(y2 - y1)) / small_area + if area_ratio > ratio: + bboxes[0]['bbox'][0] = min(bboxes[0]['bbox'][0], + bboxes[i]['bbox'][0]) + bboxes[0]['bbox'][1] = min(bboxes[0]['bbox'][1], + bboxes[i]['bbox'][1]) + bboxes[0]['bbox'][2] = max(bboxes[0]['bbox'][2], + bboxes[i]['bbox'][2]) + bboxes[0]['bbox'][3] = max(bboxes[0]['bbox'][3], + bboxes[i]['bbox'][3]) + merged = True + break + + if merged: + bboxes.pop(i) + return self._merge_bbox(bboxes, ratio) + else: + return [bboxes[0]] + + def process(self, input_msgs: Dict[str, Message]) -> Message: + """Load and process the clips with hand detection result, and recognize + the gesture.""" + + input_msg = input_msgs['input'] + self._add_clips(input_msg) + video, bboxes = self._merge_clips() + msg = input_msg[-1] + + gesture_result = { + 'preds': [], + 'model_cfg': self.model.cfg.copy(), + } + if self.totol_clip_length >= self.min_frame and len( + bboxes) > 0 and max(map(len, bboxes)) > 0: + # Inference gesture + pred_label, pred_score = inference_gesture_model( + self.model, + video, + bboxes=bboxes, + dataset_info=dict( + name='camera', fps=self.fps, modality=['rgb'])) + for i in range(len(pred_label)): + result = bboxes[-1][0].copy() + if pred_score[i] > self.score_thr: + label = pred_label[i].item() + label = self.model.cfg.dataset_info.category_info[label] + result['label'] = label + gesture_result['preds'].append(result) + + msg.add_pose_result(gesture_result, tag=self.name) + + return msg diff --git a/tools/webcam/webcam_apis/nodes/node.py b/tools/webcam/webcam_apis/nodes/node.py index 178d743316..fefab741be 100644 --- a/tools/webcam/webcam_apis/nodes/node.py +++ b/tools/webcam/webcam_apis/nodes/node.py @@ -370,3 +370,51 @@ def event_listener(): self._send_output_to_buffers(output_msg) logging.info(f'{self.name}: process ending.') + + +class MultiInputNode(Node): + """Base interface of functional module which accept multiple inputs.""" + + def _get_input_from_buffer(self) -> Tuple[bool, Optional[Dict]]: + """Get and pack input data if it's ready. The function returns a tuple + of a status flag and a packed data dictionary. If input_buffer is + ready, the status flag will be True, and the packed data is a dict + whose items are buffer names and corresponding messages (unready + additional buffers will give a `None`). Otherwise, the status flag is + False and the packed data is None. Notice that. + + Returns: + bool: status flag + dict[str, list[Message]]: the packed inputs where the key + is the buffer name and the value is the Message got from the + corresponding buffer. + """ + buffer_manager = self._buffer_manager + + if buffer_manager is None: + raise ValueError(f'{self.name}: Runner not set!') + + # Check that essential buffers are ready + for buffer_info in self._input_buffers: + if buffer_info.trigger and buffer_manager.is_empty( + buffer_info.buffer_name): + return False, None + + # Default input + result = { + buffer_info.input_name: None + for buffer_info in self._input_buffers + } + + for buffer_info in self._input_buffers: + + while not buffer_manager.is_empty(buffer_info.buffer_name): + if result[buffer_info.input_name] is None: + result[buffer_info.input_name] = [] + result[buffer_info.input_name].append( + buffer_manager.get(buffer_info.buffer_name, block=False)) + + if buffer_info.trigger and result[buffer_info.input_name] is None: + return False, None + + return True, result