From 1947dfa51f74f3b4aeb352dd1b7966e9e8e1fdd6 Mon Sep 17 00:00:00 2001
From: sunshangquan <527112517@qq.com>
Date: Thu, 18 Jul 2024 00:10:20 +0800
Subject: [PATCH] cm2
---
.gitignore | 2 +
Allweather/Datasets/README.md | 23 +
Allweather/Options/Allweather_Histoformer.yml | 155 +
Allweather/pretrained_models/README.md | 1 +
Allweather/test_histoformer.py | 95 +
Allweather/util.py | 90 +
INSTALL.md | 53 +
README.md | 87 +-
assets/example-Outdoor-Rain.png | Bin 0 -> 418128 bytes
assets/example-RainDrop.png | Bin 0 -> 274956 bytes
assets/histoformer.png | Bin 0 -> 1417125 bytes
basicsr/__pycache__/version.cpython-38.pyc | Bin 0 -> 246 bytes
basicsr/data/__init__.py | 126 +
.../data/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 3560 bytes
.../__pycache__/data_sampler.cpython-38.pyc | Bin 0 -> 2168 bytes
.../data/__pycache__/data_util.cpython-38.pyc | Bin 0 -> 12112 bytes
.../__pycache__/ffhq_dataset.cpython-38.pyc | Bin 0 -> 2576 bytes
.../paired_image_dataset.cpython-38.pyc | Bin 0 -> 9920 bytes
.../prefetch_dataloader.cpython-38.pyc | Bin 0 -> 4364 bytes
.../__pycache__/reds_dataset.cpython-38.pyc | Bin 0 -> 6529 bytes
.../single_image_dataset.cpython-38.pyc | Bin 0 -> 2627 bytes
.../__pycache__/transforms.cpython-38.pyc | Bin 0 -> 7869 bytes
.../video_test_dataset.cpython-38.pyc | Bin 0 -> 10434 bytes
.../vimeo90k_dataset.cpython-38.pyc | Bin 0 -> 4194 bytes
basicsr/data/data_sampler.py | 49 +
basicsr/data/data_util.py | 390 +
.../meta_info/meta_info_DIV2K800sub_GT.txt | 32592 ++++++++
.../meta_info/meta_info_REDS4_test_GT.txt | 4 +
basicsr/data/meta_info/meta_info_REDS_GT.txt | 270 +
.../meta_info_REDSofficial4_test_GT.txt | 4 +
.../meta_info_REDSval_official_test_GT.txt | 30 +
.../meta_info/meta_info_Vimeo90K_test_GT.txt | 7824 ++
.../meta_info_Vimeo90K_test_fast_GT.txt | 1225 +
.../meta_info_Vimeo90K_test_medium_GT.txt | 4977 ++
.../meta_info_Vimeo90K_test_slow_GT.txt | 1613 +
.../meta_info/meta_info_Vimeo90K_train_GT.txt | 64612 ++++++++++++++++
basicsr/data/paired_image_dataset.py | 372 +
basicsr/data/prefetch_dataloader.py | 126 +
basicsr/data/single_image_dataset.py | 67 +
basicsr/data/transforms.py | 275 +
basicsr/data/video_test_dataset.py | 325 +
basicsr/metrics/__init__.py | 4 +
.../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 313 bytes
.../__pycache__/metric_util.cpython-38.pyc | Bin 0 -> 1509 bytes
.../metrics/__pycache__/niqe.cpython-38.pyc | Bin 0 -> 6485 bytes
.../__pycache__/psnr_ssim.cpython-38.pyc | Bin 0 -> 7609 bytes
basicsr/metrics/fid.py | 102 +
basicsr/metrics/metric_util.py | 47 +
basicsr/metrics/niqe.py | 205 +
basicsr/metrics/niqe_pris_params.npz | Bin 0 -> 11850 bytes
basicsr/metrics/psnr_ssim.py | 303 +
basicsr/models/__init__.py | 42 +
.../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1249 bytes
.../__pycache__/base_model.cpython-38.pyc | Bin 0 -> 13366 bytes
.../image_restoration_model.cpython-38.pyc | Bin 0 -> 10594 bytes
.../__pycache__/lr_scheduler.cpython-38.pyc | Bin 0 -> 8466 bytes
basicsr/models/archs/__init__.py | 46 +
.../archs/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1435 bytes
.../histoformer_arch.cpython-38.pyc | Bin 0 -> 19105 bytes
.../__pycache__/restormer_arch.cpython-38.pyc | Bin 0 -> 18960 bytes
...tormer_arch.cpython-38.pyc.140248890974960 | 0
basicsr/models/archs/arch_util.py | 255 +
basicsr/models/archs/histoformer_arch.py | 623 +
basicsr/models/base_model.py | 392 +
basicsr/models/image_restoration_model.py | 370 +
basicsr/models/losses/__init__.py | 5 +
.../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 318 bytes
.../__pycache__/loss_util.cpython-38.pyc | Bin 0 -> 2717 bytes
.../losses/__pycache__/losses.cpython-38.pyc | Bin 0 -> 4329 bytes
basicsr/models/losses/loss_util.py | 95 +
basicsr/models/losses/losses.py | 122 +
basicsr/models/lr_scheduler.py | 232 +
basicsr/test.py | 64 +
basicsr/train.py | 381 +
basicsr/utils/__init__.py | 34 +
.../utils/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 947 bytes
.../__pycache__/create_lmdb.cpython-38.pyc | Bin 0 -> 3837 bytes
.../__pycache__/dist_util.cpython-38.pyc | Bin 0 -> 2611 bytes
.../__pycache__/file_client.cpython-38.pyc | Bin 0 -> 6571 bytes
.../utils/__pycache__/img_util.cpython-38.pyc | Bin 0 -> 6449 bytes
.../__pycache__/lmdb_util.cpython-38.pyc | Bin 0 -> 6368 bytes
.../utils/__pycache__/logger.cpython-38.pyc | Bin 0 -> 5740 bytes
.../matlab_functions.cpython-38.pyc | Bin 0 -> 10525 bytes
basicsr/utils/__pycache__/misc.cpython-38.pyc | Bin 0 -> 5355 bytes
.../utils/__pycache__/options.cpython-38.pyc | Bin 0 -> 3044 bytes
basicsr/utils/bundle_submissions.py | 108 +
basicsr/utils/create_lmdb.py | 124 +
basicsr/utils/dist_util.py | 83 +
basicsr/utils/download_util.py | 70 +
basicsr/utils/face_util.py | 217 +
basicsr/utils/file_client.py | 186 +
basicsr/utils/flow_util.py | 180 +
basicsr/utils/img_util.py | 220 +
basicsr/utils/lmdb_util.py | 208 +
basicsr/utils/logger.py | 175 +
basicsr/utils/matlab_functions.py | 361 +
basicsr/utils/misc.py | 180 +
basicsr/utils/options.py | 110 +
basicsr/version.py | 5 +
setup.cfg | 21 +
setup.py | 176 +
101 files changed, 121123 insertions(+), 10 deletions(-)
create mode 100644 Allweather/Datasets/README.md
create mode 100644 Allweather/Options/Allweather_Histoformer.yml
create mode 100644 Allweather/pretrained_models/README.md
create mode 100644 Allweather/test_histoformer.py
create mode 100644 Allweather/util.py
create mode 100644 INSTALL.md
create mode 100644 assets/example-Outdoor-Rain.png
create mode 100644 assets/example-RainDrop.png
create mode 100644 assets/histoformer.png
create mode 100644 basicsr/__pycache__/version.cpython-38.pyc
create mode 100644 basicsr/data/__init__.py
create mode 100644 basicsr/data/__pycache__/__init__.cpython-38.pyc
create mode 100644 basicsr/data/__pycache__/data_sampler.cpython-38.pyc
create mode 100644 basicsr/data/__pycache__/data_util.cpython-38.pyc
create mode 100644 basicsr/data/__pycache__/ffhq_dataset.cpython-38.pyc
create mode 100644 basicsr/data/__pycache__/paired_image_dataset.cpython-38.pyc
create mode 100644 basicsr/data/__pycache__/prefetch_dataloader.cpython-38.pyc
create mode 100644 basicsr/data/__pycache__/reds_dataset.cpython-38.pyc
create mode 100644 basicsr/data/__pycache__/single_image_dataset.cpython-38.pyc
create mode 100644 basicsr/data/__pycache__/transforms.cpython-38.pyc
create mode 100644 basicsr/data/__pycache__/video_test_dataset.cpython-38.pyc
create mode 100644 basicsr/data/__pycache__/vimeo90k_dataset.cpython-38.pyc
create mode 100644 basicsr/data/data_sampler.py
create mode 100644 basicsr/data/data_util.py
create mode 100644 basicsr/data/meta_info/meta_info_DIV2K800sub_GT.txt
create mode 100644 basicsr/data/meta_info/meta_info_REDS4_test_GT.txt
create mode 100644 basicsr/data/meta_info/meta_info_REDS_GT.txt
create mode 100644 basicsr/data/meta_info/meta_info_REDSofficial4_test_GT.txt
create mode 100644 basicsr/data/meta_info/meta_info_REDSval_official_test_GT.txt
create mode 100644 basicsr/data/meta_info/meta_info_Vimeo90K_test_GT.txt
create mode 100644 basicsr/data/meta_info/meta_info_Vimeo90K_test_fast_GT.txt
create mode 100644 basicsr/data/meta_info/meta_info_Vimeo90K_test_medium_GT.txt
create mode 100644 basicsr/data/meta_info/meta_info_Vimeo90K_test_slow_GT.txt
create mode 100644 basicsr/data/meta_info/meta_info_Vimeo90K_train_GT.txt
create mode 100644 basicsr/data/paired_image_dataset.py
create mode 100644 basicsr/data/prefetch_dataloader.py
create mode 100644 basicsr/data/single_image_dataset.py
create mode 100644 basicsr/data/transforms.py
create mode 100644 basicsr/data/video_test_dataset.py
create mode 100644 basicsr/metrics/__init__.py
create mode 100644 basicsr/metrics/__pycache__/__init__.cpython-38.pyc
create mode 100644 basicsr/metrics/__pycache__/metric_util.cpython-38.pyc
create mode 100644 basicsr/metrics/__pycache__/niqe.cpython-38.pyc
create mode 100644 basicsr/metrics/__pycache__/psnr_ssim.cpython-38.pyc
create mode 100644 basicsr/metrics/fid.py
create mode 100644 basicsr/metrics/metric_util.py
create mode 100644 basicsr/metrics/niqe.py
create mode 100644 basicsr/metrics/niqe_pris_params.npz
create mode 100644 basicsr/metrics/psnr_ssim.py
create mode 100644 basicsr/models/__init__.py
create mode 100644 basicsr/models/__pycache__/__init__.cpython-38.pyc
create mode 100644 basicsr/models/__pycache__/base_model.cpython-38.pyc
create mode 100644 basicsr/models/__pycache__/image_restoration_model.cpython-38.pyc
create mode 100644 basicsr/models/__pycache__/lr_scheduler.cpython-38.pyc
create mode 100644 basicsr/models/archs/__init__.py
create mode 100644 basicsr/models/archs/__pycache__/__init__.cpython-38.pyc
create mode 100644 basicsr/models/archs/__pycache__/histoformer_arch.cpython-38.pyc
create mode 100644 basicsr/models/archs/__pycache__/restormer_arch.cpython-38.pyc
create mode 100644 basicsr/models/archs/__pycache__/restormer_arch.cpython-38.pyc.140248890974960
create mode 100644 basicsr/models/archs/arch_util.py
create mode 100644 basicsr/models/archs/histoformer_arch.py
create mode 100644 basicsr/models/base_model.py
create mode 100644 basicsr/models/image_restoration_model.py
create mode 100644 basicsr/models/losses/__init__.py
create mode 100644 basicsr/models/losses/__pycache__/__init__.cpython-38.pyc
create mode 100644 basicsr/models/losses/__pycache__/loss_util.cpython-38.pyc
create mode 100644 basicsr/models/losses/__pycache__/losses.cpython-38.pyc
create mode 100644 basicsr/models/losses/loss_util.py
create mode 100644 basicsr/models/losses/losses.py
create mode 100644 basicsr/models/lr_scheduler.py
create mode 100644 basicsr/test.py
create mode 100644 basicsr/train.py
create mode 100644 basicsr/utils/__init__.py
create mode 100644 basicsr/utils/__pycache__/__init__.cpython-38.pyc
create mode 100644 basicsr/utils/__pycache__/create_lmdb.cpython-38.pyc
create mode 100644 basicsr/utils/__pycache__/dist_util.cpython-38.pyc
create mode 100644 basicsr/utils/__pycache__/file_client.cpython-38.pyc
create mode 100644 basicsr/utils/__pycache__/img_util.cpython-38.pyc
create mode 100644 basicsr/utils/__pycache__/lmdb_util.cpython-38.pyc
create mode 100644 basicsr/utils/__pycache__/logger.cpython-38.pyc
create mode 100644 basicsr/utils/__pycache__/matlab_functions.cpython-38.pyc
create mode 100644 basicsr/utils/__pycache__/misc.cpython-38.pyc
create mode 100644 basicsr/utils/__pycache__/options.cpython-38.pyc
create mode 100644 basicsr/utils/bundle_submissions.py
create mode 100644 basicsr/utils/create_lmdb.py
create mode 100644 basicsr/utils/dist_util.py
create mode 100644 basicsr/utils/download_util.py
create mode 100644 basicsr/utils/face_util.py
create mode 100644 basicsr/utils/file_client.py
create mode 100644 basicsr/utils/flow_util.py
create mode 100644 basicsr/utils/img_util.py
create mode 100644 basicsr/utils/lmdb_util.py
create mode 100644 basicsr/utils/logger.py
create mode 100644 basicsr/utils/matlab_functions.py
create mode 100644 basicsr/utils/misc.py
create mode 100644 basicsr/utils/options.py
create mode 100644 basicsr/version.py
create mode 100644 setup.cfg
create mode 100644 setup.py
diff --git a/.gitignore b/.gitignore
index 16cc048..2afe09d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
# script
git.sh
+Allweather/pretrained_models/net_g_best.pth
+Allweather/pretrained_models/net_g_real.pth
diff --git a/Allweather/Datasets/README.md b/Allweather/Datasets/README.md
new file mode 100644
index 0000000..f60f708
--- /dev/null
+++ b/Allweather/Datasets/README.md
@@ -0,0 +1,23 @@
+For training and testing, your directory structure should look like this
+
+ `Datasets`
+ `├──train`
+ `└──Rain13K`
+ `├──input`
+ `└──target`
+ `└──test`
+ `├──Test100`
+ `├──input`
+ `└──target`
+ `├──Rain100H`
+ `├──input`
+ `└──target`
+ `├──Rain100L`
+ `├──input`
+ `└──target`
+ `├──Test1200`
+ `├──input`
+ `└──target`
+ `└──Test2800`
+ `├──input`
+ `└──target`
diff --git a/Allweather/Options/Allweather_Histoformer.yml b/Allweather/Options/Allweather_Histoformer.yml
new file mode 100644
index 0000000..2a690c2
--- /dev/null
+++ b/Allweather/Options/Allweather_Histoformer.yml
@@ -0,0 +1,155 @@
+# general settings
+name: Allweather_Histotormer
+model_type: ImageCleanModel
+scale: 1
+num_gpu: 4 # set num_gpu: 0 for cpu mode
+manual_seed: 100
+
+# dataset and data loader settings
+datasets:
+ train:
+ name: TrainSet
+ type: Dataset_PairedImage
+ dataroot_gt: /home1/ssq/data/allweather/gt/
+ dataroot_lq: /home1/ssq/data/allweather/input/
+ geometric_augs: true
+
+ filename_tmpl: '{}'
+ io_backend:
+ type: disk
+
+ # data loader
+ use_shuffle: true
+ num_worker_per_gpu: 8
+ batch_size_per_gpu: 8
+
+ ### -------------Progressive training--------------------------
+ mini_batch_sizes: [8,5,2,1,1] # Batch size per gpu
+ iters: [92000,84000,56000,36000,32000]
+ gt_size: 362 # Max patch size for progressive training
+ gt_sizes: [128,160,256,320,362] # Patch sizes for progressive training.
+ ### ------------------------------------------------------------
+
+ ### ------- Training on single fixed-patch size 128x128---------
+ # mini_batch_sizes: [8]
+ # iters: [300000]
+ # gt_size: 128
+ # gt_sizes: [128]
+ ### ------------------------------------------------------------
+
+ dataset_enlarge_ratio: 1
+ prefetch_mode: ~
+
+ val_snow_s:
+ name: ValSet_Snow100K-S
+ type: Dataset_PairedImage
+ dataroot_gt: /home1/ssq/data/allweather/test/Snow100K-S/gt/
+ dataroot_lq: /home1/ssq/data/allweather/test/Snow100K-S/synthetic/
+ io_backend:
+ type: disk
+ val_snow_l:
+ name: ValSet_Snow100K-L
+ type: Dataset_PairedImage
+ dataroot_gt: /home1/ssq/data/allweather/test/Snow100K-L/gt/
+ dataroot_lq: /home1/ssq/data/allweather/test/Snow100K-L/synthetic/
+ io_backend:
+ type: disk
+ val_test1:
+ name: ValSet_Test1
+ type: Dataset_PairedImage
+ dataroot_gt: /home1/ssq/data/allweather/test/Test1/gt/
+ dataroot_lq: /home1/ssq/data/allweather/test/Test1/input/
+ io_backend:
+ type: disk
+ val_raindrop:
+ name: ValSet_RainDrop
+ type: Dataset_PairedImage
+ dataroot_gt: /home1/ssq/data/allweather/test/RainDrop/gt/
+ dataroot_lq: /home1/ssq/data/allweather/test/RainDrop/input/
+ io_backend:
+ type: disk
+
+
+# network structures
+network_g:
+ type: Histoformer
+ inp_channels: 3
+ out_channels: 3
+ dim: 36
+ num_blocks: [4,4,6,8]
+ num_refinement_blocks: 4
+ heads: [1,2,4,8]
+ ffn_expansion_factor: 2.667
+ bias: False
+ LayerNorm_type: WithBias
+ dual_pixel_task: False
+
+
+# path
+path:
+ pretrain_network_g: ~
+ strict_load_g: true
+ resume_state: ~
+
+# training settings
+train:
+ total_iter: 300000
+ warmup_iter: -1 # no warm up
+ use_grad_clip: true
+
+ # Split 300k iterations into two cycles.
+ # 1st cycle: fixed 3e-4 LR for 92k iters.
+ # 2nd cycle: cosine annealing (3e-4 to 1e-6) for 208k iters.
+ scheduler:
+ type: CosineAnnealingRestartCyclicLR # ReduceLROnPlateau
+ periods: [92000, 208000]
+ restart_weights: [1,1]
+ eta_mins: [0.0003,0.000001]
+
+ mixing_augs:
+ mixup: false
+ mixup_beta: 1.2
+ use_identity: true
+
+ optim_g:
+ type: AdamW
+ lr: !!float 3e-4
+ weight_decay: !!float 1e-4
+ betas: [0.9, 0.999]
+
+ # losses
+ pixel_opt:
+ type: L1Loss
+ loss_weight: 1
+ reduction: mean
+ seq_opt:
+ type: Pearson
+
+# validation settings
+val:
+ window_size: 8
+ val_freq: !!float 1e3
+ save_img: true
+ rgb2bgr: true
+ use_image: true
+ max_minibatch: 8
+
+ metrics:
+ psnr: # metric name, can be arbitrary
+ type: calculate_psnr
+ crop_border: 0
+ test_y_channel: true
+
+# logging settings
+logger:
+ print_freq: 10
+ save_checkpoint_freq: !!float 1e3
+ use_tb_logger: true
+ wandb:
+ project: ~
+ resume_id: ~
+
+# dist training settings
+dist_params:
+ backend: nccl
+ port: 29500
diff --git a/Allweather/pretrained_models/README.md b/Allweather/pretrained_models/README.md
new file mode 100644
index 0000000..40e9441
--- /dev/null
+++ b/Allweather/pretrained_models/README.md
@@ -0,0 +1 @@
+pre-trained models are available [here](https://drive.google.com/drive/folders/1dmPhr8Z5iPRx9lh7TwdUFPSfwGIxp5l0?usp=drive_link)
\ No newline at end of file
diff --git a/Allweather/test_histoformer.py b/Allweather/test_histoformer.py
new file mode 100644
index 0000000..1f85f14
--- /dev/null
+++ b/Allweather/test_histoformer.py
@@ -0,0 +1,95 @@
+## Restormer: Efficient Transformer for High-Resolution Image Restoration
+## Syed Waqas Zamir, Aditya Arora, Salman Khan, Munawar Hayat, Fahad Shahbaz Khan, and Ming-Hsuan Yang
+## https://arxiv.org/abs/2111.09881
+
+
+
+import numpy as np
+import os
+import argparse
+from tqdm import tqdm
+
+import torch.nn as nn
+import torch
+import torch.nn.functional as F
+import util
+
+from natsort import natsorted
+from glob import glob
+import sys
+sys.path.append("/home1/ssq/proj9_single_derain/histoformer_allweather")
+from basicsr.models.archs.histoformer_arch import Histoformer
+from skimage import img_as_ubyte
+from pdb import set_trace as stx
+import time
+parser = argparse.ArgumentParser(description='Image Deraining using Restormer')
+
+parser.add_argument('--input_dir', default='./Datasets/', type=str, help='Directory of validation images')
+parser.add_argument('--result_dir', default='./results/', type=str, help='Directory for results')
+parser.add_argument('--weights', default='./pretrained_models/deraining.pth', type=str, help='Path to weights')
+parser.add_argument('--yaml_file', default='Options/Allweather_Histoformer.yml', type=str, help='Path to weights')
+
+args = parser.parse_args()
+
+####### Load yaml #######
+yaml_file = args.yaml_file
+import yaml
+
+try:
+ from yaml import CLoader as Loader
+except ImportError:
+ from yaml import Loader
+
+x = yaml.load(open(yaml_file, mode='r'), Loader=Loader)
+
+s = x['network_g'].pop('type')
+##########################
+
+model_restoration = Histoformer(**x['network_g'])
+
+checkpoint = torch.load(args.weights)
+'''
+from thop import profile
+flops, params = profile(model_restoration, inputs=(torch.randn(1, 3, 256,256), ))
+print('FLOPs = ' + str(flops/1000**3) + 'G')
+print('Params = ' + str(params/1000**2) + 'M')
+'''
+model_restoration.load_state_dict(checkpoint['params'])
+print("===>Testing using weights: ",args.weights)
+model_restoration.cuda()
+model_restoration = nn.DataParallel(model_restoration)
+model_restoration.eval()
+
+factor = 8
+
+result_dir = os.path.join(args.result_dir)
+os.makedirs(result_dir, exist_ok=True)
+inp_dir = os.path.join(args.input_dir)
+files = natsorted(glob(os.path.join(inp_dir, '*.png')) + glob(os.path.join(inp_dir, '*.jpg')))
+with torch.no_grad():
+ for file_ in tqdm(files):
+ torch.cuda.ipc_collect()
+ torch.cuda.empty_cache()
+
+ img = np.float32(util.load_img(file_))/255.
+ img = torch.from_numpy(img).permute(2,0,1)
+ input_ = img.unsqueeze(0).cuda()
+
+ # Padding in case images are not multiples of 8
+ h,w = input_.shape[2], input_.shape[3]
+ H,W = ((h+factor)//factor)*factor, ((w+factor)//factor)*factor
+ padh = H-h if h%factor!=0 else 0
+ padw = W-w if w%factor!=0 else 0
+ input_ = F.pad(input_, (0,padw,0,padh), 'reflect')
+
+ time1 = time.time()
+ restored = model_restoration(input_)
+ time2 = time.time()
+ #print(time2-time1)
+
+ # Unpad images to original dimensions
+ restored = restored[:,:,:h,:w]
+
+ restored = torch.clamp(restored,0,1).cpu().detach().permute(0, 2, 3, 1).squeeze(0).numpy()
+
+ util.save_img((os.path.join(result_dir, os.path.splitext(os.path.split(file_)[-1])[0]+'.png')), img_as_ubyte(restored))
diff --git a/Allweather/util.py b/Allweather/util.py
new file mode 100644
index 0000000..b44a895
--- /dev/null
+++ b/Allweather/util.py
@@ -0,0 +1,90 @@
+## Restormer: Efficient Transformer for High-Resolution Image Restoration
+## Syed Waqas Zamir, Aditya Arora, Salman Khan, Munawar Hayat, Fahad Shahbaz Khan, and Ming-Hsuan Yang
+## https://arxiv.org/abs/2111.09881
+
+import numpy as np
+import os
+import cv2
+import math
+
+def calculate_psnr(img1, img2, border=0):
+ # img1 and img2 have range [0, 255]
+ #img1 = img1.squeeze()
+ #img2 = img2.squeeze()
+ if not img1.shape == img2.shape:
+ raise ValueError('Input images must have the same dimensions.')
+ h, w = img1.shape[:2]
+ img1 = img1[border:h-border, border:w-border]
+ img2 = img2[border:h-border, border:w-border]
+
+ img1 = img1.astype(np.float64)
+ img2 = img2.astype(np.float64)
+ mse = np.mean((img1 - img2)**2)
+ if mse == 0:
+ return float('inf')
+ return 20 * math.log10(255.0 / math.sqrt(mse))
+
+
+# --------------------------------------------
+# SSIM
+# --------------------------------------------
+def calculate_ssim(img1, img2, border=0):
+ '''calculate SSIM
+ the same outputs as MATLAB's
+ img1, img2: [0, 255]
+ '''
+ #img1 = img1.squeeze()
+ #img2 = img2.squeeze()
+ if not img1.shape == img2.shape:
+ raise ValueError('Input images must have the same dimensions.')
+ h, w = img1.shape[:2]
+ img1 = img1[border:h-border, border:w-border]
+ img2 = img2[border:h-border, border:w-border]
+
+ if img1.ndim == 2:
+ return ssim(img1, img2)
+ elif img1.ndim == 3:
+ if img1.shape[2] == 3:
+ ssims = []
+ for i in range(3):
+ ssims.append(ssim(img1[:,:,i], img2[:,:,i]))
+ return np.array(ssims).mean()
+ elif img1.shape[2] == 1:
+ return ssim(np.squeeze(img1), np.squeeze(img2))
+ else:
+ raise ValueError('Wrong input image dimensions.')
+
+
+def ssim(img1, img2):
+ C1 = (0.01 * 255)**2
+ C2 = (0.03 * 255)**2
+
+ img1 = img1.astype(np.float64)
+ img2 = img2.astype(np.float64)
+ kernel = cv2.getGaussianKernel(11, 1.5)
+ window = np.outer(kernel, kernel.transpose())
+
+ mu1 = cv2.filter2D(img1, -1, window)[5:-5, 5:-5] # valid
+ mu2 = cv2.filter2D(img2, -1, window)[5:-5, 5:-5]
+ mu1_sq = mu1**2
+ mu2_sq = mu2**2
+ mu1_mu2 = mu1 * mu2
+ sigma1_sq = cv2.filter2D(img1**2, -1, window)[5:-5, 5:-5] - mu1_sq
+ sigma2_sq = cv2.filter2D(img2**2, -1, window)[5:-5, 5:-5] - mu2_sq
+ sigma12 = cv2.filter2D(img1 * img2, -1, window)[5:-5, 5:-5] - mu1_mu2
+
+ ssim_map = ((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) *
+ (sigma1_sq + sigma2_sq + C2))
+ return ssim_map.mean()
+
+def load_img(filepath):
+ return cv2.cvtColor(cv2.imread(filepath), cv2.COLOR_BGR2RGB)
+
+def save_img(filepath, img):
+ cv2.imwrite(filepath,cv2.cvtColor(img, cv2.COLOR_RGB2BGR))
+
+def load_gray_img(filepath):
+ return np.expand_dims(cv2.imread(filepath, cv2.IMREAD_GRAYSCALE), axis=2)
+
+def save_gray_img(filepath, img):
+ cv2.imwrite(filepath, img)
diff --git a/INSTALL.md b/INSTALL.md
new file mode 100644
index 0000000..b562eb0
--- /dev/null
+++ b/INSTALL.md
@@ -0,0 +1,53 @@
+# Installation
+
+This repository is built in PyTorch 1.8.1 and tested on Ubuntu 16.04 environment (Python3.7, CUDA10.2, cuDNN7.6).
+Follow these intructions
+
+1. Clone our repository
+```
+git clone https://github.com/swz30/Restormer.git
+cd Restormer
+```
+
+2. Make conda environment
+```
+conda create -n pytorch181 python=3.7
+conda activate pytorch181
+```
+
+3. Install dependencies
+```
+conda install pytorch=1.8 torchvision cudatoolkit=10.2 -c pytorch
+pip install matplotlib scikit-learn scikit-image opencv-python yacs joblib natsort h5py tqdm
+pip install einops gdown addict future lmdb numpy pyyaml requests scipy tb-nightly yapf lpips
+```
+
+4. Install basicsr
+```
+python setup.py develop --no_cuda_ext
+```
+
+### Download datasets from Google Drive
+
+To be able to download datasets automatically you would need `go` and `gdrive` installed.
+
+1. You can install `go` with the following
+```
+curl -O https://storage.googleapis.com/golang/go1.11.1.linux-amd64.tar.gz
+mkdir -p ~/installed
+tar -C ~/installed -xzf go1.11.1.linux-amd64.tar.gz
+mkdir -p ~/go
+```
+
+2. Add the lines in `~/.bashrc`
+```
+export GOPATH=$HOME/go
+export PATH=$PATH:$HOME/go/bin:$HOME/installed/go/bin
+```
+
+3. Install `gdrive` using
+```
+go get github.com/prasmussen/gdrive
+```
+
+4. Close current terminal and open a new terminal.
diff --git a/README.md b/README.md
index b7c1f85..c9c5e14 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,9 @@
#leP?)dosz_-tAMW?fZ)5``xj)@n( z1+Uq54FB@=dvWaXyK(r~act@K08HT-Kk_Ez$G?TY`NP9F)cPL$;?KSeZ~Pg9Er%|| zmfqd?^hf>h{KjjyVCAv9aN{Q*!;^~= z)0bU~*I)m0xZ +Vz4e`OW^~7?2z>;93Zq$o9csd zgU@v>3kVPy)Bvbt3Yb$PJ}crDtws)=X*3!QlvY576KqAel7GhsFKMNr6lGhn&Y#pK z?O7EDYi&i?r&%Auj!{`gqxV!N5c2f;yM?}^K9Grw^MI`8bX)hir2tq4lQuA~ESFdH z1a$~G!G~flzkjBHng0g%w&o^TnG*n{jzOtZ<>dQh&*2S{19}<8rTZuQ3v)U{Yb+i5 z=E>T42p89rIwMu;()}?i0G9R?LGEf)hNp{56$XHmKx++5S)$+Xf%CuzS~ni>;TI-S zErwA+^+C+Stqz3!q?a@)sd7;e(<^FH)^8}pwptIs4TMmER%ZZpw*0|q;e$-7&t;G- z{i!;MDd8;O{%j@n@1%jKX)aGO(2WmxQ6l=IreHD!H0RPUbi$zujPkh_a`iBPlQ7c2 zIbGf8fxs>bKSm-Eo{{0UAwVdtGY8x7d#+yv5Xh>q1Cog^qtD%_Lpsu;{PW^TiP1GD zuq5*AM`l-*02Y{0I~{iFRtiYDggnkR1_XV#C7m1m@A{GNqxvVUTRMJH)|sLq{<%Z) zFjv=HJdV{GgT9x|MQ0I54j)F@FOWAHD2oD%i;F14d3ei@y%KBD$AkBM2VJePd3+pe zFTWh)bNg}M=RSa+`?I@IDuqsG1%G_QDct;pci{Oi+JV3RlSk17U>n4YxjvcP0WUf? zUy1UAKZ5`I)n$y2jp5YXw{h>-Gw`kday72JWCEYsvyAy~eG|9-)c4?(S6_`a|L`fC z&N3`6E~2wCh1XqkG4jqMxapsE0pk~va<4cic=Z+g+ya85_!HmEih^RjkhcEhQ-tHz zowYEoo_;I(*}0n1mgZMp?$I@7Wga^p*^A@z^Jur*)c-|+GpA2uUTyNc3e|x#`&a<3 zdj@Cb=h2rhzzd!;13db1{OYg#HI`;(F+DQ_cgOA6xAHDrKRbiQnR{^L_@g+s _b){~WO`w!!-uRafW@<#mTuYU{&WeYmXaQKEV
z+`+>S9mCYi0bmY$Zo3J$Pj1EHQWLL!&)ab6_zLd(v-jY4ZaIUAi3uz(e-nRu^HF^2 z<8Q@_UUnsJ{qSAr^$kkf2aw}g7oW!afB6IWj$DsstA)JLfRO;yc5FHLEBK|qIS4g5 ziO2Wcjz7EV8vO1{ufdg%{lECNk9-G3yNv?}?!X^icr`xq#;dR{`w(_N+QgC8%kaD3 ze?Izm{Sn^&p3mcy2cUDG_#|$A*I(n4|KSaI=54=-e|TaY0ENN=*#1JS{nXFnH@ $hyd<}I6XNh1e@4>D8=e2O14cqG`!LZ*a-vi=Bh0G3??y(p<4^k8cYsZD&MougdP z1X^_U7 Wzp;Ppc}1VSsvzV8sw7n zFAvg)6O_=|>T=T=gGB5OD*UVddax)Zy?-qtc1i9l0^3W0l=alI^|WqU>%iy>f1;iP z{J9c?sT`|voHERyCSQ`)B0Ub 8aJD6pl3 z_aehCf;9ii()eIU#ux(12csiIyu`LTi|9U9OWnxiDW9-xZMyFb_8Z6Xu#)P$MV#A} zxeU(I39>QhFByR9 jb|J+;ROi`>O(uRwyT%%kpQ}$L_$_4%pHe^i zc9f=sHaY^UFrM|rJLDezKF -I~Pg`t|5`I#~1K7vKT~JbLp# zV86?8*`=3aYGxYKQ@e26&39t)rLV%pmu$wu14p2gLaqQnqdh%?ndvp?;zC^eta0G< zw{YWE4`JJmOL5UfJFv98gw7pz;l{__il2Y_GqJV*Sv;~b9gY${b>x{-_&%OX- zV{I%hcKxY|bGZ9kcL8HNuyxy3bUOxTPM!iViwAD`3KrEiT(tE(lx2zICyxMl8YcB= zoH#Bq{n{5{N8ZCNH{OU-N@Hqb0$JX`_*f68@4FX=|HEr>@x@zk=H4e^?M9z5VHdvf z#fPwF{WEdpl~-cprj3}MnW5avLZS{Q>nvcJ!?_~ks$^U!nI81fbdH{q=Q+7p!IK11 z1%S(ee*uJ)C0%FJ)Nc|>MZ$wwW#-se-gC9YK 2@v@E?}%ylni9r8;`YEZj=)WZj@+!AipgBFAb z@D9TcS)+TV2c5af{+Ax06H~s{>f|jE7PfLPKF|b-3ygI!-YV=Fo>YJ(Klwm3z+=9% zWC2lH1 {=JA`|7Fyu(J*5|WC=rrfb0Pai@s33)a@Tuw;yhlI}u!)qfW$j648ogf6 z51c`}-9k~6UZ_>mE-9sgsaeVxh+}+8rI0%|L0C#DbUK~TFV-h_&-0ilp6gl`C7R7< z<#YA{VxMMaud*xwy9v ZOwLEj*Oz4pol&22|CU~%qA~P9 z&@Z^6G%zmlvzFwmtaKKB$72`rQCb%g`3p-tN6CvEeq4p{haW|!{gCS$@no-hMuV~} zLZ5LOHCMQBZJi0Q) 7JvXPdHXb0rLR$LhZ;)VN(^u8PQq7n$jFm{o67X4Yk{~i>j-7LKQd6ze@9(UG z66CAjPyo=tB|q?!xMbj$K=0sP`22@Ii%)&}R`k|x!Ui97cKGlKY`gYt`0Y1diz{|) z#7wJxctVEW$_h-QoqXmk0m^f5OQ&$cwJ 48 zh9|FoJznyXXW_5@@DVH+gRQTA2` 6sZcaz30x!J0l~uFx;~U>7ks z^7H24NtP9CbqT@5GNxFo^kV=tgINty%_qyUpxk6xj+K=aG@DJd33*~+*R8m9_agE< z_i~rUrRW#vmg{lN_0Pe2OBo+@rUM{?5EvgD!{#mX_||O)@q+Ka9v}SDGx5N;@5KX; zeh2$@?ZLwMMr=KAGpuu1T3Yr=WCWzn>5!71#kMV*0L Zhqp)VZpLa)bbY&cy z&)b5HTQ+0e+O>d}FSab;;Nw<2nG}g55!RR~4V1P%t)bsL!z{1P8xG1#WsWIJ+Itcu z*LpcUpx`3LKmZ8wK{P3CIDL|3t`G<^l=fv6?gPZbZCE~A=g`P$esFzESt4&VqY^HO zZ7xj-=?^}URt8IDU}J{>>@*KHTs5`>7zEU)f!vfat`ZxZrjy=t&|+$NBkRm)f@ihD z`)a+gOAX7?K~hJ-B`oqF0QEWGeS8n^@%&8bW1+(H5?SwQOGpU_M7hA<1ue;2L{!p- z=%ztelj0!!iILSTF;WOPr62_1k`$MvZiK`3Wja5@R+Oq*&xXH3P_F&TcLBF~T OVdFVb~;zpCvg;IWM%ewIi;dda5_;LDev3 zh@L&_m+1sZKU=*-SB=2|9mR-->t*Z%>ns#hct5J}Ns6nnzYLI5AT1NNHheZD2W-V+ z0C*OXz 0UKpJaj{d%8=4J vP?W#ugWclti`KiKbl^);8WKsNa|gzy1Ci}?IcJqy2bi@@aMBm{uv3x674_`9FN z_LY0_>5qO9D_eKqX*+hHEDfGKbO>YL`yPDgz1L!9;W2#SZ~p{$?c9%J^UFBXeFon3 zSMS0#o*^A-d2ycQGJwu=wA*b`U{e<&x)5XI `@exN^)A4h zU%Ug^soU_?yOyzL;{_NS8z;pnQ4Z+uAS1-e9Unjx74)hS!lwxeAs{SS)*>rkWth{s zS+}h4ciOMrlXra#ANbSVnBB4k6O)saJGs}xv17-ufAJ;w#H*i!^+J-Vw)!L}t+8>_ zIur-KjNkgm7F_$hEAZ@VUW=<<`dR=z%zft@_{J@F;t4lfVb_F@709v-c`FC7h{ZlI zy@pt0B105ZZ<*5CHCo91k<(Od37u2Qz+6uw2Y~Jwl+7llW~MOKZewX_5lU =P@h)tXE-}v< zuu6gUI|Fz*Nz9>zfk07~uy81Rz#GLoP;5`# ExD?(qZ8cG6y|#0)FZ z*LV=Ga0^C%U%STuZg3v&B+6APz62^3cn9y20zbB^rmCtwTO`<#&i@$x+d59FW1T_V zR^P8`pU2c{PpQ-ZbDZ$bn=GT5wF(SV5XqIV0zJB(nN!MQ;RfzgZ* 8bz} zKs-rI49`nEEd9=~`=<+x!NVX}lmkP^=>=0W&anIQwL Xo ^z$Q0IJc( z`R8xIw(Z-|YBccVli$Se-SBFB^oQSu_x{wE@!xOBF*Y`a#l @?<03ZNKL_t)RO*W}&eL`;$TP4* tKEc@bnG&O3%<;NgU{9v z_T>xnNXGfW1VxbP{MN4pO7bI!Jsh!N#J(nlCIkry&szH?%B+F&FT4;}KJR&0w|+ge zQdnAA!tPzW(0OD6nE)VjNPpsGx;^&$eXOjkVD0QI^zvcc{FyuO7k@mD@eP;Yxz}8W zx4in*c*92EV}I}!bh}+|o%MiSDaw7_A)n&02Kmx XX;A`{Wx-NkSZm>=s1RYH?g=P0 zWr=R5i*B!re!rm1GG&P}SP*GGDZRSOg|5l10YF(}n4NQ;1&vmq3K$*IzXSl|@52 ^xTI)WrFBJXh2IE-6g~rofb@P^NEmSG9E6l4YHDD&0`i887YDqCaq#oe=g1jg zWx2lTW)X_>E*C21HfHUcf13hgV+RFZ%IE-t8g{+#*jhi2xxFH}AOA9Je29&zx6dT& zH83}_rT_#Tpf12tQOf=46 E{T+u-C++hl9b-j(! zv4s%GGmY8VS?s#~7To>gSK^viz8sft{$5<94&ongx(ltT^_ZL(_b2AjhGq?oNy&<> zb>LIeI7hb-g0mLIw1k4=Ck8hJcEJgoZw(|5w2x(~XW)nd < zu(+^*xw%tlw{zO(h4OxYY4#3_#f1et^6 p?UI?_u+F08?jeU#2B^$S4-O3yeJ#h%#P8UxcJc#A)T3oVi959dJ zz!MZl5C@q3@KYJex0Zn&6(V(N;2i@M;UT2P m%XB!v zf)sQjPn2fhE-77_cZF=Xnpo*{(C_(k8H4~v1W8w7Mz~JNXDOrZ8#! r)a`M4d+9z2D`LsI9)o98H-qLh%rZn`OPaUVA zApJR;9zuuil^~ao;A`akai-Si4z@lOng7d&p1{#2q^0&+F=Z{tvJ9Edy+e77Jm*7F z1c#woz |XAl5NXJDLz zZse4<3qaQ3R7d1%tTm+yQc6`og%t4T$y-Pj^==^$7*)=gA{F_nSz}^UD^Fq-vvD}< zQy}@M3ZJ2nf&{dJR&)?VmSx1^C`qTIB`lhhba;T4l7P9tcY2Z~0TJ?%=P~DFV2vqh z9YKV7sRW;f>F@0iD3C1Z{oKj(98%Hue8h5k;vd(8$6YFl3y23+M9>_b;Q@POsA rgJlOi%aQpunIRpwbzX8{^NTb5x%P*VEM$OZ;M3Q9AjrSLgE>Asmy2c?r#Lt1BO zwwjonn8e178*$;x0sP6w@5aK|bMV%;zZ~}X5g2PRGbymR2%t5ENxg`Lr3EZ^meJbq zeR%IXUxmpYfLx(zdbBB|XVZG0{)mBM;HgpynM%NhAb-K}@o}sd58$)6FQD 0D}V^)k1%Sqo5~_xnBcx?S|TbfTas3Y0}b z{)S%pFlC?erCU>Z3D9Q4<02)=z&vy|&Bx4xHLVp?MgXqCDf|M%2p_l?x&+13-+-Te z_q%azYZo58|9(7q@F13#mN7Xofeoz;K!KH|P9Qaa46;0frNu=|O-{n>eF%^Bfh%A6 zLs0u3!JW6?j(@%HUMw!R@cQeXi7}kPx9@lWy4^ f595J8aNaBbBQERh$77E^ik&-m;>7WJTzK6(@y>VtG+wl! z@8zKiR%cd%jG-(6y ?m|iTjEzeW0=PDYPuf(>PaXF7(-LgB5%k z5B^GDj@Pg;;pL18rgMf$oOLQ>+Ho `*a3Gzvv-7 z8^Sr`UpB@u36fu0N@(q6otDs Kk=Lh@oVj@|e;28icGAa8&mx9fxt?X=_mTR``Cz `dHV@-NdhZjjydPN^6E2jC>`3v zScPBwnNli{uz~w>WSI%x0!)-9FCuf?fMOF?zL1>8F{i*SzmY1bd_Wa~Swl( =QKQoO>pdvfnl=%sptKGXnghy6Th_>Q7E-Nb z83lhyNLWE&mw<5Kn8N@EEhl6=;6p3oL?-`F-{~HtS0g9spau%a^1+KG2X;mOQNDM~ zH!K1EJn{FmsG3TxrTa7JHpcjVVMe{Cq5FD~9~vn!(T6o^S*tOU##nluub1_LJB^S< zI(-r*vOc9kg7hIQ9j9yK`mlQsGhg(%Pn%WKh=;kSFc2P9I8{=34`%lwi 9~;)K;J*70;;lQb#ykJ$eVG3GF-%|d96ax(SEF(BpW_qa z-T29iUy3*V$R6Cb>ml5snKdOrJvrV&L-+^zxvrE#yWPf`R>QM^(HA+-b4*WZ-0`Ka z P3Haz3L5S8x>#OXf>a8 xb>^Jd;cPkk72_l*Wg9Z-+=y}&)~LQ0&6y|L)n`{ALrxw|NecL zd#H=NJc$0@-T2g(pTvKD&D-#SKiGiJ-?AH>_IY^v3$Ml1m$or~=bz&5?{}EqI)k#9 zLQBVX=`6$gjcc*z+uy`LKjZcIzF+$o c3#? C+#aX5i-0Ko0%h>2qKIj6;wTgl9fJITTn*(Pw@YX01EkkvNP}frXqzowO+q91X z9!$C@BLam(pzy}O(DC#F2Plq DR!xr?EvQ2B8m{*As3jLJ;C}P{n;z^Etaf})-)C%X65KBdj%CYg__ri-(857D z>x1%Ya$81s4P)y**-y>I6kvv(r8(s~;TjE$uSi&E`k?at|EBHTVs2Zq^PsP)=3Hz2 z*S+_-yU+3Ib9}mOcemT^K#W3ygp`o9zz`)uJR~6TVsUu_9(X`O;Q=9mctHXIkpd`| zQKS$OKtusc$%~0{5g`|7J9gZc)7|b)w=JJbpMCzd=A2c7hjFP oRA}nl-D&_{KNBkwsQeqx>ajgmDnS$4AQL^X8=7xB0tgAb3#8gK_7-@7Gn7Vtccf zrycf(3%1Gy1uO*YiI6i%@=(S&j;u#F>@n|IMPR?*BZ#*jgb%Xh1-oUzJkPi|Twp&Q zxU&Z!ihgij3U4kcV%!nZJYpDU+my^(O`Hi{O2JqN7gv{z$cBUv6XGypw;M4{Gj{s} za$dwUuZA~)tH5dXzCt60aX=tow?AMv44CtRlsNfeNX%^uF|jT)qAQsgB?K1Dz#j%i zW@6-Ip BT@sC-v%)Vm)^mLU@FE)87uFa- qFitCF5O}QY?8 LiUKis@hKcLA6~0yRJ# z7hau^W)W1Rg9s6ITw-LWhv^xi_5sm8OVrBPDG$0aoGoOeUcDI1>Nv&3C`w^RLKjzC zWba!jHd-&_zItCON>)QGZhp6ZUXlq*v8aTN&oeYe3!Fn4Bd)Kn@%E3sg-_hDnobg= z6N-`#10FwqjGz3}g1`Bf{x5v-C;tF`&(Hr`_&fj6e}`{AdI?Ye_MgLlas6}nSAO3g z!@u`W1w8%ESMdM*<^LT2)t~#nQNH|n{QiIE58@C0oBu9`|K{Ju*PdP@0SLn$x3{-= z^5&1=b_75phTRTJirDY>c *QpnZH!mG|8CvWL{+B`K!BT@<`Sok+pC%qy}!J;gsWb{s@9KHUF z0v_Go;wOIcC-LObEdU_yu5fX&;PT=USJziaX~5y)0>e1q@WK;Z2LQtbZk{~B%Wu7n zPrv>n*aaYrmw58R3;6UW?(nz%{9nLtf8i(aPyenzjDPL-Utz?IkKg|`{^4K!Mf} 0SJ#Uwsol_v2s0zwp}c;7fnyPvhm6Uc?8#`WNw^efZP(mww;x z#lQS-{EN5>Gd}q3ck#FW=RbpA{0n~*H}Cv7e(DQfzU;OBntXYs{9{(EqpW_ eXp kGlg zFT|?hZ}cz^;JE#%*)J+Y%7b1B(Gp 4d|@0rR|YY2=bI4qPUBnHTJKqlF!p zdB#UipCTe)KkhM(Bj#no@pwcjz@x{H1W6>!(*!_0G`WU_k9Rm6KvXc#Gsbbm2OqqT zAQD-ZS67&)2~R(IYP{QF7%en@cX!8$3OVEI>dGd|G)?A3?e}}m#!igD>vz=CW?rxq zK9G&`d*AyW_WM1qF0T+G7a_=bv2eO(r|Z2Y;si^5Z@1eaXO{0%nY?iv*@-H`fh-lZ zJRG=u?*8I{tLtlAUtJ@_fN7p_b#(>G86=LCF3enZw@9QQV-)$hd6^Lt^Vdo#*zI;G znKR~}e*Bb;2th68c}5HYgV0%m7#UgmxClJ}9p~3SJTXS8R8-r3zXwsl-SLPR0>)vi zbO@KE$H@G`PK#2Qquye{G zO)jOr%rlZ?kt^RX2{&BFRiBe6XyWmBL;!GobuD4S%)GiHWwvFk#*s@mM~P?z6N`r# zal4U^H|};Kg*FRiD_JB&n?+wj&1jD?;&@~OU>HY3n1O)1;~kzpeTpyt#+UKI`|smN z-+U8q|Jd7j^5ls)*cr#;9o~QceSG&j-@&)O{cYS_-{753y@RXED}492ehXjw+E?-3 z@4SbU5^ip8@WP8PA_U;W4?o0*AAEpu+~M`tU&k #4g&9;HJIStHh-tYJJ{HnC%(~myF7r*#Lyz<(s z_-lXtuj94XUNf3bzZn3v6#cq&1_21i(+Tgr_a1)Z*M1FO`He5*^*7$Y&-@d=3r}8r z(G+x+lJVgOAL5(uejQ)>dw(DM!vR12Ghe{<^$mXWo8Q2%|JtwPrI%jB=RWsS2r=T_ zci+X=zVc A4q@QK%7M;ZqFqi=r;|KH#L ze{nqC;qyQJ1$^?8pTurR`0d~RZG81BU%^+u@>P8B;RhgzTwLE? A=g&+OoC-LOT zV|@7GhxqE3zlwL?{TkkS`)z#YGoQunqeqzM3BUE-@8U~e`Vu~P|9yP+v!B6_z4Ixg zl<+&h^B#WnSAGTG`ObIn+0XqHKK1EO<8V0Oz4zY3um6Kz#n-?7b^O>n@8F$Je;WJ4 z0l)dpZ{i>R+OOeTzxkUuO%q}O_6L?MeDkfh@Wz{OVmFTX?zg{ V*>Q62 z46Y5@L7jV?BVo2*2uy2q2J@2Jh@5w YljOSuNZ?Ew{38JEi2bPA L1g6P>KlS^XRK??Po&Kq{A9?YI;-4F2y2VRrdAvDFIqYfu1;j;ej^ zTPEh62V#oUM^8V()zuZ}AZ^Ix0cfZ&TL!Zquj ykpNx zF%%I1c7o2S@l*;MlPbHW2_I^_YDi6A_epbXbaIF>hN{OKLcrnT0D!RDv5v9+r~y8T z!2$qxcXx(ruz3AqUcHuzW+& -Z~f3kJ0{`5ONgJ`0_q=lHJ){WQr;>B zAQDqyw8XThY{XD0Dna=3OXdu3RU=pmE1ht-i#Hp;(0DEC5Q1nM*NuW5v5YkCY-8aB zi(Iv`LjWT4-t?i0G0FAH;$N~oY^opW$0EguB?#ZR=yT(fOoKd6ogh3hFL2lobuI}1 zEe6@7LcpNc+@aec)Lp`>GZ2v>T*@d2AtEm`bJ7PACr9X1#wn|FlZedt=J{DwOy+s! z=YClbGdr{~GI} 5x@Zw7^ zv5~sp;r8|xPoI8-7$bK3JuWXUY<_<9^eL8U#_gk9jJq9PdF5qBQ};WBlyGx%3sAxS zaDj0*TGZ?lpZEl>F0b%Z64BoKo%itc_!9p3pZsI^6uyZ+^B4a%E?<2OFTVJafd{!1 z%S-SHdMPFpXp*whzX(H0nDT7*XUU7vbo!?AkLOo$|IW?EUK=(43X#|ICr=*ZGoSqo z-gx5;?Dq#;-Q3jZ2NiQV-g^6OJidK|loFo2@B%I`FK~Hrf!AL95$yMSJbL_?KYM$N zH-7XDOs5l$q84$uIN 0EJpJe? z=9y)}ZXVs@;_?z#H@8-W@Ds1Ufy>J) Oz~=6TbdRE4u^||S6i0FzysyDPV;O9G}=mR zz%a62xdh6Q^Naw(qsNbNbA63*caSLeSnD_hpaceTcDvE$eN4>H*Jme2Mu(Lmr`KWK zhf?6q7)&7`V66n@qmXGBP*9DqN@1*PDgp2n%KEk3Qyc&%rz_{x#MCu9OU*Go)zCm9 zvnMm=rCz_kpZ8(Jg{o?p(#1IPD#b-|Igc_6$mrkg*C*{aZ&U00&^i=V<%d>dA^jCw zVJo?DyNkN!35d4QKkY|K$#klRryqZ01~b5$-#Bn+f1YdB?y}^@U=A_ha5x~vfTtgQ z&p4BlAe%=)pK>mSR0E 8oZ{^Y2*-VyMDLP2v+dpU_J9v9SR&I&?v;US<*FcR&(3 zos -zTR4(;K%y<)D#&09}dVErh|spl0;cMU&@QEntF9 z=8gN|Gv1?Y6mC&;i{xrt3N`)m;az|i#}F$wF^j;xdhBT!tq(xu8|wII1jLW6Z7z 89xL_B`s2`;BAMy-{ @p!D Rgb=VlTnN>uCM~Bl;QHzcmr?{tm2R$Yu5o>HZ4!JT1l-)*;PT=E1Wv*NfMGWR zvc_w#FD@_f>TYL=i2L1WeBs@0hvV%@(4~T_n;Vm*QsjF#j(FvjSAZZYJ96AK47k3z z!NtV|7v~_RPNm2&TK^^h03ZNKL_t&zvF2(J2#+4$;?d(rR;Wd !V52eo;+#j0mG2+=+PtG+}wy0%4_Sz k#tUTri9QJ#V zJX2n`JA{a3nQb2P_?Oi)?EA?xiz>qG=7|9n0E(1lj>WAB8s*=Rq4R4(6P6C(XrWu{ zd?G6mkNqIgC9P&@-v 12uqDuO7K&gW?DUM0q_ zd#O@0Wjp``64k0y*F7R)xjfp~cQijIY>&0B*@aqg19@ZrKs{#(Vvhuo=nOze?b#Wa z6Z;`V;Edt9H6A|c&yV?i2s1XW->w&qIuim{?tO;FK-hVsS^N4dMsxp(-YM~Gs+Xi4 zGz_j@L@pVzFtW>H+#I@R8bRFzLkQUKchEAq%RJ#U%}mWG+0+9VU8{?Ci~$J5$nP>q zvKxoE$7R6BcTd@{TU3Wq7V=}P6OwpBs$oY&Ml%S3!}Wy9+gUCaF@{f!pr-+O x zMhWqyl!UfXtLG@9?JFRv0qN-tTZKzJSI8EwdnT@3&jW_?*72X^JKj9$U*Gz6E-brs z?$}gFbi~`uMKh8CSV>Mp&|TQ G(3J|ZHnjcu>V43iQ+Vdx zXY}8iP?{VfrbZ@ElQR_IW1kj!gJvP)^Hgk?VX#%X=E8%xuU0va|A9)Q|Z~p=O>wn~Pz|-Hv-~9{! zCH~X@_Lp(-(rb9-l~-|baUuB=E0ftc@?@|*8S1Y*cICA9trVthEO|l9(Z(F$#XU(9 z&$8rZo^0P}01kX$wUTThCVnY5Kbt|PpoQK~j<`r-U1S|_qesac7{|c^ z<$bF7{p@xkV z919VV0zetVxZAvr^|gk2kM($h_-@PKdV_9_F0apd9&uE(GmzuNQn4h+uz3VPy_1Ma z!H|R6`PI>msmlHJ34Z`UMc1n!3MN66AQ+I$B{vR~8p};i)2e^n2-rEjLnq2x9p$C( z-+fy&KmWQ%c~CIOSZrMHnR&d`{CZm8Z3el=u`p#*I?8KXJ0DVat{5XI5C-LF*utct zmB8qd@ D#;EYz{~p)!9g=DAn?pbv-2znqpc_tn!9e8Qzau1D=T8nGJARjZVfRF$#vfhh^n{I&lBf8sy*1$^ymU&Yg>ALG%>uj5DGcmt20JQhB-=YO{Iy#II8 zsCB;lC)k*0>X91tJ~69Op(HZZAxi#y1T|u-!CXWUhdG~_=eOssQ#)5w^=BB8J-glk zAtICRD@4{G?M1B$r}ld=>BvD4Lju!MbbaE`xkSEF%TDPW7EDUHm|7t=IZgP7bi&g*0; zB=VHWj@JwTxSu3zK>&b|gx;jD>8b&vGb!j~Mmx(`Sm6u>i?qMCt2E=+P42VJ?!LS9 z&_urZoPuu4`bmXWYps1w`k2DDgX7PmRXugCZ1v0^Ta{K~;6%SxDWoRP2dpUwee2h+ zGoM0T?+ju&`x*U mHTHPZV{Q^G`A_+(IkLgQ20H)y z?rsrIsBTnQ_-oO)v8U6?k~VeGX0G@mo<4yB7`>41sAh9Y$q;Nt9BR_#B8n$!N0E4i zL;=$@8Jefz<9?Co!^vu(SA&V=dr}(2d69cIj0gyJ4~fVU`zmsjg)?11MJgRqF;#=o zX;hoz1fVP#Ar7p!owM+zOG8u8$7d 7xUO4YSP_-oC&=eDA$QPyJB-~9?v$XL& zEcTh?IDG0%YgT#;ZHQ3O hwPhdF25+}vPH11_(wuq>SDdvkM( z!(k6C7_>tF3o!0RJ3rGEybK>zUD7n5#LVCV95t(PIGu_K0ZIOjVZE^xS>rr1sP842 zKOqFB-bI%6(!LutOXxuDW?75u)F_iUbIt?R%nft=d= 1Vi$u z(ko=s !*Ivz%WNl>$cxr%9ql zVz3n=q|oZ__@X4dmsVN>xdcCXK>!AJj!PjkBKbZtt%8V50*=RAkH^WSiqt|-Z8kOo z5Z&4@`&1lcT?oaY@eEE`Zjf_`N@k9?(N-GAYHa{EPA>(-G{}a>_g?{{LQ5SdROe4+ z(uiupUw &3+B8zXhzs+Q8=Xm%6TORO9tc9a=tV1uhf} zLZHUcs))ZtWc2=<3~3T7hRUgOUkkwb9VN>%1&{zEde1beUY(6|{cWz*fh;kl73D%@ z{`kwj0*KgnAxhAs*x2V~!BCc}(U_OasC5K#k_{`%>xzc3L&sh+0+*+p`JtGm8M9=D z+hS2@Ju?D8xgdusGZw)ARyxCO7_gL?|BkLAU=iO_HsQP!=2*|1JjjOoEDv13IPgQa z5X^`wV2)#6YI*6Lv#B%`XCwuAF!eg}l5sjtwz%ab8(LE`^ISc$sE#K9k>iqw&xo|* zO$T0hQmUfc zStETuwA=sIP2WE6J{n|U;&oOgwW3!#HiCMz;W+DHmGV&!ma4FS|M`a&I3M}&*Sh+Q z8t%=w)`eFQ-2hqSbrVo}11dT|nslQJnXHz6fdycOx$j5cbg{o5cX%dn7re!Ie5i)b zRp8$v NI zC+iAl_eVi$0ng#)WUCOW?rJ;7>v7FUcdyyKbWKq+eNa7W-lM#XURAVIH#m(9==q{h zy8_953c86Y0;`$$mD8_AgQ_6-Cq6h{v7INhD4%aSZk)d%e04WRJe?y-;uz^%P=n1K zy{=Vk@9FV#Da|t-LqO5!$vv}~-`8=k8>z0 +ck2R2vFx% zHQSBK$Ngbdgz8MdEAVI{+)j!unc_|cAt9q660xd9*T=R$sZxOa!^eXoYi6C>W;_L< z842(Gen%wOI53b@EYXqeIyQI{Nqz}2sCPUbaeaLw&VS_==Uj78v_y6;%jQ}P-0vh= z PD6A}OL*k^GMYzHx z7R<{myw&1(!9`H)!U#p+g<3TN0Y=q awZN)0g!?j%YMu@4HmZn>gUxv z>y0aKAh#tWYtsc+kFk1Rj#;Q6mUa1aZvdXVht2(zN$d^W{(3ZVrf#URi2=~0H~mii z;dGU){_7^Ob^VReyR~2%Un(?C2P2QqmoTFJQ3H ?`5nAXn5zavEe_JOKjsQzhg=W z3v-)eh^&7Pp CoMB#bc!6Xp=rvLoA#DVQ~a*z2v2W0jNi z>o@?>9oBVRjjQuAb F=l5%jpSN4!joKXB>&yBcR)>Dut_fX8-$0Gv z3}&jyg6Aj^YQ)8%M#QTT9ogBby^{Y{x`noW56d0myjF$Kxl{8bG;(4 #=XpBF< z)HN9ZGKFT&rxZA+qX7uyj4>HdSV$>-ZL4E&f3o7a*4}5oudrcGQhs|8wtz;TRX$jy z`G!&$y{sr^08l4mDOx_+8-I1v)v&!cl7T9N=4HJ67^`<-#=0aMQ6`i`m`^hxNJ9!N z<`2!$5UxuR^m~~Z*;ZZLEdAn&dl7kFSc~>FOIhED1%ai1s320r5*E=~o|&(l3*x|B zFy&^d5yJ~ekee)Pdy$_2^GrNq>J8sb1J^y|y@%<4>%Tp6c5bov=S
M9o~mr{F$-d-+&+YQOm75e-+|UquzyRkR*0I^p~*aL|Ko;%uB|ti`_Gt> zD)NCqmFv 26JhzE0=;s237cvgDV@}x3 !dG^`OO5R6`%aPOd7gbWP=Rd+Tl)e4y&{fARkbSL`R3OT zjk;x_sOlWchQ3-}2GD>niY(VXB&_Dky8lcGIPa9JEu==zJ(sc3?3kg>h_dy*v^4lN zsy=g?)RzlK{{w=dy>!wl1=GsEdIT}(#&JiJGP>C4#oR7u|N(-;E z%Yq?y;aWd{+H_*7L7~MbG$BwwuZWl$Q@rr=WLHHCm9QJf5ljck_TD_tj2>s!)aCGo z@M1i=EJmst `E!7G@@Fy06PuSG<6#RBlUg((7)|wZ-gT}pIG%&74g@5mXZ|M zaNJps_{Je3Pduih=r~WOsd+eA1DhAC!{LDae$Uq;k%d8|2R+q;s8%A<`JfI~N{J2g zxhA^t*i;dBH82o#F0{t7_P;N~z2$6_Qdq{zd8>Ml8z(~k_Er0maGldIu+(13jh-|R zQSyvo+&Pr38V!03?eFPyVvTTfINHx@Ap?EhJo4 H^Ftz MY#`JR zl$N%+FF!Re0RsND&xik>Uys{jChquM^!HpO&id;dTHn9E>9`CI!5C7sd8OAR9~LJj z8bCyV^*MrRNS?j~R1Upf2PdXV;Vi2?;2oarv8K{S8NG^-mzUGA_sSPa=?KElFB0jA zDN6nU%NQ;TsR4qR5?VF%=V<2;t> WFy*Rih5xx$#t!b11rR zJ>u-0T6-Pq@LeDM`F`ixmgC(!E%z;eny{l$6E*q(b}WZss6|@DFzplitS_aO7Pn(} zIRMc`ea9?fDU~Y=~P|fi$qa* Uv zNYmZ%i0i8=3(B;_(fZ{hVp(ZDc@c%F8ttolW(5f{c>FUUMq6nd8NJq0$c#)PvLa^M z=6M#{Vl{%Au|^PCKuLJ-q)N5Yo;t{-dmfjp0FACKa?PsdB?*7#SUSynAYd!$?Y`C< zp8AXdB>!KpZTJ0ciZgONLy&b0P>tUHIJ&m@d!sp3{keK7MU4ctpxQ%w;=^lLdpxy$ zl!h9EKfmzEW$c<*yE^hc1(Y+Hxvyeu+k5)s*7FE4dP}Kxno_Sw5=`YD#FQSSa!_gS zu#zIn_hSJ1dl*fThHO*t zw4m(P4nMw%ESj^iVUYRp@XuO9H`u&iEr&aV7U5c5k1Wcodm95H1>-}1-{aLq3H))+ z&0&u?MX=$=a|+l%9dMuf$0|IW!?7%?#q6S39%18GwCB)ryZUjP?-o{74uE~YHaG5c zt+b{Nx^7S^|F$^Q8@=Z-sr(@y+t2Zod>EWooK2o@bweIw0tw2wsbOtprn{E|E2#{+ zCdypUJxgz$8mr498vB8(#;Q{s0!IxlE)HCkRn?0YR!6wJx)gPr#S}uSVa+GWrA9I! z*RP1Pmk`WdVTcfeGB=?ie;*Q8V< S4nf_4k#CB?|EStKuF90o(9c@7j)*xI~l&(Z3$w;i=;Kj+u)viVUp!=@=WM~G+E zTwOC+X1vaCMa}`N$GbVdI#U}e8Pp={-)~c*N&rbtTfsuQS22jD25u+;h}Ci LftB`z8vX3=>w^Zfzp389QG;<=T!Kqy zL~a=Y&)O=|QbQR8)ZY&up)s)AO;`A(RJkzy{qY9w{Rnf*uY2g4Yz(PvJ}~|9d&Bz& zX%88&x-lt=?E9d`jyIUqu+{_=&zS_OSLnC!{><#KL_P-OVN{Q 4xKkQBsFn@_e YR=@Z9 %DfRCV>hUB&)rU1VpFhA1R9g?b+=A~1-PP~wIdix8S4UCx(KA%@yQ+<_qo?0% z=luP~G9Y|V_wR?QV-YlQjUDgBajSK#LNnJrDh`uBtj|+0j%&%q(AcETRb;6(%{iIp z8B~CxS^lLO5B)yF4pf1cqEN$9x$kOFZ?3QYPkC< 1E3Jo0P8$qnJh-r z3uem5A=tbgcRQ&vo3R6<$5BxCLZWJsU{Z<+-qDU?pVv8LKuB~3Gih|TURD(JFQwEJ zVyd*eYIs(Ix$>EHE|^(q^U;kz$O@N~7+7M~M^8{yWvnpSDrqeO(EX^+c`YggwAZe; zDu{E|a;Kl)NbL8JYB=fZ0e(g7`)?XS>P%8=z{U_2!sF*fk$UN_H }pHM0kM=y zwP3p{+kDmJJ$=SgOos5c llQ^vj;t|*CbapDTsPjDXh0Ue*OTpbQq?Kc z!STixd)JE@3i=t{9Q6HqgLnbe e2pj^Ts@(@v#gCM@dZ3o- zt~^C@bOrty bxies>igq3+I`c$?{+(sQn26eIr&n);}d~q(XaH`z&xUC0CHwFF#C|4 zoKO3qA##|}9kG ECCSFf&AXa>+8W^F zIC7zz$cs{A>`~ $!4NU!mL<244k?*_cV8Bj!|vPBqhUx$!+=@FF~x`}X9QVz z%d()zT+u}07$Xp2y0+xeX+c=j2xqjg<#_r{I40JL_q3+1P1+3hZbS3N1}8{vV;}7N z6`*s9OjlN^45(2T{<{{9igLeoD=yud3{ W_TtOX% zAd#Z#-1}s-roT}_<3N|5U-t>UH{OBtiRjJscF3tuR^M_W`@}*{^aLWCT^PpQ3Ox_k zOjkny03ZNKL_t(RqZkr(gsSxbkUTRGag|3WLtN<^-U*YbtG1@^ACU;wRHr5l1MXuS z>(|*JmiuSX#G03);8_e2F@<`>-a}uX!1L?YyvP2hPonb4Lwy*3t9djoiSgUZz-;oD zd#kNGriS=@)+NA$#zr^UO=A@dcN2WI!R|+2zmHR`3-Z?W_XwG`5L$1ozjoeCDt&{i zC(&xWJV#PDa}DEps*Ojr{f`>c`n#&1sBF;oPYqYP+d_)SU^Rv)07-F{tq0x4MXpcB zNYPk-FZ}PmkC08y0H#1$zkg1J{rzj+!}n5VnsM(xC!Mj7dF^anp#0{b4uYM}eZFrx z-hQ6=^Xi;2CsX>NT*`;q)?BvkD3Yj1)kauPBMcy3Fq4!9PhvDDd7f?V>wLp`M7wsb zF2a4$6kkm+glKx1x~R7ExNZiz+4*y)7@OB^Y~3@_8@12Vd_O?N#-pEa{Mm?@IJtC? zHLDpC@(5dOINv1~Pz*3xC}QLWejk!+;l$I_R7$G|v5avk%m V3XyZBUlNecr1c8v!THcIg1LgI_W&I1% zlwyMF{c?W`mXaH%nGNXxUsBlLAJ+ZX^A)!A)J7}WZQeW{)H&hmw6CI0A+XGz6gp9) zzE#{&1J6ANuK13OIv?K^9|0hRLd3ar6oz$#f(&Tl^VfGZskdZtlw$W>sK-q9N4KO& zf*)X9)rzR{`WQn%rn+MH6dMw>S&?;;ELB^p%let}EymdN$(xTvv|2O$xa!}gUb8YL zIbl;18{6;K{YUTXR{vW3`k|&9tao5_FHfd#eqS4Oo_C5o*5~H_2CS4(9AL(4bX7R2 z#(!}CmEY9LJGzr>U#S3aA-EF;i1Q})Wr$!F^tpYd&NbdMRCck5LP-V0vPAf@6w7H0 zW^XL@%^QNA?+Ty^;j3|_2}f!~dZhMQIJ^L(UNJ A} U3-vFE<=^(d~QjK2BT)tIttpkRM%*iHLp z+|NX2{GKzC{g0x{`ZLc}_Zp$j9}`x8;GS$734E@3spzu*td>sh7jW;C85&>6oF4#Y z%<0$s &*zkEcd_V)oJ5H5528JbTNJV$rx4LYAF@=){F?x|IE z9!5}C<*NUF^LMvldB=>Ce84C;F L?TjRnFxngitr-b8CiwO^s?ILQEAc;=VD$-1I}x9lq{_ z-OlU20vdL;Yg1#?`qEynRv85pIH(3UJ9=K$N@U@v;B>me#l $s2#QAR#4|Odn9->QJVG?Z zoFb|vQRziQ7}3glDLS0 (U4_Uqg*^!rQQr*$3J;s|r3f)KlBFH6E7kwh_5zaRMLMu7=QevVQFPLh1RvIwKg z$*46dJ(~OVr&+_-Ff;Wb(dTSLUSpk_^YFZl&I#Bsh`YUJWuQLaQuYHkzt%ikGwA7= zTE9LLY1HA__NewV8}?J aIE>GWFstF%hw*`D zGnn-l`n&0$LmQMz@lex6N3}(D1Gi&Ff6Rd0ZioGThm)HWm4-4wGjqLxtWt9TI4tg( zr X0Ju-*vQe)#@-6nPv+3(b2c$Ro^5{}4pajZtsN z+2-RyE!DGbX(IYwUYOe#Q &C@lY6z7ld1^TIR>99Ud+8$-eOepZt-)NwGWwn- zd*xCv7+)|OPs`AW4G*0F<3 z!P$X=YOE$Sa{z8vH2!0~QHDYM` &BJlpaeTX@d$lFB-M_P_Y z0VxLP6pS%}vVuExo{gg<)ybSRY8QZMo}9s)Y#%vi5QPvh3I!`grWh_#lw%l2TwGqV zQ?O*yly;+S&Q5d8RL?Q)cIKRgz|w~L+$j32huA_nmt|?HZ$6YP))%JX66~;Jw2?wd z-1Jw}NvoOGIM_idOO>AU(yzQXrIm5&=R+Ij2Z!&w4m4}z#+tlzGIG8;X{%sSY`@l} zBA2|PhV`Y{R|eiQ8?M`}=CW*4GS;6*6Omp4Al=`MN{#$zpGycRc`=0$bGU;;hDm7@ zT0Z^$>uG5}eSs5=;8e(&BN(lX+Foz5{oPaM^hfpWHHk5wPB4Udi?iqt+#|6i)bOyP zrj`Ka__p4dj3Hvegkcy=ySH-Bk{N1>f{s!<9;ik)z=o(kC`#2RrI=QwUXNE6P!wmk z+hG{lsMUqY)IJJXg|6LhXBxwraFSxee!u4eDD!NgKsD0TxK~4bzu$9V6_Lc!@mQ9{ zGQ*XR>Wxty3*`(06 ^P2mKG|?o!9sO(fr F=LE491g})_R#<}K=qz_ zzOm=Lj=PbKKzGme-YoNsap;VEeHfw1Fm CvnA=N|;5(i?MCl<(0mA+6n zX_zB6%X~>OA{65JlMC`ZBc#D75S)R(V3}tu^8zYTP$|?T6(X(<%4l5GlJyAdGSAFu zi>XS}1rApiD#n9NB3(yR&KiTuEY6%fqk5hQ9vj`r)d+2htGM~AV-KKt=6vL<6fN4Y zPW gw>pQ57zqBl zn4`_eFJD95h*?@KVal`38wjebXyP)nTEuQQT7OitJxa8Q*J7^1I!OQg^S|VRm|TP- z1WXh2rQ=w4=)5c#_PeJ4X&9PhOMbm3&_Op`0{Y#aAZt}hJf~%@*>Ok@QP 6Z&xmpK-)q;| zl>wK8`yGP)uH~AeQ3fIrO2|lQX!iTy48*P@n>l~0q_*z;Ubi{*IRc|z7e{OR^9C2a zh_nXv&W*dhUr6?>ZA!^~$E$0vPZEB>(eIKHdxxNn-Ua!*M08}mtL7?zQYw(fh)46j zR#7IZ)J(u^X$GGqUZ!qiqp6A)V;}Pc`DFb+7$04rfjep ;U9HgqZvfjjsw3Ldsn0eWOVmZUkc4q#VUCnD&^=epjA`wo^1|fU zT6IWyl;=0CDk a zGe`_zE07d`BF>gM819^Y4X6qylKj_S9}%K6AUuK*LS&T#*}VC+XG44p(MtD!o(ds! z+2lZ*%g`V9zQ6nCE0_gD3}bzEORf%qDw43%ld;TNIdHV+kCT*r#m$<~7D7bo3@;+W zJe`<|p!cJ=1V*Y+vE8`CIE;)2g;1*_=7M>dfvFZL;khK)?@ nG=8V>HcNB{Iay zD_ _gA{dfo2~G^hTfcFeOL|A)1@*JtzLd(rpb=S@)B+H;(-j0O_|Kn +lXY5={ a) s7_iz%8x z%GCvVjmf7Io&rFOI8C!fAM&ywFN=T)1D2&2N}E-d5MIf_M?r(-94Nxf6pWe}r+qFP z_pDFu_16S&gsh>s6qR(-d*bt2JhiT>hJY#(59s>LJn1M=m@P2qT#K=)nxu#-)7j01 zD9Ta1R+9UEkI1!a%s7bS5o^NU^DL<65l&sHyU&+OCGZLkL`BvFr%UX*dd@g}Uj5;R z98|A~3; (!o >)6^o&RRw`Nl2VI? z8O -_xna#PqWbPjx`*7 zdwYu%BVR*ggWR(7V@1f6x4h( x$jEp=a$@ nAb;n$&<6!>i ?CyIu4sd_}t+lzefMCd&~b9t+O2h_!T^`U3H zew}X__1WVx^u^Ylt-~hguxf}*lP5KqDGdWct_HEH7;yD9F;*8YW)WgQB2j1w5k%RL zTt7zI&Q-?Rx<*4FN#`zKFFB+6S;os3!D#Ne8Y^ynwSE#I85&vnej!8*DPa(mjP?1} zh*iyD2zCrUM-jxI6PLB<#*hZg%WQP5QVNE7aOjQjaP^#Ubf?%pZ+!$oh5t=HZ>aqH zlBM!oDaf+s1Q#*XwRRl&zAriBG&u>*z)5nNaGOiv0vU1~gwTY<4OI_;0hk!2LYX)w z%fe$MMMH+dg+y55jj0|j#i2ErZT`9tHo(uxG))%T<+WabB~doYIU~e~BOPU2`50Mp zL`qmo5&~-uY$*=KB*L7hRen!3(1RpVw#c`;x|Z@DU@~v~*>$<*mBE$oLsWHc$}u)d zPp6a+)v2&K5 c)~&XG4+MNxt=A=fh|-Yyz3V=Xv>fjTN$hLqgOh7} z*6-hYJfX{VAf>n2^lu;ORjVNAXWj9F%Yz7-*^S`-yY7<$OIy2R|JlAsiGo~qtgY{# zy`|NR4+6mG-@_>M05z}_vAp~E&Uk?w^565~l9h%qVp(SF_FV1l=H>=OWb}g5>13#? z%ATnqc{m&}&l!iqC5DtRFSGD@BT^bMB&I~ !M6Vb~!AM*jBuJ#sF1 z{OGYP_ kze6NTxz}f4%z{X(^t=6&9T%ZCFY<%o?J2RF; zP 3)(^ZTZWPhqzwWWyhA#+L{G5@gG0vA|snNSOXc+T#! zER7**26Cv_2SGqkO|0UKsj4lkoL4G9E_}YobevM87z0=WE0@d$5D_4THb|3Pgwq&; zQ5t-7!5h%t!7(EqLRlczxHKvU0RgOuZRd1jwUv d%54(joyGGU%3+ 5QKnPD*6!> t93-LCt9f`#tj>R6TtRl{ zwfOCmA($?505A-pH5j~uL%>q1ES|mY8nMS=f6tzh2nfGhBcOR>j5-5&7);7l_h{cZ z+T1=PK$~x($+(uJWpomUFcN^!MvaKbsF&y3>iLTO^VVIDtAfaL-oxAvgEHoXI^FjE z{Cy&%Z#p+McVJz3#E)HPOk(W-v`LY{9Be&z?Vs#FcD-!;lC1T*rfq^dq`&mO1pRjN z5q3cPLNCqJ;twa+yWYb+z+4&!hAA9H9}!uf8>hH%S(#Ee|AT}=IOxH-%<*95M>Ubn zQybW8W)ot!8` Fesrx-zlQfda*%($KSY 15GFC=O-x`mTG_fW^Mak`&^w^|+ H^N=P`TK>sz9IA6*@F=$px(rGmLxAhM^=s zir(*_1!us KXsBwh zjlR{CerZP_2S<=INVU@x+#jt$H()(J?K-AE^*MitMz#WNwl swCxNj_}S#w^R z_ZrE%wWI&MZH2(1+jqP&5JIyrZ5i}^9*crK>Hy4(qjYW95gJj`Gd?tb`nLKs8+`T8 z4!& *2bGc$?Y9~q zEhRw;1`GoVWelYNi4AB?*6`Kus`uYlsj1V_SJe|A3z;hCRwr~K=D2DJRTcbiCKU1g z@CK_#iF09!g|C1X14KI(x=S>D z}Twtjwi ztqH%Lp?5sj@k-T{HYC>>v0H|$Z(GRubDf{KSVJ$l=YK2eY-QFVv=RCDL*vjZi};)a zeOCRo=$KT;q3U9<`%f#we(N)8bT$aW#saOVIk26;p-LC3T;cDV8a}G+8;#Ez?E232 z&(8y|WTKKu=gQV*_gpKbs{mF+pdMFLIYN?z#w-ZDQg$j`tL@u{ KPYYK>y;gILvlT{QJN-K$~ zjKnf)^D@gk*UFNkIO%b7EVde7r$#9vKm~!S^jZvLMQ?^-Fs^(|4;ZU*y=E|%0-^<_ zkWmNwYi-waLA=!e-ng854LF`K&5(xf@1E9Ex(G#sz=aJ2vT?XwC|)8e4Ml{4IX#=- zS0l<$5M58b7jhoqttGo@_1?MmkVboth@US<4Li3-a33;gpM}=-kF)vvPk`h_)c02r z0tyace)#zsgriSUKRYx4MSqX_@#uB5f6(Sd1-4pyIzRXBzfXgw4kV#qEJY;P9Kx(T zH=p>n#MyaoVgv$dJk2H?tvp*rxDCmW8| Oc}}SjvJODWW8*3jzf@mQUCrDk!K*8}-3QbL>)>Pk5Rq98bqu z%tbdJHLMHF3DEEQR!VjYZ!JB`2DVBQiQy4q3(>lJ?H4+tdjGUfbqcleFS^KS|9C7Q zeJGBE?~G