From 267905e6bbf314f5c74907d7f302e991f3fb87b9 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 26 Oct 2023 07:30:01 -0400 Subject: [PATCH] Revert "Merge pull request #2411 from vladmandic/master" This reverts commit 64cce8a60695ddb0418f5f965dd59e2eb52af65f, reversing changes made to 597fc1863f776f89fc3cbfbf4162941151fc3dc6. --- .gitignore | 2 - .gitmodules | 4 + .pylintrc | 1 + configs/v2-1-stable-unclip-h-inference.yaml | 80 + configs/v2-1-stable-unclip-l-inference.yaml | 83 + configs/v2-midas-inference.yaml | 74 + extensions-builtin/Lora/lora_convert.py | 16 +- extensions-builtin/Lora/network_oft.py | 49 + extensions-builtin/Lora/networks.py | 2 + installer.py | 2 + javascript/base.css | 3 +- javascript/black-orange.css | 35 +- javascript/black-teal.css | 13 +- javascript/light-teal.css | 9 +- javascript/logMonitor.js | 5 - javascript/notosans-nerdfont-regular.ttf | Bin 0 -> 2269328 bytes javascript/progressBar.js | 14 +- {html => javascript}/roboto.ttf | Bin javascript/sdnext.css | 4 +- modules/api/api.py | 77 +- modules/extras.py | 4 +- modules/hashes.py | 2 +- modules/hypernetworks/hypernetwork.py | 2 +- modules/images.py | 4 +- modules/img2img.py | 1 - modules/k-diffusion | 1 + modules/modelloader.py | 6 +- modules/paths.py | 21 +- modules/processing.py | 23 +- modules/processing_diffusers.py | 34 +- modules/progress.py | 21 +- modules/scripts.py | 28 +- modules/sd_hijack_optimizations.py | 2 +- modules/sd_models.py | 7 +- modules/sd_models_config.py | 6 +- modules/sd_samplers_timesteps.py | 1 + modules/shared.py | 2 +- modules/shared_state.py | 2 - modules/textual_inversion/image_embedding.py | 2 +- .../textual_inversion/textual_inversion.py | 2 +- modules/ui_prompt_styles.py | 7 +- pyproject.toml | 6 +- repositories/blip/CODEOWNERS | 2 + repositories/blip/CODE_OF_CONDUCT.md | 105 + repositories/blip/LICENSE.txt | 12 + repositories/blip/README.md | 116 + repositories/blip/SECURITY.md | 7 + repositories/blip/cog.yaml | 17 + repositories/blip/configs/bert_config.json | 21 + repositories/blip/configs/caption_coco.yaml | 33 + repositories/blip/configs/med_config.json | 21 + repositories/blip/configs/nlvr.yaml | 21 + repositories/blip/configs/nocaps.yaml | 15 + repositories/blip/configs/pretrain.yaml | 27 + repositories/blip/configs/retrieval_coco.yaml | 34 + .../blip/configs/retrieval_flickr.yaml | 34 + .../blip/configs/retrieval_msrvtt.yaml | 12 + repositories/blip/configs/vqa.yaml | 25 + repositories/blip/data/__init__.py | 101 + .../blip/data/coco_karpathy_dataset.py | 126 ++ repositories/blip/data/flickr30k_dataset.py | 93 + repositories/blip/data/nlvr_dataset.py | 78 + repositories/blip/data/nocaps_dataset.py | 32 + repositories/blip/data/pretrain_dataset.py | 59 + repositories/blip/data/utils.py | 112 + repositories/blip/data/video_dataset.py | 110 + repositories/blip/data/vqa_dataset.py | 88 + repositories/blip/demo.ipynb | 301 +++ repositories/blip/eval_nocaps.py | 118 ++ repositories/blip/eval_retrieval_video.py | 250 +++ .../{.placeholder => blip/models/__init__.py} | 0 repositories/blip/models/blip.py | 238 +++ repositories/blip/models/blip_itm.py | 76 + repositories/blip/models/blip_nlvr.py | 103 + repositories/blip/models/blip_pretrain.py | 339 +++ repositories/blip/models/blip_retrieval.py | 319 +++ repositories/blip/models/blip_vqa.py | 186 ++ repositories/blip/models/med.py | 955 +++++++++ repositories/blip/models/nlvr_encoder.py | 843 ++++++++ repositories/blip/models/vit.py | 305 +++ repositories/blip/predict.py | 98 + repositories/blip/pretrain.py | 173 ++ repositories/blip/requirements.txt | 4 + repositories/blip/train_caption.py | 206 ++ repositories/blip/train_nlvr.py | 213 ++ repositories/blip/train_retrieval.py | 345 +++ repositories/blip/train_vqa.py | 202 ++ repositories/blip/transform/randaugment.py | 340 +++ repositories/blip/utils.py | 278 +++ repositories/codeformer/LICENSE | 35 + repositories/codeformer/README.md | 149 ++ repositories/codeformer/basicsr/VERSION | 1 + repositories/codeformer/basicsr/__init__.py | 11 + .../codeformer/basicsr/archs/__init__.py | 25 + .../codeformer/basicsr/archs/arcface_arch.py | 245 +++ .../codeformer/basicsr/archs/arch_util.py | 318 +++ .../basicsr/archs/codeformer_arch.py | 276 +++ .../codeformer/basicsr/archs/rrdbnet_arch.py | 119 ++ .../codeformer/basicsr/archs/vgg_arch.py | 161 ++ .../codeformer/basicsr/archs/vqgan_arch.py | 434 ++++ .../codeformer/basicsr/data/__init__.py | 100 + .../codeformer/basicsr/data/data_sampler.py | 48 + .../codeformer/basicsr/data/data_util.py | 305 +++ .../basicsr/data/prefetch_dataloader.py | 125 ++ .../codeformer/basicsr/data/transforms.py | 165 ++ .../codeformer/basicsr/losses/__init__.py | 26 + .../codeformer/basicsr/losses/loss_util.py | 95 + .../codeformer/basicsr/losses/losses.py | 455 ++++ .../codeformer/basicsr/metrics/__init__.py | 19 + .../codeformer/basicsr/metrics/metric_util.py | 45 + .../codeformer/basicsr/metrics/psnr_ssim.py | 128 ++ .../codeformer/basicsr/models/__init__.py | 30 + .../codeformer/basicsr/ops/__init__.py | 0 .../codeformer/basicsr/ops/dcn/__init__.py | 7 + .../codeformer/basicsr/ops/dcn/deform_conv.py | 377 ++++ .../basicsr/ops/dcn/src/deform_conv_cuda.cpp | 685 ++++++ .../ops/dcn/src/deform_conv_cuda_kernel.cu | 867 ++++++++ .../basicsr/ops/dcn/src/deform_conv_ext.cpp | 164 ++ .../basicsr/ops/fused_act/__init__.py | 3 + .../basicsr/ops/fused_act/fused_act.py | 89 + .../ops/fused_act/src/fused_bias_act.cpp | 26 + .../fused_act/src/fused_bias_act_kernel.cu | 100 + .../basicsr/ops/upfirdn2d/__init__.py | 3 + .../basicsr/ops/upfirdn2d/src/upfirdn2d.cpp | 24 + .../ops/upfirdn2d/src/upfirdn2d_kernel.cu | 370 ++++ .../basicsr/ops/upfirdn2d/upfirdn2d.py | 186 ++ repositories/codeformer/basicsr/setup.py | 165 ++ repositories/codeformer/basicsr/train.py | 225 ++ .../codeformer/basicsr/utils/__init__.py | 29 + .../codeformer/basicsr/utils/dist_util.py | 82 + .../codeformer/basicsr/utils/download_util.py | 95 + .../codeformer/basicsr/utils/file_client.py | 167 ++ .../codeformer/basicsr/utils/img_util.py | 171 ++ .../codeformer/basicsr/utils/lmdb_util.py | 196 ++ .../codeformer/basicsr/utils/logger.py | 169 ++ .../basicsr/utils/matlab_functions.py | 347 +++ repositories/codeformer/basicsr/utils/misc.py | 134 ++ .../codeformer/basicsr/utils/options.py | 108 + .../basicsr/utils/realesrgan_utils.py | 299 +++ .../codeformer/basicsr/utils/registry.py | 82 + .../codeformer/basicsr/utils/video_util.py | 125 ++ .../codeformer/facelib/detection/__init__.py | 100 + .../facelib/detection/align_trans.py | 219 ++ .../facelib/detection/matlab_cp2tform.py | 317 +++ .../detection/retinaface/retinaface.py | 370 ++++ .../detection/retinaface/retinaface_net.py | 196 ++ .../detection/retinaface/retinaface_utils.py | 421 ++++ .../facelib/detection/yolov5face/__init__.py | 0 .../detection/yolov5face/face_detector.py | 142 ++ .../detection/yolov5face/models/__init__.py | 0 .../detection/yolov5face/models/common.py | 299 +++ .../yolov5face/models/experimental.py | 45 + .../detection/yolov5face/models/yolo.py | 235 +++ .../detection/yolov5face/models/yolov5l.yaml | 47 + .../detection/yolov5face/models/yolov5n.yaml | 45 + .../detection/yolov5face/utils/__init__.py | 0 .../detection/yolov5face/utils/autoanchor.py | 12 + .../detection/yolov5face/utils/datasets.py | 35 + .../yolov5face/utils/extract_ckpt.py | 5 + .../detection/yolov5face/utils/general.py | 271 +++ .../detection/yolov5face/utils/torch_utils.py | 40 + .../codeformer/facelib/parsing/__init__.py | 23 + .../codeformer/facelib/parsing/bisenet.py | 140 ++ .../codeformer/facelib/parsing/parsenet.py | 194 ++ .../codeformer/facelib/parsing/resnet.py | 69 + .../codeformer/facelib/utils/__init__.py | 7 + .../facelib/utils/face_restoration_helper.py | 460 ++++ .../codeformer/facelib/utils/face_utils.py | 248 +++ repositories/codeformer/facelib/utils/misc.py | 174 ++ .../codeformer/inference_codeformer.py | 269 +++ repositories/codeformer/requirements.txt | 17 + .../codeformer/scripts/crop_align_face.py | 192 ++ .../scripts/download_pretrained_models.py | 40 + .../download_pretrained_models_from_gdrive.py | 60 + .../codeformer/web-demos/hugging_face/app.py | 280 +++ .../codeformer/web-demos/replicate/cog.yaml | 30 + .../codeformer/web-demos/replicate/predict.py | 189 ++ .../codeformer/weights/CodeFormer/.gitkeep | 0 repositories/codeformer/weights/README.md | 3 + .../codeformer/weights/facelib/.gitkeep | 0 repositories/ldm/README.md | 302 +++ repositories/ldm/data/__init__.py | 0 repositories/ldm/data/util.py | 24 + repositories/ldm/models/autoencoder.py | 219 ++ repositories/ldm/models/diffusion/__init__.py | 0 repositories/ldm/models/diffusion/ddim.py | 337 +++ repositories/ldm/models/diffusion/ddpm.py | 1873 +++++++++++++++++ .../models/diffusion/dpm_solver/__init__.py | 1 + .../models/diffusion/dpm_solver/dpm_solver.py | 1163 ++++++++++ .../models/diffusion/dpm_solver/sampler.py | 96 + repositories/ldm/models/diffusion/plms.py | 245 +++ .../ldm/models/diffusion/sampling_util.py | 22 + repositories/ldm/modules/attention.py | 341 +++ .../ldm/modules/diffusionmodules/__init__.py | 0 .../ldm/modules/diffusionmodules/model.py | 852 ++++++++ .../modules/diffusionmodules/openaimodel.py | 807 +++++++ .../ldm/modules/diffusionmodules/upscaling.py | 81 + .../ldm/modules/diffusionmodules/util.py | 278 +++ .../ldm/modules/distributions/__init__.py | 0 .../modules/distributions/distributions.py | 92 + repositories/ldm/modules/ema.py | 80 + repositories/ldm/modules/encoders/__init__.py | 0 repositories/ldm/modules/encoders/modules.py | 350 +++ .../ldm/modules/image_degradation/__init__.py | 2 + .../ldm/modules/image_degradation/bsrgan.py | 730 +++++++ .../modules/image_degradation/bsrgan_light.py | 651 ++++++ .../modules/image_degradation/utils/test.png | Bin 0 -> 441072 bytes .../modules/image_degradation/utils_image.py | 916 ++++++++ repositories/ldm/modules/karlo/__init__.py | 0 .../ldm/modules/karlo/diffusers_pipeline.py | 512 +++++ .../ldm/modules/karlo/kakao/__init__.py | 0 .../modules/karlo/kakao/models/__init__.py | 0 .../ldm/modules/karlo/kakao/models/clip.py | 182 ++ .../karlo/kakao/models/decoder_model.py | 193 ++ .../modules/karlo/kakao/models/prior_model.py | 138 ++ .../modules/karlo/kakao/models/sr_256_1k.py | 10 + .../modules/karlo/kakao/models/sr_64_256.py | 88 + .../modules/karlo/kakao/modules/__init__.py | 49 + .../modules/diffusion/gaussian_diffusion.py | 828 ++++++++ .../karlo/kakao/modules/diffusion/respace.py | 112 + .../ldm/modules/karlo/kakao/modules/nn.py | 114 + .../modules/karlo/kakao/modules/resample.py | 68 + .../ldm/modules/karlo/kakao/modules/unet.py | 792 +++++++ .../ldm/modules/karlo/kakao/modules/xf.py | 231 ++ .../ldm/modules/karlo/kakao/sampler.py | 272 +++ .../ldm/modules/karlo/kakao/template.py | 141 ++ repositories/ldm/modules/midas/__init__.py | 0 repositories/ldm/modules/midas/api.py | 170 ++ .../ldm/modules/midas/midas/__init__.py | 0 .../ldm/modules/midas/midas/base_model.py | 16 + .../ldm/modules/midas/midas/blocks.py | 342 +++ .../ldm/modules/midas/midas/dpt_depth.py | 109 + .../ldm/modules/midas/midas/midas_net.py | 76 + .../modules/midas/midas/midas_net_custom.py | 128 ++ .../ldm/modules/midas/midas/transforms.py | 234 ++ repositories/ldm/modules/midas/midas/vit.py | 491 +++++ repositories/ldm/modules/midas/utils.py | 189 ++ repositories/ldm/util.py | 207 ++ repositories/taming/README.md | 410 ++++ repositories/taming/data/ade20k.py | 124 ++ .../taming/data/annotated_objects_coco.py | 139 ++ .../taming/data/annotated_objects_dataset.py | 218 ++ .../data/annotated_objects_open_images.py | 137 ++ repositories/taming/data/base.py | 70 + repositories/taming/data/coco.py | 176 ++ .../data/conditional_builder/objects_bbox.py | 60 + .../objects_center_points.py | 168 ++ .../taming/data/conditional_builder/utils.py | 105 + repositories/taming/data/custom.py | 38 + repositories/taming/data/faceshq.py | 134 ++ repositories/taming/data/helper_types.py | 49 + repositories/taming/data/image_transforms.py | 132 ++ repositories/taming/data/imagenet.py | 558 +++++ .../taming/data/open_images_helper.py | 379 ++++ repositories/taming/data/sflckr.py | 91 + repositories/taming/data/utils.py | 169 ++ repositories/taming/lr_scheduler.py | 34 + .../taming/models/cond_transformer.py | 352 ++++ .../taming/models/dummy_cond_stage.py | 22 + repositories/taming/models/vqgan.py | 404 ++++ .../taming/modules/diffusionmodules/model.py | 776 +++++++ .../taming/modules/discriminator/model.py | 67 + .../taming/modules/losses/__init__.py | 2 + repositories/taming/modules/losses/lpips.py | 123 ++ .../taming/modules/losses/segmentation.py | 22 + .../taming/modules/losses/vqperceptual.py | 136 ++ repositories/taming/modules/misc/coord.py | 31 + .../taming/modules/transformer/mingpt.py | 415 ++++ .../taming/modules/transformer/permuter.py | 248 +++ repositories/taming/modules/util.py | 130 ++ repositories/taming/modules/vqvae/quantize.py | 445 ++++ repositories/taming/util.py | 157 ++ requirements.txt | 2 +- scripts/loopback.py | 2 +- scripts/outpainting_mk_2.py | 54 +- scripts/poor_mans_outpainting.py | 33 +- scripts/postprocessing_codeformer.py | 9 +- scripts/postprocessing_gfpgan.py | 10 +- scripts/prompt_matrix.py | 10 +- scripts/sd_upscale.py | 3 +- scripts/xyz_grid.py | 2 +- webui.py | 2 +- 282 files changed, 43168 insertions(+), 288 deletions(-) create mode 100644 configs/v2-1-stable-unclip-h-inference.yaml create mode 100644 configs/v2-1-stable-unclip-l-inference.yaml create mode 100644 configs/v2-midas-inference.yaml create mode 100644 extensions-builtin/Lora/network_oft.py create mode 100644 javascript/notosans-nerdfont-regular.ttf rename {html => javascript}/roboto.ttf (100%) create mode 160000 modules/k-diffusion create mode 100644 repositories/blip/CODEOWNERS create mode 100644 repositories/blip/CODE_OF_CONDUCT.md create mode 100644 repositories/blip/LICENSE.txt create mode 100644 repositories/blip/README.md create mode 100644 repositories/blip/SECURITY.md create mode 100644 repositories/blip/cog.yaml create mode 100644 repositories/blip/configs/bert_config.json create mode 100644 repositories/blip/configs/caption_coco.yaml create mode 100644 repositories/blip/configs/med_config.json create mode 100644 repositories/blip/configs/nlvr.yaml create mode 100644 repositories/blip/configs/nocaps.yaml create mode 100644 repositories/blip/configs/pretrain.yaml create mode 100644 repositories/blip/configs/retrieval_coco.yaml create mode 100644 repositories/blip/configs/retrieval_flickr.yaml create mode 100644 repositories/blip/configs/retrieval_msrvtt.yaml create mode 100644 repositories/blip/configs/vqa.yaml create mode 100644 repositories/blip/data/__init__.py create mode 100644 repositories/blip/data/coco_karpathy_dataset.py create mode 100644 repositories/blip/data/flickr30k_dataset.py create mode 100644 repositories/blip/data/nlvr_dataset.py create mode 100644 repositories/blip/data/nocaps_dataset.py create mode 100644 repositories/blip/data/pretrain_dataset.py create mode 100644 repositories/blip/data/utils.py create mode 100644 repositories/blip/data/video_dataset.py create mode 100644 repositories/blip/data/vqa_dataset.py create mode 100644 repositories/blip/demo.ipynb create mode 100644 repositories/blip/eval_nocaps.py create mode 100644 repositories/blip/eval_retrieval_video.py rename repositories/{.placeholder => blip/models/__init__.py} (100%) create mode 100644 repositories/blip/models/blip.py create mode 100644 repositories/blip/models/blip_itm.py create mode 100644 repositories/blip/models/blip_nlvr.py create mode 100644 repositories/blip/models/blip_pretrain.py create mode 100644 repositories/blip/models/blip_retrieval.py create mode 100644 repositories/blip/models/blip_vqa.py create mode 100644 repositories/blip/models/med.py create mode 100644 repositories/blip/models/nlvr_encoder.py create mode 100644 repositories/blip/models/vit.py create mode 100644 repositories/blip/predict.py create mode 100644 repositories/blip/pretrain.py create mode 100644 repositories/blip/requirements.txt create mode 100644 repositories/blip/train_caption.py create mode 100644 repositories/blip/train_nlvr.py create mode 100644 repositories/blip/train_retrieval.py create mode 100644 repositories/blip/train_vqa.py create mode 100644 repositories/blip/transform/randaugment.py create mode 100644 repositories/blip/utils.py create mode 100644 repositories/codeformer/LICENSE create mode 100644 repositories/codeformer/README.md create mode 100644 repositories/codeformer/basicsr/VERSION create mode 100644 repositories/codeformer/basicsr/__init__.py create mode 100644 repositories/codeformer/basicsr/archs/__init__.py create mode 100644 repositories/codeformer/basicsr/archs/arcface_arch.py create mode 100644 repositories/codeformer/basicsr/archs/arch_util.py create mode 100644 repositories/codeformer/basicsr/archs/codeformer_arch.py create mode 100644 repositories/codeformer/basicsr/archs/rrdbnet_arch.py create mode 100644 repositories/codeformer/basicsr/archs/vgg_arch.py create mode 100644 repositories/codeformer/basicsr/archs/vqgan_arch.py create mode 100644 repositories/codeformer/basicsr/data/__init__.py create mode 100644 repositories/codeformer/basicsr/data/data_sampler.py create mode 100644 repositories/codeformer/basicsr/data/data_util.py create mode 100644 repositories/codeformer/basicsr/data/prefetch_dataloader.py create mode 100644 repositories/codeformer/basicsr/data/transforms.py create mode 100644 repositories/codeformer/basicsr/losses/__init__.py create mode 100644 repositories/codeformer/basicsr/losses/loss_util.py create mode 100644 repositories/codeformer/basicsr/losses/losses.py create mode 100644 repositories/codeformer/basicsr/metrics/__init__.py create mode 100644 repositories/codeformer/basicsr/metrics/metric_util.py create mode 100644 repositories/codeformer/basicsr/metrics/psnr_ssim.py create mode 100644 repositories/codeformer/basicsr/models/__init__.py create mode 100644 repositories/codeformer/basicsr/ops/__init__.py create mode 100644 repositories/codeformer/basicsr/ops/dcn/__init__.py create mode 100644 repositories/codeformer/basicsr/ops/dcn/deform_conv.py create mode 100644 repositories/codeformer/basicsr/ops/dcn/src/deform_conv_cuda.cpp create mode 100644 repositories/codeformer/basicsr/ops/dcn/src/deform_conv_cuda_kernel.cu create mode 100644 repositories/codeformer/basicsr/ops/dcn/src/deform_conv_ext.cpp create mode 100644 repositories/codeformer/basicsr/ops/fused_act/__init__.py create mode 100644 repositories/codeformer/basicsr/ops/fused_act/fused_act.py create mode 100644 repositories/codeformer/basicsr/ops/fused_act/src/fused_bias_act.cpp create mode 100644 repositories/codeformer/basicsr/ops/fused_act/src/fused_bias_act_kernel.cu create mode 100644 repositories/codeformer/basicsr/ops/upfirdn2d/__init__.py create mode 100644 repositories/codeformer/basicsr/ops/upfirdn2d/src/upfirdn2d.cpp create mode 100644 repositories/codeformer/basicsr/ops/upfirdn2d/src/upfirdn2d_kernel.cu create mode 100644 repositories/codeformer/basicsr/ops/upfirdn2d/upfirdn2d.py create mode 100644 repositories/codeformer/basicsr/setup.py create mode 100644 repositories/codeformer/basicsr/train.py create mode 100644 repositories/codeformer/basicsr/utils/__init__.py create mode 100644 repositories/codeformer/basicsr/utils/dist_util.py create mode 100644 repositories/codeformer/basicsr/utils/download_util.py create mode 100644 repositories/codeformer/basicsr/utils/file_client.py create mode 100644 repositories/codeformer/basicsr/utils/img_util.py create mode 100644 repositories/codeformer/basicsr/utils/lmdb_util.py create mode 100644 repositories/codeformer/basicsr/utils/logger.py create mode 100644 repositories/codeformer/basicsr/utils/matlab_functions.py create mode 100644 repositories/codeformer/basicsr/utils/misc.py create mode 100644 repositories/codeformer/basicsr/utils/options.py create mode 100644 repositories/codeformer/basicsr/utils/realesrgan_utils.py create mode 100644 repositories/codeformer/basicsr/utils/registry.py create mode 100644 repositories/codeformer/basicsr/utils/video_util.py create mode 100644 repositories/codeformer/facelib/detection/__init__.py create mode 100644 repositories/codeformer/facelib/detection/align_trans.py create mode 100644 repositories/codeformer/facelib/detection/matlab_cp2tform.py create mode 100644 repositories/codeformer/facelib/detection/retinaface/retinaface.py create mode 100644 repositories/codeformer/facelib/detection/retinaface/retinaface_net.py create mode 100644 repositories/codeformer/facelib/detection/retinaface/retinaface_utils.py create mode 100644 repositories/codeformer/facelib/detection/yolov5face/__init__.py create mode 100644 repositories/codeformer/facelib/detection/yolov5face/face_detector.py create mode 100644 repositories/codeformer/facelib/detection/yolov5face/models/__init__.py create mode 100644 repositories/codeformer/facelib/detection/yolov5face/models/common.py create mode 100644 repositories/codeformer/facelib/detection/yolov5face/models/experimental.py create mode 100644 repositories/codeformer/facelib/detection/yolov5face/models/yolo.py create mode 100644 repositories/codeformer/facelib/detection/yolov5face/models/yolov5l.yaml create mode 100644 repositories/codeformer/facelib/detection/yolov5face/models/yolov5n.yaml create mode 100644 repositories/codeformer/facelib/detection/yolov5face/utils/__init__.py create mode 100644 repositories/codeformer/facelib/detection/yolov5face/utils/autoanchor.py create mode 100755 repositories/codeformer/facelib/detection/yolov5face/utils/datasets.py create mode 100644 repositories/codeformer/facelib/detection/yolov5face/utils/extract_ckpt.py create mode 100755 repositories/codeformer/facelib/detection/yolov5face/utils/general.py create mode 100644 repositories/codeformer/facelib/detection/yolov5face/utils/torch_utils.py create mode 100644 repositories/codeformer/facelib/parsing/__init__.py create mode 100644 repositories/codeformer/facelib/parsing/bisenet.py create mode 100644 repositories/codeformer/facelib/parsing/parsenet.py create mode 100644 repositories/codeformer/facelib/parsing/resnet.py create mode 100644 repositories/codeformer/facelib/utils/__init__.py create mode 100644 repositories/codeformer/facelib/utils/face_restoration_helper.py create mode 100644 repositories/codeformer/facelib/utils/face_utils.py create mode 100644 repositories/codeformer/facelib/utils/misc.py create mode 100644 repositories/codeformer/inference_codeformer.py create mode 100644 repositories/codeformer/requirements.txt create mode 100755 repositories/codeformer/scripts/crop_align_face.py create mode 100644 repositories/codeformer/scripts/download_pretrained_models.py create mode 100644 repositories/codeformer/scripts/download_pretrained_models_from_gdrive.py create mode 100644 repositories/codeformer/web-demos/hugging_face/app.py create mode 100644 repositories/codeformer/web-demos/replicate/cog.yaml create mode 100644 repositories/codeformer/web-demos/replicate/predict.py create mode 100644 repositories/codeformer/weights/CodeFormer/.gitkeep create mode 100644 repositories/codeformer/weights/README.md create mode 100644 repositories/codeformer/weights/facelib/.gitkeep create mode 100644 repositories/ldm/README.md create mode 100644 repositories/ldm/data/__init__.py create mode 100644 repositories/ldm/data/util.py create mode 100644 repositories/ldm/models/autoencoder.py create mode 100644 repositories/ldm/models/diffusion/__init__.py create mode 100644 repositories/ldm/models/diffusion/ddim.py create mode 100644 repositories/ldm/models/diffusion/ddpm.py create mode 100644 repositories/ldm/models/diffusion/dpm_solver/__init__.py create mode 100644 repositories/ldm/models/diffusion/dpm_solver/dpm_solver.py create mode 100644 repositories/ldm/models/diffusion/dpm_solver/sampler.py create mode 100644 repositories/ldm/models/diffusion/plms.py create mode 100644 repositories/ldm/models/diffusion/sampling_util.py create mode 100644 repositories/ldm/modules/attention.py create mode 100644 repositories/ldm/modules/diffusionmodules/__init__.py create mode 100644 repositories/ldm/modules/diffusionmodules/model.py create mode 100644 repositories/ldm/modules/diffusionmodules/openaimodel.py create mode 100644 repositories/ldm/modules/diffusionmodules/upscaling.py create mode 100644 repositories/ldm/modules/diffusionmodules/util.py create mode 100644 repositories/ldm/modules/distributions/__init__.py create mode 100644 repositories/ldm/modules/distributions/distributions.py create mode 100644 repositories/ldm/modules/ema.py create mode 100644 repositories/ldm/modules/encoders/__init__.py create mode 100644 repositories/ldm/modules/encoders/modules.py create mode 100644 repositories/ldm/modules/image_degradation/__init__.py create mode 100644 repositories/ldm/modules/image_degradation/bsrgan.py create mode 100644 repositories/ldm/modules/image_degradation/bsrgan_light.py create mode 100644 repositories/ldm/modules/image_degradation/utils/test.png create mode 100644 repositories/ldm/modules/image_degradation/utils_image.py create mode 100644 repositories/ldm/modules/karlo/__init__.py create mode 100644 repositories/ldm/modules/karlo/diffusers_pipeline.py create mode 100644 repositories/ldm/modules/karlo/kakao/__init__.py create mode 100644 repositories/ldm/modules/karlo/kakao/models/__init__.py create mode 100644 repositories/ldm/modules/karlo/kakao/models/clip.py create mode 100644 repositories/ldm/modules/karlo/kakao/models/decoder_model.py create mode 100644 repositories/ldm/modules/karlo/kakao/models/prior_model.py create mode 100644 repositories/ldm/modules/karlo/kakao/models/sr_256_1k.py create mode 100644 repositories/ldm/modules/karlo/kakao/models/sr_64_256.py create mode 100644 repositories/ldm/modules/karlo/kakao/modules/__init__.py create mode 100644 repositories/ldm/modules/karlo/kakao/modules/diffusion/gaussian_diffusion.py create mode 100644 repositories/ldm/modules/karlo/kakao/modules/diffusion/respace.py create mode 100644 repositories/ldm/modules/karlo/kakao/modules/nn.py create mode 100644 repositories/ldm/modules/karlo/kakao/modules/resample.py create mode 100644 repositories/ldm/modules/karlo/kakao/modules/unet.py create mode 100644 repositories/ldm/modules/karlo/kakao/modules/xf.py create mode 100644 repositories/ldm/modules/karlo/kakao/sampler.py create mode 100644 repositories/ldm/modules/karlo/kakao/template.py create mode 100644 repositories/ldm/modules/midas/__init__.py create mode 100644 repositories/ldm/modules/midas/api.py create mode 100644 repositories/ldm/modules/midas/midas/__init__.py create mode 100644 repositories/ldm/modules/midas/midas/base_model.py create mode 100644 repositories/ldm/modules/midas/midas/blocks.py create mode 100644 repositories/ldm/modules/midas/midas/dpt_depth.py create mode 100644 repositories/ldm/modules/midas/midas/midas_net.py create mode 100644 repositories/ldm/modules/midas/midas/midas_net_custom.py create mode 100644 repositories/ldm/modules/midas/midas/transforms.py create mode 100644 repositories/ldm/modules/midas/midas/vit.py create mode 100644 repositories/ldm/modules/midas/utils.py create mode 100644 repositories/ldm/util.py create mode 100644 repositories/taming/README.md create mode 100644 repositories/taming/data/ade20k.py create mode 100644 repositories/taming/data/annotated_objects_coco.py create mode 100644 repositories/taming/data/annotated_objects_dataset.py create mode 100644 repositories/taming/data/annotated_objects_open_images.py create mode 100644 repositories/taming/data/base.py create mode 100644 repositories/taming/data/coco.py create mode 100644 repositories/taming/data/conditional_builder/objects_bbox.py create mode 100644 repositories/taming/data/conditional_builder/objects_center_points.py create mode 100644 repositories/taming/data/conditional_builder/utils.py create mode 100644 repositories/taming/data/custom.py create mode 100644 repositories/taming/data/faceshq.py create mode 100644 repositories/taming/data/helper_types.py create mode 100644 repositories/taming/data/image_transforms.py create mode 100644 repositories/taming/data/imagenet.py create mode 100644 repositories/taming/data/open_images_helper.py create mode 100644 repositories/taming/data/sflckr.py create mode 100644 repositories/taming/data/utils.py create mode 100644 repositories/taming/lr_scheduler.py create mode 100644 repositories/taming/models/cond_transformer.py create mode 100644 repositories/taming/models/dummy_cond_stage.py create mode 100644 repositories/taming/models/vqgan.py create mode 100644 repositories/taming/modules/diffusionmodules/model.py create mode 100644 repositories/taming/modules/discriminator/model.py create mode 100644 repositories/taming/modules/losses/__init__.py create mode 100644 repositories/taming/modules/losses/lpips.py create mode 100644 repositories/taming/modules/losses/segmentation.py create mode 100644 repositories/taming/modules/losses/vqperceptual.py create mode 100644 repositories/taming/modules/misc/coord.py create mode 100644 repositories/taming/modules/transformer/mingpt.py create mode 100644 repositories/taming/modules/transformer/permuter.py create mode 100644 repositories/taming/modules/util.py create mode 100644 repositories/taming/modules/vqvae/quantize.py create mode 100644 repositories/taming/util.py diff --git a/.gitignore b/.gitignore index 362e9c257..2376e027b 100644 --- a/.gitignore +++ b/.gitignore @@ -43,7 +43,6 @@ cache !package.json # all dynamic stuff -/repositories/**/* /extensions/**/* /outputs/**/* /embeddings/**/* @@ -59,6 +58,5 @@ cache /localizations # unexcluded so folders get created -!/repositories/.placeholder !/models/VAE-approx !/models/VAE-approx/model.pt diff --git a/.gitmodules b/.gitmodules index fc7329ce1..05e6e3c84 100644 --- a/.gitmodules +++ b/.gitmodules @@ -32,3 +32,7 @@ path = extensions-builtin/sd-extension-chainner url = https://github.com/vladmandic/sd-extension-chainner ignore = dirty +[submodule "modules/k-diffusion"] + path = modules/k-diffusion + url = https://github.com/crowsonkb/k-diffusion + ignore = dirty diff --git a/.pylintrc b/.pylintrc index 82fffad75..aa29d7ade 100644 --- a/.pylintrc +++ b/.pylintrc @@ -151,6 +151,7 @@ disable=bad-inline-option, missing-function-docstring, missing-module-docstring, no-else-return, + not-callable, pointless-string-statement, raw-checker-failed, simplifiable-if-expression, diff --git a/configs/v2-1-stable-unclip-h-inference.yaml b/configs/v2-1-stable-unclip-h-inference.yaml new file mode 100644 index 000000000..1bd0c64d3 --- /dev/null +++ b/configs/v2-1-stable-unclip-h-inference.yaml @@ -0,0 +1,80 @@ +model: + base_learning_rate: 1.0e-04 + target: ldm.models.diffusion.ddpm.ImageEmbeddingConditionedLatentDiffusion + params: + embedding_dropout: 0.25 + parameterization: "v" + linear_start: 0.00085 + linear_end: 0.0120 + log_every_t: 200 + timesteps: 1000 + first_stage_key: "jpg" + cond_stage_key: "txt" + image_size: 96 + channels: 4 + cond_stage_trainable: false + conditioning_key: crossattn-adm + scale_factor: 0.18215 + monitor: val/loss_simple_ema + use_ema: False + + embedder_config: + target: ldm.modules.encoders.modules.FrozenOpenCLIPImageEmbedder + + noise_aug_config: + target: ldm.modules.encoders.modules.CLIPEmbeddingNoiseAugmentation + params: + timestep_dim: 1024 + noise_schedule_config: + timesteps: 1000 + beta_schedule: squaredcos_cap_v2 + + unet_config: + target: ldm.modules.diffusionmodules.openaimodel.UNetModel + params: + num_classes: "sequential" + adm_in_channels: 2048 + use_checkpoint: True + image_size: 32 # unused + in_channels: 4 + out_channels: 4 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_head_channels: 64 # need to fix for flash-attn + use_spatial_transformer: True + use_linear_in_transformer: True + transformer_depth: 1 + context_dim: 1024 + legacy: False + + first_stage_config: + target: ldm.models.autoencoder.AutoencoderKL + params: + embed_dim: 4 + monitor: val/rec_loss + ddconfig: + attn_type: "vanilla-xformers" + double_z: true + z_channels: 4 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 2 + - 4 + - 4 + num_res_blocks: 2 + attn_resolutions: [ ] + dropout: 0.0 + lossconfig: + target: torch.nn.Identity + + cond_stage_config: + target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder + params: + freeze: True + layer: "penultimate" diff --git a/configs/v2-1-stable-unclip-l-inference.yaml b/configs/v2-1-stable-unclip-l-inference.yaml new file mode 100644 index 000000000..335fd61f3 --- /dev/null +++ b/configs/v2-1-stable-unclip-l-inference.yaml @@ -0,0 +1,83 @@ +model: + base_learning_rate: 1.0e-04 + target: ldm.models.diffusion.ddpm.ImageEmbeddingConditionedLatentDiffusion + params: + embedding_dropout: 0.25 + parameterization: "v" + linear_start: 0.00085 + linear_end: 0.0120 + log_every_t: 200 + timesteps: 1000 + first_stage_key: "jpg" + cond_stage_key: "txt" + image_size: 96 + channels: 4 + cond_stage_trainable: false + conditioning_key: crossattn-adm + scale_factor: 0.18215 + monitor: val/loss_simple_ema + use_ema: False + + embedder_config: + target: ldm.modules.encoders.modules.ClipImageEmbedder + params: + model: "ViT-L/14" + + noise_aug_config: + target: ldm.modules.encoders.modules.CLIPEmbeddingNoiseAugmentation + params: + clip_stats_path: "checkpoints/karlo_models/ViT-L-14_stats.th" + timestep_dim: 768 + noise_schedule_config: + timesteps: 1000 + beta_schedule: squaredcos_cap_v2 + + unet_config: + target: ldm.modules.diffusionmodules.openaimodel.UNetModel + params: + num_classes: "sequential" + adm_in_channels: 1536 + use_checkpoint: True + image_size: 32 # unused + in_channels: 4 + out_channels: 4 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_head_channels: 64 # need to fix for flash-attn + use_spatial_transformer: True + use_linear_in_transformer: True + transformer_depth: 1 + context_dim: 1024 + legacy: False + + first_stage_config: + target: ldm.models.autoencoder.AutoencoderKL + params: + embed_dim: 4 + monitor: val/rec_loss + ddconfig: + attn_type: "vanilla-xformers" + double_z: true + z_channels: 4 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 2 + - 4 + - 4 + num_res_blocks: 2 + attn_resolutions: [ ] + dropout: 0.0 + lossconfig: + target: torch.nn.Identity + + cond_stage_config: + target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder + params: + freeze: True + layer: "penultimate" \ No newline at end of file diff --git a/configs/v2-midas-inference.yaml b/configs/v2-midas-inference.yaml new file mode 100644 index 000000000..f20c30f61 --- /dev/null +++ b/configs/v2-midas-inference.yaml @@ -0,0 +1,74 @@ +model: + base_learning_rate: 5.0e-07 + target: ldm.models.diffusion.ddpm.LatentDepth2ImageDiffusion + params: + linear_start: 0.00085 + linear_end: 0.0120 + num_timesteps_cond: 1 + log_every_t: 200 + timesteps: 1000 + first_stage_key: "jpg" + cond_stage_key: "txt" + image_size: 64 + channels: 4 + cond_stage_trainable: false + conditioning_key: hybrid + scale_factor: 0.18215 + monitor: val/loss_simple_ema + finetune_keys: null + use_ema: False + + depth_stage_config: + target: ldm.modules.midas.api.MiDaSInference + params: + model_type: "dpt_hybrid" + + unet_config: + target: ldm.modules.diffusionmodules.openaimodel.UNetModel + params: + use_checkpoint: True + image_size: 32 # unused + in_channels: 5 + out_channels: 4 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_head_channels: 64 # need to fix for flash-attn + use_spatial_transformer: True + use_linear_in_transformer: True + transformer_depth: 1 + context_dim: 1024 + legacy: False + + first_stage_config: + target: ldm.models.autoencoder.AutoencoderKL + params: + embed_dim: 4 + monitor: val/rec_loss + ddconfig: + #attn_type: "vanilla-xformers" + double_z: true + z_channels: 4 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 2 + - 4 + - 4 + num_res_blocks: 2 + attn_resolutions: [ ] + dropout: 0.0 + lossconfig: + target: torch.nn.Identity + + cond_stage_config: + target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder + params: + freeze: True + layer: "penultimate" + + diff --git a/extensions-builtin/Lora/lora_convert.py b/extensions-builtin/Lora/lora_convert.py index 5843c7ad8..fb314f258 100644 --- a/extensions-builtin/Lora/lora_convert.py +++ b/extensions-builtin/Lora/lora_convert.py @@ -112,11 +112,12 @@ def __init__(self): self.converter = self.diffusers self.is_sdxl = True if shared.sd_model_type == "sdxl" else False self.UNET_CONVERSION_MAP = make_unet_conversion_map() if self.is_sdxl else None - self.LORA_PREFIX_UNET = "lora_unet" - self.LORA_PREFIX_TEXT_ENCODER = "lora_te" + self.LORA_PREFIX_UNET = "lora_unet_" + self.LORA_PREFIX_TEXT_ENCODER = "lora_te_" + self.OFT_PREFIX_UNET = "oft_unet_" # SDXL: must starts with LORA_PREFIX_TEXT_ENCODER - self.LORA_PREFIX_TEXT_ENCODER1 = "lora_te1" - self.LORA_PREFIX_TEXT_ENCODER2 = "lora_te2" + self.LORA_PREFIX_TEXT_ENCODER1 = "lora_te1_" + self.LORA_PREFIX_TEXT_ENCODER2 = "lora_te2_" def original(self, key): key = convert_diffusers_name_to_compvis(key, self.is_sd2) @@ -142,13 +143,12 @@ def diffusers(self, key): if self.is_sdxl: map_keys = list(self.UNET_CONVERSION_MAP.keys()) # prefix of U-Net modules map_keys.sort() - search_key = key.replace(self.LORA_PREFIX_UNET + "_", "").replace(self.LORA_PREFIX_TEXT_ENCODER1 + "_", - "").replace( - self.LORA_PREFIX_TEXT_ENCODER2 + "_", "") + search_key = key.replace(self.LORA_PREFIX_UNET, "").replace(self.OFT_PREFIX_UNET, "").replace(self.LORA_PREFIX_TEXT_ENCODER1, "").replace(self.LORA_PREFIX_TEXT_ENCODER2, "") + position = bisect.bisect_right(map_keys, search_key) map_key = map_keys[position - 1] if search_key.startswith(map_key): - key = key.replace(map_key, self.UNET_CONVERSION_MAP[map_key]) # pylint: disable=unsubscriptable-object + key = key.replace(map_key, self.UNET_CONVERSION_MAP[map_key]).replace("oft","lora") # pylint: disable=unsubscriptable-object sd_module = shared.sd_model.network_layer_mapping.get(key, None) return key, sd_module diff --git a/extensions-builtin/Lora/network_oft.py b/extensions-builtin/Lora/network_oft.py new file mode 100644 index 000000000..6d350671a --- /dev/null +++ b/extensions-builtin/Lora/network_oft.py @@ -0,0 +1,49 @@ +import torch +import diffusers.models.lora as diffusers_lora +import network +from modules import devices + +class ModuleTypeOFT(network.ModuleType): + def create_module(self, net: network.Network, weights: network.NetworkWeights): + """ + weights.w.items() + + alpha : tensor(0.0010, dtype=torch.bfloat16) + oft_blocks : tensor([[[ 0.0000e+00, 1.4400e-04, 1.7319e-03, ..., -8.8882e-04, + 5.7373e-03, -4.4250e-03], + [-1.4400e-04, 0.0000e+00, 8.6594e-04, ..., 1.5945e-03, + -8.5449e-04, 1.9684e-03], ...etc... + , dtype=torch.bfloat16)""" + + if "oft_blocks" in weights.w.keys(): + module = NetworkModuleOFT(net, weights) + return module + else: + return None + + +class NetworkModuleOFT(network.NetworkModule): + def __init__(self, net: network.Network, weights: network.NetworkWeights): + super().__init__(net, weights) + + self.weights = weights.w.get("oft_blocks").to(device=devices.device) + self.dim = self.weights.shape[0] # num blocks + self.alpha = self.multiplier() + self.block_size = self.weights.shape[-1] + + def get_weight(self): + block_Q = self.weights - self.weights.transpose(1, 2) + I = torch.eye(self.block_size, device=devices.device).unsqueeze(0).repeat(self.dim, 1, 1) + block_R = torch.matmul(I + block_Q, (I - block_Q).inverse()) + block_R_weighted = self.alpha * block_R + (1 - self.alpha) * I + R = torch.block_diag(*block_R_weighted) + return R + + def calc_updown(self, orig_weight): + R = self.get_weight().to(device=devices.device, dtype=orig_weight.dtype) + if orig_weight.dim() == 4: + updown = torch.einsum("oihw, op -> pihw", orig_weight, R) * self.calc_scale() + else: + updown = torch.einsum("oi, op -> pi", orig_weight, R) * self.calc_scale() + + return self.finalize_updown(updown, orig_weight, orig_weight.shape) diff --git a/extensions-builtin/Lora/networks.py b/extensions-builtin/Lora/networks.py index 40aeaaabd..3f83e7f3f 100644 --- a/extensions-builtin/Lora/networks.py +++ b/extensions-builtin/Lora/networks.py @@ -7,6 +7,7 @@ import network_lora import network_hada import network_ia3 +import network_oft import network_lokr import network_full import network_norm @@ -32,6 +33,7 @@ network_lora.ModuleTypeLora(), network_hada.ModuleTypeHada(), network_ia3.ModuleTypeIa3(), + network_oft.ModuleTypeOFT(), network_lokr.ModuleTypeLokr(), network_full.ModuleTypeFull(), network_norm.ModuleTypeNorm(), diff --git a/installer.py b/installer.py index 205f16cee..b5fcd8335 100644 --- a/installer.py +++ b/installer.py @@ -591,6 +591,7 @@ def install_packages(): # clone required repositories def install_repositories(): + """ if args.profile: pr = cProfile.Profile() pr.enable() @@ -615,6 +616,7 @@ def d(name): clone(blip_repo, d('BLIP'), blip_commit) if args.profile: print_profile(pr, 'Repositories') + """ # run extension installer diff --git a/javascript/base.css b/javascript/base.css index a1cc4878e..b5736cbf2 100644 --- a/javascript/base.css +++ b/javascript/base.css @@ -14,7 +14,6 @@ width: 22em; min-height: 1.3em; font-size: 0.8em; transition: opacity 0.2s ease-in; pointer-events: none; opacity: 0; z-index: 999; } .tooltip-show { opacity: 0.9; } .toolbutton-selected { background: var(--background-fill-primary) !important; } -.jobStatus { position: fixed; bottom: 1em; right: 1em; background: var(--input-background-fill); padding: 0.4em; font-size: 0.8em; color: var(--body-text-color-subdued); } /* live preview */ .progressDiv{ position: relative; height: 20px; background: #b4c0cc; margin-bottom: -3px; } @@ -94,7 +93,7 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt .extra-network-cards { display: flex; flex-wrap: wrap; overflow-y: auto; overflow-x: hidden; align-content: flex-start; width: -moz-available; width: -webkit-fill-available; } .extra-network-cards .card { height: fit-content; margin: 0 0 0.5em 0.5em; position: relative; scroll-snap-align: start; scroll-margin-top: 0; } .extra-network-cards .card .overlay { position: absolute; bottom: 0; padding: 0.2em; z-index: 10; width: 100%; background: none; } -.extra-network-cards .card .overlay .name { font-size: 1.1em; font-weight: bold; text-shadow: 1px 1px black; color: white; overflow-wrap: break-word; } +.extra-network-cards .card .overlay .name { text-shadow: 1px 1px black; color: white; overflow-wrap: break-word; } .extra-network-cards .card .preview { box-shadow: var(--button-shadow); min-height: 30px; } .extra-network-cards .card:hover .overlay { background: rgba(0, 0, 0, 0.40); } .extra-network-cards .card:hover .preview { box-shadow: none; filter: grayscale(100%); } diff --git a/javascript/black-orange.css b/javascript/black-orange.css index 30e069777..4677d4db4 100644 --- a/javascript/black-orange.css +++ b/javascript/black-orange.css @@ -1,7 +1,8 @@ /* generic html tags */ +@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSans'), url('notosans-nerdfont-regular.ttf') } :root, .light, .dark { - --font: "Source Sans Pro", 'ui-sans-serif', 'system-ui', "Roboto", sans-serif; - --font-mono: 'IBM Plex Mono', 'ui-monospace', 'Consolas', monospace; + --font: 'NotoSans'; + --font-mono: 'ui-monospace', 'Consolas', monospace; --font-size: 16px; --left-column: 490px; --highlight-color: #ce6400; @@ -18,15 +19,28 @@ --primary-800: #9a3412; --primary-900: #7c2d12; --primary-950: #6c2e12; -} -.light, .dark { + --highlight-color: var(--primary-200); + --inactive-color: var(--primary--800); + --body-text-color: var(--neutral-100); + --body-text-color-subdued: var(--neutral-300); + --background-color: #000000; + --background-fill-primary: var(--neutral-700); --input-padding: 4px; - --radius-lg: 2px; - --radius-sm: 1px; + --input-background-fill: var(--neutral-800); + --input-shadow: 2px 2px 2px 2px var(--background-color); + --button-secondary-text-color: white; + --button-secondary-background-fill: linear-gradient(to bottom right, var(--neutral-400), var(--neutral-700)); + --button-secondary-background-fill-hover: linear-gradient(to bottom right, var(--neutral-700), var(--neutral-400)); + --block-title-text-color: var(--neutral-300); + --radius-sm: 2px; + --radius-lg: 4px; --spacing-md: 4px; - --spacing-xxl: 12px; - --line-sm: 1.3em; - --line-md: 1.3em; + --spacing-xxl: 6px; + --line-sm: 1.2em; + --line-md: 1.4em; + --text-sm: 12px; + --text-md: 13px; + --text-lg: 15px; } html { font-size: var(--font-size); } @@ -244,9 +258,6 @@ svg.feather.feather-image, .feather .feather-image { display: none } --radius-xxl: 0; --text-xxs: 9px; --text-xs: 10px; - --text-sm: 12px; - --text-md: 14px; - --text-lg: 16px; --text-xl: 22px; --text-xxl: 26px; --body-text-size: var(--text-md); diff --git a/javascript/black-teal.css b/javascript/black-teal.css index bef9280ac..095c9c4d8 100644 --- a/javascript/black-teal.css +++ b/javascript/black-teal.css @@ -1,6 +1,7 @@ /* generic html tags */ +@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSans'), url('notosans-nerdfont-regular.ttf') } :root, .light, .dark { - --font: 'system-ui', 'ui-sans-serif', 'system-ui', "Roboto", sans-serif; + --font: 'NotoSans'; --font-mono: 'ui-monospace', 'Consolas', monospace; --font-size: 16px; --left-column: 490px; @@ -34,10 +35,13 @@ --spacing-xxl: 6px; --line-sm: 1.2em; --line-md: 1.4em; + --text-sm: 12px; + --text-md: 13px; + --text-lg: 15px; } -html { font-size: var(--font-size); } -body, button, input, select, textarea { font-family: var(--font);} +html { font-size: var(--font-size); font-family: var(--font); } +body, button, input, select, textarea { font-family: var(--font); } button { font-size: 1.2rem; max-width: 400px; } img { background-color: var(--background-color); } input[type=range] { height: var(--line-sm) !important; appearance: none !important; margin-top: 0 !important; min-width: 160px !important; @@ -246,9 +250,6 @@ textarea[rows="1"] { height: 33px !important; width: 99% !important; padding: 8p --radius-xxl: 0; --text-xxs: 9px; --text-xs: 10px; - --text-sm: 12px; - --text-md: 14px; - --text-lg: 16px; --text-xl: 22px; --text-xxl: 26px; --body-text-size: var(--text-md); diff --git a/javascript/light-teal.css b/javascript/light-teal.css index 7778caded..b8c71d9c4 100644 --- a/javascript/light-teal.css +++ b/javascript/light-teal.css @@ -1,6 +1,7 @@ /* generic html tags */ +@font-face { font-family: 'NotoSans'; font-display: swap; font-style: normal; font-weight: 100; src: local('NotoSans'), url('notosans-nerdfont-regular.ttf') } :root, .light, .dark { - --font: 'system-ui', 'ui-sans-serif', 'system-ui', "Roboto", sans-serif; + --font: 'NotoSans'; --font-mono: 'ui-monospace', 'Consolas', monospace; --font-size: 16px; --left-column: 490px; @@ -34,6 +35,9 @@ --spacing-xxl: 8px; --line-sm: 1.2em; --line-md: 1.4em; + --text-sm: 12px; + --text-md: 13px; + --text-lg: 15px; } html { font-size: var(--font-size); } @@ -308,9 +312,6 @@ svg.feather.feather-image, .feather .feather-image { display: none } --table-radius: var(--radius-lg); --table-row-focus: var(--color-accent-soft); --text-lg: 16px; - --text-md: 14px; - --text-sm: 12px; - --text-xl: 22px; --text-xs: 10px; --text-xxl: 26px; --text-xxs: 9px; diff --git a/javascript/logMonitor.js b/javascript/logMonitor.js index ca372b1f1..bca665672 100644 --- a/javascript/logMonitor.js +++ b/javascript/logMonitor.js @@ -1,6 +1,5 @@ let logMonitorEl = null; let logMonitorStatus = true; -let jobStatusEl = null; async function logMonitor() { if (logMonitorStatus) setTimeout(logMonitor, opts.logmonitor_refresh_period); @@ -52,10 +51,6 @@ async function initLogMonitor() { `; el.style.display = 'none'; - jobStatusEl = document.createElement('div'); - jobStatusEl.className = 'jobStatus'; - jobStatusEl.style.display = 'none'; - gradioApp().appendChild(jobStatusEl); fetch(`/sdapi/v1/start?agent=${encodeURI(navigator.userAgent)}`); logMonitor(); log('initLogMonitor'); diff --git a/javascript/notosans-nerdfont-regular.ttf b/javascript/notosans-nerdfont-regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ba480dba349c0c36babc351c20513e7ff70557d5 GIT binary patch literal 2269328 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�d?dvMEN-A(x}8iw{0P01D|)i^mhi51siyvpg-<5pcn`@}@_XD| zXp!Yl{?18{!{PNh93KAiclx|uAO525H{y${c-?JPGvb4B$dBQ0xyr%-a=^ntCHRT* z_k2&GSm%-K#dH>}xW`j<;ueN`NKQm1CG-IT+F=&4rYoQxs6Orc9_|cYoIO$lrVMSq z4!>T;-)9!?c#PjHBL9O4ZMQ)cVZw0&4|0+I1cb`TM=ZGFmao9jxWuqTA!3a3>5!Cv zz8f!i&jMt2D}v+%q|`R6%~WB^2x+I5S}T0T5eWD?wE39B<8idqTV8wN1iH*uETI`R z#Q6F!Z-@2&|8`i_)}?mH9gjE!S#HG?U_%&$Pf}Ab1C|GaHwNwTq08g=_UmYiS|A~w z#sC)Hy7iA?0$VK#So;u=%CmvDC;R7T|^W^F}3e=O4;MAAIF$&(7iO&YCbv@2Ee8DacV8@$d? z+N9N3yd57}8VE1*#S8|k(d3LZ0PX2-_m}(JVFQ9QR9Z7)=5k(7#T)5Leui3)=JWP5 z;Y|CHU2VDb;IT!l4L^s#$AyFC>^8&HdsbV?-t`x^_4Tz6I_m3d7pB>nwjkmWi<=?} zDGvf#19CC6LV=Nk_k^slx3eiGG&iP`ey_u3GSG-|tDTC6>VO|Ywn_jS0hPP12#_6! z11|wS;hF&%Fj#+UyVs)8ni@h*k3ntGnvK3fmFBy?MQY-ycLf( z=Ltv5S``My5O(*KF;L#Dqao%r8Lh~O;6qn;vl;eDtRKRHfGrPRAljXFkPCzx&W}k` zFii*wwPFddq@v9gk+TecyV-TMCzut_xUP1Bjnsb0tmvCwwuU{%pQ-v7w89+na#a|+ zxAsty&EeA8MD1a$hx3%Pr$!W?o; z>=iy;eLTW6CgS!Jg|ST=MK{O$tzA1iJ2mBU*=#1$ zo?UAGgC9ut0u;W2m5+D+7XR`%yY;a5<`q1k59BYZ7|z{4#jG3 z3>jTbtb|N@q$oNa7>HQ1x!f(@aNx1p=h;7=epLK+Z9~H6OTaGuX#|!l=(DO;VP94S4UGo7 z$z-R$h9-UOG4b1{A7%eo`}||gmzG^FkHZ(1K0D)&*aAka!IqAnY-&2*)O1%<)1)tv z@TrZeCY4d`O(wl2LzBT|)oQJH)+I)2v{sA4D2eiSP6z5wush;>9tevEr56`ZVGf3? z0WzC|rU5^A;%J$sfq-%n=AF_RElV%H>e*f@VVH&5Rserg=AW*}Pfcp)IoFqG|yUZ=ZWC1RhcIUgh^TBjmLwlisXuZe) z*^gjZ5y$yjGefJMksX^s>^?9lA~utw0_w(fSY+xrMn5`+Se_}kc11aU73K^CAjRt^ z9k?g$OS2J^P`To>sPC;+doa7*fq>f;U>lb{aoqQF+)xw@eqq^Qgriv~iMNRzz~iVs z2H!)jDXF4Us^V55+r1lcA{F+<;WrkG(QwRcinHUP+L6q3W-c5p7T-7=!$y zUn?}iZ_~=3rKUZml4e4wi09rQZ=^$8k^W%oBkJJdV3#XC8?DK_8ZdR&WJ9 zf!)RiyW^Uw4WzmVUPaJ1*k%wTg6!O~4a7$MCQ7uib5qs5cgKZ9DjE*J-Gurfl zDPDLi(Ugdh__4AJrE&}W|IU>?`7NK*<@33mzEiMw9AV$A{j=YJ2snHpp6~>N9{jC& zyFTh!!FsZpZ|O`6uy$%CSTuVoh+f_13BEp|RGi_u? zuqMnBGanJmR>^EVB3Nyb)pkU%VeIWkut)$Wlk8wgQ*y(vY<8!i-vo*~&CQ76|9|re zni>BOTR?MW+nE-Cw(tLgKGJI82t2E!C@yW!xv#+nu2U+AxmHr@q$B^s0eS7q$eMfc zgI_E&gQPV4h3~{ZB+r4@zLJ*BxvvzoO6|?yfLdzg|Ngyabt$c~H=ff<{z{^j zRqBaTjC%Kopj-K3?BU2WfmnZ&Q8F2iyq>wg-0T&vuU`MZe=|%kmLmep@D?H{zVO;R z&->-&4P!y+unM|ZAXt>d(ENCkua~RC6M)79Dt=l4}=42 zDW;f?PdCpgMy@0BMB_?@J|h5>J}>F!0I)5InDQ+$?EoByE3VKlM3h3>%(50^W`+7^ zXgFtnA$@1XZ)TB~g4%<#gi{;hI+8(vpn&csbx5s>rN4`C3bO}ynpB9X5FvniBt3FG zYmm5!Y4j>9k~uL6=}+KnwTW7d0cjjWd}}mOu$>4l1muDMe}@DDNlB{$O{XXt?Rulf zX!q!JieSp*(uaa-l|C74)tHs>j6tVzB*OuX)0U2CiNSotVQgtemXLhjVv|DYq(Ki7 zf@IL%6jhqMZOA+q%vci53WeQhiFxe3od%WNsCAbe@Q!AZT9wgcP(?zL--KTr7PY=r z?^mT7&33&_ua5-+`gUJXZ&G{mETn6)IgKX0)^D+E;yN8TC(s=lBt_K(9g;^Us@!&# z)FN90)Ip2n!F5q()+jY%T&LHHS_CR3%*clZ!A1&#d|9hh>l~7()rwAq&S=zF42Z!e z+bcAppRfk@Gg}JskYH5{2N$n}M^5`uCO*Dssr%pxER3uo_n-`XJXW>%ZsARf$RN+l zY^%!>y^9}akFgXcXk8Cfp3wuB)j-H{uPFzjD_IY$tAUVp<@&3LBf4J0N(hkf%#Jl1G4S_NYXO+<2VO@a2F_48Pcok*Py;g_*zz1&cHd` zfQ10rZhfHgj(7Z8`e`kZ2OysbkK!FSo%;@IKWMVVv)%!G5WRz6yTCh;#+xJP*~B~E zQTa9dex7~*RgivC^=82(UV<+4XKdN1;XTzKzYm2UhX)(?d6`W(&F_Q$Nh%~y`5_y@ zIc^YkRx4K^DSblG`sDk5jGK>k*2vmS?_J!oENjKlZ>+5;U1U>)wB6?nQAZjL_Bov^E3 z;3k|ikLBC)MQ&oHOxCbh$OO0YM`V{|nc?|X7OMTIw0~`}ux?+OKDW*fXV}i>R=bo_ z1qZsj2ZE}cWCv)U3D2P|PTrQ|a$D?NSr()n)SJ99%cgZN>V*kOG#;>4QDdYnm^EO! zs~gwPdoR@Q3*CnHa0T2exX~<9z$@kGQcgRl{c?Ph7=Mn1s0q}x+K*UhscE01Ml_$* zEWV#MV1E1uV&>AYVtohEa4P(yeHLTR{)J-c1k-;bmL0KN&r%zJE&3HOhu^`PG}9hZ zjgUcF*anz9;XCUA#sICy=uea^(soY%5l8-QIvL|JR{sgl8WOl}pnq%M>g?#~6yMd| z-PMJ^r~j$DyZe0aTGDNA$J!@VmMnpQk)CuZB`6p$=?i z9R$k945oQ72%>T@i%@r?y6A1BkifB~y+b>dPVzpWF4sY9T57)XoQf?~NWC-^n|Zf< zhu3K%|NM3Sng8=H48>x7NT{0s%{vNnBfj_>;EtaIA76qP5T1tF6MvZS1MzCML;5l1 zh3aDlvU@Q2RAEKrY)@q}C36vtZI6c?e#ZdFBb+{Gzx3mhI_R**5@}~kkE);v+HLW8 z+S$~LXNs_+z8~|@joB6!8mj=k!3d16cVSg=Gz_*6D`>1hJ|4D)K_X{nW)-aP$vR?q z8D0m0jqHv@D@Arq7A~$W6xJ4tbA`fOG2R}Fwa4Qfu~^6Z@nwm=ETKSmtRudB4b?&0 z$5>GK0(&3$RX9``$J93{$Q9f zedQ~-732A4{B!-%J78jm0|B1IdVm5zwE~ZF0n(q|fwJT^#cz0rjox7|CMyo~5oiNkl1!*t=XWK$wjw|On|ORQvs zqKQge*ApyV-WT{@ z_bd5(bq?R3bsZRr4=h@R4U11f##DVwtHj!JtC5Tj%+c@%SJoH75nICXpC}Y}m5PaC zDTdF*A_9^I7Yo8u(tg}W5??Fl8MqYsF0o2*DUWCbQjRx3{=1SkXIgb zHo9HdVl<$Npei@SmZ1}zSd)7}z zuo7%IRE`oG@SnE^o124eTe|vJ(}#2ap7(;%?)QXR;)TA5)TM5YyMpdub0XF32f3Kr zYxguIlTBWm&*h*}u|i*1L8XwmotN_2;raF0{msc_qsQ(;3lu`<;!WZytnCzbk`V}! z->bsAmjXdCP{QyN4fRoeY7Bf6Q)?UHp$hxlK&-2XhJp-2h;0V`_c*p6MDVKj>zE3V zI+bD{$pObf14HBl=(1y)rHD7|-24WHYlyle7K!uRN!wK`o&j$Qnfbue}749ulPz;IXtB@X-CusMeXxS ztKlA1pD_eMbU!TxazXL=teS6x~<7%7!P^58it)zKj9I{r;wzoY4d2bnIn_9oGMqFs=4;qGUpa#=?i1}MB{nYys52)IIn(Kzp!J|r6=*@q6Q@OR z3oYbt^=x=&XsX zb+t8gYV;UHluz=6_b7alf!GlEgn%44b@aq1ac^?!=%aiRK%E*8chD(tg{H0-^o!sV z%`9q5!7*?|k$J~CrcmLS%^UD|ptA0`cwL#`LVSEw=N9F(ZFU&KL(`pSLkB!aoQ zJSRPR>t`OKoZ}DQ^67V9lA9uZ|2xnSdA;v9QHI*&r##^g3P0IE7w}U&T@-%W{3LOc zVsURqDW>pKaFYTc8PEj;=n4wWz*%uLlfS=k^Lq3e5)wI&UH+zUDDHZsTi_1jHcc2_d0%SgXQChPVodyS=!gI5Qns8+n`Y#haHwrosTRV=!7p`g;6VfUk@9!m}H=sf! zA6I9aJZ7-5Q3ok#LWKm*Riw~TFAR^ICB$Fg6n*Q5rXPFk;Nj`{+3AJpYY&L_zQx79 z#pJi_ee$t|<;D3AfBe9$NsIVO&;U>pVh-fpn~x>|E|@ZS7j#IlrnW7Z}bbs&oA+=wcLLafI_6StyWH!Qg?qL=^)>8(I~` z@(i&)i^t+O0%RNJR2dp{%2EtLh$^W}QcMVc!0drm!OnbQ@$=VQ^S<}J@4;)Xd1k5P zz>%Z-X17l-&ajU$OXR19E60|vyKZ^xN&}kFrnC+Xwl(!=Z@!uEsxdA%)(=(2G%BiA z4cH7C6&w>NHsdZyiIK8|ZZoiqfo0I=b}_+K=q?02ZX&ElfCDR_>cHWVD#~E}BJNSe zo3p^+fyN`JuG}_$@?dR2Pn)lxE5EfkH-E4oP+fpF30PmyRQdvHT`H-APB0N3 z!$1{L^%;*T@>;vcVRvOy)kG)OrWOkP~YvDHcPr81!jIF ztJGs!N{=`?k2mc&94}Q|+f$C{XcZJK5T&sNG;DPCx`xK9hWxz_x24=ZGQGNzQ)QdB z)i_sHXGZMh7Pq6OFh+e_+0m+!dH_E#^Cb-Cs^Ua{W3?$%nADXtQ9xQco#4*)N`O(M1z34&?xmat#(NbM85IXxUwDb7bVEH#GLQxy!RBvn$5K?qG18&47GJUn9#<8gUB9u%v^jfd-YG$1}6AE_y@ z9x5Mq6{dTQ7IV3MaOkdY?AiAdwxgx-?D_g;b|h4xRkv#s`e*C@sekI>63-Jb-z6H4 zln+^wgiIJ4Ve?>&C=>-a2- z`h#R#ay&S)w~QwVw-9oCx&FrWb*(Lp z$Jk39{{gayw)J1M7nPKd+9(Q(7z_1XvB0O-5UzzN!%2@^FC;?>dYxLoq6k`@PBWK? zh^Qu@!2y(Gw^?zE#puO2@FxixC53dN=TMY~VAjRhXmR@P>0kbG_suumc;YK}|3)Fu za|;U#JvVU@IoE#_B@xC2FFkzQCpZsvgj?fBqnVWl!r=@UF!&hbDo*?W3D6k#0#M^} z1LQmvKfbZa`zJs3sq0{^}U=jSn zIXOK)2jT`jLqImvrEm{C_L^2JyT!p!2sBXTDJ(hVGEw9_$KA&lTngBf%UlKClo5xq zZ9=G#gHG@FQ@Xi{>+~j=&ZMzgu@smY>f6bI<}2THwQ{(IwzE6uPW2{yWP18>Rp*)I zooCy{l+Z$N&q870d|&T;h^=1n@a(|(ITiva^T6u;6SEKR9Y4|4bz*$-Cj1SyfgVG3 z*S{q_O8Se`7Sp3-enM*|aJCKJ&=QnS;?M?~19TUlb*#$Vq|>6^4ec|8ggGJ-XCm5R zS;R(0mkIfV689S!UjT^oPf`-vm4Tnh)eBWNMLQMBq+eV-;uz z)f0J=|NPT|^!AL@yu4J-<6{`>8<0ovS__{Kr(vKL`Y3yKz`ZYntP7@?`crS6)^o0W!&DQ^0}DVL3_sq{YEgg@P&UI2ScBjg=P55FJn=8mLtu zbO`pl{^|TLPfoHw9q{!B90hYF3rkHVm47JEIX1X$d%1pOpuzuV(e@vn>n)}hvvqbc za{o|Ke#dZki6oWQ6%>&CBqE-}nv&!)PzNbWzegUeii$@`c1m&%IA}zIQx=+N{sqZv z)N0L0>vl1e*#jgGODSnx0d_SruODW%kTyfx@vYW`jN3cdeeKlHOMGFQwbk5pyX)-Dt%%jC;<|L%t#)L zLv#hcz5JcXV|O_cY&uDssjIADp>mRoEOpr2YPYr7?ml;prY(Vw1@Rk@k5-^-d4=|H ziy;{u!2x210|aH}S`9#^01ApE5^w_o)o_K$o(=eM`FOIkoH<$EY)_ii3NAMgi~D%3 zo7Mv$?y3NigtZPahk00)%Y=vsD&<9w*zwr#Vz|(Qx-ECO!P3$SJLsMDt=SF3kq2(Q<=weuIZ3SESk-tKf8N*9k?>Su z;Zq4sT@SJEyyrdNNpa>BIx%)Smq?BzV)Qo3({D5<*p|YqtXZ_Z^2+)ze*J6F76z+URGM4A{>URZGm=rc_+IgO zXn_l1y#)p=@O)6kDf=3zHeKW;OHoG(RdJG;LZU6~q*mEmKE?+msyI*rtWG%IOum58 zh}c$S(7nlPBmYp4R%@!8JDe0&?W!Pm%i$V6foLh}zRHmNA zz_9IM5dz|f4^#w#AEtqGIQA)jU#Tq&QK3GrK-Rj+8^k0?&jd~bO#$!Wm!os<$ZDTyv?IT~*33<;UpiRTtx2{xGtAtqq6+5z|~TrH*Z08L@79<+sZNk<~jPmf2;<5k|~Qm5*7?^Wd# z)i_x{IwY>Y1Zb~Mv!uqif8de9;o7_IyRDLBod_)64)JBs-UxXS8w1vA~3=knn4YD8BsKz*ywmw*2;X_OGwKc9q&yA=)}3*8sHZ zlT0=L`|DT)J?h%-Jf-l!|4R3QCU#*gtWSlRmIC9H_?S*=E6Mq&_ZSBcSGG6{XT^WQ z7heuHlFtU~ZnT1Zm35~vAt^4#v=}O6F|ty`rR_h`SuBak{w54OIWgPd@R$-3tXV1I zCnGP2$9f*Tr@U>pSgY<;YfHMl{=1*uPksS7bJ6Gf=b)WMI3W8x^$6kNph{*8zRtwh zFRWW}hG&o}B;#IiPG;a_eqaDbV#`N(GgcxSqFgE*aCl6*j}$+2l?MaJ`*3qQl_0$M z^Vb&MnH=h`Nk#|v6m4a*@iiiOA^W!v{M($QY7xbs{|`n$V_!rNs)L_#@U)oq4CgjZ zSUOX&+~jvcL>u(vDNaCVgSu(7(1Tr)2X;JU0AXx_czDI3x7a)W3$<9Ir@yEA5qo#! zr&BQb{#!hhv#TaD2t(-OVt4=gN%rybRp`n4Dy(qqn97Wi#$YX?p$jXtUC;};@oCk2?@mFkuPe5IwpiV&lVT#cz zJ}6n8lL4IID9Or91>3rEGJRP-vnj=vie*5WaI!AuDuKDp{Zz8g2U;BTwfHCwD~uwr zoH;n$M+hvv;}(a*lAe>JSAbWhPT$qNf1ZF>=J(%~Ww&S14^b{UC+II(Sh+HxJKTY6 z8FYO`Jx|=A3jt+{h|)amatt~*XwRk;4s>!V%PsS`vIF)2G|((#mYmKZYoBtEP9A;K zWp(lJ%i_Yz?9BeVySnZ^u;;Gc-n;i4J$T^gwTBK_EZHapvfDpEg+chyZ-03I-iNl& zJhXrRL)!=LKXc~3`_7!X|5sVr*;(;Fm>%XwxJoV1>60NheL@Lw4m3hc5p@Z1O9H9S zO)Sif(mK69pPQl|Tj6kkvKezv#nT!o0yrJ%+In| zvgqd#{Ds+AvRLwsZ)>}L{Kk9Rt{LlbI z=pK*SRltXHC=yLF4I+6z5^N=&*2weh81n4GR{98)T^D{LC(^a`0h4I3X652n2)bRZ|_sf~7;FN?kq+2bGL zv_c-{x9NpSc|#2tk5qa?6Mdn@i-UNF)-2K&`62t5RA6|&5QbwJvM!8x10>8D`dzw? zv;g{DNTX*)C=rRCDP70!7xe3QM^1@XNVh;=OhsJ6ik+Mzc(9xEkvnofQ9XPI^fw$E zu!zWCBSKsP=T~FXWk^~*z;Bb7J}Ws>N=(rwt{52FJ!VU(X(_2ImUJ=AWJ(*Of748} zRY)WB?`| zxuhtd2E)Js#gNv|M4<=(j8a&F!8o8oNUA*D@-L28h947t%%@Km#fCSBY|T4`9$om10I{An6m{vqV2v3;uY??WpD$S`n z#1BwLkH!OtzmPwIQmBBR#yrAY4RT<$qAYbG{mYvS(O#IqaJNl8R|j< zT8~v*#?so(wYQ&ZYdzP|ajvy+skC&duyDDwbeY)?A31vX@X;d$1LG3|0~6!mg^iDj zJB2-j=b>U7z?Qirwhypq1Q|=`XB$T^bIo(OrboIH-=o@iar2r@j2ArXZ5$^DxFFK2 zczx+-9me4!$Hxa$EvlmOl2QcG%EAqm9=N%28{0T5?hy`40qmt({){4Q9cVgmkrM#< z7~~V}rk(UVk}T+Pq}u`h90r0>qs@?+8+JQ?WCL`4R0@)@g5r4ux=Uc`z5*tsj7kyGNo;j=%qVi;;z{oWS z2Dz&%w+rIN!{qUpNd+%sKauT%aJzcw>Z=*-0?+?KH7k`3gQ*`~`J3n4S=*f_;m0Se zHUr0M%NO7WvZ3BK6Se6$X>h2Jx+)S7H+-p}7Po8jC|c#nwwj@M<+FUX9D2JXoLVe` z1LrC4O~GDoT=EB(s=8-GeWB2{ipE{xx`lj4{y=W?NM2V#$y9at(5so|@eJqWaA{|) zBd2XgPtTs3Y-_jGwynRSqtfo`oNXJqvW~``k8ytlx=OBqzLHrOcQ&BRfvg=rZiGT; z++Yk0+gIRotH5t+o{Dkdh$&37b-?T+``4-_JpSpt=5}X&ZgEfEp{b&6d0CG3+`2}4 zU8ta^AbaUxdRm_`v%NOIE@aED?r)r1vSc)yEG^~1GBks#M(~%$>fiW->TQT6RS36+ zZAI{DB_U={%@uN4qs((BnQMHI;$NG=%-B8lXZ$qK{$ z7rjb0Cva`zypWh!$g4)UN1OXYL+D}`PAe}h%n$gyKCcmS*l41(0cdMl^5F2E5R7~d z(Rf-#TrNROg;Q`AMQxt%Thpv=6=^?hS42KD{h9VV?x@{W7&-tS=v6yc7kBR(3b#$> z<>wvizw)T)DekbavdF`1I`UBDPPRL8FB=Gr&s}Y_j94?eTG|?WJL@ZJ-B}Ge`OBaa z`q)S>i*BKunPg&QltJ?<1(=>8S&y}KumlZoJ`>bEaQ##A6MZgT^7Bi4q?2!@Y#}1F z7Y!WY4ZzX90=nU#5|dz%!E6MY{Va@fo)(}yr(pA1By?=`1CIQg@MpZ#g|waNP|S^KZ9oy&30R@IN**Hzm-+EiE2 z8qC=yx_eq3g{j|~{dR1R< zHZ$s-LLgMq8th&VWfGt^juL@!5MgKv?!(Rm1WLFoQIV{;)PuvwgMbeuevpSBY!877 z`qC)np~g<8Dt`l5puD_bD0gx)Z>DljuxPb*WO9&^D`t&nw6VE|h;$Iw-QaBt)};kcmvLt#4}aCK#8>hl|l_ z6GxZ;lPrpK3|McR>r3ED-7NBw{JlwfF(HBbBoSK73Rhfwixy3ik@!eike9n}D(qrH zRb@#r>8+%+dfZM2!6qYynmod$O|~Ig!9fps`iW z%IbVi-H}jTiW)bcy95D@oJ?=XR0b%kv_PIWDJ?1UlZ;KhdTS(cZ7l1hg$knnpH+8kePhQCz4JDC#5%M4S!jq9NjjrhXbk#Wi zF&;CD@WLPpio-#zG6FXhsehDogY6EytAjo>(=)g z!@0R_LC(8lpc};yJ)j$)*e*>sEY#mkx43&QO}DrUhz~XwY?BG*NHUCj z@+8P4V425wN%!^V5?1x|jsX9jZVvwcA<*=;qV6i-?0D*{UfL5LK z-PX>E`SA2~%c1h+D@X!=rmg|G4^zlr$<4@^nRx-p!Cls(N}I=rb? zjgMXnk&0(8;=A|2u5!@)5yeLKzlgY&N<&q_{NPt<$!vVWbUMOZB1L`IX2ASVvdu z;$q}?M6ZpY^e6C3K=|ept~j}arkb+6)@el&zOae5NNh@@fd#EdY|6Ld(p`}70VMWC zcg1AS-}=SjzvVkg{-CFt6CP7pbR$((mO%mLPsH|?n@{jMc;c%IL^lZ{sH$Lac94tj zk42_@Mhp8Gq<8JQZ<^U>n0O#X86mC1V7 zV0%oc7v#mHI!Sh;yO#@gIYy|~AV#RsBUp~#k^d$=O?_^TSWYBW{_Q27@yv(~*g0J%)5cx*wT*C<*4t(VKBL zY%=saEQ5`wg@?-MBHd4X5uvnLSZy(3ZZ`6}XJ_XV)C==0_1XCawu&%XG^EzD^9KFz zewTRs$$d8`V)%)tY0Q+vC-;;{LqItb(S??9lNr%^AXWOSn!feQm6sahrNlsl=A zYj3rhcz+>lrZv-KF*{RI55pm`#4jNU6QlFu6A zDb~+HjP-M%b-^_Y{F*;s`Wivns6@T^SH#zOo2D zvMvx;G0R?HGxugd&#vI}tg67tjGb()!pcCO$Qhj&-_|%XH9l5SU0u>xQdK3seyet_ zuWwF!&pn!X`uEn)sGF;*T2+7fi@K$%su_O!TM;98FOLzVZ7f~dPma31cd}C9{opg` z=9|(Do&@->p(Ig2h)M%flDNu)5`48Ls!b>=5B~XBTUS@x@#FLJ$9J?fH@D_Z&*b3* z3T_0H?>6zQu5{j5}c+@#e}HoFedcf`a*og50|pBF2i|1$bHVhqaX_(J7=&?Ol5k+0yA zcpunvsX~g&?NVz1S6)#D)sI{M+@BaEdK_=->TYH4CyEeda0BSV__J}ViTmVZ-;Oj< zxHT?C7esDN6dh9@mvD*4X+f0eBe59p2FIRCM4>s$YmK<=D{NY5e@2iI3n(}#(@b73 z(CjyYu>MD)nfTQDA7z?h+$g1C+r-Dg5UJ#OMlO}ekm4m^gQImGW(Lp>B!vx9JNQJM zDpj3|OC_U5W6q}&NQw{Or?q^8au3WoIpX8%Lt@irvccBpP=Fk4JowCK@-?Ke7q8q z*yZR4ML3GLreRy36MM|ob^1H`flktR?0|umJHQHNumop^Fs%w|wkT_4Ocfu)nff?n zi!5;;C+V@}^ABG9IizcJjkN-hU?68`$0O(J6wC$%(F#SnOr!v+g8Ky#$cG2l)IiI# z&x(&nzS9!<idw7B0rkg2|{nM#jz3pwx|sV8%1Rns>F3{1$Iw$bwasqyMcw-K$h~+a`t?&Z z^4Rx(t(%4v6i0Es{ux?fNAKf_@L^f`nD^niych@JT~JyO2he^QpK$tZ{Q$)%7-eh~ zaJnMy9DKP#9IZI`Fg{^IhuFp5jeHi3U@(?SyttztvmqyN3%=h7E2cf%g2D!JSAfy9f=10dPKZ1q`GlMwuCzHf zrBSp!3ss?M2vH^Jby{Qv0Gxt1oj@)Sc_*XFRtl3Ydgn%SYHjb{zLD{5fw4(*a&_;n z@|v*$?+ANWUP<{>>tJhMdtPKQub^_Qb*MSenv11S#_AHcVXSVUH5|s8BeX8Uk8MJ-`Cg5#&U<;?v~CPca1ml zXr4P5ZfU74qpe?p;O!maSCA6n3%gNfODg&j_LYnrC?ZP%JvGQdX<+DYgT^xaG~aYk znpxlNZ#jDXt{wk!^_-dMu3uZZX6T;LiBekIeBnN}AO8cuGxg!ihlXVDb+S>R@FYQhmeeDb*Q zv}eS#V@@y3x>56k)IdzyPfFzoK8!|7p#yKCK9K} z?q*Ug5!s<8>{O}5%oe2lZU#?jqad1xN{x&>lc^%7bFgp7SuWQ9_t_(?>FUudhC{Q3 z(~~<6*8e;uFOdab zsf)6f0g1&-X=p5i9`qt2H-K}+Dr26gSCN3ujBP}$&H)bv7&L;wJG}xHKOLn?dcnnj ze5g3w;C~2IR4LgrS~UUylyj~4)mitgw>Rx>dhth{2TEL_$^5r5&FJdR#re`bCHXt` z`OIX3D85lPC zc%gTQ5_;Iki9Vb^d3ntlgwebnvx%Bp`7Sp?3aCL1Vg4U}zjt>@PSHr!JuIQ)@Q&@X zrF)9UPMo~u)E6FVz1rq%$;uf0eA`e<+o0Jxn0wXA%3hL_9^e*yUHmFydf7sAxY3Fc z8R6E6_SsfZJ_H7+5>eBKb1asP1Y(+imS@T~W#is76E)lhj8lBWZ4?^GZ9`>f>+atf` zvfP9`sb}D;q%neuM76DBq@e*e9zJQxHB^k5e>ziprJ)@LhgQ4uB%GV!Z5+4 zDmU^;5(4Pr!6+xev)4Z{&K$8Ny-MfX=GLcG)dF=Fi0{}R z6i1Y4_XD|ALpmage2Luz!X2Ot7@}Rq*0&+sR6Sb!O{Fu0m?ZAo;Bks2Lgq8zwM=4U$ki5Fc4caV5-=GKTi%8r2NPzc@AJ*yldGFNtW zDSR23)%hm8`t+JQ1=|=V3_A`w>jY2O3A)2Sr6Txbu4_bFE>MmG@1$48JNB8u1^4%{ zqMy8s>aXxqO(cVZ8-HRqBEKy_o+F-Xi3ptp@fw$F2?3^vGVmYp9C;5D9~m{#lb*VZ zDM;`aWGvpeb9}DeJ&<44&{pMb@-GhN_WCkC4LJo>*%jX0X5Z~ygIQgv=>@^OJWGnN zuCT4&XlO`H&GXsawp3%FhR#j=QN2fe!Qe+7KH=i}A%BV2Lh#KK>Q5;u0=k5I!sg;s z7%@da8HUE7V>ub3UPtX}DYedrbfiS4(FyYz(uwcVyVd~88r3WNG_*YU5?Xhh2P#i@V6 zwG{YwIt9TQbOt#H1k$$&B0-P#I#pVf93Ti0tz05PN8v@~Wk8sPN^n0qyn`8MCo|u9 zvs|!r(>t;U4jq-tmez(e=?QQO41e!?;tT(E;G|rxblvW=XYS`EOV55Ey@zdK)Aukk zg%^=rD!8DzRTCZ>9RR6-q!%2}&D1H#>XabzV((yS=^f>yaAW@_XjBP2I|Yr(^_(}U zFjPdjpopq`5T`JD5l9xh2IoWU^_J^-V6w8jI8>15_hx5OhFp?fEs>r?S|2O5BFr31 z)s)$>qCB1vxyB?jZYo09vIbSb?8ggQj`G+}`(7u@UhSBii!tAydeQfcD%iFwTqt@5 z2Y9fjv9`8x3%C7hem}2hTZwWV(Gkn|pg^GgF zrimYlR1m!NQ?549C3IHsV-rQv8CDY#Fi6FOe;P-nVy_f27jcXUfaTbAvb7?kQ82Ce z<9*i!b&+3lPI&@=q1^&@b=K0E0yA)jhtYS@xw!={2{rGKDuVACggh z;ENz?^IniETT}f^i`*WK){=_TkaGpz5#!zgS5SJ98*@UuHUWL=u*hPRDPRoEBbkER zV2FRyM)b$71h_jX{sPCDIFG8oEmz}6jeg|?7h(sV(I)c-Jnus=s!3c*;pEi{E9O{Yg912 zVQHvK&**os->g@Xkce!Li3EAw)3NSu$b+Yqbx(s;0C)p2i$R9+b;mnM5RAQ}th*EE zvIeM;be>BnagZdOz_g~Q5trFrUUf=8ldn^$u?V(=nldG<-?Y+mat# zjQshuA{T%?62G3ai;q^9+3T{{`1&ibEI1;`F#MG`fAAOiB@z*D=Q%ZKrh?olRJUSv zNn?zn&77bLdcG$F!C~j%Nir@3O`!z48~-r^n=xsXy^Z~jg>ORgg>muV*AL7_{<60J zrNd8$-dUhUDR}zt6U^yqZDx`iDW;LnvSuV1HAOxrs;|4A3;xJYE#a`)HssksL*)CM z*4qvpr9}AdmK9gQo<*yf=!&b2Fu#;{loby`v}+So&_!YB>lk)CEhd>kSWTS{yBS@$ zX^E8mUU1R%YU;DK$pKsL2^YV~<%H}DPN17&oPZ>%qCzM&G$lI{Qo#&~=qzw~W&pkg z3xFR$P;+|VO&H>;c`!pUaYgRb$c5LPfJxzaj_I;ZOpyXHu@Dmn zS9tc%)#X`AGAiqB=Dx@)L!>=C&Siup@v+%R`4AXKYt{_sF0Q7I=o6}%%3u1P_-7KwTowlQeD#|P0|jq^`&;6)S$1i`kYGxF zbTRT-V_s9S$!##Z^K#8;oe5d88C0EKK=wesSO~=m+JO%3?kF!LipYs9 za#BP|=Rc{Y^lYG^nbKrP&g6JVPGbBr9V854Tqkdvo||(t=6r4!67l@I*Sn`h|KQlz z;HTYf?(amt64}ef!tfBnoS`x3FotCKJG0>P)4?5ynkod8WLIZ4kU>$w$`jhLiX8p_ zG)^+;H>49HgFXIZk-}uM!z6YQ&~WjTEFlXA9!eC9It&{*v9eE7#Gth%RJ!s+atgAr zrH@0TYBFrPN7ugl%I;4#O!W0li0^EE%}{PiaOCKp_*vwI*DJmt&Nfd^W9*AD`US#X zi<$d_6HwBb2$PZ0Yd||VFACO$51;gLfnOCBD4 z0^}wEB{iDU(IIAqU0Xf{gc^-~%QXlVf~c{XD1_ad$z=!(miu@d8*U{sab6rdZqj)X z{4jvMtfSl@Yz*${?4cb2qZAuW>6L+($U8Ecr~|OcTxCRJJ(|vcYKqlHu9y;^d)*V6 z8DI~17%C-s?kp5Y>W5VqM#~TS&e+~eBK`(V6uzQgdBBvo9WY9GA0crQ^(i(Pk>QPp zJT^Hw2_|{g74fjQxY&7If@fIp*Y7dzWJ0%ql8WsP2MkGaZcJ|pBS6$}C-rniGds(%Z-#;?aAMsJ4Cn8Sx`!!x8-z*Ch^CrW|E2@ls>-(|Z zCBW06KfKR9yhyPJJ~r9WG1=ZO&LJH;Ec*#09sC4l_;h(RQ41%F4hE+Le7KcFOh9qp zC&7cqo#8Ms9Cj<_?sD2a4i6B>vaQ*;ACo~kb~3a!V+A%J8L3tiq4_tXr9Vt{_$$C0=+%-IO*HXvY zecQ(FTV?epuDa^P`qSr*9Xki&$YU3;f)6YW8J$jwgkE~=Cnhzb?1%_U=W$0b{le?F1P7F6`ZrgAtNa4JfuKCxh?NTHA|b6&k&EoYIS)50!CO*Q z4C$QVg4jiHJw&g8;Shbf<#Lpb1_SnNCWHzCrNL4Jo&0t`kZ~yCB8z2_=_o@8^E7uk zm7J%+26zguiI-{cfDIN|c0a)N!1B8GH{N)pYAh$IGPkxlyV7qlO11mGQis-ccKaJc zy?sOd0|R;v$pX+~UuTu8#;Y+~29LI}-7R*HH{0%u{D|YUEY0l)xnw$0u5XNv<>Ho{ zU%U_WH_&$*dmC<`{>lQd#Rxl`$1MdC=6zVjWR6h z+2(BAWx_`Cxs0SnQz<70PfcMsj7g?@D!}zTc~- z2O)U{GeM`5J#Hka5gbJ6cHET^Z>M>nf;-;0`LX3{_``D$Ch>b6`8oOAHZWSfUO>Y_ zt+3LptTN*(u5nZY1KC%eANZd;k$C2;INFt@${1)~I(l@uao7q4>#3(Ee>>8~nkS3H zIh}Vji)-DU;nctMydT9a44N<9A8E`$4nxY+5)_S(IffMZKe)Kh>RJWQz$ z@a$6NKF=MJoN>3PA0HSyX3Zv{efG3ftBp(0UZbI?+1=n*mKzKOfLEY=9Q4a&xr+Nph6eWsIGR0L!fYWUfgi}sWTi3~**90tKD~0Na%WFT zze8k`dQ$>Q;#7SqZF`?CP1l{QwvFVDwj~wSIeNN8&-q(Nk9TCI`im^@V?M?_IjNZ` ziH@{}UhIOm_XG-i=I1`5O;6oB{{mJWV-g6; zNlf~%f|0QXgCjY~Z@nci>sc45xSTHBdag6=@aT44``{`CFs>rBDHG%K)UX--MKZ!cXQA8J64J zV^^qgrmR_|?oLtJM(b7%HJ@5a4_PO(uCx`Kx~GD-kG7>}w(aQN3)*z36Yf4bGyya^ z_Ia?``O{-Jbc*7y{-|N8-;$)t`T8%DQ4XGK*w{vRbU00dLUp(TC{2O{^m#KBPM`y1 zGqDpFq4%gt6-|ARfP;uzaVUFzS^gHvBT!cb+#0zGgH~29SBP3Gv5pAXTI|4Tr6ySu zt0dXAR(RUFckrg*vHyz3JProKELusj|Kum!MHRLAqeUfRniBKCaH=4amic*;hx5jy z5#g><9Q5($4bB)ja&exYC4Btc4RP^#!$bXaIo9+NS8tv-c{zFVc>}jiAbQ>?kyb1o zUXjdz8;VD}Jr%&)tBUClZ#*p``EWSAc=XtgrlAZ~a>vNXIZE3|u zck#KiXU`XVy4UXuFD`yV3)Ous%CU%BZHBkJK7YH_n5iZE8+MP?$In4e1;@}xWn=`SwcOUljEAffU);4; z#1aZU9)EpDMNMSlChx)QJ#F!8y8hMZQnFR7U&$l(8kZ}#vZJD|$NS(+UUxzJ;N}8H z7nSp3)kc!|D%O?Gn)6{B@_c~VENY}6lKT*Y5ux3{oL6_s`_l)9B3ByYlt6ya%I+re zEc1Pu8@o;kH*iLvumtIs#K|Z~TyDgGXNw8@L{r8}>u~!(jv~0%tPvoqWlD zbK0*|`X_k@I!bMub-D_XUjxlCv(9 z%2ohlRbGZ@ygMCDp@0gk zfxI$@v(A?{G}yGO@fzulJ1{>XuyI(d1b;e(+AwgrM5N-#!;?7PgIZXG_)y{>lrh5K zjNnwE(Lr4B(W&Lp$NgHz0C_R?tN{kN2Lgnae&#h#J6zRC86wO6+7SUPvE@m%>t zsV~2*))5$LZCtF|C7nNy2nDT?u<=K6QhW((G^yBJ>2TtKe1JEFo}l~!w}E1Fr9df= z<57w}3L_XCBYPS#4Y*KvAYj z^BdFE)s-WU&K(tBiVV04`aPb3LiXkL$_F0)1N(mDpXG5r$lefdfQ^kP8StI}jE84Z zzM6QF9OZZ}E`w(bCIAP4g)yFqXqg6HyE6HifL%q?9jN#s!amaT_S%Y(JLhJPRNs?m zMwX=^E#c7IR|g*b1G^!z!FEQvb2`k1vI+~UUw;L3N`+o<4*RBne64ik;9syO%V?hzy3Q zX>O3JguNaO{7!0eXDHb-O-u?&aH~tP$;jlT*}#^f=`3c}c9(jc$vK8lg-JiHN&=V; zVrt1rs>#{=ru%?jEooAcq}iFtWHif`OsPGSnwiyHU6-T^H+z^rrJy8NugfhBrAB@T zG=ra)%xC8`mL{@-$hQlot)?9{ECcg0VqOwdi~`|YI1@RgKme0Ogv}*1q*OT(utqwA zMuXFyg42Euj-le^g6OiM(yoDXxp?PZ!p(34MJcH^8}$otC~Uc5Gb+L1FYQY^Cwsw>Y>jR!>G0=1&*Cp&j954kH4uJ%}q>RbAM`K#;iz5OkXYxyO6 zN_WidzN+;V_Kw-9(YeILuufN1URPWHifVav^_ukbQESFfQ$tVdFR;WIv2`Pb{ZRb0 zkT1L?oP~@bVS~u{j0>_D*8V{jNAZVc{2fO0bnDIih%88 zVkl~kT#gTOTA_tSgBrBWbS%o&{mpL2idXxm53qkDLbf{m~%YaY0 zkg@1>h~*^L0y4!D2$@uk*&>KIOG(J`4xktXdOt<4r4P_BLU-X!Tn@rW4O$}9*1aWah_E@I%GEh{VAx0{ys?4fzbttF64kPZT$hHgbjfKf@wknjXS{^R6QmXoiHARx2NjP*xb z1^y=yI&78Vwt|WQgV$_XZgvJiPFT2`Hmzat^xG8a#U;gl=P z_j|GUE@ee%LdX+`{U=XnGvPmOk)oJOd_CC8Q?w4zRfsJP3&z%Crg37TQ5yY`yb8BQ z7pIr)iY*A2HHF~C8SF2M;0?Phsfl{t_gm0oZb}FZmr;_dIZZ;&a}J_+6|t^_FeKz0 zjBlYQA^z8>RLOqp_f{gu9-^j%l9#t8e6(ao4s(v?vnSSNU5{!k5^#nk3)x{CIWm=J zVcYU&WuRCK&$33tVAe{ULkM30gD@rMxmDG#C0*o8&gu+ST!M-txv%0MpgRVIaJa!u z*dkmZJ)qNj5_MM|0KSlOF9Y_YX>gq!;?b!8>a0p=#PK4+If?5q! zg~&R*on_oUKGZe7eOv9!n5i(y;4+pJ-Oh6IL;f6hE_$?@*F<*l5qjafv_!O8o%qNYk?_Ga{l>WlQ{Qt2Sr%pS|Oz;$)XA zlzZgJ*kJ$A_R-p^fYpC zE?Nm>+M!P>v_Rq)sI(%-dE*SjttHumr9vqVLB&u!1jm{SnTK?&#n-2BBRA&CfqQ@! zf_Zy;+gfu<)6KQH3;SyJ^zG3DB;hL5&9^se9jsH^|cw_JkfYQ>lHX}bO2B!YBNQ-4I1vFUJ%fu3tsP~L1YFvBNsor16mvZVRTSqo zJhZ$zt~W`CIHV-ZC1?_nQ6{G)Q}CKfzvZu3fXhLM82>PLmbiw zml*H6>#6ck_741F3Va~N)lADr5P%u9A%Zno-P+cA?R~B7Z41e&jG>aT_Liwm^x`D1 zSjd%Jmin#rp?AEa>wyQl+{N9qbB5(T2j>@d%X~JrvBVz6`XmdZa-2Dbgr*ur3aH>h zxwu+=3Q08B{d43I=eaA{h(4&n0Sqfb*KGM1@pD<#Fp$5@7n=Z*GRj!KGR&(c>wZe( z5&yJ4gs;IER88;^-R;LPNYmr9K6pJt8DYiOJxF^GN=Hk7G`s zpaDGBNuV16*CPI6;Fq3;yNcf-R}5Tm11|#z_+0S1XW1zIpVvc@)=EjnP}`L<0CW5@ za4;__EsT{I%26}=TYS&VbbfRpmi7*`PV5_Qoa{t!W32eNU!ApGbkD6t{);aJo4Ntb zf4+l%mggeB5;MBLFKY{K=|0G)@Npv`*CuvaiO_3S=s*e2%pNkzk6>YIN);oFTP}@osA3U@mPgee# zOt7*DN1ohQWa|}SDh-oAr3(UHVN?W+4skwKuBT?-btr0)*Hl(tS!XNXmpViNGM*dT@&6SjcLFvrLT8cjw zGoszyz>Uan0(cIJ$Fwp_BcVmhbV&Zw>$z8sa+-*(QI{*TwSW#aSvGtF^g8-ubtS=D zhrJ{_PqpMW-;@#HRetK^v$0plcY14_J1e8>(|7j7@y7P@#@oe=k-p+aul7F9h%dj) z90f(b5*aMfQ@u0DiyS(YgjjR46(eicWlft z`OBgB;@MyJmX&xM**Pxp$DK7zLulFb5(;Ct)Xus)7rs(xw|Wc9z2Jjp@cq9d2H_C$ z$n)f*4TlO5xS#3BkiqbwYrqYoWI8!fLz|Q5%p=@x)DAnSK-R>=4QNHk)jq{NOpyl} z$BSzsuqdE&ygoWr>Y+t9NxoL^K~nd=CNFOT(A4f!24ZK284 z!P5GYrkr~lZw`#KbWXL0@(QY*fhunhrRx}X68Pb(7`G84AC{LggH8bfy&TE$Nr`X# zq&`O`wFPhDQ`(2|A54YKggjugra4RwLn`_dq0*fzPco3d90EgmV+D&6-kk1W?3OF; z=<2?6-`+dB>l+?nN008i^2#FzOV-4f+4lGC+xO7y?8E!EzqM#!{lGg;-}<%(PNMZG zA2a3BxW#Y5SLzbrd51R~U=y&7p@gpSvGeGK&}X&Svn(#F%j{Fbt;dvt8W}4LRYwp4 z4oX&%b!j?pzgSpw(>2ItIezrSSbbJwQSOc-!#(|j{R6%1<-E0yW9OT$9JuO?IJ4}{ zto6G(BAq+OhE^9RM|NUZ7{K<8681&toMz#Oypxa%zyxe9IVOz|jq-~UNzEh&1@s}5 z2Z|EX%i&i=&Wj+NB~m-KeyAq@4P8N1^tgbR$Vc5UT9sgi+l@27;)&yl9Z|`~PWl|l z$v(sM&R_p}s?DIb?G(SM9u~!wJ*z#DBI&->k|z0FGO%;dM=io}dHjixRDwwbrX|sh z-_+6Hlo@1i4jRH1Ho`1<$7z?NoSGUMa(Nsb_QjX*Swy`_$Ui{ck35TLa|I%*{T~5;1Tt>T}Tv zxT2wMdudg^#dLyw^j(Xo`MS>jCRF{EZe#!9YM*S_dwuzi_Rj4UNuJ7Fn>V?qr{3&v zM0!X2oBDi-c|CQVs1}8uU9+))F@MvTs$UUD#pj;kSU^Bmdycehd|r%+Ii&<+s(K$# zpfh5hXDFZBuz-(>&!LM_iI^0Y7I7Vyf*G zb4$&JY(q-W(%EwM*{;_4rS(5cx~}}d{K~&k!iHAE^I4J+v>PJq<(2@Np%!BiNdR7%;>SQ-anQJ^Al_{D~~ zDF-f)0_(q%g#h{xKn9G!Z)c-;8c_{BETMpc74S?Y3akVr;eY|_*aVvo34kO7ILa}4 z=wg${)>D=QRbtld)2F+SU0pDiADpgQyQ6p40AU^XpP9Jfw$zmV)YP&2b{_brAifX0 z=!8G$IqW$r<(9)6M7>}Mg)}ONI1k3*WuUh_w@nDHC5D$lrzVM?c$AF7Yi_EoJk1_C zIKO**$k*nycj#|EI<(*3)wO5$#7dfRz>rEZBe)S_*KT~xq!rH!!W%Ej=wffY7_-~E zgmGXT`336H1y3U#M546B7X{-#&!}A=uO~Of;WDAUB<)S^!0GbZY<|DZ=8gW#e(Xp2 zj@Rjod@lypBqHDSNp_xn0kTIc6hpm)3IV?{TY+YTB^VO0h zko!Y9&-064TpCLP?Ted^H&|ImhUfVY-%bq*^?&M*1QFFM`Dp_ zxVV2~_QAG55>7IsvPusE9<{)Asf{y_8tI_)3C zDV)nWlm{=zJq6&}s8u#_k_vK#3Zr!S8jfHi4G@7Z;5o7Xj5yyt}OYcJ@oJs8@S8yu^9`-6H`*SB==Mn z-W|GMYbC~{Zk^$Q=E>Q#T9o^U%IZA zkNf$>wY22-Hn%0Hx+P6{7$Op5AKR!E+aXiD!cBz3rG?OdU5405RHFC=1W6?=@`wqB zjR{MjF#N7?ds1-jJTh7oSKq}K#;(CFa`$xD`8*2%*OdQq8M`H|_?$w{>)bEdCpT{vUbI`A zDUBX-7u7oA{g4GtYhOwp_MCK73Gv}u4Uzug(bUh zsJZfr`az4v)HE$aC6uuk&xzcb=JfFcbi09E8M9%0Sw-Qc%Y5-41s;0>7*6Xv4^@k1t*YWw!f4=vtU~rYD zjqUAc{rww4f8?#*0vgGMG}R@h6&kXEf(i@V{X%Q%Dpo|Z#Bj7WDuxeGe`Vc2qt10vV|kG=PR zlkBMOM!RnAdpqZxy|Xj3v$JV7&*nV4%0gPDRn8&^ge0Q*2sQ?6Y)le3 zfWbD&AP_;AXs~6l3_KfQWIil3z3+eOc2C-sko@`k-tYb1TaCJJ-&?oBsZ*!w{Og=k zD=b({<(sX!aqiHe!-o&-Ob&R1uC_o&`~CN`?R8jQy6bnB6Uj3N3>+e%kF3p3V8;Ob zNwMQ@$2=8*_w?O$hZ`*e#IGRU8E+V2jA#vMm%XIZhAoh;*KF6Mn@Zs)o6FD8|zcA(+<@yLhc|?r#`@Nd&)hXVPJJxk+{s_D=b(?qAh|#b>Sdxu_(f?!Mz}9b z3-me>4XY81OY{MV^t&9E7>f}tg79`jC!4e|cQRj$8YI!4sqU|h ztnaJUcDC0h%Qn$AS@+8Ry3Y?zqVQxp9aczHO+I{Mbe!zJIQg{QX^ zd!|nw3S4@)tyn6L_jR-r>U6^a5_RUTO+BkmqYpGN3xc%x!H*S2>HMd-U z-A&tf^6u>2sb6`l*_}J)5Bxr1k6YjPnpeJwt?6I0Zsx9@kr?Syo#5*<`KJqHpuj9M zoX_c8!%ONqDKjZdFiTK_iRjaLgK7^O52z`$?}jmY z*k1G=Tqfw~+ZM~xOhZda;%PQ~^&KA_m~B6B$#u!LZ| zZQ?Z#&25@_?V5Gh-^$*|IhXo{Sw=ap(pm0k4=l)p%nP-rsky#Dd+_w4?J@V^02RCJ zHU!zpQ+t;2L{AbeFeHJM4HPW0 ztZLaXU|OS?Er18|MOZH63H8BA$9&Sl0D@Xh#~-EKDj8KTF835Q@Bbg@@w?mC$3MoL z{nt-5{%~}?psT&_BPm@t8lr?zE3NcHOlx=-=cE zoSD0N4f+F`DeMD|V11egjyYhtqaNw$ZY$>#gpYtt?27D%uIR(EkERPQRmo2jA*Z06 z@JZgZYak&ZkOv1xUhu$b&)3zHXw?U&DxccDL z_CXe_ZZunos!MEEVmdJZ8Dh&b3jv>52m86pJ5IN%FFuip>1azb}%7%8Tk ze%`OLERPBy9&Uy=c$!6j%@~D+VBRr5JK2nG@THAw>Wz2s&=9QLEH{8PSj2+(2DA-D zO9b?a0eqpU)4M@lth$KY%_>lQ1Jw6ofgnES^M^fDy|r-tqSyu?HpPDcp%DTCCi>Za zRot-`IF^yr-+%gRKl$=!F8xTTWABayH~VShT6>2_bQX-qzTdmHy|M6&T=?u0R6KYH z<^2L{9xrN&ct`cNOd9h6R`3q;n1tZ9xGzE}FRzLhgrfi5)Y~SRP@h^6Y9guFYoK); zKJ&L9+O>cGCExu0orgbk=e^Lh*~2_265Hltmo>hSV3EeN&M{}>4w&0FzQ)q7+cN$q zvS%8Pk?uVbB69FL&V$bbKuEAgiNdnh>`2tk4(z_k-Ch&6r%qi((R(?f-<%SgwBA>@ zhl0U)DDDm9xRpE+*4)VtaQ4l*VN}>}@@s!p+`%`tp?+`KvbXWGxgBi7-bu#%4JR|O zbARY0vEvVzr}~4hI9d4UfvR%nB7H9beSM&Bl4i3+97;X*z+huw$QYeWHe+2;+yF9_ z<6+WCNXb?4HeFA9gxg_u`AKc(RB%yCFqP`2rb_r3b|Y1j1t) z6fawnh@1C)ESS%6E-He9nshgj28d8JjA$?Hoz~&9H|Web413C6o^JoqkLLC+c$oEg zM_m-Ri&E#d{+_ixPkj}!uQ1jj%Tri4bkkYA2Kb=6D?_Urm@(=iOLIYp;l||}HXWK$ z4_ZsUGhgIGpX+YRbaSq-K?p4BCH||%r>y^qYv7t4e(M$@Mjv3KF}O(NUdlCU*2)aZMLDX)CLaYE!vqAqg>Dgs{hTo z;#K>5cjptCnc~3E;D*1uHd{D*d~ka*b)b9wXl3IMm>lxg+9zgwt|^Bvn@AO-ZEFMm z!S=1Q9@nfZ+73_hSUb@kKDYwK!3bLpn!e3x>`%gg;c6_>5yA|tEsFd|BJ5K_{F>kx z)K5yI-1fqWpe6$6f%eq$QR0Ego2vAp(>4LCzuez=cz^Bh zkG6Yc7vZf~r{Vl`A%HkTiuf7uCi!x@fB-y+by~%8OC2$h;dkFs|5{UL3chZb|794M zD2j>lfrW^fXhlW_zV+Nv6284f>Cozx$p9UYi9%OLu9B~$VAtWLbGfj1TLF}ikr$8l zyW|{dDkX>m+&4z-|H`PXN89Q|v8*b4vJC5A4X7bkETTjJh+tydUWoT=c z@63_oC(C=E@dfupA}7u|0)5`@@wqo2yYelwBi%4TkI-^58{Sg?XTv=GO#mHeJD=*i9sZh^G!B@;d9n*#JV zK|Kx3WyK52lL0cK(A>U+FCGq>F%~>)Z;BYnnxypeK#T?o2?F1gbj_8)v>kX zm0W25>C^k;@klh6*s_IH)^_y|`oG?I$G|_cn76Nx2!`XR5;ze1qn2l}*J4;w^|(2; zi2|G-pjglZY}VV)H#;j!7TUpQKQl6dzaATVm#wGA);MAV|KJ}2I?`Th53Mae&G%wk zd#QMC;CY5W9}+rNKL>q(|NJPH95g2+Eqm)e4|aZ{Fqen54`djesh9<>nkkSwNK>B( zXpSqmFbd+H)U?O~7f%02N^iKzG@yyV&oe7eZJ`>AE8T1{+tK{bgBEApzA;mWwxo8G&Z687j=e9i5 z3R+r6quFRC0|swTQ3RltSE*@ZPB07ZtU?A4f8$lR&D3YMKX~@_`}g0qXY<}YH@s%g zjy-TVIIyq2-H>eCww#%NMtER!WMr`MMB~>R)~{Iy?gS5#kHCzu02y}$|4ngcK1(vs z`Y8WBjNe;@tV^-Jf&cEa{7QJ9{n~v$?7sShl*RICOC32iPaH~_dxFIKAof$jtt%Z5>?30$gFVbXLSeO##-5VH|vrB2jMVnxx*@H3i1|j5jia!xx{P^isNiBN? zdh5*QuVvqWEmzhizU!jn ze=+crIsVJ=zj7C`sj9kztgX@5wTlV6c2&Ki$5*ZTp8q$54&vCHGz&f`TX42gKITLe z9w-#pZ|6o)vILuDtbRa1xI>5CaD=4g8wIk2RT203SoC)kvrF!fSXPi$iM~J;@GWzQ(>8&;;GM)^bz}HmjFxApv z^2g3?*l{k<@7_0FJ?j6`mh~Gpmb=T{cmrdD)?df4c4JA$;=puGA(+ryp z%~g6)M>KewBG9Zd?|^A_D@v-<5XeMTBTHQXI*4quJSE1}y_?MBkdY5=T9tZVy<7o+ z#4H2-eS?+3?ymOUj$YtfF|xCByRaF7F0r(+HW$bH05D^3%hXoU8x;rKxG2!gvJHJC zf##1UFGZ|k-Xk~4)dB1ECq}kK6xn4=YKAg98**zNNlUr%-G&<6(!Xv}WqD;{%j}fU z__jEy-+9N_wddMO?vgVwJ)3in-I)watS)Q0u1pVmYUk|i&c^9|M-Cn|X-F}Lw*e0< zBUZr(V!NG0bWA}Edu&i!icB-$GL4W$h=zU&YbKb88rUT#I|Z&(ZF1;#kq6ngnM54u zZ*OCkp~3!1Vv?AW^F zKtKD~M7{B}GOP3WvH2T(-nu_{sH4~Io%VT79H?G(-`MDyHB;L+_L^}ag~LiVABr_NpFh&xoJR~Yfqbzep7p(fI4MDWwyBN1!mVSz5nxR=e4oOzRBwhAn%OL=qj}CpHyYi~LnAPb8 z?6Z*z6GFFQfsD}(=ze*7vA9`WyL1A`wa9xZ_znD%?RD}CMlJ^i^{KA%C@ zS56{sAi%klLJ%z{4}>V^T0WYayGMjNVIT*0PDFa5_S=q3p6GA9Uf4HV+16H`t!=ty zy%1`P6uPt7oYxu)PpoG@Ub|6cZZ`JmH5+8%@|#Al*w}yIh}W~l;~qV`N03sfSXLjG z;ccKRPrWokU-wu;xm?clOpWJQL zCS-NA+721rf8H*@e;xYekY&>Ha6M3p3W~f2p69BDb2>;)W9iil6;eXi2w(yn)dkZ5 zMu!zqNEB1nm?5OVKwSChSe=v$Y%@I<*vpe6F3iYqXUDp=!;>SEmEMk_&LJ<(rf8>g zIr6@i^*w|Q-;1DOuuLI>q@tn0t=`eAN`M0XkVZryL|Pp$ILf+59+m7$Y|F^r!+W>Z z_QhdsE5`Q>o_a;^vFR&@Bz@DrYytu}Gd(mOUw2t$%pKU&)z?u~cdc{boI6|GAPeN- z+R#uYmdmGo1%F4|eK&8paboAjbtmTbAMe~&b?-j5a8t+|$wotgB-X~XI(j!Szdp;Y zbz3Yf$YLLDy%GnI4NtoTiv&bZ!g)yWa}q4@uCb6!4;k_x)SG$+Bm*AU)SzBU>Q)pF z3Yb*NdUNG$GmytpKrTQBSP{1Z>;S9da)fq2a1WGi3UTuU=7?3MyNrk( zcWiOo2}R?5Yb&;hoqb4@vOY)B5pvmF6GNjzF#XmW{~(GPA7V}g-Ldh(!L=e360i$> zoC7UtmUq_!@d$hX%ML+O+c3Ke16XwEv;|F6>9eTV%o0@TG%PG&<}bjoNrxk+T?b(l z3YrVx0O1VDQrN-l_Pgy^8ocOBOX=~x{ZR9zeRXmXsab%W;h6D`Qo)n;WN-?jC+ox} zXT_wLn&qs8jX+xLG=DT{hE)RY7O`BXnWXvS^}5&aMb(Nl+UfBuzea#F-H!+INQV~> zTGu`|bE#S)w`M}WTCpbbV-xx_ji*&DnOnC`ze=^Hvh{J}U%(hi(C<>v&yC&aq@`B3 z$78?^4b9Y9P0|^d@|GBeel0S@M?)=Dwz-{GG1UyU2n&RM2jF)E%o=0Am>Zaw?4N^& zNdH^&@kH)>xp+MH1N-hZqq`h`{{#D;;nCgpw`XhiFWYL_?124oTaD(6-B_c%1u~=? zGUVa9I~BnyvaKZOtg~IP>aB$y#g5ReOy?%|$`P0zDTqI7BWJopveQ@6C+!q z{=uwI2N$i8Zk`&S+XoM>{kI=E(fElFo-UX7b#xv&{h($auqoSit;u)$(m^i}2u?HM zG+!sQTkfs95D8z1MIaoKaY0#J8>c}FRe_No7UZV&bQ4!uvFqJRLn~q=z&OY`c5I!o`o|wZ9s4`r5OzVTCRr6$kak%FG3jh79z~hnz<={`oqD|eaAqK zWOQ_M)Hj&%o8-80dj0XEoDOZJBd0DP0@1)G>;Vn(Kx1yKd+Ed?JG6ri0VK!+By3rw zWG_MnNCE7CHX~2*8K=0I>LP?#mJ-4-+Xfpu7^{+@iM$dMF(Dyo7X%i&DU|!TpqjhWR*V%oK;XDrkX0LEi zhPxet+2)^ECc0;7JYVyJiCM9O-x+=f4h{IG=?h z)Rw6_PNxa_C2BxVH?~frNIvMbhZ6us+p@=qR_Zo(UI!(Wv zKmoqQUqvPw!d_nxhel--FJWdN8MM(_8eTWCQ)k&pVds|qEv537{)scGbtknif-PytH z@v(azN)2Ym+`fJUS~}3jh7$9QFVCNv>27@0$F|N)a{ZKO_n)9$4tijT_Ma@5)=vZd z5Iq1jgruprtI*q3=)?(VJJGkO5Hx>8Zp0+Xx?`oxIpm(6Nqukww z;gu<6OQgIvX$C>wgflKiv&9V`SbTt*OEAWZQEKXnwRc~$_GBV{n2l}ND6@8DdTQg8 z-1w%lvGJvgQfb|-`|f#YZ~)YKYR|4+dm5+r?!gg*O~MG!H-04|m+0as0DeRNLC}f$z7AXcQ@jw%| zF;}^*;iN1Eioj#mx%sL!_gok4@W$i$jk8;KL3LJKi{P>+U<=w07;xviQ?nk9rF28iLMT!${d*-wRkphZXwT zG1SU{pK2#gZH2L^I0G$Wx{M)0Lu0Y61otnsb!d>mDRihlPz99OYHJtk`mJrfU|c9( ztnZe_MF9ij1)AR9fnCmDNMf%AvqU{f-HO=(l;CLiqG^2A z?{{;7h>c#dH=_k=oZ*@}^nG*3=jQgGn48)-Gj;v-=Z?(lU;V0e{^+^urzg10FW^ex z+lNp_0=Nq-cMxz@f!+#3O?=FdE{YRmPLUN0H`FwDqe-{oo6rdVluBmP$zrOQb^}u& z>y^BJnR6CRq}=e))Q}*e#Vo=k5jxL)I1X1WXECs0{o3j6bANUGl3G_+<;y2d4vme? zytC=8HSzY%o4zB2w{EGe_qu1<TEcRJxqt77ML~9p z&k8siYG}0X#+DJ{gTT7RyvGl*Bg-HkcYDFnWC(vuHb2e1`w-XSo6l&+ruB$##)NSs zn;Sk!@U4b}@}grVbs!DPWfiB9>PL745e}MqqBA%UNe`!ov)nstV~4Ncimc)2N1&FZhHYIiJ+`Ir0!zJhk8fGi_2KGoL`p0dQ@7R!K$%OVgv@^HYu7&Y*M`%OL5? z$MQwzT5j84Y^mN&3kR_!sruRL$Wzsxa<1Rg7xy}oj^WAP`t&S!S^d;t#utp@Y@w#- z>K14didEdUi|ZV0J)Bq@H1>8+zZ-}1=#yd+JxH5)ax1aIvn?1P^|-ALBot4 ztw+g4x2=qY=IW<%%V`k%_WU?208!L@MYft8p=sh#umwNrG2co99Fqn#Pq-Mhyp;}H z-I&1ox;o7W8<7y_6hsCju?8=EQ+Au2!gUU;?^aQ)$v2q4OcCENOqm=M7!0R}Geg+KMz)H{N(6BKA4}H)X>vGzWb%9g3{Rkuq zAEnVcL>>k&;hy})zQ?lOG!0xYt>WnD5P}V$ZSUEU-qSr7$OPJVm4$4?ZeRF&_9X=T zg5?DAH-Nz&LAeoFB3@B<(NrQJ@B++5U4|euOHY73amEpLeW5;@+wO~-rF_c-kutE( z6M&qw61MhI@tBWUQkt8SDXgDOhZH#1nsw|qt2|&D;yYN))Q-3=#6G_Bgjspskn&LP zOu4+d++QkJ`%A?E{mdLg7*V@@3N35=dwa*m&YsO3?=9Dc%H=_z1|*-n7{~KM1Bk|! zy3>Pt!t;=#@0m_16x7EL4&x2uc5$50yj@j8^mhJjGFVYd^XSB4{OU@XW%Ey znTD2-{-3Ph=r1@SuE$>!EZ7`ze2?!^)D4m{%f9=lCesDYSD?L*&sX~&gYWX9L0KG~ zZJLf@zZzly3aE%#n_tofRdne&wj#~Pe13taGtA3y%V+Tcmg7#NZ1I>yvRdUNUBd*Q zd6?q9V{>%0BHPgTGn|-s^dz>SKTO&*IdljX3H3wFU*O6~%e9YT6HIQIydlD~KyEFv z73V~NHhi8_AhWoAEd|=+mpmN(MUx(a_(QX-R`m!>IbN-z-Y$fokD${0AGRan6l|>G zm+?Psk#Q4ZFf@F5Om~c0UfMjzd~>t2t+s4__t2WH8FHA296T9GH`@hT%m`WNm47R~RS18YYi9864|C0oj~ih-XqCh| z)4);YECMZI(Puq~Ihw)Mp7%aT%iY(xLlZD*;)te~IwmpLxzZY_WwuPd_W72-y0?Cn3x#FPnH>Fs))tN@KyxR~4GNW` zA(xJ6D4T%+El6ZqV|npNTK%XVSbmqxaIzpP&b0>#$ssOA<9|!p(irRz)aenhT86W$Dc2e7TZ` zJA0~-Ll}-^{d{2;nb^Ev!6@!#D)zYogD%zK_uYC7-Qx~9lOA->wznSq${}|lt@~}! z;_ygh-M&zvEC0-&Z;M&8#b5l&?4X|RCs?s@|39@43c`dwGxfob_l-i{TRf1(mp~Q= zfPh_9cOeotLMxE8!kN(ATbov=QQFK?B>4>HEQ%OGuqEsrnsyDzRZp>OFq6+#&h%02me{nJ~))rQ&x(~#rN2oCcJzG zgsBz+i*x0zSru5Iv+R?9cJfuPJn`74PrbJB%@fdC3@W4XuTM5c(eXUrOPh3CU#}y6 zIdZ1cG7zE9PyoQ{GBk!53MA53=_Vzf)w>;y`_AH2S#1Cv}*3b(%@P? zISRhBH;)`^Yduv;oHJH`_TeZwE3UC2b9k#&c+D))*L%R22?G^{B z-b7_!)vsFYaPZ{4gC95#gMKtoiU=)e4rz#b+FJIShrjXgNeO;XHu#KIGbrpqzRq1t zz&&_@O{7p>9GXWS(R9NAvYHwl4P8Lz%Lj138;GHaYyq|rykp0=TseHljgyxrgU7d? zJ~216Vg2?4o3;}^x_O%rE>|XR-F5q$*A7c zJxj55Jlqcw+BC*TI~aEmG)P-;c0>%YH`@rM&s>I@fd&|&$#LzOStv59$3<~&ELf3o zP>E~*h-Tf7n5d@l2gi6R&;!}NFzY>rn8px-iXF^=2n(5k8hRdwWfkEshARve%q2~0kRXzo&x^tb+7xB$aA+dr z_=gN)J9-Lz2fKXQ?}mI1-M9bT*roT({NWeR?bjlMT~|xK(%kHD;pcY2Q;2LjIg;Do z#-91bX_q6><_#o5c}cqS%z=mYc0T^r%U>moz?HIGzj;|bov>=OeDa+UD`$(aocK zUSd@R$KL63qMVrCTP!wSLVYE~V6uqEaE7cKmhzpJ^%!3mDZv1u3uNu+F&7EwGo2h$UU z5RSgtlbfh+o*9@bxZXT}&F_ZTvk~E+9j-zqGnfhpv7D#mD3p9Xx+|4R^u&Cjlq2Iv zUL@>edFBBIq6@Lr| zf?|s|5JbaLc!UAQTbZrm)E%V7i*yfzl7GV3t~74zDx{_Zx+~B#=yT-~TJGe4BX@K- z;t6#eZ9I9rBjC+$J{y>Q>or=a=OiN4mP*xOB@~;Q-Lc2#>MC6$+@nMkTV!BISG6m( zRr3X^gMB?$AN~G)T1Y$YsQO~DKwn=VmJAOo?|iFmziVvsu%c<6l$Hq%59<;wK}E~8 z=bsbTiVpNtJGZenWd#<}+(3-rSAK!ON!*tt!7zrag^c1GXnqU7&g3#3nLxFa>(UFE z%@3a$J2QQFZ20YsY9t2yyw>A z?)G|TXI;ou^Z6=d#^Lin7k7za)CeZ&d4C(9xOnkPqKk-)xOxz3T#^JZl~wv$t`&T2 zfPMK2-OY9%69!;=! z-*x#tU(Nhst|Kv+$yH-rnSaZ4_(BP+%ptScr51-UEj$Mu3$6rtZpX=qTfmxMbYN(( zJF10TwVEqbrN|E=h(KFCldo3ug=!VY`h1|VBaWF7nuY;y;`hSd6pkr*uUdo;OfAUR z)tlb#sr+A?K4~8uZKMg&+xJ>zdXN9_>}l(z?o4QoT*i>nl?$38SefQu0p`Yy?EMsF{9#e zQQRzu{bU0GZuI+b0^{C^k#h0+VtHip&a}__P)Sa^J$<>fILT&b-Z0+P*48!thMC3( zCxs`%!S*-2q1?Cd?zg)N5rn-87hG>g{f+aF3hx!qqJQ9>h8XOvns?(MG&e<5@CuX1 zPruPPdg{$~TjT3j{MFY5c15GeDvzIL-)Vg5slS$AuY{K>*~jel%39)M*{qVZH?q4B*nKVs~@C)n)=4>s<6;wz222?0^>uvSn?IKEU^uRL9-JcKrv4-Y{6c< zFq6g~ZqnEd8h^I&q8WSjVkr*tORN z$odS0$yeA%W#jF|Gdcc+S8SzXS>kWTKn7(3b=kE1w zzH(o7(!0e_$A(63xUclZCpVwGZKU?fS3dCYsG7FLToHfNp}VT3y3sjw>0bNB=wP}t z;P88ECw+mQUb+6foOLAZZdH~w1;OqSfL#fzPOGdrswu@59uiVXm(#ha!wAa}!)}PO z&!Jk?LVrp5q(?lKr5o_xIi zoqDx2&DLQIV#)&T6O4C>ij8$q#UI^}n%xEwK7!E5xJ$BoS@*%^1-E$@;^;GC4!-6h zUcoPTOa^Mm(C?7IMbHhwHb_SO9H5+VEE#w8G=_l5#8>dlRInWC5-_{074Qi7EKj5$HVRk?OC!LwUEnMJALWUHQ3J|# zOuz@8Gu%c+YXh!mz;HNZpB$_XmSfkQ8C88AuNJep3emVE8tHPX&)>tg-L$d1X&?hj z0k5l=-q?P?VY7?!U}fO+tv#RrYGroM=A6@6TKmRV-*Q@~g57sh^F##ZI!gFA=3aw*Z{i(E17$?MjXHy@hZFBW9^+V`~oxaze<*vPtG zf8Tn~Ahu|N+xxahqMIl86&+qtihDv^rVbVoT|sN#MA;`D&F4y|Cif-W6_;U+dOSg& z6=7TSNO`PdtuN9uB87(!#`Y)tNBRwaz~OUb>QkxWa8g%e2z(I^I}+A{lH1mM48Hlf z&wnC**X`GhX-YESjV2SpuFY$Tmk;kNM24+4PcftP_y?=K(}yyOM>2H7<;MZe2~iXM zvfpL*I>$TLFvFU3xI+d4iy=gb3hPhS$Y`~B^Qnv6r-JnZnwLt!%S-r#6fjcD+Q#es-@jt$q40r zA{!89J*ws@`LvL3Sfw#0;t&W`$@k?|7Lw!y9F2N2a8c8wknGcKfZ(Hs%N9!sMn5dU z;x-!&6AI}%TH^CY6it#my6Sft0|B62-hd-ja*0e2X)02Rs;)Sdpy&_7{7iAX4^^~! z!82HhrG0j%O}2WZKybnn?Tq(oRs(VJDsC|zjfyG7i?i<{kl+}P=OJ^ig3SG6$lQbi z%>-V_Au&1g7SukZ2e;FZ%yh{#aEPfP zk|A)>cv6b+a*>8Q@>Y>IMl|#bNbMw^L0nT|>}tnsX+vAJU>Cb8y9Ixa4Cm9#77SRr*0iC)$I9~8T)b}_Nb&Oh7u zJM{|4Ttb4NPJs3#IngCSQvi$T57s3$yA7wLWA22kgd(`C^9n^irBVk5g4A2x0$rhx z9dcGF28S_kO5h*ZhJay^gnAfSF(hf<0DY+@0|om6l_nDnKyntq9mHoOQiB1gF?0{v z(>~Y$gem7H{02WkVvYIoQ`laAdKAimPmS4~#YJHo=w{o_O_#So*a{wJA-%EffWr%`gdr|# zKer@n-|*l)T-Nr~t&p{%y(MdH{{F3H61D%c40OJ00xothi0;Z_F`?l-WTIH0_A5FxAU0c=^&h#HChX<{8Pa&grM27ltx>8zmD!Puy zzW)_btBNmI)cP-!v_6NHb*_}O?(%Pwv{vy2BrSxnf{0HV^DL9JHXIFnp`_JSw;YjQ zOwg|Lq`DJ5rl9Tin}Sw#!+0|!Su6)FvBuA(2PHa(zzN+i2|S`3c*I(uN87OXa>Q~1 z?qQc%uCiQfxxsR?<#t3HxgUF`ud_U2d6VULEbp+q-|{Dxzp#AD@+Hd?mTy?TW%-`v zhn9b`JZ<@<<$pqFQJI0z+EJ)l?X1Q|*ao(VZDTvx5q5%|W|y&>+3oCZ_B!?mdlUN| z_73)Y?7i&$>`&N7*vHvluurkSVV`GTVo$Jduy3*Nu^+O3Vo$T5v7fVFvj53`!_Ets zD*yoX<0MUB674Jl&?@*_e#^h*`Rey&LcN>+7nfbVJjmc^^?k~F*?j_k6i}LsRR-vt7fR2=ui|;@oAPM=-^#P~to7Y` z-uiy2>DVuJA3m*Vr#1JgZ(+l+lk=iDFDk3P8$X@r{~kHb|24j4{(PhP^*_un_O)Xt zPYZA(lUIE=zP2KrWHfVs;Mj?IL70~wn4cHSOHpoq-#dHYw634F1)JYCD%WWKxQl++ zH_e?^;1g_qoEDp9zQrtR`De41%WuukH|xCerljrO?FL;qX)e679X$$XV^KUUC>OaB zs8%od?>B#Z{J1R6OVYe3U*t+$RuSpJW5*E4i72^n6%|Bp3BoH+H-32Rm_nEHQsdIa zOX1Gh*`RISFy?JpTYaDMUUnZSDMf-ckB+2wUMRgA&zAqcBq9BkR-VhB)AjN+bp2A($!GT* zp4YT_t9#Y=!k->J*^~h*zgf^6!w;KZtlj*6gDDc;X8vmQ9On{NQdWJlt`+Hi^B>Jp z9yogPj3As5UO7Jx5l6zA#4G>4ciVnG>VwU18x>(Rf80esAS34$O~-hLpoEZZmU$5w z*{tPC@zty|<+Swg|Cf>f>oT&PuOqWBSw_+d@*S4n!`ks9mcOxl9;-)l<@j@2H(GvU zIp176I$=A9_2bK5<*BPyL#tOQtADE)SN+ZZFS3GLvFd5AtXA9;SCx0sG{}E-xyx2i zEAzWxocnQv&g!vm{uVxY^Zk$5Y>(JNtG>T@?`s}Wlt)y@s_(Bp!vFo_qx_$E?X`D3 zqUeVFh-x)_%dY?G{s$jbltXNGh;J*iQRHb$o8lWf1!(9dGEW=u233kdd}NXHa%N?0FJ5$3ZqAe| zW~t^(xZ<97Re2XpgECgnZp&u8mH8pJ^@f`ZF;G%sC+(2b2HK2J^ zM`SHf_PFNFTC7$EN-Xk4UGT@N#S3j#&wPT`vNusGv1p~T$ufnI_Q8r2tnC;;UR zREALp?93Dw6BQ^@1RAYoc#DdtP{phcuMm;LEF8(YL36mbhmkjlHn(>7pgVTv?3$Yi=cCV70t<_O4QLNVd;u95+`F;&G^084i6CE84Qp` zAJq{Ap^G5$=|eOMlu1qFg*PuucSIy@CWkX4y=T&A#XbImH<7Mteeg~rBMaI~<5}XX z$XngYnVLs8faMm+;zd<_yd1R0ETqy0T$j*c>bhnP@I`Sm@E$P+WxyGPs!Wiu6Y=h_ zFX%(762P3%Eb{}YRRuJnXMrTEP@Z^bCgUa0K!EG6&#W(!Hn$-78&m_NYQBXp&hA7_ z^X4-0&?PUHzk}BRspFF_@E*SK2FQW}6tE4Lpjm&qgVvz>oLp#J>jAZmH@MksUqYL4)u|7(bDPUTd}-TwvCS_gAytJQJkGMkiGfks-5m zK~_A`XZ*gfai!jX5%hoxg_IMXg0%7Ou?C^0$R2Bpv7>U5jc^|%AkX&YpHqE zI!lTl^)8z#>SWV462Wew|E&Sn&l-^_vf4<=Rot>z0$J>9G~wTE{hz7l9FdY%5-v00NvHP91r zK5d~=oN@7uh?dwCR*+LCK`J7j&BQ$wu*gmDHK3h3H?A9VSW6tlB-BQLS zi4mMStKsa1h@qxn%NA@GM?+FXRUFO?HggpXOut6n5pskwPDh)Dpev#(+pQiYEU9); z74se~Y`2TPvgU*#gKd|B{Ed>9vuiG^n09Yb5R4*8Rt`}e#~rkV!nzjl?u7e^pam2| zOIfX&b;usbvy`qTeWGn!*pH~rm8x{|GDmkvwT5iowJC=UyW=)}vZl)pYZ3=w!6VLR zby)qkS$l0x)lpK@10w5Cf~vjCmeC?J=B&5!+%rtO@~aS~Ew#7Q_a1`;Z^*8iHWhDn7-kdPJt^qaIOl_1+Zd z^Q3NIQr}o$jm;_rIubfl{F)&B!}ip#po&sk+!mt9dWOxD%{v1=o22Xg zhl^S&ptumH)PSa>Ii2ASY}lucJ8WTDm9la9v>b4YlHpe))Jr3ywqh?#-aWbl&6BK^ zE)hN-TE8dkP;}LX@EVRyGAu7_ZGrHunh`;m0Yiv*>^J-T9fm6|S!WEK(BYPKN66Q! zTCr2*i{s!6r(5<~2MXF*L_`eg9>23)73Z~hO17#|Y|`yTfOfSTyK(TI((JkrwJYAZ zz1-E??FJ40X7p*Y?06)j2}PZ}=&arE^5KBmANuxyEuh_$R1#8-aFNI_lgKv1%^FI#duq zLDdwsoKxjBy2lers+#IiWly+l3r2iOC>W|{wchTi5@|0wgD^!93Sp2zl#_W`52PKE zThQZ@QTJ4p0X^+xRzY`0gR0ioZbVOZ`#f@hTnN*9tvK~n@@k?Nj@x9aVIL9Iu$EUb zQe0|WQ}kM_EsDlh3OHRcD(f}G5VnHV;rb)(v=)tSn;Mcbj7{o#!LB-Oz1DkNnoSFU zec+Ln(0z`9Nkg|K6`P?&rZu}9vLbedbIA3CAf9m8Ql8j$r(}3sZe}EfLdaG#4B3+s zktl$`a?CMp1gzasyCS0#oRZeAWQxwPywxQ+6b-wU%pMe+S*_jBJmHXR%v!Bh$$_ws zipvpJjgn%Gz>+2Ej@#Rvjx3JaVUby-=8YLV?M9M`OMmhD3sCkR#};Nn$h< zPK%<=E!hOcW3A<^XJu80Y5|*()8tcXE`sP`F~c~P&@`nZq`EYO;1>}|#88E>Q}L&@ z$Y{ra3#LXvcf91cDGv0`CIj3M3dT0p&>=}>Gg=?QNKxbMe#vl*S&g+7NvutUdI?Kq8;3C4Oz(yZOD$H|p)(q2=CtXZc`_jb#+ zy*B^l?QW;!SB%8|q%3bO$Vr}`LeF)--se0KC&eGU!XG9Hzr7lrZWtSb{J3G}{Q#=z|if!=T`i|9gAapZ)8y1Ox3p!yVnK@v2FYKQR^;=&<}7m7eMY@C;Z zDUs?T=@Q(}Wh>ku9U|H-Q$!|am0X~j;zX-Z4FQct_$v_yUBm57LkJMG8Lx^8reI1T zKKBRmZn!+j7|ZyAi6mNtgs{d!BTz}4ghORvnsO?b8f6*n6C@a^ftoNjL-8=;1~m|0 z3Gsfc3MdXz!$lIracOSDq2O>dEN?Ju(M<}RCsB@7aEdq&7gR#!1e}p7Yc9=-!^WM8 z9Hah)A1+#tf@JUmNGB0}i1<3_N@s*3H$A2k6H zaB0N=gEX`S-H%l!mYkrR=Kff5K&D53)-%_D#;C>QTi&=v49vOqP_r;zYWwBbtLM@>UNAfyC!5pIEp z5u;etB}$89Pw)?IusUhV!YYaCL0yEbQMMiRK`C|#hrh}SLr_+<9WoaI9Z;%*c}>;r ziUZNlm;&-4kO_h&f>%I$P!D7$QYeDotAkv2RSjeKz(FXPEp2 z%_i7H$X6MZ0zJr$5LH6{U=P(QGAGD`7%1QbG|~#sIW&f9Oo=gC(Q&kVfbTi82MlyO zSPiRKr1ZfW2Uc{tfj?;9#$YLE(egU+`rK=P|Jus3#ei%K}K@3P%%> z0opJk4D@$FqbJ)r~BQ%b<=geCa z29>4{fFw;|3Tijv=u(!rZzkgf&N^>3n$1VKcy7LhFHU(PrFpYiHzE#Q z@?xpd^v8i1u+$=Tp!xH>s-Z-@*u)`06Q;v=ng1)6+7NC=Yg)`=XMqRtlxIpXC| z^(kwl;(4~vVcr9175Nr$%Cwq6z1ON0RgTw@S6zj7CV>uo%u#;00NoJ0ZLte!gc4q; zkQFOKKQ(!Q4;|`tYF#rAVx<58f+p(`(M{Gfc@VV#_DT4SNqgSj7LSl)DE>I{T13Qf zgb4!mo^ zc_w97@Ek!myd!v-zz;#%0yPE|Z*^GK1b9qN;8a2n@gl*F=v!(awGR|-(afX;wR(}I znsr1m(>h+y6%?xtQu_3+74VzR(pq+8-N>>yC~_@nVc73aBA z)&x)E&6RvcAab*Jk&wDt;BBBxNdB5Vi7yALM;I*rM&KDzK~7*XKQcADi?c29I|xGg zppzjCOnOl7HfamaD)2>@%?yxt%d zjdsl=0|0ZgMF7mxreOu6*>)09e*K9y9^4v~}1OGqm<0g1;(}K?6R? z*sTWql~LW2qg%QV{ldz%iGF1-dlB?2s*CoEqF??Fj($n$QQ#g{f6NE;%VveWzoadq zUz-pk95n>`Wpi-!>!`{C-XP8;X`x?UERqD#Fc5VPiXGO9)-jJLd6uJJW5_btz5@MH zyj>e{xGK;uV0agxUk1Ei`n$Ub{!%&o1+`68UCZz^zf|D@^b6{R^M%ka zSqd$oUqZ+m_RG5JRe>Rz@K-PrtTf@Ta?u&mp#PS_P$`=5SJo~&1wAQQ3I6K0rd?Pt zUkHE6LVC9~1VzuQ$zGr_Hr;TpfWMNOqz}f*%iu3<*qY(+7c|zLdfw=7FpgHpjo5a>=21Y%TmtuP|4V@UJw$A|bNf#J|MM#o@297WO4Lq?dzz zg}nnSu`jnanANX?9v0R@HY2YoC)9jYQVIJyo`8DX#J+Iy1h5}KU#dT$bLdM|Y{0$} zCBLCKWY0?MD?;B{ zKEl2XIY6EdOW2o3Q=N<0S8y2ES7%%S`UULEXD94SWX3wNg?&N$n>1=F8wP{fIrz25 z=Dn(2*Bnwn(Sd#`idVI*K)>8-P74}#n>DK#wOGdr^b5R|f;S06Ln$moaJluK-^JM!js@3xLpn z0r!Ib44rxf?4?2NetEE$3=Vk#*o)%t{?}nI!UO*|;a)%)mf>EI|Np(X7l0ex29HT> z@M%hzPV@M02EG8Fy%_QZq~!&mFF{-meF5xQjeX%Tj^74-8UKyA7m(ZkEwGmc|JT*9 z7Xi`qZx!kV8vGxCy*L!uM7;oJy%^|4F!2Sbm;EA;7fwz@Yz*mk#BGof8P5sKOH7O2 zG(O#FFMUY2y{qxbcfE_PU-;#_8h^>g-^D-KxyDamgBx7fkE4dVgeZL?-aleiNWa5l zX&4q4PC1RhcdVHsGFy=^&)h8G+#4I;c*7f5=f~dD_{e+S!?rB^_)U%PvckvT+xP(6 zasRSwb`ujCjq?cH(D*U@$Kne|nXu5HU&0_;U<}u+@gtlEflwugfz~bkMEnlwF=m;z zY^L)N;G_f{sfYk(1GU}(s4q@t;s-#M25LnIRHh0=@1UzBoK8&y2`Uc1Lt*8csl13% z%MkuW`bk@yX}Xw5dU9CNw;dZ2+T&I=nFthjmLiGl*yhHE*p``5y55yYx-&LsqKyr0 zq~AYFrUJzsbk*JXbR*1q1YzO1#uM;myJ!5Up!lwNphG+Q)V$9&epCuv|Ee~jaL>8K zeZqt=ahp)O=UhziU437N?eK(0KZ@TsCiKAduQ3w|zgqZ_koe7_IZQo{iZ1GOM- z;6W|V+*WK!xP{`_91b$Lp1*f3{6X{^hu_ zZ_KUj6LRNX5!F(ETc_I#lkArbUE0>r#V-_Mx+274u8gk73UdQOA*Sevnv~f*C{+ii z8jrKSiGe|>Z)=CG$7YKas^f)FEcM+kZD1=BbTq!*7-J#!{YI+sFYK>z{)^@OC&jJe zCLE$0MeL#xcrQ+Z54OU8@qp#1<&@ zdED}(0M z>xJ{Dr#Doy74wsQW%;j#93$)*mXT zR;O8c$35Fo={wZjQSCd__;z1EwppaS7-Jn$S1h$z{U#@TIQg~o`zMAQqx!|+$VN!eXkD}iS zjZcR>IpyehWc;A==HXJR^u-mwGlhk=kSixO6AZSc+P<*jccyf8$H9BLI`-c)Ax!mk zO?#wjNy$~my4prPl8}kmGMmlk^x^ox2NG`Wle*Jo{iNYoxJLK`;g|Jxm)7{Q?r>?W zqB|B&uY;iI(VcFs@m1XXwDgJJ;8Yu7F;r@nwn|#mZ98!o$Kf z&%IN)2J;L8GdE)w@${G&c>0KNBdhQbQQumT98=dv&;P#A5KmZea!GU1&ey#bQ-25$S@tp0pT?g;QUR!0# zRn_jp_`&lac0s_zc>o|^aob9r>+hw!a{9*GyOAe}IhH{5-RsaD#`*jrz}`hjbDC(>HV zEpr`BdwnvQzJ;yV%LU4m*R)pjd712ZIx6#|6qk0=$dUG2m56&mJc6YG}KGC6L z@yN>T>aS_1FUxDcwyN#ibKP}&Ua0RTg=yo0%6v9EU(t=}$_u2YG7P`>-(ILpOFho~ zyZEQ$uVyq3#!ke(Opeg;Sf}izSos=zxu0cqD6uO~0U7PIJCl3Vbhm@EZ?D3o*};E= zXh)_;N?6Cze%VF!Pis3hZ8GlOm!v$scF)(g$qU zOQuVK%MZ&8Owv^;tVHKZ3;|@2a$KGJ88-R=1x&dg>c91~*TZiwI10~8dc%%m3uu4~ zvRIQjXuothw|_PK{<>~MfU)kG--zEFzb3Yf5J_)F38fGP`_0b|*}&_{fhbeiUhy{s z5J=zkfX_gD0KCrnU|8Zb{u*-ms(lR=1_gaTScqh$L+j_KRBNer(|Dm&+p{~}^r`N? zZH^Oxz?5Fm^-^)F8lC9GcbW6m#sQ$9q{?5}rpMWuuNJ&+psR)RYo&bY=K35tmUdX9 zs&PQjDp6Bb8tqDT*xXxfu9Im~k>yS@0rGrbfDfn12_3_Si^!J1uM;7zS=% zbm{%y&*vYCUv~AI3m3O6(ik0iwR82Qx>`y5i=AKY_?lg*l`gV$(}HIH`=_4zhP2JZ znUBWriytNHV@qn4h-M2v@O%;Ym@e>SotrM=Gw>z`J&G0M4;OM$3)GK-ihb+a#vRa7 zbJ2FgotLWXmX06)XVY$4I%vW-SOe>Zz0=YUEA?4>?aDh?%w4Jd+mW1h)>baP_wwE@ z)0(ri!4FxQ^`9eb#+iRQ^WpgK#jj#6yHN7KYvD{-g$Aig)I#{BfZT$NhEubhfdKpK z`*cz>-3)1cXu$YPH`@)e-I=N*1TDY-IzATtn1^uVzpB_ zWB-eVquodWbIsI@kT@N?NN`3n8+^ICaAiI&qy`_EMtOfZ|vz8CE z|4>@;{$Mt3Xv^KD+tP-&<^_kly+1Cc^^Dep6#hSlby7*grT6~C)AV#n+YxNB+C{51 z_20sVVJ_4vb#tNj{9;{K)BEeXOcmtioICTRx(%8criU8j!eW^dKSfe(T&SJNE*S>t zpH$?+Bf!ZJ*aMqdR4eC~O1<9>7Hi#Z zZE=s954+uP^UKWxORqk1{`w!Tc2=uuJzuTnH=nMqcK)||Fl+rk)+3VW{%;kj7V^~v z?L#V8E^q!ISdff|d0_F9qx%nhbFEEH*79Msu=z}7tsS+=?3u^a?eQyL zYLm`1lh6NSn`Hl&Hd#3K)??RQySA@7Q)RWh58hfT-5TtdCfUDRE!JjshMT|BI3^vFCofkk1`VV(oM8>v(z8Ks}5LltWGI$ELb z^nS5-n{(c{tKHkX&4s?0-|y9a*{K(XwYO^bx|5}3?>4@*QpiT{R_8p&p#FVG(9fd9 z4yJ#Z!v$p(RhH`Z@ln%pqg0;QQ5mu=XUA&WH13=3eWj{?T+;R)EB#niRHC<4*PYht zm3b?D2uZKwZgmf*L*-mNd!SseCA%&cFh{Oip9LbmXl-R&^R?J5C;O)yS^O6kwxWXn?l^oxkc{3B0>{-hj+7y5O(dudOO*fCNpc3BHebj>!``IwT!Q|ms_pnU-K-@4EE>z>i%ITU9kr3 zR>oT$DTmENubQeT@b8U~*b!Bp#I2dG>dMa43x?MYIaUm|2$I(VmS~ni6dQsx)!ifl zS_E!oyJ(nB65(L2GTC4y0N=8_f?>|3vwnzk*~|1=N)2a8nny6WI}F!X`pjrPKbK0+ z&)SvU&$pJ_?Pcxn+U0V){Jx-OTE&7&A;KX981$!Hf1%Xwrma%lB$;I?OfwZ4Wo7xU zt=n$dzdUSKbtjbulfD?9obr*vEr7cI&lSh&J6#4ifN-}yY^Rs+qKFV1`peeh!RoNLLk z@Fdh5bsZ#1w_{tHM3C!Z)S`bQ`Mr;>O9;D~8GvI*jHzTyk5YLj3ujm82>8tY-d9^AMavp8@c}mWOc-VXd3nGM z(jts!GZtO1TNyv0j}{%C*(ktD3i&mlsAkiS=a|kbu9gejCdn@2yOi5-w4AS;CYaeJ z*R<%J-UTe>)7x)@AU0j?26ISpZ4-UM(tQ6FexO-$FWu!%=2E?}xVXKhX(RcXmvM&- z50R|datP-!C`gigAj}l3QC3gbdJq`OD3}h^p^caWhJEX;w!Do&z4sHxx$ZiL!DpGt z%|qs(r9Y+AQ-nPv)JL5`i}9^u|M|?XtLNjdAy@Qu^sNX1A{fz{^oiDtgY<-Ul4yzy~^36=Z)vawr#TjPdlcYw$0qu z^K!ZKwvHNOjv1Y?ZTraRmMu9qn{#-lbCVOf(QVsvT+4LQjxn-zazuN;&!vNESjg8K zawU$C!ZlwD^YSLN?(C%++w&u;S}Cx8mE+RF?#Oa5wyhc6+^`jQ4{9~espXCSImSq1 zTOp@P^}L(&MP(9vG<@XK@R0=TuAv#k#+l)HYn~Ht638IqKGC5GrPVIzqt3~ z-rIUVX1aPq?|O+j(2ZYMQYv?^rb75S@cC!bqpL)Fecb4*yV`B~=S}A6r_iaa_+^q^ zU%<@*ofdRk62Dl))P%y*-84F>>gkqCj6nbeIRjzu@{(pBA{%x&|Gw6{55{+(l^Z6V zcmC!`n~0j0y|<|iZ)oa1!;W;w@55JRt!Je0LE^i`K%#und0}fE;cm1oRSzfjzgFyFT&bw}@{8ep1t=alv@q*C^J&nP2f z6G5df>D|s7)1U`WJ#N`Uj`~Sp8PD|Ir_67g`R?azZ84qJq3=yr7kieai^@EIRk$0BKAvoRNfbRlsjW2$VS*+ znH6LM78Fe!G82dxDm^mY7I~B$CNum*#;7B{Mne+CP8uxwMC&X;0yJxsRU#vU4(%-= z<_LrsMX&OVG{2E3hb-EFszdxPJ}xFV$w??fGn$%aCMLy0<9qSvg5S6fk=k>Op=LU$ z1hzjEs`PTft9;bkrqrIamGdT)N_&NIQ(2w76&Q|%SYZYo%PY^jZnacsn5H{WAd`4C zpLES*-?p6ZeTcc2K+^*2&*^IRlegK{7Y~bWtMsa=ev-6%@5+p3GNVTgE9GEs8e|EO zL8DnlcLHeAiSbAp zc9&X}WsncBklUx@Psd+{F0nWE&e$KtSh7OP%yj8Za@lK@uri6NpTKo$$Z&5W(KD!s z;EzUDA94!wLyDYXJqYF$8GB6bK{O792rK*yvT`ynWP#{cM4r$Cq1#*o78_l=gfWqR zlNZxeXF$;N-Lt|8wg!5##JD=QBVo|X@?tNS@8em8_WLqMbaH7xMyJq2Q=e+;VsV$p z($>GHoZg>XTSMDY{~jr;+%P(mjV;Pa+DX$b6upX50v6}$dDC!V1a>KJ4;kj2k0Ov5 zs*4n#z_68_DCBB+*5J{Nj8SPK*SL1j zLRlL!a=JTh1jo1-tCth#;!GeWA!}S|TKD(<&^^y_2?P3lHQxINNFwJAd9}>uwFo1L zR*AX8NS5s3oSD%rENfWc3>TOS0ZmF39W|Ivr9FpyLo6~c*uaWG965JLZ?()u(Txuk zS_ns-fcY1(MtM_~S#gZ917HTROKJmG=oKJZm+NU0^;MDNT`w3$zzl^TVBq>yHd;}T zPO$1W%Lo0nG4dMjt?~s;(u$W6kP^nNw)HXXhP30GcUH$ckhf}%9!zEyJS5G4aG@IAFO zV93#2KN&YGV@7Ho!i(PtC7fDEOio;U3c2=_2<_|Z$j!G~FMEP|?>lyvB2$19$IX?V zC`~mlYc)$1x1(KV*Gh-Q@Ljtb2$Tx1*Y)c|sAAym8YE?2cRf;g=oNKizP_$q{lK+P z-*wDV6>qq8d9zijq_~_nUfH^ZK4NvAGye=EyGMly7A326qtBoXCLM%JJM0GW zr{VX{@@BeFMla?jz92s^^#3l8&jv#nBfYMDn(xz?H+z3|8*-c!$g@Pnma+tquz zsh#S5QQNoc!3U8C{snpHcNiz~tdZHm_vqCB>z0S2Kh-~s?+O@g=kdL>0_gp zpivnZJzn5Dp`NwX+g`BkIvE{}OGb~S)5Jxa19SPEj)TL?GUN)*7Lzbmxr-}DjY~(5 zGa)<4*}_i4sJO$&3zwo?xycup^qvLO#A7qBJqOLNvBqE|nM5{HiCoBvXuyOd3hg;R zbETiDvxB*m{qs@u(^ULZ!T)$77!RKK#~kK{`ia(o6ejc1db<)-+Uu8|A1obTFQJ#E z59~NmCr0=kkAL4u=ksaj`;X_f+|h0$2v)t`ot0LrvUvR1lJ;hAH3%Brqk+^f9{USy zx_^Sqn#2PrZd}G1Y$CE@GnJ;Qj*47f#8@EEV7{9PvKf(jm?+sXgR6pBR{hC+SFi57 zA^Pb3jp-FUGwJ4ov1-Z)ZRg>YMr*m*DtbTkVP1dyp8D!ty_u`;tFPXzef~ma5PaoTFMF5GFwWSS(6J5`q)z zmUK~!P=drQ7imm5|6q1fE|`mpKLty(@C)>Qe#G-bxwRm71Gy>I-08Fz9KGxHhe*>w zVu|d|*&wlZ!OSw^lbw>EsNeR%TaMlud?g6J9NdaA{y@`Bn!&f7K_{?1{6%j$x^c?P z4wkp>@Du5jfw#?1Ff!7X>EGjh=8mhMtoqvv=stx;meEx^-wGUQ{J54q^Y7Fn_&rCE z$#B#Gb|0?H}a=o>@eEEugQ}_MdoAi~-mzRHc?4ojc;e>saJs-9%ys#C{ zvw32nT)wENUGSO^NYE~3zzql130uUk42mNjFVrBUnUm$*tkxBnoe*R zHr8M|8W1wu<2sF^b_L5auXiTZ1HE7F{WLXMnHitbM_%n}MzeYAGq*My!roJ_9?_@9 zXDVA$Dt_5l-d^dBgG5(zOWwTUF3j#(xp4RFf@|c*hn$rzl0s&DX3IOGz6YTFv`T;k ziKVAzy2Q!NtG$TqQJ5aWyCigzFtBhe{@L5_DVOiL{aCAH=%xMTx#d>-U=kzCMmyY7 zX%!a7l~Ha%S&NJNiuH!$mb6!_)I8rRG#aywak0LF++p=}ung})5^BVbfU~hYjYAG|+VWUAM zM2le*`&>M12Qy*Bn8Y;cR}q20BtUQYYq>*2W2CpE@d(F@9N^F?s^?kD5hGvR;iws+ z_6r%xw=deLXf0UQfJ?}}mRQ_H%$n{;8Afmuxj}#LB*MimgbfPw&1e5Y@};vgBvB^& zynxw&a|R-PmI(msynIRgwVw!Xz%Ca$T07k>w=oAz(aZ0R(re3i13^ph19jZD@!Y3PiNYgg5f~*!C z1qufnPSzQMVGJ50$kMnGJazUsDjDk`nehZFkbwkFrU70FC81Y(TK9&VNZs6%oCSox z45?h|C!!cdlA6*t58Ife0hfpY82``S2=O$EYpN&$o}_+|3^?dN;x`!DZ3~itggoSX zq9L#`P)0WhD~xt)B_ku^cEU}g>J6X>#wP}p<+?1#tys6o&?k8)L=yyYv#_jy|*XL=M+!8lFcvIO&;WCRwG|!wX8GnWmzzMnuQhBbXt2AB+r>rvR z>uy*r8b)!|H%GJ7u0R};N6RfE;JO#;$VP}}q^z)`hjC5O^LREgMfih)uhB`KqtC%= zL>Mj??Sf7ln%KFq74zH!rfQdf^01k)efq!{Hp`%NXc@~aC(GktKCsLgMKi6*V=HM4 z!!C4bY&*!4-2#O>J@5x~k2DvLuGELX!LX4DY*Wj78q2ASj_PSSY^rpc5`0yXeClSh zf~A_4%A~RATb8ovD^pJ;e|BfFo)|DGL0~NF;F{p?jI>8;5uS&8j+(lgl?T?dBQBXt zqK2P_L>c4>B#vmdOOy>UT$M$! ztqLdT1mGYCI+ac(N|MVDvgk^#Y9unzH<`5)-UK)*8M~8-N#;jUH@loE&?#6bG7Ezi zaezy}aw*4W``k}iJ4LW%iL)XCCSoT zEA{E=&E;mL+$xtfR}gsR-!Jp0R=i@-^G8P$qodc-Ujep$KV2@{j>h7hce#UQ9J2q- zaZ07MQ_jD#7*cwRRc5@B$3+#ipDta##Qz^E9JuY(&iU)FU0Zu?IOMuR7hZVbYjajm z$>l1+P5^^0e-q%VScEG@2#x;QSAcoq-^mXe$RKtrte)HOQZ?jrH=Q5X)9K++2AvJ^ z3Gokk>9^1oFJa};BGPy>)@85Qx+5JO<{QCJnZR{1CjzVRy8R8$XrJ52p50K7^xhyw z@82yoGrra=9Xt7f&FXx!Io~``ZnoI?tCIX^JTl+nSNxLR52bvwRQ8IwQuE;(KlZ)m ze5+Y5Jx-yQ{-0-Giv_YEk~2T0u3w9Mu+jX zBw`Q=oaeyyhG?aJu*c72pJKJPTGrou|1EC@TDp3@o3hE5R&qRJJXpCUVLzPrt&?}% zk@Y;6xIa8RmtNP@Qa9Z^*r+yI>i4jumV&rm>HYqqwYS)EzwY4zV%*UzL>D+jB5{BimVr@D- z_3XCA9XrOyw}1Wd-ra}M{l}KFTG~cBXf6iTjJDgF zxcI7%U8DU{DG1zZ@9y*z7W4`*p|_Qr&HdXD3*%>g8oL2m=~szRkXDE=1!8<*y2OJX z*}HjuF{}v4uJ5T0Cs`g;9PA=WO7n>2MPwA{thEylAB5McleZ^7 zaBKbYSKWH$4R<6zP`U2WgBweYYZAtVOJW=Xd(>GtbnyKrY|X~nNco3tWAmR4H#+~w zO;@RlgpV%V#pKEZ-*D}skxYlRlh-bOWG?UEsc#hb7G+8zb4t2oIYHM@Yx|iAm9wke z`z|MzvTj7LjIG~555Gdj6jCT|hX{}UP8(qq8$(vp!XwU9EJg{{2_A%7HJ@;R|puMHkxqRW^ zsg2n(2uE+>i94^^FA9V(QMmUXxD0U@0;bDay znr^C!JYi&*o@`B!D`q142@jY4>i{L0s7PtLFeSMwAYzPz{TE9FcvE2TrA{rB8<)8ctdi`6`$f{cVzJjy}8mQ+$|8&x}K&-iS}9=R#E z?p7yTBDu=rR;EI_Ua}%p@E9V9;}DvF@=3NF`bdu4E-}ILa1(BB1io1Isqk4LoFDQy z!tB5hMP$OB1a^Nsvy}uV^DsW%gb4;yg=Rs|kqcsyK=hYK!L6jYs9YBB_Y}&o$g#YT z^F_?(x4@3u9Ga0a7FmJhhSH+=tkf>h+uYe3BeCw371F1Ht&sb28gh-CLyA2X?nG8Hbh))SQ6t;} z-X)HS_=5=^VMxO@sw~hY$f*+G$TxCP1UoiJF)Bi|o>XHlQT>Xva^RhBita=G5W%?_ z_yQe3W;`Efy;D>7@iCiDAxmho_ymAO*pvquBe~j04jHASqa4pW6g?abfjb|;-zKSt z$cd5gPQi*uRfw`2$8*XyzKaARB6TCW-QI!-2*`&V3}j>FS~f*k2$AHXG=suz!>@`< zCOt2J@uv}CM%I%`!gr)20B=webeNjC`uEr77zm!rpNWEz@ipa|p5PDQKenfx5u65&( zRGO`*(Ah_j3vo5bi-+O3CKL$dLcr(!QxVjDo_rr%Q7*|t$cFr+h?EcoX26B6q$S|3 z1F*6k$S#(=qG8EClr|#vd;js511T1&IR)9R3UE z>ITTCoqmZx^lv$7I;;+WN{UL%$Q(jQi%f|av-pHt_20;Hdr*!06U|vmSis7+NZ8m| zr)Lrp;Wrr)5h<)ad})9D%HTXpef!(Ws^xsoGz+91IIr&Ue7zTYFY2aOuajP{V47Ld zkDM)7&ze?V%5hCxVWAdM&iA(^%prVe-sZ7x2$%2zG}JtjYQX^~gDEQPT9qe$_(a9h z-dn2GO2zZedtI9poq9eyGJJROyxJIhPjDex`2bE8KWmzGKR=YmTA8eiiSzpFP0iny z%w!9`HR9Ju`wTBwKR|ikbL)N~LD4K3K*^nY{q-aPK93UFAa9KN7+XRxGhRMj02ZZ< zrbjZCB8<@6bJ49&K6&fvAMWq$%h2BdE3zBS=0*?! z563=EypznDy!oVC_5w@oe}nUi5-XJr{J~5RVx=GmM4OUT0pgMQeFk#}Z!w7oP+tt+ zyu^glI{O{U6o?SS3yPr6z?8`u0@0zYn9W6S?@nRuA%aCBV9)NQ?7+WS_Ui?wQnfOL zY*^0&?|0=B)ZvLs?l8(h%if7TqAPWe~JTIZ^TlMvW3XQm6mSh#NU4 zg>A7E?a@4K8laDcem{T-V$ih&$p{Oys95jm1W*HnG^Cwq1SgcXt`BKH-_pVY&yXC;?KeAIP;KbNYV`qAifATfRToPnEi7NppGu`gdyNFg{oNi9UWv>|3 zM6AGRXM`2eej{8V1cv63?m!Q2PHPO)3@MVq zZ~DgH=Lbek8wLFinYC}NsNTIr@Go!FM}gGU9X*{bWJN~R!CJo$AGlzhwxUI>(P9y2 zqm3pJexgt@A4@_ctfA4*q$59#D6IoXT?{5{p5nKVF^N5)EaFs54jWQXJMsPPOH12l z78kZHZU27nYg22^*x1O?1Bd3fjEro_sj=~c8yh<*^OvuCt@^&Uv3-X(*8Alxx!jf! z?ZL&?i;V@#H0|#6#@O!eZ8zPbT|K*@Z66^VF7KuP@|M@kZ7elkJh)gc9oi_)&Ry2~ zk0TjznR)obVrL!$qjv&IDvAyLY$f5~5{cA7bj5%%nLdX=L*|vy-`B!-&dRv>`A@i}ndhD@V9)6{pH=?@zCcN!Z>;qfyp$nx;14ea2^zYX#L!(FT zJ6eM=c!R zzqoh|;H8=}b~YCl7qvGL4)Id)&2-&6V1D3R4?T4Kd)C}Qf<$$~%(XJ=2(fA_9yKm*3k{28%SwU|jXRUuZTzh<#z z#kLR(WB{!s7LbhyTYMhcO0@OzE^)W_55ByGv>y3_sH-n}fj|)^6H@oSJxpC6nCrS_ z53U_D%~cb>@x}V_oW5paR$af^j<3Pd66?D5W;rVR%pQB#G|Y9x-(V{aqGNZ9c+#%m zFK(Rm2mNXCTZrQBHqCW%3bPSKwOncA`egDukDNi6|~68#Wf7fo!al8jGs>Gkz~ zyatiKz9cBPEJ_(cso4Q^W2Vy?W=lhTp|#Pz@}1YEowhO`TAr(y^XGlEJ9&7nllO@v z62UDJ<=g3a)iF>e96M1@XHYs29KfNO9dD5Q%VK9Iqp+jDPkZH_EAqK2;(}Sc_}J=$ zH$48172OComR@@>I8QsiaNJ`^mxyOmg?6z<%W`t+-n21N`iN<#j~|)E_I}kG#%HdV za-2r0K0-vEtv9|nKe{kd%{=kuO7j?G86(XKzVDCIRtZLXndk~kTLWe67(}Z8MaHuU z;fj0J46>pr?mwl{x6J`X3g zX9zFptzit1K8~GvmNB*-t&uHJgyl)#QG{Z7-Eua>TNTZGVOgq04HFqJtfZ!iX!qk! zY{oq8PS1-+Uti8_9^=N3dD?p!$cMf7MVfbIdeibW`Bwkro&F5gYPCd^cMCK6C5Gv7 ze-%6b%oD^8UXB$RyRrt`gKLlX<<-c^I4&c8Am|2rRaj00>Za-AzxcV{zkKfYOQ&yo z$B(oFKYB;+lf^JB&Mxfk^>#0)r&r6ran)VVK6}^Izdo_r{DV_Z{N3L@aq17e5K(mP z$VC?&i7~=sXa1gN&T;nveo3B}EV$26q`O4BnrIJd6|%=v_iBk?0OdRp6jW17eBc8w zsKfQo_8!tM|H|fwcIU&|3){@U3`ipL_RROp%RlzH{9_NQKmO8ZKGUmf@9bTvJ@VQw zR*d48zEp_kdVkV8Py3G-e)F4fvR9sYDt=@9Jv{F$*7DbbGW{m2ivJXuA(mlq5rh)q zA{a4~%zUsF#JnyR9;v^KZEQ+WBS*es>>`I@32(=;rys?)C(WC3hOi>|QW`=j@S%5&S&L-|sti zt^owwE3e+P^zelni^@4nK;dC5fO{;sDerPhH|}<1*%@R0cz$-hxdR1N^0iq(`q(E7bA!@3LVmR}z^lr8Z zym?jI@N!<#vcu-C(K`(NHobOq*AopMZkB)_rQd$)mVLHqt&Yu~sOVNMuqsEoCr60^ zz$TL!DTcLn;oduRW8bi!TzcqGFk}biy`3H5L_Uqqzphs=JwG1|+dRynvXdPgH}Yn} zbR>!CuvPI=49UK}^&~!v%aBP*#1iio9x4j7cmWc?y|bY9Ajs6DB+Ed>EhF8LtRiwM zp;QI_IkEAaI2iZikG#LIW%G>%!UYVc8Q)fP0Lq^JBUPB1>^)d*tA{qP4ac;f6bf!i z_1=B6%|beSr2bXC_4A+w-Nxx}6daa^a2x8?6J27Uzg3-{u5Nxqixt8lBgEr*I6eFg zIqMrviufKlN(EoW{rFwZkKM(L8Ik_EX;X$KvcJGWuzku`-|oi%W|*-I(}0NcAlGC< z7N?8;;@rt?dC<5%rh%@nF4N8gVvehY{@jsb36uj%z~J{~)sz9U6>=#XYvZ~<}arS zJuJ<2wjQbZp4#oq&F}F`tFxzsj&x!NrsH2!e-1~VC12D@IBRFJFPk6~NQhwH>p-3i z5mkic^>YeGyMeRJY>&swh%GXCPgaFxVS(vRW${JvEle$#S3sx;J(G;|iEP9QvoprN za>Y-%88hFm72@{FUKEERH$A&xs=p@@Le|}OAwhMsX?Mspt@#UWeYDojpXgmsC}^Jp z?$rDK{D8iKuhl4|b;%5?T4~i(%~_+}#P-TCRE6tn%_*f;v^T5ucbCiQOu#}z`&O=- z&98rW7gK7g{m_DTn30@vwm-P8(hCo@(;3s4dvrZNd)>;uVDrD^3x#}Z=S+Tg&IUWt zr~=B@i|4zIVe{43Uy;^p#1xh~i4&@_v3PiVmMHZYx@_#H_;`}Bx5Z*^1Zo1TA;c*! zNzD{ZI?$((cVg!3A(8s}FG>8AMT3n-VtwIe8;>z0bgNL3wbgfby&^6h>C`Q_)) zD#O^m*umK4Vt0fwh@YWiL`rkiol#{Lr5t(fef3mxcaS6Ar&Y=q^5c6}x@4=xX1t$uFF-fW)$otb*EDN$Hkeom zCME*)d;N_UHb3DN3ZA+c1g80pqBk@VY<}Gg`CWMOIxU?ZY-wJ4sVm`**mH*6wzjia zuNO<(wrMw(b6`{DiqVE#@$`p^8FfG|xcPA-9zNm~)H+zGVn)V3@@(vG@Obybe=n7h zui>+r6xxEpP6QATLP!L9s)*Pml#4|?%A_tq6(tGh1V1ERm^Q<{&!s~snjJJP>nh0E zLXiU^_E1Lp0a|e`KJz<$w zuPSw5Zh>6|N165gS5 zv+y3bB{I## zA3EtgaZ6@AbITLXNf#i&|N66cB*+CDr*I0{r#4>RtdgSQ=v%cX|Lv2`U4zV%|LbY} zD`-3Aj~qB^l?Qd(Qvak zFE~raP{g|#7h|G>WWnlX0q;nS;BdfVTk}%6IF1#{{yj{F*Bh~$NfcHC)fYKU) zaz`PJ6J=d*UoX(rR8VVN17@~X@PrnTHGwk@$Sm{!$yY8f1az<_Y|>GeW= zmgq@Uy9W3bAT%<61uTw7c+{R*g0hfSl~eQ*VPnqHmm9OFLxvfUsty7Q@>#u*4pdAh zx6hO6{fvQ9tim`TJC7dO&#<7<#o!wIvqHBc_B4JVdV;>n$lGtHD%z>fUa{aFFET(coo{+m>ew-Ne*PD(T`vi)HT6YkRatScTH;94M)6Jb=LG!{PuVEt{>4p zDEvc<)xpW!6Tfoc4}=#Z{EF{9fnOhCmIXmV7SCa27DBUwHdc?QW*p=I2Qiv8p&Dcj zVpqED0D5BtV>?HP=4-M=sTQ-{q+BL);Fq*EESvus(ryt#j=v--DR@;%b!5ejs65w{ zh!{NlvXU~4NKg-*?sRlYYZ`f*HKZ|RDO~h25pW1lC@-eN$TKe!EtHQp-iNXoX@X|x zFnhG46@Y}nFdetSFz_%;JKz%7Snxa2-hf|Z$&*gFn>XH~rnXy~Y{sMb9qz&n zT8Rm6&;k&Vptl%ZjJ9l1CFHTFp_~Njm5N5U0k0ah8TS>CKt+mj8`OTx%sh{rFxlx( zeqyAHO1HSrM1mLk1gJXDAwZ17e!IyEP?SUKkVb0(Y?!Rad3we-+>}L_>XIQ%WC+uwl4NBs%qDKz^gD|b~SP@#GJ2e-_`GLsMuTaGU z`@k6nR*R^uA{v!g9zjmXOY*GAlq}ENM}M4iv9zL7MC6QKAa(_L(Sljg;bNfV$s|OV z?rzBw0t23|L^LTB$?_$^l+XqyCkT$o0Y?jsLQ7K?ablu_kB>GNYLfnKL!6w$3kpHE z18)(%q-&ZioFr{1cWoh7Kz9V#(~&~e3S0&Tl?b5$B10(9i&R4}idf^(?tDZOzamNI zq`AmTqC8MFvKG{oB&&lqg)AZ8Ru@bMJRD6XBFPB=;t*Ms?k;}z42YBB3^E1@AnU!* z8>dF6@F9b+MXV4511~`TH7(*0WunLF3UNrrJ0EJKP`_APoES6H(S@YW~MD> zHzkh_(xtzOw9r9BHxb?kW0RDIm;p6TBUDfp+_vCiFOSkb)Z%6NGvN?3%cWO%dL%?`+JG+p#gja&2jo%abS_M}j zFd4&f00Rh?f`4A<>w>{R&X0;Q6B!0uz-#coI^w!zx)orCly^{EIR~wPsY~|AInjmq z62OaGlH_;-7Lj|&VU9?#=nu0RJEL91pNA3PLT*KQN@9nQ^1S(=8mN8jW73ZHyB1Tv zB#+_#Qj3?aY6!Fi&t;i9JAMzkC7qgHYm@;Gx(a z6by>|0!nl4Pg<{EM9wqFbchN_eFW2^5>g#o2aym}l%f=t=yond8GSIij?}!5Y2gY{ z)%c43!Jf#O4$EZBo7^7Z~8^$Y?@di4Dgk(${{rphzpnOKb<^ItuO)toz*UN zshiv%rlKr|I8^G%`CL2dN!oFESo%o*WgnN4Eq=;%qGy$Z(LOaiOfp99#mzWJ>KFY< z?Lha)Z*IUBB1&|wgiCWjY4)>!{Vi%FZ43^8qeGA2M8o=7$cVGdY>s0W9i+95_Yl0X@)nahJ{!f)QHh z64ZvX!Kn!XDS8gB5!E#MNN_DqmEe6|eWyN&gCbgpn!jHgjzyixHRxX3WEd>;8%tN! zn#62iV$-l-@YjDvLi3R)qqPt?N$wT>Ne|IMx@#bX@?_FTvg_=bKm?_1R0GcHUy228 zsS10ewmx@#9-77UWOVeqjZbrc+ygSeBnIUrdXo}Vo<&}zLeT)=XS7hK9O_>W>gPOK zqmPG+s7;y#ryl1;_lE@FR^?{@9Ex%gX-VmD&Xv;9$s|mn>hi=9SQw5()P)qFpnS@c z2tF@rl|E5%uCNQ~7(>WbQX2@`jP+&CwSt_-DbYA)^s%5cm7}=ekNbM_lc;!f2EEOip>kO2%~c>0$=oJ2 zq-H#>43l$z0`L@+p>7P;M_7DH>P@YcS*zsBg+aK51+$e~(J6d$;pkgBH6leJZwqC{ zkZwD7RqiDdx zNP1Nb3n6k%*>$!A1)`EsZFpj7bXX&uj&V-LH^*N(CS}hs zhlkThLQAy?+L4`7&;FlKyEGsN$r}}2K}ans(=SG8`Nd9dX4pZNhmq<^b4m#i9kN(3 z-6jR21JQ>-EcqF=RWvPex^M{gMdK!#{GyW4TnLwqs;09LvQ$G_Tl#=a8W0HT8aAN* zvqQ3Px=PM8`|)7h0@^nebH41A=}h*p$q_j_>dWXiBu8^mlVOP@lj||V34wD9$VWOw z&X8w)sl(Wa$X`x_-^B?m5*ywtu{VDT8;7iV(8s2fBjb&rQN&zRMmmbjmI#&+L4*3n&B&(uRBh}NpAa}=Z<~Dfx3&LJ zoGy61&&mnfihRDjO|1l@5(h&w5UtO!HAYxSRdQbUUHPJOVQZzHkz;>)vRaPfGbhJI zIZ5E`$A9I;Z$14DI~-MCD{Qq+e?3209f|MEPfnix_wk+9shfKD-E^uHe>*uKPQTuN zr!`-gtd7oGAFYiRYqetGyw_OjBS$wjjz47`sMkf;j>Ucw{~Z-X_O2+|v5}ZUHpE%0 zMN4I(|4bpblbI$VdK>@Py2LQXrvUkpahoF5>Oth@-LBMQ?j?H*%eRv1g)Wq-)FOLK zj!?sl;nceRgo%&GH?Wsv2XI?}o}druc2hI{R?~7{z5TE5(DljntF_zb4!|c~u^_Yu zEkGCo{`iykJB zH6afbSu*7-F3-h|1nSF7U>8c$+Phb-=~SJ<#{R*AA70&i-TDc?o|T{KLx*haYfH$Ckk9aBi@w`vA1pRqvs_qm zSQFDIE|SF&2cP;)XYs-5-c2j6T^QWIQOLgQD(#)CiH!W%oZO|od-hd@+~zMC@ycB9 zb#oh8bt7ri{Qc%USuyO!^zEKgsIAt@x*7q1ucz1c%nzpX^@~ci;hbSrr|ngV*fBt^ z;Cq$)6MuxgYe+JFi>D*Vpgm<~WbEO&!_JBJA4XZa#2y-X?SvA<%8B=iewXH?Rplot zBwD(9P45TfVR!_a`1{Qi=}2}ixK1)xCJ_>O^cU;m2C{6oYCBua1+0#y>0LP98N$ox z&a?{%o@Vv#^(3n~?9?6@N%am8K=pA_kQMUhrwhe)$=?d7l~`Lda5J`t>rEJGSdN8kGC7Ao8XT?D-?jD@uKZ7qyM9@ggU}qPb z=>E(xa`V6Z5;PLVEx4#J$ucF7Nt#1^%;>(LQ-zu)(U@r!meZA^@C;~Ev|x(y(KjFp z7_}=Vf%v1R-$3?=_}lV0Hb0a;2>9n<`iEng=Ov1gj~Or8_M}_;_o7#AtDdac>O6pna+H~Pdn;Y^F2ME*G}hGJ?~&T z{lVyl{@9pbjDGhX@A81%)in3=?#bqh(X9f>*0|FgLOw*MLV{u56nPHc9dQ#xPuTjf zL_yZM%JVXCInzo65&d;YO2j9p8h=&IvV0nWtMz*Qmv~<@zaix|sFNAH^31Qr7r|y* zjDZFoz71A*_{G?pnRg=xUpF*lc^_&?atLvSbTiHX9POW&) z?Kq*$Lpv5fys)Qf1bf@?FY)p7aHxQ$wDIumns$;*|}h5^RFFe zC~p*90OERjyx^NgtCJs1{P(~nhoduL8nwI=6pS17>ML44*=iB{E1d=BvGjq~p^byP znzq$F+FdO+uI_ECl@Wlq&6FaU&5wVYIKx!raf?;MC=0IGdI#Xa)fwzs@QR5h4T$|K z+9&Qla_vm*9UvaIIyu2??!`ZLe(%YAcck~^`7B%UMPv;=%=3T$KK9-Jd+d8g8kwtzj9yxiEL%n@$^YQ)SeU^1!5s~+|b^W)M{gL9K-IlfcknCAIsp}`# zl>KnA*<|Tby-;nE<=|XN@vX-WF{VGmXx>eJt=Glg1nswBfdi10A+v&j?-I-rXOVE@ zB1g<Vp$!w5#6szu{Zb%4F)0S%fABCv zf0NVkr_51u^%u*twUq<=x9>l&(%f4c9*dJku1GXnLf=!nNjrX1ZBO8C{?Kn0I;7P# zt@Ez zDlEha)?G{*r# zV-@}~_0MT7|KZ_xJEk^BPA%djvIK{`nEK~bxmV^Nnd@I;m&Sir{U#La!bQ+7w#=~H zD?Na#Qbs*1D`4KuvS`qB8<`N#ib!A)*YhD{?5pzw@Qmq>=7;7!^>pxO0}az2f9im4 zU(owcdnU|ACS5?GvZ~AJW%sMIjUn6^*7`xEXS`h(t*_1{t3&tO#jN&1bK%OhgWgFq zd2B_soBQuk8*92cyz=PuZ`KwbAY0;#F2UY^?wJqwzHn!7thQX59RA{McP|&xwdH$m`}}Zqe5rmEyz?-! z)C%u?K1y}UAsFC z(J+=e;&2|LO^`>j-5C}d3Q2KhWJv3RJ4Yb|ik=tAm*gP5$txyuXAGHIfS8e-8TU|N!w3}~#2-I>Gvhn{D?X`# zbEm(L&=wyJwD_xYr{5ymZ_J&3%Jm0);s3Emy;!JL3lFW$r$$p_n~lc4 z#)SIQa^oS1Sb2yAK^nlw)bnAtx%#Uw7hHXLQzfdS^?dasx0akt#?f{!Ha7o8yx)Ja zGpuuse6=(a_Dbbh?V--wx|C_F-11Pna&nT4F22jJi(syEhFTB+m~rJ<`@ zI_VwAjT+ar2cQa7GE|842UVn1de4S(%au;SCilQ_PQACWmvEw_l^z6m=@q+YVz=Jf zQbvkI)ho2bB~iFNr=#A%-`V>eH(nko;ABfEr=~;~ejrA%hs-nlpeO6wXK6 z$g(IT)9|9!0pEs{2F!$bAk~}-DULhkj z`j$C>`^D8erai{`XUA9=ZOq;SLe?BqNhwV7q_l}G72~m~SEA5KteY!J|NVem?uJtu zHvA0Z7ULNq;;gI#YHJvyWe@hSxhtXMbWME?*ozE+xPXOj{ zGe-{rg|z?)R`yidsV@9QvgfOIJ{-p|3c3^L&pNSI7+fq2g8^>3lm~yv10%f(x<>Fh)UtHBwQt;F0pn*+YxP%ZA6}#Clfpg*Z?MV-lD%k zLm-lv3ZN-G{z&(p&(D`epD!-q@?@wA6@skV_YyO!CWMtj3GsY(Os`8)gh7HK8e_#Y zDV|{IE`hLK%EJe45@8R7vrycwwurAJJwR|n(I3Szgrh^BJV!^b#R9fhERHq)8`1=my?*RRe$g(}as#RYXh>17Y74%*@!0-h$7ucHs7+=^aRDP?K@#1s`DncZm@$4X$slNJ-mrRfY2Y~{K)<+=y?YjuLHFh;0- zkYV5Z1c6_+H9VSM@;bY70~3I5lhN~v?5?o!IYg1}ATMq-r(_0z%AlSK5N4Wy`=-RJ z2nJuoOLjsCB%&iqr7A|ViEw5hVB(S8A&g`IIjkY(=CCe)2lki1R!T>x5*hiL_bNbPxpsWPqna z{373q*+&E@Fav!#Sbjw-dLkPmRV6j`r!2mQ6}K4b1`GeND2&@$ETz#J3;V-%UT>Sh zBX`{Lp#MK9dl&eo&hvg;?>W-Z(a||aM@N^Vi)C4sC1HdxvMdW@d^fUjh_Q`vsAG%) zbB7Q^2-k!XLJ~pp!tzFkqKJ1>U_r6hZxi~LvQaxeTzX=L_PvM6%seN9YP4NMQ^-Z|M(5y?Oj;= zE@&fB*b7wGs>8ApGf%?9hrBsd0y8Uz-SBQPl>^Z8Z+U02vLC?&>nYXsl|FuMFLB$I!^k%t+_jiLUO63HFPT zy?aM!CpNk}*RK6seZ;KZP+$BA>c$Jgu`yI)ke-nth|a;VLFzrgS+um7+^_(Qh}|<| zetBf3bk@8cE&+S>H}-Mo$d3`7Kfm-dUTWUbfWCUSK!D@YET|)&-67w!+*wS!u@oM=9^=$=%%oV+kbhLUa z_te1Ft?c{q$j;-F&|{E`=&qI1z?BBnwRv#3^A(^p~U(^%_FyPji{Y0dknYJZkSE7K;v2@DBaO zC!$X3F+flhLJpf#0(mL0HgTAiXyqT^R@!BjLTaW}+gw0QFKLT-^lEhQpbr2j%{PBJ zau+Fpu&tme&ia9HNua!iJjEfE@3mld%fjz@g|bTl;b%Amagj_dMNSOf7Q!1shB^L1 zvKIW@+eTCf2`GAqafE#WnH?4NtPceTBtKWBiD)+wa@aem)&O`v>=y}X09g0%EgDYL z+(MI5ifIr7vZ6!B^a|(~Ah{6P3rDXE_*IbtGNaQubSJ|3#LKLy0g*eVH zVXrT%i!njReV8{Q^SG<1fzF($DB*w_LFN&D+y)dps))yFVMUK;WsGN97ca5!9jKzyjl`sf^iA<szP#=6){#i>MNo|Bc4_F*63(R0O48X zS5XlIVp0^-an5D8q17I9U`1H^hob^^+I%90m4Kb7cNFdw#~j?O!s?bYqo z0hksTHY`{4R(T&euitEZsZ!TA}{z@3Gd_@uy;3;R}Pk>3Fd8>PxqTKfh&s zhp^`7x%WyFAu-U`KIG5PJ6tqORK(uL0>$b;#QpU>r468=X(ch3rAa84zxxK zy8+e>_P)@HYzA$5pdl4MzHHT>$M0X3@JH<0IM<*3<;i2(iQ6Wht)5{gTAN1)PHxkV z@0vP~XXUT_m*rc)&08#gWVyhgkr3a-(;%WR%KLMO56n{JBw|19jm_jLA-^@E9TC*p zSjf_nrk(Vc&hqCF91UC$V51+gnb2aeD@$>&6cUa9Ha}u*5pQdLivl8>3#gSw!LCTr zr=Ul(qcLq$G$1x$a*=lifu>DuT+)_oNB%x>7)*fqA|#Q!3)~VBmmx>Z>A)P)z;2+%%d>-fu3tr)&( zVh7h0)sR<i zEeCg8K8%S!<~%GPXYygIcw9c{JSOkQdB>dlIPs{wN3tGPC*!`Gr`kSp%qjkAJT(>b z`dA^F>5#X1IeD-17%s5aaa`If-!1QxkD}u4LHRf?aEN}wc}x%w%g3Di<)h-A(os~H zkq=s}+tt01V0bY5M=z-2H(?K0TETnv@SBj|AEWFgv_fO1>&FAWfQ2L(~ zeJ|fsdJN$Vfzs2eLl$2=E|Cgkl}U)r0BYepbygwre9j3_!gM2)r;>97$E1YGgd zUdfLSu*+r~x-Jwa#!;jw;~|#62gxM?uF8n2`(65kq2nU1h_oRdeojQirhqFdo~w#) zkWkJkfXtH#T|1zn252;7{ndyMRv+rXL0k(Xzp{p+7~|#0@z@HSVLqz&5XU2J6963{ z2n5H1Um0HFYv3VZWj=oaA>R1k%#*eTa}`yFHD<&}YM5pYA>KkSeF|MJE;Y5jdN`~fd2TX6U49dj znj*guQSw>X+{Rl*={7mfaw-cVIn9X24JHN&kF503cA?4)c{_8^ynBGNo5_!uo=SC& zM1@Am79>#%eKN^Luv9)IZ+PclyQ%T=(%isJjoepq1D7WfErr3wi&NLVvBQHcErZR? zb6#ud?D97U@C_U;Txv-GWyE`Vvm~1D7w5c!$`Ct+XZezekC2UIfxI>Q+g-iCN*){c zanuI-5eu@1S+KO{{JwqX_uZ9lPp8}YjmTZvVL`%&q8^wgWTX&V!U8oe{kP?m+EVNwuY=1vyRz7uCT--G0C;5MmJ9EQadp2!FED ztB{M?k!(!_(i8{cYpU=0raE%>;5ZON7^>bJmv38*Xe0P6P>&KN2;v)hyC>s_kwBg! z{CJP<>yteyr=cMF(21$ZdNBrXlr&m8#>Rrd(u-lXFI@T#`bn_*AB4jn#fK^E4C7$w z6!wL2@Ez>@D0V#@+kJEo{0SVo(}_JhwvM}eQyX^Y)Tn5!n-2f=hZ?^57K~EA`u20x zk(Fm3i`i=5c#}Pn%YC@9@#b(a7%uT>vs!gDmm6$^v!-mfUPdorsxuOX&6>5eq4LLS z%ajEvQi!rN6BVpRa~(1{l64-iY3fWyF&34N$0jlZg3t6~DkW`x06sh#eB3;tFS)738m^lDhDvruF zdCdIW2)sz*F8%8K`CrMgEVABE#Tu?iLk~;z$(JU^Q(sT7*_g~bx##bwKe=@GqgLyq zyD!%7{dnm=Bd{K5day*WA1>ko081m6go}!F zT4Zk&$mGK!=|D^M+IThz#!?IC7L+oIW`J1>qaJu1K$vqvBf^SKYy%3`D8!>EbBAn6 zPRgLPAM_nkt*Blmpk}B9LKJF^uo&g;#l}eKcd*+gxMgwXAe4DGDG^lt6nYX|r7$lL zrJ6uU5L{Y-mq~BJ3ixdHEuZYw+>+sYEBh0is~{H=bs2D&3=C)`I1RstbIIj|dM9u= ze4P6W!Ci1i4@G?_K&^$N2WyNpdJt1QJH9CfjHv8wL?1V(G(yF$KXc_Izl7g{SW4fpgCG{p-I zu3tYGw3+wGSz^d5eiWE$#Ih4}kNGiw!lZ#@>S~daJX+Zcp0cTo8z9KQ+>iV3&sXVtsirDnl##UX3I2yhhbsNEI?pu+N;R}E17j|TQ zCjZujI#_ONkc#R!L~qb)Xo5BtG_nC(ED#Qds|6!#1jn#`;iLCdd7b!g!>W|Ezp3|9 zvdhQOWCB%c;3pR2_xrr1Be4JhjkWCFpmu`gQ5?pT2s|MJz=aA}`go znA-~UD<+6BRK2pgW17B{qTigTFakRswy+)c?Gw>zuT%9<{kc}isr`^sDMVTP4)|a> zF%d%rx~K60TIHjJsX0T6MNuiT3BG1Fw!*a1h(V-a^_T;e2a0PBZsjU=labOCB(aj{ z4PJ9#HfiB2H^2L%9gwC@l)CsgV!aHRJtO9+Gv5Xyv3PD9N zc?a~tNR1jr6?AHrD05>AU}~ZGkCU9w2#Rx1sbyJ2n=p=vR_L|FFBA-uiC3>CnnfqEk(VHj~@RBd>G2hG`^c&D?EFFk1dei(3^cK7E zSL`?ZN&WlhN%o~-^e9<3A{VAiS7TYz&5cfVRum#kV^7h+mQVn3^eE>>H#Ft#T@=yGy z`6S}z>YztLi-gTlYX?W8{8;EXWKSb-&MXOvPq;Huk*Nm`oKqy#v4@9~!aM<`hXdL0*TPw&>f6!FuYsAPH{J81h}`wZgpVdfhgY&AEN8JKEEa{* z6$l4!(_nl zGR*?;Ia2>YDr6u)4B|=6X1@6*-u*@-&w3o&GAU0S>A3M|G?w>s@uR^y1c%kdL)<5W z@kr|IrzK5w_$B5TxuBtrfatm~f(^f<=;Eg(?$E`kRC+CUc{1*g~t~mtVCIP!|NQz2mR{Tjxb(ypm1ke8+@O%Mt+7H?T z6S?-x9e504M>jqutRQH*7#8zs$)Q$$Jj$siB{-FD=H;(MbF57q%p^V$i$3#MSk+4} z(@k;bjXEKAOp?DiRDK{?{bNb`V^uCc?$8(ECr0bUJH8SXOBZsNcg8*u%VrW!Lu+9p zb;hMvjk-G2P>-`AX;3v3uO$5mBKss11@r-|QwHHai_5g8uKYLZZE28YOQ_Z*90g>H z;x=K4!n`5x7(EsCz%EOaT@aNsuDT(}iRAU$D(BUry$ilZ{s%laeYNbjXc@V4B;eMJ zy2kjz+RKlUO2zH&UU%t-@3agRHx%7+G!`=AndmzargK*=e@;VF6UOz&7Qrb>)+xTe zj#FBPN0v}@5)DzTfvz{%JF*yE+_I`WYShKiZ6u<)e}XRr8)Cs&EJzw$%c{}E-BXxH z%q4ory@DQcKC@rPE>z#U3pI@%wmfP1JtIpm~a75u!gBplUhyxaT<^oXxgZMfsf5-yjPJ%rf4UDgKrh^ z-%ipCax0ngH`1BdS4~t#og)N@D!`t#1`guPBye}SK0acDYcfBfav*9Km_v?Qf#zkY z^cV<}eyN&}8@fp4_U!0=&7l~W?soyol{B~~pX)QQlp-~mac|Yg_R)SZ9HSOMuzmCwS42Qcmh7q4WG>|(LEBjJAGPbMu^e=M7*K#jhK+>)O@bederLnN!`mcnUU7i zQ!ja|BA(SqQAPb`feVL6Te9`**%7@(@fX+EXIn;*PpZ|#m6+e}Qc*;MJpi~6Xa!;- zGy_d}@zMf21rBNZ#k@Q{h$7&%T>fI~mh5QcM89m)0-`WIEvKK-;*nAIs+9T@P0B8r zD8=+ntF=@2cIM)doYk6(#B&{0*~Mrm5+;U#!%IbHPw&DD<=T%`Fi@UNM3wPEYsflWWi&s#TOS?3Nv` z>>P=pL|C?MuW#y|hE~bfy5#rOo5x$-T)N&^QLp6=Zk+aVnYuK>NVqG%yz&URnH&YaI4S$20|BdXa?+^uh$y`s9ymyOAaH{)`iZ~NYaa~g*6n{(M8Kz ztj%U&B2gHjo@$C!7Yq+uHDwLLHx!$+X$hd(iHIPkJ-FpeWCi0qnxi2V(W1hZ!Jpq1 zDd(5XgU<5~su5>lYc2yVRM;#uOfUeGyP|$qEN6bAxKF@~@b=E}04K>yX?hS*&dYQQ zBR>DwrB@7SK|_%mE}W|Ju^|J|0@l;ej9xHcdlO^bV)eW9pw*{qj|FQ`4JydK74sWW z>qoi_2|ga=&A}&!R}{v+5KG7U%il7JHK_UTkA^vJk{C7kFVVB5QbVemI!k*H*J+0C zA(jg)3zG{@d+ienSeT3os!}D-ul$J|*^iG6(&E)N9;|@g)fe zS|n3qAvr|A<|{}o_;Ps+$*W2|jX>vM_hsRhDu9gJ8JsDgYZ47~iDv8tsp2F&3=um& zhR6psVWe+Ks`e+WtaGfBe?n9eYtlyI1CB^eI#PNenoh-^l+b1&1J?LtG~Ez3x6*!} z#Oeg=ODKW5W+31foNF{kroBBQxz@;N4?nO51bFS>nAcuDI3}3~KO2pXW#TF6B{d$; zjLmLHsd$FQgjqg?7^@O&Gg#6H5`zBfB8!~NawsvVXH4d46Zhk|wRFb2#FKyx!@5ha z;VpqpHp6joA47x?+lww--X-4BtEE@*mhgJnUNo4a&cFh4-@a_hVikDnQjGm}%l-H@ z#O{HAVEhVDnh~{UAQ1+rSjvp1r?E#65=N7%b3qCp!QFm8R14`^X-Y?*8mrVQ?^acZ>QzciK)hT7QD?6SoC`reVp z{LsP=BWSYs77o^=dWT+BuczGgVYlz`kw|r-FnYE={mf`XCKeib9NDkVa8PvV?uM-U zclBmNOz90mQ|pUMf-a-B{pG$bqxoE{dSul?gaRW@=6KBK*FE`NR(ZwPmf}ktv49q6 z{e#x5qd`xJMvL3GY9vyZM*+y77Ck{wW>i6ZBlES?)O$_U+s%4aVtd$d%srV&_3Z?v>ALF{`M zW@~qqeu`cRe>F5SGqhpocC-}MjL-jr<}=iPKtk2ecWXW$N}(A(e%);OtP2;J|MIIt zx6iEn*3j)cq05G~Z4mbcA_jd=%zl`?=W@L_7r5mjtY(YJD`)F!o5kzV=@Zrl>9l5{ zL-WSbgA^PScWMSdnkY23`SHsFg#~l(Xo4FZYVDivf%Bg_nG{S*_C&xEwDCT_TcilVQ(w)zDw~XIATwk3i zMhrL%fllbLd_;|ATv`h%&PVd-jmWrA!5=NgwHjOLLC#9YTTh`S@4Fw^xDEEo6_Di56r-Lqdir=^J=0_dhuP z+EasrAN>-62Vcr>F_5Jh(T(j*uVDvYH82o0E}t=K5&=Z?C8Ggu#xU^_va@D?qu$i$ z{mmm$YXp`qQ+JvXw8ed@X0L%T+v``|md z+?U~&^VzTb`<2%aFL@Iv(SkCvCUF(esTUZvNq1g9_{0HTl8k${jsJaU1IJ}a#il~{LXlaNRkdcZEW~M6w zZ2?#YF_!Q{%rxh*?h+)@%bSRC!k8ni+)OfWqRbME3m#Cw0n_ZWL5oEwK5r|%Zu`sjX|eQsn)OlUhR;H7`2-~=6f4?R7)p2 zqGL0=-jGwCB~PJrLBx80cO1UtawvfHE!}@XQdHCbU*f;TdrVH4B;EqRnck9^Ds$@a zFvM=`@NYnugk3?S0Tw|lgM=Z1*MPwDUugfEF$7tJp~A>i!U27B(QOv7@DXY3Vw*2j zAJ31aQoUnCEnd4dU==+rLu0+E)L1T2pW?pVmRuttBTS3}(A1Fq6+wfZur)3YMV6?g zzp773u_JpM1Yt+IF2?Fo^LGeB!`}V#g!+262w2+xMZV`};1l5CS#F>xPX<8PQ%>!n z=HIX|@I-zi1>!}R0tx#9R4~Z-q>TdIJk)v%$8ZX%3E-em&+rYLM-L9g+bVZu`u#pJ zm=L6h4>4AII}1@MciLar)!tz2Kb5ufZJBs)y;6EZ@TTqm7*)7fh*N7)-iRL!hBvI} z4EuE7nnG@wY7c53Va)=<*3iTmnn@lIJwV?R{i9k#_B(6D<*h=?$hlU#(5mMLyIcN@ zOUBve$<;baunvvsUSEu}H;gvGo$r+!Qu(|bbK3Wry$XIG@?s;#zysfO*3yJ-ElWry zTN;~c5i&>!S`OWB(H95y7wMpoxn&`tA%Yvw6m_T~tN~qjg@V0+$ItuN@7;Uv`LC#U zsqjQWvj5YmH!hqxefrFW*PlH3wf$$G-oKaKF>~%bdfl+n5%`V}$OT5)bmCdNBwzT+ zPcF!k{Y$4#J$eCOI&+deT$;C!-LaQ-?wetI_MSU`7Qt;+e^^nRsBr+^_Z<8(n=B!8 z(OPNQf;q%3QHO8b(cIgG`6wWt2tw-v4^f^>naP;84<^*K1Q8H{hcq=fB4#{k`C0L_ z+@UCPp*xX!?`%)q(w?kO)pw?PJWJKmYdFw_ip^4IYE9d`R4Ub3-{V=Lb4sH7^qX&- zD#jD>RCjS~&D4gm@rR=EH(LAqpDy;cwL}2FMq2QMdt1Y(zrYp-f{8In^u>zx^}WSZ zeL5CRU)z=L?oXJzm^RUVnmt%tGqz!B&6@6F0@r_NWX)Gfr&^+Dh!kmQ>F+&@>+fZE zw)Pf>ULf8~GNY`6DHX~u*9|fJH6-`T6Ps!(AgBysngsONsZk|q1>oI61EM|6tZa(- zL32#3eG>_yLHFAE^vTkmlc!FdVfQ?K|5x{(JBx|-|8k%uqcXQ{v@h)JUfGt6)f-2cX#rFnZX_0J3xcGU*G zvKws*(%min{X#f8pAF9YXPlG$2CV7CNXh_-?LME&;aQ1B%DS5jKE257 z|5Tx43!b(@#duh>y+q;I=Ikc;fF~wlRDNF4qWFfBZ@zKz zG~#yd!{hEh`^>(*%zIQ!r*m?wcvrFDNoPCfX^xlw(_i{M?g{Z^v&Ir_4QXHN(wXIg z?`fk3Py8;P_tdG=7cZ3FJAFTUtd!ix?wmPWY-w2>P+Hp36D+#Mo6=Le>i;saI27d4 z-U%SFVx%`7onh~uFEq@~>FjeRP`MY@W_h)mPYD7qKOx$$pg$`u=rY9TAw<=VSuDiu zs8MOqgI)JXm$ki2|5f33=i$Q#4nOTn;t8KQ|E_xBrb9EW@KNGFq&HWu_>%!H$ zpE=u}-!Tw~58QTJFUWl#W4!Y2`OfTE{2xB&JHNh|iR{6=z?bnH|7GNS zGmKsSEAymVN7!GUW)Gby-FX_Pp1%0?QZ0?_IXd-@-OrzAz7aDnzC3PDNb)o^DB^$? zk(48W8d{ssX_?XCCN#9xN}!yj-Deb{h~z||yFKqqSYe)`76Q=o_o z7al!Xp@=(ov);WkgX>CfALsTSJ}ka(6WAT%Bj5X;tGJ@i7#rhmV*XQSP}TRn(3zU=c+-qe< zU0%>hZI)4+cvsvg#K{;o4FOO(a*~PXnOZ;JaOMIY;Pjc-FP;I%e&+0t?xbZ&{QAIX zq1Bm~TpO3C3c*M;lA9Lx%S&2DcJ=jk_Ye`|8SurE7vG>o`=iQJoPVC3ZODzST|1ge z4ECqfW8)3k&}cl+9B3CY2xlto4J8^@U?s64 zm89KR#qONXgBLz~^398|(OP^&K`ZB&Lm!5^8z76pq5?fco-Nt zo;L1x&b@c;+vKt zy#r4S^tV<7%(&EN%KS)E9#A2_ND9(@VslN;s7cR7Emw=K^TD-cXWoV_J(ZRin}I`F6B^Uma~hFSRO6UDC~y8D-Q=wM?9R$ zqKF3+SrN&9XecE6_7o@XTEU8<27?RftO(Ss^|m+8Ydp}YA$U%Zwe6ojaEKE_y8rg> z@ekhyfO=umf;z#ki3s^$jP~+~IuMgWyB<({~v$zV$7Bl`8!{#%yFELJggA8tMlv+i1nN}oV!p;;qKZla>nnN7WSTyXi zM)21z`=g?y`*jUvHL==74pA_21mg>5A_BS-92bHaYGotVMN%%IH6yM9f#ACS@feB= zIfWiK{5C#y9~y_UYTzAdv0aj~4VrJ6$E)%^$k~;JNTe;?f{UAX ziAj$s>ue%8Nh9JTQ4s+sWgHqey(Qy#dIE!iOd6o41cnTWs>ld3_t3t4l3xg%JDJL2 z7;&~qFBmZc;0-7Y?Fqj3(fxZL2|T)Ie`b0Em37rnhpOAgOxSt! z3o18ZwH@Jv!LLhct2Hfsw;%Ph?*BsIi^m0cH@H8#D>lk;qp@#pOCY}%gVV6>MBu4| zZ418q^9JledJ7ld!M*c37jYuDG=oI&q`6Wd6izih)M}|{S?QYLev?$Av4R( zSALh@#2+EqX7fXC`C&Xl3{baY_$9Cw%!)g-bZDQc#C!eTEJoT-HIWx`q{jQaI=kI_ zlce^mcm3fje7lI9Z~-D$GbGu78@9Rc@nADXcxO+&nG&4 zT$uAZG68i}T_T!~?`U19I(!aYR^2U4m}gpL9wCSccOaqo$^NaSzbUVM2lP*>Wf7yb zk1^*Ax=(xn2QR>W#Ng9?7f9tv@?%5a87ST!cwDrG2djb{D(tAq@L)K|szm!^lPtV! zK;Q!dw!wkY_s5@>tvDmRGMvy0DV%|Ci0pT@WBr5QCD~J{=~d2WTu7R4-mK!4kAVx@ z(d8;R8@HRF`X|7lfLtST8W|k)(u7LYDT442ewZYVl5a8q76CxFo6xvfed|)OT#AeT zJS%Od*z^shAEI<8$D~&!h7Vb_4pu!v|3CxWVlwtPv?So}3kKWR-vvvj+1_C3$tn8c zN2G+rjvyf62*amsDz$M|IW+ZELB)`0N%EQ*$kCnxZx{h@sIw5O024^XYjN|C8+LTj zg}{I(n(m*xhA8BRtbcpXhjS1m&Jmvk3~Rnq)Of*{N^?l&M=g7RP#s(zSSAJ6VLm)y zzj^X;(<38d#^>jZ)|R_lTb!IH$hay+)!j(XN7LsJQqs6F-1!*CbxKkvH@gwG_9As1 znO%Z_NHz|=t7FJE^MqNNLagpqWx-p|ct2W_+|&>*X;K(bChTXD^fTn8)T`_l;mnGD zWR7_x8UHxsfB&-t1QaPr1@m_ugx0KbuWa7Pfi(R+(pqlgQ{p0B{il&V8C-iV#Tax^s)>KobiaW8Rz6OQbRE1YHXMSQHs0u=c z`Rj*zJ97=O0GM5>p#j+}l9p?ZdBsS}sL0#m=?yKaQ|>flP^Tp^8k;~a8SWwqCKZu^ zHz$w>qqRh0QBKNx-6+*kEeciKuF}+Q#Qs#Fc_U{z2N|v*22`i|f0i0@J{Zts2)xB; z5=u^khL5vqHPyeQE-x5xGwu-MOCD{~ULl8QYz1z-RC?vnUDc`R=@q%so0|uN-e9z) zdt$P^P{tR}99}?^mb&)kx!;7Qg)7hUO8&Ef^g#QQiE!WahhA^f zk)!g`&RP>S8rR@NR+}$9+S;4$Tb}7GG-n!gpI@@6 zrQdDr%jNpWCtul6ZZ*;nLt2 zylYYT5L(7`EcU5bXDoJ!5|1zs=g?zS0|vJMTFGM5pLcb@u>feOiGe`k;TI@pgYY6~ z@0#CizRIK0PNTOOjwPh-)HXG8Er_cyViBvH2#7IPYk~7F>E5mCR;{~pI~+a^PCfR@ zD~x+(|Hn}D(N~pD!=Ny_?wYNWjolxZO{%jZ$2?7#WlW_3|>z(}z{D+)4vP)NaNvSct0vnDHq6Z*TD z`W~q^AXkVfxbP_P32FrQ6x#PGVI=M%%Zi2FXk%t|$QtsXMPmq2YbnH&Xbn-xmr41# z6e93ru>$HY%xtjNYgVn^K0RZ#rr5^JBcmuc7OD2dRTL;jF&j~KQAsIbL=#vFX&8x| zopZTK`w8l!k1klTxqb956aqRt*1j3f9^%kNuWv0NZ7 z1H=IVeFz-E$ijpp00K521qD`)(H3z^Vw;qqLWc@wDi0}q$nt838x6^?Nc(~9OovJI z#}EaJ*#bwV{CztvK_dj+*iw#?G_%}lJ)}hf`hqho=^-vol?-5;!j)f5LA20!D80;6 z{toeXGuTqlai17!+!|B~MbTNEr@A_c8mxN2S>Ie394wT+uZM7ei_ym7q|>OAU8uj| zz5I`Q=w7kkX4RKvLJnR$aQ>jkOT#^rlRc#u986?udT#IbEiN8hcE^g=fo;$FJJx$R z_7SyO(LIJ-Bgg+OU+mu&?4Ndf)F62pVYfJyxU^gj#^pX|Qm%2~kBl=lPjEnWYMVTP zpuS1()SXJzyogI-fw(+x@V3F=y&A$acMT?$I<&T8I2PO}*yrVWt0OqHYv*v4gRSlE zStl+NyZ7`IG6T035A@wqeQdL9qJ!~VQ=J@Dj5;N%mfp{8U7k(%53{0W>X8kr& z$(e#=DR)p_E?<7mt<-?Q>zZ042Uts7RzkHQsyBP(pzY(a7JI|=V@ZRyOt$YS6CBMf5fb$j zB8r=xSX=X4dcXl92+$5ugqbo?p$MJ62ZAJes0HO*B?bXb750E02)auLymdtxO|}2p z69{-p8F$#ROyoVq1tF2~w$`Z;3z-J9HjfP0f3>V~ZniW~kX<1%s-EUQ=cM zzXM{Moqpx=C+l#sqSPu;1wF=sp<1&<8~L3qep?IXpZG#ADhRE$+-5mqIf?qVu-4~J zA^A%yE7)j)MsF^9bJ}U(E7P7dS2j}lXin@mF73)xR%Wt{CDF`cb(iOImISU_+TFMJ zaN%yuOG|yyTD^{S4bgLm#98o?BD;&9ixbzPT#*tL$`EXO>u7Ell( ztpB2^Z~2xGEmXJP<1qpWKXc0{?5p+54B1}J<#Ii(XTzM#tfII#yA1=e3QXzQ+tszV zC)-$@Y@RCC-%@jIv**NE`?$!Y(9o`3LsgD-UA^n3xX;w7Q5P4glWP=KdcSeYK*NH* z5!UB149{i?fcQHPW}&F+dO+?|FMleIMHuwB@pG)E)~O&;!BSa+pT-=>Sc5kqMv5$k zV~7?xi+YRT&ZbIVUMjQ%F)5Xmle(S*Ut5DFV?@~}Fy5=v2BA8;?#kCPr^@)Br)qZ9 zmD%vMnS#(O&lTnvw{&gg1wH&ZjI(}e(i1!`D{lTA5#*&wbf@h{id``s7J2dD@X#S- znG83q^jL*{2k%&1zifT}j)9K8tCg>-A`p10R#t=f z(jeIEBILS&&F;WtHqD1^QCyc_*H=FpJ-#`9FDnhBZX+!d{?EZ?u}bUIYL%6Ko!deS z?H1NVPvRc-&glmSYo~9p<48At7qDrYhBP zOm?}dj%z)~kha?nSHJB|2v4V4R z>$|`zI7fKswq3(j(z<)5ct?-OKeDN?pnnq5<(8V`o4qGW0~(4U;r<0tam$CDqET~J zujXwo{j9z8u4L2Dt3-&^D2Q^mc6%kz6Bi~%6S$O85SYQELUp4avEuyFTaJ)NaMAgi z77=l|8QHK^KjW^Zqt7_G)r;8XbtA*EOhn4X7YYfSh940-@Q3EQ(_24GtHFbSxw4JEy zz{v1HP}gwJ)Q7qpj*Sl*t@DdhO?TYfxorD6V`K;0=VBty3%m9T%Hx7{2j|!^DAz6C`2As_%h>xqOh_&kNsjilrYq`7y7vCJ<1{ z68_(EZ0kTKQ(VoKc@4w6iP-AbYJ+mW0_w#4Jx`(q{lJ}2eKaiy;YM6X!`!`RYlUoS zMRmy4u+8tZoPd7!G0P_`UjWbf2g^?_|IO?S7U&0Z9@6S H-==4`P&)lgb?m-J4 zh%b0enfGAz!$u%zm5y9xL)bRv@Fki(#3Gf2Nvf8kxh2+6d*e3|g$;mX4*T(%zeBe} zzf(DCA_(Q*@(P}2e1i-N`yBpMVNW;E«wO=pJ9w6?tX0#)ODs}tML1=MM7T2xm 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

>DHJHn8uvoIU|AGmX7i zF@;wP)&WydB=`xYa)an}A%0T`FfW-Jdg)&g<^87V+E}ps7S=%&Z6=$4wT@yR`sKPH zDCP-J48mZ`&hH>=g$EGGN;&s#y>%z|V|ZN)$k4Y?Djj;BnQ<@+sZu-SFlqb{-e#5^ zGyu}ckRl9rM!|(7PjGNH6k;wqSn<~3@N6W687WU`|Iiu`0s}e0RK%s+mIAQpajULm zB;qOk62Tg%m#KIo)d)pb>kMZKWq}6^m;bLG*dg|zx!ke^VTnAbBc`6$J2lmdkQE18 z(>v4CGt=ubp8M80e%m*yH*D77eop+ zAw*->aKtfr-;}+GP?P)jbZ;Xt+@Wemc?=M> z<2W=jydSmGhkB4q0o&lk~5~%r7uVX10Ik6bwDYw zbwVc)bdg7c^1$$}9m5e3SoahdU8;M$5xj2t==%o^DSynqR6lk1L))Y-4kw=6Y^-}VZX+ZhOB#FpK1?8AbX4aDnL5Yz_uI=+Y zG`{9RpStGoky}cQ>A@wj z+d@&iY0GUxcJGm!xBc{{J?>j>-?jS4%Xif-UYTwg9^Nx-5A>~F+vl@0t8I`SwgZ9X z`1EjVw74kKZ4(!Zwp{}&ci%L{%vjDt*1y7BE68mapjtV2%4>5?oyo7;{2wC_@ceV; zI1l~mx-TPE;H()dFy#r}1Bt|cj10_6>-C_3U;&$6oL^NA7C>~Cb7jMj$7bz!aQ2bd?sytP`Hpkcwm9)MseBgEeKz>@=@s`Z?i6zs}Fuwc%C( z2e1JVJL5LaZ6*YD2Xomq7n+BGlepkdN@y-MCM*C9BnXP%wt!p9+g)~O8(>KAqsw(s z2QS+-+bV!si2V)lqwt*z$i}fb;lu(OTP?&5K+Qm|ipd?lIv~4zYDT zySp|XJqDg%>P3(!+y@$Y3Ec7_xQ`re{|nFun9KEov-D$8AQ)YU#T2_Ou%9Y@pqPJ) zUS-4a(pROB+m5^xMsDw@9ap$r0+@Wfc?-x3CeCa0rt>5 z9z+D41_lgjgG&_N9Qf(%R(CvNv$F(TqFC^#-w6LEU{$r^66Kb-+%|g++?yt>k2bwj z8XQPf4O3luRax(i!Vmg@hI}s`@e)udJ@A8aN!h8BFWz5`($%~O1rvTyw>tujnn@cT zLGcj4_8skOZBRc(Oi$2McgH&3(JgXkHi8g0e)yivzI$)*X7Ueyus4%AO6v*fyynid zozjBjzBH!(NB<=!-srz{xM%&B9EIRf?sMrK1mVAH;`URB(7l(cC6m_bzuxzFgGZhi z3C;mtP(~)o{1zGpK@%i#W*JA>vuGkqV9TV%&^NDIOtb6-EqI`PqM!z94pOgVzS6jg zR8mS*X)uGch4m{@IJO(^Lenvh@L7t$>w&k>p{c^1l1&lkN!!+bIN3eX2-tCJ@zAI` zy8Q+47P!Mi6&k4BIsv!1qAPoyj5Aa#?{?nlNCKX=!)G|_23(1T^=|dM4xHf<+|Ivq z{4t_@oHhw3*ii(=BRJrcW6o9g1emRFT~Eh^z}v$kJIfBg_O&=Boq|hJavyS>Ue`F$ zo!WR;>$YBSoiG?2F`RPfjW~mTRFZZ|@t{N|r=E2F(rwpJ3IqPYT5?`)LWI1=Z1^#^ zhpDB@O1~h&qle>)&t*q;9_rCx4v`mFx{MY#?;?`&CLq^$VE*Q42<5gpNF7Gy3v@%J z;HRq!LLAdYNi3B11#rmLR4~yZ7zdaDs(yf-#Q-_ZLL{{tVuZ;UF;HSklz3rW6EW0u8ZEH$H_aW!jXNr5Q1S*< zdNxnmx!Qkj+tz1;?w*;hlN%k5j`1nX1JB!*u5CutDldjQM%MtR&3>pe!4yuhRl}aQ z$&LtT6F9f4n^SFm8MXWw?ZrV&Ys0Q6fqDgsCHt(#sZ2 zZPGXUjpX)K#&g>$Yw6Smb16;rm`iDRY&WV1AxoR@8#{2 z*ad|%AOjdQ5J4u5-RGrc)IHO)@k~Wpbz>^9j8+*EX5#LOu-a1b=6o`)zC|w7+4kPJBcW8tR~t+)oRQ8py6Nr3_dhFcHIwx`a8!rbPbb};?OzJ zz9ZbWdX^2P>c}JEiOs97u74unR#Hxzpdgjj=xuE1UA{-tN(PuXm}pI+^+dZz15xNH)71Toi2fHT3qdBhf{QmPB<^D%D5%xuySx z?EPnG{cGTFJYsnQBLqtN?KmcJ2h-a~mLHzH3}ZG@{LgQwY-MGAHUQ4l-z#PSNnnF0 z7RuRNh|)Z#vWr|xR2-Mi-LNvoN!#+8UbdASC!B<5M09>f#bV}!qgC12GOLsIXhDf z0FO)_IUZOgy~_A&*v_qX%2uI}2xP1DbgZ>Gdd2x&w=RMez42}UJNI4w5_EBB<#vyW z1?uib*=d3?=qU8ZE`kEY!qA6VNMoP^h8@%){ zmAU#5b!E9WPmLA8IYsd8EZ}VN_7C<8Uq4aiBwS~DkvRrh27P--6*es`q%{kGemTeX ztimqvHrvj_R_9}OTb{7rO85%2;j1yGr@HrcL;o>Nubu442x9fnfNnR8DPZ(|+wT9z z+?&9+ab4%)xHE$RFc|DR2!L2fkm3sNtGMr^C~l%*FN!liD-)sBYM9}{`cLpFRYqit%Z^;x0 z%nWAka_+g``ObHQKSKg7s%JXnVFBa-ruvK{xi1jGFSB8)Dge0DvMstRnb=){8p}1GfQ=oDb(9KU*4pZepxiXrI zz*=d*;1ws2K!bz62Lm5C9hQ9nfjq?+p$RZaDbp+pd@k((^EPNJJ4A_Q{~L*B@LGym zH%zv)m8t=^+h+HAfaqO%*&V*$GKB5+P-R$QlIfo3??yqwRAdP41$+2B^$2wcO$tK)wC>S=L#x9iYOVgN zxrd|Iv^(mvpy9=}4X?rMXn5Mb(AV&9p>9C(0r|*UF90F@Hb)4nfDqbT^h*HgS?*AS zG6a?^P*RI%x)%tcl@P+ke0tYiz40bLQv70x`j}moMa#MbntOe#ue}q^-3#Ef6g*M( zmmD&z^aO-E!?P6@Y2$z`*tgx`nJ)E%I2O2r(mJs54D2#7y``sly59DIz9l$LJx3pD z$xJ7JO9+ZmT3Z`%hp^+ZnQV~;EYfH9IJWcveVV6KE^z#Wzpbc3$nK#;6xBYraK8Po zrS;J3oZx!N_W*@@HyB)>)2!XAXfGJ?tEqQ|ycdjT-ojXww4rTEt`6-hvPEd8h=X9} zu+k8MKz?jXK3#+xL)rpzY)ZziDqvVjyiGqdZGKT|u|4J-fh%8mHtvX@_s!9wt zv@|XoEoQeojdcuxj#=UN01g4K!iG<_B7ejjcLA>|Hdi77g{7}bNCakU%+lnbJUjemMdut!#*cEs{$NqubNkGM>byPcYj$dJ}G6)Fj%mN>SI+YL}hJXLUdr z;T|<0njPNfC`RUTJqHNkZxjj6G=@ez4!kUIchmwM7o>93X`Jco*UeX?avZVwjL~Pc zPoFgjTPmxVbL4a)?rYyp>*3`IsU7ok9FcIuFlaq|6;_$IecV-9W$|~j%-R70k)FKm zV^>6RP1;s-bqW?FSCIda%dOz?r^iyA=ulIvqBCSP36`=%6qBso*VMD`eL;Yb_e<6T zy^e1pczWoV(e|_~eJV6lWif6Tw1x+_U=z+eVZdzMu0c2XT-bt@ueEtRMds4h)*FW@ zlQFw=Qw9d+m#p7Fbsf{6TTN_xMKyB{pH3EqI$+T#e*KEnBTn&5mOW5qeiYu``;me5 zCBt_OKU*`y)>1imK!*qe`H(fJ-k34O{HQ_vTAN{O8W3HtTFQ!(=)4YJXDiaM=Jx+rVtQh5wo10J$q{>a zaH}K0tojC4*zrIf;ww^?`qz!tU}I}${YQHHI$9<$TO}mjclH)LLr6hT&1$r4vT@^2{y@siYw`8;h{1RIHPV`A@Ki3H|7nq0t| z?fq3NVea@;BkVTa4HyJoh%B-QCr(v_WIqj{ac z#6&dO*45!_#VU+3_6LL%6847l7t`&`p`;wg6e(P)9J53K%~>P`dlO_2#LT97p~&I1 z{gr~c9x3a$Egwaasmtz^O;)EXZkFx}%+#8tDd>9jZFl+s6>7NweO+6Pq7o*sicCAt z4S33i)EoC1siN<;n%>ry2_rl`*cOj3Z3F6orMs~tcxDfbOPSY5u@ILA4(?J5!LKJtAiHgLzsYF0s)m(hee!+HFu1h zNu*$3mU4U1ZgC^le#*pt5IjZArel9@(swn@ukx(cuKEySdbLdgg9Q$)7DULe;u*gG zr0&j>6}UgAt@NC}AftZ+C?cxa4lLtjJJuv@(soBr*i3bLVf7;wupCB{sFWq6k#<;) z_03uWr;k#UjKhTT7aNC#GY&LB+;SLv=(vQvP%(Tpivyp#lEu=H*b$tmF~jX0arrUX zy}76o_=L1F3ppOXMp5BQ^KxHn^F#^oMn!G!Ep`QSK39pK@f?qNY$ZWthEPOQW9FOR zX(feg_F2#gT(0aOQ=W$3NFk;YjbQM7oA8Uu8piCSr;|x{>mtoj%7^@CsT=;;5Tb;8 zIb-nMhEEv2aEq%ipP_u)amB8K7ppFC$H=MHmlDlC(*is+N%Jlnj#+a^fKDkqfYRmk znZ0@v`nH34c~L7azj3uCy>Ev`DS0f>>9Le{4oH>?xg;EE?`rq8bZLxv)r{#H2h*K% z;#IL1y6W&(?C^NpKo`U=2ZDH*kun|O2Ay8$0}CYz_7fQMaijWZLN=LwE)9YJzha1zRAee`u@IUEm~X>zjItkig$zHlUj7@^CnJ-# z$%%wooU#n4gPb;^KH-rX1?w+W3?#ghV>?;czf{!7wxQF>xW98V>91rjW6W%T-?14H zLE@QSFg#^=C&#nNXVd04`5f1r-mvDYF2@zyCT+ip>765~!%yQcliX9+qJIFTF)VVg zWzuZT|HApG8ZACghXlIE%LkL!pwIj@UIS+Szz#~)?mg64G&EnpODw_1f6Rp_Y{?X^r%7-^YY^Q7jQuW#A+Jh|p&6&>YChgo%dlWzeje4s2D z11UU)n#rRPpT-J*dw9CqY@8f|v%cU8z%D;wv{0Otn`H~nj)H~>ZJ6lo{yWdMwTzZ< zU%j=b7`3!$Cyf14eF`p^_u;cd`}Xv>EGlw#Udjv%)~@TQh=b@|R2yn$&7N@B6R&rG z>0OOovSav6lCX)Lu)`EH{U@mlzS8aB^xtcEBX-H3)OtW;`sENofK`q)h~Tw1%=&(a zf;m@WL9gbcl36ZDNGAQ)HE&=WP4?AC$tv0mB5F6C(b$BYwhrrN2vHQR`+98bFEn7+ znq!j^qDaI(Y?S+VEKt(=LyyG;x$KzUu>sAw13LvuAsvcE;4=&0gCIzw zv-$p<1q%>gq!@6-w@BfMmF5AYd@T32VPDL}cE~BpM+58j=VJ#wJdSwRU44 zNBJE0qwfolV0f;tVjf>}8W!4A4JhFOC32PvTqZ1~dE|rEQH1$cohIsLV1%nXV}gj4 zeRcLPU9CTnp+O$;z!G(6@8RxbhX=bY@|ld-Y&koM_-1Q;o7SOkMzX0XWCHh_Ez14#C$gX{NURunTT$V5?VL{hymh()192PxkQ zceianH{50w#k1Si*|xuTY2=}8shW|+y2{NHE#`aPY#lz<%htc?%wnskR4qO%iPpNs z_it}qe6ekLuq?TLu{HGU0cqpi8g9H(`hXY#Ypn%*n+L&Lqqzp;8kk33)(or`4NlZS zWu4E7o`b8TgWLtUF%6nRTTuXD7AAV1RTst2qp%*38c>QRY5Y+`yW6p?i%D#xf$bZq&WxZG3*VDmzF+!@NO>}0)B@fC-hZxj_2winFpt$1 zG#~}duit>gM4`PY*Mp>eY5p=qz|A)%F!z@z0-8;`!{-Qre~E*?X?HP)dm$@?JR|en zAE3bEG{@F?yMYwzZyUOM=;&nk$lyJ5j@})fD2Rsa`&=8W)GgdWJm8bRJS>TRhWK?)o=@LUmSqtgU^BV5#Iv)k@Qh){BR-l=H< zy&EDt7Plmc#v}p9!F#Cy7xu0-nmPWKAR9d(z=0ocLX~6-E?_=hWCcsf_fDjpJKXQI z3v=~*Mr&%uHt{hE+_(aBx(n5M7WfzyUL`!WR@LunNfG+C<5f^GjHzx1OM5ogArtX- z@0*-$md} z-R-^Q#8;8Hm=1{B&9`N^N=bjuHVM>i;~RHnTE;e$zEbg7+UfnV_0O13G=0lzW-~~s zuc{dWpIOI%8^jpr+R+aFZm{j1F?+$})+y_QH;i?4)3*5*5 zbkz3>8Uf{d064Lpb1K8>DgrpET`B89)o4@7l%4DMc49rfXXN;X-q8ck2R0r2EVz#@MCLf9Q{3V# zEt9$tWi-kTvsHoy8@G6e>qp1; z21oZfJWQTD2SVrQVt>z~Q39d!@FC=;>=*>=!Ew@-vO}6-k34v&tF~=)>*(FXo8k}e z^<2m{SdAK`6Us_9r+moeC`#OIPx*}M30F1?PN|W^(HulSb(xQt;4z7=f3^Xa73}|4 zX>-|JL^H>YBFZSn8}^OYR*t|<5;rI-Rl`nF?)VZIX%FOV<+T$6@`NB_Bjw+-*S2v2tEvaGPp|mq?U0 zewI|B7GoA{R9FnlD6qfmPc84XfZM|2?34OC6Y|pl%9zc;Vn}<`juF05zk9T{dYI;o zhOJhML9R8o4Rr-hLuu}PV9t{lm0zK_RlZi5{#6?t6M?88YiL+}@2l%8kV^#)%nI_c!T`!o#?1r*0C2YpPKeY7(WL-R>lU;{R9GUbY7R{wIyeoqroVd!Y1tT`*p+F3 zgL`UtHvZ6x_gr=}Hs;Xwa5Q+J_5q|*{uv6X3yFGHu>$E3C3_6CFhWY}@wMpA2GcL_ z28o4GN~a4so?D3fh4R&nxDkRs%3FVS~-=#%&?d}El@L_Ubv zLMk6x`X32}@H{}0_{h68xhTjQh-M4#Ts`%2{kXP+@3PuR;RMW3h!TMz`bfx0`eeF9 zA0!rYzrR;ibD$^C&$@=ESHgT2>heS4Vjf?+13k9Azeg99g!&L%$h@fJ4kcag*!u`7dWPxJ*0hi2;^HEKkQEk1_cM@UmD;bWkxUwVe`Njt%ZNoj=jJF zG@*~u>|29T*Ix@y;ghdyP5bA8OzijzkW2zHb#tZtZ(|@vyeOAyRZqoI$ar* zQmSF)NEsA>17^^jGtg+}xK~+aJ7g`GOCZ>CuqHDUaHe9C5=k}@Y8k0j9sUnP7jGpr z23 zWwB_9#<#Fa(l99IQWV8x$Dpx^HY9BF-}!!u^5hS5EB36`ir3FV=FWi5HORS{IfF*5 z_SIzs{Sjjg1qSL0gXM*eyJpo^Efhb*cdh-ChC{YHU*xcrQGK&XY{?ND;WKgzromRQ zU~LeBYie08=^E8j+Cd1HC85|toKhaL>4Oe0%ol#e)aMjaRft*$dF1j){Z5yP)d5ND z9xjK(bJ#=BEetTn%tL`$EBjOCLQcBX^pySf)-K4*nDYVK9?BrLh)?Zqt{z(;og?I= zbEfI%Z9$PSA*!Q{^(w_>h-j)s{2G`n&N?XM*F?-yFj1vfR6m$U045=Cq&PkGCiZd$ zIx|pe!*axN#{VN#V*MY7Jvs>G7(NrO%M8Biu8GHPH0h9nE~Et|?+R#eGUr(7k=Ipo zvuiRgSvmEWIHyJ}^?Pu(y9lK?I<_@Uaq^9xg4KIRmk-L6i;c%7AQr@v{vHD{DsH-4 z;6Mmn}isA)47Cg0w- zs@>9DGq?!Y2>N+w`G+rk9(bV;W1peaZJ>k0wztMWca%}un(scdKd=hl%V2&~4a2Ph zM)Fm|F5z#539R^T#KeFW;$bb8w&lnzjtYFWb&?9TDRxJBH0t$zJmFqnb~GxgDw0JRv)F^>L2t1QoN;fUZe73$ zrm@Fw6Hzt7?rbV9U++pX_QlCoT;yoE-5D#7Wq;P02}b5sFUp9a0*;+XyK#T)!>SSs zhwWyk3=u%&TPpz8osnQo7qC#u6}AX}gE|~DtJZVP>UFiLj$u;BOP09c1-TelvK+7j zSi_>IvzAI0u0LEekH~bpXaNG&0hlVs{XWPR)H4>3PdP)vNNvH=K(oz*ET-irjacsW zP`X*uDdr?Mu5LG9%Fo$D z&q)3myy6^9fz}9pF07%+^F+0R#9$-hBidR_ORbE|0!pk#FVaZBqvy1B&=z*K#`~3w z?c16tx@DQpxxYEuJExg7wQg^jzc*YG9BHT(lUu9)1h1c=sF$9n=pG*H;gTUSu@dt=8>OXj2!U zDyq8FyR^1J|C^9U-3HZNZS~AIO-sYomQPO0qy86GbEfrz2iyT!58p*sBjLJGJXW8} z`6Weloli+wm`M69>)%q3JWM?S05R`UwFPcPTNLUU)-x*fB~PpRFd&I0k~XwNG#3A2 zZ_iS07}Ra)ErS*SMC49*prG3NWg^D@QF9;w0c_i8RXwfd$NDR%V^f1?+xm1$f zOXgui&N>M3$>!$C_G8*qNBtMpVE8BDL7@xytpQO#?94I!D58Yj9D+jnJoK?ful=od zuh-u5qS;K9N5A5Yd6^#-FJtZ(Aw;ggaK6|_2hcS$hWmxH!W*?~LdJ$~KKQJ5(X95l zXjaQCW4{lfSM@%7utaS}g^p(QRFI|J>H{cK1}O)%m2Ku?2p?`$xrW7;3@gH~gc1I% z6?r?#pO%`NOCgfTjW)wjf{nYn2#NZ>w5AnaboyP-SfMNP%%511E%I+h%>uTP1r#DM zfqS95uKtl57VZgW&<+O&7zf69r??Gqfg;rForJHrA5n0OfEJ!7peZp0@EgTpIPX49 z5&)#6#3wpjMi4UaMaMcFG zId-d8e9{vxa{uN|Pk5(_>M-^(yWLu9AA49(#*8Na=BAd-0ogdFh@mH6c;d4m$PtC1 zCls|!KzlR*Q7TZ^`uI(U!|a`9Ry)pz-Vc<8#{R|1%$e5Kj9H!Ax9{)wQ=8F`Wj789 zdA#Zvh;%fi41aF;Q{k_LC`OGz(^S=`vXEd{UKIA{r3=wN5 zOb$EdRUT0N+6-x>%sR{(>>s~kSQ36Ld=hr36jkWQ;d$Qyr9Z+C3?3AB?PgS1kK73Q z!?z#gVgQe6;=iO(m=Gpb`;$hsPWf7A`N4^V2#|gn6*TlCaKZni>Dt#g@A_Z%aG!4QrU4-Fq_n{+lI=pMu)Y{5-ANgnbnbm(`ZFq$=>WA zbhMP38O}*XLYaib1&A6pi53=5iKt$spF`RW0apS!uFU9TNjeAL6Dx@XGf9!o!S~qd zYsCC1YraOCH8#~Hxk)&ITNZvaB_MI z3!1VqGwCrJhb_!$YW7+LS&a1$fh1?Gn0{APM=RFD?rj|;iSjHU1NU!mNueWrd!2D= zNDU9%<_r#SB2_5#D3lH!zdL{=CY>b*w&&0Y;&1_5{>A`by0C1e@?Hdgs zzPlwGL`j;U2ciYS%aygp$qVstQ7f^J8LKnARX8Jj5VHsC9(T%UCh`J<GXP?T?=!qZdLGN6`WM?H|AHI3#ZtrE&#?c??O-Kf)naper3|^cAFHr_px|`+y7ZCIg!oo<7T0GwmfBow>_g*fqPosmk?L z?+Ut;#iC-1q&*M}9vZI)o`HQ$l>M`(8>O6?FR<>yxxa2GhK|$Q48@50!6$`3nZ&}3rC3X>(*<$i--7L8uJ#Ry%-1% zd?AT}tD%-6j6CV{WdatqcRrgG@La=KoE^6J{3V z#j#?aFKV}k-5zz_)UkCH;WxQd`*eHzbeYYEWfahGWfVQZ{R^M;Me%>2{bKN=sq)B4 zpn-Y+0yj>1i1|ek#c)a9QS%8}nsYi!IHs1F_@Oj^v0_5ULR%xr+t7TC7Wmy@$iK?h zpytWB8U>yP!1KFE!SXI|>;p&x^x9?|tjObb&CNS(zH{#|EAM^`*~Q-9HJeVJl+Ew+ zMr%>d6bkfW?a8^>6E%hN^!zeOt|k%}nmgyiMAh<(oK_J+?Ed5@vn z@S}*`?r}O|=!8JXKpeb(o#K(-)2U=*MA?bJmXGS#*t@;k~-$9Cq^oneE&{udzHqfP0rOC8}a`u zT3hd8lzBf&?RnID-tO@D)ag{p?smFn?YkT{?{g;8u~U-rerm0U-=)a!IOB!Dpap=D zvFiBj%<(E{3~p1zSnb9`C-2x;`>)`_AV-q5s?#>VH>}RM+}_aXFuD@w-{lRno^m^c z1--rq`vV_FBg9Elc67|81Lerjl@1@4aP}Kz`Hie=tkrBXwhqnQb!>X5RWh5~M>-Eu zV;^Mq9HY@eKMvwgy4oB00m>4E`LSXCM*Jt zF_h+*r@1eLp`V{PI606}WV1QbH`zKl*;+qlW)Db;Sn09N4b&;J$(-rGm4~f=_1zO*`=B8#HV8jQ4O>zh*$GSY1g%K07vw2m=a&oS z(LN0@HjE`!2{_>Tof9@o3^yDHzX2d`2B&q?KCcB1f*p3MEDaD{>rGq%lZWs4Xgamf zQDlp902isO>G&2TVDfK2nCyPAy{@rW>YEVn$ZUII)4=!^M{BCaGyl+SP&{*qmWFs! z!Ys(9GEl`7YbIe)oGm35Ygunayvy$FsR(%!H6=x% z-%pj5rBLO@imIgMfEi*7?z_10bD#v)I>9l?><;2 z=yt2k_@QmW1y$dfv)^D((whCSJBX|VxCFE-aVw`)`vVH*!A^rsoDzPG%1cSGzYHF* zAnQ>_+b(c%KWesHSh*(yP(5>Q>dM3S;Uz`qYoykRGfeeCUzt+cl;^6mY)%!CB)ko@2_^7KLN`nSC3kNC!hz|xlek^`z+**3)?vL)Cczkz9$8PlM z?vB~{`BNJo*|_oid(Lls{(g+v`w@vhW|WR&?{jwLW6RX*Z&+?ehfbW>JU@BOU&4>YU)&koqtY96<%)Dv66&cLBpF?FqvoPEMMGs@ zqJj1r?#APcW3~{ z4H?48R_7&wliI$6grn>Nh8S)MZAMc7W_opm(e(6RJT-M;vS#XFXFDe6`|%COWK1;) zBM00&){IW1S*cCr@0_}*&tgo?K3T>L=94)40|_Ia`f>^PH6vVty(lIx;0k+@A6`U} z^RiFMtaQ>p_|7?uuc-@D_&a%F@|28$qkkKE@+Im>Scw%HVq{+? zPHmo_;zR7h3>gUNgoMAc0Co%flDf-Z7te?Xa6fPjpo^pY$oSP1!x{?WeqzqqI$Be*zQWsG+A-sI`$l%}7-CfOYl~sK zc$=`F;{|1g1|V%4INN}#u0DyJEP7^wEb)Xy$Awo$7^Cis(r4O7v|(HGx&vuC+CF2= z8Sv~1u7-2?>3joNa9tdAz2EZRxi#Qr5h?+g{VDx?M*oqCKqWrlbrlW!o!(%;=kg&V z;j>j?_h)^9-XhoMyuqONbFQLv(2FAEpaf)J_6E;JlHqWwXhYDu6-%pct2c<_-{$iB z+;4M-{Y9=Fr0BB49e#u^iQ0lb79qhGd?V{{`S_dJEzSsvng@fdovw-(*W8~uB8V?Aw%phXw0Ie^&Zf+j`SvZT-v9rDq()S$A!akCt#%Cf*=?)LO}co zCWDnE#FYwchb9=x+@ugk;am*t-ncf{cGZT@OFWeI22kPVHNmn!b=kSfmmee!Uh5_M zcbX2^k0JF8b+8cB!bl$MAClT}Kh)L1*4Eh>hL-UbwC*u*+a5Fg7VGS24i!Z=U|=-@ zj+V5>O+l`*9gttx1dgm}$8ZyZh)e!Wn2y3rP&Z<|NP}j@lNJ#TrT|Joxp?ls6(Gy_ zn66KoQO#+*g*rPft-=oPMM_-UhGIG>XXFf-A$*>zxTEM@8}#WVcNY0sP%y2~O78rp zRobb_mDqV70(BTxS)@%+Jsqi+6x6AJELGeYhh{HI?r5Z-K=#jaMRuOoP7-=vYposR zxn34^4%ZN8mhT4p<+sM+x?&{o?GztO8h3)C51vlWzdADQ7)u;sU& zcDzo@H-28rDb9X3mu39>DPEAMAOZPhvgW}3pT}LNf+1mQP1-U17pm#2)nM6z?8oD% z==*lV^M;S3I@*hd9~gd4*zWbz3-hG&D|lwttin<#C9M^y$QLA)wOVP^i7h&BJ;$*H zG6GA@Blx2~f^9Df`IF=X2#aLMF^>96GPP9M6>_;n~;T^IHDOD|&AIet3otL3#Oos2Q&JBh}HV z2XXPLY1Y?bW#`q#u9;n5XlGt?uU?UIkDmQ)CD*^+UQVvbwg0VU)_?p86L9MtKUs$|hnD6MD?)ukk__J?(EnNqUh590=pRya+%7c7&0IrngJ^uD>{A=%-|2Ug| z(>;~=4>8`^CvLv;L~)Io=JhZaq*)%X4;!c+<`D4Oojhmmjp#)Jynq_!ND>qS0B!>K z_y+Yb102+ZU``qlXLD0)f30@8uX)UY~Sfv#)BP8TnU zh=j+}W}$~)R3`+`dqfG)ZVKo(zaykt=qDC3q_u`AXeY3u_YgyRk0S z&4WLP%Zo-J{1&=LB*vO7h*ermR?sQh`+XG=JBGi=9QI->CKX!G1V)X~fs$*Gs}n${ zQAMu@TJJ`$C_3Xlrx9nP3oLG28q>;UcB06N*{Vw5?YQb1_ywska51P*7hAy-HYpaX zDVDIA>xBiq#w)7({!+UdTG4~F6hRIU?3L1Q$*kRw8n(p}MX?0?ORqAfq8&hk4`wfu zx;(WrF)##%VlBeEL=~~YZZIgC8fm~{Skw5{fO)$~W3kZu!ffHGA7o)L7(rDLx-HR2lvd@&DGDfhx=QNH9b~;thBMQRB}t*mF|?M%5)cEy(5?Z zK^zyWp^sH#=s}bboOJvtAf==F5azxJaykA&CIBgIurMNiB#~pyj|M*4oJM2Y*}LcN zn0SxP9{=9%OvqEUv`4n2+EYH)(|xe?zH>`HNK~S?$L*<5rJ@#v*(Rp=qs1E=LPd&X zbylV37ufVC-+eeFGSg=^ADA0`>o%h)ECy|^x1ef7RNl5%0XIwz%gZ$u7jnyu?EeH5 zl>PAb#zyMgsO_r855h+8Fu25Nk>S-4;z$c%Umrpa$XQZk)1sq##H|C5g1B2Th7A0W zD4qh@d+ zX}%2qc&FFq@OmTOonHHMcPief~M^pJ8CvtBg#=v#CN9>@iNQN z_d<-qYKfrexy7pg`wC9N0ou=h;%`RH+J{l|W-r*E-l&&7imaaq5`REjv_!quBkyte ze2(Ytv|5#jW==&%;=)JfbyB+WUV??nsoM&Ob)L!|NOMhYWdO@J|3ZJG! z?LOb7Prv%8@A1#PUQGw>>*D9cv+zHHh7w{rxB$2Ui3MY=k!sNM*FwQ-dcU;bn7Y3H zy1M?pzQuFrws-C6JU2YFYu6C|XX@}yda;vbgpu0JkSGk*)eW(|#tVy!7mQ-hf$pUX zmwqYi9UfT`MHCv}^Go`czFAGSh;_n#8>kA)GtgcJNVZkkgLnDU4^*-wAPVoqdE4 zL{Eb!1!JLoDEsxtk4XG9wr{xg_}o&Kc%YFr9S94etjJcC z(k@ghUW!7r@BaFcNqVwzU*5BU-rSSv9T`vKbgS0Mw``V4O#qFn`{dque#sx zCGoHj!#H)I4d5%cG&D5?8eAz~sw~CuV4j8+{4S;f_-9}T+r7MlE$w3iBWH$ed+ht{ z`|sZ6Di&gY`oarr{1czZew1y=ezdlhjsNBD-DqC{BO)N4fD~Ibuqy*%`c1@1X>Q5= z#7oc!F;d_LL%0cMhB)u|iP30i>=s&COQxrm=)LrtR!&1BV&|H(@hfs z{@c*lxWOFg-MqOsU}DCxtUVZNv!FVOr9)w2dEIOWGQ``2Y}VniTEHuZS${6Z5u45oy{-|_Fd>X7$Qs`UL3*3hxDVm(frqGn@ih_(= zu&)q;s|0mi%k6<9kuH->R4sDfp65a?B#k#`dgIF2gX3+G0?Gc~^5b(`C=nNB+h!oE zcn?*)qw0~Hgtm_DjWrAXmZ{e63HF&yk034g^fH7B=LgmaqLLmzJz6=w(yArsic4p9 zn4=^*7&V)>WMAyq(Nw*)-C0+s^92o;FG@$mr(gx!ps@7n=c0$x%;41?YW10DB#~lP zn#NKJ#O%VvVwP#GnpkOTT^XyU=el}=u~2J#aJqV9ODtLxJvQ_tyr@raI!}+b3y zq>uKXT`KcWCW$-FEaAye_zX|Bkp0r!(NxE1S?Or;`i+=(41Lyh`7gv{s0UgPe%Vgl zx?_t7_mfW%bQ!Vvpbv~x5N&9~vCGuSoLfiCGw^2gbTe!eq3I8Ih&qw#@_FDmVMdUT zxSmze2d0$V-QN}*dvt$M5p#Gwu4-tg%dSLQI)$VV=&uLk^~#=+?8TA&J8P0`E?!$3 z??stYV{au~Y$Wp)mw(?hc*x&g*$~`t8@3}gb1`AvLmdn5=?dmP_yI3=@=zGEB!i)F zb5j+36%<%ax;^n!Z9G^yx_945B9loZYOL|f_V&sm=p8??Ufm!G0Y%NjixLg85zo+d#MA)Rwqu$EH}=+)cuuZ$WYA@ymijfe@!AVhf_v>SE( z3I7B(*oyxuyTBZSy3)|Qqpujl_6o*1P@fKuCLzg~JdZoz_`+V+`OFv55 zR7M90E}|l&LfI4ej0M0C!Wizk{4Mb)Y9oeGC$1l3I5#4BJ_NuI@KOm@(WFdSeNHmh zuX7n`6vOM(ynJ9W7{WdKN7&rx9&6Vq(vmu-ADtQxv<~bnFD_P$#;#30vSc*%CTeRF z*^7zV9~h5K&mM26tZMTg8aO|)f8S_HsQlbS=b`DEsSDHY$|?6kN4!cfS|c>(;%ZGC zV~{OQA3izTbbC=r?O7Z*8Hm!-KH8#NJHq{gzriL@Cdcd;&GB)BD)qoK^D zm|62W(Pgr^ItHRmp{U^S+Esa9L$x$N9G$Mk&&W)Tv7#nnJ^!~SKi%PB_k|9u|B~W& zdOS|QP`17|UA5e4G}>IYUL-vDZI%`#`)2a%6r0td+T^M#v(M|H9=CbKfD_4iE=$y` zu*3E$qw4AxJV@J9%x33pMSY>PNdNnLJRfHZbMV@Na|})xW+8m7wGzy5akbqWJ_49YqbEnD&83@Z*?n->IkX2a-wqLx zBo~9hMYTx}mmcq}0nZ8%Y$AbbFd?O=f!NF43eEJw1*9Eh{+mf_tbt$>imhIfl5ORM_55TK|KW+wm3JPIgEz?^K+*odNxfI%9zjX+(|e*^|$nTWH? zu1?gAi7xO_@2-+0Gl=&dQMy5aCd+Ex-keq{vNZDC^ag~Du~3+N59_JNSA!?x@f z#QtOb>xPyZhfWTelo|*O zt3tg8$Qi>8tHWv_`;RFqK^rFD1Ue9HsE3llI=3yihI6=*wkz5yXVO+VfS_38fV5Ee zp|3S~NXyi&q|7OTLzAW^XwVDrR47|hU*j->BtQ!a$m`!=pYAcM6Qy<Lul=NXt_Vvz7ISw$HZZRu|1|pR`PmTHrnlk zakR?F)!nN;>!Y~@HMQA92`!~9`fODJT zmt~W3#tc!F>$DiZwU0Gog~0J6CMU+j+Ixd%b=`Z@Z!HJ4A!2ZYW(znUAhnlgm~`VC z`y%?>_3C@la^J4qUiv+uAGIk_!>&KD0?t$D8?)0apEg@>)L361lKqMuVy3r#U%7td zkT#Dc-RQnXWT=H(T&7VZC0%pW^FI8}X)DTH*w31;=s)Q8T(|!;jFaOCH@X>T;5sAe z=j;?syzB*Z?v-Qc2L0v7zFqIKIxQi->^isd1=%=?JCR4tE>!TkW~=DE@6l>5zGn^9*KEVD2I~vOPo0z1$+Lgy9 zCy{obxqNYN-*S6cvIon$#`7fQ6&E@2P-^cmjHbPG8CsjDCV^bj5Yv6UhA&@Uh3$A3gS z{t%9zM(n~)*Qcn0fS>{anuju-6WU0bXl!K0|LzxJja?-LU%i*v`%Y{hir|5H-_o-R`*FUFpD#2t zi+~a7t1>qc&>5CbWz<(aOPtD;L?%Uc9*S z*e}2QiG2Gc(4H#)4*gi%A^Lt~giH+V*~OK2{OIC~)F8b5+4pcf#yr2aOS>>GOYJWF za=GA`-1mcm(xM$!U#}nd>`Nc{TJHP#^^}cVQ3|Rfb&&>4cN1tHA zu`l+lxi+4S-z%?ur=PHQ>FB(EM*aVM?13-W;|#se_;+AF<<9|!MZ5je_mmH9Zq!dJ z|J>694{RTB(}}C?f?bhzp2Rr_+Ib@fd(x`pj8t{aH|u9dhKA-RYT~V3&Fz^Qehu<5 zZaSyGh&9&xZZfCelF#UuuAR}J{Uv(=8S@izE*G3EO7>;s^Hc)=UW9_;y~x6RtKox& zPaFQ&@J+)H4S#3IGW3M2PNyvZUDByd*hW+I3OloWv?UJ`%LEcf8IcPD;2qT?Z5v@$ zA2!%cnWi-7i{*AvCLZmdO12v3A2t1~!x5 zM<>e;QqW?ot8{l0RJz-Itou-(qV(Lp)IVhgS>|3IsSqro?m5Pqoe-Zwy@c{q*SrcP zdx${9p`tID%rU`=LM0`Xpy_s5Z8MN#D0aG0Cf<~g%uXkGMJjZXV-6=0Cn1%NqmZ&y z3kI71THh6L>=_B{c?ujQpf%qWr%%+}|c)0|XN6Gf6`)5)Nodoi;wQM4m%iZ+;o0!ikbblx|Fp9t z0D##}+`jBjzyl8;PiPyjz;hOvi61}?8PRCM>EJgvsyVQU?j@9p#`Mh(%xYh#2yq!xX{vSBt=z)6ETf^AGQD~n7Nf@F z+HsiDhT+~}3`1sS^N)D;oGlzx{Zvg!T-{ANUiE*d@$g+R(USufVD7qMd?;}=+k71spM zsO#36LsA3Chs>gg-Z#K^LvWs~BAG-C6Bwv9*05lddJ5Nx2d1EW+|uns{mw`vQEq0k(H^T$6a&e0wFdW zVrHQ%z9;*WYgiOjvr+E+fO=dXP_l_zq+)$QZC`V~kT*#I;Z*lxdm!SUxIN>j((1|l zZ;&N_iZ%Z@)IGH9=g&bYHXmsY5J?xyB!(5pzrbkgd>MZficQ+eiq#eq8{2rSL1meA zDxGS?paE-_5C_fh(3r{_9xSquLAmZCdzE2@3*PLoDj1*K`f!ueSWmTiaGwqSb!4 z<}8s>EXLvnR#Pxo$fsxq%YGTz6!#!6 z=B-$p{yU8sgbo3t>Nyp}BB$_%hNy_}SrVF~gP5Y}n<8rVLJ`cku6rxIhgq@J^31s)q2W2vuEjg&5r|=SLdSk_Vg&suN561I{_1 ze$!k6ig3V<$OS3EmLviOMLsl9SWP8{;+M3W2WEur2R@{_bzP#?`Dv7s7h(`Swb`u- z0{fQ8hUVt}E44NeV(Mc5?)h?e6V%XY*t;N=?}DkvrmdW{;G2l|b^mxijOPm?`Pm z*{PP)SHvRWiTy;f5uu)1Eu! zrI>!N6ia-q_nK%))pZ#;)PiZ&pwC9TgytCs zMR=Pf?DP&gyMnfxd&FgP&>4%>#40+2Q&W-hpcCrxC?=vL&FR)K?<;!#-1yw{I-Z}8 zPV!01>No*b#K<;bo5+n)B}4wUz6`abZoM@=;!5>aZl5m>C#zhMvU2~(Hf6t<2>Qdl zAxEGo6^hwy!a?C?nj|~rEl&q0dLlKFT%Qb<1fBBGP%!LvmyD$Gk27Yc8a$Y@Mf#ZN z1JC@mj|$|iw=}e*T2iI}@*8h?6nx+_&ZC|tcVq3bSGFG)!Z-DRB~w$&Kl!e5^e7&K zkMB|`tUt@b>v$5GTOW~IA5#hsl`u8X8lai6vRi(%-xZuCeO1*%Wg&BaW3!*RU+`vs zHNMwY6^fN4gLc==_2%YqRk=Ty%%r`IUH;~sKMRK&<7UOL_hGUA-1A_QzV<%k(P;g- z@xqhv0Oiqn04K}nTSh&-RvXF+4EG&%G_mponn^9~i?37DdhuZ675wq%4q+pWk|B|gsOXYf-^uCT3!L;sX z=>J=v$ScihZ#UHbOQvzeXC@8Gp6(o!Hljy*&91lMZD6Op_BjE-U28$^zo0qcrrNQ~#BOA@!O(iQ zY!-fxg{$hLRio9#>fG???!LxIla*O2l70&cgcL`^DDxXi1{CS0+G!wO6;zY;eO7BWlOKHCPe}gMevQ-4w4k@_F%Bdb@N06}|m+vk_x9}_M4 zXGf0RYxaP64uvOm%Uw!?SdQkd1pJ^IA0aqxJAo>6x_IEMr4Don|0#&BQKl%?ns0;=7=m`Mlx7RHq3|2EwnNSkC>DVo;k*5bM>p9QYTI68$k5I>8g7=su2{f<*399`jgHo{E(<&ve!t z9A0c+UmEhL$)dDh5FBPDUO8nk%0Bb$wWX*?j;sZ!bh}I@d%A33U~yOWnU8m3SB%)A z;qs@%7sb7(K5T$$Jf=W%OA-2jR#OiGTKXG^^x}8CscDHi7^1}~)Cdv*;Zgruk*5K8 z(1abAOMOOh3e{7B!)kR3T@9igIkiq+QP{NIGPTb)d`W_zCn zHICVp?Bl`_WU>REr&^nWVf*F37B_+a?uHdYE?jd%ld-uD0`G8?>q_A)>xsxQR!2Z4 z)zoaj0M!SVHqs&j(o(>GM>Ft|RhDVej>S(dZlE33i2MM$gf`^x6NyN4rLy$)m{)j$ zNQ*j7(5lfQWlQ|0b)KvnW)!$bF*dR9 zZv@D`hO>DaE;H4l8D@oVCpSid%UrEC>5wFMS%y0Y!iN!Y%#BEX{=}} zU03XFlmZFKW=C;2w_p+CKC@c^2R!?(5UTu`9P+){Uo&seZ*?Y%%FC*&E0TO19)Zp5 z6Ml$xY6l91bX8csC?=Sex@Jo8gm*ib{ecwlQ(A=dDn1-<%@y%E(zf&bSi4QUuvno+ zSJKM;Z&gQ5^{(4j?69Nu&O1%YyB?$BD<3wSPMx6II}vtt^!$zvQLWx_L6WQu6N4?o z;pgv=rtcrz*f;oNpIs}WQy!J>yi-D12*GgV@>#JT_n9;hTBhBn4}%5wiI~ya`=so2 z>Le}ygCoOQ`5WBk!?;a>rMouORN5H7)wii5C;KY)$81{Nn&T&p7GAgJp?CATHTgTO zo$o>Q7Hh-!P~pA)-zzH6a6IAimr-Z>>rlwvZFm!W3ydMc_GrIUiG>6DGZ<-2JWT@a zsc)ocL5Z?JswtJzJZMSfhG4-fN-aydFlZrSLdK6M;6SKR)R&i*29yo36j&)787eCY z_6wSV$4g)e32(yxm~(fVK@;@v6XKOIp%22y>P{=FV{Y)qKJ#}aX4bQ%$6Rme#m^1| zym~Fr;qaN26~A-D>HGg!dmHdL%JW=U@BGi~?Ck99?Ck7nwc0=JuBFv#wOZ}Uk}S)z zkY!m2V}y{6Fpgs!aEt+C3~?QY5K1WcP~roQLn)z@j}Ssrmynbul=3x%P!6GlQc7G( zbEP>+E}D~%(wsC+@c6r*S(#vy^z>Zk=x*PV(NrH)cpXOY&& z^QXSS{RsS&r#|1Y@IW{c7N2kA*)zlZ!FO(EzhE~#?em5A?uUps?b!^AUCTRrqss?e zjs2fHeav$h`%QmrnKa4P=$UZWz+mpVk#6?q-c!#$>z{_pvqkUWT{ppAI`fziAUUy@!@DjNa{F!a%Gb0N+NZB+1*4 z`TZ^MvV&c*>54Dx|Gn9p+0bXu?E+0NK65*zNOJk!FI@LO=bb-0aKWdxo-sC@AZK)8 zSRUI?k2`DZ%`=yN_JUjH&Odzi;9dP+*!$Kc+9gNk7w%s2dCae_9i;s0#)lb*T=O%p z{k-w>Zy(=#L3RF9XP=ju~CU32|~wS~LiH~#9+ zf9UmV`Jv4pnb>^n;xop7-~519Wpb0e@AAhlW}DZ3WN~2e*!zb6gD<}HG1lC2VC|NB z;BEY0jE_g#%4~c0Lb+p3NotgCf_+5^>ob&9MQw#4)dY*lAyRe+|%rzeUKR@5i59LeC=gTHG zEoWR(R_f_w>z^!Y%jc6SUp(V{D7-p5r@Kj6ujE*4<=f&8#rNIeqL!@AHL}^|vvz08 zljXF5;(*g_t|kQ} zD;$}9w1oZ$iRC*Uec_K*(UtdPx>x0)^>?qY+AAgxUd%d|KfUZSb`d48tIn#`vlGS2 zmd$hRLj7!h(RCJGoIat?k(N(*23xg>q%Eg1p4K}z3`GN2yl2q|YbWaFlIJa5*gtmt zwFRu-GkFN;wPk+IxmxAL$RhqtCzFfqlOpl7 zUw>Z{pS$d@Zo!#;wN65a{{Fgq0-pMByrV43>c8m}AAR{R?(}cvjB}jL>*+a-FsP0` zTX~O~;I9c;{!OEE_q~7YUw{4NY55Jqa~&!EH=W?Opa1+rXU)&qa+a<==BHA>|Kc|v zm>eFR{H^!TJ_$f}@Nd3+&-<_cY(1g4Jbt5z^zSoQ@4(_mCN8XWZ~64%S&#Sc)*jf> z;P(CfC4an@UHq9d*U{PYn182_l#XMy*Rh}n)9n?pJerQi^FpL;cG5`tS+wHeEk{cc>jGk zu?i^f*UZx&+)$e;hId@F>9@~4`wR!aX2;iEbV>7yFZF9%kKi`o*Ey{dF6XniibIew ze%YhnBKG*LN7+Ar>(R?v{}DD*zAeq)!Tkui_aXEW&!A$6o?wb{CN5G<2T~#cy$wd*qX6VKFlFU4fB(4%ivmmh-+DgYsj>QXGremWcU>D z%e7jWH;YjTUIO1~18&tU0+okvxoc8f3*-{H;V>Ewoj&_v>#dWY<4T+qj){X@!tE6_ z!t`P&Kp(g&Sg|^grf91NrJ_0XaTXMVYCy{Tc1BYLl^5|Qm;93G8nB>F-``evQm?|p zDl|vA28>AZ8_D!%3~tp5M_v-RIf3p1p^n=`T+opnOahut;CYfK2k-D^81DgvNnn`p zo-~{$)GYr%^bMq1Q{I0(+!J{TxoiffC{hDeWrCy#y5Xkak;0T{$jPwNhTtUZlI?00 z1pryLpeJp~)-?_hTumR!DuyAYB|&qH6*9V6a}oxt;YjY;?j=sySg94V`u;usbIwV5 zJ}Ao^a+3nxom^57EhNJ#8v1g|z*%J_^lH~5!7TbhX)B{zQ3c8h7g?$6f+U61u#!j! ze3@rPR1cyPjU~bx!-t)aoOGO;Q}3%z2enFJdtN%51yV=xvcB+(qHjqq02kF6aQtb4OG<(b59kGWj{%H=y$VgdO<_~vS8M1kOqW!Z zglu&-J)xDd#&~v|lggY^-8eZr+nChtr9=T08RMlRvv4mtQ?91lk(eM@=DkVeHKk2F zg5{+Y;e-58fB&X+JpiM@qyRd$v%aHx?l}W$%`ocU5(&d*rjRcPM%rA+l)E!<$012Q zvPS5#y0Q4i&!88cs&cwAkxUmK9*AY4+?ywdALO1rDeU>#9=1KhzTEU|?%6A^B>r%E zz7TnAQ7?PIe{kFnTQf>@c<|H<`~bfSb>Np^uLis&Av7B+v|>>*pk-~Er6}+Nq|-rw zF}2x%#{~dcaGlaO#bcYKlhS(^U&um4=>ceB{DS=P;cQ@JQ4J`k3TBWXQ>Z8?Qcp6fSKy zZp+bZ{gRbi&dWwbKIp@n%4Qn<+484e;qTzKlS$8tn9tQnfKm%@@D_T*SBx4F0mK4vbUDJ=eKTK zT3TY?UGiRofzR5$e(DA8|KmsTMA{-Mdxq;i`6@r^&3m-vic`<=bHLQQm}2Ru2>`?& zX5IH>W2IA)M1}X!dTC|TH#maN6k0nK>rG^ny!&L9AKJ3d+X0Gl12zR77k&G?aVy)e zYfr}VH#q6I+EEsgb|AP~w?!4s|22I%E3HRjYxdr~`vY3P)?8twFTDPy6^KBHqBt;n z`&2$?4EIfa;^qy7VAL&lo1t+-R;=i`Bj5(#9j<>I@m;bCOme|wP7IfCD+u2 zlqCnU;2nx6l=^ZV@u@V@uImLuG(=}%cxV5HsN4mRSezpq-_+CF$hxY1!HNNE#p-gm zt~Ye>b6xJ%fwL?8R<4>TL>t zhBKdL00kGAn#^**i)wlO8Fc`ojF|`L9%B)y_1bi>ZxnvSTozfcMfeJN>{5WYFon{h z;sAMo;g8G}Xfuo#42EqH`nZ$)O(;Oh(n#=H+77zX;w?zci8X=Xcls2-_iLROasem8 zIUN45)?KfS9j@4lu9v)d9T~qRsi1dn8sD+7xbV(P#w4e4M$O=)do@k3qjF5hv*R|H zdKC;^=cLll0G^>#bp&>w5(Kz!g}pDP5_r0DUJ<#VmJi4|Dp^7o#aT%?^n&lD-w!d&g1bE=7xqYGyZ_pjtpl0H1kRLQIHY zG?KYPiiiogH;Lx*;C*+1=HTL?{6Qqk+rEQ2ipRxK6bY~R$x?g+doX_CM@KqxsOEJ<+ zZ51QewT~8rg-Y@du6(2tAU5N>u3=76Ob;$@c)AhOBG!?~Vv#R9&1uQ|oP-?g4U3X~ z$@v|tv2W}g91m5dQ*qUePqSi36aOfn_71w+FD!L-Nh5>#mmDjUh?~|QTsAmv_D-h~ zYCOKq`woi!v16s-{A$OT6^gs2#qJw#EE%Kq($0xF1FK8XCx#41Fm1)nXP>B#3T!;> zCJe(ByuJROjlzz}U%#jK^zxn*EBG3C=fC;9GfJM_*kBB>bKzXrcNT;g+|!6#lMi2# zpS*+ZZye(0h7Qkhi-&IAx$Wv1=a8|n{>~wGk3%g-Fo8-E=tand&&$iurVcb%l z@eFj;uOkleAmur9wQ8`4UC?~T;E+K%AH*i4U=8r;SSH{lr^Af?Bf%=7S*Cyjju-%! zg_#PrJm^0$0?>gj=pUHH)1$y6;Do>{NGI%J)TG@Psu+XPwf*u)shESulysay>P$up zm5#(yM^`cbF=90jG}s2YON)5ms0T}>QQuH2R6eXZj$nAiC=j4F! zfQ&o9FZjcH@P{j~t`K*?B0}q7HV{rpaJ@0^E05jky`AumJRsGQwQSy%jM*FchF3f6 zy_H~x*lR-8&f1P%MIt`m_6pYLw=vGgPzCrr#`48CJ!c!dg*1LVW}nqS#V33gS}ido zM382M5InRfka|6rCBD@9q1DwAP5v^Yg+dY@%OHx?g`sKvfgiM%5T$R>3gKcTesBV+ z%JElQlY(1#-=s_MS2IgQ8A!;9$rzUo$Wk34wYPB5!9q;g?>^L!&1&I+Af%|c(08=lyFr!2ASyd?{Ai5=V1aUQy&<`%^6GS5xG8#eDdm%Re&aQ@~Ns8p+7k^;}>9850y-R{( z2rziC62O;$$&p6u_wM^k!wux{ZL+e5(gZr?rqby{*~FNlOzkXJB}W=H_3oraM@z15 zNtQBvYj;5yMKxVs(1!8_NhsHo+(u7N|1@y31tZeocG!~H%nCIFCur5D_^;AV&bz7trrvzsKhleuKdTO)= zWD08GU)th-pwMic!<&v>-^!pu_0Efq`016hB|I^GQ+ct5*9& zF`2iE4{8_k>*n!6$H6@f zSyS|qQYvjd+8AxjXfxO=)i^p|DwS-#rjHKm`VjWf+|snN^{CM>Q;lqiWHtEdc&3*ijume)H8! zGwHb@T=wL`##;|9EppFzyKdTR?lXp_=DR1;1$*qKg1;BUO{SM7=dUi%bwDP! zg>W7J?pp^Iu*lom2%`)NTo>FUk#IBCjD?HQ45SbDlSvF;JNZTK>67E>N1osL&{HR0 zSY)%+scLoV#gor+32*V0*KT-{z4hXp2b+M#r~7m;{@d`@1aY4%D#xM#T^(!|2U(s? z|LkYp6Z|gkiC_GJO|!4E+8rlO+=22k@5Q@LoVd$-5j-o+H2%MGJYnfV1ucNV`kVp& zbL1SrVL~(1Vi$N1rXa1( zTUa?!(|h!AEYvOm1UqO*rrTbTijw zhxn*!XncF0t0+pjtd%rW+8R1gie)({1fsU0wx`OSStF9GX2OAJQA{|=^9CR$R0Ic* zp#pyTS*-sA@+}~syKuQ+jo_wWjy}i4Qov0W2OYQtsFsnKnlFGMbW!M!^$uwZQCuXC z46G+#^p25-h4?5j&00*#b2~wToMJ(PEHny&2{6>>uLW z;&+%E?2L%6ArU@lW&#s-C$vMixh4|Mbx)vlmdVPBM8Ft9vyY%?28I4nejxBz770P}f^99NaG(cY(EE|yi zp4d8qqzlXm1dbFN69-7ofOPWLhZBXakF#_#%vwu=V6M5hJ9Qx!%!*1XT-HnLxgh5@ zE*e$OEh=JvNiK@rf|$$1Rzx`2WES*&POd{l;HTm8ae=etpqNYr z(jA=2!>g4*>kGIP^iff+FsS!(AQck$YC7E&6@x2nUvD1)f4&~izQMioEc?FTW%<#o@4WiRo;$AwA0Xe2!`}%$0N*ZrEizoM8Om+p zr&BAqcRtOo#mlYTsgtjAuXm@i#5Yd823w)p@>Rr|VB!9+Ul=6AJ>maSflvg$w^oiE zPu=sC>Vy9GU616C9j`oy?>AEqrb=%fJaX{+_`=JZZ@$TP9)Y)+Ys;MaEB+q%AReN6 zQD`tADtOYkBIzTXoQ)DsX)QNjJs=*O!w1H)o`JPPOAHH<4s0pVq`{Ca<*&Y$>jL&N zEi-sEYW|XJIe3X-QO8}P)@2(9qhKR`Y=4{M^EA3BU)n`uj)>yy3aLx@gukX_N-l49 z31BTE96k`FBK!(+AQT=8XF_<3^f0{T6xe;P6B*Nrv{7`$6t6*f3B?tx2{ox@L7-eI z;Al!9W_CsGfE>pW0pYCCIDa%0dz$%Kvo%yqWbG2hQfaJ|tafM_j1{%LuT9oF(x064 z|FZQ)(H>KaA-WKp9ZXTs)SQ2efBp=xgy|-t0jL^+*`*a!+)wH^o?nhRokPWtqM-AM zEGVeCf6v`uU^Fo*1&W~rE)?!r48>gKtb1wAjG|nA`XX_uzAyGWkW+nrEWtrW8N_1* zYZUQ(G{}^Ix#DQRb@gh$w36-o| zb$D)kY>4xI!kLDRHfi{4kVk{I3{_|6>k}2+@o=@;ee%J*tYV_l)-X*iiCw&x_i{_! zh2haG%Itl49K(J73*<5IY~CY}2hR}5UwP*pHp8A|)jMB%?M_yeyqE8MReJ3%?`4dE zSiq^j=0C-c03!(%s~Dvrq?je=Ac8Ul)a$fJ1cD2l&tRepq}qT|8nyDBEcCaZ-6Z@; z;A?;GeMjPE{zNeN?)yGw;ErGV++TTLoF1sH71!zU*<()gHrv?Y62{Ryd>fga5LWoElLdO1+ZQn%tr_NA5j2WfgBH-Lb&}R`w zhlH8+#i0y;uq_u0QKY?L<1+1nW8E z4WnWUv=TBb6m)aWowBNeu-vQ~l5LDnAgN33)eK7q%bdd~jns>k*w3J5VHx^;yKqI0H=J=gi zDV?=CRw(v(0;0fPAr;WaDU7&9SS$yFqQNH;vTPP)n8W}c;3CbK=$a7;hV+G-KRMes znN;EH>Aa<;bjga?d0r>1A%Tloyq1*yqF3v9QsB`r&JvJr4(>r>F4rjthG3wbl^oJ= z0xqqi-xb3dc`egby;TqggF=VY5h_JfD=@aGi?wmZx;b0!ov8FbI+VIb7gt{7oEuO> zfv_<&R_jx8!|hQqf6jT^YEv@PW5YZSYY)Q`S3AQ&D?Feje&QrLB6ded1tBmq6vEB$ z0g2rc?oW5}LeN4yC146R4i>aTT8>+WBIe2L2c|aSK61#AyU~S=gUub`4KWsS)C0eB z_=+ndN~*kpK3FpzQoBat?o1F|ge-G2EOf%|zmDP{B8))XDa$B?Ss+D)KEiowZjqi-s5!{7ud*j2!-_J+Oiv|A7s9= zB?WWK?C4a36I)Y-RH0;{U(K9jCe`3DI=E?*NvCXC-kuzjguP=%O>iDBP46EQB-IG> zOo6N7@{~~4^4?ENOJn7_0DIV&ca)?60FLpweHM@tFyv4}a(3+5v(3?}%8;cASQq+e z0zuWGw2Pi$ijMwL2p3A+%a~5Rrz8iW5d$_gr&|G@Q~IocU_?yI3K{6=W(#6KGBih# zSH*Om7j0{WfYl(jcSSO6lo zI?~rO+_fe+-=S)ewTX^~z+>cKysx)284Gn7MI{X$2VVXd^yD^vqV0TCED_K16@*sh zyp1`=tTz4G$CCmppa}=7DoVft>%;Un9mEQ;9iBVlL9_$D=~>V>2_+gq26OW{2`sG` z4la&SuSFc=uK{{Jy&4AVWTX_D1u{ywucIX&z^YK~AxGG%WC*x4Tq0V?m!WDw?Sykh z;zUi=gbPASKS+?NvZ=~wR>&p{GHy5JopC!czj_#pq!W@q#SzAqRBm#@ zwaf}82MP))Cxg(J-}9dRgQC~Ilu}oxVSaEyy?ne!9V|t2zvSDssHS0?t`B@Y5-8<5 z#`hOTv^8s9niUcjbjCm~kyHvF%jVqvo-2~ki&oSx-rg&E=!+i}+Aj}F>9qTY zf5`WnooZr7LdfZIex8ef={N|G_kXynWxy9CWp>LPs1#H=(uAv`8VrGRgTA z3&JB_8e$ZeZjx$}8}Jz`m1D~}Qaead5Mid_vltcxUfw>2N0i#NkUu8an`|Ny##!^W za4dam8b2@I0|%b9VC3&~@2TkSHg_jG&KA5MYh``sTgSDs#(lMLtWdu9rePs{Ak{iN zbpUR~+VeI4*WKwJN(jTR^9L?Sa8)4 z6oAe=9po86PaSf8k^Ery-}lD%9=P|lH!718)yl;97pvnO-i%LH-?;C-?+V=O_ulsg zciZF=eM-M$la=x~&ZV$FKJ`s@5;^(`5JwZp!z^PS$0$EXgoB9KA*y_q-^7;rpTxhr z1!VOb_BqWgCHA#=T!pu_%o48uszn{!Lg=8!iMat;3!r64EEYiH!QR#$fnMwpi6FP8 z8$AMT|BZvHB?~1wLDtrKXX*Q*X!Ic&(HOf0H9?VJC}LddzcC@Wt~E4^H`<+Kivk&o&*WU1UC%iGw<8Huo zk&S-pzvJ2D2;q%IL^;wWMv7hRwOd)`MBRJKd%J!Df%&ZWlk&^nE4SUo$}f{i;oAfM zgyXt=+bM=ER0s?0pId&-FTb)|9^AL@!F>n(zuYGOh0kNlz6ZUh{TKRp--8$d6%D6; z$$y&H(9LEV_)eE1=ZLxweY)*)=veYp+c(jp@P}>x>T?9AWjTWqz#IL)es3w{A%RN# zQYF~p;0%y^&##W4l&2$R#-Qm51fk^ij8??&6qA{rCmxCf>3VI%8=s@$wu?1YVA6IWS zusH=$M2CV<=~I{~I1_E+4wsys4*eo4S2UdqR>>%&D-@~=Rdanb)?W`HSfpx;X1){( ztr{#zVVUpFpMUv|+1ff(ESgav_+b^9Q;dswH%7GxuW;!Ry?sPq+?x$4lNN6##Zs{^ zlj*y7vcEG~Rx+KxTY@h@R8^*SF*$Ca>x=-OLVK_G$?6Knbp^d%hJe)NKsWoRKqMfA zEKcjJNOQKc-uv51Z^BuMIDP+!)G`|d*f=%1-Fn9mmWcJqYz6zBj^S839ps{k#JL?A zuOHeIlPx2;x~oHUGJ?4i8S;rJOUk?!b$P9G^01N(Ig{D$foN!}-E)q>_e1nZdO(jR zXNOnlnNVypm#W5s;azTFblcV}zf!jBu&cCFbS4?e!->xs9T#TWDDB;eI&nO-%1NibJ2 z3hgCBP+eRKim%y)g}hkk+IX;ig#$Ml!gf|9D!N%SfTGB?ACwh_re1n994;3}_LpmR zB##S-C*y%+Isgy~=rW3gJOwRMMD^$Gr=Ev5V6<(?@8tJ;ew6msCUAofp}N8WL4C2&K^v(MOA~jS`QNR^Oi@M5gXzJ*RAAfPW@la<(Ghp% zBjNUI)LfHs+4hb`(Czz@rN{yFz~&tzQo6S^6bybK+1Y)nt=Elc@!Vxh8r&Jr3CIPI zXo;a?n0QShP}DM+OL(dOM%3!r5mS?%(W7X{6V-x$7LujBEE{O!3~&HFCrg@(q?ci{ zkmu#DY&gkmu@pg|6Lzqw*`jDlXcfmxc{_xX-2jV*PX291M;@Aj_(0S~2IOA@LNprH zp(8_6j%eN$(M&)_S8WA;4cm>H{3yzNMc(BMqdi_%a?^W5p$L*)q7gK749w<+-I+2U zkLn>l=n5U8oaQ5Fk?j_{2mT&C@X%cgD!UNPa?;fu3A?T#-A&YGbn}+#7K}q>p}c3y zNbaVPzB*gA960(?oau_SPn14eihOYJ+>U{AS8q1iCo0iI{sN|<`FSdY(#Pb8#;-2) zM6%f}d@ep7=`2-qgJPEpVB9Et@qlOJv^3^vODvfyXX;hf5p*Je8kSJ2-NC}qA@my3 z;UEwIriju(l zQKJkGf#kp@3VO#_OlS`Y9fqXarYcF9unvqx&4tT|kA$GF+;-|O`8>ZGeRFHj{N@lB z+S#@jnA0~Szw0>S?q6^FcH0|m|H9y)N6XC=vWDP0Mn(dW69|-=IK!^SVu#Qp;X~#g z5nN-LH58Ca>JuKB6D>maO+Any?FVZ1i4^pwVP%{#k8(=lK z2BOeLaSS}d4?W3QRNTY$=QL!;rd%Bw0=&aNwTN@U4d!2Qp)UIDVvLhP^2B?HP$?`! z$(T4w#AOiK(>41Z(n5p%_roiL5e;Ctpsqd%8k8$;{@AL4jsk2s7;@%Bao@4BldSb- znoJ(vUNKB`4i$MNjHrCX7Oyjmd?3>~ud2;2rRwMl7SGMeO5?G1)v}Pao7JP)<6*Ao zwp$}LQ5|>|eMT)iB8&e6aWB(FSo{uI;iJJIry|>!A!5ioVPu~mBSZz(Mnu(+KpUD2 z*ojOWrmBXO-Wdw=8deg=V(crI}Ab<+(aTrM&-5^>nSx*=4|!`&GQhgFUx z4DY6ZjSyL|47q|#1eIrI7e6ALZ}nzJ66dJ>UrJs#p@f2}!RK_LA`fL(4gOYpGT4;= zbW6D-%jde5IDSQ@FwAioGn-Tp0!XTS#FAhzE9QL6O%`)21#U%0`+#N@id|#KEVDV= zjMz?m92X$Xw@0OPGP|>eJvO<#34kt-j71r}$9uW(;5vnZ)QQ^;pyJDOyVM!;$ z8NqhAAd0=gF!IPii^5orv~zJ22#`_Xj{vWPbZ%MI;5qDMS`S!QB{wUImuwW>9?gxjR zXbu}%xR4}<$S1X&;yZxlMM=Kw*<)AV!|r3-Ty0`oy*Av~f+it5lgV<~b&}@He650> zA=`#0rQNAyqh?v$q*rGzY~1KQop7~8vd}#?RVX0{o=-ZKm3D2zNpu&cX0oMnW47u7 zfCg8%?$lTLJ^UDOX2xLkUx`f3d)vO;_B2s1Ijajk36kt=(Hcm&IY2TIS|mktAx1?q z{+j5#4C;n>q?c$8WP$gPjrePj7iWw?@`7A*5`~0)+3&zFOe!#aLI;LHxFpUY`hB{V zB@7j=;RHm2*wq(`pttv43eJs#zyoL)gp@frgBslBedh|MCELkhd^{A{uIJLKwzbxK z4&N8<2*%eTIie9v_j1S;x#5FqUMXI^7M>oYCQYlI>J%$ydbozTVtq&k{p35E-O-OQ zX+>gS9b2!5m57mEP`I$nNfE85XxfoQ?-PxJRFywC8}E$=HAvJ#xS%FT4z6Z)q%Aa!25kcowMMi3b|c!rPJ)>T<5Yv_X;hjH!bf6GV*4v(m6_gl~MnA zbNE~cJlw{&`HR;cn=})0BqK}@r-rW!*r`txx^*|SsaI5ULT{Ms>6OjVhdHH~QaVe6 zelF#Uu(^g|eYNts+ag`zAj2Ot6p6&GRVLLedxs#pnpyYT3?y0KxFjw#%uCWrk zdCJ=+bbH$;*phGi{TSmV;UjK~h_NX04&d`e{37b~gGN@fYJV%22HBa#!6x>ARml@! z^|8Sk8@J5_0tfh_?16XaMR1THIc%P+ZFCUy7853)l-wc0_5Z2AoA5K*7hm?U!^2ngePnX1Wk`7Gp9j(+nxX&9KTc?6oJ^fs+rh z`=0W)vu9s;@kQWw9b`9qHE##^*c09iyAMI>d!PL7Q>7PQe6flTX1yK!(8)tjct`Nh z4xHsl^I(H0N)ol!R7y%TL*SdnbR1gG2oG+4~E&hb@My|u&)YG+aaI(IAv9Wbw z`cU(N1}_u^0kv6`!s2w^-BjDs*m`&(KXudmjyHENozyB;01XY$)fxAvwT+(o2EQNt zAmSt3&H=&r%C=7;Bl9zDf9mrD8Rz2-4*8IUB7|N8uD@l$3lo;osePJ9t|mEzq?}Lq zM6lola1D|OCmsmJnxrj#lT}K@-NZ&n>y{)%JQbtX8Z93pKF6sL1FK{7*4co2)p{dB z#{f4XzCj*f42T`M20IbUarM&n9;Stjfoy($10ea(AJ#1$%S;r0|qLVn11S1n=leLzWmr1R#i%ga}ul?v=UAva37vAa`_;3 zHsn!p1URFRys)9KhvQau%ub}am|+NkZEFf+2n$i-xGCm8u=iS|!kEF%mAZPKdC{K7 z9%Cb>lZw0Hwd_OV=O-<<6BR<)T+vAh=KUh%Xz=#ar>p4(Sd%uu@nte{;8Zbw@(Z`pq)LZ-9<|A}?+F&1HnI`8xT(wo`uvig30=mrnMX)pVV;62M8 z!EqwIU)S@W;ywk>N~*0N9bgx5bd#Pwg_`@2G}OmLB<@orn)-Y4aue@D2x$7U(ib0H znHpqF2%lrckmV#7N3i<%0qluFSXyt~H)9vhQ;hGz3uDeSzPv-hyFblI{p#I!>%bE? zzkE?Zcc#~`pLQI^&98+P5yf~VOk$6y9zCi6>r4*{gTcFx!iz>R1KU}<>297Olkk8u zzX_>G(!#vsOwUYDSCIH$0g8|*7{?Z{VCZy?Ge4QoGO85=y{gABh1C&Q~>CbyDE2~nnA8ce(Qy-?ISllsBxQwHNhd^dfAy(XdY^b zksgg{VgRbJ3PVj0%XLFV;*kK-3c%`NNRLMH7@{+(YGpF6+Lcf*PfrEM!}v@P!tK1m zvY+)%g~K;qzn>*Puyvwm#ft|~;Kk>o<4BuW1mrMO6h2oN<4I>}U|M`8 zhQu(aL^NG+p^-a}_!@D<5LQvKAQcT167-#hb?WlhaC1y-CShI}0{2e=Jy9MUKd*am zW~kF*Qhv=jBWi+0bxRTYL_CJjWibITOd22P(3Q@Rm|s;c7!hO`ur7B#VNbb6E)|Do z$BQE>@cHH?A5Vz)@18}08qhUVe@R&!;>t^_sHeg!onVf7Fj zZz6gCOkxTl(frXWBX=eQiRdpiOeK9o`QxtUkq$UNk%8^9@Nd!N)~J0+`_PAT;yS6b zsw4|)WQ>cePAD4a++}3YpF4Ti){Na)&k`MQr_5riiGu8IMxg|xd$Kcj(B%H zaNGFAZBTS}vU}L83nzatGt0T)Y<}$Ib=U@FXc8K{{ul~Sa1l7d<>o*!HVBo55`(ci zaA(VgLiAlDfwH!it%N3u5=rMClyk*se(&hsvD6Le@>2QXi9$NB>8V61?PT-A_3CIk z1-MwtO~4Q?n&&QzgIuKXwt2r+4snts7xm?Mu?`(g0w)<9 z($4YI1s0BPJUv0q617+)re%LC$$bAoE^mVNj|H&2g*DiliX|1S^c{uSSs`g4@(KG) zN#C5d=Ot|us~E<1gAE-UC{K7WazRF#YJ~CznGCi=4+&xb zLUL(W&$yv>&5A56fo1aO5x}HXHEUvJDj*?eKvtXXCF@1F9}#2*`njm6EXyv~U|wp< zcBL#Qj9@4LOF19}2}Tb-d^jS^PCgt&ENV?)$6%zy*^Gryn$oALr5LJIg&N^0B8@;X zP_@jfK{c}$SrVBWbD@i(k?v&891+Z%TZ9WyccURhG~GmHVr&xCTpB6~5{9c)XG{7} zK|vV6u*)`{e*#rvrisLUR2`W5eGfKXK#8_uCOHSohHAwO^lxQwmB6bGtH7|i0B;nu z@hqUJMsDnZ^oPDhfd&&_}>Yz0zW?0+BboSV>b?_cvLQ%rygve zeN>A8BtVW0$ncO7S|q}d!tyKk{mGp>C)3{RAOCoLD$DZaUH4~?RI5j__isBsTrLlH zb8{PqhBgkf?2g-ZpwE)BX{gc7c26H61?8tF%CyhYFzwRvSKJMHz82Sv`dj4ulfPo1 z3&3x%`F$2{TSZ(x;lxSLetz>!F0jRkn%Fh=1@gW2& z_yO$%t`ElOAggit<6IXK%3$C@RN!Y|Okl6pZ=30ESc#OA7+TtPYxmK^dLpgl(^3s} z9#7t+OA9wG>fnqeYp6Sa@D5u>SQ9=W<$g+iA|@A?i#W#2eqn# z;0P(QP|dIx@llX7nMZ@zHUQ$LS>ukp^H|+`atL0k*_2tUcW<(7@B5M|=-vqkX779V zR+uA93`?lTF?Abtke~oAo6U2AIh?f=M9O$nj3q2d(ou!Vm4J&nQ^=-tLt~pey&pQ? z?D2jG!NgWi4TUo8n^T>+(8^Hoav>~cKhiv3|K+#zObo58p&s+?cI2+Rj!MCLdb&e^ zGeJ?amMqJFdtl(q##sHFn5|)6SX=ni-}CqLWy&}0Y7DgD2I=l(sSRALkMR)ii0=R% zvEGca9h3)iMI_e|s`jML^6=(EfW<{ADbT8$Zp4`blM0z-nbs zUw^A)EJ;b}) z{UTj35_L~X7Zi9h-b4HKwl$Cm&>`^D{RbPP+E`-n{&yB-iMXm3y?_U-B0=Dj3?4sp zA}kxI44@O()6ua183_$qIWz?QDGY||s84))STE~arbg4_1UA7Xs+IX|GlDIQYk6h+ z#AKr`k^4h%YL$h#O_J;0^|Bzmdi2<90=M(gB?%Q#!o~`YLx!)VWR)$ZmW4e7Xw@NX zbZX`L{2swk#+8Dyb&3XqkQQA39GqfEbnbiTg-0e(p;-RxnM)DE#n6GXxf+Js+9 zRcoTI7S);LCLt}VwWf$w_)?XU3p*J&E$T!um+aNYSsSX+%C#+pP519@3?pZ;a?{Z0 zS8vD~OZV>^9hIgnV`yUhtGgffq~xv#O(zY{kzkhKlPlNj_m-1JIa$t10O=Sx`DLj~ zo5+00Q*4|-qjh%BCOn@VvES>Wl7EBfus2e1C% zgGbSc(TH?teKXL9^KHk#FL#3`P*+bud0LqGfrda2l-~MFfbPhN_^NTCh)-xccX@hL z(vo|UxCPTT%8GGA^Q)hi0>Xzu&B)Ud3z?`r21UTKeM z);VE7Myysf>7ff$wfrdD-@7k)!dz`09M<9MTWu1w&$2DF2l`AF&xP`zuy$MAx#hzy zvZ7pU7Ei0OP(X;5;MXQjPLB=h#_ZJURo#t{Q}3TD_PDA3F7LY;`2Qj--J|pI-1NG| z3x4At zIZ)}F9)^!30)>%i!Dlf(L11l(NYO!_9*PX02)yS#yK8o6w6Gfq21&=rrm}Ex8P)o1 z0q=H~$A@O8rzYyzQZ|)B)S=EEnBTf}erd^jb8>EOa^ptcNTv%1(g>KMonX=}mupwo zhEec#pq5R#PCdJQV#l`W=~^Z2{&f5N&Ykmf^OK81_tE7! z`XzWv5RuID+AcUfM-{%9CAH^2HA~bE=vP%gtAVS8(w|mIPz;~3BQwKe`P~xi4uxhc zpUm2b)cP})b}(bZGnlbjx|?RKGSy%=4cj$q)E=MH#^$WiIIG#i_VBbeK2z4J8m}kQ zc_Wx_s@wj_u!%*U`B-(89+s zj{}%T_?C#P&_g#HTu6r(W29{ZIbeIjbg`!z&wWt{KPIHLHgP z$8_|++kXSQ>nEm_I^JztGc(@LXJ%~+Dy)ii_v>+8P*6y9D-p^9#jp4qUQx92&e6j^ zU;6pU$Itk~*WbCHdvpw6N9fO;bTLQhkB_%?>R<63qo@*1`%$dz)E@|57g##DO;?C`BS zci(>gC@&0-@5&c;H*UN6#%-gQKgixLOqGl&qvU-h3sOZM4QvrX$P#c-%`_$DiwI0; zsE&dKq5}xZp2#Q65EukRum;&@J(H;Mrud-)mz+0r?cQ}4^_l4dt5>RPEvuTYT#P~ z9d6L>8#enN&n4H#hyKy~Xz}-pUyc50KZJxm_CL3H_iW~5E%lKU`?h!V*uV<|@zweA z_Uki=&6Xv^hTdRSWX|Tyanm*4dE&Os?DEate!0Ihm^wiAazC*2Kkmzy2G!UC^r2-7 zK$XE&wjNK*Tp)&>MTHlbI%K2I#$YlqaC&URBNc!nK}G~AO6-5U_{m^eu2XF3tjYquBIrkr_pV_4o`{ppV%I5(aNZTsfOfBLwo%=CDF>-~eX!Cv~tCqMBl z`#}e%L5YYxVy6T&Da)#+6ng|c24sTf=k-5U!U-PcpeqAz`jJdjefbeNf{c=KKcA43 z|4+t7kVSa6#_CrwGNi(yrt96`wS4)VSJ<=OFuih1Cm&?PyV>sDY|rkKm+!_S#Qb!k zC*K|1ccCGy_HBt}%mT1=QW0c|tfByyRE3snm(=Jc=EVQ*0;#t$_^=V{*-yB_)Zy(a zCUmuwD+x3u@bF1=@FI85(f@L8;<9!P|H)RcBT&wV5-ZnRtD2$8nqtrI3>3V@azT{y zqMeE7WwFpe$?_HRl>{7Yo7tA>L!&^HXFrEc#AyXBnZi2f3X&nAmcQUCuHuw|<`3DK zu4rXL&)eA<3w}FE7%pp?ku47kQsLy!pi){(jRqYnEi7n9>M1xCsqbdK?Zc=e`F+ev zE9N-Zwz6#%^y+i4ayFqGFtqDdVP+Gr^xM^-d1z8VnpVi^Z<6kV^#SskK+Ka6^!Gk} z6unBVT;Rbv`D!Q|rAevq?7yCS?w4j~FFyC&#dFVPGe#%|*I6VL3d#rvTLZB`d+nn~ zzwpU%ct<9?69E*nswbbCon1VA$kOcW!>(oNojVLO)UhDjUfM&%HcC1?(L%oW$R z3Nqk2(3OUKK0;%XRxnh&!d@^l0Qs&p`0IY>cJHU&kJ!qef8yr9_rAC1lFuAvcf4`+ zKK|-`yIx_blTY1=aah~TsXqs1$2!WDZXIvMrg_bp zaKg$`0#-k+{mZAmi4{2teTM7+iu?KcFq(Q1Pe9gPfh?X_N+3V6??Wc7nA&aNfx^Q@ z90Uv;*9MqHydhrUaF@zuD`!Q7NF{k-dgkz^gqi}}94A+z-JRXhORrcnFG?f( z@0t3uKT78+`W4%x+=|p}CJ+wEVFo^7#>-+L*=epZBj|u&&#uS}nMm9+?XnWfaA6N^ zj~QpS3t6>mg4`sb0 zdlweU6Rd5b_F_$X=|%UsA&Gt8`vsud_WMC0s>2Fl%zuZMB7jjLrz3m@PC0^_??TAE zIQVn+@-xqPl~>qTymtU=BYD60;ujy~e(oLi4zqo%@_={ZuDj5uVApfow?BvH^crH; zC-9tHc(_~s{1{kDj^@9W{7(;r{QRvS-)hB!!jI3d@Q1ygx*=6bEl#M=(zQ zf(&vD(X9&xH#BX{S)~U`Fy@k#C52WnYx_OWMGV(Wv3$Q9ga5~gA>SOSM=OCQhTx5| zryI;_ZkO(G3*PthuHyY*566!hZ)`J+`*vxP=^2kGg76S`9K=OFpV#<1Q7ay7djfsP zUugSBjE&G4{IIYxD0!49gcczn0kCA7_!pK3@=+Ub0<>WN$g|@!bg*k=%_yQIcx_OQDtUk93IqYhr%&)d$!?+;D)7WP0M!7Jp^!Na6~GJ$GIOvH zbNWFRPyi|d(%>)>xuY61GTviA|B2xoATPgA6_4uGgWGS@H+^o6+8zoHKkzTBi}gOM zJAkw?9zZK5KZ}HB)C@D3>NnLmESUJ5*&XU$5-OIQ+=#a2pnG+4$Jzz-O90XlRsD!U zyXIb8$D&y|0=a>7Q9IFT+8f%7Fn$ejT^i9@#p#ejftZp+;-;c>oFk+yWGhpwMp7ki z1sa?4C}~4Q1DrlehavX%Xnlqp=?Is+5Z5k@sDLc>-vsd5O+9j#T>FJRc=ePwptL9=DElulV!2Eqp#4_p~tej z)6HhS$L33VVb+!56opb?&cw9GHt#v{-nCa^XK5C(%g z!GW9uG7TxkaI(S!?fYM&nL%vwQEU9IEdvsJlKvc#2Z|!};rZn8o27e^VPn2(q2NT2 z()V$W2E13`=etA3OCo{Jp5hfsdezVBJ6# z#X{bvJ0dpB0}@&Mpd27Nu%QV&u_n!S{st;h&=}@0~xpGF% zedw;CWX1c(NACKj9Q@WmBN%pyYh5F&3cM68#JV?%ily}L=meiKo53J2n_48Q$5Dv8 z^Fyj&$Fi{?5W_kJyO6p_&HqJa_==K|yZ@CBf9{D)>W#bq`OdV>YuW{Z)LdaJ$WAU! zbP73(=?N6ZDHo3^l?sqhP_`=>ybzBIa2=?9gNj&6ENocga3bd59Pqy@!T-RxtXd2r z0Nuu^Z;;BsVIr?M!Xhgy!dO6Nx(~5QpPv>m z-UIw5(oLz++-j}{un027NNGRqx1^X9EH8jGLXm=u27{r_bQJUgHtMpg1t4=WgU*dd z009Cn1&|m^_^~D|0@bFVX*TSstm9-Av!n@gz<^4nfR$K9y2U0*sb`G|2eER;1pp4) zS1x<^z$b4hD0tNhX~X*|=dy!_ZaC7LG@w)i!q{ubi7nYqGGSHHlJWW+^i^1HwUmp$ z^MaPGE7G`)P?D3tnBHN3Ok1La4=tZYBP}USyua=5fDhD-TFRB6t#kdnmIh`3K1tjq z@#hBc$NklUcP-|9T4ljJP+nzrc{da-APg>}(!Ngda9BGz2_nLOi|7FRMy*f}s4Ic7 zfqqW8j>*tWrU#k!y9p+2OKoV8iI=V&x+3K6Xf);++sm#s?Gk9{Vl_}XdFk)yqV~Hw z8$1qNtlSOxAiHX90C3A|gFvj{I(niUvVJUkDIRfBV_@WLg}4I6W1E_5Kdk8^joPrt z&o|azy-77M>F6kyvtx+oTBYty6KykXn~_DaukDC0 z6G2xs2qKCPgKC!F0nrnBB}fs#i{$SC+v&%upxr=ZAixGk+BiaD`Q_Il(24r}jzaz9 z7)Ct>khOewW3JlGoL2WDAH-2a7zJ25NFW@D!Pkd6Z_dWbgq8v+$Zm9;6mlI>jx$;1 zq;AeNleKCp<#KlbOY#7KiH@TB`XE$_J6t$!YDonD@&Z>;rfM~~y|(%7rySFCOz+-9z-&7Tcq5I*;*P@UcjK!Ma}U4DrwbK9s1!`MUQTcpd_479 zCG`qwz4b8#pk2etn9dnLN>*$21U?nqa^3Zw!a1S!;HNfa3MsTt24_VCB5gZ3y_*I^ z5@Ig&=SdwGz^pKm^n``^ zt9MYNp}b-8@F42N{sLBNh#SCS#8aZxoZ|68%Q-J$6LfCm(*sDNKh`MlTf=cBHoJO( z3yuUhabU70a)D92tI<1sRe+1nZfb;9CgQBGO8_p5Swcn(gO@^GsRgXb8$Yml-72hs zN+yA1qOhiwRNJicU-S`kgMWoZZu4uGCMYMp?>m?r_9l^llbheeprTfn_QAVnt>jExn+ z*!IrRB~7R-Ax2Yz*I3}BYRv%J0QMP&5iZ;&po9i6c+PZV)V7nGwv3iGmXgDbMfj#@ z4E!i;6pi+fHc?5{fVK@o(kvH}HZCAF4fRA%#C6%YpI1)*QvW_X)Gn@t{YuG}G1{JPPiwd@D@gluG?!m+?I&zL%?WBi*MH&Xb40&F%7=8lcZA7IIdV+z9e={iXD%EsXY%F~-#o@~ z!hL59d>)(Xr#wr^PaXmbToqkMIAcn9EU>1MUWt5bS)8``hZaM443Fm)a=*T$_jG;Z zA1IvtB^EwaT+^rYpWgBg{iom4=6-7PX-{uF-1`duF|cY6AARu!J@kTpx_98G`uC6x zcYx2@NY2k5B+TVYiT(Xj8*k0Xg0BIrKJsBA4{84xW%s&aLQExPHTo2}q)kIN)n~H8~ zs{w%&O09^O{*F0u1W6+-=)!@Hr(D4oSXs|bmNfZaw5-gkyVf50&T6~8+Ep^@?zM;4 zms77SYx!p`&g`6-+4@-0rJ5jDa4eyXkugXp{0HV^kLf=!4;?appg;B)tA`o~diVCep+tN4!nS@KcJ?1C z$Js?ce3&=cd40)Kjvws(NO2GLjtv=dY zefnwTb5HlaaN@+XCr&uZS>FjU*6O=DiV5~o39xT#`W>Spz^_F^6o!A za7rHz#SHgT_q)S}Grm0QOgN!Q^?rTrA<7YEW5LuAeY!Y2w#m!=-s19h<4sY^&IWpW zi*GyAxpASXIFZcjW{c5qq)@wQIP$MX6Pe*y%<$gs3_A-mvv^zGZR>6NxAdn``cs73 z4J&Zj`*9cMa3LA>bs=A|WcXPgS{EQozgjaII5WCz=nDR*+TI8bow1uvG4o^Y2whaV zn2dRxhP}iPlwJIIje0Da%DC}VruQh5K*O;iFz-4T3yyizC8W8oy`g(B zuTovfGu58}7%k!ib{eWyc=2)d!3-2)skjT{dgZ=y;ar|sdX+huH)5WO^~Li2Wk)@r z&hK)Z;sL^7P(t>;hOw=h@vj|qRO#2k=iSG52n$%7nyOf~{cw)wGf>dRejt}ws2}f4 z7fpitl0_^i3c+B>)RT@0gd9TGQH)`bO3=6>XFs*nXgqjufmxUNc>jqHFVF5dxpOvZ z7tS1e^g~a7TWLM5KKW2?yk5vR>L(CTC@@a8_hK!ZZ4~d+x}n-#M~;M?W~kzv-+#*0 z^NpMLoLSB`?m4ov`Bd-XQ%~K0|C2|?bNL2VOylw1cR8N8Q8D{?9!U-|pyim8UKoDdd}r*(Rd?{+BTP9=-3WC+~mY z$uh;57oGiIxbK*LY{%*`a2>1hrSWEV-?2w!FXfmru>4f-TTg!bf!#aIrEw=e_r>F@ zJN%vf@!NtA<#y($UmAED<5wmJadRR0MYEwl0oh1FScVG3OwDjprGA{u>*$8U--J~y zw1d9(!&{pc%R@TQ_dRqtf%M7sW|Uz{sG?NizxXDmTiSWunXi;00G7kX;PuCyD*lq_ zj;WPwXtXU(2gNqEK(zhVr5nr`zF!Y2G8V)B`5}FfR$p@2UqnaLzbR&tY6NCB9H;?D@tzE7jhC$yl zwHP@b77zZj%=<6#T;W?+pq3%lCBJ^Wewv3SZkkSPO6?I7(c{hWx#rIO3(Kv;x$#DO zsaE+Eh_AAj&xYn(%}hL7m_Jv}q#BI_DoTYr@MT}8SVTX&l^mbnq7wNZ!dCa#z!P{X z|A86L^*QU1nas}O`9ox+uvBIKmd5ps1adpYF1IkvCECQKuTIlcr16SiNmn>hoR1QJ z^_TLBkjRM@i@51daTq7|EntqeD&l#u1lS}w@PK%@MLG?9P_21%3Z?q;dbcuy@A8sz z%^A1|tj9{%N`V5Z5DJj<(l?0tk_+q-p5RPV@DmAsoL)oDv8!`oJ^?phMmT^cD@X7( z-EoC+Bryzh@{01rfi2qx$s~evjQ*^d)se_(*qBbnH^GGlEn7&56yk|15@aDH1Km+$ zkSuC1&>Df~$qFS6Wuv}yi<1ry>BD&|NOD3D)~Gxs94N;URD)#WPK1Uh409wws#b9L zn%stxt&XGufkeQrpA8#oON+SCgz`HOc|$M{os%uVkk4W%5sfp*|>RS8XL;_94ek??N zT*G$A42~(ufRPOTxHBFe+49jm8Vc9E+|i&xFmI|k z$R$NRBNs_h;v!)w6e6D@d>Bo}uUmq_Jz!FvAUorFhmu~`p2zoVqGGp(FT+0@j^W!} zgl86?sT!ju-na1%ueu3W>N;P9_M&9Nm;_Bgu2YvVOh^G>JEWh-( z+WVM$|9as0fqz0~>f~eyr#(JJIy5dU-#!$#PBO2Hr%+~_ey5viO^M&V@Bd&2zn@R0 zL|#gfWxvvLsoIqxz&zDyPr_zKt*THWWC<8UWlyi}5{1P|jJ%p^S>*is6GE25tuJ~vKP2^@*mS8Dq>U#T&^_Gd* zJUgeu^-jQ;6c^qp(v^?zSM3!}0!z9~%b2bq6Eilhd+MGhqfzZ*I^Ss8S%BTZzcbNP z9scQzQ#4cKshJPVW{+gPc3-w-+G~eGO)xxncHoT2ih?ziJAmc&ymh)ktiav`HA%`=5)-M5PK8H(R z7UF)Nxq^eO;uPaVt`U7v6y^>F1O9A3F49W1`nogoS^uR@!CZZNuoj%Ohfn4bQ-(ul zm4XOrAZEm`3WPR|lmhv{SX4L0Jh&FX@WC%YS|KmJ_s5Ya3P3S6QWE=?AW&rPvMX|K z%5XOZV!=qNQPYee!eo{1%uX%*3~3pGuu;lZ(?+lwH1y)N0p0M-OwWpQs zI)fJ9Q!~^|omh<9S$BT90WO|U7XDA^8s4f0(^}y)siXuMQe~6laYd%$_PA*-7Ylik zf>ru!JlZf#taB1|IHT>X&de$oIYFpU--TZlWhQFwUx?PB9t!2h@mFzfe)POYe3i9O z*d@Qx zliwJw5^)fVt}jkaL4?r@r$zOLJ{M1vC*7A?(fFn515zdgY-s-=Z==$zke3dK4Agk? z)xR*4PK^q<)GUqqy_b{9`Y1kaF{MWPUg^@P=_RGe6+t&~wxT0*L8UHxa4}q6SH1~| zw@NgH091?%>R(hchQ9^qMzTA_1%eUp#9DKT@_j@cKW-MuAmX!0`!sdf27n?mZUvL3 z!b_ULk8vYD!v{^Z{XMt$eRh`PKxHV(DhKw?`VSv_NBNB1W3p}1O6nelzO!~K&SMd9 z&>=X7Ze-yIADMgjLRyMABZs>_WVM{)Fp5@5kM8w8C#OeFDkL8|PqQFB=i_JfroAY8 zAeiYYebP-ZrKA-i^Kt60y}T?HBwo024zc^TTY zJIl;$<06LOh~K^DzEgAm;LUG%`#HQrJ38arl*f9G@{`~#iTIn}8H?O~;2q|#YF0pP zO&i9_IBN>fvu{x!y&kReSLTPA-PwQ1xZ`Yat#WSK}n*2HlFV!X5%|fWTd8mfig90 zB!}NW4R10uJMH%<7xX{VovQuO>`3nqzhfIk*XaHEWEtJ^H$L+EH{BPo-n6p8Y9RPm z-efsvua6&i@Rxu2s;f@^sTU=$JU-H~(oSr%1Zp}u_hJ`}*!LJ9AMo-FVB+Gj3+0UUb&+eQTG~ zUXSoJy7V5b?zHruG(rrWl%x)8T(1i8n0$imsT0M)@CIf*o#p{3&lC!^V+)yR-2;^F zx;c`2zwnLEm!tWNi(i$}gN5SkZsn;xt1k__u(WG&)g6zE;Bq>ZQor@@4=gOd{!eyQ0XL|7_c;+dlreA({)7Yg zoP`%)6R6(ZTsc-_43&5kWEQC|X)H;mT*%7PVPDo>$)e$jgh$?TcMA2Pu=K%vi%I z+wo<@ck$sB@yfz?X=&5sn4{;UwQ2%l$QD|M`lAm$@z7o~TNzI^_C|L+^u$BEoLqZ# z*9uW@b;sJ%3u+;kn_p<$IiFhl4l9}b{QjAly$c0~2cE(5fDTW70QRoyVeVpV(C<@G zzy7b^@6A%B&r(@M+TjRG`BuGeVpp-G5q>C1GCW+hYNB7MtWX~fnewvpm02bdTwcnHp`=O(TOj{r0_J{uV9><)%EjN}Y85`~sL+BuxF3nQ6yG9D{$ zwK8e2%L|#U?MN#TO$U>1WD8k(LkW{q3Lby3jk&7MuNwJaP>aWp)ZjHNDmIp5+R~zu zx_RYbYERru#|EkK_*Jeo zoXF}%8;U49w5bvgY#8#f@!#UPcj?oE^y(7c$p-lAHhx!%XFz!A}t z5bmh4m){gwd)1$onOfGDr`Fy(b@}B}Z|kmHd348)|8DgDqcU^pO?@|OQ?LAK-Idnb zJ?-V?wt91G<;5NmgdIDM?oj6T>^aM8=g!`T_Ut*md-vJhyHD-e@XK2#L&DezkUZ$G4ogb{qH|0|@w{;wpr`@fx6u*m)yL>POfhM6hn z^Vcks+b;ROt`3F)h1o1Q4VsSC>BmjQ`}Ngb9?S?*Srg#A8;$QGj|}QO*b1W2P&BT7 zL$>2p-bt#8zuiVL^+K{LrNGuss{gj*dLs^(TkVI2^^5F$Ap+Fd|MFZqa^>}4ndC#LzrE(*M*NPQ*bKkxTUL&6 zHTmfLX^dvdud*X5=-rfV$?-+#oS{rj`F^dI$9{2(8JE;xk*lhYp}HBp!=3z$5%EKQ*9?3o(yY4MyHmSIdo5z!l;qeWf z+=$(FQr8X+*`u*Iw&94TWmmx!WVN_u6GkOpEucp(i=Z!WZ||*0Jku#f)QD%tzcC&j zT@1I$A6Ch>k7gk=RPE6CJv-Z&T9h+K^Uk{Mxh2OR@F@{4I_JC*UpL$+583Ho8?1C`Ssp1pPa4nJNh zv7LVW!-Ah`Ob~mpDjPL>(9`evX9n zZyqf&Nty>=0dXzIeevO^Ju+GMM%#t>?7Xfo%~clm*sWS+K{=oE_Sp8W8eSPl2ttc= zUb>ThaM!#!i_E7xxq2LUcbonRRHCNM#9w@j6p=?vy$27)!(srr`{!p?NQkx7cxx^m zHIJK_r5)vv3AZ198R|IzD87gAdNO%nj1LVOdI?_Gb0&~dBz{0?i zU#FeEM7FZbxkP-5RhFm=6yvWyqf3}Gjo)unBJ~8h5CuPjpi;=oy0q=zj<32(BXaPj z@{n0vZj6g4wdr$Q@mzEplvT{CP^}Zcix@yPN0rf+38#m5vT>B0GpYquc ztS^x!Jl)SPnQQ|FH8;8@YkS2f=NT4A{a$KWqEF=SMD%-(PrqQFMkB|}Wk4YgC zRZKsoDid{i{V9&M$Q5ntyiXQ8iryF*%#JR_(_4=pI) z%BJ$Ek!~PZ*tR<70${ijt?>&%WBSUBlXmB>*j5Mzy3@5>u9oYastpg~wdIMn-P z=p3}UJe}mA4OTjSz|H&9S6)4^_}Gq*+`Kxz`{?cSUs*g@sP~>%>yE;;4;C7b?TJ|0#80 z?Mqaa!p!~V7cM^2yKt`eX!cxR{$0FJB^>c zJ1wN>De7XQ#tK2~v}A}04LU7OlGA(dIrQm6?+EIxp++`ZC#!w9oSCb~dfmxu8p_PR zy;nwKdpnamtNP5oYp;k#cOH^*mQCf@(qyw#Y`(RG3or3!h0N^OxV#oNq#}*eTi);n z=(_W8jmH~hv76CsGCOzP*qk|d^4O{F;p2CIX~!pYCG^U1ABl?Q^E+sg)!HyIfnazZkLNK01w5SriB1l1~ zc*fjtRmRQ*!+}I@c>Af67hlR8oJv%a;}G)o{ze&6qU}iOw-mWo8D@gLRpm zjpsvySCrGqp;J@AjXN^RU}9|JpgUQnvd{X`cA{*dr~;?;j=I^%+PQdss0P^}dHX(A zqCcN%jCIKeMXJWA%^;OtOVi97r4D80I#V6m1U%t?twf6O#i5}V*+}0?!Lz5xL4HVP>j46qDv=fY!oFYu+r}1y1 z{W@+ww31uc=biG-FQ{Mf4w&!+I%;F#+~G3HMehZQch!;P?n-(847FWJl=O1*W+v)dppVPwLz!al)pj;@o%To+fVjMDc0T*dkL3!IK!yH5drm&+C? ziB+kDuiC#KNQD=?@Tnhta_1ep&M5U$A3uHM@gu#5#Xxf(xfdkvSCsGLo%M3{TzMuN zS3bH2!+|U;u6CHVd%w0F$oglLzgGSUtX@#{)SOyXC2Khe@el1IXap;n7Dv3#n?m45 zbZYEr+3txbqG4%fOcz<|n?#qgGbnK92RxaWidaD#B}#=oBS&D_$bVCepAygkarzM( z@(ag%$Rrk}94@`#Rm*1hg@^ylpn1T~b0HEB( z6tOVbsY@5fX!cJ=-7NXU@8l!?PST+nAOehMiJs1jgcmV7k_JPE4kEpuJLW*FOV;`b z6fP3Io9mHFk{h58RVCQQ#P7%4OwmQzD0fEZk{Aks60W677K2;{jgyiCrA7JrHla6K z%KtJ6x`t)r>Y04 z6vl&_!^)zXXc~1lbk%~8)r~|AowQpE*Mg2Z0uhfMwFpNNbV<;XCz>G4NEyvY8VolGX}|Ps}d`uh#*_CezYb4IKi4A z{Y?u{pSOQm_8>+EWbN=>O1(E?dti%5Nz(&dS=ffH+z1;now5+*$lD(E{@C3PWMjR8 zaz#42A(0BZ$$%WEL@hNOAle5-yFS8wW2Jdk%pJ*Z-*04NJe#Va)s{K|uH$b z+|ivo0X4r!z_a3lo8ctO#r&MNi3Ka&HhSdFtKkv2*%f{N9Y@XJg0p-WqP} zsHZ0Vf6?A4S&M!NXN^P@l~Fw8)EQO3o%Nd`U~xIz9haAp2TIfM+&qIahx`EMa3jI6 z4sJ@CLTX0nF?<5tF!q|F40H4TF|@5`m_a>YB`rw5DQBox!$!!qkPFfhrbZW`UBf2z z^LXmg9kP0$VgQY&IjJ%Y&9po!X$C2VNIUrbgX`)ygWo@B2mhK>JFMB#3|tkRB7o`5 zg^+S1Fq;_mH@(BZ2x)W&1ertsAg)^_`vd3>1?w^BHJ2_)jXC<8EnXJtBHQ>lrSH;1 z=)7h`!)HSW!2=EF>>Y+Xn964dlVDtkN4pMb*zgbAQe|$27GW)Z{pd;wgd;O*5y&C!7P^= z=&BMXLKX^I6DNv+gn3nYqe)|rtI?qD;PSAJY%|p_gs_G+dS_mr2paXt@yxcJlhpZB z^MmO2RMaDiNv%tQoN6XrH#Sg9usmc;8e2EoG&q*cu)Xa7q+`lJ@TD(eyIzgYLbC3O z5qndk+(X%?%ms&r=NGXB^H{R~Sy#I|u=eH6^J?<=cOLG2cX;%R58b6iRsz}HbLo6j zxhdQ$Z{4c=k*B;q*xKCudNy(K6Q5Q}8_PGH)${3Qq1bN9+^-G%2jYf*@9RG;ve1|{ zv2OWZ|F?tm?JMBPm(fW)Wj6N}A5h@_A}o^KX@9Eo4=bwAOK0(l*%N#3*$WTGvE9+5 z%^iiAkYh9L>pLr%gPGLsMN;hQAFrR@--WtCcXBu9<2xJkGuc^cIH={B+TAC1x!T-C zgcNvS*bf~0n;t z+pYhJXWRi7y9HejKQgz0yMyUPCK9idN|I?PFchiSj#0{!z$qtl7X8Q9g8Y;vv+U+R zf3jXF9RC1e?7Xf&j-S{m=YVlIU)A+PXH%6OXA5Qf^j${|-+kf8;S0)RymDJa^u6b1Xde|F*f-->>K&By!-tzw{0D>-5(Xte|&@ud@XW4Q)uF7SZNV z7a0G5$|`*UP(f{DUX)f|mlH}l*WUvTYfcqa1W6(3D8$BFeRYEn6({e1g2tg-$dfD0 zq#lZxS_*;}HC8mX4=ebV1Nyigz`>|TqPf{r(Xdj3={LkrMZ=}qG9e!7Fiq%@q-I;& zbB(xS69u$lX~Rs%$R!Jm%w-x*YACVwM5{KK7*)R!h@kdE%}iw?y`{S`S0CA{6(ULC z2dZvZ^TARJ69H zdGlVVzk9ckvSnS(b2?q0hXnnceJ02BvC#IGASAq_(30_>4SWY#^)2lHanEt;N|I|w z_xFE&9hDaL#SeZ>xIyXdz`eTDXoh_cjfo=7Had7q95%|I?@195tb1N2WKM5<&+S_} zRL-nBW^kym`A|5h1h|4vyH0R4(>W3XC-muzUH)Of+rP8i-hTPSjEoI8(?lj~0VDq#H%=Tn7Ee^|v=z2CpN&_iatrqHt+SQU_{3PK8Tzd! z*ZyzUOGjJ}W$fIaz2y_Pyw$SrzUR>kN6!~hZyhxz1m4>F`}h4Sd1F01G6QX)YU5Y{#AxEW+L(Ex;%Rtc{Yw@OJHx0`Vr2_ zKK~rNU%&|x%5-%M|A9qmfb{g3B!i8mv3ll|tq|Q;@jaJ(xP|ysU-7QW(xt54kF1o{ z;)i=T&g@b@wtRlqOb_B)wRh_&RX?SiR8Okv=^vgr0ahW(gz~C){5!W@@YWtu_bjfQ z)K9N0u6;_~=Uq6yxIgknuh8*$x?^#^DBQ7*Np^uBI84tOa{`-E5uWBFEuBV+3 zBALXVH2Zz?QVxlXFFc+&r{G-zh$ESB-KnC*KL#6<5n~IhjP5`)3-HkI4E|9vBpHoj zHHtl@U9cyP{c!K`jkA|+X-Ai~{nBS2o12)c*Q349RYw2S&yTl3^P5M#OZ(S&E98Ra{NDEdoZzwYN=oGDE3UC`?^; z!>$>#wmC`Ls{_}=2ed=GlCjJ4Nl1uG(DNc|d@zY#S|VYxh21Ju zs)2~j>kkA5NPoNk#IW;y3EzG0**$-$8S!mY<0Z%Qp% zc6l%ib5MLa<~4>xWA$sdkA#4Fn31iKk#{xOS*t)%Fct82Cu8Md3s!7zC(lZ_5iT&t zIZ_Q(#8)Zli!R^wmOF6IOIO~1?;~n)?OAQ^{vBk=9lodcnYCv{MhbrH1;&^~w#ERq zuI~e=`ja0|2X)zB^v!Sj9iNMo@ju~1-^i!K0eM7pG9~iB*`?oc`;lRhPpo6*gVUu! z{03mbcR@@LD(Ua+PZrWFz6#D)T>39x`j~EdX3l|zn_O$5##Y-QSV-%2qOO#ZtYB(@ zBMLQ%W7*U>HxChvOp%aIY?VB0@4zyN7h)dkEf*M}p6k|T?3#1%@Zy5*w%zl4F?HhE zal24wpV{4emra6>@i`L4LUE4QCHe*pGZ=AVktS4yalVj{v{R^?ZqqeeHG=RJfi1R$ z5WEKN8pZ~*f;F=YSYvkQ>>PV%s1KUu3qtPF(WU6FS(8NmG6=?nUC~^eSR5Ry88xNW zAgeV~_VPKfh;BwDqm5)}vm&!*Xhu%%7TJOS@*rqGox0KA^lNPQV_3+|L^@U-<4=iM zG5BO+)!je&i4uSQdF90ydM`fzyf$}N?+16@dUx-+yOo^(_VAr*?+0j*g5$Xtzuqb0 z9oq!s24J<6hJ%u*M5*YiFrlPcqV55W5{}9aYzHd+VWUE&A{tcuFtQam9L}-QD;p&p zQa%xPFRq$yXUZZ4u2WLy?hLiYbr0kj4!?Pl%H!T$p_y6lEO-iMX{T<+<7+=wYCwJQ zzqTIgf*3Q$$((RP995gou*OH3#WJ++y&qL7>Z$JBfg-aS6QDNQ9cyVo&SXTb)nJ+pFfWna-Os(D}s zg%z+ySR%{&Q}K4(TLGI&y&cz#F0qu|vr@nuQNh=A9dGTaIWVaEm(}vz{xijWpYMG? zk1w@QybTa4xcb7ev$>p}&mGTk3WCQvSO@y`LOW&BhiKd4OaBqy=PZ0&gT!_R5(Rje zaJf)t3avKE&=VlgRBS4i;L4e4M8#zxuZ_UsbV(l;#e!jeKt*t5a+*@lti?x$2uu`| z$9vy?%eC)$<1dx#st1dVW)i`-?Nl3fO zg?RO4+H0utu|jO_20=+6QvBy{xqfy`e+Uw--9aR>P8COi11o}84^tI;h}pbf?nQ4RE$I{130HiA2h^pjLGJEpmr z%w)*g68jCFRRnGB#xlU@SEowzynflw$qp#g6rzo#`svSFfrl{ z#|I1NvvEqY1miF>F;5EfQ5z34M5h}WN9K^#Zvmda45y!QD&Jx$ud zAHMDDr_i2yHA8t1AnpI_I;VO1;@L-j%G=4Wl4<^396XQDiwq+^{OVlogj7FKw5jy; zlvaL3*x?@Otv-43$kCHef%TqMzP1)pfBf*ny$jL@Ucqk!$aNp-$D4eL7NSnDPj#o$ zR6`-+U{y!qpq)xjX;!*4T~_wYt6vpXuFp`x)iL^Xl2#pD#Z1O!1lC%+t?2bKf@}ymfYm5?$>r?Yi~B?L&_6)eOkHScI z(I0)x`$$aDy~Kq!ZN5AnWl|R##t;GrdJHd7Ci-R#B)aq?1{z~CBJnie)-gJe=B0?O zo1ze?xGtuRPG4m(N`SnwYg@cQ8 zQZIRZ^m9!p*OE5WRi3~%yJ5RJY$k2PutsKbqn`pNmT7d|fEgXW4O0fDPrR=O4>qZDe{cfSgyJBrZ+2r%++e;bt&AsHd#4Q^Y?&* zIOR;jjL#x}8K<+e_DJuasgg;Su+h|xvuCLW9PI)LB@v0jV-pAwF z4Rfo*6k-U}oXDn~bGEA3x?A3|BZ*%#{~nua-c(m86gabPkL1&YYn@yo{0?O}-`H)s zQtUgnqd7?i;{p}1nm1%4fYuV}oD12zQ#%;4qk}iAdTwG^qr^0R^K>CscuP1kvYd3d zqrv7C2Zt>!8`2|*Xngv;_(~wx+qJ8C)!Q|t8Ej;dlziqu)46JwA>-_&z8yG4U;6`k znJ3i2WX8z6OcDhxw%!})5TNj}jC@-et-mGysZpkB@oBMtzu#FXw4$$&af5)gcz4w@|44CGNiuzMs|0%31FZe4xU4MZ9>#|DfO%LZI zJL{`xrkq}0e9z}7SVZj`$$iv!dG2R$!|QM;bBX4{S%Od56Oab!#dA5+%$tW#(H&Ce zZJZ EB_srx9r|3sL2uX6cK1ft(t>fR2(G|L;WDQU;Fg_44K^YeeBV}4W<0fF`@kymM1Uj36w z2#@9xi)FpV{bpuw&fA_&5_+Vqn_?^D*Afjk)|pCH`RlMu5>UT`jC;SG#-k!r8oYXb;woNxrF53t?5!ax^z(fNs}w$0RXN*I}Z| z65qj6AsJ)`K`z}@z6v9|w`0e}9XlRfUH#VTDh)RDpltQw=e?O+)hf#vVkx|>~`r4L=ZKB)gaQ!$h+bLLQk<1Mr zTx~2AAC4G9kZhI%-ApND@gUOyGZ6Lk$yL`%L(54matd+US;rpRi#O2Dv|NaO79{yJ`waA@v8x8s^9VO0W$He_Pq7WosPgLK$710I(= zpel#`9hB4`v-8gp#uMty{hPT+SKaOizb;AYo8fU~w%ujd}aJMH6SD)Fx zqU)OvZ~F)1dga3PsygJ%_Wr5XdvA4%-Kx}Q>~ZF3k(WPaym@F3qU{e*H`W?8l+o!8 zM7&~@f1#~!@mvAa zvFfg!vqpnTmjdXsaw}|&46Dst6 zL}1PiwJGUNR4oKZJ46DwUBt-DVwvhG?mljtS0sBMQbx{QuU}C}mO|;Y5emfOc)*7MkxvAS>`2Th=)rh2pjjKKyr%>= zLGoC5(#KaDJ2?w@vlM=M;mv-e81Q; zVcPwFl9wZF4J~>!9U250ego7M>}+l4!or$2Kd;Qp&-Wfz&&~Hj^YiM>Qi)<7+4}DW zYU)42#gBEwlx0lEsDR(v3{_v)M_!Qr%fR>DE z9rcBQJ6P$D7|>Yex?&{KxgXMEV{={mW+R9knfXX5rW?)kbm)4%!;VcbNcfxm?gkTs zzat|0lCIUfG%>TK-`iYevd9kW0C-FJ_sI;v6cbIZQyJT$J0qd^5Po1MP`#!;w|PTt zL3a`cVKUu}IdSN@wv?MS#WDgAh6I8Zgc+#aQ4ihF;uI63SC=EhT@CBPsqCrGDKoX# z4?98FIP#g$h+}LS8|!i~6)a*-a3rAqla)_ejfDn|jj`NN9cx*qENnCvag*glP8o{D z3F;*s=u<)g2%61+qt>&fVO3e!c=lCGZIQfGl5OnXXDRz%L5KxlO_7-Z#UQ@WRy~_=1+PlYdbq65RxGi@B;>I;9CW zK|%=X!>}ooXzlt>uB)^;LTRz^QKjuF9BhD0$h$iYpqrZ_WHJ@p)jt7G7;QvjikyIS zgFivp4~<54eVb!@|B+)`ZW)KNG4PI;Uer2ThzQI$Hs4`CmO5F)^W}-=q)lyBKtyBh zb=u=B}P@Ma-1Z=$|;n;SUGpy zw3|p+YGd_=g@qed8>$7X%Jg;TR>l_=#^vvgx4rv-{QXonZ#{MDRtihnisLGQUw`8p z@1$t0qu9nDmj*NCa%Qlkg<^$z#=Q3CljYdhNHRG>{rHnNUu$OSg;+1Lo%3zqG`?MV zL(cPth7;aU@09kM*AQz{)b6rb?R2WSBA(CBDazLE zAhkmTZzy@Jf(+C2QY18_TVc$?2&Dul?j0~fs^K{SDq{wzd}^gbgG1yYI{Dzp^|g_D zv6>wmenU3V3Kk-h$yUZG6;hr%mc4#7J6Im6)uwiCik6(@<&OF-w_J=jqp@l*kt!ye z(cE_UArdh)l-#B%WV|&(0Hur}cf=aX?k*&$oVvkFOhn4)rD4lm^{{QWu{Kad|yuRIj z{h8NX`ro0r1qmSIpQ$CsjKY)cNaOTBn`B~n`SUd6{pa_Q z-PW6tqhCHjA9Cr-1LZH~PwZ}5dbs=AgPqR7*LK6Y)!cnzUba#WMps_>fE87{^x3Qb zUvv8OHS$-E=E{v0`GjUPN8J-I7iBTk%%{q^=r^L-aw^rzVF6WJg*#53ckeiJ z{iA873lvLrTwWF4B_#K=FFC$HEg_G@@$KX2M2}(}G;LpMuZyG#?e2eTmFSQEEq&!; zs`@WomwJs8vuukOGj6H5=PPRtIZ zBhjI7Xebg%2MHcoW-dphIUFeMn~G273(aU|Qz|v$cp&REbh&NUjTUgx1VI;UoUH~U zg@iYP`4$c*1LVq%7OvZN+KKkBAPOaP|El6aFfugG8^$O4qGR0npP&*rae%0kpqD|7y_wf9dlWT$+kgT}G2xp|I{` zrOQ~G?Ar(Zo`AZ65_hQ?p#mfRE&XDBNTx2$-%PpZ4^5A4+t&#@S8NJrGudn|y)B9a zpFK+X4J$m+O7d_Usn!I*&sgH<7RL>S+xxbSO&>bHqCPF}vp4M99;3;|QqkFbGDcO{ znr>n7Sc^w;&A8P9b0PWFMYY_r663>1w;O@NV5wu{cZ_d;-3_yH1@J!II#AWR>R>R?Z*n zUiz;dZ>%1knK`o3XsjHWnK``L=skrWlSGn`aPFXJ0?6dwzG&HtW@f#EG2?CN9CG8IsP*xx@?6`ys=%@oK8x$zf$ zr!PN$1^s-B`Zw5!OGGgaA)Q5;)v$@id>sqn2TTy|n-ZhTVyV@g1T%v1l$fT2Fu>AH z{DrSJRQMBSp^-R9KGX6<7s_vqOyH|k2N^PE9YVKWK$Z(U{ z#VECtqqptox*2YCDVts5UnY9K=(^uKo@rwRHm}Am8x$k>k3A>(tFqUehYCj47j_$e*5X-=JaImo50a28AIKLuLt7J z(AG-t@znOK_D@b+Tj8-VTb&sw`gY3@ToRyO{j6W2B!Cc6Kk?ZKfpd%pz$VN^5?jf` zmHC@$+G%<_U0Ue2=K~#@8b41Z|^GI?K3R( z-a0jv9O*aRNK!p8yStWKuJk@!p3l}PmT>6qa#5YhhCJ1|>#5m2jc$V!&31jyY^&7* zyI8N2yEZ&5!Cc3~Qh;@=tg8zhCeQ7&1jmvm>M`M+Q`Oi27k=1lQQ3NWaZ zdX<^nAH^k%Ti;CP?ncP#dG*%*R7=o+J^Z)+p1iMrBF_%O31%wa&YOo4Tb`b9&G=h= z+fa-GyrHky=NRpx{I8#+e=6DCy)?Hsy`-{8Qh#DBptXy$!;DGONbbaFw!@@}%M1Rs zghhV9#grEaYzd+v=|k~JJ5O06w5PBD)K%G zY3MZkx&)gLEz~B2^OH8SfbfCgM51WKE{2^a|gFOEA{bcQM0UZMY|#~UxW+c*8Z5IW0$60y<)^bqX=4MdQwWI zuAdrBsQ;B;z$`A6GMTdaZ{%Ipdf%_eecNg^yCpxeC0o_N+A3;gZk0xhDaw^|FZvag z!eQrs;GuB>t{dq6s-luj{_A*I*8cu)-GF|xq2=C+$&3}xy++SiZ6>3 zbs1?+>o4by%pnMQ%^%L4cBydc(&m@`pYENNA|%Qky~Q(fwln>Ao~zCi%%KN;pCy|( zJoqPNwA~qljf+2+az|r^@-2bSiWP9J4L05n9 zrT?yZS{e*}j7a-1h=L8mi#<&-&4@NiWz#hL-J{8A%a|Use)c^vt+7SkA5=p_!zi9d zTKkoy-WQ>u-EQ{2q7T~2bhP)lwHQ<4h2B3#9~k_@)*lXjeR#{3*^%G+xUy4M?|QG2 z{lwt#mi(^_m$sDh@5}GmvS;|?*-uSJPenAU|3MhwpYb#2n$il#S-y2jJGG&k^I#3A>xy+jJfB`;_Hq|y(+3hlDlCtlEwZ@%&mUo z`S0uJ_chc$uRclruS^)P#KPGssU1X5lGfyye^+wSE?mD%=j$4-*6#`uVi7G^LgI3+ zzU3yj)qfy~{*=}(me%+8-Npj{?C(=5Hea~|p6x3S=h6eAar&=Le}NP8OeVO%i0`fT zBr-2w%ag@TXm$F}%-@*@^|Ec+KeKe@{);HR-3PbFbWu2iNr5b`fz;nksJny%dtY5A?baHk@tb=u&gxs zd{Qw2rx{$ZPLkBK5H&zB4KR(FTNMKrde97~(DlSUt@oa0;xou9wH;Da@oj_0W;32r zmuVQ2l{G`Hj!b&70u|jiwuXo$-Jgq5h!?^4ue9z zO;e`>AU0zppE5aeItMR5j(nzCnQkpO(Fog;u7DR34>sS0H-}76#q3R{+QY*oD>f9; za@n*dr)1U*Q3r)|R1^!2#*EPdWCHfpds7l9M{;km!MO146t=@Q2bVLLC=Ctq*g{;7 z;nZ`T-6K3z7I{Q6?E^yR+~_vkw=J1hMlMr}MpAZylt)X7A_|p*5_Fy$X~sgicxWs$ zxGA9qEGuLP&$*a7=+)ejv|RtivVksSmT3pkfy8{L&A`$Szwm7_Egne={~wQtUudi> z&!wQE6*28Z&`DSh$)y3&tK>;ZUa(`?2|HP&G7Q&_JwtQx1Pq=UiiiB5Ys4cVVsoSz z+JOJOj8&aWsAuUeTpxd6K|zIJ0_jA&S(BGcvMQ{m65z)=QcYOS;j5Bgiro=qgDhKq z99$RBK%$c`PzVFk098%ZVBK{|kmbjuMp!=TQr;@gI!2&ag8cUlI&S+SGdV;QvYn$t z{ytI2rZMhh=aA`g^0w!fj8sX|i&MB8Knj-w07W<$xF(!#uoSJ`8js7($aPZ(4Fgtg zMD$s%8`BTdE=tgb>*K^6CFe(_G&zHw$sQ&>)s8ylIHa*ytpaaJ&6IOgfpmB>WEJ`f zC%p!Ig&30%Y0D=L9zkmEg>KO`EvLXqHq^V-1Hp>g(#sTfSy>D?5QLYE8S8h>ml)L2) z!Sc#8z0_>>O4pUbniv5j5NL7R^k;yH0or5rt%$uv{ZY$nAM_3dPi z;`u^jmycsIOLK_tlgo7QUS`_4)O>+E;B-;Y5b}IEF^|=^)p_nLM4>A44R;98b#fzZ zm~b{@KYyD!y-cQIms$Po19uJF%Un)W16l!0Uol=%$lftOyKCf=X z4a6ix%GI=n+Vb*6xxD+!h*wyYB>2>~@TzIHi0l=? zEHb`|EEYlDtXRqeb3Dxk*#lqYk;(tNln>k!qQDv0>s`V#*89lHV@}Rn_}ItrA=n3( z=j(h#Co{YFP`(8MUxtMLlWNEFOLXmQ&UVU`BI5|AF%wKVK^Ph@%YS0vDvGS@j_YPu zdEX6YSR3S#_8#JkNwqisaWvD21Y;0I($47H?nV)%&izJwJczJTJb3V2u*fo>cxB8@x-tome<$27YbeV|b^(Jn%g% zoy|~ORVSG$H!A>MW|YmM*-YYFkvQNatz(-b7 zOr}Y}Qy6|Yk>16`zMlLt)oh^}GGmv1=ELdDO0!sEcg`edR%iJR&d}FXeD46N+^f$c zCjp0q4wXb*L>1!O@qWv-iXzD6qZRmfgtMH_o+q(CQHuv6Og?6!oS=NLP43gCcq&?m zIx*dj*}qap=$KVE3Ya7qNHiUac0e(Zn6i`)HYeMgCY$X|BV)B8Ww=rqR<2|AVVVe7 zB}tqD6NUC*rNoFeJ~6S-o|~GV>lRwwT)8=wU>6Ia?yTS{MiPuEdTmARR zW=dW&2#Z#dPJ=4%FHHFxr^u5K#d)m7S9IyDlWqD{caj}h==vMk-t5Y?w&aTEs4CiQ z%aTzAfnBw#(-b`GjHpRg*+3<<J*o4uLA{HmYcmnkIM`|_){{I84K@c{Euw*Tx$ zy$|1b<6GX6fA?E{@P#X{oPPHY-ty&F7JY{Db@}zAX+F8W=>L1_)hkcRB=qwie}09Z zukKs<(1lxDM_T25cR#qYZ{Ny;clXyWd}xI>92n?nPg0?=2^#D$lIHgZ{&e6k6v;(m zpiQFIgOK$D|ETG{rVYN9=0vZ*G;HOGc0dJ;u|*&HeM{>*;TfYRzOpL3cEebu-6!lMfhj`W7UG{DQkB?3wnnNl6~ zEl2qpK9(ts9Qk%}qf*E4l&@T$*ZNllly&{mzjTc}@IojOuhs{Xq-krmqK%X{Tz=Jt zV#OErADvLN6;+*({dl{y*!y5O77NQCzT@%#hqyO^ljExIeY>i>T zo|*2RrAMRD%xE-{C3!5%vMkH;f-EbxF~&Q#F~%4$4oL`@*sL*J!*d*Pc!VsFdm%A= zfOQXrDDyH1935d@q=00<5;Omy)*l;U+XC5%;7QceGDc~K2t zB^QhD80-jd`mD5E*S-vuy*dQION4as+Cj3dYgLheMqfwN8+~0iFRYCFjB&%)orvTM zn|7QKtg2m&@WtlnYf$aWUbe$)G+_^#JEkYb&RgEtw`=ctqBAvguCrQGgKTAEeeHad zi{2fQX-U+iT}^v}{?-m5k3GQ~d;(?Y=r=hs6)z;U14u%~=yxW2lJuh((+-ib_6fiL zi9j(r`psA|@Pya*t$kGPHHzg-)=rsbz1Qvc=Y;*Bj&i0DiJ{f3&&&YwCiDGA#E4&GdC77pY9!A%vc0_T`tQXTHVqW3j%- zN&)XlabhBS;`%R&HyKb;18gjJ5-pTyI50A*0oB_H#5}x?OHsJl7CmICFncnfu-v|6 zK(z)v+Sij6i{9+fw{v|Kmf7KZ3~jGk0d#Fc1XAYs1k+qk2&<@Tagiq5B8zITlp|6g zjtERyov*Ep2@}kFd?M9{pC7BU44kcEUVTEY+z@h*GPJbpaGz0ryMnVdZRoFnHAU48 zLGfxaO=)XUD{>U8%?>WAIpZ=sATRO9*f99jl zJ^0{r?|rYw_ri}opEaJ-_n>{vlFPK~`3d#)@HmrIj~T)=f|+W%yRF^=RwuaNzHbH3 z4pIB{A)Ihe`0>NqAe*sG>NMHnq}9Vo-90XQj+})VSH%Q}LatZlGx;{922uNMRl?2O zO-r`0q`HPSzQr+6j=1_vxw%wahVh5lPcz%mk}7*%f3_#t{wU47S38_#rrTgx>c$?QIcaIlNPQ+%y3jWB5hqqRi3uS|KnSKh6mO%!NdXR0)?|d(NyA9$ z16KG`sbHp3j{vWNhR%xQ$FxW+y^%E|b}|FnY{cu$Mg9J?VTOX^Iip$LGc498y53@< zVgxgpeo)t1*=#15N+gU-(_cR1A8Y-^MyYsr^2Ae(G0Q&|1bHN4Bv{0|h7m2Kv;m4W z%i2th`IGJ0S)}=PPY-6iFDpeVx9u#3eg62K#o-Oi(W(8YsQ|27F}=n`nl?7;)mOxy z)*m>620A%EZv!`lNq`neDb`)ycdKj!(bp1=3Jz)I0`N?YC$kWF5?tlbPgrCoKk z?n<%ijs?7783HaNuFCO}OOoscu8lk^$geHLeE=NMZ}*!5IVh%p%GJ{-a3Fb{R(b5lY80^>`|VuVW<9%*Qhkal(N10T7h$6q3bu07-r@9*HCqQNNwx-DxxE zm9*D18h=N*)m?nWCvz~4_d@*B6T5`?N2jVKZS+KH+yDNy->XkHQo!Ng2!-Z230lKQoW7VamA0L`BrOD_~-X&ap( zdm=g#OaR>kl5|k9r^Ago;xQnIC=LZ3U)OHb8^y&U9F&eOOw^T-PAMQXRM!_xR*4#* zO12{|dhnZ6O2)B+J*SwkF)32JT&H*ZWoP;XVG6-+u>+;ny`A7(I~Yr(0Tt`We(biD z+CZ{MwY?Pwgn6eC3kw3YO~RXeGn@z2fm~zygEzrSU?(VF12I~_cMciAEyB}C z5E>N^NNjb&7cr)aIMge8yFLDgvoNAUOW{>AsVygRH6dn)DMT0xgBjVzMBY%*BCZ*X z)Q)PXZcU4GGr)C@aK}DV#tn=`j&fm0m2057Q068LZJnJ0FxU$S%5K}llYNHfpPin} zxOr51aNyzxCDqiKicEKuBh;siNd#4rRgR|cfk=bp!#%L(koWNp16QALv5X4C43^Td zVMZUK-Hr3S-dNNZ^IksG3WbNG&@#FR&_XD+p7fQfXhbl{M@f#Q0pjLMn>oKfi8%s; zis{8A;_cC9jEH0uD1LkfFbV)fE@qKH8clgndqt*3U3d``ocLrE`N>Gcae(sYtumP7 zoB)GSEDDfU3=c3xI7UUSPsVb=T%M#dV_0dB%OW8_Nzm_`!EicKp2BmAD{;am2T(F- zMZ>`;x$ulY9D0v)gq32Zo%sh8<_R1lv}h>f%ObdGL*3tI>;-!jG|Vxk0%QMwwvQ6_ zXQJblXe0e3etE??^t2ATdfgj?RL@xXh6Kb`97UowSAr%+Da$e~)<6qIf^OLR? zAI{(M9QkKo!S`#2!{Pi*+Wt{yc2;}r@x?`L|Km43{`gmJyh&fwVmGp>e~`U}8y7%1 z@OZv${Hb2?RFE={5x@1nd49|D2WY7N4tZxx8`mb`smS#>uja?L(OE59jB2!1%v6yk zVUT2BO=aw)C_|L}bXAs*d5)UR8H4s>^0wT?jErVH2;fFCKSYbp@79vEO-yDbszBCF z#4V{tO)8mkqA-WfaEy!On+x55dm%c_1>_&MH7cu{s+OhAZIK(hqpna*c7swfG{ilx z)W6W|aS5#Y)q`!>flU-Tz8ojUbkrSd+2W+4Ru@o5Af50ji)%~*t!-N22tG?;&&yJ$$TP$sm`*Bp)l4tBQzU}a}YH@b|^Cy zGbKSV!n%Jd`jhWJ@xGgGVGT(lc5Bk6}`LffC!|Ab55;#>nv6_A(DqDiVOwx( z*#uq~l4!?#APPj#N8s%h!Qn|aP#hG!I5dmvGRT3cQs5VT8D_5_`-+`G*FR(t5R(m; z^ipbfIvKM{-h3Jtu{;5%BzBMZO1^A7DJCuA8F**-cm-iQ0V($7NLcO{qcCP=WwgK) zEsMDK!gwXR$1X+rNQ|7v^KRz%EqL6p3qH_1|KmxC=BUQs_vriDr=Gn3;HdNN_dojn zQCs_~gZF>rl=fHR?{k-D47<~Jpa?6T;}U(0(I;xut&BpSR$QX1&UdGmh~f;-8D$)b z3U?-xb^9YjUCl0cuBdTrh_?xjoH8xN7J(LEOw-?AI#N2|q>g{#3&+xdq%U~SiFB3N zq5xU8QiPQTQqfey^6#r^_3ZL%=g!mg>GDi7PFVO-)40G$V5jxtSch9&GgHdzMg!*= z?Bc&V#M^3h@`laww5ILeaqpP^6>c<#g=Q?P88yOpw0^a6Np4p?nlbcjv_89e zYs`sWrLRp~i&);T4#x0%hlW}^g1c%LpI2Iq#uA3UYhre2`q%gG&~VTChbuSWX+p(A zl|K4DjdXZ;p}qBm|I=s~Mb8^OZ}I$t=U=tUv{TwEw7a!8X}_fX3i#-s(f%ua3r!O9 ziu$l2AJ~P3rUknIZGjS;{fN&I-6e$|0P=q7b&-fc63 ziQ;VAD)_f)wlZm37f^LWd7N@s+9EOY$`7=)O;2T5Ce0>bcUx*_9-kqMd7-Z~>F9+} zmZ{4`TxquC4(u}n7}?g)gvblg*1u*>mI62#2gs2poB~u8$P|m;j4V6eWyh zgAJa_fH#rBGA(4$44K#$g9+S3$?Alc2i8e^Az>!`@LU>o#{H#9v)Y&uU&%e8L^GF} z!}^q)&E}ek@Va<`%rvTPbPwafh4Zyvg*I9tvfSgBjy4o15m+jd=;Hu(u>cgi|;1i+uRbP&n0pXtPCG78}l>z=G^;>-npn1NQK`P-Cal;;p9RZcZYT< zkWTL}y5H}wVdYsb6n8FGuUcp}7v`JI;ZoXPE}oZhzdu#ph(#0WY(Ek4Ipv6hE@IYO z&^{booab&Kp5BO(%^punD}os@Z2)l&Wrv5vdjap;pc&~#_`sBc!J)}&08y9zF)|Uv zHH$#ih>sq|^)_Vb@r7Y9b<2D_ADN#wBH2JTl>*fM+@%Uq0RbC zcnF}}r4^QmQ!gWi4>oG?h2c1ENkPqwu(pyM9VRX!;wu@Z(IMbx1lx;dfoMrt+Tm@R zsa`oYQA}(l{?b} zHgM%(Pyl|<2^vX21N9asV&z_{8D+_&53Hu%nJNXBb_PaYPG4X0&aKRvzZ{4*t2=Oe zC}cLmzn45%t)`R7gSBcZIr;~EG8jv02|F^W{Xu=uuWRb-?&|poCta-_OeWLSPfdhF z(Sx}{$&MT>-0mB!&iP8$OMslky7U&-rF-ETg7 zf$?*j|K=P#oIS{)_R<;$m(}&!J6>InXQy7CoqEl9{7d)L6S?l~xh}t-&UWuRvebk~JdA#AQUt5^~t=!)l^*>QR zM6N@MmGh@7F)u4Cu99_$yPH<6;PR~xSkS6%m@HHq6lXmx>;gsMAfTx$AmKq%6=Sb% zC(i-NohG}4qQGK5a(QdTAgiWJ1wx<6kyeHUp)grd5$%P!*w+5La{sEYOa=1smA0=S8GaG3ip>*bBO0)ORAAHc5)@s zM@#j=mMowSQASi}+l;^5m)38;CnfQ>M03D}N@Aeqo$f2dYU93!89H7R5Tdql?=2cJ zc}V%*(t%K?TJ}X>>%M?hom&l@)X>$fseTZ2MP9o0hepCNzL$ z&qq~$D`v4!um)j2AP^!HB3D*8(n`cOBbV>oNaF0sSz;o>Ru&gUZ|dk$y-7gvXN7^&q4ml$kw_43NotFb|M_GQk4w|cgrm(c(N^dIW06QP zG+lNQ@r`VXwR$ddj3BY3A3%rq+9WNZhZ37bK*4-LVt~TNQp?+ppfS`Y{2`mpIbuS| zQ6mI+Nl9`{JV3+;acL|WtjeeZ2w;Phdl6rdgZxg4g%^jHP-r$7K1FX^v8I&+y zWTF5Lr1Vw`5su{yr6bEmg4MtnSiME2gQH*=iSuR9L1^~spY~lqvbcW-MHLx zR};8OD-uR#GR5l^=EZ3;z<4@Vc74oegVBQAkNk={4`*8@0?#NT-q4qlA@Xg%Ea8?z&w>Lpip zE!iD?C{wRxGqqYq{jseb-#(&$aAUeMXUFaU%fhx7%Ds(^UU|VDy*W0h$acC-3Y-1M z_Q&kGN^gCsmQ9l*xOV0+$!jZ>Y__KT5nII1q#Nkf-t)pg8#yBduC_#t98u`w|IU+c z^#PA{_Ca3yh;c9$`gM}b&nxbx|B`;fly~+r&FHR_vf0u~cWSkq&6ZcEQk6<7T^Y-4 z|7w3TSe~7G?6Fd&p3ID`PD%Os?E?B&SGvW4J^G;CD|c5`x@BySSJ{JNcje4xxmy9M zqM|#MbegSnWpuB0czL~O-tgFCH_VMbcJ7jgwu%bh;`O`%e5@Y$SmL#M7`?sXP0&&k z%!uG8ulC>Rk$GSYlv>^$3q-iQ=q10jZ(e*xf2uMUMZep<_`G2>ncCBv*;C2qsvFb2 zJ(XNx&w)d2PDW>|GkZ3sM+=#1HKShp*01(Q+k4vYRVn3lIW&M&>SuyQW960 zm%L1t=5wRof}uD!p26%(_h(aJ*Wd|($@V3)#r2!n;&u(sBfUo+DWK3v+xF7L#8MGY z4cjg`sbVp8=5NoQEN-9UaH?2xzOBvQJbvooN3I!+{y36O6(^Q?h+nuGQPJ5a9@5FC$ zCw_}V$&!l>O1KvF5q?X3UaZIfMcV=?5C%xGX=ERh_GD}W4`w(^@LeqJZT28puICGR zyQ#IJ*er~bbh^`J(8;N+*U$GPIwMh^QruQ6~ z>>lW+t-#!Y%ck|`$QEp8i}^0n;h#!CgQ%YNx*Y-S6HxdcXFg<>#M&{tf|iTw?C%o;y4h6(2oRXw&kd z<%xwqk>DSfk>-i{FlDyJ!i(%|!g0HXXjxpghl(0FTg<&XRPmrt=Ox3f;(b*wO8v8Q z=yQHG!0HyrG>U%kB>b694W)Ojp4v6FI@6y_Pv=O9PO?xxs_2DGA>f_w+M`z?#lw>N zlE)9#rU|>%W6^NDT*^3+u>e`R;U2-9PKA69@*X6Qv!ZF#0;4?MCy; z_uW%jG$RBYSV4b0(az3XP)^no`F3fH?nViK^qwiFs=AS}Sj!0&xH6CnM1d4@BD0%i zJ0FQ;8#vy>tPx@B#k7&C8X4Ajc(zb5W4`5-Pumg}-H9(irmn07GT~Nb>b7b*VMUP-O>l{X zH2>^vx7ua8Qn1KPThF74NI`e3h;~p(wp7`7TP&jt8>_^Wy8a0yVXgmk#c(;(X1)es62uULOKbdmKxO9<|>ZMM?E7Y ziEs+;)vY38hZ2Z^REf@wK4!c%isqR)ahX>L?OxJcMjW*3-s2I3`Uyx4TrCDL$gBGiQl zXT}pIE@VykSE_`Srz+vYr4?MH=_MliU8N{m?z+W+^SDKcR_;Q55-c`c^RX~$fCOQ@|pxqA4OvpIg?r4%7LOM=`Y^1Y?x z+b*GK7PL&r>RB4A$_dGJKm9G5F8ah-9Eo)ZR!g1SsuuD`wn#i-wioFw-5YNt!}2kY z7cpxER}`h8s&-YE!jS3J+{;|;*gytt+7quT)V!Fr8$U6LEh5T3oEw^_udSf@b7_f|@F66i@bKPw`dD`g6av=@$nDD2;e}bJhzG?WH^Js`h{*r5lqc%8KAKGP@SWpq)eoiA-?_{`-?yO=5yit<|Gi6$z<@ z|7OdW#3B#45ZZRqyWQ&5uhpK_e}4e#g875!TC8^(3!R~fFq>HUpKu(h=} z1)nm0BRIOp`>~+*237wZ)c*)u7V)XnZ_U<+bqg>MzHz4p`RRQ(J^F*29=%C>+bDbU zqr3H|Z_*#R>86`TPibqntdDBiZz^8@F3$q>d=YCoHXe;@TXh^ZtHX>`0hBgBE&c9? ze)*9{e)*x#YMxW4G|y-M)7!4qp1<~O`uaofeCI=Fet7DX9v*$~t=eGp>|0fxcY9ug z{P2sQn<0yju;PKJ32gY{Y`>7Lb=ua;F#Of8hkH2U28ttQ(j=p$$<={uV2g}R%RPMQVx%}?qD_5-zj=g*3ln_e# zdV^JF~8}HU0qsiiPft_ckkwJSD1;Jz7<0)vq@nkjA!jmz>U&|NOS!M(p3MtAO;%C9n zU29rRtF4_`ev|&S*ZN=exs3MVl=g{FgfH`*z9#s}XYm!&zw}kzNT2B*KCJ)wN3zGS zIR3-me%>Ao-m*WRzvFoJx4X{qEcZ7&-+STv`tNgp@rp;dPxgTLL|5m_Y;UBtH@^4w zkG}nm(c`-Nv$gZyL}j}8$>WXXnEI`)@LT@m3DJf4J-^1YWzhu+E>9P$-3iYPDw5eV zj*mIi9fJfpkAUiZ$?hs%RN_^R#L%<#Oj3O6btORH7YSHdCcUB)#;j@(mvmWC6yx0Y z;XEPk@sMcH#1`faE@hEXGIGn{vY`~FE^e&dl4$kT=d;f8J%{5nPO3e7<-SDpyk9sG zpZ#E|b!lx(d*hKq)&8G`8r3(v<+Y{MAQG{|s{{Yk-CulYX>9bdd-j!YT%6j>g%^M6 z?p@{igPM73cO3n>wZHc+YgnfpG^o5>vUYh;=EQ({B^nVG}dKW((&fYc&*juFbBYCx5T z?M#;T7k^k+5$3j6c3y&fSd)iT!bQ|J&K3vQ!M4ULX^BWrA!=bK!rm z^!5YULBF5vz3k-4o_+|R*;ydwyu7(UJovew=hA3nb@EX-Uv^N0Dl+~Dd#W+r=5$ay_Eaj2c@r1aw)sqU?LN#LFhYX=@a@QKy? z*F&k`@P-BOXHoyRB(CPjDiYkF1bQvZw3%lN*FICl?n_zW%`W?}fo;JP`;2RS^#bFN_u<;8WNoJ73Y) zeHSjoN)fWOWWVPW1sc1U?BG2a40J2Ce6^adjZFo#WAUaFipR%^af)*tScj29G&`ND zMoO`T3w`T4sE6NLJbC}Y{cn&P-+%Dn{U;aiNl?6*z_P}DNo!-1dxX3w8|}G+rnwXh z|0M6IJLb(Gz!mvmVm9#pAwx8{t*!6^WhBu1o6EjhQRPCM%tH`{%toKCS_q*74Wtx5TsF@%enL zS)7Y^L(QZSj_t?pziV6k``bNkwd?mKei=tsR9=K4c}?1J;r zPPtuqtdVVGyG^~~ZD*Zu<+KALOro4f2iwhJCwn8Htli?B)7ftRgVSCzCVN@;#^6o9 z4d&Slb`i0QF-pLFRf07>_jr3Oh&Zd3^d2r);g+a z`=VZD{==j3S_DY@f0h!C6Vcv2TmL}4hb)c!9hhjYrMjKof!Sv7g3jtUoUl#S!ASC* zRjyu^$rUb*>A?z(lf30MYE&dOA?8dqzK&2U7jy9R^0sQO_0i#=>amj4Y!=F ztvcD?#YxZ_hn7sbJT3j{=3DriF+5;?B&j(c6+8BpG;(odPa-wve)KU$NCAM zp52o%EA6k}JHKl0oV;+NJv~98v|dk>{_Xqwj6KWsmhrBiiN-oP<6Y;CzV|LWa_`S) z8^%wcQ#{gLo?oTySHn@4ghSJ+n6R+axTz}95%LaPS9$X4#o^r0dC%gHeddFy@|dGv zCEuen$Jh59KfY)E_yafXIlg|gezNS0KH`*%Y3*pbIC|`Xqemamf+tRl{w4k12OoT| z&^9mF_isl=zl*-QjQE!~aA&p7xkQKe56po(cxr^~iM%drGKWQPi0MPEq8wpE)Z#dV z0CnG-W4MUe+AG6J5xFHq5j6}JM_218F6n&+r4&Fe$0$=(y z0I9=n0n|kMj%?r{;`6SgqnkL{8l%_f_h8l5y7L5_YqyFl z+}}As=+SX$LOfmDTty7_@BV^_O6$b!9U`-Y;XQy@XJBX&UD(6kV+3`eIYdol`HqC= zG{PtlPssIs&23kCO72V zpVr>IA@A1BVexl(v@Mdq<`y__2}T%-rdfbsMTfz913h5n!K@=I4sgf<6noZ;YN4jk z;duov7SI%l-4@jpBY=0u(lvwnvPOkMU?D1M5ffS=E?}PxCoAvU`@VRboCD@wU-?ke zBuZk~sl1Vcy%;j)KHr!Vs?Q8tjd@@D;!5Og+6RNq*!%WAnusT6;#^a?q;1XBTEm(8 z1N+_=pGm~y=00CJ6&&jfTeVq#^KkindZ&G~^60)t6VpIW#ms$XG3~ERFSKg|{}mgL za!BsJ;jJ8Pnsbfz!gM3-l?TU8ON82l%{f_<=umw*Svn; zzSmv7qrb8P-;dtd*abPrsQ?1#;r96W9BNx8!OvMqps)PFq$v?ls9pI-SfOwY%mVY- z)c$;8cIR5DUJ56`3O%^6ajI6js!&QrBGFV_e`7L}49{iTt#BqCF+1nqa{l@EUpYUx z>~$AhaN7kP^UUuU1++j}ys|{859Ax#L-aJh^KcG9uLOF!==YH{{E-OwtN8MomVR6> zHOe?EH*?ebwac~5sbpd?m5OOfZbg46d{-EnIL(?5&LsT!w%xbYuTC2Z-8n-SB_()Y zh94vc>H@uVt$KYUjrXR!lV(f5@`B;;0{MHBwkx%M;Wv4c%JiB~!^tt8kk27uClCj5tF^0)FICIXSeVxNC>~l-k z9a#+YZt5Kw)3kiUAKH5`w;3MWH$Hdep&jujlWVQTiwy14x_0LC?Uf~Gy&IlO-)>zv zzi^>Bdj2P;F9_jaoj)|A-7#@NHPXnQf3fAuB(=`|(>o4q&cA#Jshhcae*gZU7mZA; zvh#4SI&ox2C>ty+9)!=W$y=)k*>-{;Vu#Ng*P z;iPZYUZuTB%_T|h=|g<(IhMd?x)_jW=ZT_JBH!_l z1gP@{Lqob!%xfm(t_5lpNI~B|O31XP`L7(ZrwiGTr6B+f1=}DkCI!PY*!G zNV%fs*=m(_uuoh)2pzNmEd=s`Y^l!<))hhr`lds8!MP`JpF=v=dS-fO3^ZoFEGp@JpRQyB5R z5sbId6Iyy8Fvw(^{Q8*f}@5-G=YRCp}j-}fvty_F_`lhA*Ql`3$Q-k zfHXt)QF!x(OeZLKL`o1ZfDfh3QK zy|r)O^-=%yyx-fu5v)qMt&L|8a5{0Zj?oi+Q?z=f>+_5gSKusL+^HQI! zY2{8XfW79=UMxhF>UL-#ko9UiylgX7|IH-cs({6p`lK+xQUW>0G!@5%?T@U>$*sj` zoASt_!{xH>ur9f4xoh2b=eS&>U#Hn2)D=WPSR_S>C^t~p$w)x*JQ2SWC%p;`DP=68 z9RIyCW=NG8XQeVDKJ-c!re>LCd||KvxsLh+QifXo17*IGIdbJS6P;669nONC8pvF9 z)v3Pm}{U^(@ zpFVGQ%H_k78268el6K?%8B_57(1CWjcw}+&S}p>1RQ~RAJZD$Z-=UxFc~!*kuuD++r?62G8ssfJ3Gl6CANP5C9n~1;EkP7!b+2q{GI& zXg~GMFsu)L%;fGRrLP&TB<$tSJhe=Q2^UyYFcqK~U`BlJ^e+KORcwM;s^+3MPu@o6 zs_oF-!NqQ(bH&~F9_{+yzt|>b8x*f#t=C;AyHZ2Qz!|Tq~CKlIp>zhIoI{9B76K7&tH1}+Vc|)Oc9AAVLekbgT9m{ z5#JkN6JE+c6HYB6q;#;m@UvDXaqxUe>X>21onYwHD84G9D=&aIC7V>PQd~%=25*H| z#5tEoOlJrZrRuuUAFqgMSvKX3!+*PRW&l}Q7HEbXrvM7t>OB4_u`PGeL0jOS%D7D3YxeC*<2Q{+-MD5P2Ny` z?<9ANgRd8)n~<-_ohvt9G&6J2jVs%~f0|5!hJebRTGHXA2?EMw#@prntw>H@6=!_2 zM~Z171K8=4Ddbg|h57K#PE`3u#LDM2?LtIcQjh}%7Zvg3ld_}F%|L1!$yigB?IYmuP-6toAoiKsu3 z%_gD%T?)Gp+kO0|Wmi{u|G@*~2Wmu6{#YvR%rxSmGe_JobP!<`3@9>TZn_n7F>6V=%_D{z2;Nk*^csx-)L09GE;j?Z}NzrE# zxY?Y#oV8n_kFKYoRvT1(Jy z1nETN_E+PFy8}O59}?k~P0eTZw`^(sS*&R@`s}beqgSn0uW1#nvIfS}PIOKm|9I%Q zc}hDK_-{PLbAi!EMg8zeEV5??*wUVT{0AS34hFxluTZ!xIh8%ZpV-mF7PyCX@(=Kd z^(3vT{3j#{v!-u+RZCX?Cae8zbX4Ly|MZ7Kcb2rQwtF%#JoAM9^qq(9eDm7c(pzr4 z@k<>|h#+PfPn!7cA84P_{!;r_-J|16Ij-1D2CEP;2C&+dL9Uok92Pjf6=p>NKL1_; zIezw|yzQh0sYE;$gcHUWPi@QmJX;b@+O5vLtLufabtQA~#pKn!X4_wbDz?s2M)A03 zTP#%}Td~xiyARuD8fOjxGj=O=zh6}5Ty?DTXWQ;Zp|J}Opt?bdDQ`ZxAAC6nx13G3 zmW{3BZv6=PXK$g3xz8p|R9=fx10}c1Q2Dt6)Q{RgZl@}NTH?X&E=~)zAzV#!ppx>Hy zF7AB7!NZ|IdxW0`avN@26lva2Mv|aOz>9kxYU0(fEQ#xsDfTOgbV4;pVPpd;Fnk2~ z$i8kwL%xvD856}?ye3)#0(g9}O585si$VZtAUqaJww%)PSY=Pm&V>Uu(go~yW=AF2 zOkypAQG(}JmFvWo#Hz`{e13d)K2;W{HGeLa?&tggzrVa(N_SFI7uOO6qHpEiJW@P_ z=_B4CS{IFpo|#I8@h9>J4W1V|7y)U*1X(DDvkxvAL=xqp+4u+I9u^AJ@tMUbQM_2> zL5v^*34u*UI1>mtC==-^LV@2GC*(_xMU#+U@ypO+dAvWo0M7({p}1lJ zW5IOGMv#GW0*GH)D#mkgK}iu{2}W0=D*&&KLk$otLHaU91X4W&Etc^^1UhYE zgo$$};OG+U7aA{R7c23xXe=GTv6Gd^p9rNF$CDFTKM}@s0|U_1<7GQpu>nq(L2PK@ zLM)hyU>d`n^_cU-6fU;xmv#U5_3QQ5=(A+S|x6K z=znCX5?U$iiwW$P>n}~2zJ<<)wz_e|TWcll+?ERu2@ClKdqYk`!_gtoN)ub@RIEo0Trc$)jFn|;Y*&anLTtWM~2pdoy=f1-9H zVlo8&hN!n*Vk9${qy~y$qg$)bf(Y>2xKH3jM6=LO9CfEbcgf@BnZS1 z(c@v9H=}{kemui{4p&*JF^g79Tncfs=?K2-`*7q0^a=QU<{k>bL>cflYH@%Rk_DH} z$DI(;6)grzyCk{yWZSUgxut4#DF=vWdlD8RxT^%TF?*0>BDq9dZgi_XC=hPV-F!ynyoiZ*@SpTN>$aKKNAD&S)hqEitXshZ zFCDH%yEaTN4sW=KCzZ&<;UY#a(t~8}o)Xv5!r2ut+H8L#JbI@HC>W_r`rCT12vxwzs>b zWN?T{6m@`}PWV^y;fAxYIM?wumzSH~&fMaH_6fOyXTg(I(q`~4SThBjR}`8eiK6s0 zv*?2Kn5cC)UQ^mfo!DXkXx2Eb>&tXUWAr_F8Wb0xKZ1T&YuFuhiJbK!0LqnqJ=jJc zu|UQdkZs6+6ZR$+&{KAB0Ad3wpU6AamjVSUPWhRrlv(C|1|Zc3B!eNCSj^D9w|>L3%+)@20b{JoVA&a@$-J znan~aJAnQCcd=3B@N|2F=bfPE|DDouKOBZXEqR4fZ zX3HWWhQxC#CgxN!VnMT^oow@=3c;%83R!{O^%kiH`Ur7^B8u`tZnI5L*@#kJPAMCK zOHk`AmoB?&jxz6y?U4-|@FY_{VCAEsfw-R4z2MjQb1_VG(R3Qjp-6ymih%Yj_Q7Kp zrJ)6A;gOp!9oM~~Qul@w$ui767tEe&rp7{%aDA{N9}A;o4<@Q+I1=oxg>t|$TA@P1A<0(AjIOuyMh0MXfaQe7 z!R`XkTS^_Ohr)^ViRm41r$+6_fdzmWwApuL11ksCLRKjgZdR>cZC4yBw5L6|Y)`AX ze%b7fp{egq#pJ1CsT%eQ0W0u7Y&$WqYh;MP6nc%9B@*qi$YLLMFnv|K;Y=5bE7hf! zUpSKu2PRMr0B7Ks*+jt(4#N>|9Gw0@F=Jd{-$eo{h&xWyROC$<`Ce!epaM9^)MV5@ zk%fF#`+D}G;+UC)Uk8&1LJpfAx~6sr>>{#YqgK_!(J)E4$OH;2X@>!Q^2Ljxcrs2d zLdaQfaeU9^^NS}5-9S5lZsKmwUHAgM9nNmZXq*KYUj_=VUB+ZQ>HaD<2R=ZSafTqH z$z@MOH72FC#H+5kB-I0!wVW8Ad@jA0>>=&xwZkvJqPw2S$A-=3_|)v)_3Lh!I-JSr zZyx>UTaKINNw3*i2BGnmup`myvGnHR`UyLHl|NBIKbXjGF0P$)!Y66tUBt5QB$oXp z`jcwZ*z!fLuIkk>bSPTAZ{4+9t7_HVXO^{4M4K@j?Gqoz5&4>Hf}=0$DN{RTL@ou`^pvsu8;sxmHWiYf`HF#L^cwIq%MVEwb( zPy#pZ5snC>-`QQVuRjqvEy4=Tac?Qp0xqRy?N7zz!(Kzt!mUmmYy-i74DAo}7k{8`{R@JGxQ1vKX za;|q(cgn8Y@ouN8K9gRAifys|YRIaVL-_1-?}I*-{*<(%nr2zm1|DF>#5tK^?3U*(^a zb#K5i(Mp`FhE4Zg>NeXq+uD%^a)>hg%6-^D7&k=4AkTqhD;wTh-gVh{Ve{A(Gq1$h zDveaPfoW8w)WzrysfRO4Js>4SwB4?p{!o6N^j0c$R2p&QGcghNIF21E!H2F0#vU4=oRUotpo0+ z+m%XFtM2IPY~`rDBXxh-k{)zhBQKeP*B#(!x2>?*! zi2zBhymC-1?XA}4!Y=)~-ScwzjVH*_wa@c&>X`uVvScoZ5N! zojZ9Oj~J+K&=G;u#7jn|(*mgm7V1PA$RP(6j6Yz8(!O+UF5b^B!8(Vsi4eF>aG#{G z2_;K7O5Yq9i)5q8?v0JGKo#Q4x)e<${WJO8qMdSVtQp>^R&c&Bf5GfjI2OXCN%yA4 z5d1uNu>86QF9rQy*a{KrkR;!5fj2T9$Aiy@qp>B9#`AIhNF1raD%@2($FD zE-Hfl|HoJ`Ln^<7HQTa=z9g|cBD}R>!qi>DJP%GQKtZgZ3Ni z`e*Q1B;SQ{A60;GXpCeiw`hm(z^G+#^oCIdd4p6DVrXa4(0jA%>(N585YNWbI~NXJ zS2}*}&2zI4962)Bd-~?d%C%Q*=-C4sz0%Bl^OjrwYB4m*Pg7kmzS3O{teyk z9zR%IH_hhKsrxsIoBMjYbM)B8v1^a%{<)*K?WcV|@I0*lo_>d9Epq3cC99RvwTN>i zb8dS&qWqG)1fqyiGg6!=m3~Dg)mCH*e6@?Ah8>f|5zb#d9FK$hQ(A)aUELEl3##St zPze$2P!JUi7g_pi@-uNKABb1-sg47Dm_O}L2|Q>i;g7M9K-mD}N`Rx)n%eN;Lw8N<0&yWT28Y1En@v1oYfm zEEEruu}Z&PD>&)-f?dYVBH`@3G_FV2H7$aoeJBnN~!|va6Uj* zSBu;fy>xox!Zi2g=B!}&0K_#Xo&wu7@OiD52l3o;x z7;}=;ZfwXL;m0+05dg;9>I;T3uZ&qJp?Zp=R2oHhv6Cc`eRtYrW_|P6(4>L|f#|?n zj%r1sZ_T@GE>YnsFnl(p2AMRCp=Q?V{UifUhMihe-I~>|lEJvW3?HZiHS$paAY$aJ zZ(jxFQ%mpGHLhNkBR_qiTIgv@|4cAt0fw6h1^~2G+QYEmZlruf0@>&}V+k!O7KD7t z1XZ>az0k-dl66T?igUnxA)X=7HxxfO=3p;J(5haNM){*%qHPAFV(tislDPUMxDvlO z_KjEs5Ix~o3OVc-et)!xl?GK~0Mi-P^%RsRlgUvYe#vo$51uXdq-+ViV(fhH z2-wAFP@sHcz@S@PqZ@@(s_URAq+KB=L}B+r*dsjWvRV-{5g&b-$Ply1p!63Ly_1A+ zitixS4U|q{JYvFRIWC65Xs!VqgKgW)^O}##UWe_)U9ITsTv7IsE*vg=z_uxa`1-9g;IlvX2YcIh17<_c>M93sKl}) zNYH`}rR)-~I#FIRb&6SX5^l1uHiQBCFOi9tu8lB5-hvd zvB`Bx2gP+%V7hZB%G~)^&&{4cgS&XW_I=_=JV`Y|w`K}snsf##c|7CI)icT5Qe%v7 zx0o<~FEWkDh{ua*;J6lax4dhEVGhW+RUdb934D7WwShR&VFtn%UbJ6a&lGj9e@X33 zms-;$q0BIumVC}S!g7MR5Dv-k=i`=eHT@EXAoHpwQo5Q!?&E9~RTpq`SEVHZn#w_% zh4$Y*9M8v%0D^NLM4lH_ND8T2&06o~K5SuN7CyCZ@W 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

sJ;{ODk46A7i zB8wye4ldNFuv&hE+peKO1iJ1F zcSq-q>$9D)boOLhYs(~ixKn|8#clphD6jeGE9*d? z-~vSbo%LKM#fDZ>KuJ@Uc=Z=xmcu`%BKL@iMKu#bBV*GsX~Ya|PMJ$)lJK@n03F*g z!!FG@`V83xvC3f6$xGtyA54&6(C2M5_x6X;A zXN}t{Nq9^YSX-C9G9TtkNKwA?YEgsX1(4=(N(SE|o3Z9S?h&&FNloF?F+C(D%m*=6 z0)lJqO3-B@CI~uY>b-x&*zMC;NkA?+xLDw8*M@-~J4(7TM#xDdaWdxDFyJK9LX>m{ zINTW4uJrH?XscC?eL(#@65?r!Lm3_e53?M0eaTE3@f0L3EwezLiDLw9v>E!`KIUq` zdD~}@X^&|3EYS5GuFZ7jG(j_k&fU+l5yIyW{2Q~I9}~XHfr-chLrctCg_mfr)u94e zAhgAxAMEj@D5={AQt6&sTz2dnk*}VMa)8BN? zIpnRn5E}@(CB3!GXVUg7S+Xx zqj5TWeb>E)lt#-Qmn2yV3bo~`IdquW$O>jC7!Z+4dcfORQ4A-=R3v4hQFMeCq%zGl z1J8x>&6kJLl`ut`Hi86eJX9ii_;L!`2ADLn3u%RsW*^m{4@IhSj`&%o?1g=&@}%tq zqVFaf=gy%`M`$jricKTWx@;1wI2tGIAhlsO9Cym2c250S%}TD1vS`+YTJfPPS%+*! zp?PZBtp6(46hSt%LO;OX)PITdRJ9q`Z?Owxwm$seT`Jvxh@C2}3aYSQN&#sATJ%ns zmH>=bcD%At(<`;t6r{`9TavQ!)O971d6h7cS>dyh{ps&I{n79F9_PvLI=!U#Bvx6P zW;NTuJ^2{|I&A7NSMK?nJu&UmZ&UUxkxjuPp*{AkeZ`9}9&oi^P2{oE#`Mt@_>ZmB zbJiyiI0{n}XWcZ3X@+K24KmrXI?eCwq0;Fk7yj8gvK+HcnAbc5j!i1A%s->XSa7cv zn)%S$kp}}QGy{l+`h@@%Dy$+*1;XwaZf$y!2WnJJ5L} zF#P0WOoVM3Hvpy(Rbgr-fi0V&WX6O6`K%m9NZdV>kW0P*xwZ4q6Qj`+5ABGH2sdu_ z(j-)ovxd*;E;qM%Y9IM1@I**=<)l@id#DYReC!h)_SXhrIvjTNsa)@&t zPK0zEOm+?o+HoL!5*2)daAnuK^PpqG$qJ@HdBW1sOEKRw6g|UzLESj54FU%`w|^dV zHN!g)X4o&NH_T@|9HCq>H9l&lhjbi#qU<)sF~2_Oo0t z{ZUT=eX-1TO-+V(3_<#aQ(VWKA&ZuCe%Z(DVm!o*X&CNXB*qBfRODTVkY)b!5S(U! zbfx)4)D50I(>+jS5M_eGn=S~`KH_p9O#5h0GXvtbhx$tvtIEt=*6`-E(b_X8Ha!EY zGGqicNC;Sr9N|F5A&^khodd8Xi((5J=nFqE&ag1ZRdY?V`Gx?T@LTXO5N-jUa63cS z*tr0bW;EGl?{N3ByK`hH42f5SgYBt{1|PGnWG*rQnw4V;o#+Z=oOcZ{%RN{ooM1GA z6wwF)&7iLuVZ%~NL8ArwIPdg@po31ThY%5bE!qmg3VH~sw0E>W&5%bUfB|=x3ngr7 z&`}~(`wop0eFTAmX^v22rN;jBvI;mraWY=wx_+_A-3+F_DQnRbzIQgaRXJF|A- zEVjCZ_{yF{%% z)hd!P8&pOMr2TBQh=LNqv+Y1h0iXwng7%Iem~G{UO*2cHnH1;q|C~LN4H6%*+Sxd} z>=6nRlD*Sxc4vRg2FUIwEG63{dFNx*@I`o(L5Z@{&}_%%LN;?Oo^8_hsqh;3Qg@K3 zJ2|u;Q;#Gq4va`V<5$jYZEsd|hl)4nliA(vA#ry$om;{#jr`{B7bIo>*IxcbPgRdO z2RKK}sS73r^G2v)me3p`CYzG#KLuoes7g`>o=Fy>7qDA}mZ&J3PKba|0uWfmmddbt zLve6YW#t+~j4XUO(ctZ!oon`C))f1LtH#YoMLlaKtubRxGH#hfo1VrI^Mf~tq<98C zzuIW537d27epK7oPD6B>jNMN?*T+ba)5tzqh=9HyaUdX&?ht_p?Z z>B~U3#7BiihOFm@Y(~MLq31MrRG?CAb!JYuwZ&0z4h`Gcz5DqMw`0TkUL_#v}xv>jbjnIO|*?7E>$^-EtGNv8~QDWb_>Eg@_{659mk3I`G- z${#A)tCQ|sxh4ZM^b zq69ORp*7lc6qB2?(DVc$Liziy^7c-B*6hhCh3oz0aCtesM2|w{PqV z>d?oOmtBAgO^7xQCd526uuZ6O(VOO;!Qr*4G^y{(Zi^F;-KoY@N=aGoZNq1E&T2Q^ zJoOOcb1R=EZ^)NlXV#15~$dvfkPZDw@yR z4Z3W`msdy&V!T@SAfU7o?oM+{*xkKSFi*V^nM=D0l3Lb1aP+>+Id|#hRcG6h$pu z@#AN2?Dgz9KXvpTj-~!Vc@q*(csSj~)xNlGM?B^?S6Q;yeEjAMI>JvX=f}XYsu&9< zrD<07670zqBFmX(YuwDNiMOSef@?JNQ|xh+7fvACf`Y z?;Q8s#$w!0@ay#MQa4IBaK#q?1>x?o!gES#uG~**nsRX4V`L?j^GPxdEB7RR8GrUpypV(qKf<$z(<&l!YM1Bbf$&mt z*(+WzIJw<$n6#GIl>l92{3R!9nF-D`QB3fK0I|*FcZXSKO6b!AAqJ| zg@&++HOslgF8Tj5_vUeOUFDs3-Fs`__o}Y0uCA`CuIi<_RlT=bt(GiHvLwsGi!j0# zwgFo*inE7k|c&n>#oqO&%=brUB&w1AUw1%T~C^!xzbrV%Mmu>`m48ZJA za13D%H2h+TQTiS(ta~5NU`>9TA|h@qQ%{BIpF(;%8OMh63#He}=8>sJ@Lq}SM_i9Z z_ERj91F#-NRKQy%4$D%JN%&A$eJr~#%C^#}ax9V_%V2G;w%SH!JKd`sY)#CSyD4;W z!~Cy~zIZ~KvZbrXBNJ|R5r1V)tm))8%kgnouhCRJlPtxuqlH8YU|Nt;U%b$OcjmM( z;DMyOhExD_Y%lRh(Hp6DK%|iHc2nbC z5^4Kf9O;W-*3DER52p7JCjHd&12N6@S)%7lzL~Ls!w1;I z5DHiWAW!?I^UElV3j9UfTpAdNS`-8%{KYuZ|S>qk|-FY zdLl2c##pVdIRECz8KFVyLSd1nCxIow;)Uz(cq{6=AgkJM zNT9OH#jjKEl;)U5pnSA z4Sf5d&K(-*GIq4WsNgfksMQil-E2aCNyE}oI`Oj5vX zj>X&MQkMx9&Y=+PdVd~A8ZF|+VctT5#*x_kVif=GUNwh~`2;d)ZsenB#9C09MmM)K zU2eIdj2B_UFXlqIZVwOc5+%`mbFk-r-aF|25_X~j{+7ZZA}%Gf49G_ItaP%*uq?@J z?KeZyxXcvmcv3ddHPl3~`R?Q(qKkQN=NmYPXyw)_H58Jcf9FlKDx7`fXXf@_h4@T( z_Kgody3(2$Y2rkL-U%if54~h|8GjP!52qsK*vO!kL@)zWis;_8^X*m+bF|KH;A^On zT_{v=p1QEHFQ1&`G?|?k9mhJ5gGi%0lB?7vuI?lkN2cqQgIBZMlJPX^_-T0g$Q=kb zXZ6nF_|n1O1t=COeP;bV6qxW1xPOI4+d|-U;BI=wG|kFii!*3f8>9`05OkG}@#bJp z<1H0sDZ;}ck9@6XPyLo0um&3NgC03wKJoem2r^)GXio zHa6~=nVy@Qp1ylCm>o3wn+NudEvEa?Ql?rd)gsmC;EUP8`0VC^`G%s)>1wrHi;P4D z?q97eEWhdaHET3cIQc=XhXH!LkJAL`Fc-@CDS_tb1(q5T{8&dkjI_tB99ODh}W zBjtRmQ?GRrx$5`FXx@wj&N)-wEzVc5)!z*);eG7kz}xXP`HO)s1pX@U4^G_4gEm~{ z+~VBs-09r!ywQ2P^8x4AoG&?lN00u%PCybsbP>xd#YXwAoIwTaNusFWXJbzQlBsY# zT~`*{r2`s)|7D_fY0bQ$z4sNCM!NfkmMk@yGD+^&)ruB(dl67Mr4QRTRo1{cf6$1U z64#biSUf9~WXiNH8qSI(@Hml)+E$jDG7mY!gyO)fz058@lZ337pgbK!gOLK!_tXQ} zsw=4JwTNC?S(6Uv8V@1bt+gZVUK>+p-&en#*G}%4+h|B?8=*O+|IHKonotfsnNlW^ z58|g7EoVdsV=m%B5p;c{2c zFaV1HC}PI=S#tEN|D2xiDPB8A6GQV{Bazv436n~lJ96>|L~s0{0#*^EBimsNd!&o7 zw&Aiwk>xHqqV{;n)A87|>JnX_VXO+^QqhymT&+|@lZq^%NFs%RbTkt)Hm|{ptN3BW z_BQMvtneVD$X-n6a_up3Qaz+HG5pd=xhGFXbGaK%E-z1ie`({W&X47?V{|Q2nAzMI z_ZhT6tLc>bnVznwpX0G*tUsiZ&@5m#A^3@aDf`8+V7Fu zR#$I((oaFYmzuNDA)wJUe zzxNOBfAI=vuf;jN&v+@?vEPFj&6gjk`xXeo;z6l)aRBg>QZoDp#B~bbi9AEFNvVy* zkEqr_k-@Tf0h^AlAO(Dv(R3!4LgqSXt-w4Woqg=5YIWPo;xi#HV^ofL2#bh95Vzu) zO=|;2&`Bl3o8rnX$*AzUxg}uUWAPQP`yG6LlLF};HWqjhykb|n1t2TF4gi)fso+Sg zizF|qJeW6E*g9PK76$t>$PaadHjT0AKwY`K^Dk)$`Mn?h_coOcNtE`XsQi|8nJkVx9*Lrwe;eE=EP5O{a$Cz&6c zZs*E@WG9T*y%Fr;n3pM%6Op0Eo0h0G=HunJJKLY(yu_v|3(fjKA?7fxLzFRgEF!t$ zWV?g&J*RwQ!&*l<;`C4g4ty{_t`z3WU@3sWbH0NGXjFMkF{DB+I$q6YvDCXLBx8I>wRP3^XylL*{`PX7knx3-oQt+V|CkT zkRoii+J$hqmNpFYkPQJ*?Xyj%B+9C|l-PPge>)9T7%o7nSp^WZ?O$mai#F$86 z6I%xeE!SSfEIX^@8!4}ftSKNTIz?p(o4h^L+AMx8?x}MY1_2rsrDLF2*^cC}Q?;x! znQ2E+IYZ~e=1V+#;Ef5V5nv}SWLgGp3iS}nYyiwGD2^)v-23} z>Yk{RtM!t37DVfG3*#=`o109={7iM`^o{Z2<{Jnn%Tr~}ap>{5oejLYCr>5k4{s$m zg&^rafbZMi2t4Tillw&Aw!poNC;e*yFvK0hDc2Yn)klAaBg}a4P{T4Wuxts=+9!up zAG>F2Jhvb^8CE(^7gsBV8Bc16yaABtIVffYM+)hYWWJcmgyOmS7-Tc_rx4l~EI`X_ zwp2!cCNx@^d)3@bYq2<8fCvG=**mndg4INKh_nh!b%c;sm1@o5n(K*@;y_R@t5 z7Z#W9T3WiW9a42vi<2c%g;e4Wq8{3I?r?FJ%x}DbB zj+LfMk)+1FJjHV_x{AKO6FyoijpUPf0(Wr}n#`8~F_*u#eD~tgg=N-=>)aMtb#Hh7 zD6nY!2up&;Z1m-{0XlnbFQS#m+-3CxI`L#z+{G zAgF5~J&3|U4AK+l92~vOPK-?h`9>GpE1O4h1*eeBVAwI2b^id&I2O!KWU>_q)|qki z*5Z+TD>u4UpML)A;6?jdi_X;8==$glKXrWJRoTmr<+9mAE|bk`xg)**GH1g(>HH2Y zxh?PjtN)vUe}z`^wC}3{b{xI{Y zJ-H~kWWf&9nZf;p1xOT#lc?AZIw~DLgn(yDJo6j#ni2UCJHX5&Z`9BDyFkcm2Gu>42uqd*bv2(3akL^ zf{5}Aj&npJASN5l6rRGlQ``u6vPtZgqfSjKp-&V>ufnT{hETlX+W0hkm9 zJA(^idPf7GkLR4sO8GGn=Th)cFht6evd}!PBvVz{dGy@OSO&vzu}EeV_5v}=mT6=G zBGE`%aUz#p zY%ErYp|d6N7`TDBhj~=9QeWBI@$f`&z?*b`KTr>x!Y8M!fMLm5i(IP+lB)649`3Hm zXrI}tv{u-I`0ngyESjB1HsH%dsW7uo85knH1Om25(y^>q!ginJ`V<{fS~+}r76qYl zgVu*i8>i2&L7JcJJI?D4UfN>NSMOU5#UkaYLV2#dZ-Lo}I-$hstAi05J~kGPRT2#G z_LtJt1f(KJ%5=uo!-pqM^p3ytcy=r^cgqW3*w56{2d~o9klcrZ5gwtsKOAr8W=?G` zV)AsX0;6>8)>9irX3WgK`z8M!ac6Iy4W^|IAB=4b=5Cog_V%O6k#u>s#JVc1PRu@k zmc3>BgG_Dow_C^BP=-oV#nyU_okr7zAxG0*Gw1NZ*3~crBo{Svb{Z8=?G-`xhfjEi z+!sK{ePQ6o0)GY`VSAHK+q2=RONU<{%b@oxe!$LQEpb?Okl7CzNB(w`7(O$G?=_Ia zuU{Y*DgnYncuwTb;3zw+XxLlIV<5FaB#J7gDk`1{Gb~PzKwGVGY9j3kf4FNgcVtV( zOHAFme9hz6Hm{yQ)Fhh=#>y2WfzWd5UDqjfP^1enuzs}JCIqlvM}nVE(DEpzQ-t#Vf?fv(p+(Qa&x9lV>#r$vb|UbsDo zDCuCqjH@iv_?3z!%&oEP!Pj3!bwgF>>CRfVEL)>6lj;YX$KG}Xi8<^UL<_mtNCjYq zQBzO}6zcKiUX1n6P5nXwuWESL@5N&0_%)uNh4ws^|GaCxPZUoOguMg zxTm#D357Bp`KJ188xvQLr(teNWiq*cUleuFQiASyWOA&IeJM@YNTtkkkZiVr)czSj z3@l6e~%Rc>+CkN*`wYl*u zCR(f4H75t>UsK@*hPZ@15KVSwuJLQ?FB29Bc$l$e13EIq*N~E7 z71-rKmlVrNjVP$;9u%_REl`%+S{m{`Yjde*gih(Y!I2NVp_p$AB# z7%^{fIZ!JwA&?}FX5fEfe_?FZh{CGmEkOjNVUh^q&E+G zWQT4^!_*{5Um{`}_PPAr@S@)0lA$^xX(Y<21#wIQ16Uc^NU-;dPFdyE?X@o1Jn$B!ck_hoNTs|Sa{81q1(l`R=$bVQzTf5$$0$tOj+_$rh+LB z!ioxyi=qP)gzTKE!2%ogWsIqU1|zUTf^RWq1eT1ZwTk*>?<4G{v1Yg}MEV4MZY!yp47Q1rnxvVej-b{^P263)nC z6$2El<1Hwb!6c?ZVDGTIMwaeeVl#3Pkh!WU`vTa>S>vyAjWK0z1b!v*4?|5(#9#p?kdHJq<1f`1 zFq&8y2)a4C6P9f*1pfcpz|HKPpA2O2TUKXxpI}GH0pf@Diji?dB(WC~Z_JBa{%~om zQFPDidig)yb=TsPPcAx-|L*U)pKO%2rt>|D6eIP$V1Z|Ar3qlvjCf7rOkAZ{S5}1T4bPQU`eSy~mekAZXk`A8^{DrY}oN%g?Eue{_mDi>nY*u{P zSBc#4q-l}sLZ^@Eo-QVCGw{SQ3wz<%@&UUA(|U3cbv*BwWmGcme?Z6%Pu z@bvR~4l)8M4l-F>QqR6%HkI>ofC0WM$S~pLITSg82mo?bp-5F+Z%HtQE`J6}TB_J9 zVACX8nyUmkjPZ3Rc@ZzXxQ+;7B1I)$eE_T0*S+ryFMkUrO|Yl}HSTcldDB>Y;z&+K z#8%n)c16L>kWr-x0*cq z*wNjxv2QsYZSQ(B<|nRXG=@=&(GuQ@;AGux_m##E4rsTEXU4? zF0W4G=S8nfH?j>&?Tv=-VZq}i2Cy$+ElAD>+Ww<~p116NEpT<<^?^Tj2GIDP1iM7h z5Cnty#Yll>JnIV*#aL^acxI-Ew+E447Ht_#=zjRh69ZTFNyQk0IpIbJAxF+Lv)Pv~<6s^?$DV*&cA>w)lrpV@ zTcg>TZ)RB#l%;fPD=pUR4NFTs(JJa;aQod zql3ftCqD_92x-}d$kcn@_0Q`WoXA&f;A{SnKDqp*nD={Unvj%Gth#itm9WCc0-#kG=brh(T#m8T5z&8LL+1%f{LM1qP>b?S1!MtN;Jb#&f7tm)KSe zgO$~EVl17?#VhM&cws_4VUr!KhHI&@Y^j!x*Hft?*6y-2jb#?k;0*`6oA{21wz9cI zJ6C8VVw@5p;ROEB>e)&&pP#H$#=+)doovz2yNdfUwi!+2<;OS+M|Fatka%Xc`(Fr1 z_^62nM{16n8iQ2icDFB4+k5Y&#uuaDQV%(O?|}2?&g-4`Iv;cXZ|C#Qm)%!F)p!g% z2(B3;ZV$4?_)>d_5c!8}h;s%|`lHW;K^29R_TWoiGjqh*8J>pi&H{wKBBP}g7+GY$ zDDrB<(}rcIEpyE0Y1vv03%8TaOy<3WBps)DuO)9#roW>ZHjN&M0c6q-&*E#8&v}|F zwM9G01{9VR9Of?An0=QeINKdlF-&ykv{`31LB-5*tGW*JskT@v!9BoGk@L9ju1h}#gm z7P;hc$Si1bK}q45Ci<(bDwLYt4(|>qXzyU@k=^zzH<2Dgdx2`JQHBg-T53y>HL9I1 zGF5~m!S1Zy;~Q=`!~U}m*~2NGCk@pFbac|9yA}z);>O!x4YS;EpC=Nhh~uasigJk+ z7g?U+7fl&ptX3JD36x!MKLxol={VMCztmYtx*H0xv@mzeVFwQe(kv2t3uZvR2*V1R zJp>E3CbI#sFkx~x5q}q?rf^hk5GsTwDv~~6LEJYmM4g3+)eH4z;-L&c4wd0N7}abB z9N}b&11(Fbp$Z!gH=?PLe5S&lZJ1J)okaCjM-{|2MiBiTv#Y5=9Mf#;NI@Q!I^AOv zb7oY4F5=v)55BQ#Fy6kYNK-3Lmef1+bGo6)N(BJo&I{f_jqz@TP7&aefTj*mJ}5|%KD#7+^TjCu&CFzwQFbq9Y&ph>-F;wU!* zTCE~Xoj9?Rm)2s?j}4qcRXj=5$+xlZ$-qc(`(Kc4rDBZkYp9azU_uy50WV<=h$liF z#KhwX;12;UW(?s3hCMmW(-}&Gl@K+DfUGsD+iY?Uxy&j?NFAwG0G}hdIG9U>`J!PB zCMwvC1ed{#1tqtV`RB3vq+@IP-`)v&6^g&F~bYsDC8O@4q(}67He3* zVrUo1nM+ii0e}OGLY^e5=snE_PV%zO0#_(o%s^wufjC(>x#Ox#9Z7D`7V58lrcX)A zDxnyzj1cA0A#?#fY6e^_17qRcQcZ*Sz<9{q411BV4l>gz$X;vFk>&;>7%(~wwKt6v z%7I#ngjhi#iv}mI6%_(~8fghr;bdq7o&?_|=DtLc`GMQQ{)i@4i0-6LptC?KJaN)$ zRV<2)MvOyiOd_ZaQf06v_C!vK8V_ElFA1m(*%KHnC8O#=Bn=YRQ`3_h2AcxRrj5W* zE{Th%Y2lT z5|Pr2qP7bu@}$!$&?e5Ofy2gV8;}$FuJ$LvV6*K3=!eJ=z!ZpD;3=h(+0y@kss=Oy z9ea=xeE4b)fZURwgPz?p0ruVm9dhikW2LEb_wb7oL-umnnDza zsg>`vJ2-(1Aq&(vI9~2oDqigJmswWL!%YmJ_Rq~u-!#KWB#|QO6gX|qoxA0QbIwoJ z*XphZ`^IsrXnTS>r=q1=O(!-28 zU(rv>8uis>Gs4=>h~61A9fGZB2#>*mK+6rCo=H>DT+!F;rsf<9a$u*9iG*)tN4~dH z2ql0JKXmRBpZvr*b8+8!>L;Ff&$ZXShs)(ZcS2*EV^a$;s1u3hDddTQ4qn6Qsx}^1 z;^jiVlV@%mrq`JMKqH7bELbUQ;$_m&i3aNeoO+leQ9LPOQy|4T5ihS|SUXTNSv-t7 z%N?|v%}<7$d92Ki`d~2L7Aed}l)1iI< zkMToI?d?IYccx2;OxMn-NgzJQ<^Bu$f}38beSDHfTF(y!u44Ru2MXmrWc;sYJwv68 zFo5! zR5-l-*SIOp$a5`ug|BOY(~V#iASEC<*-)*EOqMVr6vq8DB3rRSPlEXSj7d~!ec%;YsfVb)XB(ozR`m2$Yl94I~+;0DN z!CLkLylsrha#S=7W3BTNVNPlhrjZ$Rd4p%yGPBAoOUBNiDs}?FT;~xqQxX$E>6dtW zl3Q(HC!GRYAor^xhB2BJqj5?z_B^h;u>5ydiR$gq)KO%VnN;PY#8&6#1tc1xQxgWJ!n=P?M! z=dbt&GfQKYqEPG{+NtM{{6u&<5P>!ZvkQ!;{DWONge(y?}T ziGGG=$sZI<#*OVk69)4<;QE8fu|BwWZ#A7u%%8sF1wGAIr0XC0-gmqMUfI~smS*hX zz;%H?4*Y50tAVcv{yOk?fp4>y9>Yuh4bIKjG`hoih;_V|Zvq>~U_HuwJ2J7b|N;0t%XoeV1-0O`rO(&%!Tto^wY0 zB>ji#Gc)zE*_j4%4I{by$VlE9*e9r*j?GLr#-^vocvj8h#U%Hk)qOEH!HJ`HRfV-Y zeyr0uCKLGOBR!q++}OSq*^K~qg-Y%?CvXFjD`XJNY*#C7xYn6a2>DHpc%h(9W$j9} z4I3$&OXceEL*Pst-kIB2>c4y;ovS;|aDEhjzPZsBo@EI3N!M&-$6AU10Mi{r9oN8x zxsi<cQ0ol5a1lh!B?p}z5iPov{ z(R{kEOTIapPZh@Nx%Bq$jCLU~U}z{Bssek$u`Xi$9K8TQ;n4c@n7U+a`c*a4UHOrD z`ydoOHGK5INZ$75+fKZ3-{|2SXRAnQj75~nWYA>|)^Sjk2v;pLCiOWt(``gCY*=GtqT&atNdp%8KQpV~3hA zo65x}>ika~$b}ozzc7`FPV86QOLNijsr|f`EhX0}WU@CN*th}M8x9sWW$r`}2eCVY z$!h0kTj?#__~4Bx82?CFBoHuIzG`|rl0(D9nHs-k0*?pl6R}M1m_kP<;<>5c=#8?a zfjHCS^CCMJ)1y7mGjt8IfxmS>=#GMAa@24@4p&$lu%(@^eE#$AeeZi0FI;fO7Z>4k zFWq^UIg5Pn8^Dz&r;-GEnq|u~&rRfd1spO~a+bGUS!xZL+wNnR|JMENCtiK|SDl~! z`1Xa17i*_aR~MY?7w#rdyXZB)`+4`RpSbM0zx|2J-*o=wu^;W<{-Uot8E@`IxA%WE zz@+)!&%3|ky%>(-5YI3mM5~VFX}5-Ac)=bpaRWEwtZ4r*E7D(SKopA&SUAI#oUj;e zB+Hw9$^s5Kt6dcM7!q~uBISxwXV3j*PWzcbD+al~BBw1(YzM)bGQ@i}&ap-wLX$*! z!lA;+MMOf_J%j|X1Bm!wa7En-6XcK*PK_io)kM8UIls>FX8oS|bUB$UBQpc>2tJnh zUm$ug>B`X3=Ld_c9A}YJfxUI~!GTy%{2?3zwgI7y4~H6w1sTQ~#1ip&(afr*^WEa+ zqeq0mh2XQi-2IOGGsru10)xPj!1KYzb^c%Np(sqR%BvhuRtG-(3lAD@1+dGGs(a&> zjId11)BnJC9Fmz%g`^53)1Z*(nf&`c_6|2*3(sHt%zNReIoDr0fv;xwmFH$otT)~I z;?ZvYEw>@MG;{h*&E{>7Ez@52HIHv?MT7U8eslQL{HcC89B!?4&XkLIyL?aK!@u_9 z*rrOjbN7Gxo%qQ3_Uli>hr9pA?$wKp@wJmPKm7QMk@cB9{+hAQ*WZJ4DA8aZc-zMQ zuzT^QjnMT=H_Spz3U>w*C($$yGj9($lisVG{~AaK8pw1W4BQg9C-7k4O@R*wekJfZ z<{2jv^@rMHjK2>XC%&}Zge1RfyFfEyXpCZGrnGd>X)_g#Uq+cp z@K0eoCo#<8^KF+%Y0qco$waBLf+10rZ0fLL-7*--Ox`*RSzH`nZJ@Ip=*OLq*+#Dz z19uQ!-WV_w+~XIRMHi>4%%*OmKGms(`%O)jbghrO@cBG=#nj@A>vn3FKU>9jIg(ol zZq@2p&Oh9#cQ3rfhMhqTU$_$aVw@!N#eC-6o^3RbGG&wLbiNq; zUZ9q(W_b?|hH4THbN)+Y?a%_)v3ukW(5IVbM?ir(^^xw>*vw*+Eh1B$iYL!3!co+s z>CPkS(;EwKP4!0NnM|VE>mA&z5{g$hw?;;)BbCiT@+Hn-0TV~bfgf|w!5$VX3?nvL zlhEZ#C|^Oe-5h5l19=O2Z6Llonjg*ZMmL#FR)H1{azG~oA@60L7l`6Db(A>}!PfEG ziXdTy@s&G0XAKF|NC|nJ=+n;YpMKhT`iHN6n=@5+rhaStjn6#eJmX&d^!6K{e&*>r z?s(>hkG}ow>(@`!x4-+e^M+@hdFJ-pp9a1@5jf;E+}{P;y5IRJX+Jrib$;LZE@Hx7b^4=TjGXZa($L796ik%t{x&M;+?7O|d^Kqo;kuyM@gmc&z$ zX*izP;F7pRR)uJ34bVV#mctl81sObz%aSeQ* z1A?r~u86zDk4^?QC`QwYP*D1c*cI~7SNkA=8LPC*xQ+BXm0qOJ$j$rG>R>t}Erd^f zr;h7!48@)?ZdS05Y$#d{RKSt%SLYyL>&xy!P{TBc4!TyBSKegzY1;%v)LWGqgcXQG z@6h|S+Sl5ndS*4xN;kys2ztu)Bi4>Zs|&Yuwyl_2(#urXt05()4=5o3-eu{g`DsbG z!M|64<<~Ix#06c+D~RyTx6FT5apuR|60e6f?GQ>5bn7-q2rnLN8$>s`(W`7AZ%TKo z(iL^<64$t*KEsNTQT0&HFomjU6SVa)_kMj;BdTTU;|6uOA&pX#$J9X?{YtPGwupaL zUEH#%%)dt~%VpP#V^ImE0ykP-uGJP1OiD(l08F1zF&kUqnj}))V)71Z77tHte@h~` zlA+}qJprjUibN^mP3d5HO47hh0kU7cK3FTKPdObLoX_W7glF@OT0Gwfez$tO+kkJ~ zTs>Q>lCS?=gjCtDbq#+FgDY@55MPB~@eaS0VoL~LtAQBW;TQug*I6y-j4Omvq~|on z4sis!=zp%*C2{CaKvl&Vg%;qBI*^qhKsI=!yl#_3Qe_KzNRv_qg?m`)6Ut3!O#MmW zAu}Zsq~}#;2w<%6F4*=?nWxYqZRJRS9$C;GQ#D0(qH+)&s2#VeC$EzvEkVacL9`*Z zj5C}U1U-+c8)#E zd%R_LUxQa7!z_ip=Wh3zV7Q9tRJe0)ar2%t^Ydr!+1%o{QUAuqou~W#(|2wRZf*sG z)jT||YNLPp!p5bei-V&_2a8ADMMxGo6qHAs^Jnfou=%p*&CNaUWt$7wmHHC{tbTu1?u>aBdO-t2GA zyRC93oGlj~6%^XyLm2 zFFDO@1g?Q@rG1o9guO+(4RIaZYoR9!0*HX663E7v#(JR#dIr!RLr}O9>4~Ybis36x z`U~ED+i&sGC}9Tq#SBx}{d<(SJn#Q?Ue1!+w=lVJdT?aCF>zJ!M1T6sWa8FSf72}F zd#OQZ@|61yNy}Y+P?B@fu}_DW>v<5h?&jv8iSPLEYPhxW>2Q7O=>7G4y0}%Y(~i@D z1@D6Up8{tBUvwtTd|Vh9g*7YPi`!s)`WlKE4FoMU8#3<3*lHBk-kWij7{@E*uY9N1 zhqOr!zwMT`$f5UV8?7#5I$Zf(=u+rT9OmUM^+pyN_kPuBY>V3GiC$Y|TekQ9mHyu; zWT)B;1aIDX?G?hMR$|N)xl_>2zP|HjNuju}DAjR1oCIzZ63Ira61o*27j(x9pxExr zc%|HImdni+Uac%{Sd2YRsA46#@lLA1sO3P8NS^N&JjqqWbUMR*8_~)eEh(;n_ zA(5_R(&O1eyEe5oo@n47FPUy(1qxG7SPXOP;L6b&H$WA5*h~{tB*LL$EMAM%_D>{x zx!i0fJ(JDNq+6xJCFHPDW3|L+I9h}X3y=et-iP%k(VnfJY7*mOQ%U_*4`F2edzEIpZs$T*M|4qFoDvE*P#)QuQrRS~;>%SjT4r zhe){v3@~gsKnXXzDZ|z(GcDmRi+{}%l?v}9?Umkhgd&Pt)8*cod+)!e-#mZ+gX+7|2x5+M8uUF?cE}RmLCmQ{HGTxZVSNnjFII{S)A_{HM^oe|WHGeY*OZb#K zsD*=FciDBihYx>jaq$~x&pv+k?D}AEelQ5n&)+dW57qB=fk(Y{Bqkf!8U1+Rx7jry zi`h|baTqG?KxRfl2bf7%&qhI}1=D#Yhslw)FEhUv@WWIMw{WM}mBBV{DAS6#54y6V zMhskD8-GREm}|3iIn1V+l(K}S#LEjgX!A(_TWvummyTLIlwu=UGB50yKK6xvFuNU2aNIqR2yx88$v=&skH71XB>%mDJh17jnZT(6X>#+^j3{x;#c zyB`@_{;|0eqj25Q?c&Vld_J8n6d-drU)E>=mi`7DMPljU>`bjTmHlMDvHiA2zu$0P z((7@JQAfH;A`MekGS^vbT^Re=`- z?uS10URLGj(2M_jw0p;$73W&##m+;{JDm?<(eyu}+54|<&h5De-5cF|+(+Ff++T2? zcK_J@A5b65Uf=tD@2?SJX$KDlZw)>a{PE!b8T=2Sk zw!N|9t(fl;9wfmgcJ8r(MqxVB?=6$06d;N(Kf_o0wSXkyN9|OMbiJ}+1wpCwlMz=e zm6z7Q&@dF^7e3UHir5CJ{8HI}cW=49wwuy4nCHA~jsNd|fB`9$Dum=&X@~XQ?KpU2 zekzogcdG9F2;f19bD8Qk5ZJvvY< z(r4whEh&BfgWW=Ra%dvhrVTX=nK93f+DUDt&NS8cX0o+{g!tSQp&n9f+$m&l135{A z_Uu&c1$%s@1th0uF!U(lIXX-S4oIh%^)x6>j}nayCP^T{F_Ij4IkLt$(!GX|%8|+* zNh~VkhwZ5d%HA*;+^MjhaVWNxR5K{jKLMF^776kl+)^E6HSPJQXN#NETJjg!?zCk+ zJx6Thxd8Xl+Hl42;$67TdRmZS1vwb@=N^-LC#{9P{%Y~TmB9B97HRPftA(X49tiqV z*Ij_Ov)&m)h{Fab_$$pX2T-xo3KobDZz{w3?(P7roGU@50SVM$atJ1OyQU5?cQ)j|K>?trj8bw4E=(x8QjAa()oWIAjQ2 z9!k#8(U8mJNwBHYqvzGc81!Dwl}EoPmaaQ3s+fpl(})lf9amYNTT-lxon>3fP2&E# zUQdPTf7JYtZyNF$F)bCrXVyG5sm764;VwY}9u{pzwEK7`+BT}`&D6*{WZoafAsUIj zR~t67H!GUsk5VDGNh$6ud5D3%=9=`RbZ&-ZC<>&>=KMD=mFTSJXlZ$I6artcl7ZX= zCn*KF7k9&%XsKV3R6(wZ(+oF+6(geoff&wpJ#+(tg;rhy3z-HM_=JFg(T>EONDTF# z(X~<1hAV~1JJyl+oj!ThS_R4M_K6O-G@fP?1!z5mUJ;@3sJHzO9=dgq0g)8t%$bd1 zQ_4deI0K>L*exKxz|bM7fc4@sg1iC=tiIN|gq`qWCxjmQGQcPp%CDuJZvfDTjG9E~fp zL5@%rhiOzw^hO3n5xmBfC8|FQMz=|f$k{hg&AR_8QPTYk@ML$&;(g>K;>wIf>Zmw; zqo3*)+XxJa3`=Y6#Q2?&d>1okZ)dpf6FU|BzIW`tb(Kt}hE+PwRf|^lRGgX+KkPNV z+>bFmXWst7$ za~(zoqajd4k=T&wK+fZYA8l+h@KVCjJCO3AbK~99U6GqGH0no^B8>H4C8T;p#?i~S zXT@N!u~_coG9a3aH|IC>0{-GzN)(+OUF(-nfgrF>xwB1J?bO}Bj_&Dl5d1nIZggN6tOR%&nUkzXf(T65vuz6kg ze0LZD&l%7GV$WCSCKRM-AzG+91Q||$8(YUZo7gC{Ep~5mCJyN`5y()>Adh{Fn#N8o zO3=Tck#SHmXC-0(_Dhvl2Re};c8W5&Js$YF464^!aj>xCpc;>YSIX#OGZ=?kON(np zJl8vq@lFJr9nh{svow9f4L3|7j+U!MQBHvv2_DCyVzgyHNscj%f~d?8Ai)V66e&bq zk-bVG_ZG#dz}&5KXzGz`;_4RiHQA9^acX+Hm~i5kS0izx+!Kk2^VhmYagDP5Yf(AX zh|5U|ELw~mMV#Xxb?HPRdOdGMuTb_2sD*dnRR>fT)DY7smx2l*l5On66I+}rvi0ZM z*_j(>BqQX2qJiYN?peJAyF`)gU+*_jb=~UEjgKFib)Kp%)__fJeWMO%0(M37NE2MZ z03cbY!ZcKr`HsLHi=111v7nVTSA?}Oo4{ITG2|iI9zWgzey@T@8OvGDmJE5xhL(5? ztl9YGr+`;!Obn62M5EC<#T%;gRe-yewjfG*z6juVVRR}2aXvw?+G?#j5Fb1}EdJr~ z@qT}+(d^G}GWUal&iDSK_bzWg^uQMfo^fK#_+1V&ga!$qwLLShIHEAS{26V}jnHCs z`t(njkQ{cW*}ShH!!)(}GRg>vf$79P#8_5TUaO&1=nfh z3(}GHYSB!&5G%s^kN#Ve6XUZOrW{G-F!_ZxF3w*VrX;?*jBc;Yf+6TzM4Go6&W^|m z6*+xnPlj`$9vfAza7qU#q~NgclPqWAR0zc!4tAt<@I0BHz>*iT)>3T?#Z#zNpsk8? z$!H^jpFUQ`zDgQlkeHXqWWDvHmp*XIEg!ga^ysDQufOH|zrA#HyQ9fOeRb4{mc!vH z3;@Jhh+_MRrXehY1TafwJFm=?OvYPEA8tjndHC_#SWl5N+Tjb8n3-+1X=5q~t{zr2 z<8hGTDop!ew2b6IxVTtOmPPaKVsunp7!Fn!5lUd+z>Hlm$g^bn4lK3NUGO%eMR*jj zK-f{(T8ubOGF)sy`KC@J*Y;0M8qugwBt8h?gmG>u9kLnhguKn0L6;$C_--BaAVrTh z#5|*>5Mj^BMQ}FdwM!l@$t&$ON3si!Ye`XKf1qjc|U8> zLl$|`Tf)xK^8;@U{0veO+V@0O^L5jpYM_U?GxP0C-ys5$7*2{vI>Y5ij3okE4>Bw> zG-v$9hhW{XET5hvL9-nVTBF(~xf9lg>`sbBCW%3cM6WfxINm?^!gGDm1aw}?E=@L$ z^+3^ZGFqFK!Zm4@T1YB47IBN@24~OC0`O~XS;u!@%qg-kBIRW#8cVjSnb9QvGa2wi zyde*p$nal&KLyN9OkW+2EKE-ym8Ab`wVec=i77z%^i4P2IE}J3=HlnCYmQ7}8iqG| z?V7CIAP5?An=@o_!;MOLxgq6Q1cMqij;=35R;Zc>{-aX8{=h3A*q?7_OZdf#Ra2ag z4?J|~(q^%p&mm~E{d3H(=6oo;Fg1NO=J=;56#-zzse_F3PkSG9?>DmQBk*cJ>`Xcz zfTZ$=-j8}e?|m+K9|lauLY>fZ=&H~Ug&q#QJ@iEABjI}Zz2T39KNSOOAAXlp;(r88SVRQ|8P$iuuXza=RrB*S<}0ayKEIBsDBPx2yh^kB5qo1 zfI$uUpurdioHFHuxJOa?t}anc)f8j;?J-9=$XC>tTtY*LY7J&3txV-tdKTV43%d9A zBW1}(ab@j|Aj!Ck=?*rJ2{?Rh71AB_R%~|lfPv?>EB3kSgm!76TpUM~&7bOf0!wXU zrGxfEMMRcbrbDPYfkOt7qXSq~tTQN&)HU_rwv2O z0xkH4bROUKelSO@+3p=9>b%dzaMynJvbA9aO%ti%xKw67<$b18}QHqA$uK!>9@9@&cY7T zVKf?BVy1)CN8w@{tbxhYV#>FTPOfOegJs5)%6FGB`bVd&2-KJfCh@Rw z?KOMD6*FjdlGtaokFPgqX~=DqdCWRuPG~ZfdKh?I@nw^Ddfo)aB^{+2_((Xi2&>CT zHfTZ$@Dt$P^WzLOKzN&}T3Vn3dvun+4E(iZ=K_6&BP}hmg*Hyc)Q-r)(t_F`XH#%i z%cZ3v{;~Ydf(2j~iu!5X+3!YH7M5sEDZ*Gu`g(!?j7NiLnZ4?v32EMgjNxi*@2aFa z{vb(&oLa5ych<&+<`m7;p)vZ}?gN(KE2}_7cfPE=(k3i;pa=UM$?Ow__?ow79ArP# zDR|znPkDlOwFQj>HD}V!0+Jk>!>+P4TvdOB^uCUiCJ8sH|q=bl>c6Nzb zNYh3WNEVt)R_?$Qu5sS^t_)YUreeco%4u~2!{l~S?&g3dIQT-i&o;)arb8~SF+GBU z!5Oz0&Um&!Gn-U6^UJtRvz^T#x7EjXx$o_7Pulr@$L<7JW&-elZqA+BBv>c5Vs_{& z>KEHvG+NCeE18Oel0u9WFvynX&vJ^g=}k4;O9jL#NT7(k^q>->>tYH18P=k9TSO-va$&fx?RO746<`2R7U0N`bn&dm@b@@C zbLv>oKOj|;x?E(LEznj000BE)5Y!R>ud*OeLq`gxB3@;I4%c35+3Z(LV1Oxr2_JvF ztJl^RNI$}~6BN+`u9R3b6$z}Aa3dY&*Ka%lvllQeEiC6sn71thpLq!(b_A|HO+zD_ z!dNWm-;M$S)`I(OgS+mz90on{KPDg=mZ(^wu0dB5)r^p^U-P5jm7izd?>v3>gW)}e z+p-7@OWX4zy>?he(B1yVFs#@;4{Qz_18K}WQ+cMkd&?Mx5zE`jzEh?ia+=Ts{~rR|W=U2hx@T;l>=3v4z zou+m&JS(s#xqYG1n@CYqRLE`yg8rSKVE2(iztsqa>z%jY15PAcG^~>06Mlc;$dm-K zd>xOT-7V3Eu$yVCw=<#CDF0oA?37T<&S$p>6J=&3al>r>V^fTOQ4~S_a0=A8?i6MM zewLj`+YL6>f2NWsVL1J{CDF6P$&=j2#RV9NSF+NGMi0TgV}oe67=mxS$3P~ zpD(!XyO7-@1!SCe$v7^Q=5#Af!mRsl5@<%566~9Che&`AC|a;V)+!RYgKl%E#Zt zjj}+bg35dT5<|{u`w6uW6O8%jN9hk31Mw;KOFWFp^X)${uXv7&YOgQ7Zxn@;gnKtQ zIO_+7G5Y;qeoRN0#s=NDsEnPDZkYZeE||8N7wkM825zGPe4SH@7^WlXmEWgqa{7+* zrDzaP?XppfyLLqI$cH;wNC_syRp81LW;0QiU5>-sJ-cxH$CNCx6ciME#uxwMm-2$wxauzKl=C2VBS7G)e>JY}Bl5P=4 z4u-Wed}4q7BJB(O+H#}zYm8NQ5^A>Xt@dtMdz7Ef1Z`!t8lP3!=KPTj*dfXEOuar2 zh3YrWqUTkJg(ELczoh$^JWAtykplTQEv9gO;p?o{Oa-1bXUyE#9RXgchU?!1$;)I! z0h|KG5TY{(=~*62AHfbYNV5@bZ?gi_ZvSHf+}j7FgB#Xi#Pcc=utvS4WQRX}radq99 z#^ENkdlcNMl8V>Bn&lc5cgxf!?dG$Z;JiRPlHqgO$!9ZYa8ygK@ZsuprUxdfZBNPn zM>JSRjTsIdsi@uXJ$jYtO@Apc?EYfTiq&R2LaFt&Z=!~*Kx*5_HV;b`)5Wu9{ZyC0 zpSTwuRSz`Q%bh_mctPDUxUp6);tqni@&?I|G&gIk?fQ*_k53JPgU-3ZRC&GOUVmx( zEfNRUJTUL_-)7&bE1km{0x4kT)HJw@jOFfV+Ms_=3)P28zy}8)x(BBCFh}=_*JI6y z-IndXj*t@Y7J9P7OD*sorrk{_bjhzZlj$Pa8XL9RCh||>CI-6R^P!(c<>?#kiPN$| zATNs*^b6#Uv1h{4JF^N0=I~N6CsDUt8K&%E=s%Kn4P`|c8W2J`fAk`2+(ner*?1j| zd5~%$-XDKWJRg2=``g84`gOIVGe;hK^g^SU4ZbcJEgrr8mYMCx9*oxWud6n~&OiB4 zu&f|`x=-*&QpERkQwZozXgVFKVD7Gbhz|88^uO&{`}$oxtV^ki==oX znBV@^3^D-o<;~{&FzVS!pO~p1ZuOgo>rg6ZzW3+Qx@Y0|#LzIAGxiyFDA|xz+Q`)9 zmZWoL8q2|?hhcpwq3PH}hsfoEVNO;!B9WS-ntdYr@q$j1$idM!GlukCy*%&!(e@ki zgI4RvscgJ6D5kT;^o2OUBU!Jv>JCa0*>oPw{7!kW-kd6Dv-x~Ko}S9Due;5TH~ZaB zdS|}-4+OoCg;>E^+K?>ICWriU$Z)D^zaUR zDpH#|axWHZN?YYI^n(ru%HBoyTWHvv3)~gB2*2PXflmaU4*XHzzXkpgj`fH$;~Zqh zi){>fNTXH37A8bR!{rqjV`p$}*a#9cn9wJpmI$%p7`#M(Xl5nS@pHgAQdrJNWphuQdj%P%pQ`M%B?NP+92ud9Vy^G6yQPl zoDXvGWC5E{6s%*mEe~n}qLVa2CcDVAKq?Uec4`H?Z^461MYo8UVBcJ^A#-Evw&r&F zO_VG84K(yl&z)^2%29NAVb&$fiLJv|9mFa<`pWTA6V7t$`TagAO6Y^cC1U5Nz?v4; zU(4gMQlfpfKbNe?mJ8oRIr-8^JW^<9L&*pXk@;LZkJ2&x?`R^_2g!;T60m}coqQ68 zG5cf=`NTLpUOXw<7zY>22@1%C(6>v4a&64A3EL@9;Mg^t{6wz4Izshu8!~;g+g(`z zt&0{=s*450{cr=gp0NxePNIMaJ!K_6PWYy7>sDvL9Brx}7a_*n`|2IhPNI2mK%T z{IGdGb3a?X*?{p6Ez{572n0Suh#wk4-CmJzwLjMDVV*-l7^W5@YI-z%wo+)u~ilqJft=B93mR>#v-M|eKSNPXkx%vi4?=r zi!ja2O48!QuQ>Ukx3h-V4D%i@<7UBY{xh}7Mgp_cwdT63HgathBRs5VQkW}wM7T1P zYg@-i*eB77KxLMVO5{xv3UJI0DQy4*LevP1``D$3c?qj+C!_7A239p8Y${3zxC6M+ zLJBgdq~dxCAFzp#q)w4#&0{e#m?&VlF6b6U9ki>lnH(;Tjuu{#DaB(cbYby95G_O_ zg;p-?E+~6FET>X;Q9y)LzK6N73 zEJtIlnTrJkUnSRE=)bt;!Jckyb*AIB)ogAW@4~zsKD<{*dP}R}4?A(>bg`xk3lra#A4|~IMYIKHBoRz5kPU7(xKP~58nUo8z&Br=^l|= zgjkWU!Avvy!jWhS96B5v<2CR?lZ(6s&$e6u647cXgvwe0N!(-#OM_su<5Wc~Q+TP^ z9r5O0zDXw@+8K< zG*gri2fQty7VhDvX9_<(U&WsAa^PEp6#=W~#-f~w~nKEJ`qm5~&3*gzZl>yAF z&}i}KXm%SZG_099$<*4JQJTQwL$l>*Zki&PbeI#n=F!{NoRt?ndg7Yb-@fjwzUa}D zSXA@2?ndM|R=QO-n#1YzkyoUWgXPBNIAHo~!OlowvQUXT>3TLhma4`|N#ukO&d3Mr z6-+6oJ?x!UN;TI@p^}-a6@usj@7?}bXQwHS{{_=p`V1a z*I8WEImsQB7gA9JzVsdk)V%NVLduy0gN%2E1br1Mw%Z`8j)%ubj%^ohpmON|+^x zKp2lADLl@F~A7HFlfv#&lFxHA5Uhy4eK&AJXHw5~}F=JT^;0457;+lVQJfvsl7rbjhDvkHCN4=X@nF zMLlhouqhbzXjNG3G#z|)_a%khb|T07@iCZ2ryUFX;mLwI&f)W}I6Ie&uQ%&Gj6J)~ zDg=T|4Tu|0jNtDam4DO@Vj*Pr+ym3&J|LJjFp{Z^^O-!3!LyvKv3V-@Oi(lslxCYxLiJ%cCMa1sktUqS3lrJgSR$N> zj4X}HK6|cHE;KS}W)QBIKo}C5U_cbPw{w2cS;F4&mjaUcq;E{GupP?IIF&F=IyOJP zLPI%rf|Rhsa5e|oF^AG%9k*y4VG)V>7~alHOqZyObwmcPQe|d>-swFbb)JvSnqNWY z=t{e&ZnRm5Rk33cfmDeo*w|Fqj!m4{vG}Pm4^Tfb4b%y) z1Zp9CxIlqIAUIJI5>lF`jYAVBki|QGt}`RaCQbi)fA90?V`p}DzV@E`KIb~;T<6?% z=g)9o;#a!zz`H3_GIo0yo*9Blq?!?&L*GLCcw5|ceCqBXxrO*$gVz-fyjLF^SQ|8s zCL9Cm9BFHARRg!>L+e-KiPbi)UJ0B;EFP#qEd0yi2jG`2r*tE#u!%2EC4DhTLP;qK zt8*(pRAt52g+7#6yLyR?x{%k!R#uu|#eLLR(X5nS#$O@_S75sF71worLJzK3Mx|4r z@95khu`R6>sy`e6J*>2sReZ_SmGWWh0(IVM6BTY#5z!CO zD+&b!K$K`i@mC24LyX{T2A6~#0`1haaGupTN7Dy3)vd*i5FEP_mHgz!fCYWq(8QXm z8cI;t40l5p^e#>JB{wcdiAWSLLLwLn%Y&;&E`4sH#`WDwz9o~ym%z!xi=-&D7m<<5ImmC`3a8E&`l{u98m z^)yWdPQ+C&S5+jO@HUQiOF01)h4e7hx%(q*LJcEGg%S-n;QUEj_0rku3Kns0{pd%1 z=h^6A-hs_*IV?GzTZdZ!QsITwH|}IqIs@!f$Xm}1bO}<|FnqtkO<*ixSOK%|BL5D! z?O5Cna;?7{aQ}||yVliIOYLTPjV*m6_oGv%Afu|(^BZo^Fr>o^IV zH1N>0DJ#DHZKauuIHTFAY=Q0CGY({rlOpK}JCEGB3!TN+a{ib6&HRT@hr`FJRIvV3 zOBEYM{$}USq;nU$CCQE^oxfy*Np_2K7wQ@P{rUgke$CgS&lxzPjLOhK=r*9jI8}SY z^Rp1MM)sxWJgI}GadXh{&9aJoeKf4-QN7$uU87$&e$DD*cL_rN5`kT%WR1KW z5s)c%WjJqS6|FDPkyK4hY8!92^M`i@fL_t(H-Wc%J$SpT;OUBD?#?)BMfsT6phCmo zSHw&la&;5RKf)8xK=(xQy-`Jxezi%1pK3u$4k_-`bw?#ve zL=lxG01pFFU#C2}<8LxVul3xdnGupbokCg?Hf|K8w27r zHrUafN_BSxvt7YhZCXeh-ciM$h3H)M?6h|>$fOeAit`Wh`}j}!F2(bSqt3#o@hwqr z1WZt95D49HlvJM><$!|$NhBWD8|F(_xb++lyc|Vcx@xotc1FEEj&!p~MFdd_%0d|! zvcswamDEmUNC1hmmXHxjhC_ms?d_;9Psd{E^7;-l*~_;DjgXNL_=sgG{C+uZg#3z? z(1jZAa7>L^UEOI((?gdwYlGS7&YjWhU@Ti6%a_-oECTBEU;Wbz`pfZGoTe-v6-@PSTe~E2_Y4B7mC;k&8CrLB$QA z6VpGHXQ2+Qsa2{*OX}qG8I~gco95Sfvv?hzS%`Z?)-<_z9rwtrZQH5TaFTwUb{2P@ zV<$#OooDHNfnH{8duD8mySILY)4rm9RV_QQqMkbTdK~tcO@~ionM$pDr?4h#r zzv1uWE8&f$nn z$!iK9Rq|>iRL>{hE!;@{72J(SY?u=$>__N#kT3h?toS&tl4}|pnC~u8>F*7DWcC;(4+H_w0!g{$sIhSE~}VBciPhx5n*^kQ>Sg8QX3ZI){2B+ZI2p4com^ zB3t~Jm5s-<)(rl!#=3ON>VU^uH9GWwwP9l_sA?vQ3B5zv>`<@Z+!5>^+ECg$lowSk z(;UeCT93!u6V1ijyyMtFq5~&Lpsy_CVF<8VsK#1}EC#fa2C+%2ge8uuLwLQivI!lf zp-G*W0xEOZcv;N3&ABaB#>O-32IoNtS=KceJ$%^6J9p-dLx+q$Ho_+gHO23Q3WX4N zRZXGz421IBi4$|qvN;wpCXGWz9(!VCGmXQN#d}$T%&g@uH{J)3p_cF({i&5a@*LC> zAfHB5q7O82SLO50->{0!*lFozR&nmHKnkEVh3rHZ&AtzhWP8{_ZvB zk(XX#*S!YY-TB>d=Qr$R;^4vVgWcT+>DnBw{SNX`YM_TDcf^RmS#p!wB=YKVaI&D6 z`J2zZq9~Kr3HG33JD-(zs)x8UJe1Q5%8{BA3b(d+M%XzJW@m9e%5V84&i5gFt4n~n z2~sz*fZBkerdf4WS3wQ3**H5KI`=||earb8_nh;cY?l8}kr@W}H^mz<^2Z7V+?W6S z0sim#LA1xEIESm8m<+u{U9L`qG+(=(LD@rE*Ku8a|GQI3JCDRzNAAt(w)CR|9pfDx zy@h?E}-N+MIEnjbtvl{kz}4qqzT?@x$Y7ZFp&?HsFCpeh4hG+N}mgeWA+3 zomYx-Nt25fAqN&?~rGYz0jqv32^KV$VuHW#4uR1$NkZhQ;%d!-pf> z$xX?v;jmKt3#DI4DO;2KG1GIlxW1G7qYbYmu}kzYO|5g$^{X(uP93gq$%K0su8tu; zfAy6Na@OmMfB5Q8LXf_Yj~>2vHFwgVnh1xBf8t(yA{D)G?L%~JV{-o5^}cKQJ>27H z=NhubD!pc{sslnmqgR1~?8zvAZjw*o()z(u@0=R6Hy=NBJU`t()sMeaYrdl+-^!K@ zKEHkY3)}O@^Z4udvw=p$<>`tm_cU3G`6H1C3y=arSno<&~h(3n?@`UeCx&PvhxRu>}l^C)jdL7lh zd_rMo6~$Sm*z6=X>aHp5b>|v((rx<%Ik05Dx7MNjpNu&`EHJ5?8ko#qmMgw59Oy!l z8poacMQ_d;U}p#TMA0dC&UYPve=Aq~8;{$1tsJ3RX^Pk}~O9hHE`lLlWHJr3huLk~a|OgH6*FHfeo@7$hF+BUZ>ReWLK z@Bpy++2S5<8~e^kW@|dVH8TR3o=UO7nd}z-Kz0Pme2|{?Dc|R~pL1V!@jV(6a}l^T zxY_BqqHn()ean4ipIBJKdptwm`SKni82i*XvBG_gq=*q z?8Z9G@z@TtRoL~ytZ63y?blP>|86(Ov9W_c^v>I9(|YpM7ue_8qvJbbZGDrGuI~DQ zeDf2N9kCtbv9{qSlesNgE^Gaj+kfhTk@xT+NI^JnWD7bhj*%`>M{1zBt#@tMDY%Ot zC7J^P=cfv(TDw@m^2*^Ilaan;J~mmID#z>(D);9Yd;gGRZx#PZnVf`5IN( ze|!^rWYgvB^+L)nIIGk|g1w2!FPz`~qA)w_$#d^}P<2Be5D-oV$e@D0?8@{7an5d- zVP|L94RrsI=Am%$ODfQmjXN*?4cCY!!M)y&do^*dOK`UpI2&Z3*GC8gRy8(QGOx@S zFxZk@V=7hrCzdEoPji1ZBjTi~6t|}MGIyq6(|w`eu|MXs>^m;@!S`N71tkmcU~%PN z=1ql!hwj-r54Ny7=F!dv7PwF5+v4N-Yxojqa=-u_5DJU#%D)UPxw$h(US##ggz@H>*xy1(RU-}Y}@a<*Y^O< zJZ~3ZEMx4#T9B(TjJ<#hg=P@unU|0f*?I)f=zlF>TWP>Wb&W7e8Egu-nm8dmn2e2N zU=hq)3p~95?CLzQ2WVOeLy_Bq!!|3 zQ63FxvSz1T=x7s=6lLM~)~&pdQlzXFCph zOKU_vpojMBp;M7}JxKbzsEBgF`Ft`^`tXFPXtil$cc@{TYH+W5){|MQC&t_}i`3`p z&;Jd^^e2EqD#5|X7h{r77}QibAmw!)o(sxMwYotmCbd1J(nYdnN&RA2@b^7E?mg9KMmlNc|J&_l}^ zPy>`Vf6>MGtaHQK8s`~Y=>|ZC6;hl{_){roixhW$%Y=9hJD8fk{0dW0ir0rzY^XFo z!fHMx#HZI@a_+keH&N;v2j*G>%yn@+Q{XP(2IxSMz902MxKgPOB&AZyu+A!xYDxdx zEz>Z;u1(t0T4*|?;hQ^l!ugv=9Be81iZU z5TAnN1bV{E@1)$|=>*=Bq1Z@NbEN$O5amz=w3%OHM|3;+k8jLaDP?X~@||}N1&$W} z-Hw|xb1&|Vok$9w@0gu!@9GW?bRPt~+S;3W$vJ<@9@wVzj!C%##))4P;M%5V>}%ht2X&n? z0+dB%pg}<3p{mL7Ibcb6AjSd9Vg(5klqJ^G*yM@;_$WA2L6Zg8+1+pN*zq>2pjSUw zD`n+Ihb07M32DcHQZW=%jU3d7zgc+VQq?X5kX&1_fbIjs$rnUT?${yCx_riipbu70gVJrJ~3| z`6?x5Xz*iLoer@s_sIM zRC-FJfkCfoGO%_)&ZC>1v0YtMeBYRxGoIiRo)S4@pL?A?f@I#)kuJncS*XV1Tc zYM2S{Y-l$4XJmw!>KbGob7$7^?8v$o&0PagrzL+!d-H#uV2{21;)}2Ia=7=key6GJ zot)x)8irocOQhm z!g%-8FkBQFt;u-Bid;AZ zdt7vgp1`~d7atN5REc{$RbzxgwV~R$M3WuZ0T2M-;}t98p;}%Jg$&7`3WijvQ_?Yf zWKGniPQ}2)xM)Ag2+7L-d}NK^{{18MP!QfgKkS0A@391YWHnGI({X249ItGs_~wJG z<*RRe^PBhm@IiL{SH0)EU~>jYD=*k;ux;yr?g_rBR-BvsTemx#}^wG%pui35K5mha|tg2ka>G8zFitax& zv5N)JVLbJ3@l}l$cTWL7b|FOzdVwQn@tuwL&U3CAg?PY1UniMX3ETNH(tVY@_5O7Sy!dJ+@Sq6ocw)I4Igd(QmU` z9_@~98;BKO?asvpwsAZpz4KsDOkyu~10?@&vD?@h8;Ip{#z1r%JwN!XeEV?y&qJ1L zn47!}+(@4WFboMvl%%>aG(Uzth8-X=+~#Ghp>$P~1+0uYsWEi`1S+ryHj^h7*|m+R zHbiAofnu8w3@oW`s>TF7C@~Iyp=_c+GQ#*6^oX#q2UH+SgXeXL5N9f7iI|U5#vl_E zNi+R?ZP59xl8^%-?MiuNM=X~4u#Fc*zC%|17&5%wWlN&+iq$b|Bx?0C zD8WfkrE2}HlHAhMiaoJ+NKQ;xHPA19*ijEbUsb~=uo}J4Re5+q^uS19G038TtkpHB zD>)ak$TlEnt%coamJcYEx~QivN-#ni0`qpB zYx&X^7an8DUr*K^yA^}PxvTJ#no$R3(h;O{Lv_xxG;G*Vp+Nl?FcNzvrc*UF5E!-% zf#K}J3*u7jY!dgw`KHh=6=;_Ldh#MFC?Vu*dE=7CCjI{iXF7|EETiDo7#xK7zW@+M z0OjoDm_3Skf_vUNocBvOZ>@{StD!yC0+6BH71qFU;7GieK&eP>m<#@oI+zLRt?)i? z_4D!G?fF!x5*D%}nM7Y!FFw@q1vm>2kw>wEQ(H%}0d=J?+?DvW?KgXx1$aU9Y+o1L zjpS22H17JkF8D!%7$vTyPpvGIZ4V0{>K7J46#*a~qQdjAhDix3N>6bu@XmGIwowX$ z@*h%W)Z!W7(F%12N)tvl#rKBrMH$B4BmC#MjgZ*BO3y6_yzjZJr*;@KGsX^Xo3~`2 z-f7Gjv#}jJVzXG;iDts_cL6)=z|N>bR0%gN09l;~vPi;U>mts%q;rm?5toTaopaot z;x^`|V{Y_W(9N&XF-s6#X(H2xv9Eq$KkilM8N+!t$`E$(yT`uy6B%%4Pf{)rEKVaSF2`4{=?fIBIw4cw^#in#L+mJ#?^&Sa!ILzXu-l|U3)jn!Hc z`3cs8L<;nYJytS)wan1@?DS}_Ra0L(hse$xu&rd)5A)7`Sur~L%zR2wL-Jdhj!}Cc zmk0zRxuNu~j?92zwzZi?m!yS4xlFcem-U&98VI&(x!7n|C*pp%KgV}1cLKiQGW1{| zd30RJZG_$37<(NQChiVt+E|eOabDTHWo;yGZ_7_i*e$p18;`fxsnKh>Z5y-3N6LdA zYwql2+FN#fYuf6K;`~b>kIJD(-h+KEFG?dXw(esv+U6~3IQ=kY=p77K+772&i7S!d zf`UwgvhyGiN?SY{XKjje1+eXNOpmdzIM2v|2v`%#ph)S5+cxd} zaPJAa4#V$DYCKLZu&;q^)EI^1H^6|0qEWZ}uMPe(RA0y^mTm|9y7~!oS_;w9(eMJeEx}EZWJduh98WN zzr!)VbC1DzhLlpd-|h8wW_MsO_q<1a?fh~y56#cwt6aia04+xuB!lzZ@#DwYIE^3Y zxx2pc$RpSt=bS?9q5}PsM`z;MX~55CooBhn>6Im1oMNDV{R^UMUcITyaS$dOfgoP+ zb)#`hP)h($uByQ*K@*^AE^&-3JkFajl%7%MTxUV?Whl0@h48deK%5#VP?-)FxSsBC zxEuBncspmilgZeGqN>W?$P7Z*EVWls6lucw4xaVLp^u0=5{AV7BxY#wp_B$y~pWi*8B-#}@ocP<>ok~}ylHU=^v5`QqysX6^3jwjR-`34KKctPs#9iaO%ICE{=gaN=9h(%RHP_qSriZwVT5ErQtJYVa_k(~J z&t>>wZO|XH%ovUv<6CoaJ+~3f+oxF_9hPSKTRS_(yG^ruyrZ+#kG{GPllq@#C?a0Q zM|{`%KI-;o*So*aqscRGkxGWpAfYH@SvkL?_my~$2(P>S_utceJp9c)fLY0&h1kY? zv)@gW4Eh-Z*J9xZzQM&GkI8`@xFUTV_MQ=-%o@P9X9@HH3wn!_BuDAJx5Cz?NFHwS z?d7lGZpFx74!sbAHUv{J01ss)EcrT+HA?ENE6GN%sANw7Hb%VBMD-8ysKJbgTb#7s ze8IF&Hpkm2@zH-Bw`pB{#Y;U6U0YY*+%!QOPf_mUtVQTOez_lFP<@9F=yDj++P>>pekCu^E>Wi zK4^ifeJjAD`A_UCKqC;|yAf8)3vYzChPl^ z&NPi_-o97R&F06QFkQ-BdVPDWz0Z{u| zXNHGE^`I+_5^Kp)6XKNE9Ol+Kcd7|ZOQwWb9nL3^KG;MGdwfz2P16egO3^-!yB?>?x35fuu?Im@xt8z zGLPBcZ-Ua3FK*_yoI6PW3!`@+lF#GM+_J^YvlFR8int={*P}@x^F9k)(CYdH!r)k; zHnIk~4NUO{c&A8(QCpHbS0wfB@)B9Vi$FIvVVdOtVmo4>fqfdG+@#uVw{6L!bEn z`P&v_Xo$sz3@~cgb!mt(*FOkbF$-I9DR={yLAqg{YZ76kRLP%AYP+tA!c9#d<}eL9 zpgJ@WJ0IJai{$XPF_znnF_MC)a;~vqsu%20r@cTl3^oks(!zd-dXcAj6#jyfwhgc~ z?KNYpFvT;76aK^e2=PIZP04nlGxkLfx8js+5 zH*hcU9RmX$W+r1MN&_8zIZ3iaF>R=lmW7uwwo%dL`dlIwRmS#i1~Vy{jqHRKt@8bo z1If00E9Q4@730wu5=dey2$@N(-Wc50E(pPNtJLS*l4Tp5Z?+pdcA(8T*fdx0dvVXE z4|aoh&+fF;AG~Xi2C{h9NDXjrdg1XBtbW*3))eBYCkxNkF9FL(5~_bqJhv|Zxo zAG~iqGJoUNK3h+eyEoqU&IacjESrUYfbdkrcNM>rdmi$dkTdYQ>s$cN$I~XI(7-Cb z{`J+5J;r_#z5o8`W5vVGY+W;_-*J@ZhR2!Q(L~Ddf9fw> zH6i!H|I|^^Hua!C&^^%B3z%uyf3t3Q(UXrCi@~A3W1BZ0yIAs3lxg#^7mwv#`TAWr zgw{fnEAZI);GzTzxXvSkxZz{D1Py`Mip$b!!X}Ko-%mj6CV_k$#50ugqbd>9{?Gfh`JG-iu7_{{(Tn6o3Dbn*S&)qYx^YAqA z9vpjUJ<9F4>io};+i@A}Qi^AAUmetIa_d*Y2JhlAZ?U0b>*yIY5+4i6=TEPFVzGgzKzF#~#9 zwIMj*US&0=s6=%^J+A3?-c}52$=J+B?t6A{(+N6J_sKV&EWT{xES(d1Mc;C8a)P~J z>{ilxAeLzsg9AcPE9Yf-Z%i|6GnPD@ZWrzRkQvL)?$Z%L;}?C8m5^l?TG&&_Rfvqw z8wp~aMjSQDGN2Sm3M08`fbP2(h~-U~!Lp+B!2Jh05&sE3{LaI006WiRl2$4iPfmCX zmg4!8X;0W@($5d>*fGc*G1_#!%?K2~Cnq{O5;AvrzC0KU8(>cajWFoO#m@;MaVdg2 z>b$?2=6UK9@D*VkT?hHg!*8+-PXy6DC<7UO@)9xM-ObF_R(5j0d5(J@-QiWOxJP4& zHVyp_W3ci3oBSBBV+5|m45wG)+pZ#Y1{VXdpxsO~T4{h|K8~*pKRYQqSK+x)YYcnO za6(hi^w+SU0;POL5I1ODvXXAmLYr9GCZ(*lU6AZ2ZK=kxWUWkc9_!I_gWYwIzVWi14<6gqKJk;c+ddV2V|(F!)rhVnyd+=!y{}r(<0>+i1Hr+;mpsj}u zaIdU#9%*+T(YcgU{T?1Ij(H04>@&at0>w=k83(SGM~?1-L4R63n?BleG=266pb(G{ zt2^6rlzq|pi=!=P->^QC=?BH~_s>&@$3|qbriMkb6}Xl2=gE2J;S)HI^B3%kI8U)+ z{zUFQeGKi?%ljdJRNNCb6I9f#^1ID$tV=>^{|EK_nF7* zH*#mP#fLBlCUxjD#UAc@+*b)Wf|LA@`F=dfAaYq+eV;%pTP0*>%}3)APr@n;c`|RW zM7M_W0xqE0uc=S3Y9JL1p9w|6v0P8+xdf1o02feQ07;1~8zH?_}bTqQ-j@9fBj` z|H<>jQ3gj_c|PVmM@A)Rg4{>9?|@#qMp4EF74{kD9*FPF2hjq7q8O`N&gzgqDpeEFrGyZ?D(@?Rj_r z-~I0~UVe@7@_Rm`O8VA#=s#pG1`@qeO4Lf(jqeb_3@wwoyT{su{#c0@}vr5$$ozA(_Ystma!ApiO zON?Ew%@)2dk9%8=XG*s2Mfb>yi*ch4wpxPE~Lps-uPTbu7s#WQ2Wuj8}YR5YxoeXHXAuqor8lqrY2!mC-xs0 zsIN~L;~#idG!|=cu?E#chHkXa4Fn;e*<`8w9>rNrH>^x5+~<^YufwNYe4$5y4fNwM zh&PIJNiETe*$r&S*#$3iZ7YwqE%%q2%5e#c>!;x^ zb#}7b@c&gBhuo;!anGFq*AR(Th?AFcSeI4W_1gLA; zHNw1dH5&(0f%K=a+tBFk1upGQXnd%u4BbJuyL1$Zpi!!2)A*1gcI0gTae$BR!GTs$&utyZ zCu!`$%Xi-6_IdKan-w0BdanbL4~fD~7=3PsWO!eKhHu0_sLw18Cnp8Mg`3r(&Ec**!)@Bd4>iWjTEf1b@WUY;`Bk_wFXxLhXdeJ zKv5wdBZ^cdcvl+NHpx^^!Gz|6qpTDhU~5$gfNG4vF4PEYiiodfyfn{9Y~Ud@|k zYU6ILf6|l->Xl(|3Bo#Q9yg&%fl+FxgwqhY_W7{#U0F!=IH-pE_vv zS?0!rX1qVv(q%+87_oRu3va$87yoL}YrSUH8BW{y>w2JGe_k?#gU-GBK_Mc&kR3`T zhttWf2cYXN-2XBlFN(rmbmxIa3}fiJ28#zEtCEv&YXCr8bZ0?u9IoNE%_+a#YV6)^ z4AINhi*BSaiQ(-AZi-E(XRl%>yLDUR~kSplPol4^X8F;Z)UV z#W-@Vp`kDZO3A#R4JIbWW?g_%Q|eTtrJ@9orwHKNDI}HOx)_e2dZcPq*8kj52DXHD^O9672L0rtvD9)WY;8wMeZ`-MlJHIizbIC(* zNHr4v=$@A>wijh+d_B>#&%7i#mm*tdNSR9&ri(WNzC1)}RKV{JN?@cj5mgk` zIfc)s(?*LVYvEevvyFkY&3zhQ$9a&gqpud6UHnSW7|0TxNzSH1aPB0=6uq0DX4)WM zZ)&{OI;$bv;Eucb91tEo!Ju*1ga?3Fpb8JZJMKLv0KLQAsz5rg=-&z{MJbDh z>3KmfG={~PdT3aTPMhXzR7_46SbX9BfRit9ongk#-xf(GW>q3I4l$)R;l^OqH_cPO zXnqBmW;X%j6;W|=kFs?#oAEoJv%vXe8!-DOvidyM`O(hk?!EBVnHfHrZ|6Wg0ap{v zFBEUs#BPWSVDOwx3f64=Y?m=%OvF0*Osut^$5?7Xf4+IKKVRnZoJb1bsnQsNhXi+t zF~!e!W;cx(t%xCoUAV7cyuUGxi0dv}VQ*n%^*Yz$M!$ttqapzI1IE#Tnfb8=HmWj* z^mwH(>pY`+`#FEc2D@hljv5cZ)#d}nSs2?Ck@48EF;u8*f_!rOE8!_H3CLkdAu{Mu znDlU9x~dh<9`M&1EsVnk+#+Q;TNf>fv{ljs6Aj<5QM{d+Cr%AjNTmvKVN_94ssiFX zB{*OBXJgj1DI@`goM)tF=LT0)jTOJdc3U;9W>Q73nIsyM`;!76kjVGRWU4LB%A8-B zHO>}x!0V&n-tj}Qeuyu3po3M$a+!D#e^SkwXFAN)6}IUF^XcqZ5QJGnmj4 z+vAE-8p%D?*4kw>mZm#&)$R*-WV$z_9pdNzgCF59b^WP8hZN3g)Vu{h5^?Twc175p z)ojn=D)$an*)0)gyA;Kc8;UyH2?u`QJe3q2Bc_qNh(~}Sh9VA*xp{GwO+*L-M%cb{ zh1G1kGqIZOqw}2b)p8DZX8wKv_gLc+hA0Ql#=ZP*L1$$eD=WUqSW$QWQ*(aFoq6xQ zRnD)l?Rv+lXBEyFPhR?w>-aGLVK-;am6s%sKw@$QkyWWu2hH)U4z!{=Kab}5E%L2NphvmCQ65k-VdtyO z5}6oC$w4&K`B%yMg_7iMDc;lfHj+pFG$^r?`XQLD;Iec3EMXYC%^qb5!6FlDL80AcXfFP4Xzghb#CxXz+M2n1_cIse$j!0Ip95w&#hG3 zRAZz$D;zvno;63z+BVf3HnZY^gA(4>s^eq1g9meCqyc?sxCMh zZt;!2gYLct&g34t_XB5o(>>&(qp^x}I@iL}h;-kr7uDWauq}yCnm`p`z(ZCS2b-R% z01%GStc)$XX$+RbbT^^NKq>vc8 zG$bSpBcY2*tz0Dp`O-W(z?raf4Z-S}fIqMV?Pms&_UaELQMFT41+x~V21`o=CkSR^ z%AKB&sg)~200phYbQ%ep0_2Dgt5uM$2@PEQ4`?|$PUM4SI70|~l#AuaSqlcrByjOn zu{7XDkV|}5;~66z82GUo-+m48_@FQ5yFOQi(Lma-q@fByF#t#vPn6QJOw2crEuP;I zM$+wtQs*b=`)^4v2_cR2AW-ic00soglJNK)6WOq&UOvp$Ew}v$a4Lb9UQHHQ>wYCPNTHBVC^1!3h)Bjg7F6TRU{%^S%{^O7j8hQjZMHN|zgm2wPyb6*Y zDgp6U`Z|wb|Kz9u;M5MNVo)d9S`Trd0-c1b4;qWDg(XR#-xz5FDHu7D3M! zLRR|ltKf5o4D}2x zfJ!h_;YA;a>3^WYORlB(5Fd)gqEz7}4nu~p!Y9xFHUBZR!&+dTHq^wS*>!0U!b)#2 z;s3DKQtOjj7fye4>G_1xI6k&$FcNuSZHa=GfmzS-AQ(^}x{TqEa@62H;lp%463^!+ zkmPB^DLqV$wQFKa4063*Z0X6Fykcu+TQVI)k&fcaAvF+dF>8=6BZQ)~NT@-Ry)MS0 zniSL%RGfwlx(^iTYZLi^)E-kQ70oS!<9sJj9?IofB-Gkb(q`$wOug!-@*yJ+1REDx zo;|FZNYjxMX~Vh6O?sU#tb;Zoocbnks_ybGy}T)mS1@s0>mDPO1we#GB1#>~091lP zg*mRECs6IiY8;>9#g4|2LytkD;Fu_3BiG@kc^B+I6pxl!f@_~c%gI)e9!=HAP^Qd zBN?M9jzmMgEy3FDehCaazFZVrBZ*5jD>yZxTEI%imCzrDdz;OWB$%o>36nI~6$}cYpvFt+O;u7bz>7j{z$lZ& zV9Y=zn7!TUu1pZc4h>aBvQt0=q@LjEjn=M)2cijafoo6;7(0A`8H0lA4bUiY zv`ZbuC>xRRg^H(j@S@0Ar@#P6DpaEnC=;8g@xYVA&{)-g#;dDta%VU(8>Nm<%!ok~i7Ck>k|`C40VSDKPvTg?n#LnKMt+`xJeuLPJC<@`adepAi!!e*8#J~v9&@aEoW2Ep~I0jzD#lDA9m#tO{ zOP;El`D9qJfcZ^u^uyY%BMXO-A%W~Y*=fX94FLnQdd+|k@0ZFYX(d=ziy*u!r@S4<(Fv;z60 zOA+;nxYe^@x5MalE#Z>J23KLj`AFghxn?krn~;VHpAI!qDcn{n8#2wKmUWcf2vk}Y zS`rY%AX1K(9x}TXp|rnWQu(DFZs`!6-E!Z3&RtVe_n}_6Qc)kS#*7Yr=dQzRxuI}v zg;4E$BSo_M#`CZ9S>A*#8g=bDy%@-8WZ;1v)R{@nQza6718kA4i40bg|k zWsOpd7cmpabod$3i`Z`piW&~9$`g~;BHF{iG{UJE-@ao5u{QgCIG90Zjyp&a3)o{Z0Cn+5hHjAsR9*8Vj%`#bD#g4ie89? zzIl{AAS$RU_NF8`Yn(Tf>2^_UPl+8J;(RR|-}#^N&+>JM2d;5hQUMqh@_Jt3w z(o_PwqKuF74ULwh0L?T35@}w*E{utro{w}U%+M!>cJ3PKwvZ|Mz&kJL2e{M*Y|IVq z+C9`AFHIubWN5Izy=I^Myz`0q!W{2!5KqZN$<66nsU<&_9(qU_-)8^l`Ot8(FCD7E zrVJAJdu&Ovdzx)~g9`J*)HJlsTGUWg~o4zvc6|O)dN(-n>CJp(fN=O65x#6f<1#Uj(;6&wu+EV9* zOQPj9;3JGylqq^CyX=z7BDK=(QkfE#BuoTVdo_DdhY)ZK$Q&w@wirQMt)KiwZ1p%P{Cv&YKog4l?&$UK+_6KvVDLwoD=Ui(yOWVh@ z!#;tlL4G^cT!T!&90d#cJ{QVrdZb{Kc{zzPF0ezXj7y;T1GR5z1iu)f>MDX^vX!1a z&Jy0BL?8EJ$X`Y+suhq1e~eDAaOh#kc}J%sPbVpnCI&+NdJg3cuEeM#fC-q1=#nQ> zlQ4+LP$211s<1fsjgIYlP@2pe;6^2t9w?dsO9T^}M)upVp|kzXt)D?(b)FqodX@Hs zs_PxdeJ>AbN@;LOB+6Ujmc4Dgqd{BfCdc?bnZ=EmJ=m@|YhxXWEm=hnhuV$HRp&eT zUZq<}!4{Ych82k~FO$s>#uVblkQoE0)fPjvBmv!?a_bU8qQRE{xZ{?kbfrC9_YF{C z;D4x15U%3IpHMhbn9X;Jfl%KQ!=YiBYpfZm z9eHwdEjIUM#Q+MontIt}&_9}FyE~j~JJ{~jSiiT8oj`FWJ5@-=fY*}nwFW0mU|eu7 zye*pqS&}*rI#+_W^}@mKAVVn}_jxV&tS)@j|ouPnN?ar2kz>wQ!v^EGyVJI6+U))+?3G4AH#zKi}-@Q3p#yA&!1jP=KD^Cn#0~FWu@*%C}M$+{CqG-OU#DBn*!!zK=dyta8K9FPDrJQ7R)4>=;y`SGC2@{ zwFJ=X_oF%`P9_K$E%Ib(uu2fz&jfhuV5U)N7@<_heNdGQy1rj3ham|ANkQ`DC5zAf zV%+c6YM@LdLjQ|dOlY`;1R7eRio(LrFe*oW?7#nWkI$#ZAoIh^kpa%V$WQv2MR{}Kc)p2gX6fr!sZ&ec_KUC@^-~!)9L?VbSQi@!Ca*J?K(;&NYm)h() z=MlTMudy|qX-}c`Htd+{GTQ$0--oh?5*ctK?KZP}PVe0qFE#Y!KBVBuD3%on$$FP5 z@y)2U((EXMLNH?HrC6-x%yO39d2#pryfSv+%)EiK=@LI8N>cmu^m=uSnU!NW+|HrPq+3en~{$stp z$IvIy4s+)n?t8odzc_K68_^B`K39gzICwoO-BLFnroko!mO?|UhYd_lA}Z~4p&_`x zRC`RMzw`Q2;iq)I{xlyzEKr%6QuzL@k`)pnrm}Q2l(Fmms9umyh}=J@muI$3*v@t~ zfp+;sj34N*4CgU+wE-g74soerDuU72LKVa^wulO1c~6f)neE{(fo15iyX%mp2^_7J zK;*zT?4oG)^RV+N@{6J>Rqw{)&$HZjixn*)ZcQk|Us7<&VfDN-^Ga{8^T%wY3&POV zkMlAo;6~FH*+(wh15W3X#nK~0r}>)BV7Bfi7x!iP5$h0DP{~bNiCA(y&*{2zhhS#fOi37B z@w$K2+BK!l_qi8~Ci~%QYVqIHyk<(GmC;mn=y~XJJrJrrFw0(ep6>nYzN;}K&l9K) z-h!S~+5oZB1W@44W=3^6EJ6*dAaZGTB)7P}OV)5bhcw{~n9vQx-)M@1;=z5ft1t0q z8+3oPcdTWot+THqBPG;Wv(aHFrVvb+m|7~98R^RoXZTCgxmb5+5tt>YbjIw@o2cy3 z*>9)6&-AOPHpYA;cqfe8R5;Xy<4)ovSwFW<#}VK!%hs%_mBBFi-=K;+JZng zRBM64pfIlxS%KU@B@~S$@9CtQ8h%5P)yO+{%7}k2E#A)E-dB8;{1AL%L@iJc`c_~x zHY^lox)qyn)OzBQg=-I;*Qf{0QkkauYNKLPvGHDaML<~U4_QK}C+dF~wyKJo-b$qz*TU}B+_k@5ga z1_0N53|~dO+b*EJ7xHC(cljsAg!JzO~``(}`%(+*8U z6*+ZWElw_OlJk^B2WOBc_A~?w>Qq>1RcLgTAjz%CLtV-^{{LNRvBUoIh_~P_k;=~d z@{y^jNFE|Oj#}03UgL)`-&NRdQ6k=AWF#o&V^i7@BX8*=UR%#|Z!hwIUCjGj)PB&5 z7ZWY-c9YF+b?yYIS$Zb9SeVnvDPynS|Nb#j0|BX2Q94z{=_dON30dHDTKs)eI#fde(Y3)Sz3s{%%Oe zqJe7+ffWI4A&QrZtlWZ27$k{MNB{1RX2Ky>t9~TAtKYd9nI3v)vN2SOi8z2oYRy#E z(1HRJo3n{Tw%L0{^Wyt!=l_}C&o@Dflw%x6@vPl=HBc*ISE8|^sCgaeGH${HMSDo$ zQ=p@a^>UBcN*MVa&Wlvb26YDR(!*eLXa^$um5z)l2aQZfHEYUHX`E;K^L!YJdi7F*F1j z2VW`T2IR14VgZjE&)k9o_y`k@P>gpiFckCv!2{HUz40371a{mXdZXCz(2c}ep*Q1MmF6hlF26>d%VHc`-?7%3|GfU9NxW=TudoIveT zTUAb_Ql}I(QEP=x$SGV3G2@_fJ`hxbrX(TSj7NLVE-!?YLPhyy z>y>Z|ruUU9Bz=WY?T0J8rmVIe>{gnCneO%e^S=T&xf;@zaz-FbiD|>$FirT$zt86Q z>JU=9*e#ax*W9frDB*s;AAtk^D&o#nZU!*WMF3_YMU<)L9U@lPf`>zPky=&C4%@xT zaIf7vtaPVFpVGJXr9cx`%CT=@$ z*K(JM3L4tQS^1P)0Yyk2U>GqUED8go#)RmoV}+#oQZzM_usR6+x3NFpootP2F*}#; zY>rvo$#ye7c+bwANXdWWMWIGs`r=9UrfuprlBtk5)xjP*64f&=J`~e|*gSi|ieXg4Puob=T2@GOzasdg0# z>7gv4`{_)|cCIZXM`n^E#1p=t4=-Hbl_CoQ1j)Lnz35d}y;w$9*rTItXcXaz(GT$9 zsha9GqWCyS>Ox0_jP~j}f?toSqDxVl3iqC5m6%dXMuEC#22$JG?AC`*g^kwK_MGx2 zTZLmcp+w5B?CJ-dC)r77PO>k;4$HuIu_5n$AE@}^xmRH-z{IXZWTcD>Gy>nDE|!Fc z3JHjgv=$>_J-2ohm4&!C=rzzi)#J}iO;2`rPx?hOVX8rYhbT)T$Pq$RDD$@lg_@ug z3?9m4E{<;%E4nAAr+RwmNK9NJNt%H&!vf~y4k^KSP!V+{7%Hm`hBZTXo^8Xpxa$17 z$VC)gX-)N)tB55)fD}Hfil&m^-ypNc2m1vv+0&KD^-fMQXNcZ)Z^XN)Lv7rV)?-82 zeYuIThnYV&_<=VVcP)rD+>BTQwU-8eZ5kCIuJ>)kcm#E_!A*l}!bFJ}V_0~bXkuLC zA2(5wU?oBf7zHwI2~;ms^{4vbj>%3QACIK=CNy&UYe%APbS)x> zj(8iU$LJ`E4->Ed$KXp=0ZFR!GdHdCt%rX22*w0)LI}B# z+o!4z>~tP!X*o6jlg2Wgo|tImruRm>5}kIM@7=k{zcIG=+yg3CjjEPE53^TrT=74` zY4+-=e|J8bPPe4f+}F~lT27@QE4mTm{5W@xFfv?0a9Y&CQuDkMO~5QXCFU~W{x`y@ zuI8@U*Y=7-edV1acCc&jvHOOx;2ukb-xyB5a%!_Sb^4oOJ2W_TY=5t8(Sr2SLSCMi zVb)mXrP$#r9`dH>EVt2~B8z`&e&H-53Ho$zd{aK&`@|Ey@%*NEuk+88gG0G{kMsY` zUkmT%O2imV*qav+7^)M)iHKTk;(rrVu3#cbMQ|T8pb$K32=mt=Zg=i1OQ)T4PdG=y z+&^W8GMS+XddXP1cs#cyXPUWqSC;}_u5H)ma{RT5^Ho`7D~sRi--t*pcenG^`6FPk^F|PB;Lx&}0Hj253JMDJB@uZsb7*dyfW&=GuDROd8(0$Q{9K zTuM~%*VmV!qO>eJ(Q!R#YWw%2rl|f@#15_$hs>;=Nv_HPAo;Tk9C9;PAu87MWjiIY{_;iBGpb+ zY;0yonlO)~33Hn)=DH*|p=smVTn{L8xg>+$3>cy8mKB-{tc0|53Cl{ySV}3S*-A^t zC~N!6(w1`jYZ-K2bojlWBinI8y0YEZ{Ux@1baZsi_k7Ry`+T22pXc)s{I)jrn`N@aU`Bkp|u7nZlYH3;-wx3x=2IHz# zcE98?s{8iuO_fHxjfH$-r1v02okqG^rtEJFn_vLDV$ci(lpZOiR0_n4ap>}9%5rd@ ze<-;d@5M**5%k4`6W#A1o_;w9nf2sD*Wf~9;-LdH1^`y07ITy6!yVk0;PhsS*oj_K z6@Mbjr7!_{RaHD95oApcbmS#bPfA${M4p&Tv-*q5H>T}>F_J*6TcWci?tnYZ6u1*I zhw8ro#q9=OmL#}?#e$Htz&YON3T&I4EFM2jrLH$%21ro!E}HXsw9@>VchFLYAjA|- zDf{wc$2Mwp0-7uqjG8!;#$2H?TC@om3 zJg@GE*{7!~PwMV^qSkJ`zTPmFQ7OKDnPI5cR(3)}?EF)HmD+9ZR`%bR`aiWGJ|BuNc^aJ7oC9x%Nql-;A*o^fB{9uSh{kb)8)zMst@3p+RpR%ZmeB^%VT5ujf^;u9KQGI<-vzIGhE!wZlK8DA#1!Zvk~W61o?(6B+Cs zmFHq(wznbe{2R09kA~*sObo!`R64eDyA&yrrO+KZn7}4YfE?cmR%!- zmwShY(GHZK|5&nq?oQa3k!h;z28j;#Klj-H^k-73E)Q)%#8|aO5DIFs|_AI{1 z7g?!Rmn%|`G$nB3QQ;*rVKe!zq{x>1HKA3r4 z*Ppj%Ue!jjKb9Xq-k%*GpJq&L z&?g5SU%`vWlPtbG;fI$jv}z3+Aspqh4kkfN4G+>}f!Qe52^sLegxO!{au(I2xr>jnj68sJullDy;lE*j- zB+_P%%1Hmt%>9M*E>ZjQGIXUm0?XH0xhqK#RA1nZULV|!^J7xWZZ;~*=W=WRba0xB zIPzp>EKBUxht9f$dnB_)xWDkfR1@cZQ&dGbqia0678)1#t^&~}Nip}A<>1ic9mgpD?k zU5+USW}^M6gX8e=6|IA~at>D7$BR&EJ0uv&mo2VFiv0M&sBoiI{+uIu0|12yM1I$V z$e!l6zfMlUAonPo7^G>7mjq9_Nj#&>jfMZri5PYC&}XtMI8=6)HT*tIjCIoY|+ke&M+B1wlySBS8r z&(Hb2kpV=`?ssRHJ^|(y05v-M>@+)NhkT}-gk0k)0GK2?lJ8h5OsGXLMPOH1%;n$Y zBpM(A?0qzbm#T zn%`I09QxenLYoWw^3gRhc5bl00tMBYo{`~rWQ}2135#k20o6Nul9}`EACXhrcg5Dk zbNdRLOisC}urC*16Px#T+FVn}4jGwEn-5TPt3@|6S#Df-w5vW*;r5uV0X0*7umK?x zbs(}P0*=Rt22*uokd@1=Q=92O= z=l#9TBT)a%ekN9oSf%e;MV_AW;^egbhv;E~STDfh_Q~lz$ea>2MLt4Yp#}8Wl_-K~ z%4v2&ts^=GbB6>Ijpi6W7l<P^vExPY#tc7A1*#yDdsStgvvmMU~&R zqTYN@7UX7R&n4tltr^)>IAP(N$K@4Rq#3yNam!mL3cE(u^dlku;!tk$@L)NT#mMR% z9Nv^aMCq5fpPDaTJq}Nq{R?lQm`W83-UpEPOWyulxHNhkA7Tt`Ui=$p1%Wtb(kfX- zbJ=`zCGRx+WvD|K<9Hf>wE4A@H~U8a=G4iXy(71-`Pz}~-r++}AKBqu_q7?o?F`26 z<=py6E;_zUfdiJT()ALJ1*nfL` zQhDR&ed)=`G$1eyf5`s^>vs{WOO>-mv}CR>nz;N5S1XSC!+7YOq+^F}L%XO@-766{ z`Th0hkrZ?>u;$LgH>=;7cM@6-DTl0||G-+lg1nM@Yd%NcNW6%2Hu^{ga(0EIA`9{? z+ll&25Rn4T?NKU=SF>~Qr3;+G42pv7V80xdT9%8}HWGmNdUMne|txmbi@ zn>D8bZlikuu#)npxefi6OG|ekJOJL)pD zkUijS6ik^DPxmOQYotEdOJ`m9pBgrJG2s1QO;+vb(k>=H3KEK-Xxn-4+zi0D&rB0?_v{#6VqYE11 z@&gM4VpoYOyGBNoEA7{}+W%tz%T{1RU!+L$9T{aa&cbsFHUEs_%m)tH|H^q&$j@6C zbHcYOew!M&;Y3=){ox30G1ySG_*300bJf#EW--=Vr>P!U_R!kY_4YG6CsNbn>9VD? z4yD<%6aBUJXyW6}f4gbu*|m!Qp=E1#4DCpdPp2mO?N3{=t3H=ryK~yw+d7drnz%8c z>|gtw!};QYFX$WEesaDqaxAd-lk59C_=&M1rD%nSFbS69L#lEd6TKoWgIs;_m5FoA zfoWFbPR(<2(AgmR6eLK3uot(Z*gdj`xoQ$(xSXDvu9QH0Ih5F z$M3&7ynLB&mAZPkd`;Q4`^0M}cAt4c_4yLv{QhnEu-m7HJ}hjZoLSuwc2cM9<&>n) zwzMiu)zg&V*W~OkRzIzdi5bk0=*CVhR!Na@z%lbYs^6cb35EyiN`jJ70Z)RHsz#=k z!w*!)!s`%$&)2u>(6ujeq_o&H&iPP+B}9R!t_n#FXGI)MyU}r)h@`#;J|Y(KX$tsE z%<~N@-;7AqlS&QhF~z()CtQi=mh@Mupk%L zuCxEP+a8F;6l&BSiB*0gwZSaN#{X1_nXp+LP`4g@nSx>6_rBbvgzSs={&*rcb@ER8 zm9PKuIP!KD3oUGy4csNRU{(5KMkw4Wfz}zO7*Q)rF!2asjKcuq*e@USVoX3ghhvoI zA^76YYImp~1W8A7EMrXQsGSNxcnv@KE6H>rZ(HHx~+<2fa^u%3F49 z$a;VZS8e~I`ayDb^Llx6*_+FB52wAwkzE_Q?LQR9z|=-A=ipjrb+WUO;%W^t)*NRk zWjx535LlZ0Dwtq0K7?CC$>Y$OmOQyV0&JzA1#A$E13qA_yIlDC0OOB?pK_iP#3LoO z1b)1NR=l$ zvTL;H$#2>*YX7t8iI>;+neoWBN$C$qwwACHrS9}4NRJ*k#Krz#QS_)4Vy3j;9iX z1NYx=tSGICPJT1)^-@V>lg)>J82vN>nu+G2_KK~!r7F=6(uFc}^@gA`PQc(?GnoKGA zeOmcY(muQ)vqSxrt?pAkB0T-GzYq!LGxk4iPwd?~G3{iKE~r_{{cPlZs`gZs&&X_$ zqC_6&2N1px;weOiI_0;a6UB#m4|n)aUvdK-1YdEY8t2`L1_=o(@B+w>NW+VI+rR>+_~1;7hQYj za5keH8~OT3`ebIaeYoGFxd)XW*tc%nsO)|j0M1>N=l1V^`BlT;@~Y_G*Rp;M&E@0P zh)-3tW%;WOiDaGkTb!_Ol^>C_(VALqA44MI@YyrIi%PTLi5-SSz?H>!XflrfF} z9}}H`5rHxkTqn_^Sy>rL2fT$x%tT1jcZKy-SpBVdHXC1K|50Qp;Er#D)8m^~FQm&< z;~KZtP#aUPZRxgd)5w@L-Wo8`Xxm#}j<(~)Rln@t@~SuFN?7-J{JKXO)_wZRx?lQG z#RpPLow%8{qPReAnNhu*Jq2wE!y=TbsbjS9>#=H6?VF^jF3qoNHCn9Ox>YkNw(}Mh1*uN>ax|-~Z>;nOwtU|k(h}C) zwAO9^8M$3A>rjch9iGPh+Ddf64EgPYU{kiTj)@3B`uSADl-|T5Xs>261~Ic+6?!`6 z^gWh>TY)fuV@VjH+Mpv-7N9;H#g-*p{ky1GV{%O;uvQq<7dh&PI z^~M8F9j8np0?{(dySJ}TZe*WQS&VFxBoA%xj|uC(p>}Aacfjv#c(c9Hz>ZzX9hHq6 z(}{%3ccdlj*%jO0OV(5JsYrx`wt)UOsi!>tfFz%&pV6lVZ_av^c<=D0)Kh;Gd0)sI z2o?8?4I8W@8!GqlybaHtKlX<7Y2`@@nHL%Wbzfs#7g;48yuZ?Xt*Jg4vG2>N7lTlKSdLMa)GSi( zAU}^|;KiU2ih8|#rdA@A)e+@dp$q4j#k0o7i3b&hGNVZ~QZOEd$01Urb)C++|YMP6Imz+Fnxb#6rvLIVCXk;LE(nplp`q=MRk z_)K2AJ&>V1Y{=TQd;fvGh4S8fVi2P)Kz?)1NFf)5nC!KRQR&qA zx>^Gf%o;e!r5Ko}tr5mT0jm=`7fh|hF|l5x_&VcGwAFUF-;}>7rgo%v;6s`DtGwSh zb;_{M96fsMspIyq{HIR&mD=OHIjZ~<8FXVilEvwx)5YWt*-+LOzl^qe;6QQqFZ23Z z*4QDfmsLoz0myF@gGAIlGNxu67}RBlD{smA+jfR}B6kB962u?XDJDqq`0MfV7PDL& z9u4i?G(KNd-o=P$q1M#xT84ElfCk>iY`hRYou`P&-d3eGGdyj1QUrwML-quF&PA;M zQ8trFq+HsXOeQ-{4WMMQI1$O1W+pNpzy8YKsGojHpWZXLw~)&2E>3Mpb?+P8vNqLw zZ!xuYOD+YHjCwyVIKp0qYb=4Dn<2LULDWH(1>A1AHf%(CA73AnU#5REK2Xvl02ks@ zfR74ND)ELk005*e7HLpYCKLC9-~pXfGr^ZBOH=z-Dn=-t+6b>l#^cynmOhS1IWh7J1Mmhv&Y6qH(R%2 zfv}#@!yzU;K05nrkZH=;oZlbp`j7SoeTR(oyvzIarGJuoab4=&-N@*VGcT>jsF$_; za%4c23pwc6Co`jzF&gCwNhA<|rPUEI>VImFSaM-ZsHuVL?iiyVWW63O5&*5DeB?x21FcW{k|LNW2n+ z>UR1VDKu*H1z6d%_rzFov8pQH*s``%AO*pm_;q}2F8m0O;zy7+Brn_syuO$LRY!sV zR++~R{!`i#Zk4vc4=Qc>!s?wpJv(pNxthP(tF!zJjE!k4WA>q-H0JrOG$y8w*@sqh zQ1wW=KYR7ax@?sjd%@dZ(+;z?)`JfuQ~yH!!Bk*H^njCPeN3y>brf?2eee99`SU2a z&HyP8o@geV{7}|9`>ycSQwfKMpC0Fe!d@RZ_xLG##ty$9tJDB`|Ax!z>vlXS3_Z1HwQ&I^^SR`(d9-Qdhs;9oQQKBL6H@+HUW=A<-P^bHQ3tMfcYaMG z9;2Yh$*!)%P;cLwz43f9+nwLg{Ue;#NAsnw)Ta8ns}o(;MAqj5rXO3G9?mM$uk;KL zUH##w)2YH0<*w3dr(b66j`RG}RlhQN^7Z10V0hrwsp`wslJ__)D%Vrq2z}XIV}U!~ zM~rwW^7?%>!!;kLl59-_3%#TzG-BuB1CwDV$Sia_Jn{gH2{byx(DA0VOYSF1j>MC5 zq8VgYX&%Ob*u^TvB{fq##mwj|4q|F)`!q@7K$z!%2XaV99(nJ85LmKvco3uCt*f_M zf;6&fA z_2a8>WD|-rDnF}WHOh;VveV<#z>&C=I4Yd!h+)gaQNOL>Q(ly@|8>L9kC!%{eC>Yw z@$M}NEs-9Wx@Tf=aN^XSB9Jy^x_2^X|7%8BH0arX>gAn@FW4VQa@3Y?vhoL}4onS_ zb0Dy1XPnJ%Pc!rHH?&7eO8>@o3`teh-`or=aGF4$T#LOqL_SS5PrD}0mVMO8N^~kNBBuVb zSR{A_XD)SI@ymWg?n9-1?&sCNQ$NTS@~Shs2-+UJx6`|Rvg;5TjdwlA={e>Rj`>}V zsZkopHHG?tqKH@VeXV9YK8p9kKPub~CZAUeW@Lj=ss%W!y&?!vMZH7{rfloSBl<7h z_R~W{N}*d>WIt&?trV16Wh&>C!su5niBKG6Ago;SmU@-_9qf^76qmYA)j0pzbF1rS zPWA^gU!!HS?GT)eRJNRdaqYbqyyz3>Urp}k<(f~<)Gv1G3CD#bgp2@{d_uy6j1Kjo z*`;496H`5XaPU`-&QL8>+|^$icgp@E^=E62h#XytZ!k~33cV(oP_w~CYA()#n(-H$ z4{8Sch~i?-;G-QVvn&ASd^j@+jz$pDfU1|BLHFtmQaLdXsJtIgv&(@ulWHc|=2M`Q zTSB3P*Bgnalj=W~WLQG^U-|FQgnr|x3qz25jkmT6ba_Ld3S1tWg&rf1a_b~!|BiZ2 zk~37bHO&P9?TQT>?C1OX?90Y*^S+~?lQSN#tZ`GHM`bKE$((9$6y?Z5m2b|m9Jn1T zi!_X?NBgRGROg}2x^hciMq0mGd!gbyQl7=}CD*iSf2_X5Os*ocu&VBz%OvPW=jz$1I&@Cn{vV6Hfc^PWI_I9(_Z}ve`e>7b+)*ysl!=Q=U%bj^-Zpl(R6TPpcoYKcHM&`H>aN zDDTT>dwa9_RGIHVcfc26eT$ZpYl*uSih4OkF_HXqwhY5ac7syN*}v%fJ{MIUiI}U4RkvhUvPKt)P~h-QqiA7{sh}m?5@CZ!1a*Kz_|YW1hKB%_ z9U^CZ(wQZRksm|8VaS&FhQx-;E>-1iqkN(?VEhRTPEp?ov_vtb{CdQ5YaB91xU{jd z^l%`aNJbLjOu*7(T|*nU?HHpDC%ldkDDS-9tS@OsG9&3N&|bi?%) z?gU*)vgAU8>{o7^PUgP-3G0;fys6{9;?AuQk3?=93A8lVms-Ci^Lxs7l-7d%?APsA zc7-z$^6moijKjWIVQ9nX*cgSWS7_Q{-vmhC>yLCqtPnBO8+IyCWD7(&sg->!;7^6XI7E|FZnoPM-;# z{Z%)XFmR8SSa)ahkwg~|v|$tT1WiJ!rD@eDs1PilQ-KsL5)n+$X1~6s|TYAE>-Smn&+ZD?Ye^V}^SZoFyBHY`7=tg{Cr;XeqOBS>-~JU=Un8eAL~B z%57%N?yQkeo9)X#``O&ObxQvTuGa@xcYBC;Nc{%!YgKdQq#}vrB3z2U*{M1tITE-A zknrNQm-LQVM44=B7v7&#;iW_tZ<^e&VG^MHBvr?(J#_QQqlo(0@rj9--nonDCxCXL ziYj40VdBdnvBJ26`-|;?QdD+COUE6spfyQ0bAdQOQOM-f7mo0647ef!*e9C_P z)Xf{#CMT8bWM*iCU3c4dO^>DXP&aHYBj>l<{{W3yve4g!#K>61-bh;cDw$qZ92E$J zmDbX`Zu9ygamfh0>Pr({fkLlSb-JY7bla($Z$5R~*oks<{rYJ6g#8yop=dXzVoNy_ zv4)fvpw&oOkxZf7mGScRjQxU3%M3x^$MOXIcV=C0G}^l^6D_49UXsA9p{%QL|9Yq7 z_3riei>#TAO|~J`?P+%Mi1DYx2S6W428#RzyCpML`Ux=C3^hPT5o8L$t!6_k z=~+mM`U~Bch<$f38%a|z;bp4@!uf1AS62S3}RJCGCHZ=6&tRF*%yd_QQ9OO^#;6iP+x0EqS4N%zE{NH)CY8d*hi%SGgQ5`xN`@_(?+~ z8o1SuR4Lv>KdX8BkoLH?kvVIzdJRa%GKi3+st~0_Q5le`f!5{#i9iz7Y3no>NDd{s zQF9~d%APj-MXhZTNNyA15PQUar5j4<3#d$bU(!*jAOXr*M-@+0ds9Pu(uO&at8&<#b;m+#!9M9$ zvM&3fox1Mdcu&)zr$vz}71WqDlqxs1wln!eY&aRnrl$1 zRMrzgg?qI>!^RRjLgKcqP6jP`xTEf^Q=@EiEWU!OG7DsWpGeAs;ACl0CKu`j%Okyg=n=e<|BiICGM&P6L^C{+(H`r6(PB zHoAfFemQ&+TW0tq#Nx(|5=M!IYL28;>sGH$}7rCcH4Oj6ZXfI>vGCR z2JOGLpRTUey7vEQQo*^J!$4?*5y3cO?wD{xHkfW)qBB7sO9Q##QPyYIfBnd)HkcuP zm>tm8-|@hU+4WC7IHC<^r!qHZ`#irrb&q{fQazngKb`E>Hyqm9pP9NjGdQ$&NUQv> zWX84WflUKx_Vf=<4eIL0laxcGEglHW*f~#{;(fS zVnu5e?1`pnB6iFb5DHWY0iF1Hu)SKaR(S)_x&H2vfIriMzOYF5gmv<3ael@5n~6rX z$Vje_Tz`qOchcPKE!VI)iv$^>W4pd>Lv%H zHd8}OICj!JqYu;oh!T%_HJ6boAT;A(0v(|v0!i#LEH#ExPy`asvrTf5#ejd8hxJr_ ztoUJ;Nhvd`uH^&X?r1^N3eoO>w+NWPk~bE>(m@cVz`~U)WoMTL1s^*L@}^q@XyP1^ z2!|69EfTZ09f!dRckR6sm2Z?a-6=v5%b{+=Y~hXOVgY#W<6c+!AnO`mvOnGH(PQ}* z%9G`yx~E+B=xpFX$j5X~??FQ@Y$2z@Z~wV=m-ai%RFPDIlL4WH877`=F>PQSf@e^;Vvl8rlUG$*bJJ8msTByeJYvU6v`^b>)ou@+$k8 zLTS~s(xrAlO^#p<`6@3=sE5y-d~$sJNtxHp)FAkjR-#2qB#s%U@&SkfN5yqK%;G`T z7?rCO_+cGRK*lHPELba+!JR2qeEV0slgiS*+j5g5*~z~9r+PzWlGvd{dy7&WT$Y_^>m@dpBC(o+qhtGtUwj!aMA|E^l6V$LuB4>e43f~x)3=;Tro zIeM2}r*$rXaCfmR2X2 zrM%mY{Js?I#rZh=u3D@t6wlJO@_(>P@#!ogF8CmE!KzOOK;wVTr=tn0Cte+LnEt-Y zhfAk!<+984RjXXSp=ISz|IlTja%!l~RVwNI(}|9w`Omq}9<)m}E|(s7lu8|iNQ*8I;0jw`EvBs##k@lB~p zalyFauZwZE2{cDUBogu6#XHNx@La&d0nbJF-Nrs&9S)ZY;kydqb;DuS){7lq`=6n= zz>TJ~3!_bQ6!lQz9xOw$>MKzhI7x|t;@w~!i?P0fI#R!>p_2~Uzm-{MB}}R;X42N= zzODAv$`=_pxo)>-aLce^w@Y&-MlK z>5Q(qGSQ9eB#5upJO$R~|5g8$SoM2o7hz7bvvCFg9bPh7JS_}y9K^zw3*4#AL~CVz z755980UywO^@dy0x1`FN$K&><%#4{Z*9Te>p`;NvT(0sXu-5wA0V8Q%(h^wDhw9Fo zZ%+0UEhFHM!&KCT{oP$mb#*1LEac3Lk?W0b>al<7)uWgAO(SDwi$n5ZsTEIE z`XL8@D_kKw!_H_<*r>JOoyk{UP2O4g)HoiA&mKtq@cYRF#bfs6+^2_i;Z@d!#CbNj zW3@VQxkfaYxzZf-%XBo%E7P$vA0Hi!XSF6O{EC&lxpeHjzv{m3mMw`biEOcQow_m- zsXPZMKr!b_*Vaap&8Q7PI!?I~PH6lc-ajq?X z=9w*Wf3YMM@GHqJxMLep%N$$sVnulgy9{Rz6~w4Ixa)-djiW~exqN)|?lITVkw;EE zvQ>-hI>l{vVqM**)L#Q z9794?Ng>iX3ZK>UDM#w0aBsC3?@Ih}Gyl}eEF@oIQ(#fS35m*0Xs$%~79H<%^ogoD zb)^*;)wyYb;{{DrUG46_!?3r|hHE!qQ!-f48u-i}PQpeM^0U^;xf?;PFgq zo#sNd4WJnR<&E|&`=ZQKnMcThZc^$~_M3(1ZSkz^QSYdHMKNM;s4@G1Qf{$N^k>W2 zF4-evvXL6mBN(Q|ZYAoJ(x*fnRUr&`7wD|%3{l57vFk*4 zYF~129eHO1Y z`H3UhvE5@MLok~-+W>!~pB?MoJ(eADb`6d2v9mqfPC557tojT4iuBiS{{&08`XC>9uTner}^-IB>96J&Tr^vaXx-^9$#2?v*pK7W*ZnVq9z zmnYFU+od*7gM&rQfe~y6*x|rgoNbRd8O6fHURJPRr4EtIFR2VKK!0I3Y4ecx`D9bm zJ>&Py{C!g1K53Q4N$$3eTUJc`ZT1~s{c0(7{q-pn{8``J9#V>{{>OuoOKcq^N%4y_ zC6KLSllC*?%=N_f0;b!W=w{ZQ8Z$a{Tsuk`|LsjDQz)XuR`f`|*Svt7(qbN23LkuA!VL>hz(CT@htZZP#>-2Hx?flYz| zunj8zL|}W=d=&Ab07Ro1!J2l#EKgySa42jjljKO4NrHWqUyv}IjD&O8C2Il8JZu$b z%T+xDyxR*}I-eZoN-l&)I8X6-gSRBt$5WxN#T0aym zugeTZtu?9j?~ATU4EIvdvx>(#^M<;-IEjun>+_y{itP@+B{(X$Ap}juU?lC8L0BE4 zG9U$QCH99zI%PDCZ z5=llN+}Dkv&4Vre-b}T!V{xG4<@(R4=Zc8ux|bO^EoRZZqiP(e};SZd(U3@e0D1* zRMy%LZ%GT91%Ic+BdY$4cS^_Ey4$^Qj!Dj!u#SODu5yUc{XB%(Uy++K>m)}AsyZq& z=H8<=Hn&$d?dGa-RlJYBILDAFZ{!vj|w6@~7qrV+1NSaY~yK-f* zI6afAj?#IAgXA9-vW4WiSUg6-M)}=C+7bJ;&pRK>3sJ1eqEHJ~o&ysC7D??OfH+wk zSH38Tcq+E+UpuyJ7=%kroOm%#FG;Hqq(si4D`^&+s%ooZur@2tuASN$E;Jo@XlLl^ zdmqTIvhEm)?HnJtMXb{AzKT(LqIXNAP%sDAnB$+jvzY81d@xG2>ez^SQN;da?r-`I zD3|__Yg3V(XYvToqy?6b0ntw+X6^hJ!UasX^ny!4ZKybRq%a~%kmD)EN9i(GEy$7M zgm-eWvQ+IqfG|n^3SG!JzoXhDg}6+iOw9TicCu(hFmQTT|Zbo7^=rJQ&sW=-}|kuBjed zqn2bOd`!E9gagtH60YNg?W4m(WklJU;nD4dwWel&Wy)=uhsa%0KE!RFcb{wK-6sru zXYZ3V{9k*Yl#vr5=15YJ?&NgM-EUtqUcF!3iSOcmyQ4odz2kelkv)4N-aX@9bI+c$ zckH^r4ZDBC9mgY+lM&~P@^{ZTYs38K`um`{&OX_9dB^xZ1-U?Y2mWqiwwPuPt{>N) z*z%%siM^aRgW8M+_J7bH_E*S#l%-kMQz!<`hC7fAcoy;=5NKwFF~Sx<|7RVw)<|UH zVek<`Wd(^r92^V92T?>on?Nr>d0-D}iT#&yDV+13Io(MEMIhPrQn?wv4j|0h0)n9e zk?l0y&2I9iaKs7-7xvyc>4F3XnKr8qsSizN_Sip-0L2JciGoFbnwifffQ>;|4^@b@ zN5qBl;`thm^49`RXbD*4Zdj;Ka1$iDd%F@LkB99DjE3s<=jtqrYu?Yi9%Wv;uvEgz zZxPpd4~eZq%2iaA*r@!TGNOzrJGqZoQt70eoriEkvopVethPy5)8lUMTrLC^9wWcP z)5;EFo-5oAst%*F8DL5)4tTurOO;kp6+7J~uAp`gIW$2M#gHQqwBTlG?d)hHPlU`Q z397VLt&z?~{=!(%-svH3k%eBpF^G}E_oR{-8+iJ&ZA3iG))kGx=AiJ58dxw?9q3%q zu|lpF<832>=M^#H*-~x9Y%>~H1U>l<0t7U)&CPahMMtLqX()vx!4xq%No)(661Fru zvpIp~HqsD}n-`9z3hsjC-2#}Wj4S<-C8)xE2F-?8b2iw#ysZH?RI1&$Jsp^^2G=q8 z9?!fpD2biu%%&8Nz4k=jY-j2dD< z;#@NHdVjdy=U$RsCZ@38<@ULBS8ZLr-{*6a<>yAX)obgCs)Xy+CHUz9S2K|7m!<1{ zJ)vtJ+K4Ue3I%JKLu##BUl*d#U?NCTB%{*rtE;bf*OTPzYKg_Lb^Wfe%b#6`w(SCl zO=R9xtGdX(QF&E$)$LNbocyhYK_C3J&L{Q3{KRTEHN*8Y=ntb(OcK z>uW7l_rvz8y6f!IhTdARhKCHbKJ4=uRtwoX23I}8`G}VJTy<`Dol9TqH+;eRtJNt% ziPzV1dEKS_y$cvjCCHmPx8c?y_O_cMECsn}aWZ7Mow8HR6;c7++9Cstx0VI}Mer)o z9Z3dE%0;n0Xg&{>&4F)&a*BA}fr~+)2^^dh?*MhIJdp$69s}d;arb3hP?VTHmYWb6 z+n6YZu5l6mDe^o4E0Kx`(A?{f{9ZRmsd&`Yd$C8ssG&p_AS?JW!LpIPy-@R7p@b@KCbR< z$P@9^^l&twUhi_*zYpfk)nYbkE^Hk)@Bz(NqEWQb)+(=ik!6F#7`fLWPZk3`Mou2! ztFqVY_WIBU--9gpD6(L<=CPV@DiW1Iqr0JC%tde$__9NgKd2N8xr;d|FOTakZk>kwK5iRO_xeX*mX@ zADl7`R1O=H1cv4q71%(Qu%?KomJgc&O19>K$!WC5@gM^`A>|R=3Ujy9K}~h$U?ye( z60&0Eu0ZLiKqWWOO7q<@c^hNt;u;2-5z58t49;;W?rMS6;nU%!R3sB$5wxB29gY_n zw-_X()+&<_ACa4-qAp}m#!4TVAkqRo8v#P$Fz!Q_D1Pk?LU7vW2Z-s78Id#pPmlQ=_p~f~szn`O7>o>XsO) z>aTUXWnRi+{{Ob`22zMK#1{#VJ#u z2=Oh%0#!bH>X(FQY;Bl}xay5MT@NxrX%hlwNx11n9bvSjDMnDYp2Ls}G2LIU`e+5y zljd80h$Q&626yCVLSX)=Yp7A&*9#1EoZQoG$q z6mY5+2NTKYQm;|4di#u@*@WV46W443=X&pd8V`GL1py%5?(XJT+p;d8P3eA<;_+tznq<#s$!aFZGWkC<{;~5eJ_fW z42bv;pyZtU$i7UdKqvu$A|bzcxT;PL`;$})fit1XU*)b@`u1aRf?SCODK(W=;8k2t z`)8p{Olkpeg9di!)|e|Wye<&*7M`>kB!;LF5*y-0iXoV>Hje`>MueEm zJyeUY;c4ba`DdfI7>M)xpstyTOadWV=%rG{Sg#upTqcq;vw9}GexR5+p34@yhXOpW zP++LLkm-6emn{vh&!%-C5Rpumwnx8Bxrx+zEK_@>WyH$`e${UnnS^N?7P;x&U0Vyi zdk7r)tS+yT7&U)c7>$m`$9e-R@>zfHSe$oHm}7Ch8;FFS+0(mWQl4q??MJCWJd73l zemLsJ(46Av!BSfyfU>XvKN+bF&@8v&2Sbzycmrr%M`wEwQlprlHJsv&7ef>X-8zax zYc)H;=rqJZgb-WNP(7Oot9Bfx8Z_CR@qCtZqpf(fmG*Zdg$OT}6;Tpw@dZ);za6BZQ+@> z?o=L?%`Wms@z$G!Snm}3Nhi2RsIM{(aL`>LMbB8_&)N%>uKL2o^rEO3732k z+yuCOa?LXAS$u-$);44FJy)QYYKK;D zp~Vx8BuA+Feov-Yu#9^QtK{eODV@m( zwfLcvnx~Yq?np!)%!oTd;texACs;4!Ea3Pdwgg+ywEc|wZ=?2i?9VB~$|a+6tZ+TQ z!ZTSmbI;4MZA#2{=Z(#_a6ON6Dd8)j5VD7hw_~@>!K)sPaqS&3WunrhbU`ljq<{9J+0a!}OICH1 z?O%*3*Ts5#rET|(jor6RTX!b^=9~E?-}~Olmw)x;FUL0Zr_$*!y_vGoWL)1nhWM1R z7Y36$zfCD3J-@kC2vFR)WLc_tQ=ktXQ+fC8+n?>aVyv|F`k_$jt{vHp9f=`rz0+xt zmtH#gGJO`^+?Psco_I59eLbpe8|vPWGS-f5J9!|!C!HD(prOc{bcVTo}H4BWbaoO$TN+?Z0&Z z$pjF0olyR|GFh=t?=I{!XWO(bN$XH ziH%M|&}=}Hbe~pKlBJ3}Lsi$=hpyHbMq&6KxmvB$yt`ymwZi(&6F)z3!wn~Pr&GSv zmnY5)8_T$#d#eu~Oto|Ps%ksk8}Aw}Bx>uHE#G}&r}I61$(zIWjbHMmxGnofv!g+9 zFb~aZyHnTTKi>MKvbvr(IR1n8@!C5ENS1Pf@_^=deZu2SW@ew19(ilHGWmoz$t_J@ zV9@Yvv>G>S%fqZK5h8)0RpJ_JdK3dlgRe`9PzzxfFuqmlf*1raoefKv)6LQ5e4B<- zrLjS%&$K_A_C!tldq&h#LF2UiUk*gw33$8^7#7vk|HR|A?>gSI*X`4k*eQ>va?0ba zh*s`tR$mW04HpT%QsPBS<8#7AV*kzvBRosFc1JIkidT8e zmQLi8BcYTYf<|6nG9MzAnVlClBBmK4Zj?`l^b3TIGSQ3#g^V~(^#Yt~(R>%tpj_|f zEy*pT$t_!wqg#^dhnyf1Zie5^M7uNQ{3w!hH3=g{o$`_Ag^}zJ>(NXxGZZ&^H>{5Y z+~2p~XZGd{J<*i~!vDh7^tNs3t=m#tw-VWgaRJ#P{ndnY=_1popZLr*XbLF3;HjFW zU&u?9J160g97Hh$0u7ShDb=O2)k1Q@`8k&sF;`wen-w{^*^M(7?x}pzivvf(YPpn7 z6`V+feoK%q;EE*GFFLG>!v_=H-2>fSX0^Uo^;Jz6S7fcivQU2A#va{%T3>z5)g8g* zU9t3#zgDH_>&NSsXVcD|$Dp9>$R~WERLAmg<@Fwj8n3zLzC48*hVsfY;a1o@TYes5 z7_|_pk#$vUy?>a8LnImYKZ!(y{|*2{V`okSj|c+)H{_V=edpwu?#&jJm+t@B&yF-+pxu>IX2KPgIn5y99Rg}PyF6P<1qXy&2Q5Iy>c7T#g?Y2@m&;Tc*p5xaIB?IY(F=(Ws5nOK@ z`aD_=k5-LDrvw~%=?@7BBq`4nkQ(rO$xOB60R*-PU6%nrwGqv)=|i@$>eWQy#<*Q; zrHxByrEu=gwNlE}I(%mWfg#N$hu9UMyv*?xu)g596|b|H2Uvo3gL!Y(p zv)#EKtN;E?Zy{|)wBO!6nlt=&_9X@e5`BY-!NG*K=xn>Q+PXD~p+w)>XsJ9fQg%gJ z)*s-pN&zC_{|`6b&T&V#)kK%cS_jQU#Vm zDOVEhl;V+6U8bS4!Q)i+DA@ls2lAJZ{E*ZlFynK@Jrq5kE0QKs*i)G7rj*A^rMzz6 zDp>WHeuP>c$McWbf2i!@cO$h`W^mNIu^(1~VbSQ(I_5Zd^J*;Fvmo$9vG~nwRyopV z|4%35{FzYY_K=d#M=Hv+=pgTX`)^n`R^mI5Tx)PUX0)}9Fl|R?vr{37bpUHh>MqF2 zS51esIVdu1<>5g_Ui&*aSN#0i z2kKl5-3@l92JVSA;@rUL&@+j0DFo8BsWV5&9(dF~p4$3@^mKZwQd)Lns;*#Z{b&AO z9kGvp_y?)!t;*^fm+@@R*3y~foOCLW&z!e`I8mHm2P87HO`$hVe{`JSFx0M4q*u3Q z&wu#FC&wrFfAr(@bqU_{kHELH0FH);!>A@+m`0>7VTfoT2(zeobp@c4?`&}Voj8Sv z-l@1k6t(M@#?2dDyFPE=M*h^&gD{Vq|7-i2JJ;+REe_v5>h(Z&5?#M3MNMdP827HtKR2J18P(%ImZ!n=~N&ZNAQoEM;LD};s+Y%Wfb*<&uH zLI&ZW6=`G-QIaqxykMc7DYk|N*;()XJoI{+mXynCgNE$!uOD1lM5jb#mX=a3drL!mUbd|PjACkJdl;NPp z^SnNs5U!v9|9X*hqFJ2=+{m10{J+qpM>1W5W7XS!xm?cl_h+2f|2zG=&KjF}gj+qc zSK@k%*ZYxo^)r|~yb80vqy|j{6t{*wLhQzSsZ-uzGN$JPH)`RQX+ z2TCfhYRAXdjZJqey>gDT{B#ye1`K`hc)`}!>5#dCH-oK@i2Mu--RrX?ds~-XcNdkt zn+}{jhjH#ebad3Z8xFk#(feoEz3Drp=AySB*Y4In;CM(3k0NEHtJwy^BNiay(}C5b z-5s(&^Z4S%#>IA>ygqJ!Ce*3bcWNC*$iClR3p15r1(k;t!ZuCvOcXftABQvl1Hvh% z1n?#xjwFCjCS72`dG8%REZ5a;>A4~nUb(Whb>&J(s_eDrybb!57iw#RO`28N-_k*( zDAkn;))~sOo9vw}9rblV-iPXf>Jq=@zFp-Zs^paO#v&KDGL|GV=VV`QYfvWyV*^vg_3U93MF@ zI$O=#{|3&=3*K0&H`c@nc>>x|ld6bZM=__^Cl57HP+}C>sfTwfqpd|Y(>HMwFc8_ z{{gGx{T=Z$&&E4C;#xl5u~vDwAuw`vIJ9I*D17xuput}2uB%gr2rU!aR_ol?^xRNT zTM1G7Th+o1!K?C>`&&9XTGUOvR-ai7bq-Eb=Ya9)!b*m;hR0QvRvtywEZu%!^7c<$ z|FMr<|B2ggwErxjD?<}+erosbPkns+M*DE}9JO%x9fDUo%5%q2-(U#dm3Rytj*q^< z$*Y79xt(C)f}nQD|LpQ$olXd*E~GAh&ex=bCJ2b>6H2H_IbG*}+CJ@aFLB>=6E7|$ z^t8_xbS3Oy&`!9W=UctjLT8lWViDh3CoY!XbX|4o;n%ipeeF=<UJIH&7klx= z*ynySHl`f@_Z!2_t2I06zg6^~upWRjbCPzPNG?uXmQiYYR6cFT%#p!M>O z{%89;E^iI&`^4c-OnhNmVcohyVe7SBmtEF%?N+t!@c8)Q2|UL&Z$IwJFpj1Adj`ML zZ-d(V2(@VU)qJMr&tbuG5S8K`7vFf5yt(Qvzwmp7D2g*GiLA&lmGyl7@#nwS&O6>b z?|6)pbH0E6`_A(Q`-{fn%S4J?w%EAPmRdl}tdY-@M;13$o<4hwR^Pbz{qjv-UYvgr zZ)W#jXiMhFA2{>m6Y&n~GMNIG`3&P!S+?Zm?3R74V#!|n2OOZ@A&2?o3zfZdUsIk` zEZJ@Ufc?*0;2fvszO}Vd?@Y1RTbW`mtZT9TT-t-LY+-TZCkqVHV1Y}v)~qU?K7Heb9rnY24Ev={qiR@S|9 z?4i9IZpO__)zE6|=f9WM|KEHsy>ZU@t)}4NVAE3bu({NEao#E)I=jRE6KB^u_A4Jc z`%U{#&K^JW-LqeEw(cDbHZ=uDgG-^WkXJUFn(S}P?N#HW(g0`gbMO8DrFQOX*fkgG z_jlFfk|`)G8Zw?P^aSRA{*Li=1%_H-Mury$Z5-GtO8kR$>}VexQYwA&Wj*9|OAFCKVy@!|o0%Kv~e#_NDO zlJ@a|-#_rIFYV{A?Dq@&w&uTc&N%06uBmB}K(ezo2^@y=w#}ZL7#lztJ~p)$pL5;^ z=ao+oU4?OTfngh<;Kl?t-=b|sn>LHSCL)4)9ROSXRnut6v>h8*mD8WJ( zGo!6gnoO3@`RYngU9H!S*STA2^$AvZ_XMQ-?z-_>{jwP~h6;c4^_ZaY$~z8~6Wr*i zH!oV}s5f-=+4e3bPAmEwO`SJteq-D0jM0Ihxdy-qqVwx;B2hD&hNjjK7YjTKSCehY9IIem7e;iE2sT_ zH3S4n37&b@@7MBdI{jCzTjh}&1+)4uQk!8<^dKnYwa(x|dlf;-Bxn;oO;lKPHM|6Z z@#4OiafBkO`$Tc$8FV^#!6Y$7mqWXC&L&vZpt^PK4Qtm@|JA=$*OyQ>%y(5?t@{d4 zdv2G1yk7M$2@bhyYu&3uE_ZDL{M3j2{;jpXP)qg5+5sR^pd!cJb*eAs=TLVohr0CY zp~_`)^0m*6`(04kEsMboQh$}Zpw|tBeEtvXbp^M3Rg=3GTxUmJQ^?18I4~TJEd!?I zHildUUGE3u>)u*dTX4Bn#aw=uk2-`QKd>V4Ck^p@uSJKG^-dxe@2&X|Gk}pLD~*gT znWE-9-_QG4>{|Rk2w?IK+v&|_w`YMf-RBgcxj9cS%L!qAM_P|DC3tf3vTu>6#k0tH zxjj(b8Mw?QGgDPwP-|=L+omH6T#um>|5UHdcf{@G@mx;6`fywAZy}yjwQY~o>QKvk zMRqDBc|CWVfhN1{VYR;UdYw3Z)en~ho_ZBmtGu4>DWz4znWERgmtkB}$9VGBSlYen zPp}WbNLBkp01MJDxrIuwlX1l8Stz_J3>h z)@4uF6CNH|^CF+}M>qp-bq4y!7bwaV_8aJV=ypn-rqtTMQd%wn3#;_l#}{A~UE-}% zu2{Ty`*m_}Km70lOn;GGDX^HgfW>UUIzz@2H32|W)x>9PEm92HrF{(n=P5)3+9rRH zwX0rMFy)P4-rs-H?f!!MnbW~0d#(C_KNqZgLRLTR6TziHf9uJB`_DaR9#h}b6g++A z!Rj@u^Q@k00#G}5)y>J|ub&a;HGYH`cBk5;*B+LJPxxEvPamE>e0W+}K5gH7<_u)c zmrctj$n>Y{?T4q29GN~$eVBWvr7d|O)HE(LTgk);ad3x zMl)Ky@Y+;&O94d3_+sqJVb!DL^Jb=QLr@*^DvLZ@J&Op5F7n*!UZgx2wm-^S{(J6n z{^}lkci8viMHl@gZ)+9Zvw_jIkdKdt7X5g!GN2un!FpJ8FI;F}vdFVf-tltD#m(yb z!_EWc#j_|}`HQ%t2Y&}Wcm;O(Vq_>j?3S7CamZ#6>qyagD~=f?_JTH}1*_{dDD!g` zOZ6n#h=%omS?P#OdMTj~u+G=CycUiZCP`)}rw*2ga zzR!Ia4sISc$kglVx7AC{ix*FRmS6nNl=r}J}9{Oq+a znqjDM&m zCB$!M9=zW9lj*DV_6s_|_j>**%db9nIw^N8)9GJ5sEh!|tvh`0`7=s zl~%Ai*vEo}macWyN~SI|1`R~JCFq+D8vZM;x?WUwVABk$fZ{9UmVZDT;h7%d)JC}!LlziKQWi2s`OqY#DW$-;3CnHK-mdH=uf^XL0K4;mkBPdl+C-IRLk z-beNg1tBUy8G&9N8QVR4FzvT66qF$PLR=f)wZ^@6y%J2leedYKz2XU$mG6Ff?@&;r z!j3(%W~g7w2UYeB)CK2jMuz%>g&=nFyPxUn8d9kq{tx6;NIpT75yt9leU|;2^Ldn} zMd>Rj&-b0z&XY`ls+zOZPX}N#jHT#9zWA*H-^|Y_0gur42Okdwd?bH!#c^{)Zr>@t z_x9PN6s!!y-tGL&?8Vum+1uB%A5v0h3Es`O%$y(r1^*=WaWOW&3R%HI$O?R7&NYC} zg%}Z%({la966kUZ7A%i!5Jm9O7@he!Eq*#!0V8U&M|Sj#kN2E#z9Uw|izD?ruKn_t zuiZhDiuMtVztckA2V7V!Ab%w)hv@8f3%QBWNHK85CNF%e(n!Og6hrPvN>C_`i+Z!t zvbvkrEj_V($31FQ$gBy~9o*KtWyo5X-ms!76y|3$=1ebe{t7=0cf#3hzq2l?^^c6n zrp_KPf~q!|=e24BEy{$%k?5fZH*$socf0ydP zxMwMUlD|TlPQjZX%z0@p5Q>vOY1!&CchNk(>lxLy_^Fu_p|JW4h@?>X88NrG=c}Hj z(n9eLSCUQ)>zub3fCGr`fplGf1Zar@kBHg&%w4p9(Ywv*wzT5M8Djq84>k+w`MEj& zb9}Wfg+p3x&~CMZwOS}FiTG?JxB6%7*EOg|3v`a#KDag?SPlVCcS|Sa-U7BlqYGuU znO-V6@fsu+7q>}Dq4|}}w>OT+s%}|HHPhOfQPOBjRT+8b z@LsB|1>)X{4vvfr#tk~8dhh)=)!TmbOAXu%g@cCAzH-Y3TM7rmXpe?17d)?Yppk0f z0EGu?I_`P&o({8$`?yw%+a1v6k7C|##=L#M4L14NfcL2;;0|gDdw>t=T?zZ_#fKur zlF>f&)Z#t}Yq+5UWQBmRbT7SX)hhNLn1EOCk3Bv;eZ;ws-#YZl(9kQZR!xIbb?%c4 z=e{2z)#F3>H}m{7{iEyb8s|+sy^7h>tESmCltNs@4G*aLlC0_dWp=%4lhw~w(e*4Dr9HA5=Oo@M25hYGHnF4TGh-Wr^SINzDbIQ~>ycP^c@g0T05U8Rz5S1j0wakJlm;g;W!wE+{x zp;-R?xwzq%6L!{72GV{w6*e@U(P8TaW?AeHkh`RCj3$Gw=f}|Xv;a{A%WpzEVdJ57 z*j06bC8NCx6d88{)rMsmf$DheuYMI?5EN(CK`AREfX_^uk?_mLFMeUr8Gnh+Fq|rQ zBnfMZ>4EtKJeSdbvGzBV2C_?Fm8qoi#!zH+$w*MJC2vqz81~)-0zco&54nsIB!?gu z&RRC4i2b<~^tU&G_Z`=*RIcAXIz4SiC!DReg~X||es$8aZEI5XpUw5N)~IbEVZl1w zUmW|Vpr0DY91|WiJ)}a0rVE>NCGXusgNc}gdgdttRoQ>P?W(J8+sLy$S6tC^{vD!K z_Q%z31Te3=NUd}3eCe*cUb>6@cq64^E601BASGn)^^C8HH#f)E%0Af_OQaT%9f8`RFEm#;nI;077$?GMFNG-;JT7=Upsn@=mpX0|IPY#?x)M69tSkQT|^~JotFB!w$BwH{dmQ_EuHQjIeCwFoCehpT- zFboE(0-Hz~qm0&-QPgWQ>QxI)`3meFs2J7^EE!23s$l}-CE*g0LUcvc%@ng1ww+6L z62!Epu)2^)889#jcwrZNi{wxx-`3pxa-{LoS7daCf3mzV`N=JxxvW0xWzK(NRU;VE z5@SK-T7D6$9;$AyUa@DacEOUQz3ztYt8chCdUZ5+&CSEcGyUnVgktZpm9}JSlHDV( z+^}Y)KYo2&@7ALZ&vVVMMw5DuBQjO6HOMoY2JN;)!qANC$5w9X#(amq19R$I7tZm2 zMqS~@DFQ2UtVQ`40)qhZ-)VJp3cnbbV;l=kxIF>~;h1iy20HRtf3P)$hJ+_MEtyr- zS3!39wJ}rC^_5D3E1O|m8CCQFEwd)RRc@8xxy(o_u-8kQAR;*LhWN`?EWbW7?tC|X zmkv2`3Wnq^jr@P4pQB6*J-gBR?IjTeBoHJ%!uTQP5F}>};mg|#XzCu;4Mgs8jDB+9 z1SX9n)Z`E`OP!`f4D=e|BS~Z`Dxfq97EaK!*p8Y}C#ox~`t|v=1HkW2~^2ZuEMA~T5VKgXu+&AAnWWm8z3u_B(L>Jle60F z15NSI#(8iT95#7iVQ+@?H}^I5B5V^D`QZVP!xEc;CLP>F0@z zoP&M4!sT4TdEo)IHv=1Z6=a{P8bR5N`>pSGz1h{D2ZiQ`dLcNCKAn}}f5P(x@EC3z zB_h`Qng@GNW0s?P!;xiXI1V9|XriTHf!zsNY$I@d`9d!w)x}mq>n`(v!e2^l-3I4b zFk4yL&;~p|j+TOu>}(S*_Z3`B_pwPdT6A-i9qKkT3;PC_EnNpO2WwqbPUU2I|Ey@fUpG73>=&TCSw86*|L89vYGP=J3>WWo_-&{QJ_`aOnIdlGI4Yh1z|l zKqvf6D0~EW9u{}jhr+Lzp_%>S&aEMHOIaQN=jEdpi~4@koV`YC+k^c>CJik1@?tFfE zXU9n8c&R-8ca-=4N0}1Im?~vbbY3|>eqFtxnSmtxL%*c;kRu7H0Ds39IREf$!nt=U zQ&k?{RDSsn%11BlBta21k9tW|>#S`3-{rC5zYvXat| zs2~?B@b77d`Rz0v(7Y$S_|pOsl$Gausgb19^gVNezWFETAJ3-YP0qJ{0X(dGk9#X? zIa`Yltl4b6O6%Qa;JsYgywKK>Sp47H!}Du)8fEfR+5O(*60{q$#s0TcjoqoEUI8B) z@TuP8&fk}N?blQd^bqNJ&O+`^Qg>Gt2j`%(5mWEGd5?6~Qv4zHr&q0TZd|c( z;|lhL6<7UK4XLt_Dn$T`{^^F&^{eO>2X*7Fy{~|FCtZvNkL0bmRz#^Wc+pF97~)FhY<`~0ocRMLKYk5%HX_~wbezKMS4 z+vj#7p?zHS&rFicjX&g9jhRV-lX}R-NtJkZ1)L9Q**$<}Kxr3d2t5eKIc!2I4~%*Q z-seaU1CwZ>{cze3wGXk4K{#}INyfIz+PHnzX*(;8m>bV-BrWD4GnGfApdt9sk)4-i ze6v=0H>F|sLdSV_gNY>6E*!MzE1*TGT?lx?3=kGYx$jy!T&37>RT8vKi|Y`2WVhV(&?9?Chx`*GH|KT3 z(DOHsO!$XN_=mTk16D9zEdCe`9{Mp}xB5klAi+(3aVMv@CEfB^+{ zp|EKO6>YM2``7j_W42Lsx(c7g8&^1OV$i;nXaJSvTj*DV)k4+X#9nI3ds9_b;e_*r z6Z|tBCpv)2S*zdQziz-Tu-gsjvkdjL^TPIljT;BpOM@FVFkAjOh}V4DX-gFfsg-^i zQP0`YN@OeuFp$w{hs2wkM@Vz3i=7dj`la98URoM!*X`JBa;JgUqW|EEFA8VwF}Mb zdM2F>99H#^`3i8Vfg^!9K7gx&!qwHofxzK_UA&{(0BVRkcHl6(3+Ys#Rgq4^6Zq4_ zD@-JEU$F{c2Ha9{0pj&(kj8-{g75ti=69YXl&m0RCIDYI7d06rokzH)(8=FWHBEK) zz?qrAtBU6vsZC$_>dy7J5fNCT0vQ|D9i0A3wdOiUlw?{ zpi9Vkm#XWco>bdl5V>JJugnegbaO)&6km_3;{Br+UV^-&i%`=5qKIt@l2ub0pNVS* z;~s*pBj=1At>Lj_7&Ir+=W+x8*ypf|BT+$j#wa?T!HXJ1r6Mp4-+#>xBMPuUPsi6nYN zK2ui392ZpPC__6^V)4XmF2*e)&pDm4;Uw8k%VOoZ7BMffw&>1fJC!Ll&!aZoUxDr~Q`6p=-R3yOkgvVU}5Wtl)=16BrS zb0EMTz`=$9f6RH+c{O0G8?ZG!K>0qPs#gUdWS;-d;ws!(Rixi(2=I5E&8m&p1Xu=d z!GU-I+kwk?qm9eRGak4K!vYEM&RGlsVN-!6A|$`~B69-lajs24(c1W9(53c4Yv)3k zeq?C?(_jTUN@)1fp*2;)wQL&Ftiv>#4_nkJK-~a(W4>L+wan&CI1iCGkqDF{+I~(x z&KPCEp3vsLUA(vAjg|FTB%a zx@C*1=GA!c>Md}>sI~`#S6>|rwyT=1Zn+vgl2>0X)eyTaE@7o=SH)B90A0{yNQx_0 zU#;SY^_MCWBf%;OZs6Z9PB4=){3RN#jU6jwbI6Pv5YsBo>19=;q5mC)h=0q*vvL8pf&r` zCcQ71RD*h|wOsJoaxcUzT?+bgSswD$N*fbk4vS@!=w9ihP%i6Mqfi9c6$((;oHK?1 zy%ObPTS`Tn)R|5BPx#eS`XoFCDkKEOmDX|-R?Lqji_Ydw95y?)KqrVy1`-m`Awq2qvU>8mI2lcwq9NIZD}&ng=HZ4!kse z76M60tAkG=?G}bDLFFMdfKOU{K;1)BJ0FvEbW{tj83}5mnzm+*hM(Zb8k_}#INLrC zp=FQJfpR`O#MPd3L8n(v3n_{{f&9K)7YE_ozM=xFeKuQK!1y6q~H~ZtguPm z#nHiJS}6J3oDFLx_f5_Gh@D(Bd0>jazWaqefQJFc$qrq)+S#ylD#L!iRY1#>sZAW6O+d2tEJM)zC!fpVsd1czZya^U< zBCx$kd+7CjCcW9Y$xfwgc01*3_0o&k9yj-_c(Yy1Gz(L`yRii_Pa6Ji?v#=xyFfvT zmq`h->?l`_vbxjC62E>j>%OSl#EVdF)C=EiR~)y^lchqOG;!O#h1`f;r~Eus$Zw^< zAINp^>@x7vG>l2d-33on4i z9zec?S42AsHV_;-Oh3s4vIwd#Gzb!cd8xUKl0ga7pEo3s8YQ8D*H!OxB)5oMyb%Ab5-<8_z*6m*8_yh-(}+*5$bCJqQ=rMNB;%BDCU z#4th|ml|5wx(IrXy{tSlhWO#h?k120 z<-rz6o7q2Nl_*OPN>nAiZ)TDb^M{IWac#)xxk1Ptt^*$e6d*Pe@~kanes25t6auaT zUIC3FIg77=*8`M_We?Q6G#Cqo8bcSwzLKq5Ro8MXc2Ou5(qqSt#dI886g$>}%h|8s za$_hIJH}e|u2>(uNUwV=p3TM|Tc>F_?b6q+(_n-h)AjL3$-eN>ab3r0x3;cg{q=!v z0~Xo?ydlwqvxK8dUo5MIE)xX@u~loLw?Ta(3?xAA>^)Txu>GBc2vqzx!!|=Tw-^#Z z;0fpM43z1H7ulpAo_M+6c}OF9We&asZi6p@04VD^A$@}JL1P$}Y{)@^wFQV)(*mHJ zNGr%IO8rah90_KM`!^Zlx?>F;j3t&f-}b50_LQ@8r4|KRlHHvEjk0p5^~<1w#eC+$ zs7+Qi{$J&Xhx6U3p_Qp_I{K9o{A_}GckFQfHBrajn>6ZM>+xqyt_QpyV*|rQ5F)F$ zu5Q_|p#?ue?n>B8R~~O7Y7og6N#{$#Jcu?0>ZYT;iFC(UCxx<~++{)MNq9z3abo_E zhP+QA#Xdh{hQ9d2t&l`$f$F?=>6fnwvG%}HXYYZrk*d+^s#r8x6OC3^b#{iB_p#6C z{`B{gO1wH=Q@2QC&)VV7?4}75fWgeid+zGE=pPRB#-v55Y|4!2RSVz~il>!C?7*L7 zLsCt`Tc-vlCo#WAA$QpfJBtu$>&d#sZD5ygk`VF%3tw}j74%u348TGM{Mk#JD-|6_ zn!5VCnwmK4-#DJDJnk+Tz6AKn_VL29Y)?byO2+E?)_!)dc-}EgXJ|Lzt~>*fk-q?2 zhz7CJz?VDPYQoM!Dj`F5eq#K$k^q_{e`|dBr%e8- z^RymVd9`logKYu!v%rY+FV1Vuzl;O|BQSkPGyh0{9XdGBJ+SqzySC!z;O^URXML8o zc_pc5+XkOCja%-&{}xiwieD6w^e-5u6T5~ht=QS z`l%JmFZ~Q4uhNBI!G7fZwK!h4cDrG0_Xh%h%{&UQ3bOA)HMh>clS(!=CR36D z-GNx|&m&fYCA>+}5?MPOa5hESis2JzUbvlDR_O}onLa^@OYg^DmXMd1d2RqE1`E)#jJHDjI%r5>`}Wldi?n5sj1VYZ+7pgQ`1vZ z?6C(QoIZ65HX|k2G`m+^nSSs=uvtl3DDm}bE`I(U+C7HysZ=^Y4IUw#AByKP$B!RR zJ@wR6jVETFKYotBQctlZLPf3 zdV1IA-N)Md;*qc(X=-fCuSmN|`A}iAoynO&+7D;Gugj+}GPXlKWyDj_{MB>|JE1(q z-YMSdDrCCygA?s3@kCdlwLRTF(AsKa5+E7Ix(ECT`S57K$k%1;9#l6t4oBb9ej$rB zhZYm*tn)WNXl=aiSU6jybLQoK#+!mM!#8owZExTH1HQ?5^@sAO(u%@h{-I#(BL;hO zO>=f(`(0Oj>f|PB`x1ZXdZiF_PNKNK3Vs-|TlzRrJ%ZFL6It%LYY|y)amZAE>D>9A zrA_N*swxTC?E)fqukw4n7vQ#Rc}r&||N^{utO+@V2QoLs5*j6rHhkrR1{@ z3K=g0wc?ZR5UY z93b%-+B`84Eozw72y5(c_5 zIqYfj66|SBgk^O7h_p(*Y^k7MNLx>oOHpIdK!+tub`UifvkfjYMG{O~inFDJ0Oe<} z_x=oP7-u^|VgFMBU|OfD!l51P;wtZYnO%cRiNI5STyeflSD>$t7L&t|3<8zFjm+d( z)AmmTjO0gDglT1`@=Kg*+O!|`1^(@@uPvvewjn!m*WU&4%0RxSikfBez(ame2!G)7 zq8>i*vBjk*-zkC65hDZvuyCF2%68{+-P{&GyA5sTX}`ufb^FX|%}Q?GO@~K;IfHJG z^9M9*ld$b}ExdV@9C`3T+aHV)>QxW+v+u)NPyy1)lr8jMsf@&=&RGv>1QUP#=x}~! zl1RO&{4m>_ALh)tF~53so=551SPfbTw4A&EnEx8#Sr9A@wq>%oBoZB+&>V(XGXynV zNtGjBEH5y=75zlh%!#&|XQS(2`Lp!9PTR5@lFxFsI{#FDHCuwu#Wm)SwP^>C2C;5u7zRafBh;D-h3Ez(udIQMv72K~}0))mwcbSY+f1JQkA3elLS2E*El z0a?kmuS`C4Xs0NE_DYi? z*Nmi_=^H-q5NrTdYKA2xCBKFE)4)blaBfVe`i zneI*ZkE+J_rZor0aF8_}8}%}(0c7>zjvBBy0+zww zqU^R_uip|&585l&OkRJz^D14E+BOc*tJ#`c#_hx&d2)2qEzT)7BeZwPw}B=iOOu~W z(NZPql#s_*dJ4tqi_UInBC(&Kt%=a^HDE~i8Y=8Ug%$;&j{Hq|qb6bbe`lQF5l&Q^ zJpT~C^}v${@JDtkL(-(vo^tMyPAsy|KV&ajWJ{CwB6eI07GMctC1ioBaN36cW%9s* z$%z98*mqY>4XjzYa;9KsGB!U0DudP>4yyCdr9Y4^#<&^8xm&ylHIeHrf_fD51fv(i z5E7N3lf_^bRim{i(n(AU>KU;SEUAB@PmNl+{^Bo>6%fb8FbX&8xu~T(cWmB1x%;g< zCU+0LP~b6K2Rke0+S(EvP?!Fk6;;JAAe+uxYFErO3;VRzoX++R@0q;gt=${8Zhip^ z2@I9gf+A;I+tI^7R(LLa4VawIphs$;ox0icN1lHw)&-S-W?VFRhig^Jm`Jc1#46wdwq(rL3dW+o?9u^?gv(_RP8_ci9v$cqZ>Mh} zDDa0L;-5e$<6Y1l(1~#FhMf=M3BUx97{viwUVM@m*y5wEf!CaeKj>NZo)|2gwO~Q7 zv$O8cDg*Ab|32o1#2(jTinY0-nA%7DBiSC z@%rSN0l7WsT^_C0)_SYkWp8aSLJP@DsuzT-In_<8uA_u-ih+ay-^UhE!r)R_uKgGi z2C{)fb+zQYI8x=;tNTI{Ywl;PznMwZ{d%>(YDD#iwEk+&nQ7ogu9o3es4rOK=k2&h zkH8csO!xYYFyl--rs8e%n1mQg%NcCq%THk_iaqcDfX*Zzc<=`d%oz19x|#+?iOMV_ zFw=b4#=+Q;_)`oU%s1lf7za$Hnun|Op!egK!P6dl_Og$AgL+jM zSK++FT0gL)ym}O9CSgWFchLeO6%=wK20Spo$J!*-Z$iMJ$nH+1IpL-opHDPCjOLK=Lpz38g2TD2%!voMnivktQZ0pD{?e*L2C z$oi$$BAaco7ll^k;@XPU(eNdi<@v7hVw<>r8h!AZ5B{lSw??LzRB37z`oc;=%L$sr z_5vb=?NQjAz?m$f^x+28nZ^Ji)9@ebYmwM-9mV*<<%TOYOVogcs$uXgL&JO4VM#?t zno;QP(}-o$9UjzTxgia%)3(@| zXxqdZIoqZC(@-pAu+4h@*M+~}e+oZ_H8?>`xVhd7`JbLst0S!Kds;m?ER_0Yta%;^ z(Y8bvy1s&LV9Fs6!Z18q6@KpfThvE@yL8ygUJtl`E&)AEls zZexRNaN8>QTm-3B;e?Urx<=CacqSOkjO*!I}vXiVANGcF}hR`k1w?<+Xv3&jqD|uP|@|mo+ywEn~8eb%8m0NezdN3$y1Fq}q6i z>(sSkvQCE=JcsBBIgO&y-5aY%@D_Qyk+bp2;|gH`Yz`ccq;NUo+LV3AVA4 z97Df;6@En*!XINm_=H9&TzLz5Ucpq5@Bu`WrU1x@8scV%U_`lUz|NecP4uk&0TZGS5wKW zSVhgDj>cIfMp1C#!b1Xx0$0(8MuRTKEuUnRaEa^-H8s>bMN}fWBNDi^&CW5XSC&H} zu=CUCFjfagKC{3*BZalYg;tVh8}^#>7FIWU##Ifmoq(K}V6&jJ!dN7KA`67<+ttot zgrUV;VS!72A(FVO$y(M6dn&BO1q{}73v0_VP-w+#7Jn}2ybKJ6DHK@eOrSAX>q{F4 zr@)NDEmEsVYWo4gBuB@_rCG=7=fmXBNMg$LQ`J2T@@qsDm1m=WnG^&3b>Ms zF`Lr_D72cEHJUzb4B&&Xak%n(LIR-asVZ0V(*>IR7HnvivfNqm8;r1;nGpO;?%?3t}ju zXf5kQpljMI;g#HiL08xW`6-59#q?w&u(cv9Gf)8d=Z|sp6PGnyh;bW~ZfgYfE0 zW@if9^3dL!9@@FiRAYI+2Ae7XL;Z0*)&(T<5DY1U=}^edW^BdG1`$Tk%4FlFxo+pt z%4z-@@)_81*yZ7C{5tm=mcpvM*nX+cO7$U|7!Rx11!w`CZ@|I- zbQV#c-&fuPURtq{KVNh#C(!BZOIUm$@{vc${$0U}f;?)LLm6HBk%sX9JahUN?d`wt zeXu<|e{*Kdn{91x^79{M7e6~5b7T|Wlt-KbK~@ppQ5>7CJ~C}a2ccuoL+@X7_^ZLDXpM!=J{J3j(O#%q4l*ONqf!!9IIm#-?1(1Lc zN)@D|P0I!kytw!716ur+P2C%H-H1l}$1>SeE;{(!E_v&kkwS05?Cs{qUp|8DMs_yk)?8j3brA6l^vL@-}4N00(rA>tVO-cIJK9kr1L7TN3R$0#tDFATWj5-o8W*EJUwqb+zZa z)Of!hHFJsHJ_K+Y{j0C9nXQ9uobZJX=b!IKXXut1Rkp;!DW;%DPjQDQiOp9m$z zVV#dp8i9S)1mgQ*Z&0Q5zfRrxlNL!+_jX3TX6}lffu;FAuT`yuLW;4#+m~ND&@+|` zd(CvyYQy5Z8g;G@Rwoz8a`+;yYEsKj?woSIEXgp+mZgvk-6qWaeTa39*ciwe8cdj( zfr$2T*^9jh!H9hvEyy|guOzF6R&#bMaH&r$yOq8cI)oq=!fQ)Q>E`&}@d7~+;Oqh4 z?54Z^>KLyDP8o=0a=EahK2qTz3R>-w9G2|DLZH$bu~QNLZ(bf@8bQb?4Z;?*Sp!*# zAw(C9C=h&RniO@e*KJ=wIuY=}81j?Qh#pxrEw$y-vj$&7@I?#fwKD~{j2(9#gL^#s zhcP92E`$Ey>Y@)UNEu4{125zW#JgbBxRe#~m{^U=*V8;zaRc%PgQbO)8oP=I0{SJ@ zCJ%n&7R?A&g`{sDJj8N57zmAOVOI5f&OaU3Y6EJB-NmY}-Fo9)WZSm<5A;CL&~Eug zkek()=z%Kj(7|tM!9cBcoK>H!LfnYIu8*c;vGgM``ko-mInqOt<=o5|ziUF#onc+! z4^XqF^Xe`Yn2>MS?Kt=!^uU4z;`_Fz$S{w-SS_mtiGS}?)JIRL2{u;O)Kup@k-)m{ zxd1so@QoDXka`@&p(3T5H5Q@RZ1_UMONCDC?SjN@XopvXFgy@|$Cpl!=(eTq@84Kz zHv}X#9t+oO*t9@us0#T4gOnQ!{*v*!mZ8^P{%^b%?rz|F{5eD4n5<338=`t59*X!s z)pXflD&MrDDgR1K-OOKe^Wo@M5pxvdp$dBPuS3@nfOHoep$v0CESgvjtd6FJH1;;| zJG$V(sFj6zPXS0jl1@T90SN_!nmT*4s$q%IopHXxr@ylM#ohd&Y~4A`kaKm}sav;u z0p9U$zjcMNq@k)R)BW{F*rMe(+_2nv<&nB9&6RB3-K=Nrm-L`YgcE?vUs~%tkB0JO z5O4GjsUWd zb{oD$qFZC|TjMv>;Wc{HQes=9y5YCCTZ*EO%u5y3*9^~3!wy{%iblP$tq~nQt+rV{ z7QFOQ-K#{l**-&$Y>j!N(ab6C^KI76$YZPx}TO9&$N475XMizw7 z4<6XckdE00boS;saWdGbL434P*FpY z23!+$7{1}R2*j$?3?KI2S+-tFNt$frmPYM_VYI!_xm^+>v~Sk6tQzmxi0CRww&}9r zZC544NIap`R2x+_EM@uul-zEj8;9FS#_zhK&fBa2{!xQRafaDlAy(# z8?BbQp7B(@?(b?DS)Xoj#b^Gi+A5PRst@+3la~xOHi00XZHrdO&$fahfs~>Y1qAa1 z3V5Q8i3B4|9FdKXIMQ}Vgh^5UNK6sdGeItQF2i7jXk3hJENd=NfN2Mro0rt+lE*M& z9d!|z=M2eUY9JVt*DrIv6VJ8OeG}3aQ|DVQdhsREXd4#UC+as|yLxmpOL9Zn*fYMi z`-jz6u8ifZgwIT|nku8ZMnOw$VO#3zVqU(qUQY6GZMb?>X0o~^Ue{tZHpgqSi>lP{ zRjb4+A$}xTV?%n~h}6>M*XvW`J>BC?jf0mY)BVBvMa)Da`4?fjqHRjPczr;G%ltClk)g(eTMhj$VL2Q+Y94{(o1i^xIAQVV$27MgV zx~9EhgQ2Mz(=uY2l$ndibHT~X&`2uT+NGQ9>Y<@DSOw>PI}4j*)!<+o(3filQ^|GR z-Q$hu@9yl#q{|(#zuG3%m}*_^({(MSu@^S8#gX^t7Y%RQST6*%$7ztk8m>IZf-iNi zUE58Av4Nr5v+a-v%?im4`W<>Qkb{2WH2^$u87JC4DR>9IJL!nNpfnaDYCyi=MH(}O z$VmL+1@sMYorovnEw02QdLV{s$hjZP281=xadU(WgrL;0f$MPR9+)%)lG& zh9S{vC)bV0*>1mXr^dUx*Co<}Yp6HvEJq`$euzk~wzJ7J+jP;3OAO2>twuGJTDg`j zo;w6vvSJ9<)_{8!lgzofH1w!NDuy16(~3inhUit-(s-Gr?OhMMVuwUMz8$ynh^Hyl! zb#uF0$TRKV{~%kFB*}|K{s6I3&0R|+3=mfeMr;jQqGXtJE_gHc-fsY^cu68Z8|XCRJNmlwcdq*{vnxCAVnLl?qZ^KU^8Gk3GfC;z|)`?8W8+ z7%cqqVynaqSk9Dmv+2vHl|}@~1Ur>(Y$N8wmsFO5i$ILu4*2+LY{l-2wb-JiSnN*` zgb~1#`0t}n0ci?(tiEl1tkEJ}DTsy+h{f7P5+wqzvbl}8O8my(tRDaqXz_*^6>E-GxZMl$hd*c1he3xOLLp(H2=q{r53brhB<=(jv_ zPop_V>q;Kan%fGUdHCg{HHWH=6%+x3F^)Qu>7`Zy?|`E}s}nD2gYd5tEd`k%-h)7a zLTuCAj)YWiR0@n2_VZ4wmt2i4?OaCZ)W+RtwB|IZLma0Y^Q3GhGz=mOBD60Fiqg0Z zqX@JWAuT{5f#WmrSH_0(mEg!AeDm|T=?}sw67gtsN^&>2zXvA-aUHlOFQN)^e~`;@ z66iq&0#P~`dGKzy2KNu#6cR$PQb1RjxQsUhg?LZmlHV@@@J3=jA?VY533<6J~;7E&F=x{hi4-njklG01zkqj@C4VlDc z^fG#F6a~Vs2IUJSM+<>#h0iwdns9G{Tlgb)Qv{BMBM3Ux)Bu#pRBn8!>ek(e5r* zW%xJ458j3$KwS+Mnre)5h37_5I=oDhDEb55;a7+$S7JOS(x5wa_c9LxgTXmQ5xot= zitA_uQ#1lq2ickE7nB9M1e|FQ;h`IFNQI#CB3DD^knO3|gd`QQW()#7FQvy7M>E$^MHH`BrFY^@$Q${hsZcs< zJIJyzYBYKmY~{uJ8M^RI>G!0|K&OXkH81JAA%hg4Ez3#{)ytL({8fe&z~5V6x4$lHqokk@+?ma)-m<>4qSG*beyv zVbH)Zm0a*ZX^Q9;R6yyIfm-JuFmgfw$~pf4!9j$diSU1k>dr5m&2ST^!i@yhKNFHD z;pfcN2TMSrE)-Hh)O%KA36fB3@X&;jZ(#V}5pYg)Oj&1i4YQp$`K@l;KXzj{G!+UL z4)dP#(R|bMc7zQ??B|>E{J|k-%z2Mo>agGh5hW8VSF+&rbUN?4@yt_y%;L&c-~_m* z0+EkGvI4dg;3#oOlqGo5X5vl++6PPt#t9hIPGVh&CBakTF8`MoUKllUu4nwQ7hW7Q z%qX;SIb#&pgXGkNZUt3i?8O(xpar!^We0D8K|V|mQeybar^I>=CT3C7eGslp;IiJb z^wAez9EEltwrUn;q8Lv{fs?ukI{Fr%zsaWJ6Q~J+P_Vg?VGJl~?Dj zl>8hC?1~uuqVg{60kjQaq|-%d4ZCDR*CnleAD6N#8dA*?zjbVd6J0SDYQDVN+qC$S zHN%UWyxn7IedaU-2?%}j;{y}x4;)xO!M@YidP&!ni>2nqh80=q%DSb69_VF65uXo(V|X_jnF^Zbu17Du=uf34`QJG%=e%>moig2s@gkBuyfwSV)@0 z6}m>tJ18#Dw2>x(O~;8Blx&)aQrv+_Kp(_WID@DUXSI+~(n3I@oJJqqs5N`TSQS8X zB6Mdb0Q5dyC6tKYV+(Z#lLCz@|Hzj|KMPi+`Rbm z;Z1&}eaWSx!=Lg?RY=&!e#>w8R@F#cpV=0RFYf4C6j?w&&5_0NSR~WEba8w^WKq}M z1;d{UVW$H;?WO{|ca?L+dM}f3A;jKX<-GA}c%*qmG@$kdykn{JD@d zw(Nrj6!Gu&7ijy2`fe)q)(AxM!mpm|f2bao#uN_>S}81vC0RW9e5^%~K@t?)T`LP6 zuuFu;fJmosNdy5It@A)NW9H&S60v9~ke>5H3k=U&2tNd~)28IAhaYzCd-&n&o}8|? ze#Q_jApMFRIm;h(ZAt4R$Lk~PMmTW(I~`Gq{@A+lVYdI_=l<|4o^|d!Vl8t<`N6Xh z+qnB7pnw~Cs=B4niv@F zaz^_1O-%H&F(nBAtn)-`SNB+!O(k1XZ7Jtj-Ow%VZN#hd%Wo(9TH8`=u<^!8+(%Nmz7E=Cf~(rfg$6x{OukH)sv{zm6popW$O2}qTI z@g@4f+W`g@r4piQAw7qadRpYLafTrj&irpys!n zZ(H`z5a^nhEqhw{jVL;gD8-553Yz+U_)43eX79nT*!?;jY1`J>nZntqZAY0+7#PIU z2?m2R=jaZHF5bcug^GPQgt0OYuO!`830B}o=V%m#EUAc{as3^>&%5h<3TghiG{?tK zrN5!d<2y0Q!Kq`mMcK{fdV z_EdmI!J1-rkRT4Tzz6fOrSRb_Vl+UNfFKUoPhcKE;>>1D2TTrNT{@P{w}ur_9rBRu zf%S%HAUyy6{M!HJ#}@ZA)h}$&qdkBqRHdqVo>>-8vNLjXTeH2;SQ1~ks4Lx?W~-!2 z-I|jsh??eGy^QXW!Lc^358%$1jR|pl4Tu21}?TK+TI9)B2CjAJKrn5_3DL} ztT|%$$A(}~F8Nyhal3uR#luZaH#T2X7d`a090}AJEv?Oqh6Wdn*eb%yF?LavUd4@t z3z7|)RCdwA*y0*?Xf{Gj%7_)(w+yXRWf%agOdyuFJeJ50v}IH2B;YCfQPb}4irYpk z4$p}e7B|1YH)fbt6dp2U+cJZ2x|m9)TQdl-F)LU56e2f!YO~OSi?#rT3OQDXFA~N` zgS49j{pCu!6(x;18VZhrIy)ewnQbE%gF>4={ilte{)1p|PP19{BG$km$PD0}+D;Be z6MgN83^ufM0xPv&j+)7~_TDx-8i$~lg^xd zFffozq1iQlE~uP8139`>F!*`iMrQ|m-ckR#%e??EmsWpjm^quloVy^5=iEi=ip2Bp&d+JSi4aQ{eGs{O zf!GibHqSQPa|7-@+#rBBpS{ZLAR@K7n}_peDQ)G|+51C<)QmxBKmJatFpfZ>B6?`x z4R>Y3=?`{5evdQuo${r>Duxn$zMPz$DqcEWPBi<5XXXT^Qat*FZ%GeImtw}d-gk1C z?;he0AxCgQr6nI{O_2~rF5!aQUsgOX{qnD!>^;c}fte0Sq(1|J&|l$qS71gf6l!WZ zJ8OP)-F3-^>#l3yi*Qndi_T+o2OsXolUudm4CL6m@DQF9cETyz zd_EpNWw8)ofrK1a>{?i0H4ec7Rt{VhQeUxCAsGxcuz4W?k$OPW5D=-5kZwCSHCRZf zN-Wr&+;Z^Vy~8&RWpX)ql7*nk-n(&pz*(aL&HLtY8$_K!|=x2+LOjWJ4~DV+cNR9ss8rqgHz`!=pk6UmTBh? zt^xrFKe7(Q`h(o=a82Nxorw4qMLn**9>Gv)S0${6x`)j&yyl z&Vu|F1EYY&7?ydn0=7I$*YQ8Sic|+)xEZAqIz$o7 z&{rxDG>HgGWg$Ej2pmF$0mPI1>nFed?z>+<@!8J|d}`aVW7{seG9Q=EvV%VM)^E^1 z_PIa&+jrU6yE}ID-!uKyrtZ$z7n_>s#vU-rB!jGn9rMo+!6ph@!Y-`UqRx;ifC_3% z!QcR@a(FZWEFS9LWeq45Ao5{g2e+q>t+_MyMwTyLnw(y(zgFpzmJOGJa$SMz$liy6X&=Vf`X7?%a2KHs~Oo-wh1PIQHRojp8NwHT6#}0q9ofKLO zICcs#*8aX`-wZ_7y()aI-pgPFK4-VF`JtJ^pb7cznSDY2*esq4+X2x#-@5QR+VdT> z3v!v)qF@i^HWZpDjtYwghGQqU)hJ>stD+hNL~H*kgbmb!IHh){_cQy$`m6l9$~TDq zVviTgg;LEc#+;o0wjw*!r{u3EM&o#vYa?pS<~@O}&G!MiXiw`s>F8zzTg`JsS>C&u z?z0Kw_O56Q|Ds*Do6qIO;*Y8bO$Etrh9?i0(XmvniyEHRpPx&&Nj8o&LgpgKaZRTy z$t4UF8Bc><0l65)QAZe&yxOgIpMH*aw{kG;8@l+NPj5_O(R}*p?wy7O4OyUgCfy$?OKclX#xNnSkEd+*qjJnuYkZgd!lKq%%B!2OYtH6^+5P*)%2 z?YVG5dO^C@a~)O|*Cw4N5y`4y!0jhhh9(s?9b&hJ*w-6zjo~A$eqo;voRN@fXfjdI zq`}2gBpSq3=>-!e@O9zug`-DS63Ob5d{7&52DG2ACR9bj89C$)1`N}yRw-4`%J}`y zeMmyDx7us4ns6`z(Wrmrk!YPA{+^kFY9)*ex+w>d0m?zvsd_Nr4avyBstVQk0?@w6 z66qW9_UAq*A*4ROkQzexvk<(3BNcK8pP{7<+Mm+cC0?B(bS;JFcI>q!eNf5f&)W$V zI0T9$@FNXyAq_WLv|F{8NTL#g2TE(2ISfW3q)}Y12c44_Mpw6mLeaG!kJe`zp72~)Rd$gg)q3Z+NMTLv z0siwx_w?<~#`A@2ve%O3N6#jlS6`Mlr4eYJcpGp|n4_6&to^R>V=rMwdM>y&vo+Ah zt*Izsv3!MmDgntZibf0_@pGlpu@5z(g*=W2opaymT~?@#`4`kJUUcv~PMocL`vYxj zrST6ptG$fB@cB=)uB}hB0?DlSZ+YT{hd;>9)>-&rHnxK+egkvz^$Sn&S6~}wV}e3w zhT1fyJ8RwA)CzSGV0{u;-gf?%s^Yxt)4h`A-SsBOVpY1{Tl>dAQf>M%+}`s|5S`tu z`Azm*7u1OC``{E6*>SM^x^v&{>$RBsLULL*9%~@y?6vKHg0vSG=Vq&IXk*Il|48M&*%B2$J)*LF0QM7L$J zx~mx#&=>YNd+%pf{q$idArQPC0aw1@Q;$6SFtZ`EEsE3YBm9#QyDETXHN&of`20lK z*PL_o;dh)bP(QDSU&eO`-TTL0BwU7mDa8VzX!R5k!tEBcsZbkQ5n3g|LJ0{~hie8- z&_&l!9K&5m%|Yy@n?<)!*NBhcWI*q&Q?+2X@I~A@GI{9aZE0Bx%0vDG{vp_<$mx|` zlQJAM#{9`xFqdytV<|tK%9CAB{NyoSASoPVP+G7xH@bQAXjhJGTgJ!6K(Xm^u4|%i z(*SJYKuOxLjf};kas0(#@8Vw51~&Cg?7H(#v3?eP)GZ*d%dU)eR^E?Yon{86hfaGf zb&-&?i)jRHtMK~boACsEUVqUA7WDi9;R|a92N(e!1KzP1g-^nt@{|{r4pZQRcLdl{ z9Y=8YhCiRWv&K7xu_0(Kp}$+;a#9e@l;tymX~iB*I5?UTg6{qR-5x&!e_i6O2uR~S zjrc;Rz3duS-#0P35MUhiq&jidrxeX~#jZXSgS zFxyzOm&Wb;$_B#2#l=}YXgGWkk`n~bN0+Twpo$`XSlMB-l=4Dq-^Yt{_5O>Q1k*`rchczYHj+T4w(G4h zU5V=Laac&V1KgfSwCe5Y0Yga{@u;c}Zl2_^seSvV4xn#l>wgLQsz}2SX^zoM;}%3Y z$q3pz@NqX>vA(*PTDlRz_9S$8cmi!9o}k8%VxqbmG6}>Tz4CKmgaa_M(?ll%5f4y!khvN)Waf!tAl(mOuh`x6y$hvDta zd294$b(pTa?lytDK9(>eq{k-9ALzS$v_y(J5c^irbk#9hA6>*f6G)NMDdDN=D>J8u z>Je0s*2&dy#g|{rA0*c~Loiv(56=|%83cw!xYq+tTYiY28K&_)Yij|^pE;Hml!aIU zsSF=(U%_7bD7%V}w5D+N3I8ewg`6%#KAh2u6rQD>k&pnB9lV<`hZPf_Iul@=6aBfy z#@zY;BAp%YnJM&)%fSQd8&~JaT^Gb`2iJ3TqJP@?He_kM|H%VJehBryUlaQfjTzUU z>0;n1#Lnw;QTb?Qcen@wK@0|WTDSv)j@?$L4k`8uDSFu-H7Cf^ZcCyWrnUk*Pv00n z>~V2@zfaw^8bBjy+_^8=6>vt##UYytbS2p}w5LLfVd87f(fLCnQ5`n7@2uViVSrM` z*w?}nOC#vs6@qdAI&_|5F@g|Hq#^}FD3y0QAz43*F*5^dGoycv=3IMpMU?? zo&RS)Puj(gD)0RIO7I&dmJn);_xaCY>42}1x@FE6`lNJTamXH@?JCi0*h|rM6)7qX z|AP}V`@`}s4W1*@Gy7i&_@;aTf(gz>*Sx=!AaHy6s665-t~q~GzCD}p{YMC`@=*y@ z^><1w7jRyG-y17$f2DZ0@>;ps1Lx04>(4)oMMU-S3l*_9;b-_G=g1#>{>Jma|MO?X z|HG(s0WmZ&T`-3jpEa`-VEL#_ZWRfyri!IcC}`+O*XPE6=xF}8jJYHA);TM2#2t7v zMAf0$xici@$Lw&Ns*jwr>YoaQ|AWTl-R~P;rS+9=I1zrZhP=?%%p7pXQAAjX778@q zXAc+5&Xp6y7M;Mp<0V;(91t`n>Cw78SIyO=5iZSFBE4!!jau$55xZp$mFyb8xim$I zvcL``1nCtTHX&qse0rivx{gcyj5z(Qv0o@S_Z!B3a1wrXzj4?M?EsTY3O9jWi52y1 z2unO9#7EGxalVVI&MEiw+dB;yAj1sV*r^|&g9EI8<^ZCv6cum4H=j+$cEzS3Zv`bH z>Mrab>#^f528Q|Lpcq8;gu1v6XzHT6SvaK^e89hrY7^WF9ACTsg53k>KEnJ?8-kL1 zc;@)zEG1{)A|nLGKEv2290JpU%1(YHaR<4nXFzU)f7t@cZH0Iv%q-0o2`ZmPUt=?A zewH;a13C@=fjA(Q4dmNq3nJ7A6BBs}JMsMf#*@vBjfM58)Z~t3hOuhLj#Y-SY{z6O zwZ72U*nF~a|MNqmGCKsXK&jJ)adsc#@5tH^Y&W+X`_3B1>6D?zy`VE=(8t~bI#0nU zLMvj@TOD2sJu1kQ=FW!BhKR3*Z8(}Gjl;g7p@{=2gF){QHBxNT^kF-DZpV%t2i8xe zRt@bioQsKeE$QHh9zv|ml1{&&I0Qi&6&bXK;)*d}-9)MzSZTqhSUJodvqgNdIbI>| zfIbMA=BX)R%LG-AsZY@$;jpxy_1`(KJf)G6c98`YV`Y|W{Gjrj5@n&+>r>+J6ztpP zXadQ|O%!EH*q9ZQVk}n_27S*x-}79_|DJyVExv@fLl;6SAi!nhFBu>UHeX-^$w9IZ zljAaAz;PH;D9(8}f`UbYLZ`32_PQL0y%)Jcc z3{so=`zYjB`6lDm?3hWmT?({qJq*@4JDV^ zZnqLKoWA~g@hN)TPpMHv9fhKz;W6jDjIurF`8WRi_7gsHr+yZhc=?372M{W zf0@5ax%-vZU&Cv0M9twB-=)iO^!Kfv)ONE+LGae;MUMWg+6DDkCDS^4jZkhC)?bMs~%^2*HLMXge4% zQ6g%$I0{yIO*AnI##jO4Y@ZlskeQW6lqebm4#lCy_qi{j=Abg*%_4t3Dc=6R5%j+I zifXya)5;No_tNY9uMib0i z+2fnLh6wj6g1>`dJ_*TOUK#PC5y$#g+UO}#^C~#{`C+3Oa^4JuQw~Dw=9{GbaDvvt zUZNyU8@|~qKmsjWxY|d2sc$I03@)qxFK6!q7)5#JkMBD(J2N{wJNs|4yID4y&1SP3 zmav4)W)qUI#2Atg5krV6L_~}j5fPChQUv4_sZs<^saoVLqE$<+RrG5)OReo;YpwOX z*2A9l{I=M$ob~KK&g&P+`1?FFyGa1G*PoKz`L{Fg`_B94c|Oncd>&jl(aujg*%ZR+ z5GYuBkg10P$qqn6HwoORjI04lilu2n&ooy}#6X{*=i;Uyo*RXxFrF`*w=J@`L>95% zIUI`|jwe7Q0g^V8L@>vWkKw&e=jfjsNkMIFlpPM4aD;oYzDzQ41T$An$)eTtiLEtW zYi-D0zkU92eiChEWUiv03NDValpZMgWXShn)1jgAaxhLWO zKzVYABOg&3mObt4r*q_Hh9nqgdyo;I6$m_bLa#nff(eU?>PmS?mWK`^B>gzSO)MIz zTVU~f5a3+|zu6l;pKtVN1&DLGHhfz1L}^_lJD|;GTK4j=iHTM|$H7yFkPmHhFNog%LuB;>KUz)Jixl_kDG(lhBrTLQMJcNQfdpdv**^wZ9>XWQ$J)wMsiE+Ck(VXRrt)HepPv+V*yUhGD4it}a4fMmD@h$7@wWfdd zLHZrJagE7fus)aaoWrvW$)8HTVrH)*8~zU=OPhJqO5AIhYym%08V__Kb0H)7gEY7p z$PG?(gSEurd2~tHa8*d0AO-!19lyl&G06QUdog^BwOHN+R?!4K}@$O zDg9Z&L_xSVz|tcjWkIqtiGOTag1!?&b2}pa*<>Qy7wwpv$oKbVmSlSS^NG#zz})_o z{kg6c+eX5Pxmu)caogbFJkYqdMYOpeCcC~iS*>U)@~cjWZRqTG9dk}Ybj*wh9-`KAVq(Y9BfUwmOM;SFwMymWld`wR{`iA zMhwJykgNm_5i-mnPg&1dc@WmKJD?2)6TxUD#9e@TZ2wt_5zx+L^ZrPfjQQ6Kzq3ws zj2zAXkT@~lX8t0w)RbbOBjMfs$aB#$jpv`8>&( z2-vhV0?>}DcdiZy8kz63xl871WY_}@0-PGsZLG)qI{X^E2!4iu_)v+uA=cU754K_i z_G)$ce4U2)-Z10ZYGF2j5dojZJ*@UM`Wf%(BRNfEN?^;s1n9!7Wri@jI_*|d_IHZ%wer#V1On5_vN+bLo??nT9i70 ziB%{5S~;Htqr_fXP1uNL{N*zX$ZAd2=#zh?P~^in0w2Z$#rC5fT-5YGOMff*$&@^7 z8feZo30#U1g6uV*7EYvqQY@EiMec@C6wrBxIY0lS`FZ%!bt~uJH2<1)k3D)LJGt&r zwzNl7t@sO7?2#2~Q)w}K!C@22Soc^@4?LwFqdAV&cq@;L6Y@tjTc_@ z;@%t9CB|{l68_>U_<~?L&Cpl@T^k<54C-G^`nQy>HeLAci2^*Fx<}I#45;=fS_+@~S zSinGq=4g%su zx^5je{kDEd<7?%F77J8@j>x%Wqb3C^&bwmi1?B$!(R=^4Ch1)vH!siqX_e5iyvvZ*43NE6_szQSMocX!z2Cy#d-T!5&yVp^%c0 zL{p?SHrWNnY1I{RxqkUe7rs;m$DWIzpq!AL1evirjngjIB%FTB1$tZ+7lS9jw6uZf z9)0iQ{|DhnZL& z6ZZ-DOnBG|oYbrnYDA;UcA+RtFpG&_xZe7ui|Zbp=CvybF@Md6FXOg}1$Mb`2P!A& zzLfW^7h{A_Oo(07FUb3o5sd+ed4hKMmlFXULZ1;Ab;cD$Fb2JUgFLqDk(BZ~UbDb*Pf>wn@?%8_=!8B;g0P`Q(}>;S{pGo$Z%mi#dB z_)+6TQrx;#OtR2XUreYqkb${Yh@lM3HV3*e@mcuIUPPXGVC}>tVPY|u8kS(%L|%B0$ zi3uGNz0$OaTdEbNsiGVQp$U{y&}|SMG#B{xT;mz;R;Oek`9@T7TKKn#=d)kl;R?I9 z1ElyDT=!G_-9acnY2CPPZP$4iuiM9qg?wm6!Be;?#x>x+%V>Hs-_cRiJrVd zNKYn@d_nn86v=<`k(NxhUZ(?f)@9sta1X>!>&b@&5_Tb)?y0W}he3#e#Np9GT><~1 z>}4$*rOo!J@xBIr``YF@^BnX>8F@?&0{3yC_s5WbeWva6f=F} z4DRk(wS4a0c_u}bpv~LeHL?b}>#u~`mX_KWd?Sh9ThFw$%}wT;ku7A&rFRCw^yKmE zxb&fWm)vaka$sQ+LpLw^^4Hi+pLd72^!bykQ%G@u)Upi$cjEk}n%0_FG5|~#eJO+d z#9Lu&GwXp!@K34-WSg;i!1hJB!kP6jt09=x_|hU5s==rl-K(y;aPK^mM+5mgwRyW{ zU$urkX#5&p=cq|lkZRKE*6TLRjZB_CCCc5xGM`?RFMJ-E`p&6IWv(h#9`{jIY~05u z5zp0zalz9HQqxo=Ssvb{C8R3L{`-ir;0S-*S7E4n+*pwD`=acmUd6td8F}LkZc#cu z`eJ!H9p~D}*oS%SrO1lwJ(C*{iSipnUjc3? zgH&z#&P=AIEfbI55|LGeA|tO(CU-2;R_F{2B5qj`qp>LBY_hpba?leBgRc$$!JDxK zEL=J+A54PdrY_!(%Y0nzKrW+)^YA4@AGjCu^(4&KDKl0U1Cj(X-e$su@vs$l}*P&N? z_9kl_Uv>B0SJBo>#_>5TuI_~c;MG^t`rU^z4Wmp>3vu*ICLn4Sfk`x_#8VQrAx&`R z(Q^u&M4S!Df~IVkg86~k`?t*@c)4-wUcP!xWi;Pdxaw{c=;1Hyq;YQCPa}L+*UX)& zE8}u;PrC5+YvXHI_s&?%X3uDyX_dH=)xX@`d$ltku}bcqST8BLlYUkF@l4|ps!#{o zcr7rR0(2&k<|GPp!te*Qg~3l%K@tZlACN8X=cn46ZoQ^^(e-(^=#WKse$Aq8xCg>J zfGuSsqg&&3Bb;UobZi-%8%6+!9G*M4rGq^V1Q_pu-YJ8Fwf4ce0EIw zL|lKOM+&;GB@&_K`gq^z55P52@_S>C|1T0LbFJ4rNK2)aD!H`v>G_g5+lAnbx&pjW zk_~R2B?k?xA;qN&2KY052jK^R0AkBTOn8wn&ZgZ^G{*w)H2oVnha^ZGcS_qevmbB! z9d}t*lP@`Yy2rtkfJ1UA60gJ(t(6*JWBKdaDUPG--!YkD^G`eM0?P}o8Lr`W<2-XI z_+Hzk-a=(#z{7Mwnxu(7FfS;f5ZQfvK21ROD7a$K_)u0GXkx{nhwORS|~y>f-0F5H&4!*_!9p?8o0A_v0I9cd;KEgY31K*FX9` zr_IG z#}6t|71tQpKCQCX$00+s%({B$AQ6Iv6-^6`^S+1w8vdE|q~jCtoE z{aDLcIX{dTbjv4tVr~%g{gf=R#Wy@=nJw+JWHfjUIRah-MKD^4ZJ2bfLC!IA8#W%= z{>Rc$+N~V_fq7*NO=9EOD(D5BMZI7eN$ul067>_}IkJEOYuP({pX)SlW)|S=2TZV)+7kF35f8ZW;|2c9?u;?^!Wm+YVDjJ4Y|Eck9YjNoNa z?gBOK#zBN-y~*-)Kn> z!un(CmZn|}dm-kWb8MBSFju5lu!sZB^@cP3;B^jGv#(&bAUG~Lzz zSA0XbpBG(XByx`--oCU6D9VGConeGn z?#m{yVy165x5)pF`?C^&)yTNfX&1yrcE{7#1Qo^oH1dQ#?dAmCpb$bZyNhf@*L}xS z^_Yp(S@$<^fAC=XQ9r^3Xqq>rBAUW6*+V|c{}c`+&T9f=w*-87&C`;!NK$xJkqu1H z)=j~fJ0h->Ngf{WuVkyR)r|G132W@xrWy{^#_cfv6ESkX8^3DIktXck(f3Z$wv*3d_ckV#uiq;-*|058_43)K@ZZ)PAuJc zyAboqm~R$HG%Xpb2{RC>uM>^$V;%Y=G2a@;ENO?O`;abFILp;@4O2~GU0h|#Uo72E zxb&x#p~{Tsw8TY>r8o%Kt$74eDL@iy=j_+SErF5hsIPAEw%$5*F=jeg5<)HY_Yh+* z%bn3OzN2_qM?rlylrAiuSHEl{7^&4Cb~F!R@?qO2w#)eZ%t2G5JoCgI)#SB%OD5Bj z6wB_~x$~HDoIMYpq(i3|&1~d4!a_Yg&lx}4&FWu0MIK6U{wf)~$GLZi*@XJScppdG z{?$?axU3#h?3)wc_mXP=%AeQyV_LAbY6!6gk9}f&T6p;}@S=8fJSQ80&|S)1ckdfQZ%vd{U;* zHwgzx%l(@+NlT*zn`SHxVs!?T9->4l6ozmBVCjr6}K*Fv{cERlrIlERznB8F~(s`Hhd!RZtbBEV{G3okl zEmG#CJZ;if7kTVVcHDK7Q<^0?CsitPyR6)>3vQ*HNYkVQG>GT9o&r+1&2oDu)0fsU z7nJUd;{|dB#aw@+SnqaAJ}Rv{6OmgJ(4s1%f-t|C)?71u#S*>CDBjbQyFWz7Ll;%W ze2(gt+rA>ZG!7E~7I-a-TqqaLv>l`q2Tv&Skf<6zrJVsuIp&n+$jZjcHr15NPFFW5 zO+=(o%1v3X$a5s;&#y#-nPq+LH!>=C&t1n%FjLtx(pl5@iXhdhH<9CkUrxGvM_Ez3-*oLtwFj9$CYDP15d z*-IwLij)B_tYoj8SJQaDx2(P*mY&j;h`e+0P3~cOqr=_>PUl>34oY%(a($gA;tPRs zQJ*rUv-ZNZ-eu+`VlBq5I$vhYZV$%llisKvQe7VZ%mJUCFr>#_k18YT9EZi6RfZF@r*xG(% zjF8!Dv#$)QW%-(;7&B z^U+_-;W0Kw0L)ktJa6e?5D4|DN>15}U$PW1D`W`XLxt&+mdcQ_o%e?`(-UU<)m zZR5L&)atEwN~qFd2^|Mm9Iy|<;wYy)>6~U6^3;34u7NUDSB-V0iM1)JpcYWaJvXQI z(4p3!-nsg_ER1Az?<(@8B|~4yJjm)+bN1DyN*pILV+O-rW9_5WAk9X<2WCAMAQus}& z-5X3jnACl%?LqKN)x_^9ff=iux+u*${15s5^Fn{>w{@3fSi%2@pIwQRftT>^g>HWF z4VUuTB8^|PmTEBUzX+_FGqb#CcU?F4DO!$z&J}`*9%13KN}q}&-@s%nx_pyN8Fe=}vDTv}klDdY=1zDAK%=lE zZ3|?t;+CuOZ)Dj(-fdPt7U}#vH^h*Y`N4a7#_TWAF@qfZeAFn(X+x9g#{10Ea* z@@L)}u9k>_OJcGgQ)Mu8KXad7ilY2Qm6H?85`$@!BCIi+j73|YyUkrEyQs{@_s7di zWa7q^WAT9Z;N4qL^SxuoP$lTkZq#W%+DbvxeSz%etO2?KBE!%hD2QWRX|U#cQYI)~ z%MVDku^L?-{iUi56bP)0MnfZ5k{cUR2r;8?e}kXfJ6uav7m&9bZ;FQ`RXRiv5Y!qB z-)nsAXU21{B25d5f6Mu7=BvCzl|k9kYla2QT4R9Zd^4 zsY9k>G_j6n)`KtA8N~Z%)*6-mE0yGg3Dt306$ZPMpqf>t+IzmV2$kfWsO08z>Ue)^ z>-_ns%o<}ScPr|fLVY26oQDekn=D~9Ki_8mcogsP4ZKH+dT6p(0ZfjxPpsOR`Y)M` zOL7UVblAsChz^T|;n*%Zv`cbc zd`|TiSuT+{U!v$XH&=QQ56M((k|zmeypHY(bsSq+tXKDGbv_%Lf)6CBj5?C0a8~`f zFOL`3IC*Y;*^P>PimHPTgdpDU^RX*^*Gy~!*I1R>&#CTy@E)B& zGz&J8#K^u~v~_-3XEoyoU?6~x2Gq=a6QvF;+DWP3k^qUQYDyjfPH8V+HNC2$dU7(7 zLi*ivN92JkFRhvs3NTgh2d6bV*ra^x1${GTU*a`Bx)g$~L#&>9P3QfI)^ufTN_5&e z^($L)r$cTRnRMRtioO}G^H;FuS+o8qxbQ<&jMZOd+-=S~6YH5bIV>jB^Rp(Nvnqp3 ziDd8Nz}kLp-9Njce{WStbWnw^rCRff401Bns&}}_syFvWHPtm89sTk-mA$9E-K?4< zJF4oJ8+UWRMeU&!uvwfSwNH=qHMh=(`4ww!7&!HPri>IMOI*oQm;eEiGD#^T8Q)Iq zQ2_~=>L|MhXeHsLWATxgbdxZ0l9xji!xE<iT|X)#UfE(6OywN$b6d7)cpO z^|jNim+?IO!cil$cEj;(T|tf@Gf22T5>P^&9lBmW%$e2GbS{+PYLo4HM=Vwc`gSnb zmUMXsB*-8>6 zP1RDW+SHZH8lZ~-gR}5%Ah>yo<1V9eVBi_0E(5nHMe&Rz62=cQwRL|=26W^` zPjaHB91=vJQi%j8Iz?dzk;zBNhbj`))Lz(Pk0lZ;t%%9c-23*<4J8F6rN#E%`^;@) zC|iptHIUHjV(kIdllQ2q*6dN2C6fx0%_Rj9&=Ctygp~F#jDY@Zi5`jEQv3e*n4F#5*dsd1IyE) zIAxBDhce5CGh^HA)kjvX(z041yt<<|hO|CXx~P9pLplFEU_JzHF#S&tO*vd2$1Zt&lT#}mL%je#iuB&`JbLM@fdqESw3tGm zKJ_rO)4~56{j144-V5R4NWF^d4In#-8aR^8j=tOWL`(DjERauy%Ob!y9Q}hRM>8=UEz!;>g(BVP0q$fUVUvOmX$S4u8)tr_UcG1Cu*aw zH+MA4VT3BT)t>a|bBTX$uI@RKi$=ntkmVGQ=LhR*)W=lHPCk&!fm+u8^PtLkfAVI; zrN?utpaoQZ!Ppg!MtyZsC9fx^&!{V_sPJc&X8e%~k7nn}Z%CzXNEusiD^yM8Rh6Gw zRk-c%I;y4$PNy)ns)d{NfA76_YOim$_wLl?8NH%H&s;vuABpHwjsL8yE^}7C^;2C9 z{VVCK7k>OKeDf@>Sd6e`vqi{L@vJerxh%=Cr#cWcLD)Rf&{`%qi$lRoyPH;(>&+<7aCR zPMXBk1QM?Cvj9{m9hl4RWIGtQW5>&+QXoMhg2&;f)tTrIAhfP8R<9`VOGN?;N>vGz zOYmvYu0&rjIJd7qnsqo}xid3Ua)1e-&pAH+SHxpB;B9ze$u9?rZ1dZt8he?2dUZ`> z5c76pS&#+oDLek?%!^F_9`M3q)~4k9zrwv8nUFJD=S;+c-X({n6Ol2sbC&qD6T0S$6Cod<3q?(W3nth>f{+m%fu} z_-&GtE8-^Ly;e-f66IQiBKdWJ$5L;;ZM<(jE|5%8T-0cFpEz4_mIZwOmS?wYdG>h- zQ_=KExe#lsO=}T7pdtgN`JXJR0X^cW@2s6C&rNsKd(J(nrIYZMULgqW9cyZD-(I_> z17RkDuw`xAy0vXEs<0B+ooo-5C*?X35N0|82i0_95y z1i}-a2q>EpqrCTi+nV+c^dh07eNEf@Eo--+&xu>sP8ho+8IboGnb<59$}}RE_?1hR zXe&3t=zElI;5S{vot?vMOXqM`*Dzndq+@)z8F4y>v-F9#GCoIuoew<}wytV>g9E5j zRifxB!`@tdoVDdYwi!mgg~eH3x$ztKzPRs z8xkORQ8u!|MpaNol$_YUHzAs5Y7;K&J#{0*oj!C(SNoQ~t-iSB`Fx(mT)rJYv+B#G_wO|R zFI-F!?_=V4)_brblTr|aNT_av!53`EJe+Fb_D9`y;^3p^d?X%7B%`}`FURQBqRq*k zl`E2S-<#MnJtJy5^7hJSg>ZdKy|(y7&jt=Df(Iv74}2!z+u#nFmGK z3A?k%a79LcBC!3T#9iqe?V%fShnvCKti_2QIlex z&=~wtnD2U=ii1i7;EX$rsMF_ws%*0EFFEWlBG)o@%`)O+{GJ5=k$wWVW#}Ssr-c9t z(;@C@R&EqAsW6uwc2afOHrz-OCYBxzL-*ut1Modwf_f*+$RUgwyhyx45^IYn1xduF zT7;9Nt?5?2Zc98pIRQpZe!jWQ$@c{u31$hm1XuSHCO_ei@?9H^BVQ}p4j zfsbw#0#8Uhtwcu&V#1b8*sAzuAPNroTrQvb7oV#7Tt;6oUR7TOh&xf9uBwk;SQQG! z+2ryBySc4<^rN1(6a*)sHn%=ki{#Po#BnwrMAPlWZ*ddz^YiiGA3{}Cp+5xUPUFt< zn$4kjJha*ROoZ9zk5^vX(Q$3%90xDRbArJ+1VBG({Q^HjT^*Q5ck;LL8?fF`8~it< zAvY^W6#9?QUlHt&zJ*0YOiJjhQWEnA5+~FAnz!dtBwAnQu5|V#?t0Tr6UyZbhpY z&C3NLNZ#WbmOf0{I$#QB&S~h0RL(|B=8(*xc#-ZUN`PSUC=sR~YfiUHNftTeQNY6+ z3W%}7Z^{C}tR$juQ*E${;!*ij4vQ_x&lslkw75tH@n8VIMP3mYQxTa)g`?^xkwnni z$_OU^fEmP($2Ow&W|07wSh6@OhvW1cI*vkPS*EH|E6a8(-GlyCq<<~*GQY>|Wyl2w(UxbN;9yL3DNf|=5S*$P zLcZoHk8#KZ%8SSZFG}iUpvyQY@gk!d{~;A9?;u!qm@Eq93$hfQAPTu8OfRJn}ROfkO6Q*JqcIisHL`fYuq{fhtO%G3oRT=mv?+{>YVQo$dWObEkW%k;vGNa5{9x0zz zR#$s|nP+jeQlp1lyy#~P$#mp;hu5V>eGzC7ii`O<=2v^YT2sOm4MHcpu%aS>y5drJ z9q4MAa~3%kM5cz~=hek(I9}KDm04MIDt3=M>Ua4>CsWJpc8Aj`#vB-S7eBlBkC75tKzGDGlqE2eQg zjP`u>49Ux@7l`T6{+n{1 z5uAcKQy!B&-L@Fei41JCvtR{)@>YXv0`=38w{o4X1_cFSSH%vHCLBMwsyrl>XR>VOCtUAI^q1O zv^4k4UFmy-OK%>j4;A{2xg6^q*28#WdsO zmSx?IP6uJQP(0z_`uLdkGVWj3K=wO^wSm?GGwZwMlY$DH1ovd?C7*91?5QrE`ao&v0M6ci#z6a_RmnAbs$d)F+$SU3z3xftAw2;noM=zJy~CqxPQ@RHgkzC-DA&6% zJ+!e-dH3OTx&@S$b@ICh>S|jcZR|i?^;F0mWbJ{^2dy4tHikk;FP5NlTFM9tGG#FH z0Q3o@oFcRcv6q%Z7pV~vU*Na{WxPiUdveV{5C?t$gc^iMpx_ILc)7R-~P(ikCQt#`ehCc9yhGfW}EUI51UI z**e-Ly3e6A(i3xFn?QC%MR}Wzcs#~lAAA1v2grt~@jfd~i{in)0w0v?FkWV$73uvW zBBRXg3Z^5B)qrLXAj7pOgPL+OS`>Yz#EHWfDgzaso6EP`5&oJ84}}v5g0*iizu6NB z#2mX)t;bqZyBsG>zATmtR))UU`yYzKU3PD{rY3xEncJcKNALHLP}-}$(c1ck%6@GT zWKn*!uWIXAE3; z3oY&m*S6?LW}vI_`cPQIjJdKDCBtO3Ht*93NkL!t=xutV5%1;Ap)TZ6(nncgo3X)Vtjby2-cTHqWlni#2#8^ z*X_&9Em^cLTLx#nhv4B-JPa)HA@i{9W2SufyZGsoKEClAKpMHmFGTcD!x@kKrR}7*6o3QV1e+b%n{744>BF35$jE9Yd4y$^ib`4w zf$Z4ijH<}}Z?&x6^|L*FyT6uuebZB?fB(&(Q}JE#^rrS%L9g#Y`&;*4v$*QJ>-YT2 z?3LGl@uR1n_>0JbAE1fj3^#WQ8{Hz47n|F!LXu7m0@;lsS*t+!<)4a zjlIkFtY8CCeM#q9H4?y214$y$N&0+iRRtc=TM9!UZGIR((3Pc2>E}xyX3^f|%c&j= ze|X_YGPa6zEs1+B{AksXyI*>aB; zA{gTxfbI2QwH^;cB;&ySV&5N#2;3csExzgAxCC>I%h%P&%Vl1Tx{#9003`2nXFQSEMgNKi(-BuDCr{)<1l?>bmveVb%5X`YRSR7B=>6 z{qm9vCMV`QBs61Hyrv*K?VM*en;f29>2e8pY9bt#c(=NF$X@F>vB0SG2fV451^P%ZOO4q!!N#*XB~90pfFmkM^gar-okvjQLBmj=)8 z>OC*#cF%foty9ySmp?M2qUEB_uJT!{FKDmUs#Js;HO+2F2|d?zPl5HuuGLmn$bm@r zJkUz1Xe?<`bb4b?lB;Scv}HYRS41kGH}5*;oiPV*!Y?~+zMHu*|E#+j*je>Jd)Vo( zOPBZeG0qWA&&^J9&S(#pcTVQj0M zrw3?K6l^zRUT(m=tRnhTH?!Vh{KN5+$aZJ48hG23{s=I`l$UMK#C99MsB7mAjm|6m zwb!vXc*H}3BCh~}Jm}?o;-T$lM^=2o^9f%x z7EO6WfF^J&M*V z*~^NE-neFEMtgRg!LQ4#;LZbQvg~p;ALQ2kkPH396+tMvDWc|^uw{xA`I?})!1Cx{ zDR_RHLF{-LsUYxmIHMy4@Vw^O!R6bItX-;f_TRO9#XYPpwi{M0WvlTQc(&xBj@L&M zfJ*iEf1?-yW%KXWEZg|q+pc^hJ}0`d_o}S}k>heQzi{)09TVYsPG2tw_bB;TTVY9d@d{DMX?ZmnT^8LKUH0()*Uy0I zvA&l$pJ$_lC3h;TfDoi1R!-cu+^j0FOpt&{!pYbcVw^EgLcl`auXhh1fMPusLy)k- zU}b%);*hMo2B1@W)B0R~mm)6&p1$=VxT~{^Yrs(8=uz#ppsVKXO1I0N+CdQcmFK~? zTj^HqojKZ~OFE4|N_$t3TO-DLDL&E4=TwYMFbCXO1Kung4ZHtU%;Hhg7yKw04#qbB zO7!BH7t`-PE0U4sL*jdV9!n6Y1VT z#FC`!RiXrjSPnl!2m|O6s80i?4OS)DQ2Ssv{FQ|ZE)YWT493vdX4nZIQ2ut@sp4!9BUZlo~g7bY^frBNXh57)`j$IXqj92@Yc z@k}&4*s@U7Xk%gVMD&T`rIE!du84;gwk%eSfyY~(U=K}==lxZeI^9+E(elNum$gaqV}or@D#b?TT!8oHlmB@ipugut>#9we5jJIt17Q`M4yTYceOm(dgeOhh8#Bw z81XWsxY(0y1=R6DiIN1;pQz72L9gbG5n_S{cbFlKJq8 zupAJh0roRWrD|jX(YPL2?rRAJ+B*96_?d?C(L*N#vZqsrh}G#60>VlBh7E=g(XZEO zdWTY5o6c)>u)&majoQJ4+Xh~#ezL*%b4~sXBR^rDq{gJ%)PMt(oYSNT!9I*rG|L7} z_=w7G_k)m#COnfXPV%GM+K>g=12%;WSVDc^`^75l>8|xctG&kTfTeUV;jWE$!^7)F#h8Zz5Ulz&p| zI^$j@uyP-F_3+9YUTkI?hT4OywRmgFzkK@tuu$mezebrQ0m;0ZO{%zK@q<5*W%j!pX7sy?(9&!`S6gC(Gs*B4+bz`u>d&d71$OKy>W;@eB zMw<*x`##(DHw*hv{oN%*3-$rAHXUA+3gR3WLjU-)j|^7(W4a3gf;GJ zyq`IEKULWACxPyglLkQ!5V#9`6KzcgE{G()ax&nnl|wt1Wn+;*ZM<(~z&BXm2^caG z*Avb1N>(t|A%<~#`hOmsC(DJchiamCT)1|+EJtQs?)*o3RwHEGO=ur@C9$gFJm;Y$ zUc!TcK~UIbQ#Cuz_DJs>KJ?K?haO#j!|W?p?c2Ai?XrAS_%r)KGJg0yB79%Hy5oUg z?XPWXL8v>FL+B4D&<8YVC`ntNZ80Psv@qJ2)?k89Qs$gW?;Eb{6SkX(qac}T3ev-2 zLq+{l7^%=~%t0C?_{Gd&S}2Xm0z+EgD;$pM`>GMNCWkxEpClhZV#bOJ1Sh^lAGEV0 z)vWhkQGK0tBC2hKx5EJmY93KI&6eIES67Y_T1-?gE* zFD=|2z@d0J6cUtsgtRXnN_bSQb9lJZw38}T|I#u*lbgU8@XSicRG+&&}(``))1QSH*-dM(dvH8 z)G0(H6AK#RN|3Z0$0lrldyVnNw`c#ynl)=OY%(^#jUC!}_St7Uxi?4OVq1-0y!tAu zH8!%X``4`5KYDT~vzQJPH&BLgt{nwlnKkggf=X;6`lhdSz?>tjO`xI}4p2rUZ&M>W z2xsnBTRZ0v9IQ=8kq0{zO}~(il3O#fWV8%-$(kC?cCL%YyB06&N+*`DizK?(a}yhl z72-L0pPYRqkyG|6Ipe@RE4aE?o-_7^)+lV@Ju7shE`Y7@nVlLwrXSxWoX@YbZLmFV zd)D@x?ImO@{kiRR+b?WyqDFv6AP)yIX=elg2oRHGsPkBP;7N-q(FDJsV39@$sVbQg zETOt439`s0B0DUau%N<-G&m5K4TD%yiiB#MjS9ggI2ANyMEDTICCEBF;2`qL!lpC} zb0;J>xcFg#=S^{kp2{}`O>q{G6`cAS@JSEyH^96;y{x*Sse!vYw{Xt1N?$HIdD8ix z(<^`kw${V+YUcxj2aqCc=>QQm=I`~D!TinInaimU#I@#GZb3Ev%jtmC*e$@F*UfVp z?{*HT4!3WFU8;ur!j)kJ6!7U}Bp%Qtk2Hy^jbt@-Yfo_gSVa`B1yid%|I;Xl<&wJA zD+bvg09G=#noIO{4ifvq82n*bUi-BrzZ*sSyDV1tdbfW$#PwT zil7x#Sr=Ml6%qF^EJt)zfeb2pMusA~k5j>ltAuMcA3RF~TAfFW`UGXY0RL72mkXgN ze89DgD5TT5yh_t-LaE0@T7Re>8m6E`1#$FGT4+UBWIqsxBL~5VWjZW};EBMNs-VZ- zo00i-#PCD9SKY^Zf%&Zhc96wtdL8Bk=;PFPXi$(#3lM2i(ycLq1GxfXhDZ&A`fDyF zP`zL#MW4gvSk>u_>4DP(HLW@h5fGgQ|Huc6ekO2-1ts0r8>e!qYIFKRYAT8 zO^taJIDT+4&H>#4s)9O;+6$3b;P#NOIX^GazL?|cBJH)SmIeYVJbKs{i>~{@nkNh+ zo$smj2^qK@rsb}9Xlck7@5!clr(6l<2B(s8aDH@}-iQbvlX90T`QWetSRINQlySJN ziw7gw#VlSMXM@d!sNOPzv6YM=(r4cnUKly~bmzv4Iwv`}kR#-(y71zlXNp@X@9Mj` z*oGF)7Dcv%MaYw2V{RA*Qe!v_*`Yjli{O?PKv}Cq3lWv!naaf}8UB5h8x@2%6F7YJ7YPyAM zPHSPD5OtXDa2q<29H|#O()ArIws4THN@hFiYYR&K(u6!H>Me--!G)|APuFYP%m?DS zzZy4OZOr zOlV*iqm?hWEkhK|KAaQLJ1Kb2oQznC6f*ipvz{3rMZLEX2LR>c_$-2u=~BFnU?2t2 z5#62cfc|V+U$XXKiq*h05#&>Ruqnvwjr7WJ8JT}+PBptfx;j0@@2wFrsityBssQTQ zd{aFRHrnASg=l`ZmkX%y9}0Q6L~X5)^Ko)g?UCgqC$yx&01KjOj7|hN`4B&o#7pD2 zh!%~=%^_|j#&W=?c~rRFAf8Ia+~Jei?TLA#FJSrG$UW0C(!9PUw9Yuq+V5Hz4fOOU zwM^aZaRH7^K5l8>l7&iAIBxXG#wz$9;Xfsi)>;0kr#KZ6bq>4J%X}`DpEeyR1rw*b zM%_Wlgd zdEn)gay;&y&Xk+yO`AKo*+cPX;zMf)iqjf;Yn#{JxZ zT+QfydN#MTCU=RyBWWyX9Y9HjD69I#W|sA_r{6nPgnP!08Sh~ej2Q}@#J7`9(Y|@Y zD)lG*?ro$q9x{#t=>|r*8~&{8fqYkjYiXvbv0Mv_>X&Ty+a9(3AKO2HMg48t2Q~vX zp$G%p(Ah{4A|AEQS={A&Cb<`#?k4*BqpxIu^u zr1b4Oa~EB8j3VL_wvagHtPA+{@G)3w`WWnYmpwRE(jw=CcmLS!##t!7^9jesnJMlm zmgwG66F5iPtWf!BC^t%=(6v+&^GfXELaOlSPyWXH()vVv-QNd?Xg4&g8uVKvnkng= z_-^zTof$y(0%ry!S1@Cwc;`cjTn#*OY98_+WccP+Z${Aci>6x;x9TCIksNT29W&i< ze)14bd2YTL*KH}f;Xuccfw3GkF{mIktVY{;c=80m144rZCDVe1e-_vbumf?WxqOWY z2SD(^VFct^Cvlt|$!FX9>F))|2aD^|?L&LF?j0%QlG_rAowXw)Y-u*%U&uE1A4=CR zZpRV;$s0V|_9l zV7J46L}RxH!rD-W)L&hNMyL1!n=J}m{XNvf0V{M3 z_}yCYHmb_1pgu|sNgfVL4P#x1u*@nwP}5-MUWGA+=0DI<({ZbBRSA;u4y)J!A@?0+ zadn5|rsikY-+9;P*KTFT-?xtrd~MBjkL|l`#oyo0F1TvL()ROqU2S}W`_AmIw`Q+3 zo~y3>#@*}J?)~aIpFFcOa_O=a1AXf+ynI>4cj@_8E=QY}Y>1R&D30Df6P}LUD6J(8 zjM)l<2#z*EJZP|Bb!^C@bk>(0{kmc8@$-GF=g(hl9GKtY)LW-i#|Ng&_I1>@%xC|+tgCz3vhJ?K zU%vXJv~te*S8H4Tt*+4}ewFR{*_XK$tNZ#^_mBRftxpK}1_tAvNW8X>-L+%cvK`9~ zuGn*^XBZAee^?kkzI@LfOHZ_P94BVD9ve7C%(7Z8YkA19q`5$#hA{+eut+slG~mu8 zbrAJHU^LzcYA2RSX&#kKS(IG?Bvimp0m*U4+v_M%YF)eWct<9h(|n0|XE+kih@K4B zVqqyu)&5+|Lg9shb?XND*KLMdw#d~c5U<7oPTbztobQ{rn$xViw12R;9j0NZ&z&8b z4+%&tWM!~6+c~dg2jV-|uI+zD?_grbk|jH+F~D17+It@5f5Q(0yNZCZVFoNdpMy>e z&5JyOpvDILK`3_ECyzsb`DBSC5IG1u74Z+)g7!vAFdVhWPfP;Pnrgy)km0AXhCF1` z0DIZN-kTf*=HRevTwVFH{^m|mTA{$XORt;9KX|n?NpYqY2wT?imu-$2SC?;i%DLG& zHQ=d;xfAUAb%CfOSnJF4{=a)-_C*`s8}Mh6am8`mTL}-s*I!jVe1jSdCcL$FkriIg zwQs77{z!{ElP!WHy_@^&l?tnGHTm9PA;*WfA%Tgz4lb`rW%`{h;2Q{jF zW#w&|*PQj)!G;=1W^P%In|9h4g&X6G%wEj#++tcC53vN9*< zX>koTv2QjFxmrBAIZQN@$ujkaB0{!}VRLWiFzo{lpP=-=3G0)JKj*#1u1ye_Hgz?=XPh)!=8YfO6~%5$HFr=g z*joyat=FPn4U}abz7w>d(~1f-uL|P}6m=6mF+kA|%`37rq1 z0p<7>kFNY`m8L7l$hY#C8qik#O3^)Aj`v5)Rx)z)-sHw^K5f4jw}X?lA|lum}ZKZrw{m6?wC6Oh7ZBl)4p*j zaggMETc79~3hQvC>*(FP4nY;ZLTLBT)-H^Oz#cMp$qO5R$%WPwG5ptkN{226RFB|k z5w)!$r7t8$G+ofUWc|Ld)*qJRo{$jekwdFXC_c6GcM#9{Kd_+zP-i@l`YJc8#|i^N zy2h$m9Ok(O?!(8ly~ewtKF?#1sry(s)JL)9k8$5HzT-XN`HJW3Y_|8e-uvLmDNsbV z?OJe%-VZxs4C`+*W}MlWT`xtn@Cw^H+by=+Y`bm$0L}L(TCa)61X_j=;09zn;L&P; z=*lEs51O1G`9{ml2lEf!FF;sKtc|HAz!iP~7@!%kP7pan>BPkCGcIcKTR-sAo&2Vw zHDZ%-rx}bdoyui{*u=f2F}lnzO{Y;ljq?Kq!9L&>5=N^?0Hb{XTr}FB1+{|e!iQ6R zRFotz{v$9*B+H0l;t)roxz@{y zhBzl)#C`D!B#yh`(({ZtUeEO0wGSwjmFHc0(>9hs`oOogxhig8-H6)Z?GE1fF1wWi zb;X}_ciHV--S*nG*W2yaueBQn^WCvk6t~_Xa(CV?Nw?oAd2hYL;ke^g+4wyhdi@SX zx#M*uX?%3M(|J3yJDtY6UsTmEvanq?E=CTfcW%?P+gOEGZv5A+;{V6oy9Y*9oqNMs zd(U;xp8L$6OD2=a%w#4BlVoz62}wvoh{+H_2q7Yg0TD1DP{oJ{2uKm}hJc7lt%su& z(OPe%*3)A>>gl0WsmH3vMo%x=dir{3kL|G@+P5!ew%_krGl77uw(s}-@dajP-}l;U zul1~FJe(QZKIov1(Q%3yKC|>3ys)&By;@zpq&Ohnad&p=p=72L<#Fg8zcK0$L=){d=0K%mz(T)aQZ?n(?LZTc@Zp`2CIOF?0lHX zK-sH_fqDfdR{Rpiq%;;$(4gzZrAV->WF0(L!)cgbdfwFOp4Oc>1;WEYWMT z!B$z?D<~h(5pG?Xq=8gyu{Z6yr}Puw6q-ZIsfkBekJ+IqG}{CuT$dyJH873oES~8; zEyCFvrq7$Te1>8$6>7Bho{~$|-&=Yr+1s8vJ@~JQ5D}4a2KCM&n_!AJSI#tsH1Lcv zE_N8Y8C;O}3 z7=feHo^;NLqPRY{$ij~7iPf3$j~BHfujUt(^vt)`tmz)_hR)fxC>ML4E8pZiKT=Qu zn{*F65b)q5Fsva0E@1xars(?5muL|XZ9%GWpdVMuQsBmQV@MS8x+B}4*_Xk8_S#<* zPF)$W+plfW^lt$@bSjgOU8{KtZ{cuvcP5hor!W5A`8;Wp$w1!h{} z+C)q!*y8eh?i*y_5)eQ&Fp@KGk4UF&awdZmPJX4aQ4(ZnV^B^eDaxn%66J(e zR$n)1nRYQ}k6mm~Z?FtqGd2XzhZ5*obV-+7WHc@?)$gu1F=)1-fe=!We5S3|{k8p# zPa1NXI8_NCUuSEnfwZE}fArBnMMdBp`eMJ45O<=}9XJ3V?-xGg>1jpdMbr3~pf#y4 zAQnV>PwhS0&%9rH4vK~S!q!`2ccw69Aj~ReyBMd_CHaNPtkS@h^F#EaF8;YsbsQ9c z7HM=84quzJ4e1nHb-Ha+xBbka>D4%p0lHUX`{GBnnA|4It+(=@M@{&9bm|s$p~lW$ zcR<&+t4Rm!Du4~X32=O|_0o`HJA&b^ZF(~jT<_#Bw8xz+x zrc#e45>?*Dk-lE#P+-f*l8*l2^($7a4rvgA9F=ULKrp&tILdCh+I;oD`1}D&-IfQL z>F(8|x==v3~0ZbEk{Y^$la~CyK zQKHVSO~)Y~VC#WB8Zhk~^ei_mz!5;Gz>%sGKWv@5b1{a`>|%HbFWWhzUOzIkuCn6D ziaUS6vcAP#y-juT^5SXJg5l~w>uAXDY99_QTy^|*jeM}Pzp*72Yl$V=TN8b|`ctm5Xe(*nMwM(yDz4S}TT+X*6;&E{WN8Msp4nT23y(5pi4N>a_>eeA1|MI))nRmbZ;Zrw! z?G0A;#@B9m>O;0`$Gbc5{~2cUZ}*UGiEl3R3 z*L0w18o-r`&1ePxZI#p6tV~1hbdO0M9(KSXE+=f(CU2F=WOthUj@rsHAVv~MJ`|E= z5CxT2^^}DWtNh_RxBssj*}lzhW|*ON<=UIm0mbMr8UwT28*O%_+-Ph!#;w-ER=Z>L z3V)l!=_)fy?S?X&t@x6aQJ+J0NXFQugBN$DtyVn?md=~IvFm5*|GMKt*u^LNr3|}s z890WQ0wc2p?41Xpgt;LcfNH^iOg#W7(4$3oU51RgXM zz@W)2NVXZiAK%cwaDlV}d4xmgK*Yfnxb)c1!B{v2o`w8*@rEFCLLUzivN|Z#wu*Te zc`E>`?9}=b%^{mFIzNaRR7!q1<5>QQtt@BJNaO6QG^|o#IS@4Eg0g6Nk;l>RVDxu8Ks$rHmv({&2D* z(c+mt{+=g>9vX~Gt;y9q+5Huki?ED$JT>!uMprwPYS zv6_)ZO9x9zN-nynL9JnY^-O*9~!}zS$=x8ib*08!SRn_pvErPH`WCQW0aBTot zc|;3X{!*G7#mfvKLv=Ew{w&d6nJ5WOYiw#{zX(^(p96fKBr4wS6=q}H-1ltsb-Z+Jqqs}32lpLQ4SyQ+XHrF8GvLHBB z?g*i$JrN2Pm)!1SR#`OJgu;WNa${R%qW!HGZi&PInsmv5&{co;cR%~PBH3&5XrnLg zy5$97dZOJGPQI@GUUo98iIx2#nQ(e5+uG`Oy=c$}OlCok6aMb1_NGEvt2cG^-SWZ< zu&a`seGBD=Ny|yC0^!(1ivYP86mzy};&jXD2_kWC5E~6?ZZw`?G7%@&7&R)6|Dst3 zHiAeNIvhQD1lPP%>taU3%C5eZJ#(r%%H3vGEQuqqe_PB3SrEfza#gVgCY92iq1h!h z`l!}(d1Ybk{2JM=51HVtVTH5w;3JaCJfH=Fi}_iT!(V6)>*hWZE-iLVALwotl2JoY zCyKYO@U}}X3yxbDH@W;C(JZFTMtE76&!dsUKE-1Lh#G%_Zgp6YJ*9%K$o~~S|0?+O z81$-z7kGHhc>tW9Y6ssRCJ8%=WfT$TPdCQ6vtIuauHk$o=UpD*Y$eKIL4*&Midrq+{ z_7q>eFuL#Mm-pTNny|Dg5KIJ4A*nfpgqJ12`J4y@)DJ^^12YKE@2p4&N@nxegZo}S ztoCkOJ=&@~e){nzp;r*E$A95n2hc*nID_^{rak&5E)OHUPN=gGAMZHCJ)%`_GHSB7 zm_Q%kW$$Mn|2b^*;)4i^631lk#3NRi;Z{9t7L2}s5$0t-xzvy_30}mBfOlB^bT966 zV}X~)SWn(5UOIWFQhp!N$voiOKX)(nCr2f_LE};X#P8&K{Dz5Nd>2XM{&4Pw8=nQ& zgm+>sznMJol<>Y<+sjs{opj%`zUngIK?@MnOg)dt8S6+RnpAudGDQ9vniUp9Yr@l_ zap$*8`kTaE6HhOFvIWiyn8!6`8#*@}J-T6Rpbw1dXDF9nrse*Dr&mjcV}Pz~vjNv4 z)T@Wi^!MNdfLrUE9{upqzFu3xWjO;P_Do}|dYb2)h&qltq$%a3SY+t9I@ldjU|P?6 zSgumvdGk$Vj?Ls9Cc>$Fvf1ENf5;>TWi#|464dmtfc#?Wk0%3cxCA~5jEF#~lhi-& zfQGoeMW_OU9CuS;tR$Q`x;XV}I)y?2bC==$8vF7G=*aKk%w1F5=Uw6RF7uhKfojRY zj863yr;)j(x{^ZwDxY_W*KF}m!wxWj8MMX{pR0bQ-*cJAVhL1APNuiAT^7)EOH~1j z#kVZE)N8R6RY@*vNe0UV0eJx|8iU;N6J%Y5&Hwd28TsCn*x4POL3Tm_2HOD&R1jP^ zVj7LtqR8OC=sSi(@q3I;f$z-A_W%~&oBg;WcNNq*E4Tp|o-{B#gm&Z74U&zv?twO^9n7BoyI071M6BUjqcZ%hF)5tU4c-oW z79F13I-bd}U?!72p2-M1^2cGH`m6jEaUzbVL#R$Cu_A=T0^SOSLejLUWzxv!;X;wN zlZ1o?%99e_0?*5V)xyczuAbGad%9{*3abySTGf-m0yeUiRkSASMw3`buFzTN@KwoC zP}Q$MAmY?M&zac4+Ds4!Sh>>Fs`F#3o?1Vj2j^nVE@ZJ1{@9hQ3g0RSw}z{dAaL#y zc7>BzNG8L(gsx@dQ+0sHmvx0Y5NFUC!f{=-hTH-+Ny#pu;yj&z7ksp4VOWRk<&oW@N!5Mh!7%LZCvD8|F}a!g97Dzf>I2?!GyEYeac*Cu$lJ3^>X z%_PjAS>)s^1RiirZQ6jPWwBRlGV2T;scaeiaC-l(F-eNnexN@0!3STvVmK-VO|jit zq#>#^Cj* zzU99qhHFKKulU*rAF!?uYNIAo>=7+4E1S)A=H0a9m21tHQyL0s2$+K0~#cv`2&emuV?4R%uah~6+u&gz0$Tby_>8|&@b!Xk z!FF_RNFhI<_nu(4qBQ(z;1&?PvD`BbY;PL`XJGJrh^LD&99P^F>!7c~P(7;kd0L zOg~eF36-MZ?6m<~oy=^shMQ;kI-B`tH>)q%+^iUIz{k%Wx)%AKDCJYfxlKr_GZjamgw$;7yY@Vneon89**`;&a7Pu)0*sZ=kWuL>b#&K%O zIyEi2?a%dE1XF1B*2<1lJf7;P{GYQ2y@+ezO0X?Y+8BrprWCSwI@%mL6V5sm;9nS7KUot$TH?ix# zZ_*>Bqi{l9fG2s8!zzR9Kh5g(>^4=wKe2TyIidMC^^csc{-4m_fVV_6 z1#H4lvf}_jKxs#y3nT^?BD5g>o6Ggyf}u;ZD^Ff=@*@AGLxbY~R;|Y*w#63gJlJ)6{k+=Qvx4xl+9U?v^O*y7 zOYrtC3s~EXnAQvb6sw2zl1MF=bj%u=)xp1i%s}B8t5a_+4rrN8>o(Mj+FfRCede;>jlp4WdE~*%f2^TSk=?B5a5A zbD!+@GjP#~4^BLy6I-mVOj}~&vP9cnAvM0YyBjP8bkc%u5E}6R(34IH-s~yr!@_0R zhn#k|@N9O7U}eH(8ML8-PgPAul<*`an4th0pdk$@r+lX|ohBMouEs+pz;I65K$@62 zuwDG@_E?Cm$b6D40u>O~}!`hA$F{)UW2fhZPm{>Abk=DTDKbx6CJ)ie}1Yna`fn^4e1Fl%4 zotOicVB`?DO6D5%kC;XY;OG+-%WfH%FQ??vitgDxO_$AVDzbnwJaMtVa!cFXE7Fo! z$#h46p82xix3jtF)*A#{Rp+0a7U_Eii_;}(BOKL%7kzP}>qIvd?N=0a} zs2}K@ve;mA`?wCT;r@;v;~6AKKp$F4ffMENkf?*>7CaF!#w%&~2qTD+bAgPSc3|-J zDF5Lw-P7=Yx_4om6b!z~%4Ldp-L{(^aZM=d`@(gNv9Fn{1j+bHp}t*UmXja+@o}T` z2dl4n>VXY6|I@qQR+qfT*0b-aFZ}i$Hl0NtJ!G`_KR}(5-k#$aA?|xGtR*vmyaq6V zMKH`_$U;J)7zKT&_2`>%fOW(=XM|XrQL`A{&=0v;g=QdtSXPny8L-bZ>Rg1^5m~4& zY>b+|xrQRhWI;di-yv=3J;D=iX?U+vN^UKZqInOt zhCL(+!}V$C#pz}SZnXfbb%hc4u8SbGTYVhF8)VF;tK(GQiRs7f!PO&fDj-b{+h8~$ zdnL_O$$ZYupM0*GccGF|93wn@31EuAw#ny;>u?mV7^^#X3tU6tb57ccd+}>tc)#-w z&+p`-5U^o_*5Ns7FtbS#qNAZ65YMxkX-U0pXm4Og>uU6;_- zOV}d9%GrQBN2+`L;{pDFL}4x40seYf`n_F@i*GBE*|Wp!0-|dUDAvh7N`(CRzpT|B zbO85dG1%=6yUl4^Y=hH~D5VUem|+n?pEwh z-62B) z#5+_RB#Nfe3Brk~?Ob@S>gf6nN2?G+-q(2a(Z9uD*?4hQn|85}zN(8;KKB?{6!LlUQeAu@N7&dGg!U}NW(#kGJk zJ@@zA)b5ALab6x7F)#K*`)@<9yR2Yk!Bt?VxTWB}f&vGmI`tRCT48#KUzn?@*FbHg zT#_DmqS3DNjgUGcDTiE6!4QLP=xIPB&8Z@u2DJTLe`R2v)sVIXGx%hyLB;hL``yJqN_2{&`{wku}7oFl;S|4U3Pkdz7m&Dfs;cH z$R2N5Ao^9e5{N|nUboj(-ZZ`3Bb-Ry^nv=_Z;IgZR!f@)o0|vs^~dXK%I2+K+0owK zvGS)Fk|V{bsP^!*;!pMfpirzCE1t%F)#R{ku}Oi(_KN!FT!G|sI~3wN09zc`hFx9& zp@}KNt3<;H(2fQIQCEfBR94ocKF4Cg&K1kT3cG!5^U}6$FX@7{p^DIReTIZT z_*@UG&9;OpDni2XU?PDrYx4aFK$BbGKL`MI8$+B!A2`ciE%(+T?z_*cvy4q9|eKumT!P{S-`0v2dky;$pVU4X1Vr>Q*_kc zKxsmfR!J#ow~n{m3-&-0!Qi()(5VLd1wK`$lPfsJ_QC#}aQ(&1gXQ`!7J?!;fEiXyrn%XSCb9vD6 zv@^KgQhHM`@Vys{i>{MHj>`eCkB96_BI+7HNQa%V=)&f&VhM@vrFit>78c*Qad5$c zuavS!rgwEYiy}o}6X*adOVKV%q^rv@xgnGosPCK&#>9%uoCvZl+H@w3#_F=yrDmkt z*+h$aD3J&RgNfFSAx8{Sh~eXE@gLlPzi{{3Ut?{6s?hM`=#7)?ig9p_E$q?}^C|*TD23ieh6&hVrw+I^6aqrkz zINZ}0h6vOlIzUl2?Ab#M;Go#d%RUKuM={U>bHP8cxnO_6gU~m=3+;lGt=t@n5^3vm zxb!I-(V@baH>0^=d~X%d4Iu8oGmXvME}YX$Anvw6D7R?JU=68E=Uqb7HB z?t&nr%a=(xXGF?%H1=Q-=TU@`sykJmiMr?<_<~x+)Djt&$Yzq+_;O*y+L2D z6QcT3Ntl?4*cK-UfJ}-@Q#N&QW!qd}o(niSL8Z~6TboHo`gZp>0xagT@|du!_*vxUrqaIfO>i z7ze3_e7gVkN~MEEIMLJWh9>z@hWBZLYKz-Go2JSX=g96gpB^dL3XK6 zDT@R(J%cVsbzxZj#77Bt1&cqhx;0YV+*o1^#eF7Oyq%q|=x+yUpk|vbnc5MK2B6Y| z`Avz1qp}S;eJgt1;nrTcUDLEV31z=S6S&IKCxnCQna3Q@Gu@W1U@+uY4_XG1b9^G^ z^~U`7w}+!W?HN#VHrXUYK*M&P_j(G%PtSaMhvsT=D|)Ag^Su$K0r>@B8H{^s0YQOe zC!r2o6o;%50V60ya|j4h*9mcz@`y`Hc13f&d2G-tilYiT&M)Ylt-A=L&~poMc$RtJR(@e7Xc^FU1Kk#^;nj2U916gAJemK(G_` z!)Lh+KFb`BM+0%WBRdO;GV)xKh(mhH&#X_`&FWx1jOg?^&%xQr1l|~VkT9b1tTjq5 zKJ+S(OmLeF|{asZ@dzIkAjdk(}u{4fA`8~%cp0gq4{k1uD zH7LN+gC|7!(SfP=L%!$SBf^D_^(S}qjNN~5%-hnHFmxC)6@AM_`(k~DPDA?z9(NXM zNu};zKXy=3s)DTsLz&k<+CO-e-y4(I3!ibupTixP$fh^7Ah~1+kU6OKylIaF4iM;g zS7>G;FL0{}r3Zz#q3qn%MKpLQhkwXG2S>|0>o}(b-pl8C}Y6(FBI;hnGD# zwgSZ5x(=Gxbb_eJm1v56@9=>)-#oB=8D=(Ats4#=o0vHE(ueHjmd;yOw{{-%wk~Z9 z2tivgTDx}Xt`~Zx5UjpB^)ZJ*u?bSBCw2IH^o0Gg#Sdeiu=EUDHt`|sOa;JnoPtlF z7(J<=fO{WuKy-$#4{sdIkQg}OdnA5jKuMTe+`g)QTa(#*o!Q)^KIIBtvp3r9X7ihK zTej4zb!_Gu^+j>x>I3y#nqVR7S?*?kKm5EFGntjh_QnR{7>j4y*tIVll z!aW-=THPLEGZK==P;!;H5%z&v^-xFMsy)?gR`%x=GvxU7_tp=ObyWIYmr#t;IbTsR z?3c@sd3ih8=t~;#!X{~{$eDqcN>pqdKLwSRL!<gTcCW zb-|+IU~W?|h$!d1d-v?!i%{oms=PQ@Zgs(RRmY0TCYNhj5X%*0dvw>1j*nLs>Lsbt zX0;R+=D)wS23ssvYeJIrg^5IAAuSiKNwl5ee{Jm9X;tMxr{Cd>`W96Dh2`A)d>c0X z8q_d!J=7>TDj7&AL63a>C%1Kr!+R#s1ZM`j#6=Uo6IxW6b+C5zCA<>uFMbC2>p`IS zWs2xE@+=GJHO>;39@jvHKEaY(zeOy!tJqUFj;ITceZrQjuiprI+fUR5&unFz*yCsD zTGXy4%K>y$z8sh#Bd;7j`Z%>~v~TLSxq_W?8#~p%H=B2yTEn{V!^Bxeqx?Vid`Z9a zF_PPKs)uBG$Emff3qMTgl;B;1fWT+2cuIJl$1}NLFa)A5Hya1vV}4NLqJTinxfVf< z3?_bVF#~dkWFPTQ67-oA7oG41X6hkd;M*G(lr$x#6^87Ey6EsF16wyNT3#8JtM&HT zd$)Y!TN~R~%t!?jj#O`Ddu?xDyCf`YEe^z;ns5f@n~K6XSnrV0Q7M!Rb-M=ExPuLq zU6nnyO9yu@7`nNk%zLrTbMbe#ZTxPmab(`LrB|mC(QLk5_52iQ}|jsh%P&f!#t1&d;h`y*mOS zgC>A{L6TJOFoaTxF2Rs$>=&v+jWJ8e6-#8Q7VXps8%9>uOS+J^y|#UDq^Y&t52O&L z`&9wV?WZC;SYl~R=R)>BS3m+!UclgQtwILPTAPv(LS_B_!N!gX|E`5gcD7n9tyN{q z+FO>ib{M*Xt-+YBj~zM>`ax~8pcou4zlJrL^5_tfkeg7TS5AYN0wTAr7R0y2pMnNk zQY+j9%-EwJ3WJYqcxTq!xwGEc@ZWD7P!}9{GOH0whDPZi)sLV)#yvY{muQa?_Uu8zHGnBynD&2{rgw7H}-C&JXmjkT)l-mM;!7!wbNJqq0GM9api$4 z2X0P=2gZAv2Ch1ArR6SjkwItMUw`0AR`@#3y?RbsJ!8G}23)gv6rQ~d+0mL|iiEPB$= zWO;^rf8axc%ZQbZn{77pxZUq(mnt@!VpDgiPj{2!sXO=0`dizwGyD2xW{Km5a5dk7 zjO~YnEr%zAd-Kum_%zg0AI~r51&;?dAoRx)4w%G?okpM}{!S-fBHnZX6cgB{#bm&x zZbF7d7rVuEz?}UVU;L!z$mRF_SQaStqD$3b?cVVp1zfX@H@vmww)Y#aq|H4Nv%GNI zFR{W75_CS0tgJ083-mAf@qIN_xdk@8tgxzP%UfIS*%T-%te^G*v)r=@g$MS1GjxDP z=m2{78GVQjSyON~WDM|R4bblqDubB@$Qh^!aycXNCJZM4jl4QN@Qb7x;9xFnY08)e zD?9`d+B6Uq&>`K}h{*LwPAXH=CPBreFLeq1*+i6tdj`rLJ`D|bPvjYy$Z0Fw+CFo! z5`06({5XAdNCtUmXE`#BCN>5QW``+Q=Qb5BvXtDuXxk+hdyAiJhr|CpBj_Cs0_>x< zNG%M^b2WM!s|;2xacI1{ijdy6%;pWYudsDStlmp26SKbr8pm)bQR^$K8fu^R>epM^ z2R8{1|ARacD^|sj-EA|mKcs%E6OZ5EQs;*L9@(s>1)4jGPbks(mtJ<+-T`+ZK-0E< z!DMpU%@)}|C+=voBkoMQ9-~RHd{L#-p$!WX5>66{d1dnqa$N=JXQkl zcJ5`+>rzTWoF&vr&i!ZC8&Jv}V3x6@ zDD}GCFZip~$JDP?7m7Z+-KX^kNjAbRPKsuu=-=|mZLDk2=#F)3cPt%_#&&M$o3awE zjRj6!71mJxL``Vj`t|Fephu%1v(SWv-4l-@JvjaZU^hMicEb$ZKQW(UN`=V?LJ1({ zbITMXNCI9-pPg>OidpOCbXnMiWktA2G7Q^?+76oZLwuejUVC`_@U?0d@ZurI!L}j3 zo&8DuFKl4$wxR4+I!ySTK{~uobzgfJ%U-)(T`<(fa0q}B=QvGktcM9O_e#QgG~N2V+BEEP(&&@1nt;sLdAZ2}Grki~uJjR|xWN))Ncv z4@R+Wi>?mFx_VRD$5Oq$DPdu%mmO{o1lj`++#V!DeDHQz-W~{S&wsZsMDonwuVi_J z5@qYNkJb0~*3(J7RR8DZh62z^KD>cQA*%yjLLKlpB*w@bo~wT$ab#e}u zeweLbh`F_359H4M1&0a_7d%|>Xu%WU3IE4}=L=pe_#w)XLv!XJong$m>BE)#G-Lyf zm5?eqE;=lbsH(%{p$-c)qd}9;ePhBN(bHCv!g>I#Jn4qGPG1`Fl+xu`3KxQVAg(V4 zD^|pl($=LZacaCS9k;?+sdp)MIf5ER7(Qq27}&jk~q_y0ixrIy{wU z479t*x8%gN^=$$PJ!}AMKy$r+jQQ4pl3DtNF=%|(Xf->9H72WYt$ z0qH%-f*sd_Q)89MlKsiQzc}y3S4@&A`zHpdkVd$26*6z%@Xl)1@PNT$W?yD3BH7J% zJ{)<&82#OI8dW2__&R)bzlc7*Xzito1LWML2K|P1qfYprC)Dao@h2=PoUL5Tgr(i!_Z8U9h1wIM-miTg#&)NK*kmVCWPcZ_ zbTzsY+CsnfCDCO(sneL9NF^;h%^KYgz{;yPd|R-q>FaBKJbm`zgnMoQ80Y}yQ1dbO24)EhSkfF&E%D);+8~RtfJA$&yK) zR+1*jbpx?+R%n#Q$wmNegA6@&{iCzlq3^GptQg`VM@xMyJA3uZ6R*x* zvu3ty#qVf~`dfb~wPTV8|1RzltIX`b+4*ATn#LM+%KT*HenhLeDmgDWL?1W-Ce5RvC2(MexvvA9A)$(t)EzDfb^%50*ODa&n??Bi~ zXO8|HLoB&70luuIK^#zb4+Yog>64?oPo)vO!dLk*9!=KC!Gf({y8NPr*JZB0{PJDF4GR`-UOv>e>$>$Lm#-84CEHwF zH!x6FEPTJaxjB1xD2hm`dGiw4A4EelW;odyaqyDT?Ef@QZ-F93c7P92<^b0ctKo?y zUHJl=8z}a;$egsv(W}dgP%b?$Tqnl`397t>6i?j>1%*01yk8gDP4y-_|2kwhO{YbC zeeHrpn~k=L%FdDO?2(;a&AQ>X=?UM_)i)l~&sll{`-*J0%j!xxnU&GE`VM{n_t~$L zv_xNv7w_D;xc#btQ)wHHjo$RF8!P-yXkBr%pNG(XR-z^4@=LfOZHs^ep&S#HKx;SP z4bDI;=S9$*Y1}8S@Wo&YUkQnMHDVL5M}+5%u;AZTa3?sW?*UT!;ezj@m6zDH;10z% z1Deu9KEn$?KzVzne2CJ0>L>4EBroHi{b>1iil_K5(uj3Q;~%`tFSS)F{@N5lYdb|lfg0NlV~zDb+i~-I_ZPu z>~GrdpZc#g`+(9hJY~zSiuTECDT;b1Gdh~-867=Z)mYVGkIP#daQTI& zT^IblfUv^z{@N3+{3UVW+*Mtp87fz1^sS(xG1!o;$mFdUK*B3CNt zo4HPSPJIn(&rSjOP)^7PU^6kRKx2TdF?W1Q4Bj=cVe_Jv)$@CM`&TtD{L=cN#!c0t z-KsUn;l_Kq#~wO#?X2q;&F`5rQaimT(_o)&g!gFUP^cvwz#*2B<@Ias6aqr+*j4?D zHq`blUDBUiv2f9jp4UZv(J+L;GSjC>z zUK!LDB=-%_Paos@X#}&{h1f>I2n8p_4-ZRj13WMp&GVtXfK*MQyytJY<&=bb;LFEslv4f;niU!;<|D$h;851=!?q&>TvZ zWn;9mPeOblEY*jAJJisL*oI5FGIXhox{fBym^#z9;8ov8Qq0bvLH6PvE}IU}WP!bk zqYVy=UxV0;bIif7=rSulVU7KmC^GX7OTZ5S0BVm)#b_zLRpxmBiB# z=pK>Ml6VBLl1SP~-YrNngcVVEL+wU3@(8p2i+aP{vHcIhKR)tpwT5jS`wAQR=ZE&c z^tQvD{k6$vHDyn^oo~xF;U*jVNZrlWtNrZhuJLiTi>=4cc$50R;>{ih5m1X)VL=P( zo%;Mf&E?RYX9J`3AjU~zUDn6-6v*`XUujP+{E1HZb4$pOOuQGlT@5hHLX(Ca9a5bB zL3`$@K_^fCqFXu%heKd~@sufG>aMM-#!poh_=l>h5Y~jHfTzVKeh#^27U|KT!GsX)eo@=%#R^O@XBM_lmf z%$pGR zb~T`$R_3IBg;&adF!O;01qPF#fqWs5;xw&q)j>d}K{uXLb<(gXLS}L{0?aWWIVr{z zHW-q_2qCEhv&UVjR5t<;8}^FYWO=>Drl~Kl0fbRtAnJqbOlvThEQaEM5h-hofnuXg zGHH#7Vn%+&|C-;adOPQDuG=UxM@7vB-!zBU-V$!HdmYn!*Y$)o*6S;;)1K*$5`sV4 z&6*Hh&X~jjh7#!BG@?^y)tWR~p#*;#K^C2&544y}jgoQp56w4(OJ$vC@>|00OTdf} zcGq3v22MhksN9C|f8O>BydVnvHP5861|y6@rhT+jGHv4#ry)@dm!Wv*G--!9`>3ac z+e%SYs5WZ*xiOHe3Lc8xAP+D4?pf0;tdKMWooA2LYRreP8@k!3uS}GsX1J_-<9W+z zS*3Z_Xq0TG-J6!MqW0=7cDE=f<~tp7yiKw-(w-GFd*=0D^U0^}%gj%?Op-JZa>7b0 zj?BLD0?RB*UH5f^?#7|kb@7(U?HQ}Wt*Tbv3~pBS+hn`78L{|Q1Y0eQTCJPZ_XcJ- z?2h{lu+ifG6w_?3zWR|%d6{@Mym~XBLxKy0b3mbqq=F2FASAm2-wLw!2&hJczbLt= zZK_Gs9jvXBF6>(6EY1J2jZdC)y*MJ+f3g=W+q7xj z);+8R_;Eo!CP}PcvdD+j-7pVZT3sIv4sKQi!TS;0?hmLc)^6Xp@ph_npJ(aKKV5j4e1IPQpLiDH*JLre=E;Vfk-vS*6oi-#>3a# z_2caEyI;He?$?CW=4UPY9q{XKA#2qZc=a9oEhl!49ktkADuX%x_5CQrq@Rc|4V!u~M2x?WKK`26#`)-4*}Pq$HbzjoljYx~8d%XW&^ z_m3}HxAXbune=?uTO80Z@jKjj|LgSp_wnc7h*+FixHm`cQx#Ad&Z~kPI099`r)cQJ z=#}V<=!?96>UEQ)5=IA>9od|Ml`bVAIkuES1*JA0SvIgFtdR|6lFqw&=UT7MhFwBb zefqWi`(HaCyj=Fc$d+dXMxX_9S*71?7r{=jSe z-F2hv%Q3dL!ZHPIdWxVpTX&!TEc|tHA5FvA;@^SbM5~)u)16%pZ%W~BilLGIZr~PzJGqFe)!t; zOO~v^_F+{4&E?wmriBZe+E?CvH~ad$_V#(K1B)y9MxlS_iK#vQ{C>aws4Ta)%kok6 zcy6*ddLT5iZQDo){SkdPkw9O?Vs0DSxCZsGgXbNl2y4nmHKmXU?khd{5R3<2j_ zbFPU@wu4+}fL_6y0ML9i`g#y?511gyT(AzJR?w9(BZ_m~12z}f+aChLHtN`Osn33J zbi+T#ZvK|h@X%PU=}+k+C5|fw_EK+nU$}G={;Tqq5q94-!7X!E-Ys}n-o0f+z}jYE zAN7*MHe2tWv3;ZaEw;^TbuZsK_E@f40P8f_%Jx*$YdX*tj-%b}9>M0_$dO=?mG#G5 zodXB5C-<*fvRhW94Os0*{e1N4Ud=}Ip-yn4E(9tC^>a>N$UQl)AbQ`QC)RS6m+vSI zFe=A&t|&Rph-we52Gzs03~)9m;zM?b2%bjh&S9VZo+~!5Zl8C7pt$+#PRqTkwp=-X z*)^B!nFGHBnvHsYzLB)AM2fP>79);=!6Pp|#`dk)Z*g^1NEb>%+grD{^HoOp-+~P8?mqA z>==_+YO#Q;Yw5;6Kh|vMgBMMf`x0lzoWxRG0w-*^aO~Oh>6bC~ZbCloM!YvZ_T&{r zfbY38rXeric&-}woU18LAU{5y<*v;w4% z_a_bA!G*P8vplUrtLBx#H<)Q|8nHM?z6Pz#@aj9fJ}!Z&!{ zhFsKnPU@1oJFj|iO_sMIIEB%ta%~V$Lt>(s99uM{CesnKj54h}!s}wT!Sdv$+DWsmG<0t?&KU_ z&}YuN;P}uTQYH%{`j2ep1N7!2q#3A-aw9P*Eb3!4n6ZVZs$I`NuWrvN3%GVcP%IP< z!{-dno=HT&+0?~v@*@1?c~V7~^e3)@ux+`u7a+^daiah|2Q4Q@G=o5mJPeFPGt4pI zgr4+Ri3`b3{*!gK?fzJ2u&Z}?-8yxYHj&dkt8@8T@Ako9XuW2*f9ZDh8NW+7;|kzD z>oG@b#T>1ZJP3J{6t_^tEzmaO+#zRC<+uJIGV7Y| z*c`;o>+G_p$YszP9PSbw=u^Y7nz~5kMWyx|mR)_XqS!GRDy;{qeEC=BePdVWwly=O z#qL36x~IAMrQ`%y*Qm-iNI z%=NHSHPRda=subbLWEI~Q1%%tH&sM8;2tszZ8T#f&%C}iWs8MqgFdYYA%eir>gg682hzlw{Yb!5Emo)HyuukFClL3xlouh=f`O!fJI=)0K zJ`~SRpKkI}(nnlVtbo_PYT{eDH2|fHJxX!z=i&)3K?Ld3kwIwtBe|Wz+-~*Jsvw=n zX5@ZVWWQoT`n>%%x7Xw_aQYnnrGO@-K!d>QnHM|f`aF#;)YJ3b-Hj7-wrx9hTr#XSNGB-Y%n81DoI0ucQm|r|zqd2BVpXfu*(s$eK#Y;IJH4$L z!yHVHtSsORpL`C?8hJ*rNzyZDkK}`;J^@L^V|4j9wK*-pLW)^9XRVm}K-9)2ALyn# zwAx-G#<}ATqS5Kq(!vE>)XT1LyQ>VgbB}k)vzH);vMuP&Kb`vKZVg}sy*l0Px9fDh zw7eS&1}tiJ@77?40lP*C51l;sw3=Wr0E=hN){w)RIS?HP`Qn5{7Q%$sDe?dVH*Z*I zWF^$o;YBQBd&T2c_uh#|_QD!qb+eg+8x}0sAbhfQNWC7bGu+lQd-n>D5YS5f9g;R} z7>o{W1?UR*qj5IJRjzQ4yB>ZO`j~J_STFFQWCn(dw=jdADnpkgYN|IU%PhS zGjBel{;FlOxbe)I?zL)?y}q`4%^6DkkR9HW>;JjydeBo~q|eJqGH%doXaiuMJM$fU z0;+S7+ep~G3c0g3Ai^_y3~`VJ?=Z}NF^_nijh%3r+quqMq_D42t&k@Z}zHM|FbgW9aGdmX6?yv-fEro`? z!gRIh0J|vr43ge%MiAdA^$$9BIg4Ax`=;twCK?0J=gBCcpI2RAhl!Q;hBnro(EHwPk>4-d|pLjICfoxPj z_(St1R2eLww7ZUMczip}TOuxa*!i8@3f&Lr%9KdKF?cet^y4Jjk5WT_562Y`1Zqr+ zd!P?vOvGhS89c!X&PWp~5ri;wc`7aNXSrbobtA^~4Ma9Z=j(Cp2Jrad{2;DNg~!q0 z9l>0RBJ-&R>eEQQfqUxkoEBYJ7tzHnMtsqFEDlVGn1U??mCqjr+zhyVjE;A?b96H( zh(}Y(H{k?SK`A{4NM`AL&;aonsG>z_a9@Jsp$zYud=|W#YO7fm{?k}!DPpY3Dw{+4 z8mF&(PNCwT_&}d@`YLFV?O}eq)fsdLtwJl3Mr>=mY|DX1%2IXqjO28uW_EE>!A~&!`rV zIwr}uiP_*aT6H?DT_+#VR@nq_Tw4s_gw#izdQm7YGL+j5-~hH6?Grm}Ra%G1YE|r| zdb7)INb7Vf^3b4fq%5 zykb;BPC?L$hGPE!bA9FOOTW4h{EN(3YP3g!7Oj?Puuo&un1mUa|h{iki~VY7mk5R#IDtYM-H6eRjh(teCM)wQlp(#w0^yCk0|TR zcz%NqPsz-}y3uuPK(jLDMx2Z;83^_y6_Xa}J!8nPEuUMgu|~Cem%~3p5R=X+%feEF z)@ySHyasD|bMrvc<{7@yaG9vg$cFWxAE>%SV1*vD*;5F5*J{ua=(Spt!=?q_fNa%* z^F$-+%LHAl$n6a~jc!p_D44)LsTUPND@0SBc6&B5t2Xa3eFV*By*$g+j9)*T?I$fY=B(b4pqPArFp5_eEed z(6xGIhjmW9F)|(A2-cbrVm*b84y#o{Wcx*Z4t;2jM`Lg-ki(sAfcxl?1%8QU&5_bS-s>IJlKT}cO?);Iv_t8TOYNMqrbF4oH! zz3_j!+a8pBlsKG-8xR|$*J{9#i$5t;ZkB@%CmM+QprkLGsWZ*33MK?0azn6mW~g#Z z024DaNoMrhiVEdwlWCyPVTOa&9PVa?r6BZfEYccvvLqutqHtwHcy85PlddB!YYQWS zkjUvBpMF~#5Et+{5q$bl0zOai9y9pMLtF;x^sU+|L9eUQi31J2sZ_6ecdECcp;z5C zt^b-$-}~`lR+|VdD0`%*-Z7)GIb#dy6-g>aDM*Q|MdxAkmWa+cc@z z!tytKHN%3P) zEb0`FxjC!jLeBDu1WeEfQNW~@;>w^8!z2k^mjIJc(HXElq)>5KRtYfSqgk1&#L zXSVF4Q-}!n8i^VI-&)EcUSL;@tU(CMC+Y*A{MwGR$8bBqqGG@H@U`rHu*S*cSzxEF zZgEik=vE1cswWYj9PVEP&XIH;=rNq(?!(Y*TZKr%yXi9>3fKC%uFp$ zA(e;pD(HaZ5xDU7ZaW;d=NEGZ+bf)8R}c(-Y6;FO-IFNEAiItGdZyf8y=`jg9=f1z z{_o1dP7BTdzbQ*lwEuro79MZHV?{ieBTo8j{;Id5dEs-5Af&(OtuXj&?i9nYnE_Bl zJa>QyDx~(rCONFpiTRvUv^pe{^5b%pj4v-MO)ePe58U?8)VRcB6Z?Vdj$PIR&f#d#>@@lSk=-@#9TyH~kHf`2 zF0|krN@ayKRak(X0Vf^jOU>$ny`1W<2&V<#?&k6N`+$$#$Y*JBw1aBQv)kZ9>*iRh zDNlhyPI(p-5D*P|qSC2Pg{`G{Qamw#G|KFS$8u9;b7B+zUDBY{WilxKc;;<-Ja+o+ z%=p`TyVb$&iQnNN9VqWsb~=+$WiayvyG469OdmLuoo279_tP`tbY%O+xp>O%?zib- zvs>vAyEE(x7^uIhew9}Cu+eUyE^?TMb2epr0XVP_7YIjE9#Bq9DLPLbwLrbFDtm^K z8VbKtLZ`gJ%v+LwG?;klRFH28db4MOWl3xzP}Vujx(@TG2(F486)-@=@jCHBkqI7V zODn}uO}Uxabi2i{N_X#r*%Uh+1o@n8)Rt1R$0u+4r%*bT=46XsUmh|+(CY$w^4cFb;$bOtG4MnS?79{m7Z#bXlU>FaBBpic5T}HS0 z@yA0I;a2!jU9AC)CbcT1(F9s`lAv3-d!bI~4?^eE#QXsBs=q2@C#NzPXwe6A^*}MF z9`tr(8Y;D0Fs#F_8@H#@^x!m)Bs*gs%A1R>zwr6kd(AyPY_O-f`Fk(kddo{M-E!-T zkrTOd7}84)3ZRt5@$>80Hdyn6D`{@34R zf0|lCJ~qR~LV2ku8m$>diVO6N3rj*oKr1l_AymIq zxU95(JTtj#I5!?%R@&eZmLj%SpM4PD!piJ}e4Jj2?{5wb@i82i%H&Q5#39$N_Gw_* zfMrMvNzjbEMdZ^JOYq+bBRp3Za_R$VP<18t7zLf>mO0bq)@vQ1xdS#$QT&$9j0_w+Qj%~bwkYWkNJh& z_45XL2Ike5wKk82TF+0=QPD`;zLnXrx1@?OtQC+*oJ_j&Oz;dOhGYjL4? zJj54_xU@f({iJ+m)ZvKEEZ5B&TYb5~>C`X3a%`UN!mWnOSC7q`Vn?gw_yhDqSS<3k zR_gmyLCyGJG7q0nzP|GM5HK(eB{`}SOrqT-Xt-#xV(W(C;SDLBEbAstQWd25IxUHO ztNP%`uI!0jBVO;wE};p(V$EH@XlZ%xu5%A}*Ly83zqpJ0fw>o0y)OLDU3a|`Hu}8A z{E`=eHaR(#v)5Jfx6TnHfAtegVg?iZFPfoQOa8f*M9StJu3$q2P+99Zf5OUtHAn%a>>Wb>2FUXWhK1 zwpFr>{cSZGnc60FdT^QiZ7l-+j$JLie`AXX3=9N}7q$m0{2Ykq(B|9fWM4jCFa1WNrmyiAYNVHiR&)*Su0hc$k0Q6Ek`i%=tGKupL8(PRQKPni( zXv&A78Q!C3*0eR<*DxD_`a*#nl-8H1U70^T$P6P5i+1+`tv#uzXL3<2T-Ap{5^K!7CR5CY^5Y(mJjX__XGZAhA=Y1*bq+B6|;(`+|w zQ_^;O+w3+?(`>ixKVRFWc=(<(vcQn^>+XL0{rjTYZiK1w$FHYCF=8jKxm)!%nlzLRd9N&<=cwqfTV zS_auL)%RDR4^WI=90;2pr?v|HQ3_ovc?zIXsl2i z1#=Jqwp?ZmLme83MWSm%?dmasQ>4b#5CWqhXFQEPMvWJ7#7dzZ3>Z4#?O|s!bkugj zEM9d{#1^h)@1SYCr&rcC4Yzr#$ckUI4YxPE*tlTPMX{LACcRh{{e0A?h!1;R-l*Ry zz8bq;zsqW`PF1={T)&?RSR}Q_S8PwEU-Z_b0>=kjF*I?oI^lCaksPe{v&sy>BVo<4 zDE;ZSVR7GZn|^EK9H~Jfw$|^6gu>+JF`lHbvlp z-nKF7Z5cmdt>@#F1gw<@gd}pgj8{ z;1Fm-X{0ovv4Kf9bScz#J4720rZ%vjA=>^%*TUsS>!Cja!cW_gs%4lx{V6iNYkKV#fO(eNW2*E;>EUzS^XJ%^e&6HO7dPAaCjob~nS-R+3Ayo*Fh{s&@P- z)(maSREzm(!~%HR?qYYe>H|wQy+-49ubMYKtMzbMVnV;CDt;{w`l)ClNaV~rZ3 zAFyQyauXk6;b9IJR`A(I3pgX_i4&VNzzuDYY#~OC9<=v0=bWWYdXce7DyDRcEdtZY z&CFS5HD+d6>!(_0Wg2G-xh}5T9906KCe!0aIarm*e*bhte}hmBNeVN<8PfQf8sPW>Y3suqaVuH@#J>f5K*ef_s}tsRa0 zK+cNL%O=0*;1{&cs(Y)N{dAREzk$qiS4FF=CjB4()$NZVu#o=0luqRr!2Z1DjaAp$ zcVDNFoE>UMoc#ZhWg=e%Lzn4n4Dklgri&2qlKvIqpGrZXb5bdD;-B2so*KsZO35c(adODd=j1>pXi#nyULHzcJF-BxB)!3oxT56x zm7LHT*EjW~e+ZHl4-fBEAHpx&mR?E1tYsL=@_*E^_rl% z=vp9L6ZO{2VLNqX^3Z?>aStp#=SUbWiMc%Re*n3ok;7=?PINqsE2Ck-U4kA?C_rFD z!w`gX1t=SHI_R4xXQaWbep~DCesQ=x-KU7d+lB=--n}lVq>@2@8q_4d(&d-LeoS;p zb%c+OFXe7&^@caK!l5U+Nr-At5%&Aye1kYBCcXX)=S^WhbcM}2BvD!|xnk&9L} z=3i=PA8z{vS>df}8*ZwtoKDH)-!Zj;*hPz?RWAYu6rBy4_^@R2#bU3Dl0WiIn@vBG zDEju06eWuQ`o~?BsqrZR?~CbF(-UrAqB@AVC*~SB9;l_`wPFo72lyV3!FOR6FFWu< z4`J}$7<8Hm&NGyTC0JusPmCDq;Io`)z$_+8YC$u$Vb3xj70m;VqrlLT=gn4T#uS2B z=CkdL?=nuBA#s;gGRkaNjM9vKuGMrclxk*Xr^!0k&K&CEMZJ}mRPvh0cc~&j{vHqj z3hDbm6o0Efr(lQiB4WtTB$d-R5;pw(%Q|lhc&|ny2O%OC0>up2IY{Ni>r$B>uaAj{ zzOFI%UH6FqmT&~&VJi=sXVXMGbvQ_{# zIJ?^~rwaL6SHfaJxHGQWEHpo|aRd4MqE@r15+I?o=QP*4<2Xm<0yt&C(8dj&6ZIee z=_D=xH(RZij2Rv(NTgvES{CK)0_;JIDJ_Z38|DqIYlXwjg@+lwe3V%{(LkhsQlp}4 z8vWD8)iZxFG#2nzgiDvloUkC0u!Tt!kVnsqUFg+M>Nk_^%bSz>heTTcX|kPc*Kb~t zPZCN0aKpp-ljQlgPtw8_^b8a|M33W)W}HE!4G%S+oUD7G+7YUs0@NCJFLKVu?z`8oyPI5f z@Yu7r?fc3X$ZZoPhfg+Jr@gSZ|N1&##@SqVegEDUrdgZcSn$j1#>oJUX6dd$Rt|*s z4df_4f(I>~j^`EEcV2W+XU9bskv&WCqzBgBefL}U+)X}PoJOACx97|HiHU+^(>oVj zw(l#`3pF$2#g^h%_FcB1b9!MqWa7_Y%-O-8P&^}bS(?Cl8La><3{70hn?X6KOhW;I z3BzMAo#Hb?O~$Hlmx*Sh<{aT+_;-sjwPK~GF%_~7> zT^AT^h-=ApN7k)7LRL-=DaMTSXS4k|{bz8s=48z!iq!8q=nL=&_3Sk+-k>INAKWI4 z`|tr@@E&@bwJ;vO*S&06cR3IV}&ThG+f~dj1H|_Z0|K0sC#ZInLngn{#pK>L~<8r4t^?pa(Vz06ziLBc5m{ zg={|CgmVx9MfKpDB$`dgPlx0kY}cdOfe5Ltd!@lS8YN_Aw8JL;$fxYETemB&<)kJY zmt5_RSzcSbnZ&jZi;)g>yRe)6qqWsX4cl`fFJ0p!H>C96AqdDi@}kCvim;RH_1sH4GS*(t2B5CGE! zi6&n#;zrdQEGXoeY=Nff1OJ51NG7b&lj$gExeQOL$tz_uB4XyLt} z@hnS(+T^BDVx!hp8<{@hh@(y8!Zb#;Gv?^hyAem41sWt>bLC36{~~Z4x;qhy5Cq51nMD`G##K{e{0IH(g9bi(qo@iN|0Eyk`)l?Z z&~=Lcszo=WMuixvfyC=3C$K=~I9fCofF=P5Bqv3A0fS1B1>_I=x9#cDS1!4$`o7*b z^nZLYdHNfaAF*9fHb(@wAeK7Kgf7TDoU7(!y z6YukP)hu}S;RBg|fm}4}nJ#YCZ7WyahP=4HJo72`efF}7E<;jFAWexG^bI|mqNLa+ zkV1R`571Hu*`wqoA(MoL6YzGl%Nt9iA|?Ov$v;i&TC|PlJItPaoq>K%PF2^NkT8Ok zvO!^vI-?;xTp6x05mUeuy11wRQ@eekNNvMZyD)#?@FNTvxc-IF$8I@7j;wnC>B2Kb z#)vH zE#9!~_OB_zH}v1^fA=QltE6esKx@}P*P<&nF6x-mIc@er=0B#*@7?&2_o0ow^QT=0 z58TkVZhCh=qs!}G>W$Rbyr!QBW}>|>(s4$=|GjvxD-cnn0Xo9zbS^hTk&TJBU_=B% z)U`16`ou3#BX~c&5e4LG2|A|(qn3x%Pd!r-JP9F#EziEkjgMVBRQN>I2MxbIaOvpS zioKOW^Og6k-5>emCo6AT)4#m7CDEQ~AG&?8`t<&MV{y@ed;u|^j<7o(+CeT4RnFg- ztH1Hl`p!E$mQ=6$_KkJ--1|yrXJ6-v)&HIWqjTMy@v9qh^~3jVCcSglt($1uw;|h8 zS`5?+js9Y2EofjK=*dc!nF&2U4wTe@%}{h0q#9BL+|13VZ=jk>7Jiek$LN=wfXkrX z0Ozn*^-HL|h20g)eT~tm;&S|bESbRg`|(^KYz+!^4(vm|J1w5e8Q=MB_D#Hthgz|m z;9X>7Z-RXUz-!}yhVsPlgUBZ!e;8iK8H%F^@-P=ipQB3 zk7HT2YMVtr#b4Szr7BtFl6cYEy|x*~cD5nh5bPext&JC3lcU*g2+GV4eet+&9N9az zzSgo$lcV0zJyMCcCT)TiSL4BhE{6s8x`b)br{cVm=04Kaq%ANuS9|y=`kumfpU~DMmrYbnv z<3OK{J+L^qwq({tlo!OHr9&Gd0q@``7_UI@PA@E>KItKLDc3P?99qO`hY-PqW2^~q9nlnx!cSe`*l1LADC3pBx^(-rt{KzX z(-|*9>s9-CXM`~MsSAg;ZXK!#Di#KjqXe_t+%_+nXlqYI+Xgm0FgSPP6?3aGwfd?h z3wd8rbd2Jj%4ywQxKEu=keqgiEmPZO&z&_(w!&jx=wG&EWT28oVE1TbdU1YB7GOw) z$xNBiRtVJ81PZK~C6bHGjzz4BhzMaU<{FF16pi6!ORrjWQ(IeInx=+GXIh=CL$D~# z$rs6-=BZoe4{f!Ws(A|#f`ps9HJ(Vb=bM@0sRl&dXe6t zuI*LPZRfJypx@-P^8y=;hN!>a8T64i&_~?B6fCCxY_U9C&ZqUUW(_Oa%OQ`ek31v3Zyr$O2p=_tFbRV=&x-`JN3s4jsD<*rNml= z0e(&=7J)=W5f_(&f|k_N)MQ9I@mZ$~AXp&zK+=bn2u`vZ)s<392Gyb>0{nUVFy90F zWf(EauFuoJUQQ4%!>!QGuJj|-NGZi#UoIMr=T3sZs=tKyCtbV*=Nt9(Z%>}9Cpmtu{y+S<_US`5K%o2Ri;McVn6~xj*O2CgwNCl? z7NNknbKkrQk}!=q20bXKCq+F$NkSc1Krbml1lJFTXU+6KRqL(ru3NVjUdpn(cC9SK zgL&<`l0V*BY5dLqMl)=I``=}k8BTs!QOC&3E~7W3g~$G@4Rfwt?9dU=@c;`JVt^ZM zQf`m`u^#XX#2%6!GQ2-`{*n3}j<5c2Gyqwlzhkp~8fr?L?b<)ns_U2kSL;pXvXZP) zjTxVg{TL`5#<;PfhN?0pXgUQ~o+*M{Ub^k4N2#fh-2oV={dZ=U{Vlo}-(?g&WOqX^ z@D*U-{sbBCKrujCG=?(bhT>(ydz=b1ATi{}$pGR$DXzf42gp0O<0uKx zF+8+2x`I>+`knOl#sH*A1?c-z z%F`4&OTR*}AT^cW#Gq(KHA~+b=QX+6=Ot;bqK(50gQs=T$Rj|!YKopYP}+Wz9vD|O zWv+1|2^?U)XTv2%eoe!K8d1idE~Q{*yhf=qm!7bGy}`HMj`(Fe&qlq&o1&=!1y96a zvH|}A(hH-E#}vyW1Fj>6UjXMF+@*@9-v?dvWBOyf*=kWVa*0hQBV@!m-bFgc*OLb3 zOZs=&&60Zh-z$ZKJb&}eEJ!A+dP_Uzh2w3znOBAJwyT*F<2mNltH+U$fIZs>9O(I& z7)o;hUZ=$N;Y~pPN-sF61nxpc|CsO}%9DWED(WAyCcGJuSmq_Hv5OZKu{(a4<48b+ zsLAQ?h*ow7*w$}bO$Qk;uFVoh*QdW-TC>fxoH<~UPJbJ^?t>;P+ibR$`2C-O-%lEP zCJ)AFMcEat>_dBYt#D{YgPo%c8&6R$xoD7Ff{@p#_KGt`HdTUzt%!k5D)fK)$Ij-_ zJCvZpF*e3$c8juw9a;|ep$EyM^a2_w`|R?c5bazX4YC|#7p;s(sC0*tmD$_p9saIC zU#V_|+JmX=PjRh-D4me~xeL~Yk16Ff5FHay2*E{0a~@au%~1i&HburjI^gx88HG>F z$JL-mf8@-(Sq*l(ci~KLO)~Uk#hG5zDHFN;usg*%As{jWVPcX`HIH1f|6QtLChyZ4 zo%eV0vs;UsQ~q2&6>ir!?z8W0J^VcmVeEMqQi2E^@;fJ$WXhMlV|etD(a}ff_xQ@0 zyD+}Dpa=2Lvf3&t5Pi`^90(qYZi_zqW8&BUwVVN&_@?e)g#XtL%v3Ys9(VDCmr%kCF zSzO=TX{)r>ME$Ku)ZSeao$=!B%;G)2{{5Ye&&BUZ6_SJSr|g?^toGI=OB(0M|HMN{ za}JNmQXMg(M}}f-A>?5_2*G&zD6?YcPW{21JD*`2PCxcO89jPbf9SLLn3ME{Ed1zN z5uwt!mTJvC1**?ZL=q7@9Yh5~bqtIHBsj1P5eipLgem6;XKiB*dWVvDGvE*r2q>kK zjK`DHLQ0Wa+hrxm=K^ZrbyXhiLhzPcN|chPh6U)Wv^_|^DUo#ncx>D!H%M}lzAAH6 zZ79mYpj>QF^Z8|}Pijqf^d$SZlQNfWfDwZEVILfg(g97+HtPX%JNyRk7GX(W4t&2qrI_bXr7>#vma!Axglm`9Q{k;%9# zacM&&rT>{*81DRJq%n(2-s2s(O(ui8IQXfL(>phIqyI@~4zNctZ$N|OEV8Ht{SqOn zlP#3)A&f1gOQ9QBfi~4E8eR2DDPLVyGAKnAy3^X}^_nV$2TJu1)&-++$#+;{i z?s}@bz31Zf`f2?ReSABadE)j*N_6M0JwILYYQEX1^C9G0*?~7K9OzY! zn3IH@tf+ssiTre2x|BIA4e4)xUH`Qn`3Cvmhr@bj2l=_aGT*F+z5?)h2cb3y;ml#g z0NQHUw0451P-s1H_te)0T>9B<6GcTE_WY1xxP)9&Uek<_7^YOtX=R$4OO_sJi`=xS z;HqhavqIZ}2Duwr8FUexTD}Bpgkd-K$wo1UG$JCVySyOevDWYF7%3ttLr&}y@KIWZ|qNzvzpdp3i!#2#Es*Q9?;5Xyg|zUTj{<^Yp0+y=Z*Yh|iZbtfAG?bbqs8m1xzYr9;Dypfe|=5p0S z?u2D}JSA80M*D0X56z`t8A$b#it}Q(*PEjmMON?#@l?sw4im!p-T1uai#hCZLFPC;G32vFNfNy$ec;#AxQIJY$R zpmQtbC8W;8AfiR&xdsB?P)*Sys*a##?2&i?i4top9Kylc?z(LDSfIVm=8M=;aI$Y> z`qIUPo92#x>EeBx7DS%D{Dzlp9gA+uLjV))NEMh6`9MU>ADnIT*hIToY;PCMfv|`m zpUqO(QBC-uBNe;suAv$Bv@PPZrQp*)Ej{;&eJd93me%Zk`g(b7_sV&ySq$4-54GCi zerz9%=zrMcG>Z}YMH?=^$R0)!=GYF%7n?E8%{1$I*}e)QL`@&GgMqPubt3@So0$TV zO2Lz;S@7HegdAxwP6f3}P#6T|Jr0d+$5*g)lO?SG3}c!DXbi7n9|)Jt!eOx#O`lTSwzwic2va^uF7fo zSy}X|bN(1K?s}x8(FO6LK7Ak&lQQaIuN?7(JSXvoppH1Co+@vgJf%Ny`ZQVn4(Xy} zM}J9C&Tb*|nBkU3lTA&wUE0yF;YU?L1D3$gupCjqL?If6<_7P8jy8FZB+2Si^iibY z)HBbVdWNiqVqybC217$ZH`oRorcQu*;F(j;K6@&6?mfufS@&>=rfr6YB20FgeK)gPljGR%yab=2PJfv629?f zU&7R3p|AZ-yOXGlW;a%D+k2=eYD{a5trIG7!YQ4hE7s;7-<9icu?}We7%<7@*+J{n z{v4mETyE^IsEm_0&}jMrUnt}wo6&}PC+_?nZcSIRGx{EFpz{~fd$>PtPghRn3-A81 zu}DuUHvaM5${M;=Q^7zl{5Et1jp$(mXg9<&hWG^_E!D!K1t_Ezm83l&Vr8VX9(&*&=AVABV8#Gm+np zzfM-DzT@Msv$yG=C5t5e!Q(zvf0)T*8;BK3_bbjk3^{ELTAgaF;o`O1{w~%f4|GKEYwRPKDAfe2&q8?rI4%u+cJLG|e@c2Nqp&`mV8*U&w`}z*Rhx47j zzC)$0AkIB7I(k4HACQh4e(-Rq0vnYg&4&z{U&ddIFm zj22hjy8ph7>!mM#irCl$@3fKeQz z9Gf8Tm=_o*SI)tfq6W{959U7(zj=nOwBLE(o%TwWwaZsdC5ntl8IsIdycSaci+~9W zPL>czL{`YuD`opPN*cEH@9NLL`!4B`^#{SttUTR2iIHZI@{^53a7Wy?iXYikUn|-C zCPtI`dwTjM1lsW1q}uvjBRukLbs7pdSLtr=Mo;Sx{^c)ZW$k$kI9oyO!ZeO8^@4_j zG@z>ZN>k7z(~SVYP$yy9Gt^2jbRdERf!Yn0KpFzb4x&!&PaqdA@M)AcrN|qaxLsoh zU5MBn_L#gO#x<{I!9aHN{Ml0nW?Xc5!6nYdAfxV=;|`*@%%XS8Kl+=7T3d(O^kQ3f zaQ?cnXFBJEHj`&umHHc=8j|q-J6G)6F*2uR_WUimIUV+6jY}PDyxy_h#)%J#{+NG{ zmH7?Am9!3xE8#hvpWk)a(459;^S99NeU8_&VKj9?!hjEPSyrQhs1tsTz>`4eH7Fri zl%lCrVM7Liew!MiHK-jGsAH7;#3yIG62BB&*;4g@6837UEF1fB*p*V1tUstpK@NF+ z+a;lUtnZqZ3-TNP7?&dBm`WIbI*9Qo$^fRJB#!LgqDyp1T38n2JeLvWb*rDfFyS#* zvF6Op?0kGP3pCG04+eS=q7e8x7XrLfR|hi&A(H4BaDzhLS)ehz(5oRK03(Ho*GYRd z(p;IS7z$lYBQ7$*&`x!TaQSQ|9eqtreN8_N#f5)>`JC@w(g08t=0S2fQ}2R}Rdjgx z%5Ao6()n*DmT>=k2|-3W1_GzW?7~;)$V_s^jc`07H~k3<&wRTn8fj{ZMD;a(r)2w3 z<~j4=YiDk?$TlH_pa65%p+QMNqn_qdc zhGTh+{{&Gu9`qc!7se|dG?|%on7^&SHe~RP7gel7KW<6@kXpiD6Eq6t!eCG>@h7F( z4yt98g0)9C@VH>9jug@9kd{7k@Py-O0! zBySR*SIKv7yW(!A^T?)uAp$yzAINpMMTfny6uZtSsWwVtM#Zmqo#d$VUEbB6@A4+M#N*PGt8=N%vFKkd*&2^coKqSX z^UmCfc<)z%ho4(9ihRlyh|LSt1%^6?F(un25D@ylLHs}pF)R!PGg=BGoa&bi4RevG zpaCTb8!Iq%m_l&}Y=iuxS57KWCJGcYc?>uU=uri?t=Z-@n?;w)%A1|o(GXUMVIz$W zD>&9rT}*a0Hb&i{wkSG|qw!L|W_7!*R>WpcrIz}UTC7;KYJThRFm^<7?OGAJLB__i z%x*>nLJOK9KHk&U*OTNB(2tX4c{eA47Z*ie-k)A~L=Dh+q1tii$kdUjPJCUL!`BvCmyl4lkRJJ+I8Um8~N1B{S&n zh5P06E?ob$^UK#CGQ#A&Z!8T6ZjS7rJI%i~iu5NZ?Uat6PL*Q$EjLy_|L6`Yb|6C7 zh<>MWh_x82pBZJObkTV`UoG9%Sdt-Q_5MeT@Jjfj$D5n`$G=SuKWkQj6y&))a_e-ljPd zFqf3rM~ZsEkO4Zz2=7W&Ft`#_I*1VdrF%e$MN1%HmX9GzC56`*1F)240d)Jk{a!;d z+>Im;?m*^X)4|^vTjW#zDxb&g+_i*?Y;6mweLhWdA#^T;NF%`RKO1u8b2u5Hj&O@D z4{!a6!HxLLiA55pR!f(DW`aA(ZpFS|+B1zl!oA2?K6WsBV8D<}4^EjA3;6s^fhD_e zbU|BccDTk@ov)!0rRlTxz3{?I#)(Fr3mX53mLmMoJcCbJ-D#=za+W#$bkDPIk+Js) z%+qJa&m1%fXmc8GVRgmhpx_3zLDQ7@FCbJfuYnOUl!0j5xLmS#Q7tnl5p5<4oitPK zC5~W>wApY)emY|yl#DaXCCeDjHPcy8gZ6^AGx%yolT*VPZw3=%L^(7-nx&5b3P`D9 zsk{kje2TvZ=F33AHD#HOknE}p)Vg@5ug+y77h|KU&MAjboDQV^)m7^XrKn7X(dQ!VA*Y1>%Ak4(QTMj(F9KAy0&1{aSr(m4AAhiwV#( zt{M^-blaU?hs-&Wz8OPiR}I5fX$_@wWIQO0O;|2-izYv4QI13QmWSzs$S6}?n+nvZ zCP%HmKd9n9)eJ3qA}Nawkre7(DbZvSYhCpPLhLg**E2T1x$CY)_Z9F=N1Yr@WJEj? zFUME%ez9ue@VzaP%aNADv5e?*l@F$pc2WJ9d(!Xy=QB3~hkQMdBQ)O#qQn9pW+*c0 zXfU?G&%l4^+zw5HTpnIj4LoUhnLGyUqyVYWWoP3F%vQ`G`sdb%x1i&r%ZmsFjc$(- z;nFmpuX;fn?UqY+Io%##m0vH6=`T8z<%+`YT5dM)k(1F`O6k+6wsA(dQKPW z3v?Zjx3ue_OB;+cx*vKnxR#O`m&SELcc)rk)U^>|EUB>x6b0&EW12R~sXS7^REyq> zJjfSFfQ*8$OF%F45+C%({}6vwYU-=RWE`ZxJ;%a&-vs3xeo&<*z~MjDIm;p8L3LnEA;3FLjUPnBT& z=KMXuZ2RUusR0aM5ZapaoGO@#PRJftf{%3>QW!X|bM@6=#CHN}3`qo-e~ee-+n3r2 zt%Sb7(k#X}i?$M|C>`WYSrAgg_5?ye-r!Wl>uN1#vWxnnVDR;-(Lg@n;$H^vkqY1K)0vT5$j8WVH#*wANo$)<1RU-mFvW^Nt6J&!r% zIJ*nIeW+qTzCCCM<-w`LK7t*WDrV@-@Ijjju?Z4mD$yF?Z5jH3;c!Qd8D>vCi#|Y! zS&aeHHz*nSdpn^nc_lhoK7PTP(da4p0`vnvbjmwJ{`nrhmW8I|$0tk(VD0|9Q=T9L z^VXmApL(iTw#Lm9=~c>`2V7?E)YWhRv01EZofcVPXq=BAB5H#?2zI zmMXrcjqwfzB0E)YwQH{|IMpfgU3W@0mn&u6KVXrZuC%zJ#q38Ssh|grTtr-;1uw1& z{>o9w)TluuuVU1&NAr^?6ZN<%!_9Duc2$zR3uvYuTiU5z+2bbeI|%Ejv)OoM2#O(Z zu=+QLYiq9w+XLaMi^HA;lIuxl+9nC=3Eh=Om__GrUT~!?R=fMX%WZ>jDZ0CCEF8# zJ1Wgm8LwwhN@rE* z%tG!ku&Y+<+9!=7&c@A*{)wxpByDr&7ED60hB>1I-r>!IOxrG>$~sskS4X~8b?>S* z_YXGzLa{bv9_tGx?kLlG6LfF=c~VLSB320?>~z;NOSqdeb? zF)<;36wqK8`^-y2L%a734Lvs2l2N&EX2sap3p)`zUfcYX;8I%W#T6zHNcCsi4`kBU zBqeQQyDRK(?+T}6wMgvSm*Qvp*ykM4u>Iy07w_GBaYLQpY)<~{3V1GEcI&RrxbvcA zxQrbHwwoQ>LhK&ena^aaXqTu8CEDPB!lv}e(xj-Z#3g7HP0Hy_`+ zQuMXN6`sGw#bko{xBTABkd*3In1Q4pwp~g5(qtNqP@Zv_^GB)YW5){7r%5|T7H!)` z{E{!TXtYme2JY)tv^ZwVJ;liL_y=x-Ne-DjVS~Y{+(ZpS=c-`q)NKQ5-5>SMwBnl>bxet~8aG1YrlQdl#|rKs?~FTG&Y})voF>I>^*ZcsnHy1O*39V!y*u|! z`>1XC>^H@$m+JK2_@<2?2|LFa-&TVRBJzmX{MSFOAe28YHblhUKQy@~PkdkyE__UU z;XpX)UAD}d32|6*DV-98@}CxZ@^VrkJa zxe(si5S~C~6CkdW{xLS;#y9yN9nu5RI_V$u%3J_w>FhuJAxmmbCj}xJzlbVrY_l@6 zcc-ZBSnUsvv<5S2K?55tq*Kw>k)VI|4zI9tFT?B~qi1-dBQ4QXiuWn9A33GMts`OI z>K&ShGnAL~KfLr3k>3Nt=@8jOZqc{tTiF(USRW!!lD%#>?igzwiF;P>aM`Zg&$9c* ztn`ktk=As$PH?+5oRLVjEeZNp;f}lZa@^isR`-rozVMQ^2=Z6CA0*1xzNY`~s|;dg z{@@4X_{q~Zze~RT%U|lv06$4E$$tm{0L48WGIF1}ps@kq%Na63<@no8#TN* zVokKWG}GBAmtc`k!4b+AfPX@(2FPn%R+_05RZW<&{4qkE1c&4#qenW&@E{}vNeIatM{yfpf z5Bu!QjD|ra=3HG-it`EHWJiSn4mpT9wFP5#eZ^JC;r$cz1p`S0{Tk$fkF`-xwpUNy zc2YTlQYbnVdqAB+>6ZDSaA6uU& z?MOztQ?`I+j zuJM)urxI$&1X)oA#(uG8b63`6nV$r!s)D}LC#O5SUdP{Fkb_IF_=npX8&+;#`u8nC zzwuL(yKi-h6K_oS%p6{@GvOoGQ(qrA={&SxXWzoktE_ChD#S1EDBc%~j>u#>y#9=z zxG!GESR8rIw_$osYwfB_XI9(om_*rWreZ%E4YIt=POLW8I%l6!m7XMnRsz3KXYepU z+8RB=;L!{Y#t^Oxu#F-h4-Em^bav8CbFW{+_oefLgZW&zwRI7Uzaz^wf2~+-$KQPu z6Ehi;y#1-{yiJ3_mKGR}w{{ir(@x~cb8i7-@LKfk1l|ESesq*Lx*cNc8gN<4C!dw% z(02@w7kh2hCzjPd*s%P{rIA$cn%-1o)v{@g;`-`TR+tUW9-*$&Rp#nY)r-t&+w^!- zZ*Nn4x-AaJK46dBfY_AmfA3>Hr~)Nz01jk+#omev|z-3)8BaR@oJ+V?CMM>VlzFQph!hZ;Fj9W;MZKsp7sT&iM4?m zmRGa2t<6m%v8gK>LRTro?(MJK;L2L8Q$&6saL~BZoo@Rd?8QW4dehvxl?$Vv{svW7 z{DyC^r~FgAo%#Cu{0c1|^@i;U{xx`|#G}zDdwpUs$22Ea#kcT0A+ZttO+>agn?6(Y zS!;r9mEXq$ARJgdC4Qqbu) zg9FqVJX>Vh_j0k*ug7w{DJ&(D-OHw#VxDk~O-WZbnx-x5P9~(ViPujY^T}4XXuoTU zsj)g8qz6L_3r@S}w#vR^-1Ja;rF%-+pJuG%KQltChDBtg5`%%J?mD(w(X1|4oUQ9_ z3Is)jKh>YU+9}$rW$T6-Htxbtob^si%2v7BE;_Gf>)l?PPYq*ymF>&56@5m=M2e4s z)@lk6B;XqjFbw2E3LZs1T}lPPJ;CmhVQ~fo1{hb)^J6w9qe4CkiirzoFM|-u7Tlur zxHDAMQAJ4A^y*;ruq?V^9;qL!=4yS&sY2wKua?_Xv|r`7cdxjFNqeC#C34)GW-eLL zZTG{21UUoY`b^u=(_L|f@rR4_X2q}{%*PB5xwcvs_uHzF^hm1=TxqpcE0QhN=3zss z)2W8oMU7T{l;M4HVb)wGO3R|m+*t+LH-3tSt+kDxGTM|6xu(Cycq*%_D?Q|!Mf3v1 zm8r;M?C-?bFXJ7&h$tQe<{^zSxdRfor=B)#51T-I0uQ3@nCU;| z+@=#dgPbVnAN81su)!zgL6Jy^bMa?q(5FSh)6Pk zWuFKgM47iU14%lca>r#G7&1+iLBkkJ$c>RLa)?fJ;d_^m0OeCB*V4^i5NUwvF{;vf zO**Q-6io}>Y#i7X(Fd)Hrg|kG**m#LAFb~p1Q#%n0L)Ru(Vk;ePyg_{@u^eek&cZFPACV57i;7~MRFS&2hWY08#ur{xh)a*)hw%@cj)WA@P$&PjcS zx(i_DH`>ftgPHh<%D$~OpuZESy`cU6R?C%IR3)8iuV_BqMjQ8p8d1p;Se!5;UPbJ5 z+qou0vqdL0<3nrv`_~TWH=S+5y|nevgtB}~sZ1R|N&6`6ue_93;Uk5Ai9y~tG;Fys z&&_=_W>^^7g?Iw6Dyj)RM?_#FHB=}xXc^+14EcA?`(WNqfBP3tesKT@-?69219SI( zd5z&BL!It!f7vlI;&^%Z6Q3Y^=;5*6OTK*L+!DlY|GSgsTWU9iFA|M3N0m3=8=#Fb zKnq4z$~>~m6tak6GNFJZ8|dBfs5ichCG(A8z@ttN$M*HV-oI~!@ghiG2QMA^w7EBa z`I4L79Y4I~)~8ROe)`reU+n99X35yt5-`7bAl{*zzpfHA2fYgLw=(^JyhD{i1~do5 z#n64`;?5FcA5&GLL}i)R!|konO@n%Pa8tCkJ)C~*gQNPtE=A_Wv9Ti!JtIT&)**V` z2*A;=h_!<^EnRfYHH((sG*~On9NPBSW7~#EEq$9^&y1ZuHh1d2`?=m;?*4sKLDH#C z_7HTkJAioffXXA1J!sxU&o)|Q!YZBBoYa5H82^*|q=Du}XQlpk7}uYh=$+|n(w8rq zy>2%C9vnwPNyb|WZ2unZWG6%cMC#R8?sHHp~EOO`==W_p?NlO6O6b zck=!ux`vSnv}cY|Id|D@tURTW50^#OMlOm}{##|F*8nOo*J6BaHz?N4f_PFCK2Ntl zZ)~Eb#^h5vLnSw>r!QvTbI>u!HB26MZ#@`|J{U};J}|N)K2(m_ARa;fX(Q_|$X9vm zNNLz9CvyPhPab3+-$u(TE;qg_DvQo$^z^?*ttb+oo<^w^+EPPirStYd&Cn=iTK4gM zf>L<$q0v4Up{17n=Nn1Yw^IU-Mnq~9@Sqoxrnk^OLA29;3dShv$Imv>1Z^s}kI_;$ zm16OTdXm{ZegKCqphqQnNzE96kme|#1Kb9Bv?q_Wf<}x>s<@y;qwHD( z)jsGmbA0RA&aGQ_rfNbnn|*3k`-=W$@z{?$iOHLs+cdB$o^N09(9YSjcRsXW;X~KX zUW$M$;TU=OnI}K@^ixlYoxPTPQ+vyTx9sU+Q?$R+-nr@Mii?kJ>g>4k*v0FfZktOf z7PUcr6EbNBxZqU9EfvR*YY}6#48k%*UuteLRO(PsogKgi1ICt&$aL~GRP$7kiE%;+ z5HvU?;j$hAe>=-lk|KTcV#Fo>R3fWKn`?)0(DGYpp8A zW?d=~pQJKE&x1dC=^lTpEGp5~^rl-L*w)aHY6a&dD*&VR4CJ*m(El7CZVe`3N#Xsx zq@)lg1(tyz&-fHy%9r-GguRgYMBclhJ3i92C>qp!0E|hBZ?GHjQxOpzp09F-=aYlG z@7tYTs|rH0f#3bu9c#MVld7bS-t-a$mYF;ppaRNNVg``dqn#IJ z?w%o&rs@GN>)uHMNZ!o<$|Bj?cg4u$Xs(|^{AV}+yy)fQ&suoa^xfUI)@5#T_jw3j z%I}q9M%du>Q-?S#%@v5`fNnzj1WZg#4#)Re%>{g-(JoK{h1PmH@xMe48 z2aKEcmdf!ccL3*{U0yTk%&bvPUkB%u%duSnZ=SN=t=iCQHzOu(58{Z70c&%h;z-4V z6-O&RSMkM)uU32m{5fXRvuqD#hR-oY;ESThSITG0sLn)*jshZX%&6xC!H;9H+TfdG zbOH1L1Kk6yl%Y{BcM-5ohKgpnak#;i5A-c$SGZ z`)Q~HAR~to`fwtIP2`0lT~^s@mFtaVC6>`%QE)ia$k_s`%&ZfI)9qB8YaVVZ*0wfc zru6dsQ#|iMl35o#9jXGgEV8!f5+VhOijUqwpf|J1+a^kkauvnmdi;xm8pVO$D9$03 z>o4xyK)K>BNh(@wHp|-1i`V~-UI5hyT^ha)^itJPN$-iw!*r>?iMkkhkH{r$!&5x* z(rbN=zLtfNd>wuy*wpX0o+@7xEnQb%-p4&0sH#{=Ujtk8ZpM=GSaIO5_OsVP4x)ZP zm(clGbV-3R;m>>!@G_?9qJ2NzFBa9G*e1LEh{exP)sJ#HeawyLP zUdapcq(!vsiT4q&p3GZxd> zJ!awEhp+kmwfgq}Se&)?*KIkIg-~6`%!8x-5HseQutO|db30)^b;Y$${XE-#bv~Qz zxFX-O;^6)tH85tA*QO%edEN0f_kLe6t$KL*rJqd`Ua^>cfWI}qd;7!hi^3J(&e6Ni zI?%m6$(l?7w~6mqaq(_Z*w}wrzlnF(m^e0dbxe!o}2aW_fqbCsJ^ElG38PX{k z&czs{J*5FDk3-Ky0<_#vFwtVVF}dDV)IU5%4HAY?f(Ceol9>h|!uY0q-6T7P@oeVM ze4B009-HmsR^*b&?Q~>FlK1S9_AAn+pCGXdcv-5CI|N)XjeTG>VtqAMe7fQZ^cmPMJX#S}wRj!@ zoGG%~(BBg+&I8S8jJYC%p=#!7$l?q#%@a>kK7ml<)SQG-iqVSUOpP$GM2B&>&;;oR zC=eL&=%A%YOh`63O#=zYFcEO?a0g0grDJe-izj3}0WU!*L4YnNhrf7J@W92STKI+L za;}sGG527@INXH?TQY%f>~o+03{>7MgieTPEDD zc&%Y?WxZezTFh0NwbE=h3mGdTIx&U1TAXTjjRlD;tVrJ0=tayZah?qDoz}__yI^J% zo5P9#IWFRKS26a=E7mv~y6k~ltIRjjrvKIc{l9|Ri@CxgYNCa8UG8#e02|>=oo1IZ z@v0g!ZrjuO1_0HG%_Ny^R-3H<#_2aDYAj|m@%pSD-c;RMKikArS0%gC5*w*07A0hJ zh3UfXMA~los4{X=Lk3$`yR^7hR9gkERe`au;qc0D29lY$oXaAFx&CMU;qUcD{XFlB z_kEAtG`L5}scJ)9|BfQCs(vR;@?Dvn_v|yh@hW1{Uth#Kr-ZL|{yt(hPnFG8HRRHt z-xT(l<*8=7{_f1~PO6SaPcHjpJE5VhL;Qg`h$gd<(n0tHPd}%& zj(HztCRm%D_&1}f$n4teX)1t2&LCU?Vz3zTy`bhY^chG++f<-Bi^=*sN)4F?hC?(m z`o}y4gdK-9bdSQrdV zKxj@(|M@}`>JVlXFj!_^PkWcc5}G2#UU+|ntTOO^wV}?-_of7UV%HYGBa!pjAN(^M-7$%% zPR^HX^DD3(CVgptm1)^Lh!oy_xyBwK+OMv?ZZ5+)TBcpxNX#O;{TP{UPpt_A>gJ_L zF1&4mjCxDY zGh*GAh${s?z{lI1cgk!A2sy662UyTqkBgaLk4%W-KtEKGqt#&zh|XyE{hE|0Ip z{7^|WG;Xr-W|wGlYmy6e2VwPj{yWv{_uhTS{?)B4VMUXj^-sO=r4uiATF8yZ5Z&?v z(i^@0zT=M^x+Jam*(r0^jqSU6Y)eYT=zjDTv;#npgV)WSQn%oyh3%IuDUesZ7YBo! zSLRq4Ma>)-e~(LrKe+yC@1+Hsf7XTvE`tA}yV0`s`-c|uOP1L z!IW8b^VhFf`q)iZ4Nvvh>X&s?51zc~s|m`p5qA3O>u%V!sz@9y^S5H0TQSa`1&=vw z_%P8pv2)=Acm@3b4#M~EbKqToEt?4HOHDyd|F`=W&hf|-Pwlftj3#QAg)aC1YX6?1 z=!2uASo|An<^jaT%h04O7B}i;V0Q|&Z^x+$^1BYi!^1umbaFS-+9*ypRf28Vg zU_UXbG*d1BU;(;^c?Q!4l&ZiAB&df*sQ`ooPU&&H2tYG&x?>1s)s!Cq{Q(S)&QDPM z74o1%*vCVmVh37*N)Ilc-Lm{ro1j$L*47BsN%PdZx7wAcqX_YLx;-h`uDWNcIq4T_ z*4q4vU|YM~W`~RH6L;VJgy3@8T+7$mzz+E7wSHWC&sFr==@5mB7LU_LEL-o!)z$Rs zD#2{FUAo)_rwcQ^+~yYS#Nuo-SNqK7wk7Ur-hFKGllvTTv%PmaMG0;1byy?nz9$zS zbMw{iC2eN2uiD&(P~)1*%7zz@M1o3jR@CNjHll!s7pWEk%ox5H%Ssfm6-{%%T z<~Eh?V1w^iyORqCBO{B$tktDKPM^dpT4A#T^18&Fp|SE#*a{6A&6x`mGE@}C?Wgze zecg=ZzHfc`l^NZ<DGCtJzpeXk0_Q+tgibIaE|pICnXKf9$y z+t)juq+7N|Jhl`E$iP#I6*8hwYKFgf1pym@@I485aa?_9`qO7;{XG%(Pu+iVeFokT`WgL*erDUZe*3L$#Yv1VY5n3WuY6an znp#ChX8nENjMjZqvvuq0qF*3HKl6ocuU~c5>-u>dj|5-H+c_(IA!k5?F;X#FaaqM} z6?cJ#9IJSy;)ROWz+WTr0$MFkrB6(7bScB_jdDYn@Jbx4!KPvcq*ETyYm_)=OFQV0 zc@uS*#2wH{soy{DfUOD{?nUUk6&M&sjsmA~7DhyZ-UTP&5rA{jTQeR5^+t?SaZLdm zIN|KofWOm=OGPvR8n*DXI4v~p&Ai3AKJ|D4|Fat0V(bH;kblNfr zI4I6zIiw-tNS?6%gmsw-IthmKa^~tPIiDSWpAiKo2e%{i?&)?-M19?&N59w=3Gn!5 zUe?S(Ncwi=@R5-2*)KyF!2%@5)&aH}%EX@VQN#cjRGq`7SFFPk02E!?RD3bNTEe**X9N;ke= zbC@~NqnJ7Jx=|6cOJ5y~p)ygcm9$O2b4rA%%OTSQOOlnXsjRs2nkkJd`pq5lm}F?| zGUqPRYpqgH6UB6g1ur1d#rW?uWHG@%`6qh>6|pN3RrueWy$xU^XL+dW`7|1h#^cd= zG}2g>W!aMDS*=#KWLeU#z3cTli*X!xG0S#YHfFPImL-2ne)vf?C0QVZ5K2OzX&N|z zrW_83+tQY%G?d@=Kq-Z^q|nk9()P5arGfUOltNDr%_{eKN8S+H-gECgk}Zv9zWL_+ zdB692|KHvl(*_+eT}?kfS|IfdAIV53_08BPVkNwxFCoSR@mjUE&{guT%w^C4MQX-~ zVuBG3CEVT*&ox^6!HfT<7|SZJ#U^8ELMsK@)}@wBrW@1L&Ot7s0l`NP&9FVV`_awo zdjDwS%#TMv3Kb5XNVo2~waOPiE|xmw9ce!GRB!UBr?j;teOVhnvu;9tlBos1Ltc=# zhS_;^^ZMTR<-A-_>L;o<*Q0~z>J{D*?Xlje-YG&Bzqj|A13KQ{qKkV^?6KIjQ0>9M zDEc@o3Xlr(Un!M?IT1Vfi9iHCSG{gjsP{Y~#|?;F}TEdnvC z^)tiouYBc~RqrpWU%nroIm=pmMG(7&5WlaKAivr>ANut(t2?HSZXCJ)D@uKG^Z5N= z`S_W&86+Lx$$4U|-X1##*U(|jvv9V}WcijgFKhyv4sAlE*%96~O5tIk5;Vg3=vvp0 zU6+}t<*T#hlhxW46ZKt%U9-hvt$gDp<(pk+_2BGn%Qvcn#cA`g?&^^}g(Km0?VbCA zFoeXZ<@T){J#p;x+Ty`lF3_!)9DLo$rGh)EzOk`CL zg8);o1HZG&knM->g4y)eYR`BFE*B{h# zXGWvt@^3zff%e>&ZWZ$nKbbp|(=LDR>1U6Bw74Etk31Mw4^jTf-j{j8=5%lFqwT_D zxjR>E?G+EDMt+;0+MB$U;WgbFQaJ5yw2rR06FUcK?V6EQ zUMHA1Z}0UUJ+opOm7@oXmlOqkWnx!>L~iK)wKk^R z_ni;C$uxKF^k>{%J0kpNDtBeaqni5j4a?kpFgpbb96va|W8w%cv!p5fo6%OWi+`VKt3@J`wjb!6+H$P+}D}z1`seE&}nD7oy|ZwwC9b+ zH0ffv1xlIZ-tB2Ybe-&6Tku8}d^bl`9L&Vp(}t`Yxc#teOeY#$BTOwcMDEXW2&QU4WS#sIns1bIB`eBl~Eaad|6(U72%0`=0(o+a} z+QouIYDj#?MnGis5D&a?x+&xpo$7*mLA>I@iXECL1X>JaxDJe(=D5RFL~w@J8)~OM zrkyZGe6zP?xXR3$zHW?Z54jG|ycgMt>A2o1;s1B-+;L=l*J^3man?nAx%gH(u&WHtZVC)?ah~RC^P#3utv!m4TmHV=r-b~hZeZ%y9*Tf{j8If&! z+EAoqHOP775N^jZh>0#cwi?$>@AzmGuX$vg7U9x4Go?&NjlX*5?qhPN)qBdYr4C1b zR76PfD!Ga<8Z&a3=&-+ zp_$Me45?CqJLEW)8CGo7%2>@!Bdq7%-+N*Qx}OA@=rV=_6*D+usY%yVgH1YSnB|lm zE2iTZYWO+3SzWd54nfI|F_VVw4MjwauG2207;ZA1d)=}%8>WV}cQdszBG)k0m5b%2?qd9b`Hl=Xl|R(wXA zs{BBE!&e#a4?OjqBS&;=(y3v;A2j`gAA9cyk0yVr_rBgg#$H&t;pQ8gD?js^Q@OnC z!B(Z-2o1w+?LKz=#v9+fBG0?zng2C#N$k(n=g`~i#AkXADrNz_>-ETvU$U6=(!mj%7|1+(2_dhx>=eU;m-&_m+H`o5dXKmN{ ze|q2lX`S)yz*Ey@Nc_H+9|SL2YEUH@wp=PhYby2fR=MosaAg#1drxAyGh~L>E)(|i zv17_Cn#W%L#dEz!z+utfU_4H=RgTSL#|#7E!E;ZW$GGxl|W}#zArNAMAIPq&ausJa~>!mZJryg*$ADpob zb9Bd2$+36s_9skd!+7|85Bdi;-7{xS|Hb+bUz^8{o%rFe_06M4v3q~^#Z}|kAM5M? zuZ90jRVUVd_~PE*|IwU$1vgQz2j zRz}37N8ALvH+vqtXpj{eh0QThicW|pmYm=%c!S-LH%DUW-I^nIv<8 zpXz=0;Krqn)ry-d`wty&%`_*=#M}3O?DXBOeQSr74*bMg^?UE0t*?`s*xcb0+y5Oe z-Ivsd+{sY8clpf8mz}$&GP(AG<&RzcDU#ycvRy5SW}fbS_fvPAwQ^2s?f8-R@83*W zwlh_3&$f>of6aPc*UR}m*Z$NyKY1b-UUhhVYqdOTn5*XwZ`7-`(L(R>b5|Ujs_t-u zqIc-HHuP!Y96vigwnV~=_`dR_aCXD<6(}0<5H=!4uhT_-TjrE-f1NH0`8MC!8aL*f zV*bIB$)IfPbYa4#FAm1zz4K~zMIB$rSm+W~PU0)${pX4{b(pacC6qTf7}3R+yYDQRD+j0|Wr{`>g$d#0!hZ*EVWpR#q`Hy7ZBArK?XI zSz63zG;Oqa`;mPIiwBlZsj*60pPeks^hd+IdToQ@*<4sq_5J&*wFPBt{m8gLB76CL zcwt{wEEi7}9(+Ma?AB&&ZS~mNWt(fq?g|T*@$<(IxJfUg7d$;#o~n(VuhzVDqBgO5 z>bi~1YmcbiM5^CY*A^^uQL+w6oSxs0eIRxj^nBz`kOmDx;dbKDH!5UY_CkC?A>WA? z-Ie>wm+VTf<}qBPdo@&tW)810y_ovIsf7FIxSz^JWd_B}9oa;wiG>TppxwQ?bf@N~P2CRfzpeMTH*7An3+}Pn zmDW-?o2nVsN6(MubmJ$#YWjsrSS^pGHfE2l1m4=Q_Id5lO|P|?GJW5TDwem~Wo-yb0+LAwJdWR1lShe>kea%qkCtIOcD^$mtm)6x0 zqgqVF&W$bp(`C7}?Ap1je=cQD78Z;9mbU6gaX7@?bn(c?2r6QH{;*rjk70ys8)~_> z`O2ksUAJSXq)gHTDE1q~5OMIXfFVRrxlfgcx92MIg+X1bw!#&*f6k&kZRqRb{Zo_IF47)LoyuQTwy|@BZ9{p7!x~ zoxQ(zZ9TW^!E3eCk@TdVoj_cU0!pOz8`Lek}c=5A{FW z>-|0Bv&coB2d9{ahI=V=BSzxVx2U7(;a1--Ox&EpA(2HzSdyz?0{V`(7|@0e9yBQi z_zC{$q9lxooCl-tjM&zpk8KZY-+lMnfA7M#?s@z*xtl(Bm(o6W_x-)=wEOSB_Fc_# z?>*PPtCp*Ow2lxZx6%mX>#gRL^#4%4l7B%y%&AV;EAEOtPyf{27k=kcKlvMrTDAB0 z-9NeGQB}P2BkEH1w$r_viaYN)tqm8d&E9L5XF_AP+Wq79x{SreUQ6GGIZi-AG@E#aFp9e7Y+G3+bYnDzhf*p*(dml&<2aOFE|wI ze50h@mo1fMN~Mtp{fUWP``hA}qZ3%)f%`o>WgF@tPjkf%Kw_mUk7u<#i(ByoA^yS3#Fw z2W9>fXfP#8HVi*@H)8lCtiQO=YS|7-Fpe;_5T$%P(M@!5j0gQ-^iC{^pxi3AfPwX79J~11WscR?cH~MwPTs zc^S&JiMu7A*6|82DzM1|s5#tHC`UzzjinD73S?qmNFK9|V(llinl)+l{_KtoQ@_Hj zB-9@{vB5`jv68Z__vOYvW?TA(9sIOqIA1QhFD3j|7pdXn-&*0N=lrCp2N}~k1TAE3 zEc3E-a@bh0kqjc7?J{PPf(h%;hWw5$Sq6oUoPN{V+v|6H&8V1vaNF7=cU@~0t>^6f z;#Z9o7I~YZH{4HjpgyZrF5Ie3UAXnCPiZgf{cP`v8)8pBJ)+)x;nvuby(cd0>-$4~ zh4tP6-aX)biZ$OISZ4Oqa|z^(xV7kO<<>7SblWLD+2- z5o`qsF5$1;{GN8hCChvAmoLBl`@2`}$QM4oa)X!2%6$a+Q)uqb6Ux(qj6u@3wNR@yWUlt*NVZL>*RP zanl_N;!1neQs^Dgm$aLJA?YA_IxeR*j)Xr5I)>%b6dWtk(@=IXBVWeP&hF>j*$XHh9Dv>n_q6V|Q7f zRmi9@ZzbE}P@w6?;ApTXj|!2K<%4OR4cj5A#hTkHM=c!gJ)#DQzopa=@)7=&LH2b> z{{eRJ7hvylB{3SW8~Dh;ziLS&2zh2?l60A^e6&a)ab5aiA)7#zY=+61+PLq4l98QOgtU-`HjuL@i zMq3mDd6fr|(;=c=xiNqTAZ8FwVjbDb#b8p_M;sWJqiH(V zfb^t+jZRY%gohEwm<<}4Ec+iY9w)A2AcAl%ahr5$yNV`tH>2Q)(3&)X$|J%xx+x(B zM^Lu>fH<9&R&*m*HEnv-F>RH_GRxWNdOIE6Lr{vK(J>ujP-U$tYXr%pYkIn`#`JN0 zeAf(=yD^2G(%R^JqqC}3=jY}}*Gx}M8N|Pu*)^_HPWifTxsL6RP(#)oHMN^Oa)|rn zpBY1qGOCXqy5)^?)}d*HZPS*e@8EIMrmNt-u_JU$l_dwLj<#~#_Da}a`NS7T+WUUn z@JDRrk2prjwPp~<`bIXAiyDsW>kf{Mj=2Nz)^^4OPx_nL3i%WjwsK*@EzOols|FA z^WBlTztZ)o&5fxYYb&u^kWpj1h&^O({}KxjyB0aYYO-<9PpPp~#`iM5>kkK}m&iSD z|DeBis_M<}Sv9I2;rG3&vAU-{Ts^hs4=z1#)EqVgFYWpnqGq^xMKD#}warmu7#*;y zJ9LAYO8{;Xa35Tp|E=uq73RID0EY}sY_2&WCa z8d!>mc*=(SmRiCxjAW&hAO;9$*r+J7T?&p+N_Az=;x4#JK@5G5iT-9u(gYEYga`{k z35imoS?Y4w?TEgM#foE;sJysGh(B1#RwQ1E8bo_Lf(LYBbbz`_fsggtJPD@gALcn| zio2~j_P)L>WwS8RW_{S6lg|aCYy(78$FG9vcXEc6H_g1Me9KtM7x&mUrdBGK%Z;-i z3@RIVP{ZCJDT#7cGOSPK)LMqbS8i^6_-a3kiez<98HuntjJieT*4F1%M^`RSxKBLr zUd+nvkiTY6BMLfWrnx7hXkFk=(#fWP+7MVu-SS%*u}zY$I(a z%h05HHYn6uMsB?Q?t1gU$@;QWVGZg2V6dZmeWs96Za}q!<4CYQvMuj3JWcnE1bEM2 zK`7jAF8qx-1X+v5iP0eMhe`Hl4QE+N&xY!mr6h7$NYH1Ve&%hl|AG8s5bx$Iuw=T4 z9W*gJ1+5;1fG0;^q*PM+6kII|8JY--s{T`L6kZ;0OL|32e=HIEj)RbPm`e+*(gv4(UGlRx+w0GN8C7B+gRICO!<|HpCM+V zn}lyloe5kIn#wW=mAA4L23aqYZxu!@RWPm8NrwQ?DBa>_?|^FzwDl zeQ|HT*gCdwLai*FUcE$_RzAo2i`CR6$j>js`uRodT9>oxuN?SEcCVivc-O#t z@aFk1==1*Tz}L037C>K1Xxp!6kFTXJy#k5?}%bA3C-H^@y@Qk;yiVpnGDd zblwO`7#N5UK{iQI+{Z9MfRa23Vbh-)v-z%zQ#B6E6dKLtFC~9n&6|K|a~CrQUhSc)~SA z8WnynNwI#}sO{~!mWX&8K-FBgxuCV^D(ysW-<)gFA{qx**uT5cY;ZGCX87h}AT5x8 z9@uZ5K(@R=vId>P=wdo#zwJiPpAXyRMw|K=CJaANVo3WK-~JHFSU18_lk~E1F`VA1 zl^am~O>`YnkfPfKc|ap*IC!r{hA^3iPsH~hos+0M_lc>8gj$6Ppf60JM9bzP^_H6b z)=JSCEf(Ezu`z4}G8@}1gE8sH!id(Vj2_E80ZX$Q_s|*)LXuh(f zT>vU%=a6<>)P^AUXNqYM6pS2FBb|WSiYBKSjbnm|?zf@;DHX|zm&c+yk=hWcH=(O2 z=Qoi7`b{6*OEYRiAnLTNlz#RtH;1M6G;kWl4(F>qFF-5p7lV{&UxXFP%=?*OS{z+? zPbcO(2`9ci8d1xl^AYgNRB;!7CHT2#pVWV5w1)bN1lCl_(E>3ngx_NUHOFJp6@V-% z$k0JhMy-kJ2^wW2B3Z_QkwJ|l&4hcU0g(tKEAkC_3xb^9Ze59Sn2zXF3W}8ufQWv% zxy>I(MMF|ZG0?JxqkahgN2_2h`Hp~fJ_PpUIC#5V{7Ru{R z5yCF-IfMC~Npr81jqW=FDV2f7!%7c36!PUEuCSEpjeDLG7n2i1Ig^fLvBB7*NYhBZ zfH$`lm+B2&Z102w)FC=!&sI8Qn1(8(oMw7M3Y>?ci5|x-`kDJ$Vc{VhB#YJP}QT-uPkdoW=I$mk~rJPn) zg3SyO={qTjRSpszYBC4USxAep9WYG{Ss=Vf1xL4A*HO(~rV8kCb0yu|u z0J+H(xoVkkDGUZ=vSkj9>fW$Eq|DL8&tu4Q8$TGD;)HbqSV~y#pcozvnnWPCD`AdC z$W)5qkuvudEYHYDowl2BkX4%_xQcv+TFIj;O<;-fWa)?8PyY`1t}`* zU&bUWYM;zmT;AR;f2Lq=?2vz5U+K@(}5jGbzuaE;Db~=}F&97baV6H{}eK z%lfEa$XyS&()%D~`mK`E>SlF#CB{8+*2s^D?HY%)(Veyq#+bLHltQp6lORWG#E6+q zjMX!`SvG^PUa#gw&R|+OEK+pS9F9z}banQUd^W5oqc-thsL+iEJHvytnD+M{k5F~C z9$4qaa)ZpW>M}h>hCH31p5xv~Fyb8}r%|n{Qk)XLEXlZUx7$eAjHFMZMNW`s1(`$i zLJkbCYp}cwBWdL_gC^iCFPXInAINbuKIR~q712QGq|?w38nr^%cWPR*J>R`oMPHiV~!S@vurZ-ZC4E?q#DWf<8w z>LZ&>j?6cwWIW-=Rg;94q_rWgvD6#AgLS##50XS+X0n;K8FMdr4`aN zP${#k@~mZeaHfGuxS%KHglVS1wg!L+9su5E)gZ#h=z4}pkck*^lETW%EruO1aZH5@ zq=@V%+$kMp>04xKV(lfPuJb@?fna!)1_%?B!VN=iiSFZc?^2q9Ktaw4kQrGEKdZ54 zekJxm>{z6QlK2UW;uYXU(Qc;a2Rh<}H=_->L1G&Jd<0fOx0H}*Z0y$EY>zu@Zn z$aNK`yE~1l{Nx=y_l_&nJy-gBP&Zbt4OF^v$&xp9a4)=K;#nIT(X>h2uNHm0j4Kh> z4ga`pNJq*M(dA4}&38k@>LeHmx@qW1sC@%+2;w#9q)Va1nuE3pI^7V%#jGfQ98HvR z5mOEKd*Od|LzHjYugsEKg%W85uDwuq>PLhI9#9VsNIBia(VWyy$$+&%#>&!8yRsFSMuq<#NWu1WPW@RSjo znr;VT7oI^*(;QJi&^Ba|*-RuPg;R`r(~Wx8r@ARB9gN)R7Oe%d>q1e>v<7-t|LK>=1PZrwRstSvku|)&W`>(}X zy;zvhsw$>iM?SlsR8?knHdCZmpe@~A20QtP<*WHgRhZl?z08cSr1pCYStssiidAHs zWXvY(kC}u364Pxv$SpcvGPch!TEBEfZW?`j*|O1?M>xscjcm5&xRb6oR;g^%E3@@C zxETIp3GO#{wyPKcxU*F!HCf9~c!hOcBVXN-eFf!uxiVU6*EPa`>|`cd1PFG}@#3MA z)!w3BEz)^CEEKwGRb?64hOTxp!THHXTo3c*CKn_7!M7u;KZm7A*q3q&9}mOnM9xA) zbtI5b0%TAw0b4WVrPTg;U8|md>}LwGT5DE6_EK~H*u(d0$A4wE=0ER9+o@*lDQvAW z>BMsE+-EO*;xBHluHSaz$kpvV4}AV+|KJgIMSAw+`l+{V?fb=7?>}0%o0o49EVVBS zd>oqT;j|334``Hkbcef`SE&+Pih9qY#o{n*AG+QRy5`BYHZzc}nqRF2;L@Y%Js zxSKCKX1Vn;=P9pLXy{JG3tQ>c>F1;d8Yoz?Hk7o^XBD`Us*r% zu>&_hGccLEEnD^dy;p5+b&iDjjg!|}n0PErFAl07pfsu%nwErA@`rxQ^w}OwXis1A z?5Jn{@tHTo{+W1DK9=Zsw1Ipi5dmnaYe?^7q5`3(Gkw03k&rpXI&ew;qIA-vxyQ6l z2fGOGO9@eSjw`k{%Ag*WGgz~Lm%3mPaE+ivF`AiW%jn0W3A5_q^B=0!>tBBQF1~Bw z&t0v8{pDBv`G>U^?q57mDcaSO#cl^qD=df>5efZ}HMZANNh17@6`DESE@X)g5zAKA zo7X>Y3Y__2Yise0p+b;2koI6`(JGbZP<|~f1_Z&*v z_%s;g6|JSS>tB9(7&|`urKj(D!*-jleRl;0R!=^C#qxW9@!|h$4eDgL$Dw7|YhpUX zvK>t7p2VJKF{Qh+%^`4^St%OsOs(s;bNdQ|_4gfgX3tK-SB>V3;LM5{)Azl+0_8q3 ztUK?m|Kbft-hSe0e{!~%*OTs{;&}M7@xuDsPi+kQ2ltpk1nYkhdnz``Jaxo6jj`@@ z2V|N$Eo4vRYfO|&LF8~O7(c@GC=8}!GXmGq8!*+==&QGXMkkT*$}RwSiztG~=^`iW zcD9a~>P}6+(yXE*FoyJ%-q#PBiR6+xsA3C`{zCoE;hV{~={EnHm)9+OWApLAU7fwE z_Ycd;Y@gn(%)E=22IQFQ?x;L3S6n=O{&RSloVs&${R}=S4{P1)%~>mFrHrj!_3SyN zZqzix!&V`4I2fO8eL&NLiaB)nTxuO|9#+J26~#ff=KSk77J9wl0W>)2yN2Av@095qAxsn`6c;x zWhf$X4TI4imWGTlaNCDdG5yMl>5JHiiTwdKD_ICQBE8Y|Su>&ex|%iOu`zYT z8NECMN$=S8$i2r};>)%I!_9en5pF$yHZ+r!$w}L(94WAf%!g$kaK*^0@ET93^Fw)jbRZTG1SMwK>*1^UQkbB^Nu{vy(mj~I zNyEsUw*dxx6g-DGdbl2iBp{*4W0QyqF#8`;9!mE~1RsN1rATC{bZ^uhBFDP=l37)# zq0Bn2uT;u@e!&m zWVi!IGvR?eSNIg1v`{(f!((`G=k!V)lS*qaS8@x(IAR`ki40_&^mkyC@Tv@$LLF9B z18X0nZVE1Fnmk{R!8~J~!7l^j3RQQ99~TlHPEi5T#t?3@bX+R zjA|VjM>Xx&9>G&QZhJ9SazD<)zp@VxVZk;*M)tkL5q}|=A>0SZ0{R8P#1cHM6k@v= zHgM-FK#tB;+Mwcw9Jc26g2iJ9#)XRS=p?ErgG?aHn+*YV!Q^+{^@D#=Kls62cWF1h z>c&?o^~!6NI(s&zJ*KO#UVBzkubRa3OnpuJ+SlW~xvxE;eN8>_3d!lao&aai`tthRC_%8qctHn|{r$LN11-kffhFn?|~%3$5H5h)pv zor6)pDd-$Hoa>(y^#c`$I~9rw&WBQ<)TS^e`PfYw@FpsR(mbcyqM^Vns??aK=B9kn zigJ@yG#g zaoFre^sVfA(JK-{2*ulHr(v&=Kp4ST##M1spPqn&js!PC=>yy#aCzN$6MN=TO{8~j zCO?7<2?=yc#I3ODqXrf^si;mJwj3rLRj2D^SatFWy8F}jh*-m!fvSOyjU+(wc)5ii z{np5(K7$dF&&cegNdLu698ojPLK~68)QU)UrtQ!v1p12Bmhk>4(z| zSpu?_$2@*#G9ZfD^~C8aD5S3$PK zaB`(|5u5ld(RnBg8R&}~iPS7()|F8f{gAxpqwj_&JhNaNxzuW8Gp$JBL^m&1$ai-x zx(rg>K07aDWH`M?n{UoFqiY#ZXEc$~g|uiz8`q2N86rC`Aj*P(4LhbS5@dcD-~m5_ zS(21ke2d`$z?dLmq*KQIJMRsO)SEQ6h_7>~7f{zDE41PW)+PH5%LYo)&N!|}1EC8h z_o$V_7Cy_$9P;_i*+?eNx{${K|C^hzwNN-|tZqb{3)Z0$UKGGOgXLi+mASY#e`y#gX1Rc~_77DF=dxn3RYw#5-BJxq_&pGE|dv950u_3KA*-Z4Y(~ zhCi0Ym|g+~3K}A>F8}l|?{N}gE(A@GMHS{_5>MkSS^IwA791~wVFKDT8L7gAmBY>< zlNmyFV$dWDCu>aj(k{#nt2&b4npjz~W*J0qBV&~J2b~ldG{hwsvI}Cvh{MkyJB|>N z328**ktjod=zoV4_7-f(unDyM6t;rQ8WfH-xDWfX~a;)qgqzVGj(*e&0iR3N6V5Bol6HNV~v0Qo2vax%QRRE+- zwY>U~b$=K#C2=PA

$f^@jL%;3uJHjHWwg?6R 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

xJEhl;vLq{tBI_;7vMkB6 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�&*DEQHM~hg4 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

k%?V0QN9mG1d#$DiU~Lg$iMFrH77NFLa-W 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

?V-g6xg$(aUJwa{Y-ZURBsa-8g4bzbrD_7 z_;AzVndTNleT!fkyS@R?n`w)joHkiE zH}q|@kM`BuyX)&d<7$S_ym6|rR*%7`FZ)W_Cxzb%{|#$Z2WMs|!!V?+5d@Tu{CFfF zGaeX;^uY3p6nuz=fnlu)h-ZUj0Q6aMC4yMFf*80iC%jMpQ~G$wo{m)|-PVxbYZXO@ z+dULb%VMb4EBfP1?{dctaW$mWDz{$xl45ssc38yP3jJ13T_9?%5OgE;sbq(pxlC?{ z5)MVG=F%~JTyGxn*H><>ijTL=*+(rMoqsPXsY`DTt)vm?=0eiK_foz9?AaLDmJMV!3=X2-} zxrs%T%4oFOezG@O+n{G|pzZQ~m@t?}1}b~(@$*7swYQ1^w`h>;6$U}r12?J?RNRi3 z(vB2WN$aww~L`3KAZ2Rg=vRLmPt|G(YHBtXJJl33w%C3V-I| z%{U}H)_+ky{<4Ae^?uWsy*-F>&?f|%E`Czwr!oZ~mu9Tt{b zQmJgJ@7dvAv%aCdy{q;_>pQLoy9~ZMW~ymyQ9s?;t8W-{*iBQTYzcJ~%8r7s+^%Z^ zc0CE}6RL&4Y+Cn|0%}L5Jp8AKHu?`2DgGzuo=0*2aD>9~HA+tAvsxQ#8eG;|1UM5V zgvn$P%yx5agT>{r);e6@s2OXhw<0RKroJghJTI@^6=}PA_U8M;ev#5O*u*N)Ug>Ob z*E^cx-sn^~5O6x&F;A^os&-oKAl3^+G55}cO!g_r&?Rm*{c*VMvFE}l^Di`Lj6*Xe(}f_$zkH|CZ*~}uUni595xm~b0b z!gAo^0q)KYw-P4qox=O4p3BUAWy>>fJ@s(r^-IGUgk9dV?`js$s=vAUZklUujNLC{ z>>3DPsu+V5B#K0Uh^}#xQBER&3-=8GO@UpbN%!E1!$K-^SiQaPQ0lXTV|~4X;hN{( zdTu9-%5^QiuO7cQmAUuc>5-}2>wqR~3c3n_C{r(Af7Z$ik}aZ8BIE~7aRm1nZ9uF0rSvGlvFb+CR<6Aiw;skyWY_HCa{9Tv5EJgw(v;7qU8P+eLX$m3&g=BBK? zPK7?K;y$yoYD`~<#X$fy$XXUG=Mj^r7TGHZ8@Ur{)kCX3;m{|&zTEIObSg|A1~n!G zIcqOKYX24wW#=s0hFLl@0xr!(hnu|u+X>c+wXzDaITWi)&TLAhh5%jQdHdNWNVNf* zL?%aVF@v5P>&a{z&Ut;GT)kF!D}-e2&x5Wt%?zurXIXgM0MiQ@V1$3osLu{>v&>~f z$o{_ic2s^}eHIZG>JP`=j>TvI5g03r*au|fn!E?OCdt!DdBcjnPWXLNjAwu(e`ht> zI_>#nmN)8=m$sL3TM9?^!w2{psV>MMNI52DMn=Oo7PSPEI`pfS%aZI0ggC%;s^P`c zqQ?qGNU}%Ey1zlzsnvNRQl%8rTqJx5_9Uc~gyw){fcW%`gdEE)S zezv;Pgx02XLRWW&HY`j{^-fRq;(2OXXQ1rO4V^A(Od}gcO5}gquYO@_n!e^IX=T@- z?1xY`W!r(FQvvE4%{o0}Cd0jiA*)!tAnMf_;h`|>#Y7hi3)!Q3jgwf{e(}Y0{fjO_ zB_6QW2eJ@i(_x%}<*8_h z$2QKLd$d1lW=tD+p?Tg*3e7>FIpU_m;f!YLWl!b+Lbc|s9#78_AQiX?+{%|5RdxnP zM}s?+KO>89S0lUAZB^%Rnw1^3x>c|lqoXKi*sl@$RD*t^44g9hss(onvj;j`Def7> zpA=Gv+th&}Qtk{QvosAB_WF_AChbm*x6NsvyiL6i3gk){+-!-kuqzx^6KqUZvn|^V z*PCXHW=CP&?apRj{>*0{Nu@S-)zsD1=u>2Vr2V4otI$&~#5*l64x0!e+HA5y!Ki@2 z5Q%OA4~ih_Sr`Z*`0bebMhHoKV18iA%8GCyA{zgAKs^`+p)2f3i;F8I`%o3y*b1)* zid!3x`HqHm6rj|Jv_r#m1}VjXl)IX)nkc+c3U74Y)lVylgd*IIXV1NoS4E<%d#zTy zV|IF{ZdW=H%A%amZh-npGJAI^y2gaE_DeN!uY_|u@iTf!YIpT2H`(W#+}aDtg9UqZ zCDsAV)Ae=NVbP@&ZgHf@mV`F-DL!;h%25PYFCxDB7i*vg7>$rx7FM?GV(#ZZ*XBy$ z+M94~pvef;9XBEmJK{LCp-5g8BzuJi1j*1I!xa~o*UBJI793tix+ES5bI!9K^61_p zD^v#Mj$#tkJ*I(tsb(c_ciz{Rd)FH=^Pq$fk539f?M4!Wd zOl+_ToiqU8G!J`HgiD`Hs)zd7)yZ>q&SaipeNFex^s(ccf z{m_85dvSeP6Y_Vx#jgoj2jszU%>~QB?pe--X`L?BtKO4jSNE#-@BF^ZUd4}t1Ai@u zPxPvX=(58*XD6OcHQhg*Vkh`M{3_O`UhGb0j2%QmFj?z%$lxqNfL6I2ff)EN%ndw0 zrK~7Z>i5qdSA)27X!hdjhk{9XSM5Sr@oTTC9~=-&s~`SiKM*Bo|F*I;c!<9X9gBD| zU^jG@$Dsv){1H|I0I9Iv+21-WmM7#mgC7vWa` zj7{4Ks--y6Hae>tECK=oI#U8d3y{g#2t%rkFe)jb6OQT`kfh*maS1GNX7}B)Z#Kq$1yZjCFdaVI`q_?-&k9ExhiA9X3^z9q&upI^Zp`&v z+pazfAuHW_eP8;#;&?%^LXH##uaNV?Ky0g~AZfF^?)fmaOmk`WCd2yKWD z2Zi~`nWHa0dzb%W?+y3=*}mc7eSdcUZr{cJyPkdV=nTc=WEPj^zp}f2%Uxic40-47 zJaXrpH?%Oa?XE5LyT6hz^Ek@vL7B9UYh_9XePffgyHPL6+`9(QfNt*+dB3xnkIFtO zJ930U1Ik=C_*aka3y1eT`q&n&tn3vN7renNZ(J~OMN*F5w7h&%R8FE@1)H9hn~Gz^ z*KI_EZ{#+6FG2*M^>EiDjSAt}-q~LD4xVBDB=>s*#+2PWr{0JULmeHuUVNWL5acWw z5!fRQ9a*9j%x39lA}BQxn}qpXDBr~So7Qma8eIK%R)v^t*)>{eSj^5?o4c@K4r_(6 zlfR=vw-Yl1IvKn~KN5bRi%^d%u%eLV8m2)P%sv_gf{_fk1|$29*b*ML$!4b_rY+mW zNMyJ&_|isuR4LoESVSd2-qh8min-VrCehA*w6dAMG>=t3$! z*xpcr4yBWk2}UOr4Gq{!rQ9=*ENdK^U zE+JSX#P^i)vhYXm!X{kL@0rFr1+4-Y#Nxw3bl*n8Q)t3v_R(D#^;?h&zzTB?=dY zhdt{Fc@xpyG}#fzM1n7%Ju4KB4~Q2eT#8Y>6tc5+ZH;Z_tk)-LfF9-8z;!`QmJLJ` zp0Gy%OO_`A(iw;xZ$e8NAtCq-1(Lp0l&ee2B}wEA4Wg=$x0*`*Ng%eaFnm-+4FMy3 zDIza|A&s{y5aWqUI4lb7D{%W$-)Zgpoh1YZ#xQN8l z7`2$d!k0?=vREc*4bZ31r<6}XD-+&|v)!=(EII_j7Jh)GkqJ@6g8|y2%?2!)0S8NVq@ha&8TEj-ET-hN&!csSgL^ zUN0g^xlhRKA!Zropx{N&VeDXLkXS3!Z%YrDP~#6>k0O0as}}J z2voMkx*@?)b$Km=P22Q((dP8Y;T04VN~iAXO1qpL{XLiM-gD1>rFrI}z0jdv(@S14$6R>R>$^Fhv=#dInG}QVK?BZzl*{u!Wrgb^Y7HfU2-V6_{UZ|RUe~EU`rzXWeQdMcVKCg#e4{}m*Cy;Wh5gr1wB2Zt4Xqfnv|qvI zLLfbeZZOc&erbe_0aJ@V5Ze!_E>?O7J1C*<3AC)>940013}@ZK<6yO*$dMf4V}Pzl zYy-Vt!BPAZ-iJK`%iC|WAjqR{^Z(R0f#}NrgO60>c~K9ptY9rDUAqIg=Vx6D6s$V5 zvYv4TU;SrNx!B3oXN2K1EeN!#3T0_vK(B#5>!AHf!59GfA_STQEZG#^im5_C&;lrw zy{b34z6T@Ycae;vx4GrS!orCY`vI;#FxK7(EZz9R!ct4i(!#<4I+le?i^>>-UN8^6 z;Ou2+y0`=%0#x09+*k(7!qhoA2~%h9^mKZCQHKl1VGgD8^kwIM^sw$K-Q|$Ztgs(a z^suHS2CD?A6V`-aIb=u77|tTW5^z{jyeG)wL6DsgM%~Gz8;`$ho1bT`t$2!tr{$Q} zrS3>VM(`)qpY;Ky7*qfqTAhhTpWLmxytlS?^XA&x+U@P_+wrJ}{?W0&UB#fRSAW{a zd?}Z&vuSSH*C2M>(fPy;Jg!)a=YqvsVKKxRv(YK_Yg~NfeO4H|qM zxpOQ%cbd>RF91qKdrQH0AmR>S9V9#glo&W>5J<`-hcS*ZuS42k+-7iWuF+MfZyr93 z)ry|jEd_tf)z7iXd)eWItoks2q4f{GpaNf2J&=HqN(+}qQR~^N_+UzulQ|El&DYni zT0&6;tMXy>dMdUUgAcl_QdTMi<~V&R$eW+tj~3bE&1RW8W|KJMQ0_-JTd38xdoUe53hNd1C+z-LR{x7|B>M^rs8_xsT)JjE zCJWP2R{hC!?EcmN;?A zNM=A^3eDq@wV+7#DVT$4%p%5(_|^#vS;xIiK*wXdA|;fF6cVSIEuaij0oq`t7)?TY zJM(EUg5nWbS+0Nk_gd#SzA|i*vDMtHH7dX!=T2BK5ue5oh86_tYjS*H-^_PuC2Ut zrv=yY`&XE|A~X`RG?2wn!l=r^Pdj zF5(9HD!4%|gq585X|Wb!W}pU`FYv+@MlBs#^MMQR!o&_6vwFK0VY9SE(+3W^FLT2) z#tiNiIa?6iyPU<`EXed{+<(c#y>6xAX72SyZTPflZWUsww6O+RF5o;p`K&HKEn*wT z5~49+9v2v#LD2T*Xe5!piRS}lFVgi3wn2&u;ucVdvKYfi^%k!V^4SJZ2Iy!3Ib`2k z5*!U3diA*v8n0hHR^M4S*rn@Uee1xK(6x-E0>U^OJh1xEwOBw-Hr5PwpZXpa1sSn*Ue}64J`M^P}&;-aP~@Rm(_z+HwI% zZ$L-ozy$!rlyPHhb!gzd?ct}M3U6mm!@Qw+Ajgraa*^#he(!wvN%mChJk|oj#lH(2 z3zbvICRTR(E=8p&ShV<3OmmJe%~+*rd5p`nI&cdcg}?E81g8esIv{+{bAChJATe5= z`g0602sQXXclCg#z~h#V!85wJk^!qz4sng(*?k628Ty=`i*G}|Qg|KdE)&D;Y5hSX zl2!+@MbKm8=v@674@`!uMW;~R3iZS|MN}01Ls_ux=JN)EbpY+tZX}uuy>)pBf2@O^ zm$Z9SSR*R(bt*Z3JvBA_so1{?*T|lp6fS>J${V}(70jqdum+Z4t$qjl2xajmEf{Mc z1{Vjz+o14r2?znP7;ZG=WU@AJ*c%P>yX%35yI%=u()&(W%Z&$O&x6@)aMb(@G5Cy{2JTSWb zfeBN6$FS%Qh#y1jFcdyo`#Z}Tbe{pgj~0#JgLYFaIP5C%pi^rJH@4($D~c2kmg7;) z35hw7)kqydhL^bR+N+=2H8Z-LSx8MyL^7kn!L3R_vag;8d}_^dd+y zkm#F~?d+T-d2@rftL273IOp$bcJ?Q#16Hy6WN0yzYB>;6SEO36&Ea)6y3LNZsyYNi zQv5?fXCXUC1s|<4MFmloM-pigSf-7*N0>Ytm?Gdg#8|Th*z0^K(i8jsy2vKPmK+y` zzgif?#EFX3+eOqiqVmB$Kz&yZhO-9U4d^>8 zO?`L&M60FMVzmzX1c#%cnZ1{X|7$dwhnlm^21A$0Vfe+VjLPf!FW@ZuF4i9(^rk|- zUXXiIC@dMvXo!=IigSJ9c0y6*iQ!1Z*A?XaA{Jzg0OvDKeYI>?CgM&P0^}nOy(8>Z zX8NSsp=-OP$2AVqLG6VH%Qi~cJhACz&H;D$^2i#+HxRmb z2{AyiJwf~c;R3=P$n7=J<*L~{SH~_#K!RaA(s3bVPd)iu7jpwND>2 zSuQhiMtS^8q}o~=^Q25F3*1K(z#)mw<&&qcVJFQ91Ocn z`P;s_qTTmJ?Io)frHz3nF$T`Z{A=R54=*m8L*E#K)D49Wf{QQ#+;Na6(+XP9Sy6;S zfYJ#nZ4-C~NnNF`!$d{cBxTUTA)qwwb3k-@d?BT+Cl!p?EpEGW^*e4}389kC=Qg-^V1sJ} zo&@G(2-8S=Qfh;%hJ2_qESy*>vAexS{vP(L1$8bEh1G2bVer&=-q%&&bRn7MEb;;= zx@0*l{9h+8tnNIHrIzf5d5@dC-mt9whx^b0l}1Ojts?6XXpb5rPaH z@WermRG)$XN#*CD>f**gkX+mQ&_C=EbjNZ=TZ0Qa2jzYyERiNL8e_FWHmdXu5rRvVl2u@k8s{fLuzC;Ri^-2^1XawW9P*11Hn z)^2Za99&3^zqT-y&A!_#u*Snkt6S3miy!opwXz9M`Fl&}bVh|;=&UtR4<0|h`1@*+ z$}Sh{aXME6d~C3K0I67*XYl}_vy1$W(p|+v_$>zv+!G3iI7J5T*+w}kKXMT3Q*tHF zVD%9FdWoF1g-3pXz+Dfzr)(+Klya%$b$Q?p$d&}PrbJ>y{AqE&Ic?sO{C+1C5^lu! zgzSxkheAlot9= z=r2-sC_*Aw4SxyuD)x**l&l07yAnbK`Qy2t;*rYdQMsyH8W z*dsUx4&OMq(3gO?r_gqyMFEk)pVNmv3B?N>1(MlmvAWC^8RRf!R*Tatg~LkNY<6-o zGN;*$PwH*aa9fw(=C1eF+iPc94 zeTj*oWJt)0(Z8BcpVxme=>XGxqg&_yDk`dPr2ir{RNJ#Ba&;6LK86!*iS$xp*s~=Z zy*jd|$9+!fFVeH=1j*F}naaXbPOl*Yea1@f>7eS|E$zaz`U1?9owym1QmL z@4MAQpKa(gf5_4u9Y`?M^6JbO439XYqXA~c<%W)>>=R?Shm=3)tH79F$USZv8&Dc^ zfRzvAx~Vums{JM4S}{mK>cALQePhlT!RPRB?6I1 zIudzT3gF8Ic8SMt4_5oV>6xutBjyUf*W4>6mB`>=L`jOh;@I{vv2X1_pV{jdDDuYY=K>M>NOP&cGR;r$R&+bk|(HNGxWA0Mtmjv~Qd zFd6V=5g;ZYk{;aU8kyrZMh(wGnqOHpO4Gvfw6Gz*R*@B>8+tPtFZdI3oENlKY$OEJ z3c2k$B?2c-M9Crd8V<38nqDS+30WeoIOLwT3y_{z&yV=y~vwLzjK?G1@s_yFfB#u!j>DW==& zbl59hVl}A0>BTN7fvv%BhSK$ldQejxSYA!x^Z`FhP4){BISkI+3vohI#TK#JQ4x>R z^A=G-a`vPb*Uv8oajUSU35E##*HLAo47;FkuB(e-?81O;?PxRhw{QGd-D< z7!8J%2*N!ES_XiW+wX27HX%6Dgx`h)r#*YG`j3Y{EqwdlP*NYCNShIR@}jAC^6RE8J4awsD*Z<6&mMFc26VLOX48Y;)qDj>!;CzXr^v}41p>Gex#j@ZNU9Nz|cZ!W3ad6 zSB`RYYktiKn;3KuT2^6DE!r7b%PD){dsI?zJ29|1+u)P+DInDcL{2e_TUGErPCWw+Pc+OH7vNUPHTYr=TeZ` zL~$#DD8OLr8WPDj@1cBycpE}J#C%IA$EfUqk&pnYmAZGKcaYykL8NvPd&4wZRqC!; z=aDZ*l3my!D3=9gjlyyh$69 zfB9v4ng{RD_^n6jiP?w8#)jMahtBC+eSn_Z=rj8lr_UFxigZ{$DzjG!Z5@`N&&Prh ze=_NxO{Zr9$z)(AofdwZzaXxj9FUaoD_eHSWE*%<~ZqN4%|LUTpHwp zKhIAN!5Z`iu!&X)23I#eIUz1f4!_`npbcy_?8p$9aRI=CU{D?qU~bv_plAHHaZ-Wj z|7u=P@5BesgJA(5*wG()6t{1r%516J1e!g;VRhRPtm-rc;z(htc)W~-G7P@nCS&JM?wp+Hoczk!3N060 z>=J_>{|xGXb!lV&9poz2__~mlC*WZ^WaD`gkPBI@|K>NpscTf{=x(wq^*8EoXq-_j zriSa;1Up^?E6F*OHMbf(8(6xrX)@9gyKF9r?@91ZU`cn8CyWd|YwA69A;%dy3`+60 zMqHt(2X`^EZ$;Qe=;K(QckUXVyxU}2!Q%b_BrEWK$xf3usfjPEPxO2;kp&&u;t2b4 z2EScg*$1M*08@YM-W*BrT;Ar0TQi&FG^QkVPCq?H;?dY*7WQl`PfLKXU=9`50`kkE zgR~gE^%f%LL;?g&tESC{=WJDUX}oYO3D=u(*# zVHv}uB$y7SBzCVRg-pw|(xD?TFkyzLT?)qtip(R^iA9M1)BM7JNK}w%g}q9`IIOs9 zoW5FTo%$DYowK$+;*>{By=)u=pMzDNSe-px<*gl^b#EDrhNUK_XqRoGq|}BY(}T0@ zC84Q7beT*pu~xKTNio-2TvnG@-=gL$=63KWncB^kTdwcw0;7`EOpAlrY7l#eufGM_ zW)|~n7IV_c-2~A%x^PP=uPEwb64EvZfKO~%gRdO0c``EFe9;aZc6rG9wql0op&RS4^m*q1cgzC9Wjtk}r zv*K^tyVdYkTexS`P-!v@$C1>}vI$AHBJw5#>fvmQ!zjFKicUdWwF(tT%_Y{zo{t5Q zPa`Q<{dESrzZSNz2*$>Kj19td5U(-}LBNe@Nu_Yp$gZw|!Meai6qk)qyWsa{LF`mS z2~*8Sdjg_&Q&4yhCQPd;+H2f4np(&*)dIz!zrP;_YaAHMEU2fBUnhcd^xz}oyPb8i ztHth*xodXU$Tj#&X&7~ox*tD|;NiZcx)2%>EqmJFtcGu+t|uhhv40ZQ7mqT1+=0f!Bnd8@bK?#I7@vqml- zRgaK#zgC}G$VnWPny(KeBLnS882O+K`5kr9x9xr*7JK2n7h<-sa^{M(O2Umhb}TOL z@S18jQ~_&fA(ox;*46P^s_5OWUff?Ng;K)#cHsw4l+-<6|333&oV5g;R;3HHD4aBu6&1 zQ~jD8>(BJ*14>_54`3SYRi$MUVy(DKmX=Nd!wIM|MAx&iT=nQ=*WGt_{p{|psj04w zh5pNA{LZ_&rtXYSPIXb4>*M=SBDZpuAT-NyZXpRh}K%i7qwAMPE^W=Hq(=M9bf`Pc8h`|IDh z=N_$HrDb6d737W5o}}nv^y0>1vXi@a@1EIEKvsQyYWMEzW;V3ttk*{fLSh|vgf)#U$6-hPU`wjgS2H+R z<720Y=myqt;mB)Q>!BRv!ZFlm9`&J>ywIDpKF0z`!C5F$mjEFI49D-QPc?K}^#)fX z>#et|AF{?1t_G#GNX`LyQ zf+9#X)aE#6EFs}dxJg6AlJ_OUmzILdQTJ&`w)50ZKEWS4zK8@EWZ*=d>yC{Pe-ntAMC*q^dXymEUg|<$YvfU}q&js7q#`$>MVN+)xusZxVU+;XN zeSqf5`gKQ!9CyZ^Ha_N5s1Nyx&Rib{Y5)|xtU1xhc7-_r)Bz0M*a$Y(Y0(N{){?S` zZilG?_RRrow6kZ}fIKcssKOxy-PUTW*-{PrmK&t9|H{d9gtAG*0nH!|h;p8km22Qs zSlJ~fM0J>AFcn>$i}(=kp=3L#5LND-4IGlclTdf zGvr9h$@3?AgIlTxkH$TdS4<~1+@B{Cm&DU?kALF2Oh_JhxGUVzg~hM5PFk$W8KeOn z`U2EB8;gH?c^O>P1(0NZ3s$esUT^^^Y^B#e6DLp5m_QN&?0&lS*Z#%Af^TMTTif25 zzTSC_10>(&^Ep1cgH>kNB?`f$D=-@?Xb9Nax_zWNID z_-=-QyuPf4Gu=BNB`~ePzDf6sw3vdK7C%C z<0jVc=k;eh{x+;5f_r-^n@w#W-jeCfY}vSioP;%@M|}al`WN^sEVie& z73H3Bzb!3yV@o|#$bCifjQ8jkP}hcVG+b(4z)8A`u49rXX#?0G5&7OaF~{G7>Q;8Nk6=2sIiY7Mug7 zUhXUfeo0#=V})1YCWoV1bOwjd4@!DaN7$UrsdFNJy&&}VEAiCSw%gla)NC0IitZYJ zFiL1pSO%L~`r6o-6m01av!TY#4Z}^{7L&ftSKr>yUfXFmo9b&!`pHMOU1OU4{I>S= zHG>Z8(fuP66N9d?4h}$FXsT`RYoVty+`kz3Zg0|c!QxNRjvc@T^uQ-P1dS195GY;{ zwt=tXej|qwYaG;dtb6!^82fyTXd6NM22}`%YGM~f#{``S8t%d%=w>tND`n*0vr59l z>c0;1ceNKxPTV;qNU6*BUY_Dl>Ysk{la|FvZ;v}-nGEA~a?%}%TX67ahG@te1%dAq zQ{i*zO)M50``nKH{0~z*_On#VKVayUtt_B-yL$C__d)Lty>z|bpBktK8suvqy$ai9 zFIs9td|!btjeOq7V#?hT*tZMz4=ktz3WtTH@CJ*zb_4>t_NI75DjcZ}lF1vycS2j< zaQFr+R|N6BNmHus2y{lziAMhyKAjWo{O{{NeGCU-s@&25wqusEvz^$rz}><#OLGnv zd|J{Fz}mC=ROzSuSHj`0y2-9q;daa)bJ0H8$+MsggTr_V5#X1XUB`K1(K_0+vHB5& z5+jy&F1Y%+=Z0MQd4E3a{T%56YfrT3*_R4^Ry<)7uL2wCKqw)O#HAW@dXkQY)1p*EH1$%Am=4q7&)uzE^5+^g z-OKAf-K|~n`&w~d(!N{L7_+hR6>Pv==v!>ggfR7E@HZI=Mg-f55%jp|T6jiip^%!; z&%`goR|B7xg|!J3G_+I(aT-=O%(n!V3|cm&1gr~E0)QN25Pw2{Q>U!>W9euTsCl1f zd=kzG2`m+D9zP5&$!I#}Z)}Vy>Fm(8w_iJyHJOabfvtf=!YOVYNC~3Q+r8n)0_b;z z&2Wb6^=Yff-@>Haq1W88O+W zvJ<9&d3bcg5%@>RK}xy3R&;lOQ@M2R0q&{ zWy`C>&peY>^|%N-PQPxFmg#4Oza{NhR(na$0w>Z7-}aH?$InmAp874{rwTUvdh84r z=hu@sO^}i&jy;AZMbtt_@sm&N?n;0VUHgjxAPi?B+-;hd6d?qOAn;9CU8E>^!C~#t z?S&bQ(z{Emaj#VCrfOsco4&v0kOu!Sl7AA8YmHbjxJqHQo-4Rdf{ zbY2bytS#_&71~Z7d_oQJ1qBNR`FDXDqE#~gLJKNEiV_(6d~wCfgY&gRSX5z%m-1_? z&L--H{QY5b`8j^pKiOY@j$u={-#?@iIlYB{$l%C-(;vog_4|kPHF)U@n+^CG`sb^{ zK|dfMw|l2R7w)@V=%4f-6zB{3Al#1c@Ig4}o%S9S@~3LY^5a$Wm7I(01DBL-FS`mJ z(wqJ>+XuV6wn!HF`gMgxvwGv3!pX&6F>cj*Wvl5w)+?)*A%sD$k=XefX6Ai~Y_rl4 z?b8vw#ewJT5}9l_^xzb_YOM#UW2p!I)UoKuseET&*nh8P@CAs7-mqXES96~BS$$pm ztUjrohW<7bZ5y;c^l~KH1(-#JZMC@3YVlwY(P7O-lzgO!0uml^&L8#!blox)jNpRVn3RWwioo-|3BIu~abLwT4A-~eDe zf^VCz_=x!gn=7Cb1oj}S5_c&2>4mElkfdGJ5C!mJFm@9eUXX|WkV`E=QUp%KJfQCZ zDRt?BQxjlioS4dFCNtwtj^jDzAKU7d(tox^l7>F+m$q*EN<8HETO2iQRzcq|+7{U2 zKDW(aR>W4vCXY!9r7kc9Bf;SwWI{=%&$Gw;LvgpuG<;R8Cz0sMP?$+39I@Hj+HAJS zV5r}qPn~g!;TCcqQ~7hW%Z>OnEM6cw~u`7 z5l3~VdY^NmdT({ciAVJ-XE^rG`0zO9v{1r@unFrJg|*uVnk<00hk`FOx`P}U43K*a zIGneDwMvzJyli*bzOvhBtk9y?!0)+JB9IihnRtGQ*xCoYgDbFbBL4}zxfmxU;j1vs z5mOCW4#|G0<l9?H6<@r_>bD!! zL$EM7gZHkUL^!NurkL2TCJdHM=1PMi&Q@yZrAkwqK_EO6$2C>Xii&ndg3dHm3klX^ z5&a&emK3j~x8|<#BMxUisw~TcWhDnQ#Y`rD;8%RQdP&*B@n03ItDTs{5GuMfUEj`;a!8-YmX0P&` z8=4zc>MFJ$sgrjI?C!g^&$LNL{&f0NyR8-3Z=ajKXP`3X_Z-<4360H-EQXAo?!Z(m zX;r!{0yyJ#&qN2&br{EP*k!*6yDa&d8{wmbdkb~|p17Xk6v4Awd|!^iD)AUu2)N}s zB~kiWXlPAaEcdtkX&~^=t!|6O-C7d}up?pRz1DxWwpeRhBxUtZM3yavm2~S?YqNDI zoK|!ltwYup%LAEp`TGK5< zFbBdu2w!s$zUECl(x6PUnFR=VgxN|_G8!WQ5pFCkg$TvNzJ>U?AG3zWj^*ru6Q%eT zGdx8yqRR``1W4b?TLgQ&t$>>gZKu-Tm-`7w>=Dupgr-4 zo%vI>dKKbXJg66Dt74?O;19NUBc2FDgpCpm{~!ayHW^otPlCo7^(=7Mhq^{O{=L;V z+PAs4ce8n7>A?dpjWu@ZTCK-!K}7h@4`3`iYPTbbLcob9+kMt{oHsI^ZMo$~tuNJe zGT6 zN3e;|o>9)LdiTX##q3 zr?sXTXreCwc`^j~c1UkEeF1iW7SVh_KXv1TUcb2&$1pnz?II353GprSW!IGLE&E*A zUDQT+8H{nll7p-t!-DKasD4phSAIvUJ8`c{TMf%85@aP{_rSIlgJ1*6719m*0G7Ga zFAUS%>4$_rTEskETwcx&Z5vI#Aj}PEMm2WzS$mEN>w0b|8n~=}((RtA|Kr%GJ!Kt@ z{c-)ILC9WY(5siPzo@+au7H29vU4T>~8M0Ub)lS+uSv1wc~`Q(=L}g?K9bLw_%=6=+4Lb>wpyP!>njV+%);_ zkU|D4N=oQwMc8>lYQkJdSTG*RxTF{{LnI@twZosD2V=v-^TQ+2 z9```)gY)h>LpW^k*08>?!BaawFZm2hNq@qpJAXO1>j!`0@%+gTcI6%!-ad@Ky;)II z%u>7mfoV^-?Zatrq$X#wcluT`W^)#x%uW-rXOEqZ~r09 z!hQH!Sp)Ji{sOB(fxm)eRf^lilB7e{k8+C9r7$&TEKW=o$h{2<9!5D{*^^izL6nDd z&WJC_9Op0K)bRkVyd>P8U44t4Y{|A{4rDS1vg(g1mbMvdjRjm4k8UWFc#X6StufB4 zjIwnoqrjd)ub8|rI3WoN9)&v5Il=tr@ zEVzIp@?~4IC$dYK<;;o9eyzeFw$9>cGgTQfPC@6ZB(}YB9;^=Uty8vjy2vR*|0#CH z#$HEoWlk8zp23c0R-hfMAiEzs2|ipgs`KZaG$6lWN3k)$r~lDUF%O!H{f{UlBd!&+ zRpX(f$Hcp}@4fe4?YqLCs{eK?GpM;+S%rFm`tP?gGwb3nwC}BPH+jg@j76IwQV3lE zj4JM25sPN14v=`Ro6z(Y1n<8eIt~wqS&2qGQIjf~4}I$p`!Y$p8TAdOWR^UUSf0_8 z$s==f+I|*?%(oZycH^!Q-wEXKAdr_0?Lq>U;BKElIiOq)h4>{&B?vMjvb`o^3Efq_ zqAaRbhch%B5ydO!FAq*^Q4H*T@zl#A!rf%?o!v}bp3Y8ZGcCQM$lf<7TPA{+&tD;m zkzvT@s#TODI1S&uwTHct!HLt$sFVTth;Ha1h;?`a%Z#RMx7eKCvZh1SOO`T2%%K4k|TBjR4^_=iOmh6_z=PNi$ zuQD~K#8xTc1C20JvLL48(zY1gLx?fDy_wf7gX3E&U(XcYAf?uGY>(2`hFCKs<~d03 z)?=zqt0&7xN5;OnwjF8b9}F3Vm9*t`0l6 zm)cL1;p?w#L}{R`!KN!r0>=Mh$@Q1q>P*ML;mF?#Erip{0t#9zV+m!dk-@_~e_ML* zC^8a}v0#_w8oolRsTK@JS(s$#oLrHj+dvjNdL>ja(zNVF+Bo4zMy)rAf>ks>YBE~| z(e(Bef@B}Ug0h@Rnm1WQ)7+1N8eBA+K4TIsn-)!G!TW8!$&RGE3@kqMY=AH0o9NTE ze0{`=O^747%#nWy|FQP*SKynFq%**A$Ou3KD;TeIoHK+09*r6k|0T5`wlB#mF=Us7 zD^{PGqtZXkei~YR^){;Y5_z{DGmBJZ)J(9NP3W>>o!oYl{@dTQ`b~yqa(aW00W=9R zoeFtu(F}tKje)1iE)`ZwdDqDcK=3mn(_Q0TC#7C^jK?C&F+K62GZ*JW-wl9So^`#z zPR?1^>Hd5T*FLqLBfM~ROzV=XE7-5dCXg>d<3P_Bpx%oT#<@bYk=>oB*4MBX4#Xi6 z)*eeR*5S1QNAVpxl75Yb1lhd0@D?3Rwg-s5E)t8$cmZdc%b_b+z=a>=7Iq*4kDz6E zj_Tb(w<};UJFP+;{N?bN>tl%!-njgRYO~8A=mdR$IEmbD^64W}y+xen(N~MoAMJLU4VCzae=I_mXmo>fcLo@ON$BRplgA?$2n@#| zPy+x0qR$|<;=eG-!~WeK0QLn?pMuNN>T$C#s|UGfWsh77Z$-QnSg&%8GD_DS#hVxAc4acV<}i+m z*CWPPL_1JUWNhq!3X?%0k5}YTA38uP08&8Gi~jW$<^^RldZ*Ru)U)?W>+EhUw53tK zjP22T>iuoE7rN-phLD^Oa)qnX2@(#(%N4cbb5ZBMy^-pPsnd zW!N^b&-oaG7R-J65HNz;Xn-o}6ps1BK3~}XJhxMGlGh~}uj(F)p95S?aigHDgI2#NZK=(@MA>bsi0YE(> z4T(U03G7`Q`x3XsT~a{qP85j_f`!&`mT27|dUE=KHotr7i*5CHI>x&l=kLxe29nEj zZS5A`4kaRtWFv~2)0&xoAu3bc0^X3!)TjY};p8XllXI$n&%|)X9Eou6$25eeh@wPR zw5B_?7qpLXwqF2NiLkr{>1{o_6@UX7+Z?8$_V`I*Rn3Ke@e4@w5Y|h9yt#pQR_~!H zr^P&%UcVRlE4AcT9{qEjS+5 zU@E$CZL3M9aY6lKxzx{BOx$615RY(})z zxq64i&~3U`!<^uL>dMyoVXW-JBCe2_Lpk_62HwX1q%Pp-^#6~ycY$xSyzj;RypknF zR%BV0^h&PbGW>MBnJus-;iw>_4nLpgwkcG!& zQHI$Q*Ta)_;S(ln4v;#W+X}-M;&Y;g8Q5{j6;6g-k^*;0D%3S|$}g6X-j{rVx%cM| z#3kQgd7Q298yIavZHw!Y_>9lz3JaAAbfrgOz%(hp&L)%oFLv&lo7;7_{6FK&Ri%6k zpT_FuK8GK`uhYRju?PpDA70|&LGc=H+#{q2&kjO9IGZ9+!h!lKArB9kK9N>I*kp9; z)Ro4hu~Iba^}D|6(>TrlV2K&rMKQ~(CYMg|eN}i9!2q592B)F;^b_VH=M832-6j9* z&vxBpD%R{YIM@v_gCQnAA{uYi>5DZt-hPHvLo}|pDs_no1ChzLTt=LQ<%s!4HFI+> zU~N)-MR84MOqmtCWUpMc~@7psV2lYmi<+7%k%V)~u{);WWslMX7c;`V^P&PQsW>{c+ zd?p~@eAbIfyR|Y5G$T_A)*{%s-#`G#jH&zz3UtFCUAg?I7!d+Q}FC0)42Vp6zI8m~U*% zd^Xb##fiU(II$upvhP;8LjKhrfWndVV+is`V(CRQm&P1(`m^FKX?Pk=s>{}isyeS-AaCd4f6LCiu6pB(J-d+>TiLD7K)~5M$>N{w@FnF7JI_~+j8u+P3UBYY z=eCaDbhHZb#_lHM(40QqcT-jU_T=Aff2nGuYGkAu6M`Yv;g_%}YKh4!#=SK#>EMH+ ze+2XZE)}R*Bas{Mts0&v93k+(WBVo=VGxs9!(TR+d)6mznT!rcTxORU12)iA87w#W zMWWNm!`diw&PyX2VorvsQ9Z zF?QT*1opRZ6tGFS+{i~o9CgB@ga@?S&C>p=s{QlyLu!b}hu5tWQcqE>**C!*qi=l4YGYU&Xr ztO*f?BKN)L>feHu0Jp#ef3J&(yvQX?ySkP3PhjJy?ga2PcodD$=8D(g9b&(c?@zMl zK~M{UH<=q7%`*}(nPzFm($Hw$B$@4kjS8b+J($aqWdM96%gm{#&Gw|-WWoK7n*0XV7Oou@^FV(F>?{m47Nv_37ssn*XN96Wq&H5-j!663rr@MfwXtn{Zk^+iQ` z{?sD|z0apN$j|Y(2)2LWP=8Ck&8$|#IGhJU+hDPy07*Un0^g=l-LfBfd;@xYQg7Hz zUlzT7w?U6B6JK=B78JO^=k-tNbGC|mVKRQ95=3p_g$ajvrg_GDfsBoa8=yQy<3O?# z4YMCA9Q1Moy8oE4QI4=5fmwDGr;aqgk)b%trMP2{gR|) zJ4oUNs<7M}lG42&ZyIQnB%|44G&hXnB*6Zi$sr~k#>cqvuj zhUn%}pk9of?wh1`2($oMUtpfzNnqAwBoVO}q}0Jlp{+>oD4}u^h}sDaicE|2-rgW? zP#FjRLbz-*-ynNgK`^Xr_o>x_T* z+4!I9Ji0lduM*O&>ux6B2ML9*(h^8ex@63u$RAB|tH^JVhJXg#WbFp~eXuk#i-Cu&4pnp$o zYn?C#X0J2#jjdx*dj)&mHoa$0L+bP*8-8OwrI4(5nh#lPoR&k@>h+GwN{6sUK4UhA zheLWxw0fKHeUl^gR+5;hg@V(NQ-GWizTWv^ifRT;tP5eECYq68j)QX*Vee=YZOc_c zjAP4aBS75O1A*!7=!5VJy{oEFYZ9EG#_~6t`bPUq&3PEozZiBYu80VS_Vnq>Wi-pNe3O2qb zRtkw@no>7CX|t395#+9;sJ?KQ=F0|kL!IWPXWo2f>(*y(uBoe5TkLK2BenhNbJ4P6 zcKHK?;maD$mfg16j2JgX7bjaA)lV;%7H-s27iyjHsg$6^M0sEKtO zt$x?ga8HegZ5$dNmQM!;DQPk(bz81rdm{s--SQd!6#IhZipY>`%KDwb;pG*&dA>1m zQ+Ck0<|nl>y*UjIoy?fxup!A@TlqqkU4rdXEJnQpHAre5i{J>FNhI`2X%g9>{1&N+*8=VgtAl|P~U5|Dtcbx8J8?>@Z-%W&sAVa!! zUam$Nvq&lQNT3isX`xv^?iDzkkflN?FN^q#?x~WqGPk(6I~8xN$6vy^{?@T2#EUm=#yPADZQ!dETeIGk*e?&6=1-)Q-4F$XpqI7TY7~290Rr+VsVdpfbUZEucZ7okk%^(g!9{Yhg%bf_=~V3z7Ds zMRb(TxDpR|`~gXWH;PS0gujf$t<}W$B2>!F(oGF7HO$C+g)^SCS}BszLAK)j9o>r1Rh{x&LN{Jq4)hn1%Cn~}G$o-^`Y z$$ykEt)SXsAI{;lh#?@m3G=WvuNao0Fchc`29KsLtFZ?@?ce=6qyR6y4yr_6_?@rw z{w1!nG@f07vpgLpFCox@a*LprK(VB-gzyTlk|4Z`*_ADzK%_hu?uY6G4#)}2f-^jD zKFC$XGZ;F|t&Gl)pG`wm8mX@J`hv?4Zvz4e*M}_t12V>!8G-bW|}!t^(YpQ{+c5me)@s}M_;@qv~cHK%Ixze zE6XaO2%gl5V4pC+T|@srd!IsaT>QvsKkwELAoXrA0wnn|Y4ToH-mTSPL19ei39VD5Qi6lkl@BD}F)`0O?2E(-FC2oA zu_&e1u^J2rEH1hxuzRR)IfVL_HpEg2P%V?39B>tIW+E6Kc#PzKECn8O$Le8EnBGaU zou+r-lYYJU7p%EB5UhNHrtmDQNXkDIo|WW(RI6V`Q&p|#0xbw?XG;N7^{KQU+YS;U zFf~QOp~ToztF*&{c^oXvyu3Nv5;B(DK3k$NgCTd3JDC)2FqjUszUdpew_d~=c{vTf z~Q$AY z!NwZ&?(H4AZ_vL8PT|-!+vx}NRBUVid>0mnQl7M6M^w)K*fGas;HeN}kWbUY_5s{yq8WyqW3x*$%l9>~farNHo&Ai#!y zxIa{zPI1~HbuPlas_RkTgd%Zu$`QfO0_;mAaIl5IPc`2c^x92YLC_V8^}#-WxqJQG zoM`bcc{)a<#z1_JyGQ3o-Kr~%`cgx&k&4Y0S=Jbn{(w6GNUA@nJkKsD&&v_iXMu*r zj8^O$&^L>rZ+;E>CfUf!Id_>d;-t|kS}la)T4vTt#$8v;D5YE2OR=Ydc{)O6THt++ zieY3t-43r46m#hnZSvgRW4MT zrhqXPA?7Nr5;7v^gnq8~{FktBWPUb@x&@rt$vlVodw~ikg$3^dKQ`&yB>cRh6OvsU zBI}CG!B~{?rYOx3$_M#K$qRy^f~_gqv=}-iR0o_U3{6p^ct@<3-v$njjF}yMgQm3x zhv~0R92=hmEZJ-qb!N7KBkXP%jC|I7vb_Ifd2Qr!c6{V3O=D*8AREkwOl6bf$4-3L zWVf3J`^wDBy!FUd{Fk3kglfx9n&KzTiPl=ko2gY$j1f_?fn5-oNQh8~z>C@;@cO`w zPzg67&*6kuQ4zKf_fS%SfmcAKkc-do->kAZ)(zgi3(ekmdq6R`Snr(hA>IOZ4gOZ4 z&)qC{8qMWSug~XomYa<%q1Zl1(SkTw*EyLKUO_^=Q5e4jk|9|jh7ugarih(~hLZc9 zD;Z*oieLmXQ9YWL$eoiA*z>bEpqj))z&halx+(QPAqHp5R%#6jmseg&& zwezbCl=9*xrJRlMGM{-*0W0BkMw$njQ7P<;ga{b!lAq_!O1>W}vQS;ITJD+7i~|>i zJn?+@Anc?TXEUA8@K|KTu?icMX61!^S=cdtyC|JpXx^6<$cCmN04g4OTwEZhcMdiI zY9-LOD|qo4?n0%#B7nT?eaOqUV}yKI37u5eqZD(ABh1oxe2UofQVqy-g^DUH$aoHxl^abNjD3F0;K8ra}jBy8&ne1vSRt*PY zQh8WnaukL!v14BU)_b=4y=Tp0639IXHOdUUrN}%k!^CJaDUA56aM| zz9NOnc?=%>GVr79f**yfJ9xDCw4`(}cxvI}suN~)FEJ; zZCztsZ+(4Db$wGm)+6aJx8gdFK*xYJ1@|I0b$~h*;3*u@8os5`h9;cUw10l<=i4o#>;2FC^(8wbV)YYYa95ia|_CAu!{&0I&NQi)I`q4lZ4 zP+7Tx`eyfCb+1ZXLA@|1|Kzq0NIiYC8_z*KwXmAoK7g#bdhCSVgm-~7-rUttqD_Sw zk0L6KXj@iXR-SHDkA)<sw?81 z@z@}{I2enYla!DL^N@Hfdx~X;TsYX{ZB9)!dwXgF>rZ>U{)FG_ zIlVrhKa#!`N)OoeVztM=u;BMN?VmN(81}w0s8$cYve)1)wxA9Mk0mWC!ec7pEX(K| zIv%BKD&5_FECLTQ7`UcSO^@Gu<1_5f z!ke4D*KcX|%BOXjH9D(UVznBP(V^29>ew~nmg~LEo2TpK#yh0(!~2GY_8lIV?hxKZ zi6QBRt%;$b#Oo5u4pBc<1v_viuETzq@#t}LW~q%s3M^^bLTle*uf3+m&hD{QSHDjh zzwmN0>9VK3Bvw1^!XB|kKClSP6Roq%bt&(PGU#}f5N@+Bv!JMMYXr$qVZlx&X2ps& zq5z67ZDn@xJSv)#{HU3j&eQk9dvjUA0BT8p5wj@G@yAmU=!EC=e(4`J&hN(!IAZu? z@E9nrrPe5@l(LV(OvU*^>IgPKPsiMR9}eMAQ&a+k!A^7rEbRfa)FuC=5d4316U}b*As})I0Vpy;ryi z&#;Q-KUnLo4Gyez)pcDxyv|c&9B6UZ8V8!a-In?$daBuL=@vBUf>sI{wCq9+HIHW& zPcBfpo$@S=Q%;B0VxKO<2;nXe&VYO1wV1B%SShMG(b>)RaU*t~9qXN(1Kb{vXsi!t z&%MF9$=*fEcYxz&o>z{fYpV;^e)OlR)2bmJ>ult#;W(kllHjpMAQ&_AiL=*HLNH1_ z(MrPF(0D^|W@u5m6sRu_{Nn64thxE|6L&^DsRg73AB)|;)#u-O?^d7p4Hm!NxsRPZ zIiWA!+SN*BHL;fSJw##^~nM72KH#|{&ue)^>g}S-az0-S^2|)(gCp2 zE$zWu?1$S3jPEBK4xuoZQ}{JC!gNZH)CDErTlnw5Ru;p{Tuaeqpop?wi7v)s{^$G)2*Rm4 zqD^K?>S58$Koy zs}-yxR5zj6G2aYnSyJqo>jt;iH{JBiO<&o%L;eb8RyTWTSiZ1sddJPrY>lt0$1}rp zoZlY3s}Ii%@3E}8#Nv@Z)D7S+ z2FyjMvt&_Yhrp>G4b#i~CXl)nrpnCO0hb?!R|`-GS}KT0W4*Hh{wwe4rBu-i3J@2s z!=wR{6hGxRl=6l7uizf!_n{ox$BI6Hi~#{lQbR0QJUpivX;6f^&6jYxr#TX7?(qWk zSwcU(^!HMkQl~1L?|$g+=1R|wdv^3x`fk2whlf9dy)zTXA^wVLp1DRo42}i!#=w`O zScPP?hpj}YQllv(SD=Csiy*3wpgG(abBY29=&gPNl| ze;$8G!T0fl2A0MFCIRqa3_7=>gYhZoQYNA`%xw-h7YHK~As3udi$;TZSsLflZ|Fhh zxcmWDOOIh}jQ_>-%3rC@Ec}4Ismw$64O%Pooyjp z^lqU_aZffhB=2o$>t!=wG8W~INmSSFF`={AmGd|(Xsb;KYebuaO6@Q zTiLJW?eTtbU=sSZtg&SsThbQq7~qjgzj8T`M(BJBJ_OdLVrpW;7t&Y|s9c!EghfKt zXJrl{w1$hA=I+n^ML_-zk7Sn{QC4$Spe3yVrtMUtnWmZI$ciXw|AM_5) z6A5rqF^!+$t;_F2UjvEBk4Vo7djQnRNXzb-o*(cY{Qfyg#mwuNq`VXLwCQtl!0T`C z1&+89x#^D~AAqnDpaxTrxM01qi9s?lkg|-&j zBcQ~KmuDR3hi&u4m=q3E$re=)1(Jc|?8iAHcX27Q(tI6}TAcrfsc>QTU8#dfKmH*A zOTXYzyp&9>A0riahQjh8bDP6&^M0=;8QMp6(+NIMRAe>tUszqlDZDv?@G$6f_%|QB z)$bdaS904=#;*=fE$$QndMOH&|9i>ZgU$mjVHK%9%vXm})2P~nqz~Lf^@NDs!+jJEQzUb{nrFXZPC0@ zc+Q{^O+p_v^BnNo$$5AShAd^qIiuz8Sb@^iM+NV_`nJS6UX=|>TRA=_;zF@<%t{T{ z=YaOczOJww;jp483S|`4ppGZ1cQP2%#7RJ;Lqvo`SYNj=j8HU^`=?+ZONJGgCy7+C?S5s5;ygZSVC%_$w`H1y-0_$@Xxk{l-kU~Mc z!qCsC3NH*H#C5>E2Nx}Xs1XRs-{OKo7Mc7MEix?^Q8=8DofZwd5sV~f^JJ&9FCFt# zVYKMpARYj>KE_2k(zDumSnCZ_Bu6;1B&sU^DhaN$+56E~EXp+M198>aIS#nbkJmbB zPA|W*O3TYMf@7SRK9>bluRC~94EC0Z4)HU= ztMqw@ekAC&ly`cs?QBh6&f?FNCVlNz_vb`&IU8)=@?eWAj6P7!NG0E%ux*uydh}xU zXuK$AfJ=uX^vH@}V9d%N(Q}2-ScxJ8VRuWzRVon+o1e#F1O(Hn@vflX?WJ$88@Uj9 z-?HJ+hX+Oy8>7Q_*(W8pUvRd&1fSm{>J7dbC683_3l59m7cCMhkO)5dF)LUcg3luM zSNfTU+ioHAv3isieL_YtHnk64o%~eo!S0s?>04xnYd~hfF!7 zg&*7bEPp%Ocj;~{4zD}*$A5gR>uLbBE-?zJw-g5!JNPjj`f1m&srl>8rmNW{$AhY2 zggtfGUuxjbdLP1 zsjjf?=r<1UY3%J76u>GyjMKBNCv|Rq{+l4u1#IO;qsvlR?sW?Ba5CoKIW#ypwA0@{ zD98t7D4FDkxEwNUh75(!j0lw;<@~6k*6jsXPEE0pCT9?EC?Rhw@7Zt-h>ll24eyQe zY_g+cdt2M%=ZxwxwR%irIA_rC2W+ULzPcbyX4lU8MZPQyVwVNXqNVdub0s|8}8&CRk;)5q>3fHJ^}GDNc+@cx?Acv%Hb8;*x$9&Q(Ynh)Pe|&iO@$nlTeDDT#{J|UW*W{hhGUby(otmI?= zKq4r@%rEX74_EGjD3FWz3Q3MkT)7zcn=p3;%XKNRK*0@C5+;3$Jn1oh1Qc)%WdntGb9JZ$;5RX~-jI;F69H)!vZI!^Sqckhl{QJaEF_f!dp}_| z$i=>-O*gfZI*2A0(RN;aQfd*e^%GOqA|2o$AjG`R?`IP{?L({elY4zAgp+goSXT4< zw61jgAhx}1P`b#=E)#!t(BwA^%cdBMO_O@ zAiY|XCPu9vIH?QW+ICB{g&^O>@ino5Bl-xhnIkx$>iQ$ ziVy8CaW-5DU-lKm*W-b92IB%e9ux!x_XGJil;Ol#OrBK{%+*$|HlegMg$4P0%wOcN9C+_|A=1RO6xpR`fZXf)kd4y-rM&Kl!?Ft|al!*jkM z4)4*P?%RArkCp@eRsQHuf3(?P_P5GAS_39Sb2Qp54NoN>?HbuRlIV(G7HBtH9pyLe zyRoWUlDaGLpu#7%2QG_uCGd`}N0U>-Quq4sxF2Uv(mx(X(0It+-_<)K4h4f(+BaU- z)i3@bYLEF4gE*jSfHhdmJx`6mH+DgroMt-AICRhn1q>b@ed#j>bxE}JT+PsS;=B)2(=QRkK%yEMTkG6 z&;ni9<`fXFu4@*clH$YMaa1f&h&mRDM(|d!L7|O0H#wNx8Wg(2Cm{z~j(LI4Y8LPm z(MaLaBB|J5;=|K`KEUNlAPI6xS9j6>7GEe4Z? zkqIbPq!wyprN2=dCajTKVL)xb6V2hUUVm+$MyDQA9oHMw1OD{`8Ux6^&2X-1@Vn2Y z{cjEjs?V3QA0;$)`Ns~2{9{p@0CSG%ABtqo+dlg*u%^w5iovV@RVm7>m{TX(}`t zi*SMFUQ-F00{@jk5Hus}FzetpQ)^I!3bhN@fbUQfT&q>9jiqKaWLcxr>on$KjY?;T zv0k-aGg7lotv9Z#9u~?-WI;%p4T74P;%bm*m324ibV9UIw@EGN)#HMpZnt32tGhea zLDUMHEy>~)hKKa}hYS_+cR>OQTJA->5z?r2tUw10!A%-LBUtoCNTq|c7BgPeR9919 zEhaTSfK~*x(P+X5G)bV0MBy_*V>FT^^wHr27}5boP^-~DybnXuYD7VKL1QxGSx|aX zV!XyopVxKcvU&qPt3iXr6sbq=HT@&F4)2=5sK;e7*g}0TbaMYkAFA$Bt>wBA*;Pn< zL_D<>b1lxQ1k8VUdcgHaMluRq!3DvwZ{WP8uoht~K#o}#14gAv;Gbc)>jhi2p{k~) z>QMcbEkhL*4ROJ2tEs7*sXJ6BG|Fc}`s!M^Yy^F6?W46b_0`ojvkK_@kfD#jS>Fi&|g7yV!s( zZrM^F54G4ZL^U;EtwR@in#tML$vVB-R;{m{seQbty~pzizc+Ln)Ax&RW5LL9=P>?{Fr<{QNkf%Qt*_!ePT%WzYm1HF|Ho@*nnF{x zdbOq6P%~5aRX$hDq1JeM{v_HaT84+$4J)}R!{|2rlTQLGcoBAvQf-)G8MM=qLx=t$ znn2ngXhqp;z?la;>zkv2K&&ZV7Z}_pUznMnIj5=}+;?PpXriY#66x)k7@9t^Z%{sa z{`|R5d41^ZL+S#I18$&zSC|bX8PwIF{FbA$(}C|-1Jl#2v2l9(r|bd(kfhfFqXV6mu+*>%@k<7f9=6UATm%7@q_ ze{rw;wQH{N-7`iuPYBrgyMUdi>H-zSi0)DGjfylDx4^Ih}MsV7=>f8~GoA@&54id!GoX$)nl_bM71 z%WQ)Flzf~u%CFiO*qi`r$l2P(u<=Nj$T8iheL}A4! z4<7SIVjf{X{gfX`Chr>?TA+9n>FxoTSP0d+`BD@hE+3iJ-Z0RADuf*MhizB5)Q!(p+9)A+2#lx}l&pf$Rov z*q3DQcw#XR4NA=J~6uOxKT3P{lbltC%&jN46SP~ z>Y2E6tp9`A$lUmo+$P~ zP(H(_gMq@J5uAox9JNy?XA)m-jSuY}PxST}P5VcrBQ33a=F}&4PoH}6Ev98US7u0BcW~>Oj1BVcSwd6~;%E9vs0A{O0cYxjlpT9=mrC=|<}|)pdmS56qmL znmT!i21fW07|ppkoF;?&j^Rl0wZ}T9zVWx;m;wlgCrN7xu0_l&iCWx*@uhmsJhGj2 z$F5vm*=LwaMB!N|;u_G5!@z(=fRbPegh^6R7jG#brOnDOmiVW~n5$e}KH|7SetyJI zV=O;ZX?P*cIG3iAQ-1lBU&5UHvqygA=&u-YsO3KxHX6$xtu!DdumIMOBDV=UhKb8f zds!SSAqem>C|tD}>0t1rD?O0Ig>PfoE@Zci>@&iNQTDd)Nv|U#s$Kr$N`rw5EB}kq ztIex*PWbl7Qw^-GO8)tyZ`&cTT|QBT9zy1dCh$C!)!6ItjYpF5t3i2^`T{>#Hnv9o zK0>_l=#R4P0W?Z!4m>xBpQ{+^)sEE#HUMDGDo5%!*##V=MEH#mmx~DB54D$-x3!g* zm0ceQT#s*(As5G0{22wyeO8~wo3SL0oBA;85m@fYP@DtH91#F))TH<6re z1rw^0H8dP}He_spRRSmv7@3%bPmHL_0v zTgpz0NVs0nLJ>Oo4KPo!zt?+3xNEzT^9)X4RN%sSLpe_QeS&TB@9tG0dPF?~0`Yh2 z*~NbqDp3jL0%iP5)XCqce{-m2^g5tjG#69_e}Fykdf*1ip>1?xCg4txVB`s|ST2oPDickH&YDfx1G4v>hhjzCY9{21F@ zd0C)iZ1C1?QmB12x&Hbo`St6n085uReTQ{hni$kp+X^mEK9b2g?$F`RPv ztDCWN5x-D>!5_kp`GtaO3pN#OMLq1>QU8rlS|IY!qIfRlI)OS1x;Jc<2!i}!z%ya8 zp^_ANFyVh)4@EL!CEpi(pIYE(yvTUa;`1Ut;l?|xAzaFe&^U7B$}cCx{Z-qk>@|<#343gxXGzPez3G0kx7!Q)F;Bh&J()7X0eJJGIt^)d zh`!eVpNH6~;vcq=A6U^w;TguV=3ZkeD$jG{redyUCO9=k#zeTi)Zli84W3eeOKf2L zJ>5v(K6>wiv-foeFK1U7EsYx8L5IOqYq2n=J+@P4s}EFp!fqW(RyoJ( z&Bbc7*xr-qYYDPe-(`O8A0WOOrA|~)76n&O3Z7D;Kft$}4!8V&lft`H+Yzh)Y=Ei> z<-UcN=jr65Zvwd~KsQ=93O~RnT)z-b2m*t*UCC@8$rvOBCl6t%J;y{A|C5t&v zHJ5VouaJDHv`a2Rb?gd|gYeJDLC`@1YQR3+4OXM!f(fh=+K&~*OYSXey-NbM+;Hl^St=fN zQt-_Y4e@UW)hMnSZ)k`&-ex&ssj9O{FEdl`M7G@GPE z_GQm~8Sl4M-w$4IIhLnjAdVG+YdQPpi}*K=u~>lU&6m*c_Ao4YYyuy94o|q0+t^`4whFqVw7+2mt3va_=kNu zU8`#uf?DMQz}EchlsuudzZUI( zi2au8Ai^rvy1W?#=dxx|Hw(UCxQRo>2%^VSR}H34CTN;3Z~3WKoz6Gx*O=81P5AKC z;32u1`=!8;$iL*dC?d5!$*(yIvG_P-)*0nI6Pf`*Gk4}Y-^u5w$ zs#oae08$FpZ-|6%xg{LAWtZ1GJdAJQWg7BXka8+`HNZ0c{0+B6hWQ(YG59nd89B6& zgL3D|D+lZ0lJX$r=lyS*P z2S%r#AL4F4xcazU9B1L-Xj`hSgQ|SgstuldUb_lTyO2{T0bDa3{-Nd1ll+B*5s86@ zyIn&XUC~}kxa7MgW($~&h5mO&e#0I$u60@*fGasHPL=g}Y5=uRG{O&b!|kbf?D))n--yLwFYUzy-y`lp)DJujwa z|Lpu}<{0h#oT}+>mBubq$%7rgIdkT;j@r|le(AJ)Xrx0J;X2#%(Am2ASq8-O8tg;R zZLPw^Sum$g%%a}W?DOn4c|TU$z_|hyGk^4&a11i8g^VO2NKTc^bI>Hzf#?DTJn%}H zd<^ztYd;%Iw6^^GQJgEtI&7`2RW+r4+aG=L#b`f!vZlo%|0F&iPu)(hfEWc|TI{2l z`U)a6d8NJ;kdMFuQp!8rmrJQpJlQ{qDh#VW8Y3nMB^4x=QDzEPJR>|7-mH_qsoN6v zhub|q_JYsT9+Hp5y8D|W%>An?qCJIRLK(IU|E2X?KHGjMzK6*1ca> zTvcWA#0@5~+&w<-E*DJ(zu8<>(=-!rFg6%XDyK!yOtq~ZlthXXpW{sb!w$D7x*b%W z4J_28$7SMFgb6NSsA7ve2r4ng2NRJVI3*Eo_RpVBJq;BI>3(7;yO}dUUV(82v@xt^3sS!*a`~1<=0In!t>z-JHGPz?O^T<%danM0}&N!!-~63 zE*x^DlJG>%3jaGz2H3#7m9*S_v1=Th&u11*EGnY6r#nK7S8(>wT3Ff-!XJG?PmqAI zw7vY%%Jhmjz3~5%odXX7-avl;O#8&`)d0Lv6wHE{?aS|rD&`g`UIHuy?#}!n5tSxfCm5t<&jLrWpJDlH1GT0OP+t+1u6B z8OK*=^E#w=D}LJuzLZDG`pL+y%mG?I-8{#0_4|Y_K}Cc;@BQ@spjnf0cT&nSQ(E7Bw^HLS4WXzGFIo;zcp7{OQzxAORox3z9+cvwI7 z&~S42p)q~#A={jZP7Ln5HIeXiU7Y}r$y%M=X|N_t`l!Lou4bQK_95l->^P5V@E@}9 zS$4y4m($-+Z8aH6M6=UhZZ!st4rdn`*HmZkm#}qev6~kIL(MZCO4Hi`a_4BVM7;2& z(OH8)8t!F@3*dFEuaFI(GNp>*lYUgwya2eCG^+0oBm>?0Q8KdOze3$?`AnK~MGJF# z4%y8!+3vMVIk)@z>aX;q!;tKSFIoh>Vzn3+UB|mlt`20|kIQK38DpS+Ngc^1Ho}!c1?Fhcns=mPuBH>Hrs7YTvubY^e#g zZa1~nCL1xND7H}3A{!bTlWfP)qoG!$>&tJ-qf|y<d z)X;kKT!U0UH+GbA`P?|G4a+V*H?=cC(HSI#ePd*!k6^2c65C^0rL@bI<90|$)P(w<9uM&_E7 zJB#|InAJ_Vrl%13*7S>7fdet!fNDs&JljbXVh8RUf`j~xq5FPFt$nq<^Q+s~H?nQb z&!?uXv!_OXw0G~1Mo-P2%WFBG=SGPq;-mx-0x_R}r6Y}K4kLF6NM{T&wt0kFlrbj4 z&`5jGH9wzJb@N{-Gk7EB2?COw2VkAfN&XO_tmOP+{yWcuE(mpQv3Iq>r*SuY8lM5O zXt)4M2qG&;?ty7Zj&huf91;p0k$&^gJi$p}T;h;~i54afe<*B}&86WWQ>3XQEKC`_ z05^g$!G=0nI6na+RZdY=OZ$<2FY3Ol)y+LY!6KT?iLsvT_ipbUPngX}oq(NWEDfR{ zMZX|~$2`q$cj}^9G>bm5#X*721%)O^*xpuFQx902x~d^mqZ17EmI}Mk++qLHQ1AAh zp6$IuU$S>t4E72j>GY6ZSe|joCg{4d&Qdd52O219Jg2 ztUmg!a1vueh!4yJDs@BAC)^U6(4GsB{{S)MCYoF*>{`BF9o=zlGrtzl!rj|tLE2f(UI>sUVE1^US zo=!ckR3)QzklSOa?oR=QoG4E*A5c>0=Gp%!(S4EP5B_u(Af{a~byofF}8p)=<_p3x!f%&a{|okNmhi zUW_r#>YAU>F6py4=`Y8U)(X{H!!OWoN?=xncoE0>) z;q6qK!Gce>TnOYh_+?kQ6%VAF6RyulAWy@8H;hx7d(N{rMcBg|6^ijbF2s9yoANGP zA~#ns4;stkUXSf2+_J z?eFMK#N}sRG<{b=3H&mCNRYY@37Zfan_Hv;06S_MUqr#OVxDeKVyuT$7sBK%E~=U`T-Y5WqwMMAOK|%eq*`!f zXXv$+c|KL}8P%ZhJy?i(MBZsN!9(XpwHr8^z2RmS)Ryuh0xqkH_Vz9N?~~u!RXMeH z_U@Vczff+q$j_Z+fxrH~(6N8Ny!$40Tpm9=I)t)gt*k;A>X3hCI4_jjL}n7Q>qvo>nkq zbkI}Mbe`faKcB&}sS>ouzfBAs&o5nF${~*&o`G7yJ zpD?(T4qsXnIoY|w6KUMyx!k(M?q$a>KIg;XGCXlvAs&K zTML$(EAn@$J7bk7|E1Px*P4vxHO3Zok=gC7lgO#z(Zr7|Y%Y7-*=w#c`HU#CLGf}seu(m!GMBH@R$_I;N8=8w zx6@~`TC1&Alf~hvcQ{nFcn98^{~7sWYb~lb8odrrB;s*+jgA0LP;zp*?RQsr%F8_! zOJuGD`Zk0+uv}(1eaWg*Y&tFF$!bAb0L>qJT#Um}R+Baoh(pr&4Vv`{R%tFTuU%^_ zUsXyu9@vWa(hr^d?0aHFQ&zIWY+qYs-oYt%aRO)ROBDjC;zN)dbjzVqWuyg8U;^P!@v=qS+g82mJ;;b6Nv&1ZHZQ6CT`RjMpT54MXsMlmaKY0kQQ0 zMs=PGNf8zcF(4iS7^a1yP#e#@%FI_1ji=j%QJhg3Q1Z|5$v_5m$foL)|6E_bM*efF zm0f`c>Q^m{m>m8o5GLJ*J{x5X8 zzc7@^_dC`qkJyef`98lV;A*y@5~zwH*~zLdv6w`7guK=b%}$TAm1?HlL^naWg@Ww2 zU{a9}AhTB!La>6*=nbgrbrsE#v~>Z7?iI>8DY4`h0M^2q##?5crr8Hs)(bInDIuUB zSf_wR>y`h+T2*Od*IS%URxJOC#R;on_NWzALDMi-<(G^yW#k}DBV&aI#ajwg8gold zOYSPnU1>Wy3)>fXgzK$U3(EUu({Eq$7tW|Zz@BtH;@>9hcna2H|t1Ti^& zm?s@`^Deu932zN}Q1+Yk_ukYiupRy5itU-Ms94e1xDH;9-!wMD+kyW{#Em|LX$s}F zqq}GO{_Q;y(fjV7949W~xbOQuD}vpBY#pxCmP z3n%|{rn|oW#rpb5HoZhpc~ir3@%koaeT$2}S%t+nUr?;qI*i|!F@97d7Kl*9p13C{ zCN2Xqu)mQf_PjX%%O1U^5s(B}FJtoXMd3rF)t^}m{Gv;fF)8{95sFRVaHh8H2XRkK!76nH2pAB1m8LN zX}Fc(t@-tggf`lNz1Rip1=VCAiws^3n$F682X{K(@Ce07&RJ&l#$XbN+K4dNKiY40 zR$AV+R##rJ8!CqUJct7>z$e`uM>WsJc$MXltr|a2z{XxxWtXc7alU76ez$TCX8fw3 zs$CD29RZLj<`ypWkM?1eSbm9mhlyE4qvWID+@S&^v+NtI7DGHVhwTuRPROQLa)NcR zKLK9Os)DFo2J#_H9`sFx{T@~^IPSYx4e=ZDrSn3&K5~?Cx zH6(mpagWZ!Bai>GB-_9Z^^W^ag1X&s1O2>{@243qK^?5m0MDaQ7+PxT;O;_6^%!hD zPJ-=0n;Fj{(Nof?6tck|&Uc%iDbAn8a=WJIiqXeMaj%a#ZEssDoo3t^OXUpi$Zlaq znbz`=s#c5MZZn}Ktki$VQdMm`WUh`k#=9Hh)#GIq6=iYAMz*oSb4ZweFT9_`K%jWA zEx@#;T|2M~Gv!`2A{nNNv>!ij_NCXC;>5mHp6H5PmFVhET3FwP_U_mqOR^i6Pbl`? zbRSUw7HHPgQ+rw)s%;i~ZM}sUyqlWR9WY~x@yQmao+xqGwV2Z%!jjH#k8@9M47Zk? zR{R^TQh?%+z(Jf>BKS^3#3^ZvwJs3TxJ#m?m4Lb+b8$o>!*8_Z8T66~0RMbly zbV?eXh6(N_#~s}h`-IeGx*L7Y`{25jP$1Pe2iR$9>IHVK-{<|-He8TBFmhQ5V&mtXL=_teYp z-#eyHUzcMg#T?5ZD~-N zX-}0fuJYh~0up=g_|BnyNADX%=$8ZG0FaB%Nv-Se<8w0Cu7&$Xux^*P?Lo?tXK8Ym zA^@}$?FuPy=4>??Rwtd(;Y{2`hgpk3-*VV<)f3>Pcr#Q0@h9ug;D_C2uS1cWpD6cM0AxX#Jq4I^8Gon3 zQ0awLg8w{u9tddY0cmJ!Cg~Ka9>HPxR)7?qOf+Cua^T4U{7(`mltI^^GjvBrd#>#6 z9=vQc(#@Z+n&5ZJh6fN%r8SjwIII1h^5)7tmCfayA%AUosmWw1b2o-N(ocH)&T6^j z$}1n4EPCL9qDjn+oR8KGZ1m@`S3yyUDRRQPf!U$a64DAX5{3#A<8{yP(6RL_*E6KP z4)?4N7^%v(_(Q} zZm^c$tnPJ2++p{G+`@J z`)DVmS1AJl2Ou=47y~w#Hn1jjy;R?rY^sC8mZX{r0NBs%5#D}jex96slEpI6eR#m4 zYiZG029htm^pgBj;Ql3??u6Z%0O#L4)&%J-KyP@U8lxpH10jUDREk6;R6h%Krm0Ot zzz*{u86HibJ}++wgnEe3%v0G+cplGC2G1nc%@D`@D&A7EEx0kEao6zs4KtHYNjjOd zIMcm_fC0KKHr0?)qC#?2c5RB66jzCf;Mgq^;L7qFfMtSfZ=EFFHY~|+>*AZ%HPzQw zSB0vL6^ADmlh9cg)5X(s^Nwnb0Ify_fVE^v9Vj?BsSw-p0#K=g4{v<tiLD}L9WGjA--3( zMI>QB<6sI)szHp{k}h5RcDl8kFNN9~AjX>2dWFR4XoPHR;!;UtpJ;RT!q5N-ixs=H zXd~FElUYc2Cs%<81Zp&EhprUxzCb_&Xl7JrE&@9Tm`QXRhbE*``!xDMK<(41Lwb`= zYor&<#bKSwXE?986jvF%^EEz~MtiwVZ9@Dq3O){XRBcdO)R(KZwLX_x*N+1AFzKT@ zjR3r=O?`QhP}k!1w$#yh=dc1a+LYtoMEWrU9*>vKD2XA+%1u#*$qtVUZm<;<9o=aH zWhLd+i%3Nz6jn`zs#8-nEpz*^e4%@wR3yR{>{!Q4!>ea1jQiQKw$Zk>(XHdr=y>&1 z)xAQT?Pn9!A$-a_H@0JN|1mZsH6S{_yS`C=vu$kaSTu?sZLh{#2M*KR$lw~xT+R^e zsMRDbB>NdkGNgp4)+L1lIfc_Dq_wvQM+U}7*uzzs=PxXp-x_GZLCkO z>;BuEz}U~_LtJRmH*$N63nv75&p%#o$3Vj}av|uyVxP%W+{nQzEqq==~*D(G+ z65m+m)X94(_D1y)YJ^9SU$JZWYWb&Kkx0DKmGO$QJ3)Sw!}P4>r{o4?R~0C$EpuhC zBf`Iia_rC>c<~h^15;r|Btb=pe;9s1xVm5!E4A9l`KD1tP)$+E1E5@4R5tLqeP!k4 zjs~~K?=QEx+}1Mx+~Pv^aqjh@xA|I9=;AR=&E^{KcAu-%BpIwzRs*)H1<7cgDm6$Z zOSNx1Ocpcy;jezh_o$2=y%xR)$`-qPd0z-gh6+k~Jt8C)oXSd%&>Pg_Aa^qF1@BY3 zclq7HQ49#J{D#5l^4cpa#d??1U?_L^8pI02?EJicwda?13I?-0VK6&Aw|JcyD>~(IXUMxO+Q3^y99GcoY}RFW zVb-{2^Pg`vu1V^R2DLsMxh|qJ`hEJf^GiqEtzWzO>=yl6;~KxuUj>+Rsi-wW! zr1P@xgP+1pESihsb=bu9DijWM3obiibtk6zO7J2@a>NAZ!ml!vojo987%zu zWDyv=*CvblhIAwOk5B26x@(ukrWYw|2xfuui?~Kns{=ky;`P?!kp-h&D1zTM5(5~^ zT^L}X42ck@Ik=ONXbrB=)AFz6U$JuDGvULC?dR3?y6n zLZQAE-0#fTlVwivtsabi4Z<#j3G*UiM@JH^v9xVW&VTTUf>hmxOkt$-#BA7G0U;;H zIY^oLgDRl&lD#O9J2raIKL)xoYnkbg$uTj0{KQ}5C{5iNSDS+6LU%$6_n^`ZQU)Xa zbJFzxKMlC+`wqic^TFXUov0s3kox}DCytL#I7|!|V1FKnhb399DG!={flo>O5%8Eu zgZnV1PZr!STom^35#?;erJpcgsPre9gHKLih9oghXqHUiw}e^KH>evvsgrc9pHQO$ zTQ)gBn~+Se$;>`ZwMFZ=cO!G2FAa2-0zgZpFLB)R&}ymI%1(=Q#QhC=!*sS9NNHcQ ze16jJ8E=N#mz1n;|I|Y75aGZAun%x6qKkbij5mxoTg+{ss~_ZNq&n|BDNSS9R@uR| zBmFR~848e))Cech*uw5=u|@1jk{w=x;XljrE!WU}}^k z2kIWxITOaU9ECV;}sO9E)~--9>0v593rBm~kD0K`vX`TdLYe&ifx1cOQk4}l^iyNL_a zzy)I$6nP~W_DBvUkr(q`miIR{zWh?&6R-!`vB!`fSB=P&Qdy%FD=-H7i+qW4usnp| z@`7-b$VxUKDQtp}Czd~bpHNapI6|@q(8A;pJu5;JAd- zu!BMI0Wzl~%`@mGo94uJ~KEl1`zX^#EMchPRaBQ(Bd}J~A6Kb$V}CDhC~Foz8s| z6Z@QJDjN{5JLvSLXd`Gw5W~9R;pZinzRoG>R~u}$i_Ma5b&ZX`gJeoa$s5HVq0=;* zLqRd2xqN$)>&g3GR*hZRg-KqGDMDyYxofd)as!?)=#e=B(no+^`4#*RB4GDRoG{s`+bmoQJ6*pH<0s#F>B~sEqiWDe1yU#^C zwBt`pyDhLHnvb!u6%iaI@#UPfetJH`D{F?~kK&P6MDvs39XoPAD(^q*S3y+*kusTCv9#+((Y(}(heUJYL~?=l9R+@0ReT`DasIUMOa&Yl zWRc|LtyR4-ZN^oNYtv*loMp+uFQ$AZ6n!t+vyut=nyF+-=cXJGBx%>(H7T zyq)%WYt1E%aJ=x?DabEWn=Ge4Trdx-2*IZUd-w&+g@@mFpBh-Hroc1N&^uL~V@6gA_KB4+F}XUCCTKsPk|++mL`XJmwoz0!#xbsDT5kJVtHATJ;g zr3iloSSr9cDb?p_zYHTA_F}S`U1&eaV@82bq-_^JUqBDZPm)ZhNv_X5u5{R>NgTiV z0%jOHvIB{^X2Tkw-oQyfN-D%BjKmT;Dy->M3Rf&@(N9l3=$+Q9%{6+ck#jQ#xE?AW zOr}3JtM${J4^B?&El~DQU&c;r6cV)J_gAUz;o%uv11>bmAd7~WPi7=+aHrPq!mljR2B%}?6paD zl?&AD72I{T;>``^DOWnW3_d6@wqX1D)cXe%3rHj+EM9=kVgD)5dn?{Rq{+zpo>Tj2 z16gb@S&=-l_|$;?$Rkx@7Oa@D&@~f@6|)wA2=d!!^EUix#vi8}SPN16LfA`|v}AIn z^h%Q=?~2wL%?Anc9{gln&sqV^i>1A4qa7Lnd`?Jjx)2t#$R9R(pMBQ5@q?AgeeA81 z`!{qPKHR<$bQkhA1l?laz3}x0c0=R~OXOu;@$A6dfU6f8%St)UW=ORmep9Yb8Tc&_ zZV9QGBsG|5gtis1Awe`}Z?6VoRYlMYp_ExE+%IxvH6S45q+uo^vPk~fd&_jgrVX{z zwSEg^{^BSGXaSMW9<{Y}H~t%jLL4HUIWL~S10eUMU_ z5fCE3y<-X{9JRVV-lo#aK=GQCpBQA)Bk6^OJK|5?amQ<$UkizO^7&7mQe36j01LgH z`-i&Ca5RWaX*17wK#+cbP@{pijW9zXG21(&po?rgh`8x;NiRp%si<$rr8es$qqmLD z%+z%@u+?6R(i7_}x}2F|kJUCcgM{=ACm*dVueFCl&ao1YA0wAWHNkDqM)f$Sscvbk`2$T}1ti3y|#*y{3c zD{RA!C;^%f`NwHwA16jKUlRcgY@zxP!zU@R6ii*!#M@)83a0;nWDmL@9CIjqcM*_I;~(Lr$+PLtV)3VOh6qNoeu*}&#P zLS2iC!QYACIshP&?<0t-tT#k@IO3g+oX9lv{&Mc)uaW32w(3MDdn>b#TpOyZAU@s| zsqklk7zp~k+-w&)E zwO*sV^x_6Y_GQmxEElh6MIEDHiK^H_H(!HmjAH(wl71whl1{lnA!ACuh40r-jfo) zGyF*Cb0hLI03`a4&?8}KwpqJTt8H)6*6yjV-=j4sn>w_3r2=Jhp#ww06fE?9M_n zBPWu^4T;?g98)V7dqp@c^^`B~1dbfQOL-nYiRAe0rWEr4P>}sj*wbtj*>7_N6b}JF z=HO)O9bI<7tJrHd$RYR%Z~o{bIkKsyfgNn>mfxF}fE!VE5ucbT}spVeqt z_|-rLu!!MjqhB&_BqS?6!YPNqu5ILQ%JIF1Z9G(wNb*3G2tjY13aGCVm{oLQoNLnOJAN%;NXc};Rm)djo<2qYiUoKW;z zFo=R)6M%C_BoH~v;azRM+eDO#ygdRqCGd%ICtg$RgU`1TI~fq1sE#AeNX!eY1DMIG zA>43?AqUU-2|%SJ{-G>Y1%hFq^sq;s(6*Y`Saa8DdwbV#*K50WHFw3k?ep_(_U>1E z6umvKOoX;d8=o-LV2<>kmb!md7dh0oOsxYz7S<*#Bk#JZ6D z0)6j0k&(00EkC;x`Tcw7!wi>-g7gDz-3?`Ey^BCKj!mXgHRpk3pj}w*Z z0Jmcoh8Kr=0s(~*cQO9C03a~|_~rn|2j$WE;dsYJgcNVWns?aDdCw&8$3ho{g|-_E z2@q@2cRikY6wQ7wu7o3A+1A3OrnWA-ylMU?fOSIT2)dE#YEEPE5}mDhe4GJrDv_86 z&{Nwr+RM5$RZ zbwlQXwzjV3iFB``M?R%#H8pj2>}hT7w#)B*tb1U#EzvaJeLZUi;8eQr%q#3}Lrs^x z$8g6hc%v;kS7YG0cm+JMwH$xM$^F=bCsqD{#6-cxf2Op{KtUp~cZv20|mPJ0xd!fX%*Cf}g~uBsd2ToI=VKd(pF#cLQJiz8mXek zOW60$!}|peNX74Rl{;t}-s}zI+Mag-fhHI+HoB4#vVM0ycwPW`_zs7!;U- zS0BCdV?R;A+eP>Q_%sjnUS@dYhmcX+X1ALvM+_SEcxALkc}S%iYOqfyGzR=hnpk$Q z@YkN{X8Y!-p|@@GaBcIU`j-0UR`#(31{t&{YF{#R*iUluN;&pM!YLxDt{>}xrj1K+ zVdIxS=Fa%$?(^NXlF?!_G%D=7!2a?{SLect&RQ2mV*^(LFUwT2ChC#>NZS4q5)2?RF$OpbC%namYRZ@yfi5PfQLG z9=s}`sz&>?zr6>!p&8r}@)gxa@S{veg`>+wd_rt~K06uQs=)8Z>|k8l^hkXFruaP7 zbugIXQT7NeTJCw^%Xh)o%=V%EMBr1(ycpOG5_^@1~ z&kgS1A4#MM)(~wUBa2mLU$$*XbqKbCw>L*Z5wAtuOBrN&e>vi60efY;9}@A0nT^up zs6W6C)GG#$B}orpdHECE!ZO1r!5uE~V)}qL>{)~W%7;&!h@arKoInP?cuah|czm;M zGQ_Szhyl(U5XW5cILbjip4pftVN}t4<|sRzlxNUxVP0gvS59q#Uiwwsa^|RFP}U}s z%$ZEqbHeEc;4{PgUZa?Y-cR%r|B$;+m=OeEXoS|Hm?zTSc!P*!Pa}OC#3S{N#dw*F zv)d(!x|(RlFSUz`I@GA58Caf2rN@a`XqJ3~Svj51LXo9H4Feq%4~SNFD8^4fst+O) zK0*EvQKtpfy?B4H=h=E(W(0dz+o~(DIc4)_*c-{D zRgPMd$^Gvq5KI34!JADK*Ilan7zurtudEQ-|uPv+ZD6q8bw z{G-^mr{X-IT~;O-B1TY&_LKimd`m%lDxtC?qFzX1rS(OC00BA7Xeh66A>oAQmqJM4 z_zR>KvHkK}NKvr2#pf|!c45ZMCsNI~?!R?Z>Im)W=!tFJ^5mBKzW#}R{Ol?&IwIfO z7ahHIbo|lK9rgIkmRMi=hBo>XM2rG$4cXIsY1$CFLM2K}K;Iz+63!oZc+5d(-Wt9X zbxqxwXUCXQ#~!72d;4Oc);%Hl_dx4F+odGl*WJ{Hf+ZnSPgiWqmM43A>+cB7i@w0- zrNcaU3Uy<0Xfb}sN7#V_D5%2$GLd9bY^Bc|;}IOfqj8~33eXD$m5}0FZI~6diLjI~%nz|m2?>f6;&Jyne1y17zbEAXq)+xZ*;<5A$T#vA4z^~~ zCYn?5nPzi^&5-2mT>H+J3Wdg9g60YHyb(C9WTF6A57LDN$SZ(NAzpAOQEsQ8{4$?> zBruX5VUvX7C%+vSL6|}{qq*wg4cLa2Ge)2zn zH`{?2Pct+Z%EW_PHVd(cc}^x@5lo2H6-0_BRj=>hx*$AAh<@XS13nRzv*9ae!?XCg zGAuRHBd+Nk!(A8qX7@XSwt?I4{H*Dk;Yz*d?`~Wlkkdf?$B*=l{3mC_ncwBUg)g#G z&jcd+T~qFRZVP&L?wwLz`z^Eg;>=4_D_{WD1J2iM%m?M25teybPC9~RgM!$>Mg$+= zWTandLey&`<^j{13`vMAJ(!f=WX?i0M>;2^=&^-tY&G_{?A}jx;dtHxwL$7UwL!W% z-J8O{7r#ekqsh=LeTc_!W`p#Ed+{6W3X48XpGoU2X#m&o3;tF*Rg48y9EZzu*{jhr zvSH(AD%{(Eg)&=kpga0II3~TQ?q1K%ks6j zUEpm{AxCP3_)5>tY-?n+GD4QXX_jvpH_cOk_F%>$?AA5Z6cL4xOwk?=efkrPXF1}I zUqijd5Gcq+9mA^vI|L3&K;l%{a8g?!XwZM+ZbI?s0%T`IhsV=nga$f4FFhgunzhKk z2Ifbz{1*L^ef;eEa(~UzcP_qr964p!S<{L5lJ}RMfL(yMQNZD_B5#_In}z<29g%i! zgwRK*9ImL0iz>ZHls5T;6kr}^CXphIoN1V{df|pfTsiz(coeB~K!E0%)f7v}D>jnp zfIYtP&_?{+v9UikJ$`2_b|=c{&M%};T39}%QnpB%j><~4q#mV|X>FHU*{oExN>Zx| z7wRsp9)B89ODcn|LnF0H%63V*w+6L2nwzCRZ~U8I{LRK#IKZ*#p}n9O@!xEeCza|! z6|`O-Ae@yxwbmf1BWe{w+#2x*wNkIv_$n%VYK>mJuQ6ymSuoKhd9%1iFlrd{v})A4 za3QaCc_tuuA6$c>2kGUepuamnhLw`^a)+oboLZpT!o->J=~zD)Mf{4PqwPxcSks}y zrb;|e8#IgAqpL&~`lv=-Sqbj6D32gH3sp;Mh&4qOi38ynXO%o@MQ^Pt@I%srL12SM zt>;_;sD^mopjG>n3I^_J45~r3CWo`i@-kEt?Ou=yv71y;Dl9y5u(|jHlmtvDCkSN& zNEXUVDh4z9@u6VAvBMR)I6gCeQLxvx!!>yE_$=y9em8mMXq^4rVr;M5+2pr0Cd|0H zrO8(Z01UL1#o2ZfGJN9}0Ng z7T-GCy1^KBwYYQ&nT&@MwWf~RUG?2H^;j;jIo5=Rn|kUS;%Sl-aaP|Vy@^#_0~^@@ zq#XxOYo^9cqT&o{El`s-9!HXWfwKm_HWHYzvA`3IaJwMC4k&I(>kXF1!A6UrQl4#S zs?%c&t*&j7e`uq(_#fFV`Y&|`jq*Ro6L%T<;_IsKO2mV%;Zgtk_5RUenI2sEm*NU} zHYhmLhy5y8Jw0vcg@TBENz^UB1v;=6(nb@saDfR>Y#~E1MRtiJNe0>}3Y7*Nh^s*O zF)mQg(&z_-Bdn*zZmVsD0_r~61$l~NI)7E-&C*Ya0`Ms6PWlz?ne-!`NoETp4*zDv z;Zw{B#R=ss0V-sI23^(Mtd$jv0zn(<58}V#I;i^U%2fd+k35Nw=5o~2Uil56gbe+X z^&yc+g@X8DQ#QFt-#r~>Hd)1k(Iib zhKhi2KN{iaFy~0f{E3=uZ|(vtQjV530;~$1)Coe4qP_01v#3$F3%O2yryZ*sgPQyJ7fnlbzleE%}q`U2Y0Lz}VF>L$4hX~zXwya+asyd6PyMT%0~ zAOwh352-7H$a$J{Yt*>qnDoTSI}#IV0iVHc()6eesD^Wbvkb_z+}Z(JMMe4E~)o9?#_c+bR<9Nx;v=)l@eXTp2eb$`|M^rgwKg>L-H!Nj0;^Szt3gNcJ* zc`YYRPXdPb*?Wnu&-Is#Hlas^2m)^Y(0+h1M z4x7%`u`iZGgIy2b0!|eK6a}i`7lJ&9*fI)q(H!)+4PsOb>iM>kY=#A}nZ&0c)dc^R;to!P#-7`lFk6ffy zZn(SsM){~nZO!JsDVkqbDqqjMt6hEROZ$3kPN%JB-%FQjSC0)(>#ICqz(ub5y4H)l z38&joH5DCm!}^NUSd{RkC-D7#oL4UBmcUahu1=tErICxckY-)Dm2alHH)i}+lhlXe8)C=7^yKG+bn=F-p~pI8GO)B=fAo4~3+*^HJ$*ME z-MaN?Q_?r|X!r$gn3T3%cXV4&hrfng*$g(Y2B_YG2j96@G%dl&Uz*M|A^)Z z67ZQ=fRu=4b`eev9JA&CGA`lrhOruk0%(@VfDxtx)4y4%;D*1l;`Wh4og?fe_n=mm zVM>&cH3?gz+ z24&OYBB?H#e6bP9czYA;nOO z2&O4s_=@^phCHPYfEPm;dSUE;6zH)SI1@%{n%-81SSw5j%1pou{Mfi(3Y!r3u__*^ ztgEX$Mh{Sr-Pl6b#jlgJJw4IbwQl(^&4aHFxE`6>_VnA9$`pMpRcRUU_OEXzG-9V^ zfQ_kk#v6yc-fLqT=Q=k|smRu0_1N^jMRZcW$vNpnT^Zry1)-$vo<%A|RMC;MX*L4d zEOv{@Iy{GB@(EQj-QP21`?C!FX;b9t^)n-Y-82}BsJ}MbD>2{7z6{&XcVW>KwXpaM zhiu3~KM{ibos z-fIdm z6&fORd4Zo{B#DKpEI~Hom@&bKgnbJG4A44WVTc&1!hVf|hffEy;;HD)&&oHA+#DU~ z)_?Wkryl;QKF}}!$-OS<>X{CBVs$h9TXh{)dd;e$H%Fs8FB#g_97~RjBx8;n4ur=; zk|FpPL16BTZ8bN9#={crBl&UVazu5N4O9SCnI9y0q}?+pjtD?+82v3cVoqcDrujg= zVV&?}WKLv!AQGX`L z1px%bP_8gy*0W1F05P8ZKE6M)S$=2B%*|V#ZJKL4)q(_Os-uM}c#6tU_ZIoBt+6{c zHr>4--;1!9=0sCBp-?Q*4rgP98Q}nkw>`U^;}4x`n`?Sz>#fsUR5_EYx z3wJkdyd$=iHE-$ZThva@zpF;sngev>axZ4d-j}1@5NI*85GVJ|7#NJP~qDsh_PDq+) zwhB9GzRVPvK4^9k9uT&9L4Nz#Gh+y1R+jBVK7CkmDc2t$lzZI&9MbX2DnpQ^bjTy!YJuN6#OL zHx>B(vf_u>Ckbo13Gviam;Q^?Iu{RGLNw*ZpiWv5eLjW+|;FGu}es~Ol=5<89Z zUpjN6C-6s&uF*3*gWLjO%u6oYaHrE$S)RGH^C#PVL+oi$0&AhtSX%|42(8|7Ft{h6 z!s3nV0TyXB81?bE-e|A_LQ=n=TVvK490qGV-Z{~zsxvm5>sdJbQwUMC_8h>I-lwR7 z^%go%&dVYq-&(j(20S%kVFp`!IaH)4nz}=%f_8F#KCx@pjly#RgZhzX9KiY) z=*9VG%_|ItmZWuWRf|ly3toDMV~^0I#_S12G0ctyQQz%&`&V zAhr%bkk7K;HdZ~)ffiB=KHK-j+D@gg)NHvVh{OP)98=*%|uf}x)MzNDYjRwI03=;yinrho#G!#ZI8CEJUk z1k~l1Qz59!FMnr=hBN<_`ztn1*UrX0T^!WZ>ch4TV-t`@vV+tBzt}PTI=p`QutI5 z85&PmDiLDhV|BPqDuM69Espm+m6k*TsVIPEO+a7qeex5l`LIk)p(6Ku#|IRZo02kkTMq_In(+&bO z!Z8rbkOY<2B^u>_;qS%cd(a;O18CUOzbgVpBqGPVTVv^Xchfwaob&Td-LP=>^dPEq z*Zcz3$1wV^HQNUsA+yZ*lmoM;ivSH!sem8xOL1NPt^CK~=bs;DH?y1O`lE?^8_p(2 z^FMnY(uP>1hb;eN%`;_iVt&8S^{2K2kl1kv3z;$D6op z!MqfzOMv~Qr{CkL(0SH-EP4u2!iBn`!n5X*GzaZfG_P-yx3h=V0l)_rNiu~b8GFFf zAFoiU@fY@hdQD~nP;bn7!J~qFQV$7*s4CFoQoue(_81DmBh|>P$VJPd^cyTmRCN=O zRMLbXDY>9qTTv0ml#jbPIcfIuz=Hg*ATu41Ud|HbOR|g9Ye3GA6RgJ5zrO8~MCRX! zAh$J;0=e7M0g_>GPW}q#q!#vR8`eBLp%gz3$yc0lyZ}1#j_8am#GPU<6kZ)CDY>N3 zVn#!;M61j8hZJcqc>{*9H zklat5V_xsr_S~njlMye)*V8G=`Mh7MNX8)1y#Yl;C`eYCOe|w>w zV%tS|D}f?AF!QB$Oo{$v;L{6^>h>{E+v<5W}huKJAO|r?0e|(+r#=! z)G|%5C_gTyLYK42&=m1mQMRSbtcXhg2?@%|t=m~5gz=x2Hi*a{0x-sLT4*vzH`z6V zBlG*`M@HsHH;idt8D7NLMb}XBDl$tQ>4a7tiKw+{o(oGdl8C*lraTrA6-EY?QQ9(Z)$JpZK;g`R6lO3$?P#VHP%S;X1lC3Nzukm zk1Jqy6s9x*gEnAn{ryM!DXxU+wSsDG<(oLY=|>MmlhgCtYtPK&+KB<(TU;gcT$5mMQ2 zuvfm0y>i+9L)M`Fx8(ZIOI=A%MAK2D-DYBU8}0UgFk50ZE@aettyV9#DVO=uvsh4T zswuF*(+L((Y>23BMRlpeWy9b<--`{vih+xShgP;g8wISo&Dc#*r|^^IP2Nrn6XI?} zK?Lkq9AdjcQn6o0TGd4M2X=bQ)| ztyZ^HYE(4Wxi-kN>zt#sM|y@ozwRrICsLYcH@NB$DjIOYs+pMR@4Up{=k=}wKIQt$ zeoEh~^T4JCKIUXqEj$4vD`J8|*Tc4mDnaDgg#U75;*WQ23_ZHz{hz^+`3|u5Z_q|#pJ(SfInRyhfe4Ex z+(4&+)C;^i7s})DNFVx)%E9@>>jp`aQr(Wzhy9%%-$LDXaUnk&8AyI3l4Onk`z~VM zI*Y6%n}4FyA;G?f3!kJ|@Oe7@hfi;>%U?$O&Ku$Nc6x<{i9WJ`Md8sW;Nc zF!19U(o~Lqs>o3V&5MxJ!pH`>gzH~3Wt&JtB}*gKb_qBX)EOu8v$Guxw)7&<2Hg{# zM8!Y;VKZK$lqh`zKE2X!7dq`Kbow`uf&G!>z_!7?8-O}@@yJhSlxEHZr^kh4meSXm zUquFN{F5)@JMw>)d{-^_k!K8h8mv2!jL0`yEF12<=g}$;keaZ^~XUE$#?NdG66(cN6?c{qQGMBe$)E=K*IUu5%70KeCal%?`5x_=OY@ zN1_&xxT-1Ti{Ps$2ryt~PmJsu?dpS*iHs+#zPTlS>eRyUTtmC2zioOtI#=JN>+hI; zZe)H0$iBka)I2BcI<-rR%trg{?S|U+{>Zfa$lz==Wbe?|(^dFbsI#u~Url;HbHSG;^!_(6p z{kpFDxu{AgqUrrwb>d`ZLkqicdOFnKUTbJ?>l&VukIzQ>+w1im?OoB?LB9S*KRK<~ zq%h*F;<0c+@^+b^#v>>a_ILy<^;W=?NN*xjMr5M#4Tc`*Gl)0Ar`WSe_@`EH(X!RE z@^f&B`2N3HTyMd9&jH8?G40q^8t7B{EPqPWyGLWoxA~l=+_F+VMV;8|T z1Xq&JK|wx+cn4(HR@+uF+d4!l&$0uESZ`BEwX*1>;d~OU9C?JFp_RWtt4`SfVPBf(mwHi?8QGO@Jy6jrL>4p(4 zz{n9sb$xA(eLlf%zCqVu(&`&pn$j|YIcW|%fXA>;u>mqAS$x{i6QnwbGZj)xxy|V+ z=Y|yMPE=i>96^csoS0eD|H~P$DtK5?z_0rqoa7QArsd z6Y19pO-l_0`Xzc5z4LdEJ$WxQMEMXq)7%BcPnbJF4@CvoU^D+0Jm>02(1}CZURgB< zrpEp(Y!wvRmD6T}*jEv`;O7!Bxpa`0hJL`WhwUmSKI(i9#Mlda24W}pKLmKCQgKWP zpi%1Mdm9_~K1C04Pb2~@P}-k)uepmII8L^XUGnU%I9Wh&QPW(5pZV#r(dp^YvFSnj z@C45l%*Aj~oL?hkg*KcDTvNqD6O0Wro@~XvU9d2sOe$B%>1;5AVr&ZvIx2f9bxncz zBhgz{>bX&YVWXDe@&epe_pU9SRH&r zrufG&{DykF)a^By-`l&{fk#{W$ELK2o}rfcI4nfB%F*$zd5KjHS9WY^Y8jhm&GU(q zn3ddiRKW5kbQg-hgzpHsq7+bxk;q-FV!4nBq8$OZNa=-xRg^b{BTIao@&!(d921Lp zk}i|?TI>Xniv{TQLNeYdzv)$(*IGVP!<^>KZQ^4$lZ#9Gt~lJqqx>Iiry0p1Z`x|t znpOBFz&76WdP$MP7o)6Ad<O|@(m02si#Alq-QZQ>v0k8-SzvpI-F{34nlB$)9ILKuSu;(~&UcGYhY>jjLB zTJgkwRZ6OSV4m6cWET2%v1SA#XNle@;5VcrAicSlI)KNom-awDa0kA!v40n9+0&ov z%b!pR3!lgOJbNy{26J{#RyY^(_Ccfn)6EG1sQM{q1vfeVU+H}lgyTYosO8dkf4jYy?6E9UaclI<*Aibqo6 z)U~I=r`V(U^(XHYYwl@C-dJ_=&lkcAmG6h%qxPv*ji`qw{MyZsl&LZua-&E{r2HsA zg&+`(Qas2?S5ykLf}tD-StgrrjklzKdo=y~!FVfDv80hiVvk1EHi4k89>o60ldLTB zYb0kl5oPLzdz$zY#| zqsWZa$p-46*GD1{CmMDkJ<{|L7~Oir+mz;XJACuD*hK_(f+BdQ_Ytx?=s z!e`7lk$ch}&#`>Y957n*v=9YA7C4&*h!94wWzny#sv5!trS6r| z0Z3I&38Fs^LY7%fN}Lz6L@i4s)oN{8m~(bM*BoXevA}6 zOCmpiY4xp?F^XFu{%UE$fGIZl{gNpI6DDTOxj8~S$;}l7kB}^CeFY3D5a4miGlfYa ztBr!1JQ2%r0(^kK|5o_>SLfF-uV#Xsh`7)FXDj80^w%N)H?waUp0HsEwe!RmAhuwp z^a1|jbW?YV*PcGk%TK4<5xT|slBWx~(o0dZ0{)Z|v<#0*h|;m{czY}GAX@RbtD{8_ zNc&3}*7OHw?Ju7xE5p29!Tth)8h|0N7Lxu8^;1PLfIUz-g-67=^tSWsh$({LE^F;- zCyO{5wY7+mKle5yzcMgz!bk~NjRAdyr9-WDUSw438z~M7eSn6U?+5KopIwUn<@Tlv z>O)~RvV*Me{QW3esMy0kcaC1rzQz#KOjh6Ax)l|8@*W4AJS*MYM)OXG(B+xGT4v&Y z*grLiBdULLA~Y#opXjn5$LtM}3mlc~T2YXbs+FS_Wx5SYE$wqT`Y6wT3942ych4ql znF)M0aXQuoVrUBEalRe6BSa{M9=b^x>o9B{(ha) zoeQCHIc(eKblXSy>viKh)s`#mx~f%b-HqC6!)om@6o*^y8`C}iqB<_EwbYLHjk#Ik z)`z|aNUS(@5i|yJKZ&l0fO-=H1@p$*E%E`p{e$^(kTifL+_S(T- zxpvp^e(CO460Cl-{U-Uv`EJBD7}OXRSTau2xa8#K9HM|j_|YgtC;$c<^Uz$?L^Ss1 z-=wn0T-#D9L}2jn8pvf3nDX#kP}cd|LpRDlg3v~BSM08`HIf(m7{wG|Uq$C|b9NWv z5m4_wfEc`#m?!JXb|63H8?ZMwkz7aUD6k$QHynoo<)2lNa z1<{F2iJv_|GG&s%jwT=llVX4<5!lR+!M%YI*#01qvMEe`S;W?b4Rr_HFTx z)o2_xwNA$p1Zd3v@ci@g^lIXBfri!GlU8)JNxNv7M7~`0 z+!Ja+=b-RhFSBeauNZhv{umIJAeEgKPHK`T3TxE^O(%3=>&S_+;En-)p3ru3{$<6+ zES5K~zZ}~{m3H+C*4Q?)zwd`(d>B7sU(3YVt>BC<4qtG< zO%yO!@W2RUh#yYynwk%tGZ$h1Vh$%YxSTZlLLZF_MYUKN%KDR@L;^$%QC%;lWrs#n; z0})5q8IY~Wi?5kPurwr0ig<@Kz$3N&6fEw{K}G^`fVvn)!PK$sI>)-%-jL7IH@nUo zw_4-gb+dgIU#NF>okO>MA8UM&VzbB#W~sC(dS*wY(98{U^&MMhds~{CT6$+gJstIP zNHUa0W;5!$YxT?y7+t;mjvm0qyE?TuYdjPms3?qD%2bpk$TOaAyiBF4&j0c zjO_1!9a$7PTs;;1J%my0fv1(K8%22pGlFiqU4dYiFPlWkGeG<%5*%UFcR_q_aWyc& zM>~1}URME$N`WeH;2(r~KcGq~keAuPN=ji%gYym2%zM`#{XOMi-JnrL58OF^w!7Y9COkw(K%Bho;#=Xr`}k=7!n6zFBr>D$z4| zx2h6}UHoqC<98o;N>awhhYe#lj89A~bWb#xT9{!l(K9CB-5%>eBt<+iXc&D0!!q03 zOX;4R@437s3;N%Q8G>oS=L1h>1u!EZph-GJ(Oe@YBHS*RD5%z$ceI@0Dg4u;mP%9G z@itSXWprA8Ze&(De&~SaiUFnGT;s4cUD&g~5$fc+f2XUMGnGr?6-Q(G03DOhsdluh|DViYU7$kHiQcJTwPa4Gr zx_M}%n{eNj=6?y9*t2!;wLJx2n~2A*E-Nzwc+QVsga?_C1WFmAX59d59*EOJCLCeS z5#~JT^&Z4;*Kzz~&G@~s{ouj&j)Mn_b+I+@3uFIed|wFy+l0)=3LCU{W_ATTDa91! zItWxQC5_O=nfaEV_R?$WW|Mw@a<+~V*c~%R{E|yPFqBMax9w01d=J{I0bu|i zv1;7&0|Ur{w!MWJv4AP{?!I0BeHgrhvZZqOs&T4%{3CynB`7`6N5Q4kYEbPawH5 z9@nh4+A(mphSeH)9oS9Ok~Hz6e1t^-P1K&yYFa8oO$5T<)URvNK)O;vqWYX-H}buz zus=0(&m$^TQPhzLqR4fo0$~c+55Z1EFYwD2MN3d80nUKlN2l=}EMr7qC`=W+@FrbT zfd&Zxf(v@=q@JyJZuPikDSCkdFXCps4@LuP@C!AbPQ2&xu+wUv-dtyuJT6i`JT+fn zQ3OFC1fq4%d@MG<=e0K4&)e{5Vh;@VtD;-x>Ie0;Q>~li58DhT{E5BVKiI#ib*ffB zSi4~h*BL9&$5Hh06<7rI@NRh!h4s0zE3p3?0BH-RB8!N3Z9O70vz-ZuzLjUY1TbtE z|B876;7w%Z!E{27352yR>N8VQPvS}5)rBfh9Zl z#5^N(5ky1q)rY~+vGOI!r%-41@0LnX#0x^xFrc8B7oD(9suMo1V`k2)*SO^8tl+)l z_r|H~cuy>qI^Ae;4aEkzrABOMzDaAoS6-J_J~BP6|I)12Hsh;vQM{tc$$lpKwb6!t zxjnpJ6epQcj4M`S@6%x}!q-J!HKzl?DpWxP!OI+H8A0q=^8PBCU6j^O<|M2;Sbfwm z!8orSx5kt{`3viJBH$Q)On_sD)+?26cgoG4hB>J*u3L+!rsvlsAq*mKg_%8nd_=}e zWLqST;jB=VT>;FcHy9$Rj{nAfO+yr!vz1 z=!iDwE`qrW9==I`!k#5Qqp%wr9E~2UBM`7PdK$l}SzW31vPySIUKekY?+_X5gCfl4 zhx7!fDe>k;@k;t{R^w3=oNsD0$#1YxYlF2xe*R&;KodU&i%t6xhIW*9sAZ8%=$=}(ON^L5{FvDe(QJ7%T+Cc^PfMD zBrln3G;0n1w9jVqv4cLF+k@ctX8=o=NId8n8rd9`G_Jbl|As_1`h-&Vf~ZX zm*j;h%Zp;|`dVpZDV}(MF$bPJB~nXxHpBCtAt}0uIg*s+4#lwi0Q&8vIO7Rrnqmle z1q+0eF-HgXwTeZ$G`Ee@_N3rRFZah$^nL{E>WO=_gX4qRyK*4|J{cBjOH-Y>XKy(6 zR#>_$^8&V9=|g8{r;{{o5+MnQ8Xu`^vdHSaq1fADoL>cc2*=1v(ZASVjNC6y@iRdR zB%^@PY=}c|n8ZdXdNh;BAA?|Uve_H&C$cb8{sHYj7aHKYKG*N>0-+bJ*@0~mTFnb_ zBuWr*Q2$YS;;Fsi@$ZM2v6w=lSl9qlz=LC_Db|H-fj_|7zLaMSQC)k|-+?T|xdrKn z;(-@aM)eE$+rT7;djT~S{L)GA>u1ArX=2+my1^I`#<%fn=p7_!I(Dd&1W)*kZ*ExL z3nBquSfJ?4%aFHsI(!TA+YTR_*g+dlfi{xqyGWxUD^#Inmmh6|i$pFq2neQx8j|}+ zDL19|93AQOj=NIy0@)|+b1qLbv-Xg4Z%`Bx zOKCGC1JL9~LC@v(sEzX5kWnZO9p@L{HsaEsIgg4)F1u5qI!!$DE@vpogNx@9uQ+lJuqU#E7%mg(RZO)*G9Yjm}0Z z1#LL)UpKfVG`@wb0c1^`=pWjO)TnIE5PiyREv1QSR1i(eHBOMHx$r3LtXa}s(g$Y~ z0O($ZI$(mrG^g3=Wlt|f1G^SQemsIoqS^-W5o4OSDHkD;Qtmco-`Jo~U`7EY2+9S< z4EVrOG@Z^I0a(QtJl$G6;}IZHI4YnvAQuk-g!oseHLw<%qy_ob2>%iG7IL{|wF5$= zvpE1}gPmE}p>Uj4&^`@8?o8$g?Z9*f_%h$xUFl*Ez2K_ceGK;1W3ZUS7viY`lr4QK zvoTJ>P=Y65xY+@~voHJ~(t;RD2v4r_&>{!VPYm!5iI{@?ovR@Qe0ZeL&?XmHblCd| z@!W}DYCZ*<4L^|tR}eu-fWEAPFnmc~WT$g@hQtaADn&dYjc-ne$&CY#ourvpwA%s( z7LW?%0f(vrs4$c;&5ePynUrJnC4hYA?oLA>MKKyLWR4v35BZT^$|;d&77qrp-Z37i z-Go`^_8?Xd5uFK2!YuG0N7D*t#(9QeF#)Bs0qy4V#Mi)%6%zS}Yso(22c~L-wNT7( z;Z+4*k){h~2>4^6zGa9vtp%HnD+wv2)@4;DHLk`5tyBJ>z53g%pY zIp1**&!fU;u-oP2Na+wT#Vna$K5TKu5sJ#6fLDO`Saa-J>%F19Udrc<5g_&Y{`zjG2n`0O$!_@EV=AOj_LfIA=yUy;ES1kUwjzL z7+Fs2pAqD@X=FtHhH%@nuM6Q_Iz=Cnz9iscqq=!zIE-q0HoGB^zPiS zD_Y~N^wxRS*BZTzHbrN^_a*UHpH*qp?zYwqc4G5NIy|1RFXHjB)3*f9{3bAv3V&(v zv;Ic6s4Z#jl&7L|jz+K967ke|bzU=KHerV-?00(f+g9ovWmTo;io6kqo7~pM&yaW7 z7}-fE-#}pL&>Ex241xJ5L{UN$%8n)kFJcjC29;0f`fxoa_nvmXG47qH~PJ(wPI zmM(UEbPk|zbHhlB9Pa8;$|w7K>Z_+A-Rt-#Pk5>Xt;NQ9ujRl zp^W{AzI)$XTGTVkyfEL>)?_l&8d{WjfONmr0AvmHZEK7 zN<2vnDH)}*MG7B!jy}r9_!sim3#9Nsml01CTb=7uuATs8{S6~8{H%3JAP0o&Ktc)m zlxPBySsM`mXT4XEn|1k~5k5ZkhsRnwz*7`2;@zSitxqrq?-{+bK0!S#>R##M-zY`n z#l1@)0qCsPC7sm4Hu5fPBTnoXYp_!gS_S36QcXmvuhoZWT;M2Wv5;)yJf=JlC_((U zI(IF+uoM8Adxzjn;NPZ$a4iQ#$KjaQPrmF)SfuYE)i;GoqL94tdx8>w#Xsr8gfTqn zs`IosJ$0_j!^S45(bZ^GI0+RA0!8j~l#WBm!XDWC`E3BTG#T<;;_K_|T!-ZrzWis< zU+Ua2ydu6lzqisH)$teU2?;23i!GQXq#D)s0u^|sJg*rFF5uUAgl=Pij5I88bwh{|8IB#XdEpiigW^)w$cXxk)*_*cz0$P zf!AG%$~XY|+3AJ0$xzowS6>%?qTT%yuQaqY)V4LS=v)*0)0+JFQGbNZqyzrjOUlN$ z>;rO(Cn2!u(Iad!aO$FCgiQt`q3ygQ2KgEYfJPLs4KOvlX<|CM({LkTzPS;GopiHv z44eFRyu6ZC+8T^BfWjrqYwpTgxEkS|+nG9syu5P^o6wSgFnv>3O%!xQo zX_z$NZQ9&Q_yMir5k80!@bB};rTQvzyc%Im>Ra|+z%P(c8X$nYjwJzfqz}5A61==I zN*5>CZ$m1ne{-a_ds8IQT;JB((pKL*5i-@+x7EY@zaIsVyX|e_G4sO2#>nP=srG1k zqQ7ILyRW084`NUx@e}%RZF_wYjb!s88kZrWs?htw6(K!BGBCm^B>id$YD(!w5Z{wh z6aj~iElmvjQ7P?hZ2$^X8eXP75&lbz$*mX@1;y2%=A5k@Iwz!h?%Q5sgjy)584kM{ z1V4J*NC?C-$&MC#N@-8DA^5S9^^}epT%*Np(J8}y!Z;a3@&j@$a#vOG5Tf%cgA-;L z_Le+d(gqxYN7%R$#upG_mqv=)k$+KnT%N79_b0eMl$&dH%gzv9J!)PlTkzV-8VUoM#}ugdqK>;@s5&M$5jQt~lu zRmE)%ee&%}qV8%9Z^OOsO|KbbSpWQuLEf3%s}jZG@#t-pK+*g)5F4EB2C=P-i#1F9F(c zCDakPOT^H;40Rv}^2x{WMVKH#lO|xp@tl=%*Tqp&NE95FOvt#ViYw^PI>g)Ef_OWj zQ7rlnLavbAW%L()m;DSfZJcdI%p)QC9v8)lq3Z+d@fKh`l8nG{^teSA+X*D}A}VNu zkzOZvulP+BQS~^z1z_w%!i{`+I&>Gv!9ua(-@(1F#=Wc1MRGmm?WDN310~53d678+ zw+Yo3hZrZB;_`i*yyM6D_XTZ)rLw4pTrMM-4GXupmpeIxZ<5`ar33|H=^c>6@3$HB zJPDrRetJ<8i3_0&+(<1zdu53#{keAL`}c5=E|p26F<7n$Nw!$f%9foKR;q7FMiAkFZNh!l<9pc6Jh%)h`;gI~K zFku^;o&+iyb7?9c2iCFcnlPY8y>98(AKd;gtU}nS*)w4l3Wxi{;RRvPW>+^pq0`B4 zXz;}+8s&d=dt>*Ehp%x>d~nCVg14|YgVD5L1J)7(EUsI>95Pe8w`}We(U6H@@7IVFA z9iz1ACh6WB10?F98anyAu*LvSq3!n#D0#r+{9Pa?k7l@H4LC_oI#{G(xY7UTRZlNy(d#mBQf(fSNwp153 zk%&FZ-EB1XPW(A!<@u z-Ks!ml$nN)c5dvn>DP{ZZ_MFP4yEEKqE~X)cM@)w%bf_|-|x&SoqyQz8Fsa0ATrRX zbg0!8^5y!9*vyb$X|k%;M!%qPtnd5E;Ef2Qnv8y~#T%?*4z04P(t5FNmAkS{*`N7C zFz&{`co4x;Bn#y5O$Gi3&yhkRJ5Cc}AkF!nkp=D-XOy)V+4PEZx)&y;MR2ZK9^$9#Tp_;UJu|57Y{T++|x@uC3F_0TDly!=x4bqP%P< z9n19zalZ{zOUBD+V(fxw_W`>F#qxe)Z4!bnA9_r_gLFY}&>!Hdk-yE35A6S9L~$u1D#1wz zl=PHJm%bc09(nkSfWrhHH0I>%n3Jn8D@hK=SmPH)Uc~l0ev<}N7DgkLh)b8j$*{nK zJ&=opJhtpyUImS?lFB!bkXQVD*i%Zr56 zxG)Z+g;LyTVJyHZ7k=T-CZhb}DUaTf9h)4T^<;4>gSr)*YWs7%({c2@;== zPlURXyE-RtYe*)~sODsZ-N24&}hV0xZ2+!tQtj&_^-<$^R&3 zbAkWsa+4W5a%_nqKwMS~Gp?{Xr!j9Rm*o4f`HHrT#oQDv1^IfLE37w6o02t`p!?!J z>+&==koB;b;vB6-D-WIxEMq~k6fItVge8}oHAQPsG=p|@Xy+-=yBZP*qKoqFU>1-A zsC2Fi?$|iDNF1W565+|E38Di$E-KOh;w}}ZVG&QLghFe8Rwr?h(*l+P88Ev#_!D^% z(gIT%9OeC|`pPGm#CTto!G5h*Set|UU5Xhw(-NL(Bltq4D9G2im1@k*2@iJ4de(7l3h8mOTtVy$x}t6tB+_g`HRd-DYPC zkEud12o-9B4FDI21~Kv+_o`rZPRYv0faKGj*!_;WNqpAw9_4S=$iEY2)y-;pY=6*jfRL{ z-?+vRc6SbI$}?T+a=oFh&RMS1sx)g17MEJ7WKx}8qhbm+Q=~qh{ZJpDs zQEJpuIs3Myea$7B)zeOs#bRuk)~>ol*H$|_I9sK4_#X_me^#v=)%z`Tktt8)VT)fs zs#O2~ti20(TjiB5>~Bk!grdl@yrpePmSx>7%d#!&W?Qywj1We+#zz&d@wmnq*LA`r zKm*|thlUU!TneO=;bbyB`Ez)DJSnAlQpzyE(9X2e0eT4rI)%f~P77rCnLf1TaOUv1 z@#cTmwuWzk)z3akA^8T3DV7{y`CP@ibSfjtJHBx2Pp>Vg^ zv>0`FMos2TW>#&eHX&Ddv`If2X*P+QMV-!WtZGg9+tKvU1N6Y&EC73BgBCz%yX`c7 zsWDy1@R3AVOt~X)GGnJTmmi8u4F0K&`iN-Yl@3bboO|Xj;iU-?-k65RvpP-fN*C;P zMA_e-+F0`cX5Eho@@t8&w7O319^>WIIW9Y3Gn&Nm=%;jvz^*s3e?&tLv?LoJH;?yE z*qaRldi}W1Y8T|~`q37&mVvjHu`L;BFLobhEy?X>(bIdN-i`}CA7VV`4d5HEQK@#iQWB^{?HGRyZP$B+P3X;77MuZ2j;#$H^97qn4ayE zA1DgHite%K2a1NS8wRi4_E%RI`Nsou7xoX&y|r(2hY|-U0LQagwFPTN6~zF;1`GWP z|6zfp|4{1?ylD|~%!8z4@~`F>=3hE7cw+D!VHapQ5N@_`@4pZJ`{29S%Rg6fj_OOo zCmY|#PbUUs7wBKTk36c}=$6urvdWFnF0_w0v5x)+_Z-tY3cR<{7)snf?InaC6*VhJ zHW-FTP!$4M4Y$%dn#uv1VU}3*I=ox{MB&(GpS(NxE`;xw4R8hKo;2C*rX*{F-+q@u z9W7_MP$WYFKDRme+=xy4EM-tCsq%ih3=OF0&f zCFO~JYhx{-&9zH}g~oRX8LhFi6P`?qx;~H{)dw2XGt1*YwwiYp?M4k7NS=Iwb!7Ej z^1t$&2Ew)KW__Su-DL1JC~a1rzqPOihbW>EJ9`g}RSr@_?(0BT_GUiQ(^Q)MXUp{5)Ge(slCIT7=<1t&$}qRZ1E%MZz%llm&@ET@8?H1}qhW@{?exI5060&TNV} zr*ii?rm}d`y-8(K@<+y#Se$jQBN4>QFBgCQ^Z5y3B9m#di!Dv<*{M9ThD>E9Ci?oC ztnk3E%3HgEyZ{_j`YoQ4$d&HR?Cj(gVDb)r=qd-SPC$CjVwIo$fUBR?q43x$%O<5YRt z&Qc;G<>uB>Jb#u{z!~A>{@G$J(h-7=tkAE@*yhbxCt|<_UknQ$-{YZ)MoBaECsI5L z8S^2*?J1H)i#%r7IH3{Hj_k%4EYiBa0Z|Dy#_}Cl3@3pw)Kc&avB~1)){De~tuQ;# zl}dFDZ27!xSiI1BS@DFbZ~5cd*%PbZU0QuPABe~E{SEdyPkqz>>~5(Y7W3j{TlYk+ zJDKdxO?0lpgth`W_(82PS&I+-rfGk_}${!1$mpdrcp{hA$O%$ZzjYk35fqBJ~69J%5S4 z$A_^>aW+8wRtCK=a*+>%mT#oC4&um$Hdo#S1Bfob?gO+r1>)4;s~HA;I7lc%5LXhP zL=Ow24tN(U29J8o>bLX;y0n`;O2y^m@mgN!HL{Xg;9t_Ia$? zrn-#$V=%;+YZrDFtmbxI`}Lw#Jz>Hn-}PPn$!ird7%|tFnKmB5mL%NS7?6L}a@Djv zYcTeC?(Fh4tGoMVhJIA0mJy-IDg3iaJ&7Z~8~E2^#TU^J7*R&bD89mb6NiqE+VYBE zfPpavxr77I)+ALB4mKX`q9k_m5OFloNJT)UDx~YxoJWzTCZpCWX0=8K^)Q@XsjP?>+k5UZ)nfU zFAvT1b*r0wU3d8UjD{{wxWk!sN_|gyhZESk7NDxmrM_S7cDS5ycI4e2z@d>ky+yzx zCMBuk(dAkMn<(Aw4e|y{1cIuu$BkfMvBnz>LcbLO{RZe9R=Z&*b@oB)*fnvpd4HaX z=<%kmJCi;HO_;6oFI&YXPi_0-CcO1s-;wQzH#MQ|4ego!oz%xqB>N0jopFDMytQTP zw5L%BEdPotAul(Y?V1Lt8s$Ew`qcCDLko`s8CLq3&cLM+Nf~$Wa;1w`dpDItXCkFN zCLarR2?hSpFloEzkOjp44lmhi*Z^1hpzoq=>lQ0ojel26%X zGwFc%k_6{Di&XP z1spTf{^cCsCY%~_xNloI?OYHVoF$k%CGVVO{iZ`);~*O(h7ZI&l{cM$O3F<+BLww`PGnI`uwup9wKTYW5dookG zw&oUVEs{r1=x*WLQrAqkp~=^zzUFHyyHX?BzLD$lq{@dA5PTFknIIH$+xv$09@;z9w|yJ7v@jbCcU~qmo;+z7o*PhT>jvhAWjj90cH#$7f7BOV z_;wypU5s&2hZQ1B93GXh0EV%c#efzMB8@LoRa2s093ipJ!7l<6nuQ`9V_*kceWe;J z>YHjuHJ&l~j>e|LSlV7gB4~M{HlV*nmUrdjU|lQ@-90q(_)KBvE9s)GZfua6 zgJ8hMddy?lNMKr!aXHE@N&Iz8HTJ=skBPp)CSW}X0ixG~VtHyD4OR^E1N;|pJXdb~eSXR@00 z+wFQajccL83BBPPSZg;yFDDM6loZnV)>WP6iQ*EPX1pDO7F zalOmGfv7lFl=echr8sfgj?kEa{z{I9mAJx6G@Fyrfj1RU!hV8DL{5@9Ai(xwokP&( z{~Ef6vc$_bk3BlZ?iGWr%$gW5AE~?Wh{GGpGvQD4rF)Pyi@x>OSBE2mkzy>FKUX8> zV_wIR>dX=Ig>mf1!1t|{sS)gjoup~4NTn@zcnvb_YT;^j14$2WEDuyg>QyX7ium{< z#>457=5P#$55e7&FUEV=y(YE%%b`?Xr_*D$cQd_GjiHTXFP2>oqQxCb%mqUcW4Ows z3Av0)oqE^RgvP33ry{(cm+Tm2+kxAvoK>)bYTGGcWI2Zg#e!QT;>5j?;G}Fa3-a3S z<4aA#J11U7m(!pGKdtGJ2H~caAk*PHhbIrzC6_WMikx$nv(GlZG8WGfEQOE7vK&rG z0bkDsa#Xr4G4wJ{-oPS$mN>x3_+G|(osTGlvb73}D9)kpmX9bw5DmBRGd!UO)(4q%$>zCebg&ac*2b zX2D7um7-AHK`pFJ*xAUO33j{n&+DaF;#74*b0JWZmLE=$V*w@=*a0B$$^-hU5~qq< z#A^HjyI42kD?e9pIUo5|$R^ziL!}G9RY0-yLd(`#lPNKUxaACdD;ZoOlJ0d!_WF1V|)Y70vBYa=i<+<8W)8+2g zdnM~0n;_+J_3w2ZzAKH@U7K#w>1?&8*0x%m##t{bv+een4Q7!;( zM+mvm;lU?`KQz>B)fKvt1G1nO4b2S!U9D(*M&IBOw(7rDn_YKR{f?k(X{$B1*hT9< zb`+cK`hw2b5-48NQEX~#Y$|pnvCoyUWIAYw#CKbs^fj3N+&;I)M9AJi_ruA#M}Crz zgF$*E6Bc`&F6R)yrvTLz&8p^Z2m8_u`0hDCIAVsOzbvFLCITf*v80#<;WZ_ljO%Cc)p)ekIBle=1MH1D zkIPlx&~VGx{^GXhfA##f;{LITn~V29c7I{V#CSu!tHD)Y4_b}uG@9$wddm$*rW9k> z)R7x3UvVCqDcoPc&&(mGS-boPbe0LZQfPgLT*RkAIC)~$MO+r5!gy4SmK^AqO@2g` z5a<9Ye0MBAArzk6fqwwogpM9n`H;}J4+-c8ekcEiy^PCkJAS)^))b6P_-p>hN_-yq zT1g8mw|%N+#kqnIJw*rcB4wxWgu!E#J1F3by z7w#}uD}D!aHLHM&8fIK{nDQJ&(pGBo$`u*Zu+Ea7g)*XGj!LT2L|MyLvC6Bxl~TDp zJba2k@{zdwPJ4GrDm=CMeBWh<7g@zRp+JlWNfeQOp*&Tr(L*`rsFFMo3Chug#0i+v-{eXALV1Iyl4 zBwTk_;?P#%91g7+d8XFfTA|QhC>5~o{tAEgo5g%=et}B)RJmcf#59EDYi}+;6dU|L z-Fn@9WkW1aH$7Xqv0ahW+I6=sQGXlGDEK0ZP+A9^SBS?PMm#3PIub^mcq!04P_LCU z1uU-$?JbzU{RxglPtnIv66q)kHo~$}!T6;fa!O<6vkKquxdb88!2r0&M$#51g7Vc3 z%U^>H-|KY>M(3(A|H%Vm>?i4deqqe+tKBFV64BE?-qkYLtWI_qG(yXu+&tLQGB`fi z+&sAakpXXZ;#cUPb|c$|qI>AzA5}6&mBU?tr)MzyaKIvB1mG(q(;Qu z)EJEV{kxg>xcnxC4(4Cnz$nZ6GZF#^y1*b^jLnaLu`46Ko$%ga-9_acLruKVAp+?T zcr4)3Sso<5%hUb}y46F^bpRqHFE(k~MZsPj_8$1%G6Z{do<}*Q>G(s0!yc?0q!F_yL$(JK#D~^!YW% ze#)|;;?5!V223aeiDEG^K(>^Q<+I#iS()-Wr5?;Y)h1({tymIn3 z>h%JS!14S37Sn%u9=s!JbShrcY*NUR4y}B7#Sa$?#fLS;L5r2sqy}nj;`U{NCsie^F&URZD0N^(sr~A zH8*DM0Z)^?y{@jUuE`UyLw3}sjA^KbcF_tJ5Ze8)qH*t}vg{EyE&z>Cs|qTfDSqHo zs)p5rR1GaY!5()(Vy~{$#)-&%^1HWzHrHYFekUEgDud?F^}6*sG;w(7=5UYaK|?Sr z{B4Te5weuqyYIXQCLjMYaBuHQF+_AnTG1C1clGwQ+UoIV-dxY9cfVPrbq%_43HMw^ zeJHqVQud+c#mev2AhTJFG-5&s(lZA#lHXLp;RAxd7V?SvPgQKfKE4e5(uF)9BUVIm z=-@CO22PHkbmRt8@U!p@aL6>l=D2r^F2e~zq+I;ZByxMDI6~h7JFKEXVk1`#JFNzD zeK4H?SiMs+Sn9yo)?^FFayB}j4%V9umU_QEWUDbdtOlvG-^jAlng05wdRL=M`2Lb5 zpnl8k3e`GY4I#<#8@1cMgqXPH$D>)N)Z}u%q4ryG0dRQ!O{dgov$I33IRxj3E|(}z zTIw4Bq9zP^5*YGVFaik0gPu)dr-F8`p`#eNMcmx$=m(z|Pctc5tAv0@*eB^!*5Flz zD?z|!^9Fn3%$(>B$}b|cNN3Cx(iWW{SW?sM*()sekUO1j9r%)WJkfWJ*LzK0V!Sv| zBkFAyy;wDDq0`4F531f#-Ne@`ijn5nOsrY$m>p6KKBn^FXT>za>V;S;=+VLm3nhjd zN#cCE5m}lWbB}%K_$KR^y~B{v-}Bb)9!fiTNskggC0|cf@ZMlh$YHegw1?~E z2c6CBs+)vwcEp~&;kH+Ao*%h+1V2T8%#R=WEnHGQ3wqp*ARY{6gGHmh+I)_Y=93ZY z!(&(EFE3?^8T`n1&synqUtba1C^~OtK2P8cnF;YNZAN3I3c-leW>AMsI!Ua= zXD*k@r_qU$%Zt?Aj#$Q*?vh*$VSic@?E|Au$*5Ng;aCjhM>@S3SseAcVv*hpYQ0f% zjt4_+0wJV0kD*^% ziAErcIt8B8DCW#|t8iQw(T?n96Y8Zq|ijmWL!bG%erA$-frCnUV}% z7 z^lZ-mgDq_iI)mXnL#W=iU#2iR>Bibg99TQCOs8_sa;IO$ppelZ7x!O zla;YnCVc~nM<^B&-VxwP!|UK{)cKACE*4D|2N!;z&FJq^zeP6;tu+q7_V;YV@xSHjYiOvU{vM9{AEx@x=5U@};f z4K2-g=Y2bK-D6^{uA%OCTL*@wW<{NEzgXX**VUWVg4o|I7+VS+ov{4+SH+v^zpCuL z4g*G~Sfw#AcFm5?Trm@i8C>&`-SZCr15;{^-)^cjS@iItU$3uqYV_6T7w;|pqd;w6 z#;enM@?ESaTG+1b#BvSG80@`VYk<>-obIqzE5o#g_9qy}DCIBep5O*;99o2L_OWww z4#(nfXXkLXvoX{m?@*BQ@0+BAI~W;=guDrhX=qG1I4(S%eLRy3jQU5D<|2K{50%2ScA)s5yWe=rVd&xFd)@Z#u3GUk$aJR5D8Rdh7C|f z3^{d}Ev$^E3zT{-|1q(kq1x;)MtqJn-Qz>A2?q})gqw0yA=iL2B;UVMn+r?zV@teZ-LFyyy}H}zH4a!B{E_2SG5k|i9KzYg%!IGD)-9m`{kEPcj2^<=!Y+T@{#Iu zDiO2SQPu~_02!W13pO*jB}yO?iWSFffL4Gt>Pu7Eh3piMPy&eutha!i5(8wei^Bj0 zx}H#BDJ2JdQ+vZMn8lXJowBs+!>)WG-~9w+E$jB@;Isl+u?E6=ltNpn+*3t$tBW0W zwW^ED*9ghHJfZR}>s;8lgpUD(pmr8mJ8Z*WMLWB>Z;y0p?v1jDP-{8JCy`79k;E_v zsw31x#aU{he8J>eQWV$^bq1XfbGsz-l35Z2r#l8UL1#fMhh~$`!WOK$O&Yac z6^wo9TyY-6$;dM`KR1x>GYHS>4D#C=L1Wk)1~WSwiiVAw@OF(IO9PY%5UYN!_e*XJ zR$Af{ylwddA?ZLlyf*n_3P2?Rix9zmV!}xz=8-3}oob6|sUS)?8BBb|8&;RIc!$jC zP;u{|{-2}X&44t{Wv@rDPSGMF%*h;_pEVe~<`B11|Mmyl)H#FUFCHk`qW=tHAZ4tU zdEUs{hOtT`6o)2SEO3e-2MZ<9R<_d`r4H<8Vl>({boJFknagVS&CML2r-c$fFK(vwxM~1Gs>YGIg@J_A4KL^8rVEnw|DM?=vYJs#gF(JOjo2FJ4PvzBhyDG- zIK`v??!X+5U~K6yx`1X_u}E;HOsr6((Gzi|M#3?f^w1zQ3bYeh;;`SbH%er#dy2+Q zBDR64frwu;R24P7;YSvR^5LF;df)*mnzc)k?fj_9r_e1g&%7P1abOJ>F^0{7cU>Xp zxo%f|D87Hc?SiPS)-FXa#9mgm2iM?4MA&=6W@3HR!Y>D98jswnClR}H%tiWSOwy=g zS}(b%028Ao)o`Qnd%FEi3xcq#>AAo^-ZGGzkt@_{Yw_^l?ViK;cpm&_qvt?Ae)of4 zjpqlalM`gse$QMqCXK~w?yH|+k60VU!inMjdvxr+;(@`oj`o&(Kfvm$ifhq!{b)Od zt+WEeo3~JJNliz?MVLk9udb}YFO##Kt$vr)vILfY^m+z%VzMp)IbBzh8z;i?Qdr@@uu^hQI4xsj2n&MQN<-YuEQ z-a_Q7*?Pm&9>n4Zek=V`|gGGh4R; zKaNZ<06BAAM3L18S<5^lik;|mY0I*b?+~)mYT-srFyO#Q0tJP175|y!9$y?Kslx?N zVfy~uxjp&iw+DOr#wNEFOisaZCh6NXUG`j`4~V~I?%{c&I}Z?aGA5kBZ`NxPhpmWIyaufv5j z4mqw^C}L)hqBFYd8hm}!89tbx2KxplOI?|S`9l7+lRr&9Qs3PThg-d|Hn9Amy%ylWoTsgWZ6DcLXp$c^PW{`+^tIWq zHWxCJL#nQhN7~y5`i7?Vd)jmX!Dy`YsO1B*v%`1ze1U5}9@$msA5OAst{-Xld5Qtr zW7f*r2ub_SD66WiBx-3E!9l3VjMW`7bFn2Y+?cdho-X!if4n;br+9!_+eq|L!*q6f z`ZWlD-*lm&*fhAY@a_A%uJ^>n(QIR5b6vb%exH3)sl{l=j5w{iYNS0^6s-dGAWg+D zpx?ZOb+isUa|`Aev0EeJKtok01OSnEZJl4`(20<#VfmB6mnz}Fqf z(7{akW_wb2%i85kU4H##oji+fV*IP)<9)du7sy5ni#gfT%O4+%vyOOd>(;1{E~fF@ z6KiN{z|VJ6?a^(Q^FK?aF6-MfLn3r$j*bU0=a)=Rdm(QwXFn}B}9+(cd4_R%6lS*2#C&)rebpBEK86{x)`|L1s zb0LW$XN(gTm3&958DFu~oia$>Uj^PHf#qktNC=4<1ZdiXToIeE^ zPxukgIyl^Ep~up)KSk-jg&Y|15mIma z4E#^cnT$OUu)`X+>QA(wPkiuRK+o}iYOyM~FkyU4K|tgMhK10r@^4i5VsvaQstD(u z56S!3jt|cy2YYF0Yz*w+^1m{le0LG!UZ^-9`oq;2_n6HXCot6{dNC9%Fdg7Lj)Nb; zv*4$t7by;U7xuh=&;LC&82ab`xcj|=`%v=6Yi>BaEtJ|;cdsM0&+RU-FT8)(d;aOI z!50ee?|a{M_{OVmNFCZ1PC52Du1W3lPPvtGdyt?02IQw#k)0VC1%oNXN#nmZ$jgRC zO11zTeaMpn^#QNQ6DfF?MwiZWh_Zi_CGz#%_4}PSbUo~!@sFnKFB;Spd;HFAnQi7v z{<2e;3@PS|A8I(A z6>)x{LJ2^kWEUidJ5ZfCVPLu=o6bmLAmeIq`>pPQgGet29|s~xPyXQeaj?(NvB7%= zwvM%WLkNNQBVYW$@-*S3C|D5B4;)`a8c?pItS#S%brfjiPn7^9e!O^wNkmhM8|F#t z3KDK$d?T5bNE|kV!75rXY761;r^r?$xnwu{h?rJngQ>|L?(S)7{}sa!q4wGX`%WwN z=}j2R#EoC!4~km!U>gB^ky%v&U#QTY5nxhFsYIjDB4AP_p-*8zxrO4bx4N&que0;M zD?eJE{>i{}&^2-I!FwlM!RdjY6f;}*d}YwoI@uSEj$Zeh_62swWL_A!Z^Cfrft55gjnOHMkPR@qjeq=#W8cd=>vL=}4*xN7OGrkcJm=0&DRWoxz~n zvIR+W!dqhbSkQc3zBL+2q?}=gCE#*~FY@Yx%5wGJZ3BtU%hH1Bx;z|LhH68x=LNNO~%$j>%kJc1FKkuALe`T!_YYqUbchm7gr zm>Ntjx&aY>g1dkV2y>rLMGXqls$w-6mPX7t3___IuZosNcGoW=SwnGTNaMC9S>4rZ z-pTI>ubfnOMdY6_m&Gn0qt`4U|Hfhm%L&AXPiZrJN`Hc0R{=aB^b;&*2p20?2`67K z#pThygqP3)SZprXXG9+JAws_6r`2C5xG3Mqd~wwHl{{G2BmN@D_pqrtbMqmDvCS28 zlhpV3qAo$yh5K9laWi28v6v(2E3{sIkmQqw4&ABh6@NV&zk0&h3Um{kF0m15G~9DZ zZ*P-PgFH~^u}#SWb04zR5wMbdl$%O~9^J!C+%F@@F<}5-!4Im6@;`_2zl8F&gdW1e z!MxbrROI)OVr+=L#`<@jkYTxDlP7k{&kL_0>6U+S5sn>TDi|7w=GVQ*IU5vq!D~cX zRSzElWit!hi5R2Is8$6CXY*a!7VbO0){D8?t2lt z5=87uGjJQ|X_`sY&gjRo6eOP_4_?7b3d?SW9Ho$ywpMOALL2~C$%rzo{9PqHpTtIq z5DX8c$5>txMJXSXCrCi2tF~B_8l1|9B>HptcIqM?`em*yAPmY}Tg=BB?M)_Ab8S<5 zerGT2=h}~T`1ZD z77~+Lv5HQvZSuhtiRHg4wYh9?I(d^j51iiM_}e+&<_v9aL!18|;~P;GG%+nuqu><9 zD^H8i3&Sig9m7F@v&qm{>tY}I&y~Wi9T!|@X|xG2v`u8PS4^G11;DcBMG(8!^$l7B z&0|J}T7Fab8K4|p&{MPio=hlmwD0f!uJ33hG{PCLlsTlV z12wR<`EZUxiO|khc1q~(q-Rpvbv&iM0VtbLH=gM zuD^mocdnm|t!P+ml!oKQ#l>i@wKWHp96IXs~GkmBn_!cUmG0znQX!|X+#>N#2jFTW;Ys_HUShkGLZuKIK+(w{K?mn!{Z z$B#c0Y_+?KDXBg+Fd)2!Chw%fhTB}-W2g!D+~DsG4Z6~yD{s2O9=9e0bclRxI^r0Z z-g|{Our@fsQr>5idXrg?A1$Chkbd&!#ra}1 zoajsU4NL)8@|P=fB#gWY5)Tm=79uzMDN|Dx#^ql=XgV=!cpBY}Aa$A|6=;GBan_WW%_vw8{8k+~mw?;i!21-6m!+!Bhx+MPlS z8Rb`&py8J|c?qQizzCn2obCcZ0fE3lildn<&)~3GBY;vq0=~xKgN5}tV>IX>%kH6x z!(TS^o7F}~IG18S85*7b7V1qR0j@B5zxRhAy_l2qvqQJ#izAOrj7n)+R1-;$R_{KM zcx-a!TQf)(UEp#G*z-558hLv-KczLG_NQ>*8Qg>7bp~Sw6kg1Wrnu`%2qgbjn%ZJ; zYq>2R7(s?UdGevIkteWrq9+q;+5&bA1+R7XEuj>yI;|b{3{D*0qVG3rj8YWYGN~QD zeQQY`NHFq1xgoX=q9H~f86UNz9j*Fs`cmVbQbWiFMM~95J5c_yOG(C(|5QQ4ohEDz z?3xsRz+(^KW5TLy!88d5FqkO1jeN=0SPDKT5&H(wXi4bxROJMz(=X{9NbB6of#@4A15(-%DQyw4dD<)GXEvzO>V837O=MX;)9i){ zk;3lbO(2a5tic<#tG`(6H`mQY?6H|II5ucfIT3!yVNRzV(_g5IU2&Rj(fj9*z|({} ztC0vDqu*wk3)d-fo~^F&oA5(!Lpp7nf%IR(SXetxDca-=Er%?u?v0eMQS$jyE1jx* zy;Y#O8DAM^q<8(fbCVZ(`j{#&VmhNOTGqJw6gf6-1yVE&A+B*n=ci~C?Spjd>rQso zGu0h-JyQNC6{|CE~{(37Gb!5n+I&K zr)#W<;$F$5w^-e7tKJ!uYD6I#bvj$>(U$6p)%B|l7z(m-Y}}gK82S65=R?F8M8tpY zKd{u`OjpBewbAk>oZi5Dyu*UUTWhX%*^Bxrr`deow*DLfc)8NMetFwKr+Heek%CUW z#e-C9dQ+`b-`WY+$$u2tPe>6yZIwG^Y(j}SQxDYOfC?M0lxo9SeWSst8y)E$!Ov*d z=;%fxbUUS?8s*utM*sg9rw{`!D@CRansKAK8zxeHqTEl9K*Y10!v9$o8lscjYxmc4 zKc&5ZqXrZclw=f|On8hIMn+pw_5fU=O`!@_abN~F8ogtMZoQ@9oF0q1&RM0h7QiNK z+Kg*fm%h7Ja5znBTyyBEHX6^rw{{y`RzugYsY>TC7OZNMvrcX4F*qedcY&^%3bksJ z%Z9qB){O6H#b2CpbSrDcMgzF!?5nxKP*+ZJ>j(Al2Gum3-W;g<+3w`?sJzRf?=Bcu z8$@O8*1KxgHHe+sV^x0-DhTn7wI>x2#2iCKu(3G6kjT>ltEMHG;v730b?I=4EN3F3 zu;)zVrZ(z5LuTsySYyOu3xhaqA{>!wVQ8M1=)F7#6xrJKC<-l}a3CA6M`!M_715;? z(aChYBKlc&Q+kC4DAH=^2~SL9QJu5wk>#g9rIMPJe5hbqh9!xRO@JG~B<6-Dh=2qQ zusPnO)=iNeD!Aq-xEv4{v;zXoS>CK-148vfJ#GK9t%n{8tR94pPpke#_(0x=P$(WV z)xVv!xf+}vB%EP`NIBzbXkh(3F!zDd=XY=Kr}l36ItR)@Wyurz8v{-S2`RqxQ&+?@D&=5+fjZ7g|K6egOxM#qlrN4(EB#@PN*7Tlk9%IDv}EA(YztuIKU}~U zm$Ud7f!T`%_D`efOZ&jBQ3TSU@6z_?I!v&dKs6MdyqCP>? z=N!}muACZL8-RxEK`?v}Q;b}VPT1_7jDQZ@b?d!%3Moc%Q@IomIZNdhk)_-x6`6L9 z?8{KAIxjVjk;iz1$O5U`J;!c8k;C_rO2)OqPF7YFE9$_h=X)+nHk z+JbzrWu6o0QA|S7YY`|h^kk6SR^(cVvAsyYK$R>x$FF+`v~~IZDy$FCsKMz_4gG6v8ELSGVDKp65IEl^D9l{ooV`jpKtgba8?RAw z4?6_2@keV515t^RD?ojO>T?4yE#1JhG!a9I6h!bFM<$S-mm>cK4@HF=16nBz)j@3f z6k?*$(ie!m#H#_s9zI7LxV>}*e?kf(-ojRhzv72n;Gl45ga)a(U44$AX)}Zaou3OC zMelI8KfEdRIhEQjM6-yM^Ivi86@GQ5vEOL-Z&F9j*XKIj#mIOz*y+}(^_@X=D6aFn zBRv)bB$<3J6YPfhO_G=nyZo$9?`!N!HU^RtzNGoVM;^P~)Zv?q`vSFkjmhW{@M)t@ z-O&AS2CGlh_1~hl)_J=120vg)iTddvvrsle03--@S z@P7Rt@P3)_N{^Y`NH~|2RISxmSxu!X;rD1ODdMb#w}l_^1}Y+ND;dBzTeYbALh=fO zU1(dBj~{0-akKmrOPzXA7qVFlEa9p%9@mF|C0eciS_w=yo%dg22jaz@5xyuJ=GX_C zx^dEhTT z3N*_T$E*!I!CfiLip@>pEciLW{5cEvn>YieNA^3hzQOvA*w7&6#s-q0Sr@iqn6kq! z+x4SM_0|S_r%-D$;lV8A%=UuaWWs}4Hq=}9iwz#@esg^v0`fBUzYx_`Fw#GhI zaVG74BZYngB_6lKTQ2-=4yHGz{O=YW`Hojs1+=knn0k))D|8&%)c%^7E1~s=#%iARL}AhL#^# z5*W4($#2t# zLOkju6w=%LCWj_iG-TD`@B%WIWL3U`KjMhFP5+FPn|WHu^OSqjKa{=&1Ci`Da; z_oTw%)IFW^>WjHtEA6(Ke2&ab&;i;l5f~j%Ve+Jz@ZR!o>7>TKw>0vzk?vlVZzN4u zme}{wBg^Jq?$=wzZf!;uObK|TQ|^US?213n0tdw@ca?ulY}xP;o^tO8ilMosMfsRg z1jbGopQVf)vihN)sKfl#TI{3*spOgmwCI(J@Y2{(DuKPYBws{T5$e;RjdCrYbVVXA zb{h(2+Ty}eM!slC`EX=_K3V?Q6)BaymZL>P?AfWcBPFz*fsy@`sx7~p8ht&@;UVzP+{cGT)Ru$lbVD_m;Sh=}PT#bj+$zEmFA z>E+*3qfraap>@_Z8%Bz}d!^+_zg%rE8~2j0>nn_OzQAC)@oqTQxykMH(f$mF(Uux- zwTw+TbrHt~?uN0>@BCS#T@VyVF5B^Gtw`V5a;An{EuV5f6B1y39ZTjOGX0!B=;fzM z)+Mqr`A-`m{#El+*K^8HS<|NF`mQl5eP(@cyYk9(`G+OWT$u{L5M~NKaB+s$+0EmmSSkIGV9gmmSS1^26dF>eW(;Cg7}i zVVX>{SRP7@OJaj{A#=nHzisXDG#ht!&gAVf6k zu=0ss-bZYg`yDnn3oGmiD?FFSbR9YavsPZHt7IO%RS$*tLN$_xBz5>Ph-zH-iUz$K zmn$tMb%(A%N?Nn!camwCI`$8pegZp?JRD<(>refQgR#>5XsPs_E+$yFth!rhj zf!53$gaY@;^_~QdS)z79DhioZ?i|5#Qro`XL71X8p;dwxj;Y>z4?dUI|`Xc)xheeSdR^rtn ztB#v+_?6JDS5;6EE$Y^=>J0q*{<7+a^7jkC#_O1G?(4%iOx_ZAh`4}+?;LM)@E2n{#@ zA>@4&9pIh+Wwa0-J57@L;=&q9me}CZ(l|dOr;xX45%V3k*pyq*C1U>dY<|7$UbX|9 z8;le}2T}KxI2|p%O;6oRmWV@pI8PrSYGTzdF^8~*md@UEU!1+ z7xeW_$4vRJ?76Zx=*_QLyN%HMh{04T9uy_O77LYhyjHB5Fbrz_afQ|&*>AN87z{ht zmLMceQ`tRxU>pp}v0xA0%+28qyJL;C2fTp%6<|*4-+o`u-hrN;)%VoqPXWJn66+RO zEIFbI3a!6V+5W3xtpdPx#omFeQwZP2a3Rh|FfA@Fie&J}MuFs_EID&?!T~^$m){;A zXCJ*~AnpzCaUQF%z8vmFJ*6Dl8KD&tp77LCZdQCR-Irn`JiVa5?HW_IPgud$)Ta`h z2Nv`$?uuIQ-I!NP6$CvXyEc96FVhE?C}WmVAApz-vlHoC-{|@#8(FQ(@&n`J0-6O} zN{Ag8<{TEp&WXqme)dYiLX@hJyeZ*GXl~g&7h*#p`AM8-7Q!FS@7gs_dC*v3VS)At z70#bO2ga@oGrF1>{A(n)N~)>Ua;t(H#0yA7;r>oRa3%+HC;wMsOD_*^ci6iV%X`_2 z@>W$>LADPGcQ(bhF2CH+XwD=BliY!|skdT8^%db5N+6~*Xr#0j5{@wl6TiwE^lIVB zi%E!+`ih)lgLCh^J9qDpbFXuFFDq(V+ZI3}rC)saUG}mJLUXp`caJ_w@~-NJTC`;a z+r&0A2lKCldJE^y&dSJ~H9HFhbJ7WbWGKvjhu1HZuIF$i*hQhze9cf|8#bZturOj$ z;kXOIfsN86Axx&u+UEw+8uLNT8 z*)5BUTW0wndxorc6u>p-3R8p{nSi&r+oPTn3kBYx;N}G1(|h21(qi^(MjMswz@)pE zTFaf1YJ#b14DO)2Qk#1x9Oj=?@4ByA+1L8lEe@g>P8s``!b&-d?<+_TB`YTZyInBJ6%&s@PI- zb;WfRHz3#MT*dB+y);6ASwYk{6y#)z%+LUM!sG};Y$?)?>_TJ>0wBAz(NQC=VB%Lk zr6Yp!cjY@+tP*7|_#$G?c8a$H`+~2bOjHM$`u%(?x8CS zyUevoy?JL-Q=zf3h#&q!escYLinlDs=nxOP9lMl=PM!U5%N5gFKU9v|eG4C3_Nj9867alCRZ zv=MMmVyc5)sa)ua7%Plp!h1XC=Xc>YiUy5nT4IX<2?`bPUAQ_1))hRjyu*B3Ez1f7 zvJy3=lt$?_N}&)i#=!i6*#%D2Q?3H+5GxlxIZt%L^PcwhhqtzL-_jgkd5>k13n5R! zN=_3G5Aq+jMtW~<7B;P3TEg~?^=?%rS_M#lMErmxIkut|0uiR@j4K^e5@Gong(Rnt z;}y>;^(O>2WhLQzS#+;bcX@KdHlQ6Fu7Yxj>zlv}9iGe#=>4@VgAfte$CMseex}?8 z9$oW1njyaO9xf(D5^#j9v_fw)dkpO$jILfP&ZA#;U2Cwa09lkp5Ru+%fpE5l+^35uPkzCmoP@=kfZ(l1yZL(tpMGuYNetb$rWA zcmd7AX8oP_$c7=>U1!@zB){?2nlRjG`Gt9 z=sJumo^^YrWy%s!G$Qs4$RkmHNe}iAa{Z9`8o_A@M`-ubu*Ge{3^*4omsen3?63F++Ogdw)T^*H8HFkV zXcnPG#X}g%VdNR1S4c`wx3%>uM0}=NR4gXNR9;oAGGSCnU5pmazL-i!8tF=BHg}%mW}{{G^78i_4i)%_bZTRDZPoG-lT>ZQ8%_Djm~}H7 zH(JKZ*As%i%3v^_qgJ0|G#IM%f;`>J%<=~Z=8RQ(wOXw!=r+U9%hA5)1FZS zet;-d^1pGFsa}ORP9zz?mlA?8`o+R|tu|Zhd4(@VBO`<7Iqssm-Q_rMa3sf^nwHO9 z@%w5=K)SjwEC!u5e;vwaQ(>E0KH76-@FA+tLzYzf-2V_I-DZ={FZ|Kb+CMxoGcnxX z>aZY_xl&o=F1IA*-=?Pqqp~akHYk5EX|VX@Z&|&G%tawKtWJfko8`xhP?qbA?0)e2 z8MFcjC06qjgkj^`R&htge?{FPI?^{`a9wV(t@(!{~h zwnd=%hO;ikq7cCg_G&57%8$NSLR^?nK*Nw88iXRrzlxbg8YaS>>jl4Z1$V{FQ%P3U z&2AzCva4OuzJc?iPT?rE+O3*U@9-Ck=lX^4V5eKYz}pjbX&yIPbQ(vn)uwhv``-}# zj_PWM$z?R_jhfA(+vEm^rOzdbj;eZN4XSOFg67b#ZBegiLbF?pJA@j48>@76hHbWR zXJ2Pn5?zjJbsf7nobPmNyy=MKh@I!(Y*3mS1-tycBW(tq#+@2uH%Q@5w{`R5g00nG zUuiZ4`=d_TpffwePEoU2jftcY4Ci#iuvt?Vc8FSo#!wBv_U1m*35&Ni7IxRF&of#K zn;mUl%R!Xi+ZC>@4Rp@Rm!((_!H!aV(0Q~)=6A)LZGTewm!mOIvwBGR1phVE2{aNY9%5zdjn&aS3kyxwSDJ@FVbA3k{Su>8T=c}44X zCHO)A4tdciXPH>B5^+*;rqL!+vOpmEm>dI&Dc@gK>sAb^eEV}Vf9GW>*d+FmF9cpl zN*cqwW*Zs)`ju!6)y0%v7(3o&ox*j{VB|OF3gH5rm5OnYz$;2@aP*V^RP9C{;SSEa z1pZkOyz>LxD*+(MGBY#sRYoxpql5fNdBGzrU0c5shrCk+RY6oSjP$SaV;oah*S9ogw~uSFyA^L4F3-2~s!x z{0xCJ!)+vxY@qglk>aX>Lh7>Zm!%eQ6-uUHA=j}WDw4D}u)~m!L@7yF7H9#+H}a*H z+lx4YafSim_}bPYNIJF8$~d7mTRGjH-d064ki2iD!G3e*rsA!$+FT3knhA1wKKTdL z*HnYB>o|z%g<-;Dk`wPJoG_{crZ$Z?WYo1%WV;J3ox_=)w|gIRnydAu<@(~`%sbt9cV4w# zU{^G~J2$_8xLA?vxt}?1X^G^^B!_CO8Mn~$=jP~l{g9nU-~$Ce@cG&K$&a8zXQ8L1 zD+aMR^Q|IISTBoM#oRP3m3Vz(B%!!{{tP`v)TlTv11Bch+W`s#3=IE?c^`CsbU}Dd z)}@C-?}rVVJbTvC;Ie+m@|Jq1RelPIC-GK(;|CU}^BJ91+++eLjmc2+55~)i>Py}QsMr$Z+qhAM;pN>|wB!LJwgIIlD85`L@K+eD`rbeW8ec(xrgR|UP%hN#ow za87*A56i8hyL^E9*DK)a^}w`U`ALzN%jMG{ZRjHIvx2NqHbVqmY-f?>9)52 zk#~U$-6FPjTz>E6?AMq*ztDviz+aCU1bpE90{$(LQBebKvbT}T(+~S*J!jPE!MO&S z7Ds1;?2e9Imf$G~P)TU4A#^yT)2pNe z*+O<&{1(e6Z=hnm(b84)xlr*Eax1)7k_8yK&z9sRBrT%?khrYKd?=i_8c{gRGiNFf z9fKT5-$f3jOE?asZ1v$)xRd$pXq{2Kmm=s{+iF#2ph^{=*7-dX{`y9c2Fx_DT zAU=}vfAlyT#{rrsDX{n9uL+ZkV?2LWaI33wRO0V(Ij+>y|B+i^m78TjYJkrNUClhgSAMDSig9O@Q&1I}KMH zn1M<#X@PnzCdg@LeL8w`A7c4tzV*{@Plx5VfEim_5?X+T%``U89Qo-J)6n;-g7S&k}bs(#`K zL+8_Roz72OKLP^0JqU^qBpF*L4)D^kB(wqHRhsRkOqL%lve{V>%`Do&06#|+p;#sn zk3o;&nZio6YVeXTPEIZ^PqQ74)~H`EhHa4X$q+a~nAHXs1MOpF*(f`x!u7b4AA|3? z+!O^lDEA5fU*in~i4zQ3ibYd{UaQpBSXWd{_J-jchuvN07A-k_$iCCoq|f23<92v# zb)lUZ7I8(Rt_XYOaM&Wx9-gxN5*35ui3}HNv*ZdV`iCYKzA4#lwclKr80t@iL6E(C z!M%^(Hl7IvGvl{CDu3KJG4rE7rk-Iyf0K5Ies25ZP1WtH2=po|#Txi&MHzYX8qKnI|mmR$Mv=|7TsC%+;a%W2Jn)6 z7Ag2_7P+-Ph~BI4lY%V~@dSY{VVEvk=3@bsTXIX~rXYhfW*qww&@9~}x&~2BTI+Qq zwWelMt*m~By_8vgCZpJv*^lSvcRmRKfZGkGa?q8_$GG=QVt;(@xMJiDDLmW%ALM~e z4wFhcD|!C#ner?u@=)%Dcfid|F*RhZk8pcEVk7)qAEW7qW&qe3Cq_OE83GV-Du*^+ z0s@I3=)#&rPECM(C?bka!?z0ZlLZQXiLx!0&S0=3#5TDn`uivP_c`}1>~qos$jrdK z1rkJ>v_-6v6t#ib_iKv~Ool?qE8X~d|3vHH#Khp>fG*Dzu+*t3i-&SS&4Z(=aaud8;wnr+hk55#NOAz8<1ds0V581bj*2R%w{q>Uo9*)IkGnK8Y0t zw^^N{D+5$juXmYU;V>Tb-gKwG&Jymi*eW~Hc~JOeLRF^Dwur%M?ur`BmLPJ&i%lj` zG~0A3r{7s`ir4v#83%U7YNI*cX)>E5i4L7kXzdDB);j7g*Vv_WL=YXpDYsGn7ItfR zTy9n@J1k(V4tIyEjZ=di8oQ+@Y;goSBZf|ctJ>An1Zg&P>K*<5dYj(eBe_`4=GEy; zmL98F<#L;B=jak?r%vy!j{1ulw=L~7JKXNiizbUJsA}!4F<7U94l(rq@b)h7aaGsd zsP>uB(P2Cuk49%68jVKterY6)M)S~PG!{Zp6jf1_$wUZ6RTb4Z!Z?mG#u&$P2(K7J z9pZ8m>T*>|`Kh{G%Exv2rF_I+;$KP#Bq_!;O+y2D5aNT$zz1LoQ?e+MtUTb~zr^D@qx=Hh@u?X|8t-9jf|Ax7^2Hy0eRbQ)m2(wW! z*mQmP>#Ht~e>p4PsNUG%d}| zjgp9IXTr@ROSQ$S6IafD`OmPk>7A2QLxZraU!d$2HyQ&nq8WDz8{e% z6mx+uJg?wI)V)?Rs$6Nu;ji`?AcqVuFhG zW`5v!|6{jx-|A2+5X3X%8T<*mVcUWq|HVAQ3)HjTJ2>l+1(a$d6KXcqCE^kfYZU61 zxZY6?>W^xLFqgdB`{*PWp9&n0ilTB?`6MSev#ITgtgwi)41ACpW89(Kc{rv+GAW951ez6w zNCjSbiX^dmw1fgmXRL}E+fdn-;P?g*Fn*XHZwL@OTSG<4z($ZTubDYc%3UV{jG$5^CFR@U7nt-+FOgE~RZ)256i9;s$NAnWZIe zf>BBY_CXZ0Y zb2@Ilbt4`b;0E0oS?bm&MmaIxe%1d0tA5!|B;qvggDscp(8Edz9PD@pcg`0~rafFs z;MzdYO#4#OO^llCAR}FM<&GiTy3Ki)kUfTi*H4iMdDC-reC)2#fqSE&sbR#MEx+m1 zGFSI>XXo^una<7`X6#Lj9KYcwT+f0vnBeushUQPq=#zKDn$_DUTNg>{0sAWMnDtjcEBlDZkTQ_c z>5xfarVIm*zHU?Db%RK+iw3-2BLw0B{L%Wah5z6$;Xg27B~U&&vdIgj#A)&f{j<(W z^?~YYJbaUaY7i7T;-pxEY+bI1B8nF1q=*|72`VAZe)Jamt+&0mugTxOu>4Oin>ICm z=?h8LnT+S}Zfv+R3HkU-oh0dg>28-=lFYi;CXD@wfn}TRg6XZ8PkF{J#g>%UUUArm zrj%zAo$~bXU^EqqA6zn7Y$lla-Os9o64D#{xn)}_Nt zq>)>aT$H6%7N9&_vT_PX^nHQO!NJZzzitW}{t?+`3s@|#zqss}NFc#AUtC|WnGOMb zAM5J5Kr7r9QCJ^yIh)IFciBRVp+IXi#P(5an4oK1S>aZpr#5}uYr$Cu5G z9{AjzqAe6*s+2y8!n!zqN91}G3&25U(Nr%JX6f8wvT5-Pg03J8aEw|+l$tueX^ig~ zoaT_!QBhAuB78kWqzJUA9s;JayJlqYeMGS%nk z@T!VrZ+ouZl*3`kloM{ie6WXzPQEr@z}h4nd>S1KonWapRKeC}rQB0Ou&^dkhMtdD z5b<(g6(USBB*#E?^D5=tjvA6lajnGLVFE~%0d=W&dq~aE(PJoSG?0=L-fy4Pj0u}52 zcyQ7J0uir=CPHf<6@t?6oPfKcG(43mvfjX&#Fa`77_gW|0K+%0ItQDbMKmLg0Wgmo zM9IU~$G!orAWjIvgeYk9f>E0D_yB{vgu-PG=PBiAX54Vn5xUJ*c+KORlMF&0Ul|vK zaeO8Aq9Ek65-p{D`z3=oVDZ@oM1yo6BKRT>!%j)3lXmJYr&ueJ#GEJf%yWl+7rnU4 zU=Jb8@4kxfrZmQ^i44&)_`p-2ARE>i<8G#7_Xcnjw zN1hOvYsC#l#e>=_d{hWr3ehmo*}Sk*K;R=dI(3mjvUyG^-4j|*{0?K*IGot#ny}j^ zT*7|jHieuq&m(>I4M#4cpRm~`T-)L!_^RALFe`l9SRThvL0;JBi+VbyR@m<0fXrof$Yo0GXeQ$2tB5t z9CWzBL}cuR!k|IS3lEf!TQv^V8^J!W9oTzU7da<{2d68J0I)`yEhtCkMes*E0+v>u zo^QMAYdofhcGJz8<2ZjzNX=sptM|FW(@&j&8xVOfP=J=_$xAhl zg}qFCt0`eEQ9Lq4P@P)O1`anj;w@Kuhk`YQeS-T*VFT9mDNpA4#z}c;?YG?WdG}|J zcBPMAFC60gwD4fT=!-q`Aq+9nLGSrP{q48w|B%|&RH$1qtJRoag`v!qAfLrkiy6 z&-e9x{#+3@2zmrGoEh@nc0otI1v;vR@~{Z|qnf)%lJJnBl9$jzVQ=5H7i2 zIm|Cf<||GYLtF(Rd{I;v&bA1VsUbNLTz%24nTd(aHP`UltCgF|mwq#Y+&#R<{~lXI zGmXERU!~^SKy%mg@x}R@^1}kMsqP3YWz^#1b*D!>Fqur}VmU(B|hY%qTKK|`Hz zeg1mlv03PeWAGzrpo@_{UqxCUqUq5zMb-Mrg$Ef*XFqbs7EEiTAR=cK=M(gp|JUG_ zU_01DvyWbX{i7)Ub!JNM?P%{xuUG_JIX)_6O&lC$h>8Dlo~&;vvw@*@p1a-^=28lgi5z-E1=b&Nna#-&B5= zWMe%~L_Z^Z6rV%qucZ(5Hgp3_aI!~{67SGVe1n;i%0SN(bI7*lYfi1HM{*@fG17=D zQPtGy;sahK8g({0+@zmE6(pks&xj0Eqb>+u2n0Ox6O`B`c89F!B7*iBOYt658SqQdt|S z=8o)H={LOckfI2(C#F?id=0fTv$g;oHg^l#^3qRJOpBAB-XgKsQF0=zZl`f|JN{bh z6$QH*l+xOopnr8szhMUDHH5LEsnOSB%tVd3oV>AT*h9yZw@^GtZEcM0IvRh1x95U= z^B8Q{gaAW61CovSHMzB&FytabT=V)e-tm zc2P$oR7?lQ^eJgf+?~E!XJWP1d$?uD$xsv#CfaCuBxRF~{R``@$P0wr!V&f=r@A;) zjo4a?GhTZU9y9s528%qgcXk9iejESqLnr0>^l|8=LVKjLg~!wms$5@-n4q74*97wN8tr3rbG(B)u15Xk~<6&V6_ zu$z)J;FzNY;L)H5y&=PBp}@Br7Dqg%Gpy;WgXXsGp+>@(Yi@R?&ix&0*j?{$u-tUa z8VcHuN$eRIitNl9mcm)K?^wspk|KARvOebP8JF9-%q^KAx%zWVPoFxRzZ=%EdIhJi z8v3^KiV2|l$J$4{KK)4hNV_xDb9G~5TGOw5KG@{$+nX%G$r2Qd}_5yBZpfJZ#Fg zi3{7cVgss z9a^N6*|oMzZqU*nH!e;|2H6E|%=_*SU>nLnf)nmepz1@&q(vS8>|@m;e1-^6QGa3d zA)A=hYwfU_19cQ*h>6vhDW963LLdoY`+>5Q_Tcn_smE$FdsB764!*mxD&5@NNXij$ zn4G?7W+)zTJ-PCmNUJ9i$~+@lEP=p;{^8gNq9dZitwP@6iDq`rYP+X8qhrp%NT25! zk4L~pti+vBUoN(5#Fuh7L!QACf+ZRXxMIp1k!U0?H)=gPq?7l6zzZpI=7Pcp{6g>va)I@X@OjAj4V|@NWJLvVsP6%jb^lW3A^h<`8f0&j5OK;(#WG`l0y%C@;CAIjz?#zy;FBjSiK8c(&_y2-Bf;^48= z_)(B>*PiT-?DzrYK}6g3ckh|W-?o3!;~5$K!O#d&YJ{r4gx#hVx}1@t3dUiaqw^&v zMV%{BQV{4HCpR&5pS|3cP7e3(IJl$t4-)A%-t;HFvhXIko=cFIeE($^D}%eD2}lo5)MA9>L6)CHKG@;%b@xQ%tRy22cZdJ zXADv#$e*d)26eM0NUA0Uh-xN(B!+oTG3>L_stfKrvk&a1+*8EWpt;4XEs9?qBm+ha3WntR3}wH1r^1bX(kgMm!eM$ z$xkV-6Gi_bBO7YT6&JMexICB&*%9Dnrv|%+VWFoq1JBBGrM|iu_Cv%#gEN_?Bx8eQ zc%lXYb1g1C+OYN*+qEw?p>CK1DCf(i3c(fKw^*7(6jq{`t@I zIa)qvm2cZasX;lu+agOw7FCWh<)DY?fVF&B+4tJfr>M_m{h2TgVMJP=9tgD#uuUaF z>l3r^qV1@lOc#k+hZ5tD>|YfQR8X>t1_woBZ$JZfP^wdV=>zkaZRZ{~J$4DB^Mqx` zH}mTa5c#-Oo4Orm*)U>OAv1|SLc->%EU*hm zs=1d^IliVH>C{*gZ3xE1J;?)1YSwAWPRt%|bvosUNWpi;2C{<&Q8L(U9#7O4k{vT}chguuGw772@0xAx-`yu0`}VhX4$OMy zSTq2C{SK$yBWV}3Q!E-k(yx_lZ~>X(@$IpQRy3HLfuX_aXbzG@jXQZk^A+qBgjwr> zhS^>92fWuf>QE#pnaC(~k)#Q_oPbF%01hz+;7qI$5ttIhksqnuB>lJsfFXc8#fm4$ zHK>6U(ZjBB(V=+S_&ihQ5+v@&U5Bb@J{P&Vnhk3y2EdmlJfzqft zj4%KOh8>^0eqJ$1QMx?)v&LG+vgF-77-~h3LN4-XXWa9QFQ&EpXyv3Q=CJM3wQfVw zX3S@IFymfNY}cuu?mli}fr8ZlojaJgb)_4L5Ae2-FlLk=+_SZ)*e{1w?(gFGK=DR;mh zO!4`H85SU8fglHyQW>_~Dai*g6m-iV{b(iI=YV5YZ;u)E_Je_}+7so0?5>}m+7B9c4E_R;tZ#+UoD=%X)|-}7~c zD-v-TRJov)XAPWFP-aQTLHJ2Ql6Fe1vgg>`>`~nT zw`#nCppfgC0ZoBd?qG;pF$aBWYSgt!CO|<&;}83 zb$f>S?D~Q{Rq4{f{zYdqjJsH8>2LuSjP_w#H#~X`aZG%-q@}TysVm~SW1gu9(>`!& z<)i>xip7^2z3bNdUYY0?ML~eH`F*6-<+L9qlAishvLNj{^*f6ki}$BelW*TTqmO0# zCPrq5e(!U_!%Mnqk*9ThjFPLV@d_Y?fJ%uZuBlX#M7oL<3z%r+Y+R=8k(6Oazt3XO zYCA_y-8!7DATS#@>O?vIw2&A-YIguKqD<)m(;e+k^~Yn8y_2VA*tu+p-t73^p&9Kr z4~am7RE)Wv)T z0g{qxgLpCHl-JZzLn#fka|0TrGKLl>;Ur?V2-y-I9{Lp?o!`20Kuu&ggOHOXd5_rl znfJ`fFWL8%Brpzm7T~MGU*}G9kaIO_UO=q~K-A#}W}An}^MH7mBF>P2(+T+u3AwCD zO=W%)<+QCLVthWUxCsZZ7=9uQt~^cuIm*(ff3^8?Y8Bc>9+TQImpR3lrCR7tK}0Hc z0^2j$p;~{i0HFdK04pF{Whza9a5i#m$Pg*btfmf@N2rWmb1{m&D{Dqd8+(Y~DX#{< zVzM4iGVj=z{9iG4yY*Wd{98BwnfJ>Rh)x2e!{+5)%79aE+0CN^EZ^t}x@Sj~FOFV? z$8H40gx!N9)5`?_2)<5>HndGR^CGr^`hU=-$}6Y&nk*n4vgOc|)M z)C)bAlxm^iQk%bV8cCpt#aT9*wOs?fy+w>pt+$y{*1xM8y_3Z_sP&n-zYds?A&O%6 z@@DtmfvG`l5%sgT$*4Rth9lp1{r>!yxt|hwlLf!523iQMWbjVi<*7 z%wR%iHD+hXfNGq7r#+AJSbeurz9om=+r9v=V788w4lAeL}}@ za<|o17c)NK9}n_#4brhmW(U0;LBGXX8%&BYZ&7d<)ca0Y;gc`{X|s@oL#o($X?Hmpu0$>AW`S?R!wj+@K1c)5AK9@~iFmLgRQ- zv$8Amd#ShCozzb>|2b$DD`<8=r-@;mVE#J*)Mi^2~tULuiDt~4ZK80)!1Uw@}9iM;V!QlrVR9>ZTu>aY2tdG6D@)U(! z(wvrMj0?L9&SBiXOQ$J>%iZ>(hLxAE^fm>LvXk+PNDel}uIw4gM!P_{} zi(h;HP^x&{)L|LmT(_@FbJLA>TVI5U99Khx$dp*_N_v` z>mA@FG_|29bDgbshrJ%U<8w%WCI^MqnzSw9353yQn<9aQ!bc*Z;*|T%Y;n^$Tn;oQH1^ zD#9Yh-V0er`AC~cYp&e&DAO5sD(HQ%Wnr&VEjXkerjlgP!oljB@O2(r&6>m;p^?Z%?r-#7KZB+&u6_GK>D zC_|VW;1_LQb5uET@B(dfIfi!K{zkML6lAYW-bC&8_?;~`7us!s47(FDjQ77%hF!dU zVY=iJ@1A($0)$jbEMYe&as`~ACrA5sf}}KV5lm=nO54{Ra76YK zomf!Fc5wr{`d@K6K+H36BHS7;a+Sl^DXHti!r6laLCeRr{5i`v z>nnUoq2ndS;2K90DY(YUjV+- z8IQNKWWA%exzYZ6cNiqi3!^%n)1`Y>lAIn%IezYhA?Ao?j7Jyp`IYzg3Ketgj13REKC7p_2!nJ`m8dgP!=x{dm6^b5hB7bc&&vZ8e>Nw=J(~8?3f}y*3fcR)j+` z%gteS^58c7bcPldZ&( zO437mVx?blHDh5?f^{_@qrCWq^90v`y+Jc)tkIh5!+@%Yw~X10)uL7i z`c0Bul*~dyxZ58vZ}#fyO*MmpaWm5*DVIs_>O)qKihVD2?m6~bA?C^qbdLx^3^g}$dvX!&)YQNp zr<_Idv&(AD&MH4Q=~6>;!wFOWSa#+}_PHEg$wNMF+9Oq)?-q)!Ag_ie=qDu~6_POy z2qb4zXb-PpsaZ9X_dlz1NXnD4?74%wGWpIa7L|d0g;yWR#_!)LNnh97**?48bO(qN zUJ{P%R9@2C=#{4=X%eYa+*VU;lhFMUdr6ifFi|Zs_$pNl(#x7DGQX&1QsM_maE;b}=w5#*o{y1yo_DCUig?gLzRD zI*3#kTuNyyL(UU8J;g1WG2qes-`nd=!f|8$%J$JOp>SUF%C>sb>7~=aK6o5RG28xT zdk|?W#_-skWB7ySf@4P>zB-IQ>F&2{0Pjm_1S$Ej$?U8?N3T`%eZGty$GK$h4o2tV`k&i_u&fvH6c&QkVA>ocJ zE-yd|^4bW7B}0aMgpPa$eM2a z-Br}Ptx9CQ&Im6xwCBSkj+Pqzm<|_dFpS<59<(*&f~$I0cO~!hif!Fa0$;RXXV4=W zfFc*V)t+)#NY(xUVO$fafxSjG*Q8VMHPUILl=lL@P($n}JRB25`%wUMhY<0iJ>+wz ze&gRQ zZlNV34y8LVZ@Q+#3PFY0!W{DW2Aua(bg)1UiKgVzpx7?dnkaOx1ToH6;(>8!V@G%G zQ;c?T=BVRQwlk2>I_%oiNM~orm5}UChv2c<^*_FA`@TJ=c4dFK?N;YNRy0Z7oq|yg zbp`^RAzG{lcHfJ)$M><{nOYq`PI!P_o9G@*gT?>XS>>8X!vR_<7zb|DYPgW3w}-EZU=aJ#-JmK`9M|*WvEfoKb_8Ng1xLwKsqByydUb0 zz34luoHV8GeS0vskaH!(QQ;jJGeE?TOs3b59XjMX%c@NGF8rR^ffAN*y)KO&7lf%i z93muVeula@|A;e49oMB`nT0U`M#w_r(4{oUMn;RsbutTI$SviXlE+>=mTbDkl)6XD z1_eT?Uqn{qua3>nANy5PQu*~QrhC36;77fFJ^J=%h)p8Y-YS1h5Db%H4D!( zpeNw}U3l?WHh162rlyEV&OlGKdFtx=;Bj#+3``9yxZ>Gp76n-0oh{P$I_UF(K3WGV zeMwZ9rPDN`J;)~y($we{b4M0ID>X7;Fbl7*B^mEqtejvcE0P>)6+LqHMdUrd*f}uJ zIVsN`h>IQ@IsxMKM7%!cIV9zF<)lNNSm)s1p8iTU_mxjLE`3{nf1AGDy>QhZUbWzEr`XeW_ZK86Zsv=@}k2&B_Z#$ADYU#NAwdm#{yL_#CZ1=$H#?aCY2 z{UsshS(wQVWh^q(nO4`}tn!Az2<4_v$anX7qC&(aWX81dm{F(e&Mf%yE__>yTrwL} zgFPn0L0{~MFg@MB+iB~Dht}1aowaF&s3$YfIjYsh`XXS2ZX>)q)Ze0?M9tG9J=vRt zj))u`{8J@4$XuT2hAoNowV$D4I8XgR0zs}NBPw^#4utz!uG=>9p+)v7l&C?CY}8ZL z4$Oup%?66EMeahCTAQ{8GSdQoa23v-RFKvUyA2K*9aMzf9Hi~v3NubpIG0BTEnElN zaRV4QAdsRM5pH73fGQk9ZtAk~6F^ZQ`v?Dg-YDN$t;el_zD1scE71z{91X+~Wjw>9 zN3#M$YcJnuz=5ek%oUm?T{N96Z9XN*C5*N!ziM9+;3%Qo=1RCe6`+r9m0>jv@!!-e zL?J@oVSkU*kxIC}MdXuQXZ`5pi0!(y%sA{@imRiwPlWwkhTH1ooHE?-DCVY$oP2S(GE}-si(e zyskG5Pn}3Azfk`9&4ihrE|Od?Vl8VB?N3qmTy7$7sa6A&4gmOZU5w)JYD?o*1or(I z^FB26nZfPK-BuG$6S$)Cs>v!0QuYNQNkU}b{l)L!&gK`X^_4uQVet$H@&!MXbBtU( z@(H>yosxn$mm%_xc1jAOFB_8lkf~;T#Q4#aKDej z8eXh@_SI~{-nqY1r6SWB3R$h09ohVZy??LF565a2)v9?&RP}M$z0oX?Nrd*88l>sa zz3K%=0KNrLd&Q|qvbWcrns9SZKHHu*jv6D)v1~rcdg%S%OQnfSz6qtvlTe*S}nuq0FWS>?x zl^Tx$!6I>2_Tk48%57Thq1b33WMd18lC#-TvpzU$QaG50T*Dcl73Q-V`06m*+?kk6_Kc;Gu-TRSlRV^*@)MLg&NeqIzf8{d4kzm$7`}OS z_%le=POkhGK6s9vdgap1Ae;zMk-kDeV4zy|cO3QLlWzo#~8cu(1LPJf_<9o+TTfvb}Hj zfXGnwY2hfcpdE4jiF2Ws3VCXv3pT}DxlNI0z+NI0`8NmHp$Tj1kLYm7o$c^46}oMhIMtx^EUOldxif#^Ra93@-*OHLwu5$@VG2 z3=0zxPbo{_2GumI`+Hkm9u`-#(j7`yviudrt^oRQ!B5ZjJ^(FJIoK| z8LB)?`M`WHgw9E}W)THAQSuOmnreJ3G~?)BVJx~fnca#E*+PH}S$@*XH+&Y!;+J`ysciPo z-D|G%om@1_g2!f^5vJGf9fCBUdOnmPZ~goSM~@4v-%?J;R}w51`)pS3RH~A)D5ZY zR$W7xc;5Zg^B3t4jM*5N%IhdUX?`TMtHNl z;C}b=^5;Igwk`LC+CoWgY70!iZew?0DK>4;7`k<00wG3g+k-b|!{? zcB95_$}WR5F0U;>FcRBt&{pHC8#ZQ4Vwqo7TXh#|YdJZ;QBx~VxFHG7>lZ9I8;_wC z*k~K{>aLC2IZZ}`%N#$endHYtEkTlOFjuEvF36N}ED-It7vu|!9~V{Y&edJdL)vW6 zn6mYz?ionZ%V>)Ni=bhx&lh){UD}|f)7VeewsYaNV;^?Svo)&`B01la0+xh}{T z0RDn(+^AzD**0kH^fH%mtJ}ID57KtKVM80vRq`Bk(+1QX_!{aC7;70FrO6w^EuDT? zKmeWv07o^yTs0VSu$zJlhTx=%{+Ki@Ipl6bytIbAQ}jBU6GVg7=z1Za-#azfx$<;s za8Yomkdzi8JOcL&GUVj^5r^Z*;=K;Xy*YGiB9lpLb@j%^)6M<&6Sd2W?E8Rsz~B7h z%9jg|riX^7r-z58m4DAn^!83AF3gOxh&?EkP8jT1b{yT`T?bmz(L)iqB|g8 z1dgh~+v59HPEF)*Ri2}ikn9OhArS;_u4rU0yPqIny5Z9L>Fy_U+@`APtOlI>slMpi zmI|4c0A1j@$Au<&+?3Y?`zv{_evm4+t=xy@()ZPg$6lXQ&he}iV0Gf9924^UR*|wB zuWq0CYVK@S*iG3dKi%CoLN)M|!>VnFFc^LbEFr>yXoRO>3c5rR*?Yw>6}TK=q{f7j zRW!)CEu8|aEw6e?P9BdLUQvV(pw6IqHMDIi>4CSO*BSJd5cV{|#R6)BRjv-U4<+^W z;a%CG?qaP>u&Z)nvZCi*-(?ziQ1&u zBz{<^))oM5scxt`7UW0sR8627eH9{A3xUx{pMpyXgHsDrKGL8uB*2)%`3xryZxACu zE*b)bt)bhk6jzgk1;h$4KFWVIdk}Avj+?EPEeK1D%R#=Nkul2d5$lv+^K^-~7yWHF zDlJ$q?Q&wC~nEXheU6W>zlPYy!s~usu=g* z(*}@eN`zSaj$D1ujVn(x?@%(lb@RZD0W&;^%I*(~CUa6N>XOoCGlrzb(~Q^~jF+53 zmE4pTmYzYagz>8Gt0%Ny^1}+H8irQb;K=4laZ>3ZR)_k4?rYutq*eHCTVq>3+}QH5 zR%ctjr7=9#(%80LcyQ$xZ+*4J9nQBjw2iW;vt_KUv16>cv2A8$+x%Ok8gm`;WzAmH z!nOkC5?C%)*p1R;@1=C8QsuLnL>it(k{U2fsPYEUBosdDjn_a5rHC^{FeEW2ZV=og zlev&M6sy}qd5q*x3J5O@wV*zlH`t5V&(&A&yIPY@$PvxeIx#P7 z(z7{pyjE)vmqbIYv7up`ev_CN4OeScy|0(9Yh{6Wd~w79xfE*coVj;qG1E7(Z+tvs z5@bZE>HGDDW0Dr4Y&X35;%=?U7|}|{bjE(sDLV8+I>Rwh2$?K&U`XnhvPV<+lG z-+F;DslYw7!hX|%a0V-K7c3m_<6^@Try!j*g9Dp(ox1N>;4}@YLfW&zn0>p}IGDcH zGf`hZ;kmYbM4vVK`z?5`H(zuJnRe|M8m zH3Y2H&w$fEC~ospCe1Z*xOkOV?fg#;sFq29fc_amrWyQPmu{^Gg`3|6N>IeUY(z?!THJN7ozq8jF0H{J0A{1Ax0ebit_r z&z5V&){%6sgFcAZ818MRv>wzTbHjHEH>UOL6$x?=5ga2MnL9YQSPU{%_l=dO^02@2 z_zLo!Z$|7K`yW|coI9xeMMYECj{X+a3jL_> z00(_JK@UD@%F$bWZZJarL;6=7`3a1)(1A*8M6%q0)rOz^nZ?B=ETLNER;WeDGP3ehI}2 z5eCePkiTT28VNkeIw#Bx>~4_Ra8n_NmO^vMZNf3X5V%5~E3N_Vb_u)pDbanFp&0Wh zozg*zV|LKhZ@Ep7jW!t-QH++TQ<=^&`@n2IMoQ`K5Y*`Lz3ipE&bY_K@OD{#S{@o2>JM~_gK?xkA>&LYWJKvs@r zb0)nZkohf2yvG*zVEK8G!p5YR8GR!rp9^^ zjYi~%_Ede0&n&b+=)iPzK#{_n z0r@|OyR?#OV&SvR61IRVYK#If20^y1%5KRiMI^m#3JA+X_KAUkTxKZaO11V6iK5sS zbvQ(6B(gIUh-i22D3R|8%xO&_39O*}z~h_oL=3XWU{rp{fG-x6TzV&Pb(l0b2}gJ9 zu?IU_WkiEX5$8xG7q>g*p@_0~HsmsS>_#~zJ5AekhA6VuiNBu&=H3cot{M8kq0g8?MtQz{eKCx{w97!Bfao;hAu9Y8>`MU#e}mZ$@cB?czQ z{%P#a5p8~;IXRdfYip@93R=C%?(f)&Gw|@%j&5yJdL%fN4%h0L#n@yEE6?fWA78cY zCapO=rCkjAtrm?hf6qg=d(F8vDLvTVJO8;8O@3dyug%`LVzSE8z%AbTKk5wUdfjc_ z9K3~rci2X z6V&(0=d$Ux1zsciGZfUfP z+&@z<4_sB-@G+bh#o-U*9oggW*wQ4A+&^tKf9R^(dj8HI2m!dU*x#smitOf!w}yxM zBVPU`x?JH)A2rbBO7HN?!Dzd@L|$CrR-=!opn%xntAH{^dLY$01G#Z48Dzv7ddj5_ z4k#oOiNCSPA%ETjfiCUz{Uc_lG4A)s-tbjJ z!(zDJFk+Yc1}xU;W216Y@tqErC8TCGvf4YlzxA!mr^g5C4Q8iU+t}1#a*9CUx6`bKY=v31(X6`GUXfx@cz=CF672h%PE^qb%c6fRg`BPraxQTO<`#`K?0&z0Uvv2g0xgP(U_0N} zj6TdEiPfw#RY!gS@q67Lq)b9c5rT>Y;r+;@Z-w*klXLUSD1AZp^>Y8P$9@oNl==q& zP2tg4riGm9_t)L~=aDXdhHl!u@3-Gb_dk@6Aom|B|Ev2CYwQQnf1XWQgngs2Q2*np z_csRYS}InKjB%7fm`9rU`x^)4gvAyBq%`1>LU|OPB3?ICeHt|#{~9`mkC3c*8VflJ zsu8%&B`!%_9=22Nm7glXLcxNrnWD{@my+cL&_!TRvzOK5G+R2i1JT_0(R@|CSCVpf zvaSYcI}MVY?3U@-6-eG`)ZrP6sYa;NIZ8jLvb%KGV%9ZwHHdB9ZDK=L;}w=wPj%24 zi?Jc~h-?l4zp5L3pffl;p*)_LP7A5DdaG6kz*&&vdqYLw03GfuL%e%6VqV<{6{ec# zXjr=b!!0BA4Sx_F+$rT(UTE$;qs_2C?#*YkXI8d{r;ZqzU_3GvRvx~iu?NOy;ghFl zue)ycbbfHg?{@oV2J>4ZpO_4HUDurL3QsBv`I{#uZqA<>N&=3hjJ-qW0G{%`;(bq3O`bdkZuDQ9+G&UX# zj^qCe&CN1iVDg;$pw5wN79O+C&+!*!1S~$uKQ}(USchl$>^yxQj}b#z98VK)pq$7t zzY?haAH|rOoxpI$(aPLG`K$;T2;z8(0^(u~j#}%keh54>59X$cNLIr#9|)3`o(u>= z?#|KAPRyL?*ml#lN53&Y+k--KzZ%o$3@Ky9qMx77hr{`>qSudgr#Ii;J3Bw6@#J-X z_~rbzM}pI*JCfhI@w%sORNo!Mn?u1|bGv(eDLntt{n5T=X1(WP}hEjjjov3Lr9i? z^StXb4N{~4P^r1)v@(11)M>VGGrI#NUmkdeJ+w0S&g#!2V2YCQYJ!^>ZP@&_4fT+$ zX=?)xUBz207+x5Hib53ra_7Cm?g%$Gqlm}}Hk!`^h3alpnOJV7k2j-&9I7`g;V{5P zQO$C`xfwF4eC}x?jI4CA3R|5!NEz!q(G@3K%8sC9G-R--$|3koE%kyT-}&VR82Jnm z&{n9{i>-yloc`a-!EG>>4|igQ;jx5Oc8lYC1RhCbAFsV-K2 zp=kiJ3Nbq;Lvug1R;=s-gdbk`Wm!0my5l%E<#W8&?F@a0ii+p*g^Dlaom(WKSJ1t> zs{R(ZW}oDFG-Mwt!qAEUs3!j8Hjt!0ZuR1*SM3RQfQi|^X@kL`kBnW}T*r=<9DIKUG{3b2k~O-HR4`88h6BM2H_rc%%}?F8VaOe1NA zr#n%xd6;JjX}?fszFnDv_wXm&c`Q`j_3kU0X84X>KpsGS49qgDAsBN?bw-IKLim6m zu7<`2n_NxNfy6$w*r=0qE$+6-CU%ow*A`x8=>|b?+abnR{-a(Ngcg4oH6ifFrsnGd zO?_8xk!3dTjPR$J8Nx)_0(!7A{Rj`# zV6YkPh5nb=4-Oq#R1Tvw(Db20LLUZgUvA}f!J+=s_)+}pAAq<2NBKH+7$_^QVZ}Yb zeY{E0e6nindd2vygQ&Kqu6)Q;DR}g`@E-jSPgfc_5UVPsTTJ{OgDz6-T2}C&Y ze~jAC5jFIWysM8w@3|IwPq8m`JPxFY%hyKeDs)8(f~)Q@Hw(!Q)XeM=01pXa(}Msq zNBAba%4CSIMk151a45|`DTd#C1Y#0o%@Z(0(1}fl)I^Fp71P| zsf}Jdg7RETOF64)8O9R2St6lT1OE6l_+vHQXob%VMu}=y66Il*gs!9hVwP5ZPW{gb zPR=#NIrvxm0C9-2L>LuP1*s3z#%dl^=dIL+7uTnDxJ1TCQS4ob6L4yO@eqCr+;HsJ zvDfdTpZu|7e0;u+9N%-?2UFy4@FZ$FAV;#cgZN4~M{@^SFlCr9;Xl>GXq>qmB0D7xqC-#Kfm&t;G};6AasyDGqgSOF(~F&7VD%|G4O_( z0j}=0w9Zj=jsKG#N-Q9}iQAPkoDIQ)!b|n0m8U6TP#84TuRKM7bSKLk*iEk&Mhw!m?asu^i-N4#XjsQ9v%^>%uwMTh#B-sG`IhRy>4<&{Fy& ztEs$%x{qwediqba#*v9=4q!Z2b1Th=Aq&!?R#yN{0+=ZEAm_$>u{#Nbja+QXtIJDB z>t5pZ8D{Nd?E!j%x(r-+@3PC1FD20D$vJMT^!F zEm?xf2vFtB8e~b3)?5t=thC6%KAK>md9$n%B%9lnUJSYL;F;w!Xg;^R@-zy%3ZGO7 zIb#lO`V!H0w=QTt19EcYZ49HB9&{||orO6dDUOIeXp$sYAcQ-h9|8pI$z@W$*$C<* zd}=ud#ceqU`9$+q{w_r_t6ER_+aOkR%X|)pDE{@O9IL@uuK0brK>7P-wQcI_sta%m zDlWlEsJKYJ;u?@I1scw~46QlpOE`bJKn)HQDE9z*DlbMfm3fk~#<0(N0jGr^KU)6g zjx;!p2hw!_AFdo{HI|nQ@vR-3JD_c%v&(n#XHq`=I;@Reo0WYEfTHe3HVRpxoIuua zLvDGg-baRa8pm>*O*pwCCJYn4eyjW#bs6o}(a*T!Z(-mSf<>zT1}^d zU)F7XBd*!7{W9CEBA|kGD#lh_`y@ALY?CySm7IgacO~EC3c{m2HxzqNlpU2KR~EpN;n>$Y9N8|B8!oKc?F!Wu5{MR^og z@dphaL<<+pCE0@Q6F=%EQ@&n#UQ|w7d&MHZlrLGyGo@>;;u}>iRPfC@7cKBlvVDW=Px9KFuVA< z5T8-KfK~ez=kWC@*@)jRKuZPBsfU0fF=f6h^L%mNt}X1L>b|Y++H|l?mI^It#wxbz zaB&Z3Bl+;kBPJ_0ZW}gkHmdH|d?Qs(=e!nukyU21nZ zH(zQN9bVfm%+bY$%Y7~EJr`_vO^?@adR3R#X}q%A75!HEkD9+A32XX~aS!Lq99J3v z&Hm>v3vq5*K0^1&E^Z!alF-M8Nx4W0gcytg`YL zlx6Eh$yhTMm%q+d5wmJc&gb9quA{EC^9QDMQu?6yw|I==>|3UF{S~L4_f`2&#D^jp z3n#0MIQhSR-K`1R|`(oetVH5k|xi=!vP?laQgsO0##yhySv2bl@?`y9Or2FJ~*ZjV6Vx;nd zWTK?Cf=a}Q4f$`t``wUMf+U6G2uQyOT=ne8DZf)vLevo9w0^x<2O2>aJ6ARE^Hs4J z+Nv$@{#x@_nkLw>$Rb7BAnYU71ak7~QfUpwhVaXfOR37Gq&La#f~#ElGgkLlQy7LV zc22_ZEc3Bpo=;>YiTz0V=LRENT#32a?SE}1pHE)3eQl|7X#Pt0ZJ{mf^<;}Vzb!=Q ztk=do8~tDBU;h{%Fl|-+yZR&Qh>y(fTc;mvo}-XZsLIt>Gu!#ig^NHX z_z31nQ}zDu-eOYW_ReE<%)TUKV%OU;! z)yd{B>2*i!K3^gbU_b85=d-OAL?5(TTrP_ew?~}WfX5LV^s$|Ko%~zhznCK2H{gb? zjxe#QelYAJ2ysK`ZX(T+S0Jg&Q-hE|#5@K8N&)|DDgiIK6>)(GZv4yW9|0a&5*CzY zW%<`^!T%}G3Ab1;q-OXVuYb|`#d^_w!aeo%=(NscmxO#qd51}vHx}%Y>j~#n;E5Uj z#*c1w-s+ojJ>imQ{L)o#X+AI5Fn&}U9GJF543eV!#Zb&NjS_|CQ)(1J&cY1?(<1!F z0XBt7p!j25K#s$sZ&0B@y?NzfvtHD>8?`#EDb*-UH>PXxkhMdgCH;YbI?@X|(NIGb z!$sMex}z^T^YL>odoGi{z_MZp|tW zk^>Y#l;922bFFGb?8m>s%jx4t&fBX#hbz{=xeHf+Gmk4pQW7|TqT>NuQ%!wFxI+NR zhP1jd6SzTuIuF978+awL-gzAiinqn`N3I@jQ1w|IIDQF=U@g5O82{AS-O}#1_GxdI zt{duVLuI3PT6&ae6gV}WL0u7d)8;zEfF~gNjryPnynbWd78oqoeKk2Wv}0&U_)PC7 z;cOi5Rfk>p&3(@9))`NHa7LB~XI#&E;?c}8AdLEA2lXKlc>5+qr-xiFgUs9U8TA1X z^$3iz(HHPI^(Ovmz?i3N?-+`V?;pos-j@Jp!&oSkayPbM6sy=8dpXIc8o2FvSZznEIW_`UjY?a}Dr1enFQ>g>KJUoEzTJj!MdI*58 zxSIN!_c8>kuce~f%G0Kb-e@m<5LE97jPm7+&6m3szO-?uf&{2~_ddj9@8_~00uLco zG99V{G&*38De{d&azQV|0;%@!j1vULkl};>t6?*xvj-MUx)`xs4IcnJ4}~x*@;>C@ zMp#64IH*=;kQFn~h5bCA?;4mq{%#d`V4%JI!vHpi7Xpt~u7U?*QPZ~wUxa2qJSqpaZW$*GU%gZMNWf>5|(RUhrhR}LVrrW3uKS+AFu z*cnP%pr8S07(C&+PCYo1S_Vi?f`deo5JXNzr&r!V>T!E86bgs8qC9iE-@kHCxs!?> zU1>h4YmyIIlO0}rLy~|+1wtadd&DyADt=pRiIp>-Wyx~{_kf$k4G!Z z_mg6Bzkj9!$ZmkmO-`@ock*{bYXT@1<`e1$j=t6KT-9|j@2sjqAiEkNEMJ&W#%6?1 za(!bt4{2+n>r|KOt++jf0xB2~T!*fF|{_NavbCcfPjMv(OdVflJ2us@o51o3)?t`}u zPmN#;0$^>j28>xhq-ipEV00l zLo5%m80l5#_Z@NJA8g;Dk-hgm{|OuUAUd3+fQ{;^>ygj!VfZNji{=^0EoBYywf8*F zH&g&deh{61=o;YT6$+}Y&+mWy!Ol4nV0hL#QqSSawUn~f-v=Mmua~C&K@84`cBH>I zvflAt7HR85CN$|@v&erQL;iD-Pd-((^8+3sq!1!8NX2wTecieQ&@YhUPA z*G{aj!HV^T-S-2PC3IZ1vR}UEmF2jht^aGW4}fh}?w)V!|7uSa^vfb%bO%*VCkz-K ztbkZE#2{^e0}rL0W*?uVtrtrLn-(c3I3K^Mci02#A;R|&X8hnJHa|p7p#;ipsl^fvGZ3-+)JR3BQF*)K(iMg!oR9Ku@eUG3&7y*Mr8OFia}#k zHYQES+yF35I?_0lUu^Qtnm(Nv9gj#-LYh>@l}Y8tc2_Hh^pExNVOz@Pv#3AM=EO)o zB+6n+96C3C?t99Qjq8mn$#WXV?_wOUtTI!UUNw)DCeRwnKg2Cc`D|f`q)}@UsI>sN zPE6-Ml-uL#EmCjk3xz_jajT4v3ui4dN)L4UEFrxiWC^4^mVP5vw(UFu1#mwVlQG36UokC3m6fDR_@Z|-yXh62r?Y7%&CVZz)5Td{% zD)Xt1S01$nc|^T6Uc+MM!eX%&(65mde&H2n5zfvzruN5L_fH8(CgMPg+U&>a}Iz;EnYK#^Q>3Kv!doI*cKv=L@zFFgiCoZXAPmt+&y;aAJ1>J3Cw zC4)jt;N(0|x5PO=+j$O|EF+DxV~T+rA!e@EBZlfth^djJ8H#QA&LAp=rPqnBwYm4sB8>fFhJHt`bB4{ zvY`4KB;pF$04%5>JFFM#Lrsz%3HpKQYn1$g(ktAo-_+=9i)DL;uDtOoo!&HIYPCId zb3YZ!5+vn}mF!C&QvS2M;{mh$=uJJ}?fuT9hX0?scMor?yz)lP)eD8oqkM*$?55M;?4Q3_myNjaYE=bf1Hz8($L0ms_8P&m(`&Yrc{bE}|MjD|u9Q-j z&0Kv$&&0mD(Z;%5LvTuP?_|1V?q!2PeeHY#;Cq=;jt}Jl5_9bZP!kaZfMRVZr zi&0sE-V=!3E#yy}Iey~A3F#`bXi;GtN>$9!B1Cb4NYeIHWTLY<~+B86Nhsr@dhuZ6r+Cuny;S6DF7>O-#46 zG0N&Am26DR%PLD!c zz-(?dheGDydNV5SkiW8oL+L=?Lr(=%K|ArKqn?}obPl!l^!B<_Obo{EO`T1d^`YRxqn6lcT_Tk3*NMz&2&R=IOd7Ju6I*xp&ruo^dk&3H5 zr90H!qSfj8I&Yp%)T_T`L)mVt?MVZ(w0g{kB3pZAdfdL@)*)@yl(Rnem{ooDkd$xv zzV(qutovFUEr%j#yCn8RJBxcF!a_wp9_Dqx*Z}AU%=%;1v{9w=%wp-Aa?;l+1L$rLwP`AG`@9aT>86+Z(g0r zcQ#5HUxbRrmWqC^rl*xh^q)l!=Gs-HVB3RwbTOU`M$nc5#6`23F4Wwn9xRA@9!B{b z!A$wrq^@}dU7(Y)WRM4PutXy4ODknY*-UXl$2wbXNOpkXImxQxvZ!d!W!MiDjVuMx zSQszl*q~|SENUSd6g%HM7xYHenS}-xsfvXU!NdJ~stW97Cr&?L4f zy{|q9F#^tw`RMaZnVRR+pCFuDs?Wb3Nq2UpBQ%bdES$)Bp&qs@ z9g-0Cz{vGO<5rcXYCgrDMH;FkBjHO33E3fDWPV1Gyd&gw7MW}h31(?ySxyii1P8N6 zD;HQ%W`5+*J&GJU@{&jSd`4t&fK-A<>L$|221Kcwv~6Twy8`EV%nQ6avJ85=kW7SO zBHXf?DVy8lc-Q9f*fNuijoonBFYT7zZ|!V%t1}UwFTxrn%24VWhBqP0GCs#+^n=H2 zo>WT;R+m)D;c?fb*zKR!*3>q6A`6{9$uY?85FS(7JNQu>_EFS^^>VKnWz?>s7PbH) z7u#F13jDVd9QHc)*<1~HYG8Arez&K^j{I&UHgR8~g;171V{OR57hw#Io zL5w2YFh|N-I?P^MM$B7!*y?I)(3vf+^e5rw!e91stI^2TndQ2s+q5tAclyx4l6(0SBv9 zW~cFjs=hC<=i8RfNCC=KQ@z3~&mM<|FB)i)7OV(2SX7U2Uo-bOv(b|U^{taZa{F-) z9-gTW^&t0e7xGVS$h$>k2=WS>V$e*GvEPk;4S-KXDsYcjQ9VR?CsNMiJ?we&V%X~q zL(%XCL+XQEm8dapur+8i?AoV#lDEAj?Nd#F=IdODcsvoU`K8_2Eg8w!RPS@Q4n6dA zqVLup(R{INKcWhUsF20vupOALP6+6vOWsuLm{;HF(n#uIR%6Bn6#p;vHLV`lmE_GtBU)m8}m29WWnQX zeu6j~vA3Wc$_hrgj%?3o}q0QCDLME==_K#of81>i*Z8TRn&?V^VjcL^71%A9x%7 zfoAk-H|-=y$Ez%@#l>aLUd1haHd(rx8|#;L(YhPlJO`y97=p3T>nVg`djDG3E1@E! z`eUqJenh3*X-RLtuXoiwJf(X8be%4XAe@Mz$1}nDjL)n928k>Tvv(|3ho#r2CJI=3e?KQxw#fkSK@)~L%t6@k@C`=69lm%IpAjqwxbQ|oR`>+HiY=9 zO^OB%&Z3PF*SY0i&{kS6NeNpfhpQA~!34DmAWZogu51*kdl?Jm=XEtjqVgb>o%VD! zw-(pRB7steSRzA8i7L=bl-dpKuBAsgog_Om@BZ05B>BS09(q#WzUrm}bp z9XLkzCwo&h^$fkO3Lj}Az&IdS5S+kc1L@FJ-5$AH`oE&x%FvE0KKaS9t=Q<`=-AH7 zM{f-Gw10MJ?CwpQ?j9L=B4=5r9ou>Lru^e`*BTA$^URN!_7P*s*pl&$dL-kZvH1WF zje{m&V!UljNvba;Ns-jjQwFotno6~5O$KRWDn-+C1!GgldmZO?w+cXSRH3W+Ax8E> z5ZFkINCy3{U~nl(^Bd-n-ikEF^*0c!z;j(GTGFS04_M)W%M^#*0?yKN&k=U5M#<$y zrbb^Fof^sI#&g3j4dXdCIf^r*lch5YG#jved0=(6=6kxkdlr`u0cZ<)rHU&Nb9fzM z4%hKHpd2p|i+btKugkycp|7tyg++CHkyVj6*+08m?X+c^V0673W&3~E}|!@5L`k)v%S~$;!i!wU#0(A zKJMk8k(`&~xT>)Vu6ZSSnRo&o0an3TSMH^ig^kejxi`sYW9re;7pQ-ha;<64UIotz z^4Y2pQ$k1x#fTz`B*Gj_kd3|4K_CxN_}(jq^@}D0>xKF#ohpQoTfNg{NTm!Wq}*{- z#nsnd&#&z&UYl=4V1Vr|U6(}(xuVL;umA2zgK2TSWW}Y={Leh_YL{NZEf!W@9`n=; z0A@@?KkNi|qF;*nzMXsiiYBKf?8Ags5P){D5qsnTMrt~fLrvg-8JgbY+1A%xv zuyhBW*=DY|@Jd4m#OJRG)qy=KR0*~C%>eys1x7GJtBb(;7#BraN!D0yMci7IcXc%} zWoe`IK6P+)2Ft+Ec1(~%+oe-D`(*CSap_?33{IoJ5KcqM=3<3)E&|OK=u_2{mu^Q# z$J{IO$o;@;1HKAopx9wtzXTvSI%RhM}Y}95m#NNn?J+ zaaF>QSh^n*oOG4?7DHrH0boZIHbuc*GKX!mvQu-MzNz4kg~83~j$}ds=CTD;?}LTuFMTrY6xdHk9jw(+ZO5%MFe7 zB(^T{Qopbl74K1vRX@OSO1Pm;4{H$Y4;1&NV7kXJr@Qf+>#)&WeMhV@+lLCNf%I&w zEuCqL&9bRAZoK8ism#)W*>v}GZek)g-JPCg-xE~O_s_{y{MbE5<9Rvm&m~xi0zdqWg6gf_t&8e+F_+}w|*_U^2dL$F=ND{|Ma2yN` z-ke!=eX2druMg}v&XP=UtLNOw2m2Vm(bE4NZOcTJP<5c~tXrmM#V!4@>D<+Wmu6!Y z?b$cSBqvpT5i)KYny**txemg2g2f7$PUtFx)FvZq1pNdcZNPLU zcS5nd>Zv6=7&lBYa~iGEJ>B@S`bcJ`^K{K%Hr42Mdpv;FuJuWqd@XJd%CPoytG~H9 zk8id#zkF;q-7fh>n z?p8Pc)E|l0mc3@8`39qWGGX5Dx8WvE)tkXy`%U5Y_+1$TFKW>+7^-#x|*QAicYGjO3?zNGrF|f zK9$*Q9G`I^6>nzTxH;o;Yq?o;0DTllQDNzaNE%T8Oc)@)3X|G=v%_0sUf-Y3#HU=J z$-AavSyZx_$0slWne|~Wfk~Yf0iZO_Z$V9iOM$ym2YspuqY2b%0AEG+1W`K$M@Z$+ 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 z

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 literal 0 HcmV?d00001 diff --git a/javascript/progressBar.js b/javascript/progressBar.js index 3da707627..5d8e65323 100644 --- a/javascript/progressBar.js +++ b/javascript/progressBar.js @@ -42,24 +42,23 @@ function checkPaused(state) { function setProgress(res) { const elements = ['txt2img_generate', 'img2img_generate', 'extras_generate']; const progress = (res?.progress || 0); + const job = res?.job || ''; const perc = res && (progress > 0) ? `${Math.round(100.0 * progress)}%` : ''; let sec = res?.eta || 0; let eta = ''; if (res?.paused) eta = 'Paused'; else if (res?.completed || (progress > 0.99)) eta = 'Finishing'; - else if (sec === 0) eta = `Init${res?.job?.length > 0 ? `: ${res.job}` : ''}`; + else if (sec === 0) eta = 'Starting'; else { const min = Math.floor(sec / 60); sec %= 60; - eta = min > 0 ? `ETA: ${Math.round(min)}m ${Math.round(sec)}s` : `ETA: ${Math.round(sec)}s`; + eta = min > 0 ? `${Math.round(min)}m ${Math.round(sec)}s` : `${Math.round(sec)}s`; } document.title = `SD.Next ${perc}`; for (const elId of elements) { const el = document.getElementById(elId); - el.innerText = res - ? `${perc} ${eta}` - : 'Generate'; - el.style.background = res + el.innerText = (res ? `${job} ${perc} ${eta}` : 'Generate'); + el.style.background = res && (progress > 0) ? `linear-gradient(to right, var(--primary-500) 0%, var(--primary-800) ${perc}, var(--neutral-700) ${perc})` : 'var(--button-primary-background-fill)'; } @@ -106,7 +105,6 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres debug('taskEnd:', id_task); localStorage.removeItem('task'); setProgress(); - if (jobStatusEl) jobStatusEl.style.display = 'none'; if (parentGallery && livePreview) parentGallery.removeChild(livePreview); checkPaused(true); if (atEnd) atEnd(); @@ -114,8 +112,6 @@ function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgres const start = (id_task, id_live_preview) => { // eslint-disable-line no-shadow request('./internal/progress', { id_task, id_live_preview }, (res) => { - if (jobStatusEl) jobStatusEl.innerText = (res?.job || '').trim().toUpperCase(); - if (jobStatusEl) jobStatusEl.style.display = jobStatusEl.innerText.length > 0 ? 'block' : 'none'; lastState = res; const elapsedFromStart = (new Date() - dateStart) / 1000; hasStarted |= res.active; diff --git a/html/roboto.ttf b/javascript/roboto.ttf similarity index 100% rename from html/roboto.ttf rename to javascript/roboto.ttf diff --git a/javascript/sdnext.css b/javascript/sdnext.css index 1c3313ade..65152da4a 100644 --- a/javascript/sdnext.css +++ b/javascript/sdnext.css @@ -1,3 +1,4 @@ +@font-face { font-family: 'Roboto'; font-display: swap; font-style: normal; font-weight: 100; src: local('Roboto'), url('roboto.ttf') } :root { --left-column: 490px; } a { font-weight: bold; cursor: pointer; } h2 { margin-top: 1em !important; font-size: 1.4em !important; } @@ -72,7 +73,7 @@ button.custom-button{ border-radius: var(--button-large-radius); padding: var(-- #txt2img_footer, #img2img_footer { height: fit-content; display: none; } #txt2img_generate_box, #img2img_generate_box { gap: 0.5em; flex-wrap: wrap-reverse; height: fit-content; } #txt2img_actions_column, #img2img_actions_column { gap: 0.5em; height: fit-content; } -#txt2img_generate_box > button, #img2img_generate_box > button { min-height: 42px; max-height: 42px; } +#txt2img_generate_box > button, #img2img_generate_box > button, #txt2img_enqueue, #img2img_enqueue { min-height: 42px; max-height: 42px; line-height: 1em; } #txt2img_generate_line2, #img2img_generate_line2, #txt2img_tools, #img2img_tools { display: flex; } #txt2img_generate_line2 > button, #img2img_generate_line2 > button, #extras_generate_box > button, #txt2img_tools > button, #img2img_tools > button { height: 2em; line-height: 0; font-size: var(--input-text-size); min-width: unset; display: block !important; margin-left: 0.4em; margin-right: 0.4em; } @@ -96,7 +97,6 @@ div#extras_scale_to_tab div.form{ flex-direction: row; } width: 22em; min-height: 1.3em; font-size: 0.8em; transition: opacity 0.2s ease-in; pointer-events: none; opacity: 0; z-index: 999; } .tooltip-show { opacity: 0.9; } .toolbutton-selected { background: var(--background-fill-primary) !important; } -.jobStatus { position: fixed; bottom: 1em; right: 1em; background: var(--input-background-fill); padding: 0.4em; font-size: 0.8em; color: var(--body-text-color-subdued); } /* settings */ #si-sparkline-memo, #si-sparkline-load { background-color: #111; } diff --git a/modules/api/api.py b/modules/api/api.py index 6cdb362d5..29689ab85 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -356,68 +356,54 @@ def extras_single_image_api(self, req: models.ExtrasSingleImageRequest): def extras_batch_images_api(self, req: models.ExtrasBatchImagesRequest): reqDict = setUpscalers(req) - image_list = reqDict.pop('imageList', []) image_folder = [decode_base64_to_image(x.data) for x in image_list] - with self.queue_lock: result = postprocessing.run_extras(extras_mode=1, image_folder=image_folder, image="", input_dir="", output_dir="", save_output=False, **reqDict) - return models.ExtrasBatchImagesResponse(images=list(map(encode_pil_to_base64, result[0])), html_info=result[1]) def pnginfoapi(self, req: models.PNGInfoRequest): if not req.image.strip(): return models.PNGInfoResponse(info="") - image = decode_base64_to_image(req.image.strip()) if image is None: return models.PNGInfoResponse(info="") - geninfo, items = images.read_info_from_image(image) if geninfo is None: geninfo = "" - items = {**{'parameters': geninfo}, **items} - return models.PNGInfoResponse(info=geninfo, items=items) def progressapi(self, req: models.ProgressRequest = Depends()): - # copy from check_progress_call of ui.py - if shared.state.job_count == 0: return models.ProgressResponse(progress=0, eta_relative=0, state=shared.state.dict(), textinfo=shared.state.textinfo) - # avoid dividing zero - progress = 0.01 - - if shared.state.job_count > 0: - progress += shared.state.job_no / shared.state.job_count - if shared.state.sampling_steps > 0: - progress += 1 / shared.state.job_count * shared.state.sampling_step / shared.state.sampling_steps - - time_since_start = time.time() - shared.state.time_start - eta = time_since_start / progress - eta_relative = eta-time_since_start - - progress = min(progress, 1) - shared.state.set_current_image() - current_image = None if shared.state.current_image and not req.skip_current_image: current_image = encode_pil_to_base64(shared.state.current_image) - return models.ProgressResponse(progress=progress, eta_relative=eta_relative, state=shared.state.dict(), current_image=current_image, textinfo=shared.state.textinfo) + batch_x = max(shared.state.job_no, 0) + batch_y = max(shared.state.job_count, 1) + step_x = max(shared.state.sampling_step, 0) + step_y = max(shared.state.sampling_steps, 1) + current = step_y * batch_x + step_x + total = step_y * batch_y + progress = current / total if total > 0 else 0 + + time_since_start = time.time() - shared.state.time_start + eta_relative = (time_since_start / progress) - time_since_start + + res = models.ProgressResponse(progress=progress, eta_relative=eta_relative, state=shared.state.dict(), current_image=current_image, textinfo=shared.state.textinfo) + return res + def interrogateapi(self, interrogatereq: models.InterrogateRequest): image_b64 = interrogatereq.image if image_b64 is None: raise HTTPException(status_code=404, detail="Image not found") - img = decode_base64_to_image(image_b64) img = img.convert('RGB') - - # Override object param with self.queue_lock: if interrogatereq.model == "clip": processed = shared.interrogator.interrogate(img) @@ -425,7 +411,6 @@ def interrogateapi(self, interrogatereq: models.InterrogateRequest): processed = deepbooru.model.tag(img) else: raise HTTPException(status_code=404, detail="Model not found") - return models.InterrogateResponse(caption=processed) def interruptapi(self): @@ -473,18 +458,8 @@ def get_samplers(self): def get_sd_vaes(self): return [{"model_name": x, "filename": vae_dict[x]} for x in vae_dict.keys()] - def get_upscalers(self): - return [ - { - "name": upscaler.name, - "model_name": upscaler.scaler.model_name, - "model_path": upscaler.data_path, - "model_url": None, - "scale": upscaler.scale, - } - for upscaler in shared.sd_upscalers - ] + return [{"name": upscaler.name, "model_name": upscaler.scaler.model_name, "model_path": upscaler.data_path, "model_url": None, "scale": upscaler.scale} for upscaler in shared.sd_upscalers] def get_sd_models(self): return [{"title": x.title, "name": x.name, "filename": x.filename, "type": x.type, "hash": x.shorthash, "sha256": x.sha256, "config": find_checkpoint_config_near_filename(x)} for x in checkpoints_list.values()] @@ -500,23 +475,13 @@ def get_prompt_styles(self): def get_embeddings(self): db = sd_hijack.model_hijack.embedding_db - def convert_embedding(embedding): - return { - "step": embedding.step, - "sd_checkpoint": embedding.sd_checkpoint, - "sd_checkpoint_name": embedding.sd_checkpoint_name, - "shape": embedding.shape, - "vectors": embedding.vectors, - } + return {"step": embedding.step, "sd_checkpoint": embedding.sd_checkpoint, "sd_checkpoint_name": embedding.sd_checkpoint_name, "shape": embedding.shape, "vectors": embedding.vectors} def convert_embeddings(embeddings): return {embedding.name: convert_embedding(embedding) for embedding in embeddings.values()} - return { - "loaded": convert_embeddings(db.word_embeddings), - "skipped": convert_embeddings(db.skipped_embeddings), - } + return {"loaded": convert_embeddings(db.word_embeddings), "skipped": convert_embeddings(db.skipped_embeddings)} def get_extra_networks(self, page: Optional[str] = None, name: Optional[str] = None, filename: Optional[str] = None, title: Optional[str] = None, fullname: Optional[str] = None, hash: Optional[str] = None): # pylint: disable=redefined-builtin res = [] @@ -553,7 +518,7 @@ def refresh_vaes(self): def create_embedding(self, args: dict): try: - shared.state.begin('api-create-embedding') + shared.state.begin('api-embedding') filename = create_embedding(**args) # create empty embedding sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings() # reload embeddings so new one can be immediately used shared.state.end() @@ -564,7 +529,7 @@ def create_embedding(self, args: dict): def create_hypernetwork(self, args: dict): try: - shared.state.begin('api-create-hypernetwork') + shared.state.begin('api-hypernetwork') filename = create_hypernetwork(**args) # create empty embedding # pylint: disable=E1111 shared.state.end() return models.CreateResponse(info = f"create hypernetwork filename: {filename}") @@ -590,7 +555,7 @@ def preprocess(self, args: dict): def train_embedding(self, args: dict): try: - shared.state.begin('api-train-embedding') + shared.state.begin('api-embedding') apply_optimizations = False error = None filename = '' @@ -611,7 +576,7 @@ def train_embedding(self, args: dict): def train_hypernetwork(self, args: dict): try: - shared.state.begin('api-train-hypernetwork') + shared.state.begin('api-hypernetwork') shared.loaded_hypernetworks = [] apply_optimizations = False error = None diff --git a/modules/extras.py b/modules/extras.py index 6e61b8426..a84b9b28e 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -54,7 +54,7 @@ def to_half(tensor, enable): def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_model_name, interp_method, multiplier, save_as_half, custom_name, checkpoint_format, config_source, bake_in_vae, discard_weights, save_metadata): # pylint: disable=unused-argument - shared.state.begin('model-merge') + shared.state.begin('merge') save_as_half = save_as_half == 0 def fail(message): @@ -319,7 +319,7 @@ def fix_model(model, fix_clip=False): "vae": vae_conv, "other": others_conv } - shared.state.begin('model-convert') + shared.state.begin('convert') model_info = sd_models.checkpoints_list[model] shared.state.textinfo = f"Loading {model_info.filename}..." shared.log.info(f"Model convert loading: {model_info.filename}") diff --git a/modules/hashes.py b/modules/hashes.py index 21c43de17..e4d13f2e3 100644 --- a/modules/hashes.py +++ b/modules/hashes.py @@ -69,7 +69,7 @@ def sha256(filename, title, use_addnet_hash=False): if not os.path.isfile(filename): return None orig_state = copy.deepcopy(shared.state) - shared.state.begin("hashing") + shared.state.begin("hash") if use_addnet_hash: if progress_ok: try: diff --git a/modules/hypernetworks/hypernetwork.py b/modules/hypernetworks/hypernetwork.py index d36edb0d1..d3572cbd0 100644 --- a/modules/hypernetworks/hypernetwork.py +++ b/modules/hypernetworks/hypernetwork.py @@ -460,7 +460,7 @@ def train_hypernetwork(id_task, hypernetwork_name, learn_rate, batch_size, gradi hypernetwork.load(path) shared.loaded_hypernetworks = [hypernetwork] - shared.state.job = "train-hypernetwork" + shared.state.job = "train" shared.state.textinfo = "Initializing hypernetwork training..." shared.state.job_count = steps diff --git a/modules/images.py b/modules/images.py index 158c613ac..ad5cdd15b 100644 --- a/modules/images.py +++ b/modules/images.py @@ -135,9 +135,9 @@ def wrap(drawing, text, font, line_length): def get_font(fontsize): try: - return ImageFont.truetype(shared.opts.font or 'html/roboto.ttf', fontsize) + return ImageFont.truetype(shared.opts.font or 'javascript/roboto.ttf', fontsize) except Exception: - return ImageFont.truetype('html/roboto.ttf', fontsize) + return ImageFont.truetype('javascript/roboto.ttf', fontsize) def draw_texts(drawing: ImageDraw, draw_x, draw_y, lines, initial_fnt, initial_fontsize): for line in lines: diff --git a/modules/img2img.py b/modules/img2img.py index f01031cb8..b05254434 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -40,7 +40,6 @@ def process_batch(p, input_files, input_dir, output_dir, inpaint_mask_dir, args) btcrept = p.batch_size shared.log.info(f"Process batch: inputs={len(image_files)} outputs={p.n_iter * p.batch_size} per input") for i in range(0, len(image_files), window_size): - shared.state.job = f"{i+1} to {min(i+window_size, len(image_files))} out of {len(image_files)}" if shared.state.skipped: shared.state.skipped = False if shared.state.interrupted: diff --git a/modules/k-diffusion b/modules/k-diffusion new file mode 160000 index 000000000..045515774 --- /dev/null +++ b/modules/k-diffusion @@ -0,0 +1 @@ +Subproject commit 045515774882014cc14c1ba2668ab5bad9cbf7c0 diff --git a/modules/modelloader.py b/modules/modelloader.py index dee78797d..0173720d3 100644 --- a/modules/modelloader.py +++ b/modules/modelloader.py @@ -85,7 +85,7 @@ def download_civit_preview(model_path: str, preview_url: str): block_size = 16384 # 16KB blocks written = 0 img = None - shared.state.begin('civitai-download-preview') + shared.state.begin('civitai') try: with open(preview_file, 'wb') as f: with p.Progress(p.TextColumn('[cyan]{task.description}'), p.DownloadColumn(), p.BarColumn(), p.TaskProgressColumn(), p.TimeRemainingColumn(), p.TimeElapsedColumn(), p.TransferSpeedColumn(), console=shared.console) as progress: @@ -142,7 +142,7 @@ def download_civit_model_thread(model_name, model_url, model_path, model_type, p total_size = int(r.headers.get('content-length', 0)) res += f' size={round((starting_pos + total_size)/1024/1024)}Mb' shared.log.info(res) - shared.state.begin('civitai-download-model') + shared.state.begin('civitai') block_size = 16384 # 16KB blocks written = starting_pos global download_pbar # pylint: disable=global-statement @@ -188,7 +188,7 @@ def download_diffusers_model(hub_id: str, cache_dir: str = None, download_config return None from diffusers import DiffusionPipeline import huggingface_hub as hf - shared.state.begin('huggingface-download-model') + shared.state.begin('huggingface') if download_config is None: download_config = { "force_download": False, diff --git a/modules/paths.py b/modules/paths.py index 0fd7b87b5..d3cce3f4a 100644 --- a/modules/paths.py +++ b/modules/paths.py @@ -17,22 +17,13 @@ # data_path = cmd_opts_pre.data sys.path.insert(0, script_path) -# search for directory of stable diffusion in following places -sd_path = None -possible_sd_paths = [os.path.join(script_path, 'repositories/stable-diffusion-stability-ai'), '.', os.path.dirname(script_path)] -for possible_sd_path in possible_sd_paths: - if os.path.exists(os.path.join(possible_sd_path, 'ldm/models/diffusion/ddpm.py')): - sd_path = os.path.abspath(possible_sd_path) - break - -assert sd_path is not None, f"Couldn't find Stable Diffusion in any of: {possible_sd_paths}" - +sd_path = os.path.join(script_path, 'repositories') path_dirs = [ - (sd_path, 'ldm', 'Stable Diffusion', []), - (os.path.join(sd_path, '../taming-transformers'), 'taming', 'Taming Transformers', []), - (os.path.join(sd_path, '../CodeFormer'), 'inference_codeformer.py', 'CodeFormer', []), - (os.path.join(sd_path, '../BLIP'), 'models/blip.py', 'BLIP', []), - (os.path.join(sd_path, '../k-diffusion'), 'k_diffusion/sampling.py', 'k_diffusion', ["atstart"]), + (sd_path, 'ldm', 'ldm', []), + (sd_path, 'taming', 'Taming Transformers', []), + (os.path.join(sd_path, 'blip'), 'models/blip.py', 'BLIP', []), + (os.path.join(sd_path, 'codeformer'), 'inference_codeformer.py', 'CodeFormer', []), + (os.path.join('modules', 'k-diffusion'), 'k_diffusion/sampling.py', 'k_diffusion', ["atstart"]), ] paths = {} diff --git a/modules/processing.py b/modules/processing.py index 616533987..70db97412 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -442,6 +442,8 @@ def decode_first_stage(model, x, full_quality=True): shared.log.debug(f'Decode VAE: skipped={shared.state.skipped} interrupted={shared.state.interrupted}') x_sample = torch.zeros((len(x), 3, x.shape[2] * 8, x.shape[3] * 8), dtype=devices.dtype_vae, device=devices.device) return x_sample + prev_job = shared.state.job + shared.state.job = 'vae' with devices.autocast(disable = x.dtype==devices.dtype_vae): try: if full_quality: @@ -459,6 +461,7 @@ def decode_first_stage(model, x, full_quality=True): except Exception as e: x_sample = x shared.log.error(f'Decode VAE: {e}') + shared.state.job = prev_job return x_sample @@ -769,12 +772,11 @@ def infotext(_inxex=0): # dummy function overriden if there are iterations return '' ema_scope_context = p.sd_model.ema_scope if shared.backend == shared.Backend.ORIGINAL else nullcontext + shared.state.job_count = p.n_iter with devices.inference_context(), ema_scope_context(): t0 = time.time() with devices.autocast(): p.init(p.all_prompts, p.all_seeds, p.all_subseeds) - if shared.state.job_count == -1: - shared.state.job_count = p.n_iter extra_network_data = None for n in range(p.n_iter): p.iteration = n @@ -806,8 +808,6 @@ def infotext(_inxex=0): # dummy function overriden if there are iterations step_multiplier = 1 sampler_config = modules.sd_samplers.find_sampler_config(p.sampler_name) step_multiplier = 2 if sampler_config and sampler_config.options.get("second_order", False) else 1 - if p.n_iter > 1: - shared.state.job = f"Batch {n+1} out of {p.n_iter}" if shared.backend == shared.Backend.ORIGINAL: uc = get_conds_with_caching(modules.prompt_parser.get_learned_conditioning, p.negative_prompts, p.steps * step_multiplier, cached_uc) @@ -913,7 +913,6 @@ def infotext(index): # pylint: disable=function-redefined # noqa: F811 output_images.append(image_mask_composite) del x_samples_ddim devices.torch_gc() - shared.state.nextjob() t1 = time.time() shared.log.info(f'Processed: images={len(output_images)} time={t1 - t0:.2f}s its={(p.steps * len(output_images)) / (t1 - t0):.2f} memory={modules.memstats.memory_stats()}') @@ -1036,12 +1035,8 @@ def init_hr(self): self.is_hr_pass = False return self.is_hr_pass = True - if not shared.state.processing_has_refined_job_count: - if shared.state.job_count == -1: - shared.state.job_count = self.n_iter - shared.state.job_count = shared.state.job_count * 2 - shared.state.processing_has_refined_job_count = True hypertile_set(self, hr=True) + shared.state.job_count = 2 * self.n_iter shared.log.debug(f'Init hires: upscaler="{self.hr_upscaler}" sampler="{self.latent_sampler}" resize={self.hr_resize_x}x{self.hr_resize_y} upscale={self.hr_upscale_to_x}x{self.hr_upscale_to_y}') def sample(self, conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength, prompts): @@ -1061,11 +1056,13 @@ def sample(self, conditioning, unconditional_conditioning, seeds, subseeds, subs self.sampler.initialize(self) x = create_random_tensors([4, self.height // 8, self.width // 8], seeds=seeds, subseeds=subseeds, subseed_strength=self.subseed_strength, seed_resize_from_h=self.seed_resize_from_h, seed_resize_from_w=self.seed_resize_from_w, p=self) samples = self.sampler.sample(self, x, conditioning, unconditional_conditioning, image_conditioning=self.txt2img_image_conditioning(x)) + shared.state.nextjob() if not self.enable_hr or shared.state.interrupted or shared.state.skipped: return samples self.init_hr() if self.is_hr_pass: + prev_job = shared.state.job target_width = self.hr_upscale_to_x target_height = self.hr_upscale_to_y decoded_samples = None @@ -1083,6 +1080,7 @@ def sample(self, conditioning, unconditional_conditioning, seeds, subseeds, subs self.extra_generation_params, self.restore_faces = bak_extra_generation_params, bak_restore_faces images.save_image(image, self.outpath_samples, "", seeds[i], prompts[i], shared.opts.samples_format, info=info, suffix="-before-hires") if latent_scale_mode is None or self.hr_force: # non-latent upscaling + shared.state.job = 'upscale' if decoded_samples is None: decoded_samples = decode_first_stage(self.sd_model, samples.to(dtype=devices.dtype_vae), self.full_quality) decoded_samples = torch.clamp((decoded_samples + 1.0) / 2.0, min=0.0, max=1.0) @@ -1112,6 +1110,7 @@ def sample(self, conditioning, unconditional_conditioning, seeds, subseeds, subs if self.latent_sampler == "PLMS": self.latent_sampler = 'UniPC' if self.hr_force or latent_scale_mode is not None: + shared.state.job = 'hires' if self.denoising_strength > 0: self.ops.append('hires') devices.torch_gc() # GC now before running the next img2img to prevent running out of memory @@ -1127,8 +1126,9 @@ def sample(self, conditioning, unconditional_conditioning, seeds, subseeds, subs else: self.ops.append('upscale') x = None - shared.state.nextjob() self.is_hr_pass = False + shared.state.job = prev_job + shared.state.nextjob() return samples @@ -1293,6 +1293,7 @@ def sample(self, conditioning, unconditional_conditioning, seeds, subseeds, subs samples = samples * self.nmask + self.init_latent * self.mask del x devices.torch_gc() + shared.state.nextjob() return samples def get_token_merging_ratio(self, for_hr=False): diff --git a/modules/processing_diffusers.py b/modules/processing_diffusers.py index 7c3506715..bf402971c 100644 --- a/modules/processing_diffusers.py +++ b/modules/processing_diffusers.py @@ -63,14 +63,6 @@ def save_intermediate(latents, suffix): def diffusers_callback(step: int, _timestep: int, latents: torch.FloatTensor): shared.state.sampling_step = step - if p.is_hr_pass: - shared.state.job = 'hires' - shared.state.sampling_steps = p.hr_second_pass_steps # add optional hires - elif p.is_refiner_pass: - shared.state.job = 'refine' - shared.state.sampling_steps = calculate_refiner_steps() # add optional refiner - else: - shared.state.sampling_steps = p.steps # base steps shared.state.current_latent = latents if shared.state.interrupted or shared.state.skipped: raise AssertionError('Interrupted...') @@ -133,6 +125,8 @@ def taesd_vae_encode(image): return encoded def vae_decode(latents, model, output_type='np', full_quality=True): + prev_job = shared.state.job + shared.state.job = 'vae' if not torch.is_tensor(latents): # already decoded return latents if latents.shape[0] == 0: @@ -150,6 +144,7 @@ def vae_decode(latents, model, output_type='np', full_quality=True): else: decoded = taesd_vae_decode(latents=latents) imgs = model.image_processor.postprocess(decoded, output_type=output_type) + shared.state.job = prev_job return imgs def vae_encode(image, model, full_quality=True): # pylint: disable=unused-variable @@ -186,16 +181,17 @@ def fix_prompts(prompts, negative_prompts, prompts_2, negative_prompts_2): def task_specific_kwargs(model): task_args = {} - if sd_models.get_diffusers_task(model) == sd_models.DiffusersTaskType.TEXT_2_IMAGE: + is_img2img_model = bool("Zero123" in shared.sd_model.__class__.__name__) + if sd_models.get_diffusers_task(model) == sd_models.DiffusersTaskType.TEXT_2_IMAGE and not is_img2img_model: p.ops.append('txt2img') task_args = {"height": 8 * math.ceil(p.height / 8), "width": 8 * math.ceil(p.width / 8)} - elif sd_models.get_diffusers_task(model) == sd_models.DiffusersTaskType.IMAGE_2_IMAGE and len(getattr(p, 'init_images' ,[])) > 0: + elif (sd_models.get_diffusers_task(model) == sd_models.DiffusersTaskType.IMAGE_2_IMAGE or is_img2img_model) and len(getattr(p, 'init_images' ,[])) > 0: p.ops.append('img2img') task_args = {"image": p.init_images, "strength": p.denoising_strength} elif sd_models.get_diffusers_task(model) == sd_models.DiffusersTaskType.INSTRUCT and len(getattr(p, 'init_images' ,[])) > 0: p.ops.append('instruct') task_args = {"height": 8 * math.ceil(p.height / 8), "width": 8 * math.ceil(p.width / 8), "image": p.init_images, "strength": p.denoising_strength} - elif sd_models.get_diffusers_task(model) == sd_models.DiffusersTaskType.INPAINTING and len(getattr(p, 'init_images' ,[])) > 0: + elif (sd_models.get_diffusers_task(model) == sd_models.DiffusersTaskType.INPAINTING or is_img2img_model) and len(getattr(p, 'init_images' ,[])) > 0: p.ops.append('inpaint') if getattr(p, 'mask', None) is None: p.mask = TF.to_pil_image(torch.ones_like(TF.to_tensor(p.init_images[0]))).convert("L") @@ -388,6 +384,7 @@ def calculate_refiner_steps(): clip_skip=p.clip_skip, desc='Base', ) + shared.state.sampling_steps = base_args['num_inference_steps'] p.extra_generation_params['CFG rescale'] = p.diffusers_guidance_rescale p.extra_generation_params["Sampler Eta"] = shared.opts.scheduler_eta if shared.opts.scheduler_eta is not None and shared.opts.scheduler_eta > 0 and shared.opts.scheduler_eta < 1 else None try: @@ -403,6 +400,7 @@ def calculate_refiner_steps(): if hasattr(shared.sd_model, 'embedding_db') and len(shared.sd_model.embedding_db.embeddings_used) > 0: p.extra_generation_params['Embeddings'] = ', '.join(shared.sd_model.embedding_db.embeddings_used) + shared.state.nextjob() if shared.state.interrupted or shared.state.skipped: return results @@ -412,10 +410,12 @@ def calculate_refiner_steps(): latent_scale_mode = shared.latent_upscale_modes.get(p.hr_upscaler, None) if (hasattr(p, "hr_upscaler") and p.hr_upscaler is not None) else shared.latent_upscale_modes.get(shared.latent_upscale_default_mode, "None") if p.is_hr_pass: p.init_hr() + prev_job = shared.state.job if p.width != p.hr_upscale_to_x or p.height != p.hr_upscale_to_y: p.ops.append('upscale') if shared.opts.save and not p.do_not_save_samples and shared.opts.save_images_before_highres_fix and hasattr(shared.sd_model, 'vae'): save_intermediate(latents=output.images, suffix="-before-hires") + shared.state.job = 'upscale' output.images = hires_resize(latents=output.images) if latent_scale_mode is not None or p.hr_force: p.ops.append('hires') @@ -438,15 +438,22 @@ def calculate_refiner_steps(): strength=p.denoising_strength, desc='Hires', ) + shared.state.job = 'hires' + shared.state.sampling_steps = hires_args['num_inference_steps'] try: output = shared.sd_model(**hires_args) # pylint: disable=not-callable except AssertionError as e: shared.log.info(e) p.init_images = [] + shared.state.job = prev_job + shared.state.nextjob() p.is_hr_pass = False # optional refiner pass or decode if is_refiner_enabled: + prev_job = shared.state.job + shared.state.job = 'refine' + shared.state.job_count +=1 if shared.opts.save and not p.do_not_save_samples and shared.opts.save_images_before_refiner and hasattr(shared.sd_model, 'vae'): save_intermediate(latents=output.images, suffix="-before-refiner") if shared.opts.diffusers_move_base and not getattr(shared.sd_model, 'has_accelerate', False): @@ -491,6 +498,7 @@ def calculate_refiner_steps(): clip_skip=p.clip_skip, desc='Refiner', ) + shared.state.sampling_steps = refiner_args['num_inference_steps'] try: refiner_output = shared.sd_refiner(**refiner_args) # pylint: disable=not-callable except AssertionError as e: @@ -505,7 +513,9 @@ def calculate_refiner_steps(): shared.log.debug('Moving to CPU: model=refiner') shared.sd_refiner.to(devices.cpu) devices.torch_gc() - p.is_refiner_pass = True + shared.state.job = prev_job + shared.state.nextjob() + p.is_refiner_pass = False # final decode since there is no refiner if not is_refiner_enabled: diff --git a/modules/progress.py b/modules/progress.py index db32abb31..968c586b7 100644 --- a/modules/progress.py +++ b/modules/progress.py @@ -66,15 +66,20 @@ def progressapi(req: ProgressRequest): paused = shared.state.paused if not active: return InternalProgressResponse(job=shared.state.job, active=active, queued=queued, paused=paused, completed=completed, id_live_preview=-1, textinfo="Queued..." if queued else "Waiting...") - progress = 0 - if shared.state.job_count > 0: - progress += shared.state.job_no / shared.state.job_count - if shared.state.sampling_steps > 0 and shared.state.job_count > 0: - progress += 1 / (shared.state.job_count / 2 if shared.state.processing_has_refined_job_count else 1) * shared.state.sampling_step / shared.state.sampling_steps - progress = min(progress, 1) + if shared.state.job_no > shared.state.job_count: + shared.state.job_count = shared.state.job_no + batch_x = max(shared.state.job_no, 0) + batch_y = max(shared.state.job_count, 1) + step_x = max(shared.state.sampling_step, 0) + step_y = max(shared.state.sampling_steps, 1) + current = step_y * batch_x + step_x + total = step_y * batch_y + progress = min(1, current / total if total > 0 else 0) + elapsed_since_start = time.time() - shared.state.time_start predicted_duration = elapsed_since_start / progress if progress > 0 else None eta = predicted_duration - elapsed_since_start if predicted_duration is not None else None + id_live_preview = req.id_live_preview live_preview = None shared.state.set_current_image() @@ -83,4 +88,6 @@ def progressapi(req: ProgressRequest): shared.state.current_image.save(buffered, format='jpeg') live_preview = f'data:image/jpeg;base64,{base64.b64encode(buffered.getvalue()).decode("ascii")}' id_live_preview = shared.state.id_live_preview - return InternalProgressResponse(job=shared.state.job, active=active, queued=queued, paused=paused, completed=completed, progress=progress, eta=eta, live_preview=live_preview, id_live_preview=id_live_preview, textinfo=shared.state.textinfo) + + res = InternalProgressResponse(job=shared.state.job, active=active, queued=queued, paused=paused, completed=completed, progress=progress, eta=eta, live_preview=live_preview, id_live_preview=id_live_preview, textinfo=shared.state.textinfo) + return res diff --git a/modules/scripts.py b/modules/scripts.py index f378744ee..323aca6a1 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -321,6 +321,7 @@ def __init__(self): self.paste_field_names = [] self.script_load_ctr = 0 self.is_img2img = False + self.inputs = [None] def initialize_scripts(self, is_img2img): from modules import scripts_auto_postprocessing @@ -355,6 +356,31 @@ def initialize_scripts(self, is_img2img): except Exception as e: log.error(f'Script initialize: {path} {e}') + def create_script_ui(self, script): + import modules.api.models as api_models + script.args_from = len(self.inputs) + script.args_to = len(self.inputs) + controls = wrap_call(script.ui, script.filename, "ui", script.is_img2img) + if controls is None: + return + script.name = wrap_call(script.title, script.filename, "title", default=script.filename).lower() + api_args = [] + for control in controls: + control.custom_script_source = os.path.basename(script.filename) + arg_info = api_models.ScriptArg(label=control.label or "") + for field in ("value", "minimum", "maximum", "step", "choices"): + v = getattr(control, field, None) + if v is not None: + setattr(arg_info, field, v) + api_args.append(arg_info) + script.api_info = api_models.ScriptInfo(name=script.name, is_img2img=script.is_img2img, is_alwayson=script.alwayson, args=api_args) + if script.infotext_fields is not None: + self.infotext_fields += script.infotext_fields + if script.paste_field_names is not None: + self.paste_field_names += script.paste_field_names + self.inputs += controls + script.args_to = len(self.inputs) + def setup_ui_for_section(self, section, scriptlist=None): if scriptlist is None: scriptlist = self.alwayson_scripts @@ -377,7 +403,7 @@ def setup_ui(self): inputs = [] inputs_alwayson = [True] - def create_script_ui(script, inputs, inputs_alwayson): + def create_script_ui(script, inputs, inputs_alwayson): # TODO this is legacy implementation, see self.create_script_ui script.args_from = len(inputs) script.args_to = len(inputs) controls = wrap_call(script.ui, script.filename, "ui", script.is_img2img) diff --git a/modules/sd_hijack_optimizations.py b/modules/sd_hijack_optimizations.py index 87811b48b..9a3d0d237 100644 --- a/modules/sd_hijack_optimizations.py +++ b/modules/sd_hijack_optimizations.py @@ -317,7 +317,7 @@ def get_xformers_flash_attention_op(q, k, v): return None try: - flash_attention_op = xformers.ops.MemoryEfficientAttentionFlashAttentionOp + flash_attention_op = xformers.ops.MemoryEfficientAttentionFlashAttentionOp # pylint: disable=used-before-assignment fw, _bw = flash_attention_op if fw.supports(xformers.ops.fmha.Inputs(query=q, key=k, value=v, attn_bias=None)): return flash_attention_op diff --git a/modules/sd_models.py b/modules/sd_models.py index a8828f176..229e449b2 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -848,6 +848,8 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No vae = sd_vae.load_vae_diffusers(checkpoint_info.path, vae_file, vae_source) if vae is not None: diffusers_load_config["vae"] = vae + if 'LCM' in checkpoint_info.path: + diffusers_load_config['custom_pipeline'] = 'latent_consistency_txt2img' if os.path.isdir(checkpoint_info.path): err1 = None @@ -858,18 +860,21 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No sd_model.model_type = sd_model.__class__.__name__ except Exception as e: err1 = e + # shared.log.error(f'AutoPipeline: {e}') try: # try diffusion pipeline next second-best choice, works for most non-linked pipelines if err1 is not None: sd_model = diffusers.DiffusionPipeline.from_pretrained(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config) sd_model.model_type = sd_model.__class__.__name__ except Exception as e: err2 = e + # shared.log.error(f'DiffusionPipeline: {e}') try: # try basic pipeline next just in case if err2 is not None: sd_model = diffusers.StableDiffusionPipeline.from_pretrained(checkpoint_info.path, cache_dir=shared.opts.diffusers_dir, **diffusers_load_config) sd_model.model_type = sd_model.__class__.__name__ except Exception as e: err3 = e # ignore last error + shared.log.error(f'StableDiffusionPipeline: {e}') if err3 is not None: shared.log.error(f'Failed loading {op}: {checkpoint_info.path} auto={err1} diffusion={err2}') return @@ -1155,7 +1160,7 @@ def reload_model_weights(sd_model=None, info=None, reuse_dict=False, op='model') return None orig_state = copy.deepcopy(shared.state) shared.state = shared_state.State() - shared.state.begin(f'load-{op}') + shared.state.begin('load') if load_dict: shared.log.debug(f'Model dict: existing={sd_model is not None} target={checkpoint_info.filename} info={info}') else: diff --git a/modules/sd_models_config.py b/modules/sd_models_config.py index 163390ca6..3df76caa5 100644 --- a/modules/sd_models_config.py +++ b/modules/sd_models_config.py @@ -4,10 +4,10 @@ from modules import paths, sd_disable_initialization, devices -sd_repo_configs_path = os.path.join(paths.paths['Stable Diffusion'], "configs", "stable-diffusion") +sd_repo_configs_path = 'configs' config_default = paths.sd_default_config -config_sd2 = os.path.join(sd_repo_configs_path, "v2-inference.yaml") -config_sd2v = os.path.join(sd_repo_configs_path, "v2-inference-v.yaml") +config_sd2 = os.path.join(sd_repo_configs_path, "v2-inference-512-base.yaml") +config_sd2v = os.path.join(sd_repo_configs_path, "v2-inference-768-v.yaml") config_sd2_inpainting = os.path.join(sd_repo_configs_path, "v2-inpainting-inference.yaml") config_depth_model = os.path.join(sd_repo_configs_path, "v2-midas-inference.yaml") config_unclip = os.path.join(sd_repo_configs_path, "v2-1-stable-unclip-l-inference.yaml") diff --git a/modules/sd_samplers_timesteps.py b/modules/sd_samplers_timesteps.py index dfe0a857e..0e8e01909 100644 --- a/modules/sd_samplers_timesteps.py +++ b/modules/sd_samplers_timesteps.py @@ -49,6 +49,7 @@ def __init__(self, sampler): self.alphas = shared.sd_model.alphas_cumprod self.mask_before_denoising = True + self.model_wrap = None def get_pred_x0(self, x_in, x_out, sigma): ts = sigma.to(dtype=int) diff --git a/modules/shared.py b/modules/shared.py index 9fa4c8e28..f7a295572 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -474,7 +474,7 @@ def default(obj): "schedulers_use_karras": OptionInfo(True, "Use Karras sigmas", gr.Checkbox, {"visible": False}), "schedulers_use_thresholding": OptionInfo(False, "Use dynamic thresholding", gr.Checkbox, {"visible": False}), "schedulers_use_loworder": OptionInfo(True, "Use simplified solvers in final steps", gr.Checkbox, {"visible": False}), - "schedulers_prediction_type": OptionInfo("default", "Override model prediction type", gr.Radio, {"choices": ['default', 'epsilon', 'sample', 'v_prediction'], "visible": False}), + "schedulers_prediction_type": OptionInfo("default", "Override model prediction type", gr.Radio, {"choices": ['default', 'epsilon', 'sample', 'v_prediction']}), # managed from ui.py for backend diffusers "schedulers_sep_diffusers": OptionInfo("

Diffusers specific config

", "", gr.HTML), diff --git a/modules/shared_state.py b/modules/shared_state.py index ad2d992d1..06378d7bf 100644 --- a/modules/shared_state.py +++ b/modules/shared_state.py @@ -13,7 +13,6 @@ class State: job_no = 0 job_count = 0 total_jobs = 0 - processing_has_refined_job_count = False job_timestamp = '0' sampling_step = 0 sampling_steps = 0 @@ -72,7 +71,6 @@ def begin(self, title=""): self.job_no = 0 self.job_timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S") self.paused = False - self.processing_has_refined_job_count = False self.sampling_step = 0 self.skipped = False self.textinfo = None diff --git a/modules/textual_inversion/image_embedding.py b/modules/textual_inversion/image_embedding.py index 7c9fe881d..a59798bb4 100644 --- a/modules/textual_inversion/image_embedding.py +++ b/modules/textual_inversion/image_embedding.py @@ -133,7 +133,7 @@ def caption_image_overlay(srcimage, title, footerLeft, footerMid, footerRight, t image = srcimage.copy() fontsize = 32 if textfont is None: - textfont = opts.font or 'html/roboto.ttf' + textfont = opts.font or 'javascript/roboto.ttf' factor = 1.5 gradient = Image.new('RGBA', (1, image.size[1]), color=(0, 0, 0, 0)) diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index 83aabcd17..d7105f8fb 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -425,7 +425,7 @@ def train_embedding(id_task, embedding_name, learn_rate, batch_size, gradient_st log_directory = f"{os.path.join(shared.cmd_opts.data_dir, 'train/log/embeddings')}" template_file = template_file.path - shared.state.job = "train-embedding" + shared.state.job = "train" shared.state.textinfo = "Initializing textual inversion training..." shared.state.job_count = steps diff --git a/modules/ui_prompt_styles.py b/modules/ui_prompt_styles.py index c394cd852..56b700a27 100644 --- a/modules/ui_prompt_styles.py +++ b/modules/ui_prompt_styles.py @@ -1,8 +1,7 @@ # TODO: a1111 compatibility item, not used import gradio as gr - -from modules import shared, ui_common, ui_components, styles +from modules import shared, styles styles_edit_symbol = '\U0001f58c\uFE0F' # 🖌️ styles_materialize_symbol = '\U0001f4cb' # 📋 @@ -34,7 +33,7 @@ def delete_style(name): return '', '', '' -def materialize_styles(prompt, negative_prompt, styles): +def materialize_styles(prompt, negative_prompt, styles): # pylint: disable=redefined-outer-name prompt = shared.prompt_styles.apply_styles_to_prompt(prompt, styles) negative_prompt = shared.prompt_styles.apply_negative_styles_to_prompt(negative_prompt, styles) return [gr.Textbox.update(value=prompt), gr.Textbox.update(value=negative_prompt), gr.Dropdown.update(value=[])] @@ -45,7 +44,7 @@ def refresh_styles(): class UiPromptStyles: - def __init__(self, tabname, main_ui_prompt, main_ui_negative_prompt): + def __init__(self, tabname, main_ui_prompt, main_ui_negative_prompt): # pylint: disable=unused-argument self.dropdown = gr.Dropdown(label="Styles", elem_id=f"{tabname}_styles", choices=[style.name for style in shared.prompt_styles.styles.values()], value=[], multiselect=True) """ diff --git a/pyproject.toml b/pyproject.toml index a4ddfe7b9..931e8ba5e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,11 @@ exclude = [ "extensions-builtin", "modules/lora", "modules/dml", - "modules/models/diffusion", + "modules/k-diffusion", + "repositories/ldm", + "repositories/taming", + "repositories/blip", + "repositories/codeformer", ] ignore = [ "A003", # Class attirbute shadowing builtin diff --git a/repositories/blip/CODEOWNERS b/repositories/blip/CODEOWNERS new file mode 100644 index 000000000..522fa4a0f --- /dev/null +++ b/repositories/blip/CODEOWNERS @@ -0,0 +1,2 @@ +# Comment line immediately above ownership line is reserved for related gus information. Please be careful while editing. +#ECCN:Open Source diff --git a/repositories/blip/CODE_OF_CONDUCT.md b/repositories/blip/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..b6724718c --- /dev/null +++ b/repositories/blip/CODE_OF_CONDUCT.md @@ -0,0 +1,105 @@ +# Salesforce Open Source Community Code of Conduct + +## About the Code of Conduct + +Equality is a core value at Salesforce. We believe a diverse and inclusive +community fosters innovation and creativity, and are committed to building a +culture where everyone feels included. + +Salesforce open-source projects are committed to providing a friendly, safe, and +welcoming environment for all, regardless of gender identity and expression, +sexual orientation, disability, physical appearance, body size, ethnicity, nationality, +race, age, religion, level of experience, education, socioeconomic status, or +other similar personal characteristics. + +The goal of this code of conduct is to specify a baseline standard of behavior so +that people with different social values and communication styles can work +together effectively, productively, and respectfully in our open source community. +It also establishes a mechanism for reporting issues and resolving conflicts. + +All questions and reports of abusive, harassing, or otherwise unacceptable behavior +in a Salesforce open-source project may be reported by contacting the Salesforce +Open Source Conduct Committee at ossconduct@salesforce.com. + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of gender +identity and expression, sexual orientation, disability, physical appearance, +body size, ethnicity, nationality, race, age, religion, level of experience, education, +socioeconomic status, or other similar personal characteristics. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy toward other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Personal attacks, insulting/derogatory comments, or trolling +* Public or private harassment +* Publishing, or threatening to publish, others' private information—such as +a physical or electronic address—without explicit permission +* Other conduct which could reasonably be considered inappropriate in a +professional setting +* Advocating for or encouraging any of the above behaviors + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned with this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project email +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the Salesforce Open Source Conduct Committee +at ossconduct@salesforce.com. All complaints will be reviewed and investigated +and will result in a response that is deemed necessary and appropriate to the +circumstances. The committee is obligated to maintain confidentiality with +regard to the reporter of an incident. Further details of specific enforcement +policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership and the Salesforce Open Source Conduct +Committee. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant-home], +version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html. +It includes adaptions and additions from [Go Community Code of Conduct][golang-coc], +[CNCF Code of Conduct][cncf-coc], and [Microsoft Open Source Code of Conduct][microsoft-coc]. + +This Code of Conduct is licensed under the [Creative Commons Attribution 3.0 License][cc-by-3-us]. + +[contributor-covenant-home]: https://www.contributor-covenant.org (https://www.contributor-covenant.org/) +[golang-coc]: https://golang.org/conduct +[cncf-coc]: https://github.com/cncf/foundation/blob/master/code-of-conduct.md +[microsoft-coc]: https://opensource.microsoft.com/codeofconduct/ +[cc-by-3-us]: https://creativecommons.org/licenses/by/3.0/us/ diff --git a/repositories/blip/LICENSE.txt b/repositories/blip/LICENSE.txt new file mode 100644 index 000000000..a63e87f4e --- /dev/null +++ b/repositories/blip/LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) 2022, Salesforce.com, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +* Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/repositories/blip/README.md b/repositories/blip/README.md new file mode 100644 index 000000000..7923e2119 --- /dev/null +++ b/repositories/blip/README.md @@ -0,0 +1,116 @@ +## BLIP: Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generation + +## Announcement: BLIP is now officially integrated into [LAVIS](https://github.com/salesforce/LAVIS) - a one-stop library for language-and-vision research and applications! + + + +This is the PyTorch code of the BLIP paper [[blog](https://blog.salesforceairesearch.com/blip-bootstrapping-language-image-pretraining/)]. The code has been tested on PyTorch 1.10. +To install the dependencies, run
pip install -r requirements.txt
+ +Catalog: +- [x] Inference demo +- [x] Pre-trained and finetuned checkpoints +- [x] Finetuning code for Image-Text Retrieval, Image Captioning, VQA, and NLVR2 +- [x] Pre-training code +- [x] Zero-shot video-text retrieval +- [x] Download of bootstrapped pre-training datasets + + +### Inference demo: +Run our interactive demo using [Colab notebook](https://colab.research.google.com/github/salesforce/BLIP/blob/main/demo.ipynb) (no GPU needed). +The demo includes code for: +1. Image captioning +2. Open-ended visual question answering +3. Multimodal / unimodal feature extraction +4. Image-text matching + +Try out the [Web demo](https://huggingface.co/spaces/Salesforce/BLIP), integrated into [Huggingface Spaces 🤗](https://huggingface.co/spaces) using [Gradio](https://github.com/gradio-app/gradio). + +Replicate web demo and Docker image is also available at [![Replicate](https://replicate.com/salesforce/blip/badge)](https://replicate.com/salesforce/blip) + +### Pre-trained checkpoints: +Num. pre-train images | BLIP w/ ViT-B | BLIP w/ ViT-B and CapFilt-L | BLIP w/ ViT-L +--- | :---: | :---: | :---: +14M | Download| - | - +129M | Download| Download | Download + +### Finetuned checkpoints: +Task | BLIP w/ ViT-B | BLIP w/ ViT-B and CapFilt-L | BLIP w/ ViT-L +--- | :---: | :---: | :---: +Image-Text Retrieval (COCO) | Download| - | Download +Image-Text Retrieval (Flickr30k) | Download| - | Download +Image Captioning (COCO) | - | Download| Download | +VQA | Download| Download | - +NLVR2 | Download| - | - + + +### Image-Text Retrieval: +1. Download COCO and Flickr30k datasets from the original websites, and set 'image_root' in configs/retrieval_{dataset}.yaml accordingly. +2. To evaluate the finetuned BLIP model on COCO, run: +
python -m torch.distributed.run --nproc_per_node=8 train_retrieval.py \
+--config ./configs/retrieval_coco.yaml \
+--output_dir output/retrieval_coco \
+--evaluate
+3. To finetune the pre-trained checkpoint using 8 A100 GPUs, first set 'pretrained' in configs/retrieval_coco.yaml as "https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base.pth". Then run: +
python -m torch.distributed.run --nproc_per_node=8 train_retrieval.py \
+--config ./configs/retrieval_coco.yaml \
+--output_dir output/retrieval_coco 
+ +### Image-Text Captioning: +1. Download COCO and NoCaps datasets from the original websites, and set 'image_root' in configs/caption_coco.yaml and configs/nocaps.yaml accordingly. +2. To evaluate the finetuned BLIP model on COCO, run: +
python -m torch.distributed.run --nproc_per_node=8 train_caption.py --evaluate
+3. To evaluate the finetuned BLIP model on NoCaps, generate results with: (evaluation needs to be performed on official server) +
python -m torch.distributed.run --nproc_per_node=8 eval_nocaps.py 
+4. To finetune the pre-trained checkpoint using 8 A100 GPUs, first set 'pretrained' in configs/caption_coco.yaml as "https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_capfilt_large.pth". Then run: +
python -m torch.distributed.run --nproc_per_node=8 train_caption.py 
+ +### VQA: +1. Download VQA v2 dataset and Visual Genome dataset from the original websites, and set 'vqa_root' and 'vg_root' in configs/vqa.yaml. +2. To evaluate the finetuned BLIP model, generate results with: (evaluation needs to be performed on official server) +
python -m torch.distributed.run --nproc_per_node=8 train_vqa.py --evaluate
+3. To finetune the pre-trained checkpoint using 16 A100 GPUs, first set 'pretrained' in configs/vqa.yaml as "https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_capfilt_large.pth". Then run: +
python -m torch.distributed.run --nproc_per_node=16 train_vqa.py 
+ +### NLVR2: +1. Download NLVR2 dataset from the original websites, and set 'image_root' in configs/nlvr.yaml. +2. To evaluate the finetuned BLIP model, run +
python -m torch.distributed.run --nproc_per_node=8 train_nlvr.py --evaluate
+3. To finetune the pre-trained checkpoint using 16 A100 GPUs, first set 'pretrained' in configs/nlvr.yaml as "https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base.pth". Then run: +
python -m torch.distributed.run --nproc_per_node=16 train_nlvr.py 
+ +### Finetune with ViT-L: +In order to finetune a model with ViT-L, simply change the config file to set 'vit' as large. Batch size and learning rate may also need to be adjusted accordingly (please see the paper's appendix for hyper-parameter details). Gradient checkpoint can also be activated in the config file to reduce GPU memory usage. + +### Pre-train: +1. Prepare training json files where each json file contains a list. Each item in the list is a dictonary with two key-value pairs: {'image': path_of_image, 'caption': text_of_image}. +2. In configs/pretrain.yaml, set 'train_file' as the paths for the json files . +3. Pre-train the model using 8 A100 GPUs: +
python -m torch.distributed.run --nproc_per_node=8 pretrain.py --config ./configs/Pretrain.yaml --output_dir output/Pretrain 
+ +### Zero-shot video-text retrieval: +1. Download MSRVTT dataset following the instructions from https://github.com/salesforce/ALPRO, and set 'video_root' accordingly in configs/retrieval_msrvtt.yaml. +2. Install [decord](https://github.com/dmlc/decord) with
pip install decord
+3. To perform zero-shot evaluation, run +
python -m torch.distributed.run --nproc_per_node=8 eval_retrieval_video.py
+ +### Pre-training datasets download: +We provide bootstrapped pre-training datasets as json files. Each json file contains a list. Each item in the list is a dictonary with two key-value pairs: {'url': url_of_image, 'caption': text_of_image}. + +Image source | Filtered web caption | Filtered synthetic caption by ViT-B | Filtered synthetic caption by ViT-L +--- | :---: | :---: | :---: +CC3M+CC12M+SBU | Download| Download| Download +LAION115M | Download| Download| Download + +### Citation +If you find this code to be useful for your research, please consider citing. +
+@inproceedings{li2022blip,
+      title={BLIP: Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generation}, 
+      author={Junnan Li and Dongxu Li and Caiming Xiong and Steven Hoi},
+      year={2022},
+      booktitle={ICML},
+}
+ +### Acknowledgement +The implementation of BLIP relies on resources from ALBEF, Huggingface Transformers, and timm. We thank the original authors for their open-sourcing. diff --git a/repositories/blip/SECURITY.md b/repositories/blip/SECURITY.md new file mode 100644 index 000000000..824902573 --- /dev/null +++ b/repositories/blip/SECURITY.md @@ -0,0 +1,7 @@ +## Security + +Please report any security issue to [security@salesforce.com](mailto:security@salesforce.com) +as soon as it is discovered. This library limits its runtime dependencies in +order to reduce the total cost of ownership as much as can be, but all consumers +should remain vigilant and have their security stakeholders review all third-party +products (3PP) like this one and their dependencies. diff --git a/repositories/blip/cog.yaml b/repositories/blip/cog.yaml new file mode 100644 index 000000000..c1dfcc430 --- /dev/null +++ b/repositories/blip/cog.yaml @@ -0,0 +1,17 @@ +build: + gpu: true + cuda: "11.1" + python_version: "3.8" + system_packages: + - "libgl1-mesa-glx" + - "libglib2.0-0" + python_packages: + - "ipython==7.30.1" + - "torchvision==0.11.1" + - "torch==1.10.0" + - "timm==0.4.12" + - "transformers==4.15.0" + - "fairscale==0.4.4" + - "pycocoevalcap==1.2" + +predict: "predict.py:Predictor" diff --git a/repositories/blip/configs/bert_config.json b/repositories/blip/configs/bert_config.json new file mode 100644 index 000000000..3ef38aabc --- /dev/null +++ b/repositories/blip/configs/bert_config.json @@ -0,0 +1,21 @@ +{ + "architectures": [ + "BertModel" + ], + "attention_probs_dropout_prob": 0.1, + "hidden_act": "gelu", + "hidden_dropout_prob": 0.1, + "hidden_size": 768, + "initializer_range": 0.02, + "intermediate_size": 3072, + "layer_norm_eps": 1e-12, + "max_position_embeddings": 512, + "model_type": "bert", + "num_attention_heads": 12, + "num_hidden_layers": 12, + "pad_token_id": 0, + "type_vocab_size": 2, + "vocab_size": 30522, + "encoder_width": 768, + "add_cross_attention": true +} diff --git a/repositories/blip/configs/caption_coco.yaml b/repositories/blip/configs/caption_coco.yaml new file mode 100644 index 000000000..42eab7030 --- /dev/null +++ b/repositories/blip/configs/caption_coco.yaml @@ -0,0 +1,33 @@ +image_root: '/export/share/datasets/vision/coco/images/' +ann_root: 'annotation' +coco_gt_root: 'annotation/coco_gt' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_caption_capfilt_large.pth' + +# size of vit model; base or large +vit: 'base' +vit_grad_ckpt: False +vit_ckpt_layer: 0 +batch_size: 32 +init_lr: 1e-5 + +# vit: 'large' +# vit_grad_ckpt: True +# vit_ckpt_layer: 5 +# batch_size: 16 +# init_lr: 2e-6 + +image_size: 384 + +# generation configs +max_length: 20 +min_length: 5 +num_beams: 3 +prompt: 'a picture of ' + +# optimizer +weight_decay: 0.05 +min_lr: 0 +max_epoch: 5 + diff --git a/repositories/blip/configs/med_config.json b/repositories/blip/configs/med_config.json new file mode 100644 index 000000000..0ffad0a6f --- /dev/null +++ b/repositories/blip/configs/med_config.json @@ -0,0 +1,21 @@ +{ + "architectures": [ + "BertModel" + ], + "attention_probs_dropout_prob": 0.1, + "hidden_act": "gelu", + "hidden_dropout_prob": 0.1, + "hidden_size": 768, + "initializer_range": 0.02, + "intermediate_size": 3072, + "layer_norm_eps": 1e-12, + "max_position_embeddings": 512, + "model_type": "bert", + "num_attention_heads": 12, + "num_hidden_layers": 12, + "pad_token_id": 0, + "type_vocab_size": 2, + "vocab_size": 30524, + "encoder_width": 768, + "add_cross_attention": true +} diff --git a/repositories/blip/configs/nlvr.yaml b/repositories/blip/configs/nlvr.yaml new file mode 100644 index 000000000..2d1122aad --- /dev/null +++ b/repositories/blip/configs/nlvr.yaml @@ -0,0 +1,21 @@ +image_root: '/export/share/datasets/vision/NLVR2/' +ann_root: 'annotation' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_nlvr.pth' + +#size of vit model; base or large +vit: 'base' +batch_size_train: 16 +batch_size_test: 64 +vit_grad_ckpt: False +vit_ckpt_layer: 0 +max_epoch: 15 + +image_size: 384 + +# optimizer +weight_decay: 0.05 +init_lr: 3e-5 +min_lr: 0 + diff --git a/repositories/blip/configs/nocaps.yaml b/repositories/blip/configs/nocaps.yaml new file mode 100644 index 000000000..902813585 --- /dev/null +++ b/repositories/blip/configs/nocaps.yaml @@ -0,0 +1,15 @@ +image_root: '/export/share/datasets/vision/nocaps/' +ann_root: 'annotation' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_caption_capfilt_large.pth' + +vit: 'base' +batch_size: 32 + +image_size: 384 + +max_length: 20 +min_length: 5 +num_beams: 3 +prompt: 'a picture of ' \ No newline at end of file diff --git a/repositories/blip/configs/pretrain.yaml b/repositories/blip/configs/pretrain.yaml new file mode 100644 index 000000000..02355ee02 --- /dev/null +++ b/repositories/blip/configs/pretrain.yaml @@ -0,0 +1,27 @@ +train_file: ['/export/share/junnan-li/VL_pretrain/annotation/coco_karpathy_train.json', + '/export/share/junnan-li/VL_pretrain/annotation/vg_caption.json', + ] +laion_path: '' + +# size of vit model; base or large +vit: 'base' +vit_grad_ckpt: False +vit_ckpt_layer: 0 + +image_size: 224 +batch_size: 75 + +queue_size: 57600 +alpha: 0.4 + +# optimizer +weight_decay: 0.05 +init_lr: 3e-4 +min_lr: 1e-6 +warmup_lr: 1e-6 +lr_decay_rate: 0.9 +max_epoch: 20 +warmup_steps: 3000 + + + diff --git a/repositories/blip/configs/retrieval_coco.yaml b/repositories/blip/configs/retrieval_coco.yaml new file mode 100644 index 000000000..a8569e9b6 --- /dev/null +++ b/repositories/blip/configs/retrieval_coco.yaml @@ -0,0 +1,34 @@ +image_root: '/export/share/datasets/vision/coco/images/' +ann_root: 'annotation' +dataset: 'coco' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_retrieval_coco.pth' + +# size of vit model; base or large + +vit: 'base' +batch_size_train: 32 +batch_size_test: 64 +vit_grad_ckpt: True +vit_ckpt_layer: 4 +init_lr: 1e-5 + +# vit: 'large' +# batch_size_train: 16 +# batch_size_test: 32 +# vit_grad_ckpt: True +# vit_ckpt_layer: 12 +# init_lr: 5e-6 + +image_size: 384 +queue_size: 57600 +alpha: 0.4 +k_test: 256 +negative_all_rank: True + +# optimizer +weight_decay: 0.05 +min_lr: 0 +max_epoch: 6 + diff --git a/repositories/blip/configs/retrieval_flickr.yaml b/repositories/blip/configs/retrieval_flickr.yaml new file mode 100644 index 000000000..d75ea4eed --- /dev/null +++ b/repositories/blip/configs/retrieval_flickr.yaml @@ -0,0 +1,34 @@ +image_root: '/export/share/datasets/vision/flickr30k/' +ann_root: 'annotation' +dataset: 'flickr' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_retrieval_flickr.pth' + +# size of vit model; base or large + +vit: 'base' +batch_size_train: 32 +batch_size_test: 64 +vit_grad_ckpt: True +vit_ckpt_layer: 4 +init_lr: 1e-5 + +# vit: 'large' +# batch_size_train: 16 +# batch_size_test: 32 +# vit_grad_ckpt: True +# vit_ckpt_layer: 10 +# init_lr: 5e-6 + +image_size: 384 +queue_size: 57600 +alpha: 0.4 +k_test: 128 +negative_all_rank: False + +# optimizer +weight_decay: 0.05 +min_lr: 0 +max_epoch: 6 + diff --git a/repositories/blip/configs/retrieval_msrvtt.yaml b/repositories/blip/configs/retrieval_msrvtt.yaml new file mode 100644 index 000000000..395f62542 --- /dev/null +++ b/repositories/blip/configs/retrieval_msrvtt.yaml @@ -0,0 +1,12 @@ +video_root: '/export/share/dongxuli/data/msrvtt_retrieval/videos' +ann_root: 'annotation' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_retrieval_coco.pth' + +# size of vit model; base or large +vit: 'base' +batch_size: 64 +k_test: 128 +image_size: 384 +num_frm_test: 8 \ No newline at end of file diff --git a/repositories/blip/configs/vqa.yaml b/repositories/blip/configs/vqa.yaml new file mode 100644 index 000000000..74327e6d0 --- /dev/null +++ b/repositories/blip/configs/vqa.yaml @@ -0,0 +1,25 @@ +vqa_root: '/export/share/datasets/vision/VQA/Images/mscoco/' #followed by train2014/ +vg_root: '/export/share/datasets/vision/visual-genome/' #followed by image/ +train_files: ['vqa_train','vqa_val','vg_qa'] +ann_root: 'annotation' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_vqa_capfilt_large.pth' + +# size of vit model; base or large +vit: 'base' +batch_size_train: 16 +batch_size_test: 32 +vit_grad_ckpt: False +vit_ckpt_layer: 0 +init_lr: 2e-5 + +image_size: 480 + +k_test: 128 +inference: 'rank' + +# optimizer +weight_decay: 0.05 +min_lr: 0 +max_epoch: 10 \ No newline at end of file diff --git a/repositories/blip/data/__init__.py b/repositories/blip/data/__init__.py new file mode 100644 index 000000000..0be209acf --- /dev/null +++ b/repositories/blip/data/__init__.py @@ -0,0 +1,101 @@ +import torch +from torch.utils.data import DataLoader +from torchvision import transforms +from torchvision.transforms.functional import InterpolationMode + +from data.coco_karpathy_dataset import coco_karpathy_train, coco_karpathy_caption_eval, coco_karpathy_retrieval_eval +from data.nocaps_dataset import nocaps_eval +from data.flickr30k_dataset import flickr30k_train, flickr30k_retrieval_eval +from data.vqa_dataset import vqa_dataset +from data.nlvr_dataset import nlvr_dataset +from data.pretrain_dataset import pretrain_dataset +from transform.randaugment import RandomAugment + +def create_dataset(dataset, config, min_scale=0.5): + + normalize = transforms.Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711)) + + transform_train = transforms.Compose([ + transforms.RandomResizedCrop(config['image_size'],scale=(min_scale, 1.0),interpolation=InterpolationMode.BICUBIC), + transforms.RandomHorizontalFlip(), + RandomAugment(2,5,isPIL=True,augs=['Identity','AutoContrast','Brightness','Sharpness','Equalize', + 'ShearX', 'ShearY', 'TranslateX', 'TranslateY', 'Rotate']), + transforms.ToTensor(), + normalize, + ]) + transform_test = transforms.Compose([ + transforms.Resize((config['image_size'],config['image_size']),interpolation=InterpolationMode.BICUBIC), + transforms.ToTensor(), + normalize, + ]) + + if dataset=='pretrain': + dataset = pretrain_dataset(config['train_file'], config['laion_path'], transform_train) + return dataset + + elif dataset=='caption_coco': + train_dataset = coco_karpathy_train(transform_train, config['image_root'], config['ann_root'], prompt=config['prompt']) + val_dataset = coco_karpathy_caption_eval(transform_test, config['image_root'], config['ann_root'], 'val') + test_dataset = coco_karpathy_caption_eval(transform_test, config['image_root'], config['ann_root'], 'test') + return train_dataset, val_dataset, test_dataset + + elif dataset=='nocaps': + val_dataset = nocaps_eval(transform_test, config['image_root'], config['ann_root'], 'val') + test_dataset = nocaps_eval(transform_test, config['image_root'], config['ann_root'], 'test') + return val_dataset, test_dataset + + elif dataset=='retrieval_coco': + train_dataset = coco_karpathy_train(transform_train, config['image_root'], config['ann_root']) + val_dataset = coco_karpathy_retrieval_eval(transform_test, config['image_root'], config['ann_root'], 'val') + test_dataset = coco_karpathy_retrieval_eval(transform_test, config['image_root'], config['ann_root'], 'test') + return train_dataset, val_dataset, test_dataset + + elif dataset=='retrieval_flickr': + train_dataset = flickr30k_train(transform_train, config['image_root'], config['ann_root']) + val_dataset = flickr30k_retrieval_eval(transform_test, config['image_root'], config['ann_root'], 'val') + test_dataset = flickr30k_retrieval_eval(transform_test, config['image_root'], config['ann_root'], 'test') + return train_dataset, val_dataset, test_dataset + + elif dataset=='vqa': + train_dataset = vqa_dataset(transform_train, config['ann_root'], config['vqa_root'], config['vg_root'], + train_files = config['train_files'], split='train') + test_dataset = vqa_dataset(transform_test, config['ann_root'], config['vqa_root'], config['vg_root'], split='test') + return train_dataset, test_dataset + + elif dataset=='nlvr': + train_dataset = nlvr_dataset(transform_train, config['image_root'], config['ann_root'],'train') + val_dataset = nlvr_dataset(transform_test, config['image_root'], config['ann_root'],'val') + test_dataset = nlvr_dataset(transform_test, config['image_root'], config['ann_root'],'test') + return train_dataset, val_dataset, test_dataset + + +def create_sampler(datasets, shuffles, num_tasks, global_rank): + samplers = [] + for dataset,shuffle in zip(datasets,shuffles): + sampler = torch.utils.data.DistributedSampler(dataset, num_replicas=num_tasks, rank=global_rank, shuffle=shuffle) + samplers.append(sampler) + return samplers + + +def create_loader(datasets, samplers, batch_size, num_workers, is_trains, collate_fns): + loaders = [] + for dataset,sampler,bs,n_worker,is_train,collate_fn in zip(datasets,samplers,batch_size,num_workers,is_trains,collate_fns): + if is_train: + shuffle = (sampler is None) + drop_last = True + else: + shuffle = False + drop_last = False + loader = DataLoader( + dataset, + batch_size=bs, + num_workers=n_worker, + pin_memory=True, + sampler=sampler, + shuffle=shuffle, + collate_fn=collate_fn, + drop_last=drop_last, + ) + loaders.append(loader) + return loaders + diff --git a/repositories/blip/data/coco_karpathy_dataset.py b/repositories/blip/data/coco_karpathy_dataset.py new file mode 100644 index 000000000..a34d29205 --- /dev/null +++ b/repositories/blip/data/coco_karpathy_dataset.py @@ -0,0 +1,126 @@ +import os +import json + +from torch.utils.data import Dataset +from torchvision.datasets.utils import download_url + +from PIL import Image + +from data.utils import pre_caption + +class coco_karpathy_train(Dataset): + def __init__(self, transform, image_root, ann_root, max_words=30, prompt=''): + ''' + image_root (string): Root directory of images (e.g. coco/images/) + ann_root (string): directory to store the annotation file + ''' + url = 'https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_train.json' + filename = 'coco_karpathy_train.json' + + download_url(url,ann_root) + + self.annotation = json.load(open(os.path.join(ann_root,filename),'r')) + self.transform = transform + self.image_root = image_root + self.max_words = max_words + self.prompt = prompt + + self.img_ids = {} + n = 0 + for ann in self.annotation: + img_id = ann['image_id'] + if img_id not in self.img_ids.keys(): + self.img_ids[img_id] = n + n += 1 + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + ann = self.annotation[index] + + image_path = os.path.join(self.image_root,ann['image']) + image = Image.open(image_path).convert('RGB') + image = self.transform(image) + + caption = self.prompt+pre_caption(ann['caption'], self.max_words) + + return image, caption, self.img_ids[ann['image_id']] + + +class coco_karpathy_caption_eval(Dataset): + def __init__(self, transform, image_root, ann_root, split): + ''' + image_root (string): Root directory of images (e.g. coco/images/) + ann_root (string): directory to store the annotation file + split (string): val or test + ''' + urls = {'val':'https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_val.json', + 'test':'https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_test.json'} + filenames = {'val':'coco_karpathy_val.json','test':'coco_karpathy_test.json'} + + download_url(urls[split],ann_root) + + self.annotation = json.load(open(os.path.join(ann_root,filenames[split]),'r')) + self.transform = transform + self.image_root = image_root + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + ann = self.annotation[index] + + image_path = os.path.join(self.image_root,ann['image']) + image = Image.open(image_path).convert('RGB') + image = self.transform(image) + + img_id = ann['image'].split('/')[-1].strip('.jpg').split('_')[-1] + + return image, int(img_id) + + +class coco_karpathy_retrieval_eval(Dataset): + def __init__(self, transform, image_root, ann_root, split, max_words=30): + ''' + image_root (string): Root directory of images (e.g. coco/images/) + ann_root (string): directory to store the annotation file + split (string): val or test + ''' + urls = {'val':'https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_val.json', + 'test':'https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_test.json'} + filenames = {'val':'coco_karpathy_val.json','test':'coco_karpathy_test.json'} + + download_url(urls[split],ann_root) + + self.annotation = json.load(open(os.path.join(ann_root,filenames[split]),'r')) + self.transform = transform + self.image_root = image_root + + self.text = [] + self.image = [] + self.txt2img = {} + self.img2txt = {} + + txt_id = 0 + for img_id, ann in enumerate(self.annotation): + self.image.append(ann['image']) + self.img2txt[img_id] = [] + for i, caption in enumerate(ann['caption']): + self.text.append(pre_caption(caption,max_words)) + self.img2txt[img_id].append(txt_id) + self.txt2img[txt_id] = img_id + txt_id += 1 + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + image_path = os.path.join(self.image_root, self.annotation[index]['image']) + image = Image.open(image_path).convert('RGB') + image = self.transform(image) + + return image, index \ No newline at end of file diff --git a/repositories/blip/data/flickr30k_dataset.py b/repositories/blip/data/flickr30k_dataset.py new file mode 100644 index 000000000..018ab3870 --- /dev/null +++ b/repositories/blip/data/flickr30k_dataset.py @@ -0,0 +1,93 @@ +import os +import json + +from torch.utils.data import Dataset +from torchvision.datasets.utils import download_url + +from PIL import Image + +from data.utils import pre_caption + +class flickr30k_train(Dataset): + def __init__(self, transform, image_root, ann_root, max_words=30, prompt=''): + ''' + image_root (string): Root directory of images (e.g. flickr30k/) + ann_root (string): directory to store the annotation file + ''' + url = 'https://storage.googleapis.com/sfr-vision-language-research/datasets/flickr30k_train.json' + filename = 'flickr30k_train.json' + + download_url(url,ann_root) + + self.annotation = json.load(open(os.path.join(ann_root,filename),'r')) + self.transform = transform + self.image_root = image_root + self.max_words = max_words + self.prompt = prompt + + self.img_ids = {} + n = 0 + for ann in self.annotation: + img_id = ann['image_id'] + if img_id not in self.img_ids.keys(): + self.img_ids[img_id] = n + n += 1 + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + ann = self.annotation[index] + + image_path = os.path.join(self.image_root,ann['image']) + image = Image.open(image_path).convert('RGB') + image = self.transform(image) + + caption = self.prompt+pre_caption(ann['caption'], self.max_words) + + return image, caption, self.img_ids[ann['image_id']] + + +class flickr30k_retrieval_eval(Dataset): + def __init__(self, transform, image_root, ann_root, split, max_words=30): + ''' + image_root (string): Root directory of images (e.g. flickr30k/) + ann_root (string): directory to store the annotation file + split (string): val or test + ''' + urls = {'val':'https://storage.googleapis.com/sfr-vision-language-research/datasets/flickr30k_val.json', + 'test':'https://storage.googleapis.com/sfr-vision-language-research/datasets/flickr30k_test.json'} + filenames = {'val':'flickr30k_val.json','test':'flickr30k_test.json'} + + download_url(urls[split],ann_root) + + self.annotation = json.load(open(os.path.join(ann_root,filenames[split]),'r')) + self.transform = transform + self.image_root = image_root + + self.text = [] + self.image = [] + self.txt2img = {} + self.img2txt = {} + + txt_id = 0 + for img_id, ann in enumerate(self.annotation): + self.image.append(ann['image']) + self.img2txt[img_id] = [] + for i, caption in enumerate(ann['caption']): + self.text.append(pre_caption(caption,max_words)) + self.img2txt[img_id].append(txt_id) + self.txt2img[txt_id] = img_id + txt_id += 1 + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + image_path = os.path.join(self.image_root, self.annotation[index]['image']) + image = Image.open(image_path).convert('RGB') + image = self.transform(image) + + return image, index \ No newline at end of file diff --git a/repositories/blip/data/nlvr_dataset.py b/repositories/blip/data/nlvr_dataset.py new file mode 100644 index 000000000..a8d6b2d7c --- /dev/null +++ b/repositories/blip/data/nlvr_dataset.py @@ -0,0 +1,78 @@ +import os +import json +import random + +from torch.utils.data import Dataset +from torchvision.datasets.utils import download_url + +from PIL import Image + +from data.utils import pre_caption + +class nlvr_dataset(Dataset): + def __init__(self, transform, image_root, ann_root, split): + ''' + image_root (string): Root directory of images + ann_root (string): directory to store the annotation file + split (string): train, val or test + ''' + urls = {'train':'https://storage.googleapis.com/sfr-vision-language-research/datasets/nlvr_train.json', + 'val':'https://storage.googleapis.com/sfr-vision-language-research/datasets/nlvr_dev.json', + 'test':'https://storage.googleapis.com/sfr-vision-language-research/datasets/nlvr_test.json'} + filenames = {'train':'nlvr_train.json','val':'nlvr_dev.json','test':'nlvr_test.json'} + + download_url(urls[split],ann_root) + self.annotation = json.load(open(os.path.join(ann_root,filenames[split]),'r')) + + self.transform = transform + self.image_root = image_root + + + def __len__(self): + return len(self.annotation) + + + def __getitem__(self, index): + + ann = self.annotation[index] + + image0_path = os.path.join(self.image_root,ann['images'][0]) + image0 = Image.open(image0_path).convert('RGB') + image0 = self.transform(image0) + + image1_path = os.path.join(self.image_root,ann['images'][1]) + image1 = Image.open(image1_path).convert('RGB') + image1 = self.transform(image1) + + sentence = pre_caption(ann['sentence'], 40) + + if ann['label']=='True': + label = 1 + else: + label = 0 + + words = sentence.split(' ') + + if 'left' not in words and 'right' not in words: + if random.random()<0.5: + return image0, image1, sentence, label + else: + return image1, image0, sentence, label + else: + if random.random()<0.5: + return image0, image1, sentence, label + else: + new_words = [] + for word in words: + if word=='left': + new_words.append('right') + elif word=='right': + new_words.append('left') + else: + new_words.append(word) + + sentence = ' '.join(new_words) + return image1, image0, sentence, label + + + \ No newline at end of file diff --git a/repositories/blip/data/nocaps_dataset.py b/repositories/blip/data/nocaps_dataset.py new file mode 100644 index 000000000..ba0bed06d --- /dev/null +++ b/repositories/blip/data/nocaps_dataset.py @@ -0,0 +1,32 @@ +import os +import json + +from torch.utils.data import Dataset +from torchvision.datasets.utils import download_url + +from PIL import Image + +class nocaps_eval(Dataset): + def __init__(self, transform, image_root, ann_root, split): + urls = {'val':'https://storage.googleapis.com/sfr-vision-language-research/datasets/nocaps_val.json', + 'test':'https://storage.googleapis.com/sfr-vision-language-research/datasets/nocaps_test.json'} + filenames = {'val':'nocaps_val.json','test':'nocaps_test.json'} + + download_url(urls[split],ann_root) + + self.annotation = json.load(open(os.path.join(ann_root,filenames[split]),'r')) + self.transform = transform + self.image_root = image_root + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + ann = self.annotation[index] + + image_path = os.path.join(self.image_root,ann['image']) + image = Image.open(image_path).convert('RGB') + image = self.transform(image) + + return image, int(ann['img_id']) \ No newline at end of file diff --git a/repositories/blip/data/pretrain_dataset.py b/repositories/blip/data/pretrain_dataset.py new file mode 100644 index 000000000..703d543ab --- /dev/null +++ b/repositories/blip/data/pretrain_dataset.py @@ -0,0 +1,59 @@ +import json +import os +import random + +from torch.utils.data import Dataset + +from PIL import Image +from PIL import ImageFile +ImageFile.LOAD_TRUNCATED_IMAGES = True +Image.MAX_IMAGE_PIXELS = None + +from data.utils import pre_caption +import os,glob + +class pretrain_dataset(Dataset): + def __init__(self, ann_file, laion_path, transform): + + self.ann_pretrain = [] + for f in ann_file: + print('loading '+f) + ann = json.load(open(f,'r')) + self.ann_pretrain += ann + + self.laion_path = laion_path + if self.laion_path: + self.laion_files = glob.glob(os.path.join(laion_path,'*.json')) + + print('loading '+self.laion_files[0]) + with open(self.laion_files[0],'r') as f: + self.ann_laion = json.load(f) + + self.annotation = self.ann_pretrain + self.ann_laion + else: + self.annotation = self.ann_pretrain + + self.transform = transform + + + def reload_laion(self, epoch): + n = epoch%len(self.laion_files) + print('loading '+self.laion_files[n]) + with open(self.laion_files[n],'r') as f: + self.ann_laion = json.load(f) + + self.annotation = self.ann_pretrain + self.ann_laion + + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + ann = self.annotation[index] + + image = Image.open(ann['image']).convert('RGB') + image = self.transform(image) + caption = pre_caption(ann['caption'],30) + + return image, caption \ No newline at end of file diff --git a/repositories/blip/data/utils.py b/repositories/blip/data/utils.py new file mode 100644 index 000000000..628894844 --- /dev/null +++ b/repositories/blip/data/utils.py @@ -0,0 +1,112 @@ +import re +import json +import os + +import torch +import torch.distributed as dist + +import utils + +def pre_caption(caption,max_words=50): + caption = re.sub( + r"([.!\"()*#:;~])", + ' ', + caption.lower(), + ) + caption = re.sub( + r"\s{2,}", + ' ', + caption, + ) + caption = caption.rstrip('\n') + caption = caption.strip(' ') + + #truncate caption + caption_words = caption.split(' ') + if len(caption_words)>max_words: + caption = ' '.join(caption_words[:max_words]) + + return caption + +def pre_question(question,max_ques_words=50): + question = re.sub( + r"([.!\"()*#:;~])", + '', + question.lower(), + ) + question = question.rstrip(' ') + + #truncate question + question_words = question.split(' ') + if len(question_words)>max_ques_words: + question = ' '.join(question_words[:max_ques_words]) + + return question + + +def save_result(result, result_dir, filename, remove_duplicate=''): + result_file = os.path.join(result_dir, '%s_rank%d.json'%(filename,utils.get_rank())) + final_result_file = os.path.join(result_dir, '%s.json'%filename) + + json.dump(result,open(result_file,'w')) + + dist.barrier() + + if utils.is_main_process(): + # combine results from all processes + result = [] + + for rank in range(utils.get_world_size()): + result_file = os.path.join(result_dir, '%s_rank%d.json'%(filename,rank)) + res = json.load(open(result_file,'r')) + result += res + + if remove_duplicate: + result_new = [] + id_list = [] + for res in result: + if res[remove_duplicate] not in id_list: + id_list.append(res[remove_duplicate]) + result_new.append(res) + result = result_new + + json.dump(result,open(final_result_file,'w')) + print('result file saved to %s'%final_result_file) + + return final_result_file + + + +from pycocotools.coco import COCO +from pycocoevalcap.eval import COCOEvalCap +from torchvision.datasets.utils import download_url + +def coco_caption_eval(coco_gt_root, results_file, split): + urls = {'val':'https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_val_gt.json', + 'test':'https://storage.googleapis.com/sfr-vision-language-research/datasets/coco_karpathy_test_gt.json'} + filenames = {'val':'coco_karpathy_val_gt.json','test':'coco_karpathy_test_gt.json'} + + download_url(urls[split],coco_gt_root) + annotation_file = os.path.join(coco_gt_root,filenames[split]) + + # create coco object and coco_result object + coco = COCO(annotation_file) + coco_result = coco.loadRes(results_file) + + # create coco_eval object by taking coco and coco_result + coco_eval = COCOEvalCap(coco, coco_result) + + # evaluate on a subset of images by setting + # coco_eval.params['image_id'] = coco_result.getImgIds() + # please remove this line when evaluating the full validation set + # coco_eval.params['image_id'] = coco_result.getImgIds() + + # evaluate results + # SPICE will take a few minutes the first time, but speeds up due to caching + coco_eval.evaluate() + + # print output evaluation scores + for metric, score in coco_eval.eval.items(): + print(f'{metric}: {score:.3f}') + + return coco_eval \ No newline at end of file diff --git a/repositories/blip/data/video_dataset.py b/repositories/blip/data/video_dataset.py new file mode 100644 index 000000000..0a6f8a611 --- /dev/null +++ b/repositories/blip/data/video_dataset.py @@ -0,0 +1,110 @@ +from torch.utils.data import Dataset +from torchvision.datasets.utils import download_url + +from PIL import Image +import torch +import numpy as np +import random +import decord +from decord import VideoReader +import json +import os +from data.utils import pre_caption + +decord.bridge.set_bridge("torch") + +class ImageNorm(object): + """Apply Normalization to Image Pixels on GPU + """ + def __init__(self, mean, std): + self.mean = torch.tensor(mean).view(1, 3, 1, 1) + self.std = torch.tensor(std).view(1, 3, 1, 1) + + def __call__(self, img): + + if torch.max(img) > 1 and self.mean.max() <= 1: + img.div_(255.) + return img.sub_(self.mean).div_(self.std) + +def load_jsonl(filename): + with open(filename, "r") as f: + return [json.loads(l.strip("\n")) for l in f.readlines()] + + +class VideoDataset(Dataset): + + def __init__(self, video_root, ann_root, num_frm=4, frm_sampling_strategy="rand", max_img_size=384, video_fmt='.mp4'): + ''' + image_root (string): Root directory of video + ann_root (string): directory to store the annotation file + ''' + url = 'https://storage.googleapis.com/sfr-vision-language-research/datasets/msrvtt_test.jsonl' + filename = 'msrvtt_test.jsonl' + + download_url(url,ann_root) + self.annotation = load_jsonl(os.path.join(ann_root,filename)) + + self.num_frm = num_frm + self.frm_sampling_strategy = frm_sampling_strategy + self.max_img_size = max_img_size + self.video_root = video_root + self.video_fmt = video_fmt + self.img_norm = ImageNorm(mean=(0.48145466, 0.4578275, 0.40821073), std=(0.26862954, 0.26130258, 0.27577711)) + + self.text = [pre_caption(ann['caption'],40) for ann in self.annotation] + self.txt2video = [i for i in range(len(self.annotation))] + self.video2txt = self.txt2video + + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + ann = self.annotation[index] + + video_path = os.path.join(self.video_root, ann['clip_name'] + self.video_fmt) + + vid_frm_array = self._load_video_from_path_decord(video_path, height=self.max_img_size, width=self.max_img_size) + + video = self.img_norm(vid_frm_array.float()) + + return video, ann['clip_name'] + + + + def _load_video_from_path_decord(self, video_path, height=None, width=None, start_time=None, end_time=None, fps=-1): + try: + if not height or not width: + vr = VideoReader(video_path) + else: + vr = VideoReader(video_path, width=width, height=height) + + vlen = len(vr) + + if start_time or end_time: + assert fps > 0, 'must provide video fps if specifying start and end time.' + + start_idx = min(int(start_time * fps), vlen) + end_idx = min(int(end_time * fps), vlen) + else: + start_idx, end_idx = 0, vlen + + if self.frm_sampling_strategy == 'uniform': + frame_indices = np.arange(start_idx, end_idx, vlen / self.num_frm, dtype=int) + elif self.frm_sampling_strategy == 'rand': + frame_indices = sorted(random.sample(range(vlen), self.num_frm)) + elif self.frm_sampling_strategy == 'headtail': + frame_indices_head = sorted(random.sample(range(vlen // 2), self.num_frm // 2)) + frame_indices_tail = sorted(random.sample(range(vlen // 2, vlen), self.num_frm // 2)) + frame_indices = frame_indices_head + frame_indices_tail + else: + raise NotImplementedError('Invalid sampling strategy {} '.format(self.frm_sampling_strategy)) + + raw_sample_frms = vr.get_batch(frame_indices) + except Exception as e: + return None + + raw_sample_frms = raw_sample_frms.permute(0, 3, 1, 2) + + return raw_sample_frms diff --git a/repositories/blip/data/vqa_dataset.py b/repositories/blip/data/vqa_dataset.py new file mode 100644 index 000000000..92ec1df42 --- /dev/null +++ b/repositories/blip/data/vqa_dataset.py @@ -0,0 +1,88 @@ +import os +import json +import random +from PIL import Image + +import torch +from torch.utils.data import Dataset +from data.utils import pre_question + +from torchvision.datasets.utils import download_url + +class vqa_dataset(Dataset): + def __init__(self, transform, ann_root, vqa_root, vg_root, train_files=[], split="train"): + self.split = split + + self.transform = transform + self.vqa_root = vqa_root + self.vg_root = vg_root + + if split=='train': + urls = {'vqa_train':'https://storage.googleapis.com/sfr-vision-language-research/datasets/vqa_train.json', + 'vqa_val':'https://storage.googleapis.com/sfr-vision-language-research/datasets/vqa_val.json', + 'vg_qa':'https://storage.googleapis.com/sfr-vision-language-research/datasets/vg_qa.json'} + + self.annotation = [] + for f in train_files: + download_url(urls[f],ann_root) + self.annotation += json.load(open(os.path.join(ann_root,'%s.json'%f),'r')) + else: + download_url('https://storage.googleapis.com/sfr-vision-language-research/datasets/vqa_test.json',ann_root) + self.annotation = json.load(open(os.path.join(ann_root,'vqa_test.json'),'r')) + + download_url('https://storage.googleapis.com/sfr-vision-language-research/datasets/answer_list.json',ann_root) + self.answer_list = json.load(open(os.path.join(ann_root,'answer_list.json'),'r')) + + + def __len__(self): + return len(self.annotation) + + def __getitem__(self, index): + + ann = self.annotation[index] + + if ann['dataset']=='vqa': + image_path = os.path.join(self.vqa_root,ann['image']) + elif ann['dataset']=='vg': + image_path = os.path.join(self.vg_root,ann['image']) + + image = Image.open(image_path).convert('RGB') + image = self.transform(image) + + if self.split == 'test': + question = pre_question(ann['question']) + question_id = ann['question_id'] + return image, question, question_id + + + elif self.split=='train': + + question = pre_question(ann['question']) + + if ann['dataset']=='vqa': + answer_weight = {} + for answer in ann['answer']: + if answer in answer_weight.keys(): + answer_weight[answer] += 1/len(ann['answer']) + else: + answer_weight[answer] = 1/len(ann['answer']) + + answers = list(answer_weight.keys()) + weights = list(answer_weight.values()) + + elif ann['dataset']=='vg': + answers = [ann['answer']] + weights = [0.2] + + return image, question, answers, weights + + +def vqa_collate_fn(batch): + image_list, question_list, answer_list, weight_list, n = [], [], [], [], [] + for image, question, answer, weights in batch: + image_list.append(image) + question_list.append(question) + weight_list += weights + answer_list += answer + n.append(len(answer)) + return torch.stack(image_list,dim=0), question_list, answer_list, torch.Tensor(weight_list), n \ No newline at end of file diff --git a/repositories/blip/demo.ipynb b/repositories/blip/demo.ipynb new file mode 100644 index 000000000..3077a1a42 --- /dev/null +++ b/repositories/blip/demo.ipynb @@ -0,0 +1,301 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2b949f9f", + "metadata": {}, + "source": [ + "# BLIP: Inference Demo\n", + " - [Image Captioning](#Image-Captioning)\n", + " - [VQA](#VQA)\n", + " - [Feature Extraction](#Feature-Extraction)\n", + " - [Image Text Matching](#Image-Text-Matching)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "cbcb066b", + "metadata": {}, + "outputs": [], + "source": [ + "# install requirements\n", + "import sys\n", + "if 'google.colab' in sys.modules:\n", + " print('Running in Colab.')\n", + " !pip3 install transformers==4.15.0 timm==0.4.12 fairscale==0.4.4\n", + " !git clone https://github.com/salesforce/BLIP\n", + " %cd BLIP" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a811a65f", + "metadata": {}, + "outputs": [], + "source": [ + "from PIL import Image\n", + "import requests\n", + "import torch\n", + "from torchvision import transforms\n", + "from torchvision.transforms.functional import InterpolationMode\n", + "\n", + "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n", + "\n", + "def load_demo_image(image_size,device):\n", + " img_url = 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/demo.jpg' \n", + " raw_image = Image.open(requests.get(img_url, stream=True).raw).convert('RGB') \n", + "\n", + " w,h = raw_image.size\n", + " display(raw_image.resize((w//5,h//5)))\n", + " \n", + " transform = transforms.Compose([\n", + " transforms.Resize((image_size,image_size),interpolation=InterpolationMode.BICUBIC),\n", + " transforms.ToTensor(),\n", + " transforms.Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711))\n", + " ]) \n", + " image = transform(raw_image).unsqueeze(0).to(device) \n", + " return image" + ] + }, + { + "cell_type": "markdown", + "id": "f72f4406", + "metadata": {}, + "source": [ + "# Image Captioning\n", + "Perform image captioning using finetuned BLIP model" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6835daef", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAZkAAAERCAIAAAAmJE0sAAEAAElEQVR4nOT9SbMsSXYmiH3nqJq53+FNEfFizIwhByAHFBJAAawBBaBQ1V0sVkt1C4UtLWyyueGK0iv+EG4owh2nRZOU5oIlTaFQqltqLrALKCAHZCLHyJjHF2+87w7ubmaq53ChqubmNrm53/teBNCKxAu/ZjocnT79ztGjavRP//iPDDExExETGyIiYmYKP0DtwOuHAJgZAIM4/AkmhJ9kiAAiApEi/Br4N/wIof7dektEqtp9CyA9AqWfiihEJ8919M1XCgAQgABKf26k7Q0tqbqpet+ORAgZ1hFGkjejDck2LsNIwpD5Tq+uJP+/ROEyVRjvkVbPbi2oG7837R7NvlP8utzxVNPzHIo51D7WIAEZs8E2INt8CABEDLCCANKAPeCIF0rUnqjYBK/mjG3CE/pmaQv1Wq9UFbSJbp1GCHFb8tQNUucffo+3IHYfHL3w1KxvN7feRtiKYlsz2Ts8CQy6Qmj7YqLkJaXaChDj61kYb3sI8IRacoowdYSJkyIES0wcEAzE6Q8CMRE1YIt7YI2IiHWNMcRECGCniZH1c7Huj9637Yd1ZeqHsaieGvYBB21G0/S/EB/YRNUpLdiLSq0IvYKNdNIWvMZazqFovc+ns7O9R/9QaOU2TvqaYL2fDM2ETwLdWhnWrdp9Hp5Mn43dt3WvTa/I0HjbKZNm5KFU46tyN9XWEVgXV+cwReY6gmUOKEZMgXRhjWKIGNbCtfUfNWIFUqNg0i6EdRGqi1xNubtJkPCr3U9AzcW6bdrMWVWDOhwarfFvf8Lxtrt8tNZk2wqUW1Gs9bAbrR4i4wIPTb9WhKbY44rMOHar6pBu0ipoSKRmSzYTdn+PCDkUeinwxFWkJdiuE7ub58gqtTWfZibTgb7b40Oy9SZsVbw1qcchvvt769C1hpQCK6MAZGDShF4gAkF5jVoRqhjKqKkYEcANItaqTOvPrpTjdYj/Dle7a0cbqi0AQFoDdARNuslb87Yu/fJKROvJCJo0Zb5kuUPTvre+vcknIvv00B36Q2KMyD+xiN58uqVMzHan8ISyfRLhL4uotlYf11okM9IyThQM9wHFqI4ZgQwEBQOkaNKx7o/ef0MIv7lWFptkrRF1GxnZeJ+mWf1Amz/GwXS0lDY6j9Sry8DH7Vzd58343TW/9XyI8HdTTWR2LbFHZG4lHMLB7vMpKvOQeFv10OlA/4TweryjL7MItTTxvQXbVYb9Fu+h+g4N+JF8xqmuJYq7mJFhRWSpiRqB4mYlRR00aZQJbTjsIA5DWHPa90IAJ+YV3zYharPafTAU/hKAOw1EgAKSsu9Z8OtGnDImRuJ0Xw2hz8TQ7a3mSJouW3fQN/OZMleHoGpiFfZ43guC42vDFOLWTLLrVLyS0Gp57ItorSVk76xaDT6xWerSx20L42WNxJkSoTeOZQrbmMHcj2D871r6UWNZQLFgVUPcsmxFw0Bz9/yLhkW/jtxJ0orQ+F1vTXahBA3T/m5rfuvVZUbzlPExEmcrRO5X4v/AQy8IXmG7bSXgT0I3b+a8X0X2pmm7pro86vXmY5t4Fb0zNiGsTpmADA1G1iBTHWm6ObSjTQC+bp6pt8AcrNQAwshoLeZ1If2lj7RR60nvyBvPsLmA9FZkaLhPsf1NEX685YdifkHCuMC9T5q/uzpsM9rQ211nzq6Re6doq1KXt7p25850qjVEh6ekHQrd8d80dAxlPhHsWiFiGTNHP4xNFIuusNFABiQgo8iF1lxs5Ac2Z06kXSPCdkTvGxmawFCDI1sdJRG+fUIv7ozH2S+3oVfMvPeA/gKi0hchjMxJmqx17qRPTZFnvIgvQthb/x0Ba2wCwkS4nFii5YaG2Q7JBBUYGYhYQcQAdVXL+s9uBXibvjn0Z1NcIgIEAHOgYCGChPehRpeBnlbRe3Cx6WUN5T8lk654XYp3GVC7vPrTuxG5U857W7Vb5Q61yZQVqzfb6fsMewjf6tkuyO6HoVPwotXgl7Hi4dJYj2kyd0u0HLzIEMCqAUzBzQK0Jl8aUkc3rSaQ9f6bfsTc0BlkXRDsxAlNHKoU/1UNeTaxfyNhd4+vWeHu84lhV+gZL25Itq6dvrt302yuEQH2C91m3DuH7vPL77FcPgwBWWv+dCdkrRZ1UeZqDW1NAS5j/2oG2lTrun+2Infl2aO46ZF7y+qu1uPbApaJkr9FxBQG1fpjgisKzIeRNMQWkFGMsEav+s9RCtY3aGtrvST+RZ0Ibahq5lPn35o5U4ZFF1IvEyZm0ou8TyFsHRy9T8aNUFtL3FHGL1DYT/hdl4QpK99lwq4o83TgbGtWzR9D0GZTpABPGogYc4NbEaITBoG0oVqGhADCJkBzQgZo25SjlqbxRBqIJylREBSN7PvzGXo1nmRoiqIzS8f5xVCEoQk/JeehgtDpzisJNRMcz5M2OWNXts/F0NM0IV9hts3B0EvNur/rHm+xnvrtlCExLszWEjGtH5s/eqUdSbVVS52++A0VNN1k2ZutrX3HCMQAJ/WtpmkJyIgBorUb7YYVrIUaHayJf6ZmTxBWx6m1QmjjjHczh6E/t4LaRKTbkDPINGGrsZXbkKkIfWNiKP/W4tMr/MQwZapvjdA7wj53Q/XQyrQ17KTnjgy8bgcNKaHTDV5TwKgXT7emHQk7ceoRi+F4wikFDWH3UD6t9rQATHDf5xqeGkCWUCcwLxoGmvF/IwXj8Hvtkd8dMVuBbCRc7So9ERn3LnHiKnqZcLWc5crD1rm9a2546iDbHHLd6lxtBa88jDPKoSRPp0Z7GAqtCf4WyT8WtQpJ0YzGDQpGm+5azV7chKeQWDcjA40jROGfVibNamAYTSYCSjfDkci9z5udPaXE3rStyEND//K4c+XjbOJSPDFCl7pOMb1Nr9QlbXbNXt6Dz3blfEK41jtf9si/OctajHKiANP5Zivt9FJ649cP2/aycP6ovkNiDWRIThrhOTaMZZs/gI2bcyj4fCWoWr9F3wjorcP4qynPh+CjG20PQ8BQ6HqHjaBh/fCLs3S38Hc/u/V06Nk6/59mmLLaTclhqApfnF4OYai+O2HiF2oA2xY2EdWbkvGGRYRHa/CS4DEWatFkSem31pnVz1v/og/URgbTTgC3E+RhoCO7JU5cqHshYHwN32/mdMOToDldC+AIwLWarv5zpFDqaDdd9N+VcWwNV55hK3MMTO9uv08RY6vl9PMClNbUGLKrTp9Bu5be4WXdENVJ0PpGHyQHNKDvVNDWNW0ivkwHpumRd8qzFw6GIu+Rf/12Sr9efsp1k0/ZsWr+OYSPTehpytmFPN08xN5Cul4ZxhnreA5TwlOY9t1FcTzaeJyJJaKz0uxR06tVF6ZMyb0Lasm59vaPr9PojPeRgUijT0Yjh0b8YURr5TxewynzfzxC71ScCBlb4+waeiFgqDWeUHhyZV35Alv/nj6se3X58XH1NBt/SggWn63L+VMLveNzD3me8jivgw3Ctgz/rBQ3A7CBYRP/bYXeCL2DbISRjvzZfdW7JI6071bA3XXMXUlftuxWEy2mTTsuDTCm+s9xhXGoT5sMq6s0DbXbRKrSlK37sLfE+mHLft+byVPTxXrbDZ2KtLppuhG9Nn7vhNdbW2Bohk7hj/u1bW93T49ZP7TU+HxReGE0KZTxtFCbU9S/R0Cq+7DVNNOhaqhKQ6E1nbbi1JRX3Qy32sUnRtsaeudks5TmZOgiV3ed6JWnu4oM6adDS3cvy+iVdqQWvdn2itRbQUxD5z0kvJIwPupaSxdGp/dOq8J4id2cW6tRK7fmGBtab6YU1CxrIryOZEhEduNjSwBr2IAkauBRLzSMPOyFsz3CFBgagq39BuX0wTERDYeijYg33rvdGY6BSX61YTo3bIYnIcx+cDmUz1NouolhCEF2DRNhDpPbamRt2FW2KWVNQfDeaJZIkzqJeB8G0sUYm2U0/63f1l4IvXDWSt77djxmb5yRVM1qD0HbThxwerQpK0wrfmuJ6133Wt02olYM8Y6W0b23UkMtPLJK9ebQYn8junlL7Ilrw0ho5txLLnqLRqMjpmvxU+SZLnBTmJFCLwk93ThDHLk3Zu9QGZd5epze4ddLvkYiWMSPGYE1/uRNlbMeZ10gG5Kj9WrKMG1JtnXtnRLtkmEiNu0KYSOhF4yemn3nixku38VX24CfS3f8FR4DQwxjovrZDOkuxnRnP693MvuX5a0r8xB+TcS1cTq296txc0xvJuNcfQqdHA9dttUSrF6Kh/JsRehd50OXdTOZ8mSPMH2d2ylOM3T16xZT2MoImi2zU9FPNGylaS2DWou/j1PR3oKmc8yRWTBkdMPmjB63mXSf7MGRbbjnGum2shaQ9TKyZlv0zqLuxB5nc+OZDNW5W7GJz8fhcisq7ToBhiCj7rPW6NxqpEejxeq+HAHrXjgbb5adwnQDzfQMxyfhiNG6NWJ7tfhuyw/Nn+5cnT7/d63dSNHdQrca3fdYqKaDSG9B43pVd+3p1mh8fNbPWwXVfzKB4/eW4isigBTxX12nmQIuE6WZPu57G+UK5+FfutCq/tbGfAots1OHXj5cCX+cHq68rC/UWN1pwb4qVTewyImStN6OJLRE1Ni1JAIad2QgQhso7m4iROnnaN0/uyIO/dkbf2tuGF4nd8W7iatWHUa4Q1cJGhG1t75bTYcjOwAT5f8ih516qnds9HLb3oRNjtb83coHDVVuSJ69GdwQsR0HlFraS0JMr/xDc6rXNIFOY44X19pyGQnd9h/J0xKSYglqELSNSsYfCiLSxmnMCHGND/0OldRlhj3REIGymxydQTm0sbLTOjPyfI+EvR05DmToUO4Wx9mqbX1xwla6tB+f2i9Vtw17tfiRspr90h17mDAVp0PMxAq25NkJwoaKaCnaW4frFMBtitfb1FPgqRWzK2E3JjdZWP20WVgLMtaf+w0dikjkmvFr9BnKtvUwvRpcby85dZ/CzB9pwPBjfE5+AbHpr0zods301h6P2ezc6bntN557V+76ya4L3lVpi7uGXlY7vTXGLbMWiF+HawJLN0HPQwrfx1yTOQKSo21PDuiMqrYs1B+zN4duVlNsh9RHkrsRtmaCjhrYVWp6jfTNDJt6zXhZTyHsyqqGthpGUm2tS28RV9UCU1Cp+++4JaEp3ghdogYf7I2wX6BpxLA3WqtTxis7XZ5m0TuN5611acnZG4fTScyNYdOaY709XWMYYb1L0CvpELSNyz3+ezzV3tEuM84ub7n4qxd2mhtbB/TnG1qGqumjdOue45WEy4//KYvTFzlwBDKAWGuzf3yXvmKOviULHW5CyUmtGae341tAOWVYdFu2RXmGMmyGEWW7m8NE6Nza5a0IW00247k9oTCxFq35vFNoYf1Wc8xTm0sjbU6bJqTekY8O4br8IJwYto7SXlF3YhVT5kJvwm7RVxWG5LEAqDblN1wzupK1ednAZKa0RdBb3ggETESxZjP1IuxItkMRxvXwKflMR7SmtBM1tSsJU+q4X7YjeY7omBMn4VNAtO5isxVSu3Vp7i3sF3ZKO2JTn7iNOEWYIeyemBs1bC9bC9219ZrVDwl5kyIBtGH36Z2Bg71LEQyVNp93yv6ihZ1km65LXnJwX0m4DJPqhvEVaL9wySb63Fu4FVrL/CXbZ+8VeihMXCFGSGg3ty9ICLyslpsQlM1oCNsOZ+sK17uZGjcT6lr25oDO25HfzdDqjJFe2fqqqe5NHzTNoscXpaFXO8XpDd1leRyz9rajX6E9eChDGtiQGad74xFGwtYaTcy25bQxOC/69ny6EboC7L1R0M25y4yGsh232V9GmCnr+n6l1PFt/xoSvlOi7R5qybdOGBFs4wbaWtlEXx9PGbv7aShbW6TVvrtKUv/Z/HdrWVvDroaMnR6OLAwTJ/beoEYTtob3yLP7uynhiLRXpR9MzKc7h2nz+CRtmjiucAepi6cje6ytJL35DC2Tu4o0UQueCG21VOtv9278aDiuDk2G1jpP6Gxi0oay2ZtJ91Wd7ZUzgktG+4LQ6UtajkcW5PGEl6GQW+NcFb50M/yC9NoXLVx5+1xtDzaxZXrOG/ayNkKTgqRpQWvh17qkpF1uvMImPg4YEVpy15DcG3+EZfSK1xttaOnr5tCM2eUC45Rna+gu183n9dvmn1MsqeMLe3/3PQE02S/sMbXGleut9erdkdwpTBlyrfyHemGPzKeE1hx8Ep0+fcW6JEfB8Ajf4GXNItPvfYbIurH2E7aRT6ugofm/X7ZbO3UKOO4dhrjeyEDvjoZereELAkxPJ1yystNh5UmHpuLZfPiECrrybJ9oGGI/zWB75/PmbNdkE+tHvW4OTWto3EfQfmDbdYmYOP+3Pu8V4PIrxn5hylo9vNh8bmJPD9MlfHIVmSjD1W77joSh/EdMWkODfygM2YJblP+SpewRv/53J7vY1rCxjxn+rb9hvi6bNGwE1AawOn53HrYAjuvzTZvSdy2OzayGpm7zz27M3mjd582+HCE4veFqgWO8ChMF+AICWW//fr7h8mB6JV3fWu+xufC3yuq1Nlw+tMZ8L5R0Eap3xW1G7nZ6c7Jv3WRrJdfOvt9W0ORWK46ACBDcNfqtRZQCOnCjjU2Aca44XtsnHaaU9ZSn5ZXrAl8EWNkvTJT8L5321A1XUoW9kfcyCVtPwgQfAcGrDcyabl7sIAsagLV+S9Ibp1bCW0CGyMn6c+6i78QwErkWtbvUNKsTHj41naIVNIXWw9bv6XJO55V7SLtrGNozuWSeU17tUVxvzr0zc4owE0Nz/Debq/ujd2JeSaixpjvYRuywQ0aeLrUcomMTZRsqZShDS0T1xmEro+7Mjw8bdenywB5RFIRIzdqUbcdR2G2vEWY3Erm7Vjwhq1MrW214FY0nbGH9xPhXGOhybihdFL6qnEfKutrkI0NiaOTsOoq6nTvSbjuF6WJcVYnom9RPKFCfpYiBtWtYE/aGcBGNwwDT22vI0WwKvRoJu4oxnlXvcvQ0wxB7vZKsdg1PQr19omP9KevjT2fGThTmaWoV02flrt09vb69qaJPRvpwSTuLHrKWXNJar7rQQ82gaH5zc5zAD6HYyFTvFaArzB5FbA01Ak7ps53o1WUG6NMH5SnSDqknn68A++XWombTZdhJgBoOWkXstOxNXKR7p/B4ziNZjUg1ZZ7uoScxgLTTqNxydu2vyRgMjYRAzRh79nZvKV8oQ+/4YJoY+QtSo/0w/ZIRnkJ4EvxligJxhaG7Y4jRtt26zHfjt7K98o7bmuF+TRq/XVK76EOh3JNNI3cFlCKhS6k6kbvpoQnGqPXf0YT7suvW8ykwP735tm5Z0Ob2zfhCNNS1410+knCPaN2ixxNOz7YZs15yW2Xtms9l5HlCYWi3bid+0Wr23jHWNNVrn+tGr2DTq/CEwq4dNAXvWpGjr2z9yZI6QhMa+xQfBVQBgmlN7FanbnSkRiJYSzCOCHs8r2Vo7SrU8XflrvVw2Un723sAjdubm4N4YhG7DtCtc2PXMGW+Tcxn77dPIewxUIcid5dhbC4DvZogJqxAU0pvhW7pV5LtFYZaqs2bYwkU7pedOm83jHx1/MEk1FFQ9wpPk9I3B0rr1WVm19DbnRTVzzG0hBnvkadsJv8rFkYm1GVq/eQ65fJ7aE1mOr2Omy7+6XoM1vWJo15dKang6+S9pbbiY+DG4d4QInTnzNZUtcDjIu0UhhRJ7IVKl5x4O9Hvy2e+R247cdg98uyGLxTQt8IVytY7pId2HrbOlK3F7UHKdMeb8i7fOPVM580/I+I0ydoGcWtno1FD7SugT/AemtOt+ZS26DLBlsxbJNk9DI2hPfLfFf52LWK6BWpKEVNMAa3ebAqw1eo3knPz4RcZrcbDTliwU6jzHFHhd0KWKWn3hsheejHU73XM6e3GgNKmK3996f+4cLEsUmIJm5tbQYQasHdV8/ZzDJckLN2wx3DfOjImjrwpGnFrSevNuQVqW3v5qVnBrmRcjU/Uy+e/d6DhL4p3wyXNXrsyr6sNY13w8O6nRETEYfhtJGACEVQTbg2MY0qf10wq5/ZlnMB1oglSjsdprd5X2MpXm9sXNtDnvQm4a5giMG0aNP5yVfAyQQeOmgyxtl1b5kpacusa1sXluhbN6jRjcief9fIbP8+0DYOpT8cckTiCesp+Svi81sNxzevJldsb9sb6rWFKXXoZX3fR2qpi7CFeN9UUgafruVcePl/cpB23uT+X1foyhTZr18ynvldWezJXJd1SamMQo9Y0RyReP9mlLr20tndu7Kpj95bVzaFlmMATxtARwbbGaTHoKwy9Kkyzwder4JUqdDvtau2Hy1cbpuS/67b49DDkP9Rqvcs0wkRMGJFwPFrvvK4hbGgYcMOuoYDQZgBAsmUnMTxR1aaWig6ojQzENgBpO9UIJvZCz/R+Gtq269ZxSs5by73CST6e1ZVsR27NszUoNYWh5LtO1y56NnMYHxgjYWi2X0a2vcOVgxptcyquS3lCcDZ9n31kgvemqoGit4jOtYtoX+kDIJ5tWmPWhkC9f2K0SrT5pq0ObKtXb833mCpXHrYKcMnJPCWTq7X4bg29zb53Wc36PrUO3a+UK5dt4nrZK8nEtbxX2/jcZ81OYaRxGO11vg3b4d9wjrJ3QWug22CpLRWXiCjcm5b+1xEDmNbKdaqJ6N7K+apW1ysPba7aCUOvdmqH8YTjfbF3QSNh4rxqrs/7ifEkeOv0onWbm8tOy8M4YZkCWCNtONJQVzsApmc1pJfYTrxo8EfLsQhAUv4GuyFcpQ3uNYYNyZqeT1pYetnfHg36JAbuCLenzeNHUyJPJOq90XrHX2/MViYtiG8pdC3Jt1ZkouTUd8PyeGgT+R1Ln9ho+4WtFZmoT11JqvrhePKRSbRTuZdhuCMCTBzP3JeB9muaIJ4CUqQg0c0cmvDXP1F1WDnqfXrpcOXUegqHGkKNqxVsep7NYTQRIHr1lN4leuKCcfmZP5J2ykzeL+ehV18QlW0rBDRb5mrbZ9eREPW0vs+/Yxshbf5pgfV3fesxHZXAjgZKSP+kYobaIu4lbL7pRt7gqw3Y6lLCifVphUsuOE8htOjJHgknPu8NIyt5688hFtZdk1vEbVzCPWjd9NBdOXql2kOGPWTeT4G4TPIW4W0lr/uojrafYN3W6/bvk+hi2oQsRJ+M/mr0PRzxJeumJRkiVZfEkSfUOpcMe2sBW98OIU7vArhH245bZ7baFoeG8hRJPpd+7J3VT7nQpxC2WuWejgwTXw1xsemSc0hK/Vaw7ifXCNDaVD9MJpMotDNCUwq9z7GmjZNqODRq9x6+uyoXI9Om++ryVGvEUtsrW6vQ5io3RBibikCTgrVIdzPOuBi15L1dP65ijNdupKyJMT/HcEkhe4Gspce1uruXj08RoNVxQ52+XztPH/xrLRINwwdFhU8BofVhcyIgsDjWtl/FgKxr01uzHXvxiIgYBO3ncvWsG8Lv8dBSkfYevtNL3NqRQ/xlomyhQbrYMZ5Vs9mnzJYmkGkjdHGwNRlay8ZWkN261E1spaHaXSbmxHC1mDie2/TajScZmU3jf47L1ouPE9F572bccmlPkGG/rPeUSZU+v/3yncI442jO4S6Gtvp4P4AeKXcoq/2gfJwubWVAl+y+oUa7TJ7/Aw+0i4lwv+4LY2b64nSZELLaOA0+lLeq7yIaATzBAEQM0G5aIYGaHme43MBtosllbJxDOU+Rbatu2zvnW7x9a8LeON3FsMlwp3PArhit3umqGEOyNXWQOow0Ud3OXdAfilyXOMIFvlBo2EujrgQImg3SW0q3L1oF7YR6rUy2rnPj+UwptG6ocLd/Y8yll01R6sHZLlKp1jS7TbD5c5cdOgoeGv19ucEIhl81Jdl16k4Je1Cb1u9uf3dZfXOG946zZsxWi/UiY6sphkCqK1IvKAzNwBF1pjfnKZO2i5W90erfvUT4CvHrCscSphlb9wtDmbQ6orVgDI3JiWG8T6dnMiVaLW39CZIpqqyidU8ZIdrOtnWGIhjY+i1Em2UoVOtbG0dOO7WFHspw9M6TvcMlqWIXp3YqcSIu7zcZhqq2dYnevvYMyzOyFHfxaI/Gv1pKjmH+eFW5Xa20Q6G3lJEBMCXaEwpbG8QCUK3hrDGYelIqGucBhonYBuSn1RgIH2LS1vM1ymwspNQGqjpmN/ORctGZCZcPQ1OaOvt9mIwLvUO5S4WG4K/5u4vdQzGHBGtlMo4gvbVGozfHadqUOM3hMSTAeGOOP98jjPQFhkfIeIZXJdvW0F0Up6w3vQNyqIjuTNyDUuyUkNId2Z0XHfUtPdaws9l6T33bmn0hHL/cp+carK5tUL/CsAc/6j7vbfpuqomwMiVJb1lb9cShfPZu1ZHlbejJFHn2jrNTcROp7pRSnjJh2TXUNe3yrCcHqU8BrC0rdH2QKWjLHAgYh17Z7GMiSspmxxLR4GBD0q/1WQUwCekpRlcgltmlWlt5ShP+xjTcYSo0Jf6Ut+MTfkTXqOlwb6O18KubeYu51KO5BbLd9mmtHFsRqhVtiLNsJWK7LuPd0Oz6iZEnxt8p50uG/SBma6rufNk6mMejDY2cifJMCeMCMNJ2JAHApurX+NHMK/Q4IC3fMdYe9jTA/wNB6z+zOVaZZv5oT8KR+g9NrWbYFcgm5tOSRBuh+ecUeXp/tx7WE3JI7WpBSXMC9w64Vrkj0naZ6VDkZtGt30OIuV8Y74tu5Ok9/tTI13hTXBLrm5y92Wu9RW/tze6TOs+RkTMemqNiPHlLx2zYttJNs9yQcevICLI39c0t1J16DrGPB9K4OZC2Iq4sbKVIlwxTMmyysKGEI0A2VOgIQk2H0aH5P1GSXcN+Q3965jvJ2Y2897Sc8vCSYaJsrfUDDXI6FH8/ecZ1kasKHFRGViDdgdgdQy3z2WaEjbVXw3GofnMbMFSNCVf/r+ceoqikgGr4dzDnjhlrp3AlndpiH90chphznXA/ybuEtEnc0BnBveJtnX7d/FsyNEFzZI61SNzW2XiZ+TBlke9NgoGWmZjV1mXjkvjY4to7ZUID5pfWmNk1524ptJe600w7JADXdnvaTFn/XjdQXbGNHgVFxW8D0anz8fPBViABJImxYwdETfOJwDxGTQNDIvVG7qIGOgy8d+Vvpe2FA22EcQlHlL7Ww51UiW6SXlTq1rElzE7zZIhvTgmXx8ErzHAkn13n/FYxto7nVne04rTWgJH+Gl+0LrlID2XOG9FS7GbRzVyo9SjFoYYdqy6p+/HzbaFtQdt41zfZWhztSV11NjlMgYAmnO2xntO2HfQw5oYGYmv81QOrN9vW2O2F0d5SmqHFAbth4rAeWifGU10+PIUihsKUqb61eXcqbrzE6ZRqIkhdbdvWWKZEGv342/tQ2EC0HkFrnVKwOTTjFdhJ7nG0Dv8FRZ//raK351goqP7SisZXBMLuOkVv/JEeGiLnI7l11Zb6zy53a0XoNuaIFtClME1om1iFEQWkJUCdc5MwjjRdi3hujTYUroocXW140lC4tXmbYSha74rY+rMLZOP9NRJqmaeIPVREN23AsvrifRAo8ql2FmvmNVS+xlsx2ra2SJ2mCl3j356DIGqrms4a7Bu2dvDEV5cXoDuMWsAxJEwLLlsQuTWH5jyZuP7vSjm7Mn/RwiUh8ikg7HS6NDHo5rbj57tITC99feePqoIEJKDaobULnKNwFu+5VkA2FBbEDLtXbDcnFeoBHWxwa71RGyX3h+7SFDIlSl9HV6WQ/16m1l27szm8htRADLN67dvD7mqC2IZKveqbbrZArwBNitcUY/r63+rZiWGPJHXY2pvjuL9rblcYLg8W03OYUq/mmtf8MVLKdAGGBsb0ru+dCAB4bemK4ioo7UVKAKfWIr+Gs9GSNwlaZHqDdusBqTV9r0m0QQwx2nY1p0iCKCRomgGlUzaqvayt1+p2SZYx3klNXawZv6XBbRWjjtPb0+N65UjO41DY/d3ULluC9cJidzHbb24PCTZUkdbDnbpsj7B1xD6dsBPotEbmSKtubfxuaE38phIwUbzWEwaUoLVTRDQ9pVk+cDhpu75JYY+xo2+Sblc2B3LsaK+jg6N/4CaBKUDjE+POLTAaYkxDD4eGztCKtDVn9EHGUKruIOuFv62/ty7mQ+Hyc/vKkegvS+iiQ2+ciS08Qq6vhKP1yrZ32nBOqTuysf4UU4dw1uXW5cftgp7Zu9YQCRQQc4oNq7UaxOlEAEXvjZH+GF9pt7iyafykQSvt1iZuKXpbV7NmtN5XTZlb+fSOlRH0qV91cxgiVs0II+g5IvwQm6unR1OqPVb1bhihrlPyvCr4m95WGFCsRlajraW3VpTeYbCTIl/PPu04Ibay3TXsgZLdGd2MzPFPlWZGqtHTgQisG94VoI2ppa3bjvoECPpmc8hy7UzWqU8dbaCqGv8XQK2TcIgddBuFatapiY4289mMv7Xvu2K31rShbusdbc0k0yGs+WcXEHujjcz/ZoTBNmwoj80nvQXVENbtqZGih0rfmmrrwycUdiprZDEb6bWnKVhriLZAbSdMHJJqfBa0Inej1Q8bPhkqHZWxrWG2VM5mRep1fEiOtlhYE6wtMTeq2pRwzB8Nm2jb9zq1QtxgUNQAB6zNatPq0i0XOw6+iSNjPM/uIJuCbrtO9en1uhIQueQc/iKEPdphfA170uHKC52+dO0XH+07slUp+k80UUqAyF+0waTSvxu585gW3biFNjxR1NdhD4Uu72gtZcRKvN6B7Wp21HdTWA99Q1BiQZR04RZ2NwttCTlY5fUi1p2QtLlF2K1aU7yhkd0kLyMaX2/aVpyRtbEFiN2YvfStt+9aLd9dlv9qhKEG7IYplP9qZNoxw+5Ia+qbQ8P1MmFoXE0M3EwZr75QgFpmdkGa282QUunwdF7HTT82mZQqAaztM+77BYp3b4RT8WtEu+xU0XSaPYWJB+JbqIrNOb+rbFMIbCtmS8ntzWFIKevlcT1rwAASDUk7ffT/FaBjU8LT1ByvSownuvb0ahVTAkM39gdVFVDqGIzCqUmCkrRdGRI8q6poOlveeL4RM/w32qiajCB5yLJuxKeOf1Ovdh0exmikGg54Ni7TGIKVLmWLT5qqdNi1IKpBm/Ydgl0xhqwPvbrhUJM2F8mdKMBVRe4VmDadM7oVvJLlvbehJqZqtt7W/Hd63pqNV1LBPd6ORN5VpC6n3jX51gjd+T4xtzC0bKQwxFjfS60IgJAuZUwJwisiKClJtDW1ahhyII6noXomraqk4utXANau/qwAkQDUuaxxvGJt6hG/5gRVDUyTKHKsuppNwbZ2FW2kA1RBscY7uXf0qr3o9FC3w9CYeL1rVzOrOv5+xppu8ina0EjkbnX2Xg96e2piNbVztrnVkr3t1m3GoeJ6ozWtDbuGKyRBzZpOrE5vhNYIbObZHcPT5d815gYZWtvLIKTSHX19xixRCEi47e8VUsT/AtvPLaluUKcNWbHrrRl1hkC7UeoiouToWO72CNG+JgFa+nLT9W1wXUFHhB8J3Tjd7m+O1y6n68XHbugCxFbxWgAxJfnec/syNGdraDLcJ1rQEwrTe+pq8+/tl11Xmp1CS0uj4tEn68KI2rsBRAA3SUedOKihBGpcsd0r9IZH10CVmjadntcaHPaJhDaAYNcGaq8tSsRU39Y9klv3bXfdBhEapwYiY9u9B7vKb28VdLMdujjei+wt6oHJbTjUPlsX3pYY09FhbyIzIsYQ1eota+vDq5XwqYUR+jmeapyoXhXu9/K7EUnqhxZAsv8EyJKwiddUCcOR8xgxCk01tSFNV9BqUug28Di+0GE5mupecN9NUoUcEkio1kKEr86N1K0OzT6rZdPkfKsqMft17Ta3cRuZoDEOWkMhRegklUjPwssW16TUfmjg0RRUbanVvWrmSOjVynt5e6/a26z7yIC+5DzvHa/Ts60bamgGtt4OcYpWjZqjqDfbcXmmSP7kQlf4PRTAut/bs2nHMJKqd3x2JWk9ZNKWiqdQaW5jRjDafNg0GlHYiBSlXlVrHS38b0PWviC1SthUD4Ms9ZUblNDx6oZIoyyShh9tN4zpUBtDP4x+pahua9w5ofWdaw21fHPbBJfGgq1hCCy6v1tJmoN4KPlQDp9L6IVdbFNXwyLRZbXdOFco6h7h8xWgXoB3TXgZpao3cOBggG5CdQSmhoiqKlBBd5pR9MJvumiND5HJcmssFyBC8FCLo0cb3nBDp8Q7Vo/meoJON2wiiKqKQjWcTY+7vTVX7Tc/9VewbqrmXbuNKDVmqkJl4wJLqlsjPaXOvkGrj7t/aiO0onV4ZTth/XtIQ2zm3MKLiYN1oorazX9r/DrzFhcbitYVbGQkj3R978DYykH2DkM9OFTWJXXDbhfTZuhG2y/sKmdt+18ThDotxS8tNZ6rQoUCqDXKUI0XIVKDvg2NgcTOpi7pTVwDgFoqAjQehyIo178Hri1rTrleyfrmc/zfOl+S5MW2hoOkPrfp96SQtNt06j5eu5Z08ih5PI1AsZihtur+2apgc9w3oaobsxe2Rt721GxaI3y+tII6SvoIcD85Aa42wyca/wrD1VbcKoTAkYRF5wwAABEUTEiX7dQKUM0XgtdEk6AhTDeoEAUzEIlI33KqsYA0Z3vZRLPCnQiI6BaNXPF9Qx+IhwoUGw4T2ojTVPVb5oCh9lJVCoBC0dhPRKrS4E41nWqH/mxpME50KWnskqYnFJ2ZG5WhAX24TeI6dZzCIIbgsknutq75LbyYOI6nz7TxhaqV4Uj1h2QeejKS+cRwJWhyhbgwpSW1YY4cH04jYYgON7lzM/8tjV8++oB0/YXMyJp0YwEnSp5aKVpTDm0g2votGt73je/S9SHFuohxHMHmHGhpSUkYJTA6+5KR5fSCyY5GXIwymlCMQus2bCccLWDihFQgXcbWCaNmy9iVowb+wYQD0VqdsjW3K2ciO4WhiXFVUg3B+lZ5PsfQFHhveVoN22rPoebdWtz0bmJSAtJ5RoAULGGR1+5MRPSn2sy9GyswBlWKO4/B3tQfohLX2BnYNWzIE+mKpD/iu8AqWUGiPG3Edm1Mm2KvNZF2HFKKV+NKunYtKshEhMEsY7ZbKlhXZyATbSja/XE2V9HLa4LjRGbvrHZ69ZTVtM9XL77a0DKMXBWwdjWe3rBrcSNMmampPaaDhgmDgLQD0Ii2tqDVliJSASR+jamhwgWU4hhX0HXHbVcMNaLV2lCv3F1dph7Q6Yeoikb73fp5SBiMa9whSS3+WPfx0NgdaNkAqOkVqUK0vho3fSFmuBF6rFc9ZWmrF9rPU+EbG6YajqBJfzPuEfoMCP3RWsVtdtakgsZlmI4vzZGAvuadQi1HMh/5c6esnlDCkQx3XZZ6YzaHREth3Ck0x8Z474doNpq9IjsjQAgEcHDoCiahDvdSVZ80x/g2Gae7kYH4SGvFs5VXR9YAZ9RrA+qOvG2KUgJc6jnATqIgSK2Wp9ijSnHPVs7Qn+vnAEiioS9y1xq0u7ct9eXQynlgw6jfBpFaPq4QQNMZsCdyfzf2iNRa1YdCd0DvZ1oaF2bXmBNUbK0ZcGtxnVjclfOdKw9Dg3kPHbnbv7iE8t7UecdzCDGtarjg36whK41lbngLrFOh/luxkQaI3S4AafyQWxqyjboDAnBrrvRBhgJx86FpGroEva/1XGpJxABUKRnQ4z5r3wI71MFd+anPMroRwuVrsXJpvugTG/q6MSWjg4hG/tj08VWsm6du9179f0QB/8uohfVAGxlVIXEK1vDtHWKgvZU8JdspYavxqBV5ugxPJ0w3v16ylHqONMuyQFx7iDjARtCSggU9sC3pePOno+NorveNfJUoHAfYoEK6RoQaB7f1XPrcSMgwOHh1lKoNT8jeptwcJS0Vb0OMaIoKHI02ALtHuj71pLXsd9XhjSfUSlszV23gxxXgGm3UMeL2hjDaiNdpwvURekmXVXK7wXXArN4KW8G6N8JO83wktBj3oFZFLOrc2aeollk+J3UAYA6FiLIjMnOwgQLqu8KP85Qpsv0lCr1aS63Fj4yEZrTwpLffm5NofAzYxkDWADJJ9dFAiACwYouhK1KtRogqTHDO4IHUodDRu8vCFFr7xGuyyFGLr10iBLBqkKMUSEC0wVPW03UTYNqUcveJ15OkWbnhIdGoQq9SPhY2UXVCfE3+cH0NP0ZChwrdK0I3TGzwbdFSYxL75aOHP/2n2fH12cF1d/qZWzxWX3J+zFJIec7zm/bZr2S3XuNrr8DmEIlH/TbL2rs6f9lDF8WGcG2KVtvEuKZNo12EPHwX4PV3SoiDBT6FAEPhH2ooGmvqQetLJ6h+1pJXEb0B0uURLaE3FdVRW1V8tfbmQrQG7UjOJ4zpWtGKOqdQ4zPsEVmjwE1K0sz5qtjEtrD3JvCVhcDod2Vb3ThdA1MTFIbauZXDFlEH8gci7dTlgwe/+OfZ7Jo9++jszT91pw8twRpDRIBnqCGAQfNr5tYr+au/zV/5A3P8PLwCvrsmfJGx7MlZA4b46a6ZTI9M/sE7ACnV12MwwC3lL+qbRAA8Ka3xqBmpqRC1AUvXsEAaLyjr7eCN/YRmfRqqkCpRzQyisWetkKWIVzC3N2tX28MTtAUWW//ZsCmmyYZms2yEK+IRvWkI0ORn16xIm3XukOVEaVFb35BWr3rRQfP3UCnj+fcuEi3Fdj9O1/xTXLH65T9d3fm5O3mIx5/Be3hnLTHEGGuYAWF4ImYQ1In3OLrJ3/iH5pv/mLJD6midU/SjvxphZIHpPpwIo9PhLNyTobw+CKgRr7S+zKepUBA3zThQ1fUWbG0m7trCGlgDgmgLB9ch6Kqi8csC6GV5m39u6FVrS/baX37vAdTMt7l0bzLbmjTUZqNGvEsO3n1GP2mirXWHRL1wQ7onwOMojRWqBcG6wSjJMH57ZYuXTR/Ku9rX+rGPWD/74eLn/3bx2UduuWJj89wY9QIQk0jFxhhjmZkhzExkkWVUnvkf/N/LT39if/e/tNdehLTh7IscnuhGzZRt7vGemk7u1t9hYsg6GepvfGOdFSQsrZxsWIEhhZ9ooVfcr6y1T2o4OoFVEQ91tuVTlbClkBzEfDcO1fMhHVSsJwmDgkk7xcOG5+glzWsEQNaSqyT8CudD02Xa4dx7QGHVtWLf0PC36mJTHg5H68A9tSI0/XgROjf8WR8HDd1dS7sDpmz8S82HGACy7uZJE9HqoH0NuBPcd40sm/Z+0vJx+dF3Tz56/+z0TKAqTrwT9eK9elFRFS++EhEFiVTh0gHKMjs7zj/7ofvX/zt/+rGy6Rb6hSVlW3t2v+Wk26dD2XYXoa2l9DZmy+6+/uhk7ADVzdsR4/EAbiCDNkJLJlUNDquoP1gXZ3U8FUAaj6PHf/tM1wnRBKihrdMKmtpA43fhtL7aoiNX4387ho2M6kxkI0+VdFQzIho3LvPglBKqUKW+duvt2okzYZJZXdegkC7aFUpUKm0xh6MLUvf4FI7TX6KuGaGqatrNCYsjybp2RBuW2uaC3MS1kdKbYnST1znU4NK121J1cfHgnnM0y2fMmTEzXX/cVQGVkJd4VQCZKoXBBmNtfpg/fLv47v9ZLj4LrhvdDferZUBfWHzshulo3ot6LWTszW0DyyK+pCRrYtXCgnRhKgGQWp1Jc2CTB2zM+W4/hgml0kjed/f2Rn1aINLIqmEXityiO3TWZG1dl94wddhpTV8USNlulEjxLDgRsOanih7pLh+2L7NxStfgP5qChrXBRom7kqN0OH7r/vgOYUiMIQTsCUzVarFa2OMbLx8c3TZm5nylIK/sRb2oVxJRcaKiIk6VVI0IIArvlAyztZ+9WX7wR1KeY+wLi1cTpgzRp493NdZcLRvdmhUDm+X1kvANUETSUKI6o+1i0hrYLitRofZqhTTP0fO2U581pWhXhZDmXetsZ+8I7xWxVdbQi5hj8wcS11iHBK66Rrv1OwIRxc/Gd5h5L78YD0OMvU98TRHXYShu49+RDHt6vyHYmlpSWkmQfDuotgmsWRtp8uCNDRpShIHYMErG5S1Mm9QIDR2h9b/0dkO2TUascvLB+9Yczo+fmx3dmh9dB9nSOS9wHs6Lc9559aqV996JqyoRUSH1Kk7ECcjacpV9+AN//lHL6FI3Ql1kzeQ3m2v/9W0nu/uu2e4hWCvh3lWrh2hzrHbniA1+FooRBFUApJQOFrWjRXNVW4+j9ev0Ki3Hw6aitKO6raU1McqGSpLEbFZvWxi9U6JRWFPzGYwSN0BqYG60AOpPFmxmp61KBB1wsvBod+dAruMhbNAOR298HL4eP1QXTqkNaWPIEnEEIqpn7LpB1jAfF0OF6oYIAo1Hu5rUOyBdFy4V3XHVHpDNTOrVcL0qEpvTzz48e/8Xx4aBnDAnXxg7LxaVdz63pKKe1ZCKqGGASeFUocYQxBCzgSqBDO69ox99zx2+YvOjtTJBCuVgid6UlYbgrDYJbtaiv6+oscBffsv4asMT3V5Aqo6Nml1EENZks1hPKEKwj4VfNccItAKqoDULSqbnJtGLqSgQJ2oUHmOlqzSIqO6SZgYdoQGoBkceCrYpbYBEOj61Y9tRfZ5gDUQRfIKDQ6yjSMszngKzkHoVIkDWF3VgY/BpbDA0p9G6qNbkVcQFYN28TQxZS1i7DWuNl81GDI3dmjOhN1RVsT5KQXVsbGSxAWLoIxQN+RPlWg/fjXmlPRFSERFWqZVko5imf8w67xYWrkG2buy6UrrRPvWIknu/+AFLRZkNXmLEGXHmPVWi3rtZZiCwDFVVQyreGKKqzJATkaAyaogZZFCs+P3vu+e/w7e/Qenj2RBEe3RYdBviNNahWqRg1lzva61fb5IDpEUkau7xCY2yk8GG7Zpup/P9ZqrxzGv182oBzhZVpUpsMjA1u7xeEkK7huZaOyCgnkxUu1Fpo5XXbl5o5AZufnMkvmjNL209J6A+802Nrk70dc0LBKkO2uC3scnaM6c/9Lbv5hK3AXj1iNHGMfykhlMNH61JljJqTDAAa+PCRnGNj7msBUgirc1eoaEoARTaWN7sh7p5GmKvswj/TcecGgpCmlcb3LK2iUQk3ew49IeNNm7aVhqKGDWoX/iHGnI1Byk1VtiQjOLarAAgqqCmm3MKcYCEEo0vF9XDT45sPG4pIkTG5vMK7Dwq54nYMKl4NUZVxahCmUCVszb36lWVKSMVKNGjO+beT/21V+zBcWNa1dWOT0I/1bWOXVOPblAboVuXgtWzAI2Ma4cm3WrqHO6VgSc75VMrhr0RLqNy9opn75+vAGWyRKThNHkYB2kAaRw+wS4WM0urHsWjtvFhGFEMgDfVrFqMKET6o965aiJE8o7SeAQ9HFFce/qvB3SI1JzusflaCKnpHpB1BhuNgsacbFSkV3bCeuTFJqpHXrvGAUBTpdKUHxtf7Rm8+Uq1edtFLVZd9VaSersn4UwjbQOCG0+Ig8FaFVBR8YCytSCjAhFtuNFoKqJVZv24bvIantCxxtKa8YXDIcwp03r+00ZjJ1+XZiZNkt9snPpzCYBpDI8EyzGiACAmcZWWFzTPwzsN0phcVL2IV6xKN8+txIs2oSpqyDKrOLAlhVdhrdhmTJmuLrK7P/cv/SbNr9VVrJElejXV4rZ8NlHzgqCQrvtto6La3ipd/1HDogIE2RyxraCjb/cIe9vXLhns6cUCAAgcvxoX77eHKhhMNq6AbKh2Ja/XRqJGG1C4vJkCusV31KA1Gr9Ntx6pRMRpNW46iIf/aJwBlOK2mrumWkQ+mWaGkKJlQYi5aWTmkd2kh+20ARGSyGvojiAh6+0PQrJfN/B2rRIkSZoAs86yObNTWbGWKW5gJtjMtlPlBGGJeKgS8drVawPRYkcxEYiKxfn5yf2Tux+d3vu0XJ6JK6F6dHTw3ItfevbLvzK/+SLszHufVLR6SVvnG7NudFUNl2tUq6UEiNZndYmIyKgqsPY1TdcTpLkd7zTZaKLNugsatwrX6kHsww2PSEr8VaAkVUEgZgtS4JBgSUopy6KoRJSJCucBykzwFmcvYkGqzFApSzbMqmxAEMNGlfn++2b5KW58OWFVooxAKLG+jpkUPsgWhvxaznW79uh6wZ9lY37FG5VVwycpKJTCayBsNBTicL1CFGuGJstuSt7SNFtJ6ghbNdCW7V9VbeUlYA5RzW8kzmtlZo2jVlyAnbUarmA2IZeIFERM4GB5C66joSQ0Br/6BBr1B+Jqnr+2m8e5vbasrC1sVO8EJfxKtlRtal9hltUqXm15r4f4WnuCYm1sijC02aybm+tr4hZrJnH51HD5m5JG7dIDQJqtGr1piRDu54UCytR0wAr/oSbAbZKOhhSNvQSk+gdek6ofES3ianQLWVetHmTG2uXF2afv/uLe+z8/P7m7OHnoypIZxlgmPsvto7uffvzeW7deePWl179+7ZWvi7KKn7L81g2+HtZN7rBJ0+oJXGvNRJR8p8EcuqzeEAhg1AKs6P7FbKJZJGYVFFhKjE/TqhCfMPHd995EsRCUH9+9s1wVGePoYDbLUQlc5WcZO6cK8aK5NVAQqajAMDGcOotcvTOaWVUxSpRV54/N/bfkhd80lGlc7hr9mFZpBZTTgEyW5y68bKRtkvv16YrWPVqUplzdtmlGbK7pI903Eta2hWE07H2+FaTq/LeW3jSAALDKXLeHiTf5EHFYLxlEtQ+a+nCzf/SKEFULpeAsHuEp3FlTn5SMWqLIusiNs0VrjApvw+qdaAPVcTZaImLDWrWTRMnSLK1TERA8VMNaF+RstAVAGl0hwxQPM6leLDWB8nrnieLop7VypqmwqFchasTrho+iN8dW4wW1epeY0Jzz9XNsjHFNGSbsblxku1aWY6pgpmxCMqUA6L1PPvj47R8//vTd8uJUVfJZPp/NvUpmrWG99szzPDs02eG5p3fefuvV8we3f+U7zhyKaFMVr2vROwpF2pekBxaWkAUiYQX1ib1QYmSa/l2LX98lNTziN2q6SRbrxS50KhOYjFlV1aN7n+U3j+998sH8YPb8i8+LcxePz0onpQMzQVgrVUNQFauG2Hsn4q1hhVdSFYjzqmSIDFmpnNx7l32hWT5w94FKGjuJHjQwamjOayQASLwuonHrZk1dW4riA6Qt1B3xq2nQ3CnhUFaXzAQDVbDWZCAO66O1magQETFz3U6aJoMoxSu1lUAQARMxqyoHQ0vQY9KJ4hSZuJ5mafKkedbu4M0/68m2wUtjs27aR+phmhb7NWxroIIgUkjiKqoKcMRrTV+KamsuNb5pja6tzkj7RiE2E6d9hk6l2oBFRDAAmLmZ57rKmxOVa8K1ke9Gc619dhMJajZXc57Et8xQ+fijd++887PTO+8X56dZlpEKsSFm8n51fmKNHMxncvHY2vzwmec1v/HmT36yuP/xl37rd2l+W4Vba2NsfKynUWsaJNlM3cjRwkB1O3GzhbE24KFurmZ71kMKiOBIyfKxYT/qtH+yQxEA7/1r3/rN5U/+SOGevX54dP3GPMtg9CA7fvDgwWJVrUpvmeBcaOdwToXAPvBrJYVjJu89YJQ8ssw72NMHWXmi+XW0TRfhu7Tr4dEcKs2GQmQApuEZQzU6q9S5hpWgPUhqYkHpkptdgaRHt60bsP/sRDt5i76NwNlOJrbuoLIgY0y8+J6ZGSwihuOqqKoiwkxxXeQ4NzhM8yhcWt4aylc9c9bWo0R6gu1GRVSFg/mjueZ2mq/3z+Z0bTSBqraBTzS5sCaboAKixNDEVExNuhLFWytHIadIlDTKudGIDf5YzzTFWjw0Oj5pQOsx16lCu77UubIuybQRmmjYzCR80w8JiNeFsvGuuvPh23c/ent5cg8KO5tHwXxFBDZ2fnRttTg7e3xijV26QqTKj5eL1cUPv/uLg3n+3K9+B8dfFh/uAmiIkTpoo/qBFnQhe23aI+pb/GtDZ3hV87sNDA03ViBw+7gS1LZUaawOKevArUlqKIWSsR4MO3eC+49Ozi8utCqOj2bGZl5WK4E1IJCo5AoFWU7WMqgh9qVYawAoiQWBPWDKs8fz8pREiFg3Oyze9YIpgTQRuI0c1o2OTUq30XyNXHq2NQeSNVJdBY1CYzC3Hu6EX+PBroqVtSYoicaYejYCUs8iZiZiUMOiDAKRVxgwkzaMiNQVOtrYgtyqYcJo0sniMo7ABTecUJrTr4eYNNoIDZxurRj12tucPHVuNQuISVJWNbxqsleFqRC05jD1amjrXXOaFak9f5rCtHqxmcm6BRpzvlXx1rRv8aNGBXuWfWPtxenJuz/97v0Pf3lx8hBk2JhAU4lg87mvVgTMjq+rCKnk126dn58tVpW9ZmcHR3rr9qMHD80H715/uTI3X3di08mzBi0Nlog1FqOmzBoubaJ6y7KHj4RREcx/gvglHRXB5ghJSYSINVKmYOCIQLbZNkg4K7HrUZs91SvK7NpqcZ/s/ONP7hTF6ptfef3m7S8vyvfK+6cEiKi17CvxIl6QZ4YURqFQw0oQJVZAg9EdLs/nbrXC4mO68SugrAZroHai7OHv6z/XQNTcIdekKwHJzhI6vLMGSKxbcwAkl+U1qDUaaq1LpriXB7KRHJrDo7mMtenzANi1sEJV7XJ5EUz4AM3ncWX2XlTFGJNmX7hftWYWGiFAlQyMsd57EdEapxqkqS4s/BaRwBRquAkJiUlEiMN0Wk9+EQ8FM4sqoCrJmVfr/XuBRnNME0ZFpBamyX3q591orZhDnRE3oFLlVKJNP5ywIYCjzr7BROJwCQy1AZAtot4UoIllLYDuStjo1/XdJ3XCOi2xUZV7n7z33l/8ycOP3z4/eeS9miyzs4P54TWvCudsxjbLfLFanT8WV86u33KKoxu3L85OV6siP7rBVXm+qg5XbvGLn778LbbXXvfC2rnoJpSnqrXlK4FIbYNf79qsgQkJcRo6dG1KTfueKVZyPUx0OLY0UVxum41DRMqAj9+ojhJwINTGLS/y5Vm5Ws4Ojr7y6usKfPn1r1nWZ555wX7w8aJwFWummhn2TgQQqGWIqldYoxAf1WGvChUSEmHAn3xGL9UwBCKKH8FKcNQXNI4zAqjebK1roSmOiUoREdBDaTfsjeuna0tEdNiLfdFmIVthrI2eo7aw3rethb+FTc0nWwUgIgsyAZhE1XlP4kPTee9ns7kJIEJkjamzrlylqlk2g6r3Pk4wVS+igIhXUUCZmdkEtgUg4F1TiFoUL+JFAARiyMwqcfIpEDZHFeq9iPdMbJjrdShouGS4eWkHh1KZ0zjYWAGwyfJCfG3wiKZsIScRDZjQxB0m1jgpom4VFrS4igYyweuhFzYSVJUpHILYcE8JLkWEJmZR0thAlGbmAJEJJTOZAB+B+9SVQpLh7OTuhz//wZ1f/qhaPC5WRVWubDbL8iyfZa64MNnc5vlysTDixLlycTY7ug7w8uzM6OPDG88szk6z2Tw/vPbgwZ2bt4vlyunPfvLsl1b25pdofiPJJqrgUMGIM+F52mhLTUgbTmekwbLR6IhGH8ejwxvwFOheZB9IVC8sVFxrEaoanMiYOZl/a2fJNch++oM/Lk4fVKvFLLevfP072bVbWi6cWx4cXbtx7fhk8dAA3qtmZJlLpwoVBpPmlgJfLx2MgaiXUB2qstzIo/thTwxxk5LAkjpF08WmmzMUadkjQGuflc3NEzXr8RNbs/ES0Hrk1dC4WVLULimtsj3gNYWUNejj5LBVsR2HsKH49vj4OPaxwhiGalD2RISNCXM+4YsYY4wxtrKVdwFxAIDIMLMxIsJE3gejNgwb5obrFZExps6tlsNaW8MwM7MxtU2K4iYEMTMUhsiFh+H/mlanBCFr/SXwN1VF8OGuMUhVUSOspoWtyYzSWwqIDHDw9G7RYFFh4rBzEuExOFJCOd1gVcePSlPkAsxsCKQUtmFrqPVehGrjJRkJzE3XVAK1GETx3sqoDlOSeaMuofAwez95889/8u//1fmDT3PLxMaLtzbPstyyyWzmnIp4gs4Pr12cPKgWF8VySdm8evjZ2fni2WefPTt5eOP5V89PTq4994IQf/Lem1/+xnfef/vNs8fnr375wfzrf0soI0CVa76FREAbTJaiG1WCJdXQtnF8N1eLZhuGbq5Rfj23w2igxNyQCuQ6Pyj5gHZMrGQBDYc9CQyosi7u3jl/8/uHB4d6cPDw3mePH91/8doN8ZW60lfljeNjfPbIi4oAEM2tVYH3UMNMcKoMw+QgtZ2CSJ0UN+bHVK4gjsws3SCDhvVCG3CfbA9R/vrscu0O0FURkukwLugAotEmZBS/r01xpWwARASxqK2G1mqqrljbvpPKCWpESAcWIm2k+ttUkVCnOmhikRpKiNIi4V9ymtrQNLF7CAntjes3JDhNpPYgIu6oOaoKa8OV58w801nACFG1xhhr6z3hriqXBE3rJlPNHWIpTAQKGBcaUgFB2Bng+FlJgjHGWDRHuqRdiNTEcYqEVjcc1avAKVOtOcFZnZJVnUi8qT0aB0GAiigRJ9YWEkpUiiNUNude7SCbWFV6plDvfRxsHG/HAIBovDOpOBJ4inhqAiSrguqhHNvMh5WGDBMZpGvRpAGLFJ1jAlYIEZ/c/+jdH/73J599zIY9mGHI5qRkZ3PK5nk+s/lsVXqAraGj6zfPykKxWlyc5wYsJXyZ5cfl8nx+eK1aXsyOb9z75N0Xz885m733ix+/eMPOw4lUXdvmiQim/lhT1BPD9ExTKZk40ybseigr6o2mNU4RB+tZXKvIJ+slE5lU7IaXbFpLTHMRQl1G0PdEZ9dvaj57/+2fZvmsWl3M7rx76zBjw361dOVilttZZs+LyhKVXrSsKLPqVUSsgSqLIgORSgYCCQxBPZGWzul8rr6ijBIqgSRN6mRkhGq8OSTurgo2zI4JEQgNHIi2sgAIUVEMG1qajkBHKETtt4eku1C6ST06giqFr61RvM80HOgM/nvSVAXSmUFN/LEe5xprAa0/ElKfeiGA0j1+oHVVSDnqKxM0yvFARNaY5O+q6rw3xrDhYNMJsBXbMlGbWB6BKHrKWmsDd2qOvJY/ETMbY4lIxCNONg4jPxCZUFYoToK5TgQcqCKrgpiJwCBmCpDnRSjd7G0o9oEhAuDEm4RBaaaoSPi4OhKtWdNDkYDeQS+2lJRiIJmZasVefOyiaDneWEnq7YvQnNFfOJ4J4zgwVb16FlGoj4hvjbFIux9Y0644Iuo1RUTAHPaAoQiuc2m8BozzAQeTZT3CLjG7YlmKZPksm+VsDIG8917Euep4Puf5QUZMXHiyQppZvvncbZAuLs6Xq1VROvv49Pkv3z49fXjjuZeWxQoq2cH1T97/5XOvfu3hL8+z229QdiS+QkIo5XUVNCmE8eI21aA51W2FtN9KHExQXlXJMKfB2ViENN3KqXXezJQu+9R6ra93G0ASOFEQQsJ1nqEBA9slzQ4OXvidv3f3zierxdnxwZG19pOP3r11/bqqOFcQcP1w9nhRhkkqKkSaGYaKKDvylsmJGo5eXoTgRYtVWbIvdXUCewQv4V7aNGLCsDfBEkIRYiR9e1upuRWWDA5JqTQJQ2qfbSEAHBRPD5W68dcolHgTrTcQEpEPGkVcCTMoEwkFx+MahIkonCDQRN+I08/k46sblzysbR+oV24NUyYZz4JRJcXaxKbmzJpC2WyYTuK98z4YrURUAWM4N4aIFZGJqKpLnuVMYW2ENRz0OMMGSqIVUVA6o7JWsxhmJjLJB4eYDHFQBIJXlkpiHQowMaIWqSLhbm7LFG12qhog2AuJeFUJ7iSo0Tbs4ogENZCiqQKqEHHMtrlHEYCGSImk4fdUT0NfVyHQAY4UIFz07ePFueDaUyyATiBizDZ4BTAHoxurqvdeACi81m1bMwhJBvIwjSWWuwYFCBFZA7AX8SpAOFTEUHEiBJDW38LTpFnJvbufutKxMeq9qBpjrM1yY1xVrpyX08cHR9eMNUzkYUtfZHZ+dOMmE+59tviLNz95+YVbb/zab54X7uT+J9df+mpx+jDP7ONH99/45l975Y03TG6VCZ7X34sMi8d63Mf2r/fJWl0QujXgTVRVvPiwywSASEkCLSbmGuDroiJ+1V7ZceaE2R6GB4dRIeKjf7c4ESFiY633/sVv/879D95974//xfnKl7rSqjxbLF967oaIqNL1wwNjzksXjKBKYQdBIywLq0K9qCEwlCBCwpkty4oefIrliTc3vKiqj2bMYCdTIWJig/riZQKQDpxRWvLZpLoJKQABB0IazHKhkQUAyIDrw2oa727hLNoaRTU6FzHSJ9WUTCRvyayM8PUisJIqCYiC6a2meWnzgRo9ELQPRToHqevXpMSaxmLom8a5tAYCtsx5m8g1hbJZw8xsrDHWmGj+D+qeMcHo413lvcznc8PsvIsaEJlg1mc23rswPr1479U5Zzh41Ub7mkq0nqhKYFWSvidIVNtRRLwPm+6GDQWHHFJVie670aRF3jtVSZcYEhGrioeSCiuEIlsWVa8CVSGmeH+GEjjMcQl4ofWSEpZrYmhilLFlaxxHhMoAdpJcfmojiAb9s2bLYUeluWeaaAiQrjiK/m4KkYCYNd0Asw9Vq3lWg/EFTSb+kQwDCGfKkDThtOwTk/nolz/89J2fEoStkarUqnSqNrNsrMlyJq4qr8vl4eGhYWOYV56qqiKTZVl2MJ9dP5p98Mn9P//e9/7ab/2Nux+/f+N2kR9eqxaPws0kX/7aNxafvlNV9vjVb2uWeVd5X0EhjRNFxlisVw6i5t5xTeXCVpKCo3OFQkAkAKJhMfgkGktsjGFt7FPXjRy7T1RFEviDjdH4Rbh4tEUDtw1bVU6ZrSXz2m///sHRoa3KxcOP7n34rlstvHBRrAhirTmYzc6LpSFIsGLCW2PghRnBV8SQliQzywDEe1j1lfdVZbwHGZAnsgxWqjVKMBkC0nXuyZzIJh7+CIyeSbk+8CfiXTiXgzipPIUdZE2k11iwgXiSKg5ONoAJziLhnA9CoQlckhNfUGLqs1ABFVlB4HSkHuuFAqg5VXPGECF6L6UFleu3uuaCa6pFwY9XJ5GvkWBDd1prbZaJSFAYQeyc8+KN4WjYgxomwKiqeHgIMYgsUvUq58NcrKoqs5pzJqre+3CkU9RrJURkraW6KZXj91AkakiqEm60Cya8sCQbkzETc20NI60JCKLC772oeiYKlBAEBsdPrUQuLgoYNqrqxROUyXgSkuA9GwACQhK04OCPEkdMBBHBBpULkoQBuGZztW8qsw1HCFvqdmhPonqLmoggoiJaa4iI89wj6kpxU7IBZxtixKEU1Y3wNlr62Jjl44cf/eLPtSoW5+fMZPIDX5WuKliESLQqRXx2cGSzzCkVZWmJDHMBZdHZtZvPEX9nnn386b2f/OSt6zdvvfSlV4uzhzdffBXVM7I6M/ns1su37/zwz37ys3/2wuvv33rtV2889zLPDr2rsDZXyYaPq0ApTiQN3wQByDASj/Ii3jsAbC0Tiai6yjsXaa8XNhZqlUS9FyiH1dcaJB9G0tCkjkhV1LuKjQnWgxr7mMNgduKcGgXR9eeef5Ad3vn5T84f3jFa3bpxvagqESUmVTqa2XsgJ6qKyguzUfGc27BseBFidYIDyoPpJDC46vRiVhWcWXUcP/mevEoCZSBCIrDB5JRWPjawJtrLgHDOOZwrJSKEzSUiogxhVVavUkEkfOEzGCUbNvB47Dc8UhCIqR4qYfxE8EkIB4RTjMmJLe4grW+FUghrYFVJ9aRkNq85W4Mpo61Irl8lQ1737XSAs0G+aJkmyrNMVUTBAdpB1mZELKKrskrDEQRyXkQcCBDvG0d8mYy1mTHGACZMS1IIvIalkmCMBQWsj8fVgx5EgJqgU0gkpZSW3PjdTCIiY0Sji4aqx3pik1fPLISMDYfLa4LZO4yt2OPB3qEAx20pXtNyiIhzVdRi2WognwHlyAQQEfFEhqhGH9SGZ4lcgBPzABReRESiswmzqho2os2t1bVRLGiQAa+RLGi1Q2+t7dam4eaQSLSuRlImIqnKD375o6oqHt65U1WVEomIODef5WqMzfOAjn61YCJDyLNZ5bwsV4EROScHN55h472visL9yZ/++X/8pdeeuXmzWpyZLL/+7EuczRaPT++frxbIP/3k4/c+eP/42s2vfOOvvfD1X3dKEB9Ov2lSvSWYSANDEhEXvkcDA0PBOhaNXAxCsJcRKcEqyLtK4/ZSaoeoioqqUVU2nPSksAmfKVTgEiWMsBC9QJmD6Z8Q9bmqrO798uenn7x/eDw/PrrOJL4sFCIepXPiPTOVlRhm8V5YPUFEM2YRHyAPqt57a1nCfrNoWZZaLeFdoJsatp05bMQbYlbxlGoF8fF0kyqJsg86ocBDWYlZQcommq4SAIQKK1mAiJyKwpWqmkBz4yhMNHwF/XjNkdZmsSbCxDtg4p9BMybEKxSCzhn2RzfUjlrzjxsSCR+DpT/tztZFBGpdk4M2bE1najZMs0CpiIxzwWoWsJclGp1UCUzMJrF6AKDKe1VhKLEJBjEGZ4Zza5nZsGFKlxGyihrvJQC4SSqjJQIjnVk3pApO1oy0u6pRAQx9IURMEBUR74JNxCQ7d0jofQUYZU4fGpeo0oazUwjgCh+cewMmUVwdgq5HaSc00CqEgyoQUQ1IZ1hUjYio+nB0tXGvLAI+eh9gjk3an11vbkAs2RrIAkQyQ5Vrg10whRuz3oAT8SK+4RIV5qNJehyrrkk7ImXDmz/4o0/f/8Xi8UmwqbiiKorSWDZWRJ2qZNYAyGa5F4di6cuSbZYfHop3y/OC4KvTZW7yLJvdvD6/d3Lx5k9/9vv/wd//7MMPYcy1Z154eFbMdfXo9PzWs68IcXHy6OHjk3v/5r999e2ffPv3/xGObql3QBhdUErb2EQqKirMRJRFoZkpLAts1MZKAYAIrGVjsnwm4oPnA3OwtBsTVX44VxklZmvYhMEaG5aio1/Tmh7uOFJW4hliY3F2cHB8+xk5uZlnnM0ycYX3ogpfVctVcbJ0q0rDjFeiynu2XFZVNssBeFEvDKgTDQgqqlmel2AtlxAHWBABXstCVMhYzWYUFFQIyEBFXanMHEz4rAH3ow1eOTqKrwMphLxT8TAZ2IBZNQMcwkQjE2zZpA7gpDzGPXrESc7JVlJvI4TAia9Jw9IlYT6zpC37uI0RKAnVn5fWJsVbbwhINPEh7YnWkdPuR3qwXt53wLKiLGd5Ho6IQ0Ul+m7VM4SJjSWNTlGsoqJqiL2KDWoXYNkQI2yeMZAZAw5nSiIQM5OCHYmIZw5jDVz7iAXXGwocCt6rEIEjDQnfpItQxoCKIWTMWZYhcRYR8eKR3OgVJN7HVkhbx1CBMlPYawi7UZEcMEVfNCbKs4xqyhRQXFUBCabiCEmUZl9GBO+d946IjbHr7TOo9xJskU3zUKtvNFptolZlrY2nXhv7l41llcMU9b7SeMoibn0GsGbmtAoom+zRpx989uE7xfkFVG2WqYphymcZEVarQq1lEoiwtZnJsvmhzWdsM1F2VaXOzWb5YrGolsXjxVlmMZ/NXn35ubOH9y9OHznx6v1yufRqDl64XVXO+vLwxvOLi4XzhZ8f/7s/+WPrz7/9D/+X3h6LlCwKQdq1DsuDiDgAzBmj/rD0WmuWda1Jg9sjMwnD+zBSpRIKtjCycJ7ryIjYH5EdSUPaUMnTlDUmLQwGokfPvoSLM39yl8iXZbkqC8t0erF8eF48uqhEVMN2AcMLvCiYVpWb5ca5KqzQXlCJEvNF4W4d5hAI8uCMSYGYVYW4AsaKODY5MXN0qWQ1FipKZGxObBBueBBREVDt9SQAh/WVFOAMbEAm7HWCFCYjtkh4kOzuQVUkjbeZRS+OOENq1SbsSySvr9RI6/uJEo9NTyjNrQYupQkdncal0QlUo1QdFangNCES4WjKNRhqiwEAa4wRkcCkALXGBLN4cLUPUMEcbXwa99xEAKI4l7z3qj4nmxtjjIkO7wTRdIwOQsQCGFI2bI1JYBCuQEAkotFRvr5Zn4hJJPS/wjCBwskCQyazsf+Dt7yIMx6qEA3gE8kXUWD0LALxXsWnJg6LCCmUAx2K3q2xhUVENFB8EHNUQkAgWJtFo2ZtEtD4B6UQGjOwSx/cxKKWq3Xr64YpLepNEmz9yVk3blwnC3qdEMg07HCrBJIq0LjTohpNe7768Jc/Xp6eFMsFkYp6a9irgKmsqvl8bm1G6kEa5pLNZrNrt8hmytnMV6vHD88fPrCGK4Koz+eHh4eHN265jz5+8O6bv7z53PPOueXZYyUU7nYgGibPTWapWBoyN5576c1fvvX2R/+Hb/6df/TVb/6ak6r2bvLehUp4L4GhAVAfkaZ2Y0aCs2BeEFEJ/++cqsS7CWxGxoBAhtlkxAziYNekWusJc9B7Cl0ex1Y0ZaYeAzFEsXj4oDq971fnjxanp2ePQ5H3H188vKiKpKDDCVm2TJVXIiq8N56YTOmFyJTeW8sZGRCcIj+c8cEBQFk2B0iNFZNBJSjRRIbYUFAboSbLAtxQvVNBACefCIDUq9QGNQaMruuEiNkRt0FQASUYa7q9EoCgCMQxH01gm599jL3AWg+6OOFY47xNKFYjToQ2XbM8WmMXUUKsNOtjhPQw7IW20KtFzbp/1r9tGDHOCwejiCuYTNBuoADDqQ+3MmU2U6ghIkJZld67PMuIjIHkRBkjM8SkwUmC2DCC0ZoBE7gKyACwxMGEIlJCBSJePLMhNhxMJGCOfNgQh615VS9KEO9Jla2ykjE2OFYh+MOwibuB8eimatCMw+Y0ACKvQiADNmGLMEyV2pKVjPAAmEjIrFuLyFD8EnJabxTJB9gYjnsFcenSePBUPZSiH7zqGupq2hdxh4hMZsM2bCBikixi68NV3Nz7i4SWRJzEb5SGfUCJlTHZ/Y/e+uyjt8uq8s6JiLWGmAROfYANYzJr2UK9MawiIl5FDBtkOc0PD4wlY5ePH+aZPXrhpcMbN42dsYg5uHHnk09Wy4vDaze9e3i4vHj2xVfnh8cZa7B8MbF31bWjg8ePV7j/2ds//NOvfvuvabBhBbcVwxBVVWNsIOdJq4g7NetzGnFxBpMJV6uA2bBJPmrEbNhYEKtGz6zkdrv2c1RSBgf7UZrL4Q6FeKguoadaa2+8+vrPf/zHD+9/dnJx4Zw3rMuVK5wvvQaHZ1WFR6EemTXEpVdLuirdPMuDddALvFcGMmZic35eXF8+ZoAMgw0jM/ksXiSmyQExzm5Vygjr+zTX87WesWrSqsoIm5tU7/lAEfZ8CQj+tQTQ+p4RSPMgU/QUQDLsR5bEtQevkkaDRpQzQk46wELJErWBf4n0Jcxar/fpyTrimom3qhvr2tihro0nI0wtcFE454kVqmVVMTtrrA/marCIOOcIgT97w2SMCVpVBc2NuXEwz5grESkLBC9bISFPCi/eZhnZjFLnqUjpC1H1zjnnCUJhL4vUEhk2mTXEhowBscJ4lco571zUGXxpFGaWsc3FGJDRcCApXKjgHalyYCpp5yBdB0PGWElKBwfzeZzTJi4nTCCjESXIJOAAwARNzrdR5YmG3OTaujafanBkZQDGUuJTddD1CfZg5IpYxumUQjCNJa4n9V5qPSCCeS64DxtjmU3c1FcNp2uZIb766Jc/Xp6frRaLqiwNkRfPzGBbFisQpFpBKp3ZmbUEWGtNlilnXtT6Sr1wfnD03EvZ/KA8fZTN59nsAN6BcZNfZJUH9x9QtVA1n3384XNfeuPg8LoRJ2Vp81nm/er+3YvCHR0ev/jM7Du//zeViK2pHYmbzAu6Pi6WLCdqTNzESAM+6kfx1v6NaQDENb5xojbEofUakLym08IUMzTGhIfRji7effk7f2N2MP/Rf/2//+zk7KLwqnLrKDfOlReFqnivzOxVvFfAZ5ZNBBQtnZtl7EQsGydijS0qx2wJ4h/fZ/HiKrasHG1JlJwRw6SFqIiDKllbz8pkJNk0kgV+llyksd6IBIWDoGuyo7FhBekmd0C5fp9aj9IyHB5wYtBr2AOUJFjxofVJPQo6mKbrUai25iOB6zrXaaFZ19bvdf+utZONYImoKAqwMdGwLwpWQJxja0FkwsFycVIuSLwxGXvOIfPMMgFSodIKqMrSeQ9oZpnYOAcPqGqe55m1YHJOnHdFVTlXBS5cq8QKEvXkq9D3kBIKEMNYFamqKjqjAQRhUMlkDAsxmZzJGGvI5opwqIApXOwdtguNgbHGZmzCkSAE5kQEYktKzGSsUZAkb/vYVRraQkzqlLAwBdzyGthisLZQcpFD3DEKDjtcn4APZ6HqH7UKGSxlADRYF9PhsUANg0NG2LDjOl8QNHxHRKP/HoEVJCKcRieR+ejN73/6wTtl6ZR4Nj/wPlCzzDufHR6piuVwkB9qDWXh6iax1qqxkj5ipKL50S0zu+aWJ8vH92yWszFEms9mLzz/3ME8u3jw4Pozxx/86I+Pj+bPf+kr1fmJUTbizk4f5/Oja0eHL7947drNW9UGiCNQ4TgAlShauOqZWespyTdZSYNLoHoig9qeg2TBaSz/TewjEg6alGhjs4ypvgAStEbG2EP+5gsv337+dvHmhxer8saMROyt44OTi4KZvfjA3Ym08uHMFtjAi3pS53WWcTDBiFcmOrlYHRgjxSmRR6hCPAxCoNpdNa2ZKnBeqWZGiaGJrg/hRgsuoKwUlHAfaRWSWk0cVFdVILyVCr6CMSAbdgACJEQ3isTdKK7yYWT6Wl1NgkqKHFMpUN8QGYc/xcnVOHzaMoxNDdsS9bAz670450grYyzn2WE+M4YJ6tVn3otTFWfUW4KKKNSghIcXD5+LalmuVlCC+qBTiWcmw4CykAGZgtmyCqisvFetwtJBBgRNl/8ABHVSLNjOROFW574qZH21dtyD4rglmI4uxycmaCapUZnYgg0BTGrYmHyezw6y2cyYLKIIMRkTXefC2FblqOPEK1ZqbSQ6CabF3keToaZzxpRuoUE9XU04xxJkFxVIHD0iIt4YG2iC94HyVhqPMXCqXVBQfTLocH0gKWEpifeAQryItTaT8KkgMqxqDD2+98nH7/yMmA7muXNhmmfeOfFiDDORyWaZzQSiomRMPj+aXbtGgF+c2DxXNgIiYhVlm/P8cJY/bw+uuWJVVUtyhT284RanR9dvzth+9N476iW/cQ02d0UV9rpza40vZ4z59VsyvxabQlXTrVC1B3KtboeZpY0AeAMGJJhRRL1qtOKmnqb1kTSQxCMQQWdqGJN1vQ8gBunUPxQ+WCHChOS4wefBpjSHvqqOcnNzRvfOl6dLZ409X1VewUqi8XYDUfXKJARWr/Ci4gVsFewVxtiLZSXXZ8X99/PFIxwfqXj1XoNnfzBSEiniUIIIVL2vOJmtiE3tbBixHorocrC+7CiM21jXQPniUReOO4wEsK2NWYkrJbhJK0lcXCPxC1aUuOm4tpbVuuK60BpelGtB2UTNN5LBtYVeG783UzcY3Ca1q5e39UuthUpiqFpXFG658quLitnms3DWg+FJsRTvvRP16XRQZC0AwskdLx7qOVLWCOqR4EXbnwmX0Xqo96pklLOw/SK+cm5FRBAXrn6VamWzmcK4YuWqwjtHCIuQGCYTzyIQEYkKkWVjCcIEitvG4eBF2KmxsV+MITMz2czk1pjM2sxam1mbZVlus4BnwuTFg42187gSpkYjijeqhWURRCwkquHqQkQ6Fj3kAp4hkfz6plxR9mlXunm/Y31MYH1YvYGJaVmK1rc07aOmxpRFwA1aAccDXkRUFcsP3vrRanGmriIVy0QQArI8c64MlwZbq6Lu6PCI8hnZzJjMKWg2tzajfEak5Cu/Wki5FJuzL2x+aGeH2dFNEcfE2ewQrsLiUVVWy9OTg+MjgLyStZmIU9HDg7kvVzcO8psvfxmzG6jvtg32bFDwumuo26FBNOmDiBaFeNZLEa82iW0VLTg2szbzVZXmgo/TXWuyEElfYHwhoWxchE9E6Vu8CeO8UHX07CwzR4dzX5XEVFTOsKlEyRhiDgbdQPKNsbkxqj5c9VMpUDmizBrjVJ3TsxXwwQfXlnfo+BUVgQhRuGEFSmHnP4xoVSgFoF1byTW6wsXr5zVcEhe3xeP4C84WSsGBozG3KXwPmwlkFUzx7HdwpeU1nmj00Y1qY/Sj4nAKJmEfR+tw0ms0YkF0VeDk0oBIc5MnH9K2KQIPlgijaVMTiRom62hY01zyHUFEXg03nZCqkHeoHsCdEhTZs+AcsoSS/ezhiS8Lt1xaY1TPxZcZG5vNsnwmKuK9iAvmJyZmZu8KFUdJm+ZEG5iITEbM1pANrhvilYKjgAWxqJBhiDhx4p240lcFVFWdihdRqDIXSgxVcU5cpeIQTzupYZh0VSOI2ObMM6hTX1E8jBZWS0tsEQ7lEzFbykCVx4qZLRs21gZOlkMta1C0RCGcZfksWuuSiywbzjLLxgAGShoPGAhRuJ8r8CUfrpcMklM47RZPy4WlSRkwTFASiUcIahZmrY0zGWu6F4za9Rf8ICrp7LRJFzHVgRvmI5B++IufP7zzYbG8KBYX3jlfVYaJMpMZS+GzTyquWDCbalFlemjNDWsznl3j2dy5ikye5zkAM78h5ULKlaoTKdkRxJt8ZmaHbIw5OMhvPMvER4ffL1elKLGrXFWqwldlPj/kPLt2NJ+/+KtErOI04U28FKJh+ECibCmCIQKMEQkecHFjo14kCAaknJnF3bs/+bM/+/bv/8HxtRuqQjDxExNkgvbFzPEcLtfb4bq5YKxDEgBKPH/htfl8ls/yx6UjqypYVH6Wz0Rclh9amxWLs6osGALvxNhZnlmbq/qy8mR5WXqA5pkxzBcrx1VZvP2nB7d/jcxx8AcyCXFElJlhLHG4bouITESk4InEHNsFQpqUTRAofss2sgwNovsAOoiKbgmwsqX1HVbB1kbpTGVCmQ0DGhAN//Fmk7iloAh35yId5gwHMCmJkTxvNaFhfZ1fHK5UncJfwMyIcxKnUqI8g5Ywc7CB99CKxGm1gFsiP6TsGADYqC/hlwCIMyLS8pSqk6ik03ukBuxhr9nF+SJgk2EDZSm9B7FgtVoVq6WqCtSrOPGiyjYTEfFV0KyMNdZkHOetJ3KBbKTbYYmtjU6KzARlNiJOXCW+griw7YVwzi42YkVEogTvxTvxHuJUBOo5IEhYdwyTKY0pOV48K9H2SDDEzJaMhcmCM1bGuapXX6rJFSacZ4NUS1+xKhtjjAFbJaMcNDBia4kYkNzaeT63sxlxFqwTzlWAcJCEDEXPXVUyZK1quPojOdIACtboRswajiqAyTAZGyzbDV8y1nRQiSleYiOqUC8kSYVQiq7Y0byRHJLj8vjxez/76O0fnT9+uDw/Xy2XVVFVZckMJhweHuSznDn4lLA1hkGGmKWSlWP10AOTHYNtWVXkfX5waPJnxFXeLaVYSbmEnKuba7kUQ94YzI4Pnn3l9d/+w89+9t2jw8PFYvXDH/7oYD67eeP60eHhCy/c/srv/UdiDshVwfCsUFA4Fbg2cyEdgE24FlRFDaZGJgMoUzSAItw3BrI2//hnf/H9f/WvPlmcffMP/tAYFu9BwcJgaI2VceEnGICUVclBa/NQaF5RbJDi/ODg2ZdfVTtfLlcXqwImB3FRFTcPb77w0qtf/ZVfZ3tQVI7yzLuiOHtw8uEvTx58XPnyaHYIyGq1MKxetKjk5lF++9WX9fRk+fFbB8t7dHwccSoesI12A2YGbNrpTg3jFVCOurgk6qIcBhgzYIihgTEwwxDCLn0kTw6qIGHE76uAWWEAIY0Xx0QuBAnDDmGjEww2JFW0GfsC4sLVCICA86SjWWULcDTJUfAv94CoK0g8Fg+IDfJjtYcqFZ3f0UdvQ0rk19TO1JVwS/gFgTQ7CIdzyJcqBUDEBmTUZESk2SExq1+SVCAFMWkFcwiTE+WAgjzYQr19+PAzEjXMeZ4ZkHoxTKvlhfNVuVyI88GS5AO7zXJjcwptyiajTJjYs6j3voL4gD7MbLOMidlFJKL6iLWqegdITUBUPUTjxbCqBBIVcV7Vq/PBKYxq/wkiqDCD2BhTxf2psDAwsZKQEgtbtcphIHAmpCzOqXhgDmb1XqsVqScQW0tmBvZsQIbCZUNUlmEWFEQXdmGzWVBWCEQqFkJx+8AQKTGLUpgQSFeHBzYeplagr+E+MnDGxMaED1fFPYBwnSNBlSkco0wHoePtN+KdiA8ZCnHAdHBwnbEwBsxQqsri03d+sjo/PX98RkCW58Hk4r2Dl+WqKMqSIYdHR7PZbLVYmCwry5LwOM8zqBLz/Potc3AEM7P5vFRnQCB1ZeGXZ+X5I3gXMMIYzudHeuNZc/OFay++tvjwF4cZ/8V7H/x//+gHrzx762tfev5rL1x75fd+3+fXyuXCWKsq6TSlIUr+90RRa6h3vwJTCW52yQitSuEz396XwexibfbgnZ//d//1/+2Xy8V/8nf/8Llnb1QXC6lWogRjo69WjU1IyhhHzSdpPPFixsRD6j0EGM5uPvNsmR0sTx8fGFoaC+Wvfvs3n3vmpeWju3fv3j28cXueH7/0+tdtnqn3q1/96x//6I/f/cV3H50/vn44m82PlqtFVTjD3rlqdXZ++9a1j97+4MZnP7Y33gjdGM3kNZRIVPSQTq6BKO4ghBFFEmz2xBw3p9gQGUAIlYoABt5BHGwGzuAdqpKIdL0pGXxYjIonKTWuuAwi+ArOqXoKRq5wMLFagQ28I1cgHBaUitTDzuCdlqeUHyA/UJthcUpEajLlDNWSRCGOoHpxD75SO4PNSQnMZAyyG1CDooQv4FaQCmRIC5CqnRPPAIaxYKtSgWfgjHyl3sWr6NNdDOQWUAfjwTOYTH1F/sye3L2TZ3lms8oa9Q4ieT5njrejePXkldkk0NVwfI5AWZ579VoJVEVFvRNx3lVEmueHSpmTSqpSfQURSpMW0ORAELVCqEKFhSh1pap65xC9QAOchYsxQPEcrxK8sDAZcDw/HrYUgzMRvBMyYbtKqsrMDIi98wRHxooqcaZqoE5ECQ4iUCEJp+iErSU2zFaZvfNanUO9YYIxJsqPyAKIiIMCQ/F4Qhgf0RGNg5t1NIexMdkBGUOcvhVC6SohEZWSwlZbcJ5UH+7PIdSDvDbkKgHEFqAV1Bhmk2ez2dmDz84e3Xt47361Ws1mGTOpUVbKs9y74HnniWm1XBaLZVWWJrPhnENV2FCjxfnZ/ODAZBlMbrKZVGUA62DUUdBqVTBjfnTNHN2io2f54MaBsc9fOzp/8NEn739qrb1YLp6zZ3/3f/pf3Hj9r60WF+DMV6WKV+fijTfBZBa2lYnIWGarKsGNuT7ZGo3c3qv34Rya+tIQcTarqupf/z//q3fe+sXx8dHXv/b6P/u//B9f/rVffe2F2855jUduiawhYlI1gSQbw9E/nokYbMgYYy0RQST5ejIo3IVJ12Z2bumj08WXnr0xz49f+/XfMWQ/fe8XN2489+0//EfCVi5O7n34vsIZ5duvvfHCN3777PTxh+/8+OH54tpBziavfOXF56A7H3763Jd+02Xz85//yTOv/wHscTyV7EVJ4x6leKiQ9wpVNrBZuNcMliPTXJtwFd5R8PzSAn5FqyW5CkQqjhTKlkym4uG8QuKlaeLJl2BWtqSqpAkuPXylvoKrVCFguFKLU62W8GWEuWC45/BpG0U+h1QoL8QeID8mMNQrcW1aRpZTNgNA5hpkSWrUByVuDg6mPQsmEMPkUEA9pAIJ8UxBCFqkXwFBvELhQATKAIEXmIxgAa/iSJzSiuyMRJXYgiAiy2KROSYFA8xk2CizA5x3gBgVRrjrRJ13lYhAvZ9nrgobc1k2IzLely5cSGCttflqVbqqUl+p+vpQPxGFO3lUo0Wp1tGZ4jlLEhXvwvZO/fn4cKCP4rHW4LngmcFCxGAGBJRZJlLx4kWMN8hVxBULiqYtFVUSH65MUO/FeXEVk1eCOK51vWBSDV5H4krxJVE4OaLhWo205U/hoAkoixYFVQo+t4EHqCgY0YgTnLYXkZhwBiJVx2zs7BBsICZMYFIl8QSv4RIkBENtcxdJFcLkQazeB98MPte3fvLd5cX58vw8s+yKZeWCV4yyMd6LcxVBhShs4Jo8F+/JGq9wVcnEomqzzC2K+aGZZSwAWWuYwGyYbTbnfD67CSKy8yOaHUo2L0VvXj967Xd+//v/n//H8cy+fvvWMVW/9tVXn3n5a6UatvOww6FCSuzVUzKKhfnhoOS8MV5UxLvg4hvBm0i8k7JQcUzExrLJLipxXj774Z+99dYvGfLa6195+OOf/eKH3/vW3/27YBL1UC/ekQoqUmIALn7nPGwNssQBR0QUjkmkwRdO1pMymG02y22eFVVF82d+7ff/8dGtl/7Nf/NflavlrRe+cvLg4vlXX1w8Prn9la//+F/+vxdnZw8/++jGM7ef//LXjbUfvvOTk4uTo5kNO67npXu8ohkKzvKff/8nf+u3fqS3vq3xdm4OFjEVDw0+J56gxBmLI2OQ50TBWJZFW5gCKrS6UFmpISoKWa3c8twVBYgzAxB7MILB0VXV6kyd86IEGL8kNjA5WWuyOdlcxcGV8IWKB1g5995DCioXrI7YJC1/pjAgKCllBzq/xszkCzU5OIN4qFdXkC/Yr2AyEKkrEK7qMDmZcEjGoyzjZyeJoFV0vmEGGJxBCq0KIoJUUCUyyA9hs7B9B7YAwa/iLoR40iIeFCUDD0BI1JbFynG4AgwWNMtz78jBgeEAVzkvzhDZ8AEfhhCrihBW/rwqlqLI8oyIs3zGxrAPl2SE6HBVqfEiTU22cFD6Wl3YLUeyqAlIvBcvBCUJzt8Stj3CpS6qyXwbQEfFq0CJES4JiCY6aNwd0uBA7wpXqMkOyFjVuFJRNleQ+oCdwZZTAeFmIVZ1xEazObGRagl18RYPYkqFhyVdRcnO2WYaFM944SkRsYiIL4ksYMhkxmRK8FWBcF1t2DUXT2xE1OQHcYszwJlXVYEEvTjYiUgR/aRApCQEgoJUiOA9ffzhO6eP7harFTOvyrKqSu/ifZDOVd4LAUywmWFFvNMpnECGWsuZNVmW5XkO1cVyyY9PZ7O5zTNrTT6bZbM5E7OxNpsLyLMhV7qTe1WWZ2557aUXbn/918zP/9vf+PL1v/33/vArv/V77uB28BqJvlMQUa/ik4tSuHLRqXgBNPjxe1FjYHV9JwrUZjMn+XnploVXwIlUD+788me/8DbLZ5rPDv703/7L41u3njk+JDtz8hgqxPHGFyQVLvSJipB6jp97BlTViahnir51BAVbZgtjOZtn158le/jcV//6/PiFn/37P1msyoP5cTY7evTZncVn7509Xs5uzE8e3rekdz548/FnH778xtdu3H7p0cnd5Z3VeVHOMhhmFf7gtPj07Q9fuzX/6d3l2Zt/fPSt5ytnvHdhE1u8K6syy+dZlhkmAyWiSj2RmqND8Q6rM2MymBxK6kV85VdLuMI5p8SqplheIJjgXQHvhJnJGkNGnCsuQMzZDCYL18KoVAC8FFgVzpdkrPEunFAmFTKGeI5sDjYAqVQqnuyBEpEKtFIE7zk1oiAnZaGuTEehSCkHLJyjYA1nJgqOWQzKQAxjiBXigvuaike1gi+IAJgQAVCwgZ2DDEAgi8CispmaQ/IOIJBTLyAC5+AcpKoOfmXL1TKsUcThi6kqVeW89yqO4EREnCH2xJaJvAS/BxApUJEnoPSOwN5X4p13pbU5lJ2rxFU+YBni4ecwtonq46tM6iEqzIbDcUunogxSkAZbIwBQOqsXgEZqN6VwIZTWc0ZESSic91SG9xCvql4LBRmaWXvIxrpqpeVSvZNyBUi8bl89wlknAlcEYnjPJgMkKUHC1hDniSmqBjceMjAGysFSo1VF3oEgXsQ7SmdMYWwyD4mKix8WE4URKpeqIGOZTToZDRVR76HCxqiK8w4qYXGO3tdAVDaNXVxcPLr3cbG4eHT/waooV8uVc855UfWx11WZWVTZh7tDFKKGSbwPh9VEtKoq733lRUHz+dxX1fxg7piKi/NslhfZYzufZ7NDZZOub6VsdlAuzqvl+Rvf/I1/ALzw1/9w9vwb3sG5QpxP0YL1MDjN1BcFKMIV58HxNNyiThy/sylcercoitOL5fnFReXFGCvlCuXy4+/96Z99+N7q0cmM6f6H7y/PHv+Pfvf3lg8fzI+OTJapV+XQOGGPDeHEGsJtKL4kX0AkdiATxRsODBGTychYcAa24NmrX/nGB+98tli5n3//3/3oB3/ixL3+2lcfPbg3m2W/fPfN45u3v/+n3yX4m9dv3rx5y2TzsijmN547vH6b7t/zjip1RJTZrCr9n7/34GuvvP61b33t9L03D7/2cHFxsHSuKlblcqXexbPl3lny7EuFVM7ZbMb5nFT0/JHNDB9cY3ugqr5akghDyWTM7EVBZIwlYhioEqtotXBQsTN7cIOtDWScaBbGMJTFC4h5dg1spFqB1cTvOjLncxAJIF4gQnDwFRlDBIVV8bI4YTZKTFWpvmCoCquZazYPB+4JXi2TZmQysOVgzOWMoFqe6Pld+JLIABkYxEbtHGyhjsRrPF5oAFVxFHYVok+Gw/XbKC5ABnycvOGcViX5itQryLqqZGaosLUgI86LlJWrvKoDqTFQ8fACeGIb3JWJAVZGOOXKSuXiolotRJyqIsw6QlWufFV574iSATNtsaczI1V0rCMmazVcZBg94UhEIBJsnQCcc8aYcCdj3KViQ/WBtuTVQOGLbSART85Hr1VidZVTMXZuDg5U1ZVLKZbqS4IKlaThzjkFSJmUmVR9VYqIzeecHwUirQQRH3wCIqMEi3daVWwzJaK4FylwEi5sDq50CnVV9EwiO4Pm4fBguGLbewesSDNlWx8gF++kKsHJZcpX6ipSD2g6VB/2w0lI7n324emjh6cnp2Xli6J0Gk4ksBeELUuKt0sGVxKFCgGWmZlUxXkvCu/AhsSDWZk0n+VZlmV5BgUxC+CrStw5AC8a6ut9dQh1F2Jfeu5Lf+8/W6y0OjmBksJFChmIeDS8R/eadFZLSIOBi4zhVVHev/PZoiiz+aEHiqI6Oz8lhc1sns0Wp4/cxXl175Of/sX3Htx/fPPa4fmiePj4kbL5+JO718z3vvW3/44x1quLgyHkzEwmA9mwA6MygxxAfLyTmuLNv0GVc0reqZdC/JIuFq9947f/5b/40w8/euezTz+syvLll16x+WE2Mx++9ReLovzkzvsPH9+fZfmqXF2//dJLv/rrF3fvZNduv/AGPbh/Z3lxejjPlucPjo6OWN2jC/ev/+KTv/8P/8b9t95y9943N3/TWghlGWXqHcS7YoFq5X2lxQUZouxAaaY0V5Tm6BnNZj6bh4vN2M6NiooXgncVSAyExJHNJT8QEIoLIqYsVzPTLPfMqo5Aykzi4CtxS85mNDvg2dx7DztjyyRC6sWX0ck4fBLQWIaEOzLDKX9PBs6JW8HOmGCI2FhVKMXLuNkSGYN4wwfFW7hIKVxntFqSE8Ao52RyIobNaH5dzQxuAVdSfhisTupWgMLMEO9DFzVAsYDJ1YSLZESdQ1WSO1VfEJTMzDrnLDOcU1eZPDeQ4FHpFY6U1AQ3Re+9U3gmQwgYIiDPbKz1IuIDjfcEXbqqKlb1bpt3DhrOgIXb68PmOnFQGGuTWfB917CARhdKIfKiRAhnkkXC3YgW4sPGbVC+g5sGkVENW4fhAygVrW/p1HB9uS8WjlkBZgObKYcjbD4cCg6uSYH9BT/p4C0TfaI4I3XiKsAheE2QFVWtChVV78gwcxZQWqWK916QECkoj/qOMRTvzgrg7tX7YCAIXFS9ImxiyPrWqsDAFLVjUfDFZRCDTVGcP/j0w2VRemIFzQ/mmfequlquvI93tlvE7wwYAhNEXGZM5SpVMcY68dHorkqkM2st03w2C0AWHEF85aC5sZw8HZyr9ORstXhQuaXe/tI3b66W7mLJ0cKp9fZIcj6KV9aoiIZP+6iCyLBV0MeffvLhxx+tVqvi7LwqyuNbN00+Axv1KrPsoihO7t/HxfmDd39+//TcGi4qd3x44CHvfvLoUfGnX//S37cH14tVcfrogSiyPHciXmANZ9aaLHcCVVhjmUnFR3soaXCdrrwsV6uirMqyLItVnueHh8ff/8Evjm/e/vj9N0X0tTe++fJrv+JFrT+7/+ixzejO3U+YWUSO7fFsdnixEOXDZ195WfSlBx+//eG7P1sWpRddFEVG/OJx/uDBxY+++4tr1+f48z+6/gff9CWYMD++VpWFL4rMGKszOK83noXNK+c9VIi1EmHOzAyqpGEvT2ACezLCK3hH6pWAfA6yRh3zIRsLm4MMSIkzkEK8gNQx2KsB8gOeHcPOVZbQSpwXApERGHWOOWMGaUUEzI7J5oCqL1FVTIQMLIZsRsYC0GwG76g6JylJLbNVeBIPIVVB+Jg8vCrIl5wfanagomALOHUreI+qgCiqAm4FMmRn8CV5B2aIV60i5DkHX4JBYLhCIYBVFRIXuAWqpXVVKQQmNg5wFR8cQNQ751U9KZwLNxOIFwW8FwsyBsHKAFFFtINHv4FwlYr3gBrOZrM5gSpXRpW6tuWnyzMDcjNRpapijYmbj4AGvVNV42lpxDuFmSncTBrtVuHSuGAbJ1JiTRewC9YnkxBPFakvF6owxtpsrpiFI+a+WqIqQw4gQyZjzoiYDLOdIVyrAGjlRYJDBhMsWRZV9YW4UojJWJMdhPvFiCzC1y+lQriHFhRulVLxKk5VFMHwGFCN4zYCQJ6ZWMWD2HCevi3AbPJg2UheSGHvgc/vPqqqyjvvirIsVgxUzjkvlfPee2ssVJ2UJGAim1kRL04rcfG7us6JCoWr3aDzPDPMRFyWBaL3AomqeiGtylVROHlc8GeP/d0FTsr8Jusje3D+z376v/mf//48N2VRhY/ZgojJEFtiluhOE+5ZTBvZIDZZ6eWjjz/45OMPrbVH16/NcuuWS5CXYqFgNubOOx989OFHX37m9sndjz6593DlhJhEdJ6ZR6dnb3z9K3//P/wPf/0P/l7l/Pn5+d2TczAzL1el96rqKiLM89yL2iwnw96Fe1ayslhlhJvXr+WzXMHCxokwm2vHNw4O5p9+ev+jt9+SYjE/OCA2r37tGwc3Xnrp5ed++if//Quv/cpf/Pm/DYZaFZkfHIkXVX9484asTk9OHj8+OymKUtQfH90qLx7l89m3v3J0bNzPPr73+DP3ne98i8tHq0cVspk5uObKlaiabK6aeazAhmzOLCYcysxmKk4AKUuoM6pEopoZkxOBbUZ2RkTpwy2wJicizzY42blVQeyIABWYXMlQfghXiquoOFfviIwaq2RAqmxMdqjiyBjjSxQVTC5kvRctF+RWKo44AxkN+cdr7MUYJjtT76EL9Uziw1lMRQaTIXwWI5wZUJCZgQlupdWC3HnYoQZZFQcoigWKBXyppAhO71ppUARJVVeQJZm5cgYGwGRyGBs2Q1XE+qrygGFWJogUxIZRlaVX9RQszOyYrM1EAXGqJF4z9rAGxNFNxVDwnFCOd0gAqgZcseHAgqugFYXbXVkZxsT5KEoadmjUsCJ4lib/K4XGi/7D+h43EDlsHFBw5EimtKBKiHgmNiZTKK1P/AV8ZCISV1a+MiZjYxXMxho7F3D4Qh2xNTYL/juc5WwzNhbxLJWQVOKr6PklnmyOoECSUVGv6oOniZ2xycI+qIioKz2RqnDY6AmedOJFHJEBs/gygFdwWWA2wTtDEvaFGipEmQhklMPBKSUsFudhA1tcKeKdqHNBb+X5wUycEkn68LIw1BpjZ3CuCi2tIhnPiEikmmU2yzM2BgpXVOo9iIht5XG2LB+v9N65Plzyqcuq55jm8+OL/Ddz+sVy8cHHH/zg3//7v/V3frdYrk4vzk+LEpwdHxwczOdhSbDMXrUsCnWVEpssN/ns5NGjB/furBbnh4eHQbux80OdHRhrq7Iol6vHj07efuvtZ/OZO7nz6OH9u2dLsHHOZVoefOnF//X/9r+cP/d65fT9T+7eyOj+u2+/9+6HbPngxjMvf/VXV2WRzebEfHGx4Mxks7kxpqLSVe7s/GJxfqqqID6onPeSzQ88c2ZsRlw4970/+6GHc5XzSt/4rb/5/Fe/mWfZ3ffenN987u4nb4nS7OC4WC0PZrPj4+unj0+Or91/85cfu+JxfuPF5156496nH68uLm5cuyV0tnL63qn7ve+89Bqbh+9/Yl9+VeY3bXZSsQ2ugrnNTD4jZprNg+cKWWts2LuEwhsib3MJH0BggrGVeJQVM1nLZGy8WjZ8r9Z5mKjQi3PhxCrEc0ac5SY7QD6LBiZfgYL/GtR7lMtwcl4JfvmYqiVlB3pwEyYT70gBcxiwCerhFd6TOHjRPLfZEcgpkcnzeMoxePmpJ1/BexUHZYEwCfmVuiWgambEVnlGxpJTrS7UFTAZjCUyIKNQMnMAsBZE0BnpIWwGk5Gv4g5PsJSTpeyaFe+JSDRemFiImODIrirhY6pESpBws5WXcD+hD054LPAizIjfglYWIB2HU+eLqsryQyKrWpEIjMZbGBHdYpnCN2YkuplqxLDgxRBu7jCGTbiaPdhfkj9qRLUwy8mwzSIFQ7QmcTxfkdwjlcTH20ch3od9RiVfhe8wBvWIjc3Dxerx3ls2BBZxYSUMBiCR4PPCxAYmI3HRD0yUGUjfUtPwpUINJwYKLx7ZoclnNj8gw+qFfRnc/6QqBSXEhREM8UQEEz5JEDzxGFB4R6IgVjIUtlTgq3JVLpfWWmI+Pj4qitJaEbUEUlExjkDW2tk8m2fhtkJ4V62Wq9J5aw0TiRcCrJ2bcLpVdFXJ8ry8KPWs5GVlzjx7xwtvCpobElJDL1r/uMjL8iWe/7hY3Pm0/Kf//O6zB/bFr/yKybNc+HRZWC6ryhVe81lujYHqarkoy9X86NiQXZw9WCzOnfdsLRlriNVXIM5mB+Vq6S8uHt67+5O/+OlN5kMtP7hz78OHp5USiffO//bv/v5//J/+z45v3CrKapbl7/3Jv3n7kzcNuz/+N9+7cDg+PHrjm9/663//H9z80htFVdg8I2OtzQBitvnMOe/4+Hg2n1+/devw8MiH71aIikrhqsf3Hv3sz//02q3nHp2fZQfXX3jt145uvfjsoTz86Ijs6Xtv/yyfzbxz128cPv/cC2zz+Tz/4K2f5sfPnJ2cXZvdVJGiXC1Wy7sPPrt2kJ9dPP7R28vf/Jvfuv2KuWV9XpyX+XW+eZDDg4wak9lgG6F8dhROUCoxRLyrvKjhHExEjqhQkNiMGexKAOp9VVbWeg6fScxnfuV9uKeXMzCxmRt49ZW4SojDqUxrZ2Rn4gutCnVV8LgWt9LFBRjKGQjkQWRYlX2hbE1+qJzBWEhF1QJkOT8gFVcsgjlBfBXWel8WGu7kUGU4SAUVmEzJgDJl1nCEnhgmJwDhU6euJFWyB+Id8QzqAIExxAZsYOLd4kwzQU52BghcmbheBXhSq+rjl76C66oKOVEhDSAQvCZ8NDU7MZEKhK0FESGlcPUIa9hKVWj8qJfGWwsgspzN5myMiI+HN5PCGP18AHiicINXugw+AhSTAeVsDcfzaVJfV4Kwh0BRwSTmBGSGTaBj0eG7DkHVlPjZShCpeoVQvG+MowdiMFoREO46BYwN12YYgg/oBiizpWwOAomYbA7vJV75T0TpYyjep2tlEXm2OHEhr4yMMXwQfIuC4wV8+EaBInyTGMkLOFwrUp/hAYJ1j5iL1cXpg3smz5enZ/lsXlVVNlMtytzkrqwE/vqNAxGZ5ZkxNrOGs0yNLZbFolQV/+B0eefh6aKCMbmBu3H9eIV8Vel5gaXOnLI+A8kMnepvHOSlwU9K/6vXTh6Uh59+PzMGZ0z/3VxeeO3WrdX58dy+/9mnC/XfeOPV5778yp27jyxwURQgE+6NyLL8xuGhJ75Yrk5OT8rVyrsSzAwrorAcrvaunPjK3fnoox9873uyKl568fmPHt7/4OHJymlmDKv/T/8X//lv/O0/yDJTOg9jK9VnX/ry+w/e/fKv/NoLv3j33Q/uLovqvZ///KMPP/of/2f/+Svf/PY1okJ0UVaIrHt2dO0Ycnh8dI0ti3fGZJm1eZYFP7cP33nn4uLs+o1nTs/P3nj+lXd/+mO3XPz8kw9e/Oo33vnuD69dv7lcnMHwl778+osvvVGV1fmD986XS+PuyepiuTp79PBOUa5EpSiL5555tlhdLIrqpz/42W/9xtfKg2v2wOAgOzKH4SS8FxVfQSXLc2OteAnbzc5XNs+JwtaR8+KlcOIrEsvGZLMcmlVlId6JAsaqydXM9cCyP1RmgZIXYqHg5JLPiXM1TCJevBpjTE6cYSYiqjYHWGhBojyzNpv5fB721pQZSloVxCUVDm5FNkd+CF8KAlkIVwESEcRVXpzxy3jW0s7AhrOZ2DmrgDMNR1vZqmZQT/DhmItWC5EKZABFuPDU5jAMUZQXCg8iMjkYlGWQKvozGEPeqQr5EloS2Iqmr6ME8xeCcgqi8Bk/8hIvHmSV8Eh4bYpiRERhMmDy3kHTTdoAwuc1w8c3KWiOJhxi5Gg+ZwJ5VMGDlJk5RmMizplzIhvPPbICFdTFb1vGTy1RYmFEjX9MxhTOw8SzfqFIIN6pSGTYWFWJWwTpbpIAeqoafF1ZRV0pqmQtsYm3MjGTGhibHRxmWS4i3jmpCqpW0a5MTMkfzZgsQGo6KsgIKqc64iyYk0BsaMbGQlzww4geBfWnnICyrM6Wy6IsDfPxbHY0y4LrycN7n5p8JqXL5vNqVS6WK4aQ6vnpmXfVfJZBZXYwn88PiABmIfvw8dmbnyw+fey909PH5xcnD2fPfTl78dbqgw9vPC6ev/bMe6LWZjN2PjOzX+GLN8svAf+AzD8F2JcPVhak33rp4I0vPfulV27fvHXTkBKTr8pitbx7766uzl/9ql47vD7L8xt8/WzlS1ElWpXlycV5VRbeOSfel6t4LL8qma2K+qJwvvKnj88fPvzJj39y9869b7/x5VW5Ol+VCjKMyhV/62/+zd/+e/+TlRObz+Jti4IHH/380/sPn/u1G7fe+NbNg+vv33twcrbwq9X/6//6f/rH/8X/6uu/8TuL1fKiKPN8lmUZWMlmlZOz0nEpUGGQzexsNqtEjo6OP/3kzmq1+uGf/3tjbVmVh9B7H/zyYuXf+uf/5P4n712/du346PrR9esvfOn1+fFzt2f40SfvVK74+M4HLz7/8vG14yzL89lssVyUzsEeXLv+vFud/OiXJ1//lpydXby4OjerR0S3wRbiSR1DyRovUi1XWpXWMOc5VA2RYfLOExT5zIvo8kKcq0QFavNDc3A8I1UVYzNX+bAeq5mHOazGMyNslAV3qDCYvA9fhlZVsX6FaiU2V7KYHWtxzmzN7CjcBSVQdSWqBUsVlCaaH3F2oGTVVyxOpLTZzLD6aqkqho1KOOdTsrtQiGRH6r2Wp+RXUBd8axVe7RGREYrfiWQzA4fzWIhn172j4lzDh4YDdfFnogVKCzsjzhVeycCHA24MIqLMUvgIEaVD8WHbihAONyvigZ1wNgQULNrwkQ2FT/4a0eD8aeBdcH6KdvRgEBMfNhCMsUFfjEe4iY2J24VEhghMCJfaMEcgmxGxCXf9c6UwKsoEVXC8hAzhS9ERFZmZa48bYs/hGFDAhXB+ZY2iFgRmI64MZy2TAmvYxGvUI7RByHtVwBhrDkQzZmPz2ezgaHZ0TKCyKFxZlMtztzwPHu1kLQiMjNjG2484mvOImYNDkEJ8BSEYQ2yz/ICZXVn4siyqsqw8kxgS7/3KyflyVTpniJy4pcdCkOeUO1dUhZ3NH77/oSsLAzEE5/zFxUVZlNaa1arIMgNjZweUzQ8Wjt56/97JRfnLjxer/Ja6kjzlRCDvf+85/mcnr9wrfms2O6/01o37N7n4/qOXlt91VMlD1X9SnBS5/uqz9Mozx6+/9sKzzz0Lwwpgdeo5XDkN8k589emjk4u33zo4usnV4uj4us+OSi+ld+HyEvXOVYUq1Hvv432fTqpquRBflcvVyWefnt67+9pzt+j8NDO0XK1EQSDnPYN//Xd+1zDNMq7EswLqien+w5O/+LPv4+zs6y/eenx9fu5vlGJmRh+v5F/8N//k9utfKR2zOJvbg9zmeUZ0pCDnKu8qJprP5lmWiaiWxcXFBbw11lS+evmVNzibS3X2wYefivr33v/5LMsePXY2f/F3fufv5vkh3Pm9d9+69fwrH3z8rhd1Vbksll6Q54dszgmqnL3y+rcf3H3r/OFn/79/+6Nvv/Hs2enZzZNf4MatVenCvRdhf6Wqymp1od5lTCbLjMnA8Orc+am4yh5dswpRD2Ywe+elPIFqQRR0K1eujM15dkg2V0SnVFXxbDxxOtEc1sXgGuW1WolUrMJW2Fo2x2QNCKWriHMyrOJhjOHj4KRGxqqIc0stz1AuiVQhVbn0xCQF1CO/JvbAZ9cJQm5BZkY2gzilkszcULjm0MEtIU6hUK/wyI8rysBZuuiZSRx5T64AkWYHZEj9iohhDuBLcivgglRQGOIM2QE4V5PBexvO4DMxkwn6jmi60wOwTNBwj7pJV9DEo1eBRRljws1ZIkImfrzLxOsMItkKyGXDgQBrw5dAApqxYSZjjAVUvRDDGsNEM6IZITOcERGxEofFhcGWEO6YY8OGbdgRYEo2NiJO16ipEsMGh9LgEgmioF0SEbNVAomReJ9UxDs2Nuw3R5oWLNfWqjHx2CfPsnye5TM21tiZiffNq3FzqFbFqnCeSdlYy+RcVVaucs5YY7PcEoxWXpdCRGSzfOa9K0U9ZdYwMa1WhVucXxTLx+fn3pUHhqydl8QADjJ7PJ9RnlOW5fnM2NxVj5bL4t/+qz/+0Y9/8ezNo+PD+c3rR847Q0TZ/MKJU338yMmjM3sgBS3PCj2Y5/lR/lWsPoYuTfXX3zj7dz8WXXn7zx5hmc/k4kRJ4IuKHpO5zmdH2f+fqT971jTL8jKxNezhHb7pDH58jDHnqUZqAKopuqFBbahpBpNMSKbmQrpoM5lMJuur/gMk3craZDJZywTCBLSgaKCFRAMFxVDUkJVZQ1ZlRkZExuAR4cNx9zN9wzvtvddaunhPFPhF3HiEux+P873v3mv9fs9Dm7acrvyq8esmLpoqBE9UUrcl500Vybl6ARAAzHkvJUBOeRqN9pKn/ZTI7ch5A0LniJyqWMl5mtQMDTRnA5imaRqGNE6766uv3ll88T/4S1f7Yf///htq2qeciqQiOee33n7zzsNHV9tt0y6NLIk656q6Xj94e3Xnterktfc/fGezrKOWDMAcOGi3318+efzmN38aJHv27B27eRBDzDyZTSkTZ0RjdrGun7/36Xd+41+/eHl+7+w+IHnIH77/Qw7h2bPHapDAqlCtlkeHbf/lH//i1UfvZvTPn78PCE2ziPVCFOvl8ZSz3x0AZEzT2Re+ubmzef+3//Wnz5/91L1q17t1vowuj+KMEW6dEuB98FWDpiYZSxYp/fVFun5u4x7BuFm5xTFXS2QmFdMCebCcjNzsAQ2xNl8xO5OEZVJJjjwgUAi+2RShUpKZhRDJe1PVnIDZuQgIpgnBTEx9xb4iJNVsAICMs4p5OpAV4iDzZcFXSKgye3dQSiKsARQVZgGjcQCOiMrE6CvLk5nZLVK7IEdAIJksT+Ci+qYYYJF5jo46mCUiMgrgHLoIuUMtRh4pgvMAAjKB5fnsYgDoGjQ0GBx+Poy/7YbavMIFB7P3DXG+whKTmyHMTDQjW8mxc26WWd1e6ZiZyNGMa6X5vgaEyERM5Jl5ntCDzsckAEAwZjJAco6dR9OIWIF5BEf4OYUC52Ld7Y5zfsYTId/O1oiYAAF1/hPPvzcTwx8SWQmYAtJcyTZVLTPYi9m5gETswxwTIOdn0BGgs9vUHAI6H2tidysBAswpFenTNIUQUpq6wy5N2VSu94dDylWsfAhoethvS8khVuQCoFSMNYHML0wTGEvOKThHrqSU+767urke9lskjM2ybpfmXAFgxiZW0Xsl8D44H9oqDPvL7/32b//LX/nVjz567EN8vkuyzfRiyuhCuwR2Q9EvsH8O2Ispinc9luELrx0vl8ev/IshH1IeP75qsvQwbOG8gJYf6tjx87c38WizXAauA3hHBBaDN1XJxUoyJlFFIkYGU+cZVIAEAcwwhKYAq2ZNA5JDRJUMgOwjMs/DTS3zi+kWfFVKKtOkohcvXx57/Ymf+2Pu0Tef/KO/CTLXTyCLFpHA9Ed+/heO7z+Kjg3JAOe2jkkZ9/3S+TsP3vjk6vL65vwgoCXvRNjXUy6Xz5995cd/phh750TL0KfD0AFSrNrusL+8vJE0kua7Dx7eXO7/27/+33z0wfuL5fLh61/ZnD7sLj919Wp78zJlZcYipW2aEGO/v/7gt3/T1xtXxZvLF/Pm6ujOIzTb3lzfe/jW2B9MxfKoNi2P72XFjOH8+fVapkdXn/DdG+a7875hjp8bgqGBigyHPPVqqOT96q7b3GXHHCL5SlR1ZpQyU73BlrVMaHo7XTUzySVNlpNpzihE7GMbnEPUnCZVSSWF+XNuqmmccgIklQRSANmIKWdGIDAjkpzRlH001xrNq8siqkjsqzWB4TzsQwJNs1KAyjTHqIA9GliZDMnsD+ficLuoNVNE9TWCkElw0RgtT6BzFNyhTrcl89xjGcESSA9aWVwgONMOdASo0FeIwQRQBtTkqlirFgBzn0+1TQvNBxjHzB6I5oWmIbALzG7G+DATO+fYIQAiFdM8f9cSxrqJzWKx2YRQjUOXx75Mo+aCM7LRDDkQADtnZsyuXW1EhZhXx/em/ZXtbiowB0qAhljACtKthB7/8MIIcPtkZCSM9RKZNY2gAlI+bwMgzDH3+VTGrpjmaVLAV9c317vdom3WiyURVsHbMDJx3TSikIYhOqoad9n149iPU1p5SEAQF5EdmQ45W55AS1W3Ptam4kyAeDLM5IPHaIUKFJGKIK436AOCpVRyKVVdVXXTZ5Fh0KlDQkAvJRPIcVU1J0epbesqro9OY1UT09yVspnhpSUQDfubX/mX/8N3f+PXX//6T7ije1ptpVpM5IjD5HR4GJqrNoz2syjfMP8DK99hftB0P3Xv8rtPli42oWnwdH02dkfeK2wiaCmJaTo+3jw8e+N06aqqbtdHkrOWSVJixwaQUhYRIjJV8h6kmGZkb7dbmVl5pUjEIZgKGqoIzv2w+ewxm70B2XlAVNWckkguYsBBu+4nHh195Wf++HV7l169ODz5GBBFM6BlKT/9J//TL3z1az/x41/ddd29oxURGqBjRuJx7KGunp8/ffw3/59ffXR/GKcB6G7rP70ZxixA9PSTj1WLiA0yTqXkIod+MMCFoWc+2yzIGkBIo/zOt783TXm9OVkfnx3ff+Ps7p0PXj1frE5evXwSQ8xlWrSLqlmUIjDt3//4o+XpyeP3v1eFSqSwi+vje6WUq4vf/+Sjaya4vrmIsX75+Eebk6Ptoas5PO8mfTU8/s4P3jr+Gq2XRQGIkqjdwl6Ab0GuSMQu1LRYOQRQYbSxP5RSbD5oiLFH59EM1MWMzKg6o998Tb7m4A1I1ZRdlzKUIohAJEWT9LcbdyMwQ0bwrQVAAJp/GbhlWgHXc9gRzCSPJgnKhDoSUnYeOXCogbwGB9AwO5bRDuc2zg1wULeEao1QiByQQ+edaQExTaCiyGCiMppdUVxhvZp5Tei8iIAEsERFkBjcEiyDZZg7lM6jX4NUQLO+gEEVgA29CzGq8PyIYWYi1EIwR+di8Mwz00YBjh++uTg6url8tb+8IARiR4SemQCQ0YmyEnnXLlfrk7M7999oFhvHvpRUShr6fR66PA67i3Mp5ej0ATr2sSo5h1gvNicAKiLL5aYMdw5PPobD1rFzi7UQSt/pOJjK7dUUYCpl3/fOx5PjmjgQcwEmo5tDV2kGMwGsYmRPaiBGjChSpjJup2k/JkNOCtXmBHxISGY2DlPX7UOs4jQZuyyQpTSHPpfCRIWqiTlPQ9dd1Y6W0RexouYRdRoJLHhHjkOs1lW7n9J2vwMF7znG6GnhQihmVgqrTgDTfKI2BS2OcFG35HxXihnE6NrVsp8SgJFntUzKgQmZbfb2kANL//Tv/LV33vlh8atPnr5w9VI4gMEm5CTY11GWwC8moOY1xQubeiTNg4q87PzkVqFpp378E8PiVyCONX7jQUt61wwXjV8tF5bGkhMRSynEbDI/mPT2ZD07oRHnjZtJRmIthYMDUCSHBloKzS4rx2RGoWIXbgcTZghsOKtOeWaiADGa5ctnX3/j7sOv/fSVVcMo1bQv/TZ4FqNxmCgu/8Sf+/PtanX30Z2+6w5D7x2wI0SQlIzo537xPwpj9/1/9v/FkndDWqyWhuQZh2kSoPOnz4bDIWG4vrpAwrZdnBwdT7k47wIjVWHRtNnwd37rB1mBfX18uvr5P/3n2uWqf/mkT+MnH/8AzJjdYrFYLJbOV5HtnXd+//js4fd/79dyGaMLZ3fuf/FLXz+9ey/n6XC5+fjjd4ahM9WHq9Pl8ngcx3FKVe33Cb5ysvi1914++NoP9fW3Lycg50K7AYLZx06goKpANvUw9kyW82h50pIptrzYGDr8vPBnCODjraYEiQmZEG4JAmFeuyGaFFEyP5eNTNnN7UrJ01TSiKpMiuQpRGZHCM65JFKK3E7LVWanMZDjqkJaqYpKRpvBD3M2SA3QXOTlXZl6ywOhUtUURMsT6aTFQWLJe8jDjA4DVyGSAoGhjB2kAV1F5LEkAAPywJXdIv8cECEooIDNflsC187PfEBCnGm67BabjSN2zuVpIqIYwzQOzvm2XVZVlfsDM6mCb5o3vvHTi/Xm+vrVp+9/f9jvvA+hrquqlmnw3rsQqaqqdrVYHccqxqqdr5MV1Eykx2emksbhenMKAKvNndvogt1WrA1n+Il69g6AXMCqgcWGQ9W0I+6vcRyRyHu/PRzO93sBV8eQ0TtDmabrwzWyE5FkYAZFcwO88JVzvnYsami+qsKCQ4GZ16pEfBiGbrv1hEerjayOlEilKBGGZijFZFoRmRmFagIKVbUGCFWsHDv2THR5dXWxvcokZ4vVYrFAQiBesSOkPsv1OFWiG6/Xl9uk2NZ1AUpmICp9h7dLJmAGlYlVCFG01BROly0YkmO5PfAaluIYWc0AvPevv/7wxdNPp+r4cdq/XQUCqjB99Ti9f23xSsNLVUKAq79nUyCNiK3mhM37B26aG+/f6HfDq2wHKZ91w6fPL333MmX9yhce/thb+fhoTexBNaeR2ZkZMZlqSZmdM5O5vahmwKyAPE8rAcGA4HZ6O/eUPAVkdr4CmjmlCgiqxZAAUXLJOZUiROZ2lz/zzS+tv/RT+8wBzHtUywKwiG4aJKv9+f/F//Kttx7uD+P17nC8Wh0Mtjc3YJrGw/X1tfPh5OT0p/7H/9NVjL/6D//usm2bGFwV6TB5pznJ1fX2yeP363tf8qFCxFyMiU7WyxADIU5ZRpUXLy8ff/Tp9777by4vz9/6wjeHwe6/dnLz8tmzZx8hWJGyXh+98eaXi2LF8uknH7pYf/jhH3T9wTufUY7uPLhz996rZx8d+t1HH7+72+8WdTtOQ0pTd7jebi+GsfPUPB3Lj90cPrkqL89fnXxtuViucHaAlbkcYoSI6NXAea9p7F89Kal33hEySpbDjZJ3zlMMrl65WJkaoM3eczYFxzZHsVVn2xk5h2zeVUykt4yYUrSUsdexszyhiRJzuyYfzABAZexQbQa364xmUQUrIFm1ICGwR1eBqqmATiTJ0kHYAxo1i3DyICdBK+y95uzqNSKUNOVcLByjS7er+RmyhDNzQcEEQVkFGAyMzGBmN/va5q6e49sMMCBImTmGgAaSkL1qgdy7r/7kz3vnhr5z7BbLdaiqkrObBSQiaRyYGJFdDPViiYgnx2fuq9/a31yzD4vVJga/vXzlnavaRb1YE7mUhpLSZIZInt2hlG7opSRAakL0VdOGoJp3+90wdMGHQNQftuM4MbLmjGmkcWgWaxPor66V2DnWgqOxKTXGvl7cPQJxQQyLUpl6VDlqatJMlQcXssKQpuhCqOq2ik3w6IMAMrGYjlMiROTYlxKZO9BU5Gq/y0jgXBknAPRhiHWzWSxuQULEIYTgVghmyJOI5rLvume73cV+XLfu6sWrk36MMfbjUFVNNsylRB8kjc+3h27osqEbxxhi9Bydr2NoY2TiQOCZprHf9TkpdCKZHNnIRG27dN4RYDElMAPsS7nc7urgv/Gzv/jOxy8vdtZs8Orpi5Ojesr6u58cxlKC9yd3TtatP6odg0getEjJxYDIuaaJ+fLJzYC/MybvKn1AN+fXcLk/ev2LVzl8573nP/+teLJZAXMZeiKaKwczrweJUVFV2QEYuhAQqah48HMaUXJvwEjOEBwTMpOL5AMSATmVQsSWRhUpU0o5pZTLOLx51Lz9C3+sLO6/3I02Q0GQqV40r3158YPfmkL8C//5/+onfuE/zNNQez4MaZqujtatIZy/uhyHwTkvxs8vb7Zjqr/+R77+5LMXjz/YTSM6x0ya0BSGMZ1//MFXHn5lP+m847s57IvkWiukoEhg7uWzV9/5zV9++vTjdrGum+Xzjz949vgH3/v2vwjeNU2zWh3df+3No+NHd+6dffKD33XVcrt9udtvGclUT45ONQ2//E/+wTR23WFnoIToQ61qpmUaDjdXr1QklWKGn24lMF/suzuydfUZIUgRBCo557GbeZBg6hzr0E/TQOwhruZMBnJwVWOSy9gROwPVPIFKRmeSLScixlihC3OnhtDlKQEYmPCM1YMZps6uWkG9QlQUAWJyQcBEVHNvUwemIJmrJbioalAy9FeWBrxlLs6vMEQXKdSiCshURsm9pIMr2biyPJJMYFpcAF9hmTBNMBcfgQ0KipoWY0ZXAwITIoCUEVMCMLFCIMAR6hNkgDIgGYAAIviIYmAArgJ2BgAcwDnU5B688UVCGscBFKq6mlMOjEg4P8UVAGWauv31R+9+r++HsZSjzVEbw2LVENjFxYvzzz4JzrsqrjbrYZo++exxnlL0PhA/unt2yDZOw9LzZ7vegNaVf3C0QZOLy0tGmLheBm95Qna1j5umparxxzyJ3YzTNA4EOpia2SBFDK4lN3VdhYhS2KgK7mpMU5qW7NbtYlnFUFUK2A+9AC0Wy1jdLrVGtf6wn4ZhyhkQqqreDSOoYbX0zjnNr61bpTDlMo3jMHaYBrdoqqqO3hc1A8ySVbVY6qd8GMeU89Fyeff4xPtoaJ4IiIlYRD0gIxxVDpt1Xi7NxBEbspnC1BEYkTktqaTtNJWSV3VT0BHbpm4pNkWFEEfV0nWeeZ5iFPDAvlluHGodN2//3J94/tvvLtL23aefre48ODk7fbG/+Pobpw/urTfLJWhOXT+OhWJImCn6XASR6tohTFevtnS5LWFp7cKub8LyxNXrL9yHd955+hvffefP/sKPu3bpmpbMkJzkiXkwCVIyec+30ikzEzNl9iKCkmkexjMbM6qSC/DvYsyoqkhODYBc6vo8TUTYaHr4+tlrX//Wi53imNq68oRmBuzQB/q5P12G/ps/9vOnX/vJYTjEqlHVwHR107188eqtR3fiwxgZdtubKefQtIaOfNh89ZvDzauqtK/2O7UZYW59Lu+988Nv/MKfWbaLGGNV1cE7QptyFsk+xPe+//t//f/6X58//5TZ37332tgfxn734Ye/b8WKMBJ97cd//o2vfKvbXk+7C/WL1dHpJ5++Owd4mqZlxo8fvw+mSDQDIA3MeY9W+1ABkshty9qAP9qVOtKLJy++/PT96Y3XJfWSE6qaiaohE5FDNLOi5OLpa8RhNmYisa8qxzRtOymifU8pu7kHPUsBspiOOPbkAoIwe65q4OB8mDOdNputDGcWCztnmk0NVHA8aEkmBfIAJatkRiuSAQzjkmNtq7sqtwYA+jxFBeQECcMCJNm0w2qFAABMxBZagorII1rK2VEEh2AG7MBXCAgqoMWsgClqMSUlh65FXgEYlsmsABFJRjEEgnxAm8AAmcFF4AryYLkAobEAknHlrl6et03rQ8wyXby8Rsm1ZyPH3msppuWw314//URLrpkXhKDu/NNPCbVZrg/dbuq2R+v14EMeu8uriz5lBV5UTQW6T2V3GKu6Pjs9VYQDxMvtwZXsyiTkzo5PHpyc7ARTyf1uq0DH6yPPGBBzKaDCTFVwAbUfevZ+vbkTm1pEgMn5aFPWadAyrtfr6yHtlDFpmrbp5gY45JTbumLvnWNn2k/D1ZiHcRQF9JGR9mMualUVHbn9MIJk33NwucsqKmrmCbth3I3TTOIf1FA1hjBXENZNo1JASlGx6eCZ2qquqipHPvRDVgjtyjEqzO4oRyqjlMvd/ub6YspjYH+yWs0bSSXcjTk41pIIbdlUXEV2rApZlQCq4M1sbuWPPnd9txvG7fbmLt10lqqqPrpzLzA09zb3NrXL47gtRDgLkovO8BWIMQBA6kcfw+v3j6rgzl9d7z/eHvpJq8PQ7f7tD93+6c3dH3szpYlCIB8EIMSavJ/2BYD8/GI3I2LH/jbHi+TYgYGYOl+DqomwC+Tj/MGRaQJCIGdIuWgZ+3HoEfGY8Zs/+2PV8cMffnx+KHN5mJrg2LngHOBwUPran/2LLFPevcpcPX/84T/57/5e3493H77eHbqf+aM/s7r78B//f/7hB+/+EE0fPrj35a99442vffPRl7754v0f4OUrj+Hm0GdTEVHVTz592t+8XL3+TTUbRfb9UEo2FUZ88ezV/+u/+b89efJxuzz6yZ/8o/ury6HfPn/+yTiO3nlQrJslhoWrV1+7d/JvfvlXYs3f/94feF+B6aJdBOf6vlu0G2TYby8BAJEJzPuwrJvlehPrlY81u0DsHdp2TD2gvEo/b6n03TgNCkqfi2JR84w2LNMEasTOiMBUi6CnkpMkVY5AUfOkZhYbIJt1nRiAwc8qH+crjjV7x84je2JCRClFBVALmgCQDTuUCSnI1KtmkmSpFxHk4AhFFKZ+hrpYPqALBGBA6Cub9SU+zLQbMDNQq4/moKuogAgRKTAR5GnCkkRGnGngwiATEBkFJCIjxw6oRWZmRFBNk4hhaBFtvsMCKPganDOw2SMDJoAMVkAJRGEY5tid+9f/v7/PDF96801kV4os23aLMys8PX/+0jkfm6YOIfi2WJ6mUdkYNIl98P47ovnOZll7Wh4f90Uurrc+1Ow8mPr15tQ5IicEB0Ao4jQ/OF6tqlhHTqKrdkEhrl1QKceLBQIs23Z2L1VIG+fvGpgpmopokTIqHobRFLAkV/I4TVpEy7TZHKfQwDiI6S6rlbJo3M1hS6lrHdKiLRQJyknroK5cjOSclVszbp9S33eNI/D15ZCdDkg8qdUuTJJ311f9NGUgIm+AMfhlSzXDwjue5Ssq0fliWAcOwRUzINe2i0DovBtzEbWis+nLkcmd1fLuqk2lgEHlOEsx9gY4pakgQsC9SHdz49mxj+QDAILJrtt772OInsik+FhdvXyBu5eboLFavf2Tf5QZbz78/QhlGPpC4J3z3s2liJQzwMxKuu0/SC7MdrxwARdTksNQPXt5c/jshxwXy+Byv3hyvnt0T0O7YA4qBYnYR5MsSRkJHftQiYgUmUvJMOd8VCSPZkDOz1RFdqyqn2PD3bDfD/udATjQ+8vw1Z/48cTLdz86nww2y5X3XgymoTs/P0ez46N1RAlZC3ntdz9857f+h7/3dwNKVdfP37sci/zzF49f/8KXXz158vTZc0T47Pn5t7/7u8GHt7/ytdfvHHXPngUEU90Pg2f2SMM4/Z2//tf/Z/+b/zJu7u2319vrK9OyXC481r/yT39lv99/7cf/+J/8j/+Tj37/D7rQXZw/7vqOCEspq2V7enbv+uKqv3z+nR+8eO3LX/mVf/Q3h6Hz3r/+6MulTPvdflXXm5P7aiVPaRwnRCSwtl2dbI6JPFeL28cZc9vUw25IRoehOHKr9SqmmM3mDPlsd56LxrS6HR/D50Ua5NsuOqcxj8Ps8Z05DszsyELdGEcznWtuapBN8zQ5ljmwZKIGBsNB06B5smFLhNAeIzCAOQ5YOZt6nXqTAX0FHM2KTj1wgJRMM7nAUDi2iL7kvpSCWsD0toFjOpvxzDQXQwwpJyuCJaFMqAKzrcIKIEO1Ih8IBKcBLKMVI9MsyA4wIDYUIoYGrAEsaAgYwXsEtrkKLdkE0AUkNh1ABhh7960vfTGlqWTZ3uyPlos6uIRB03Sz2ykgx2a9WoMPOI3DKG272qw3R6tlVjH9Uqii996xB3bTNJ6tjwSAEQODC7UPFRE6RhFlxHJ6bECHLAIo/e7l9bXqzKIW7zgi5b5ZVXWoqoxw2N3kImaWp7GtayO3P/QvL15RHttYZcD9OPi6GVO+mc65WpY0DiV75op9Uov1IqE92/c7fQm+AtM7y6VzBCKTFBCrQhBEUQvOLdtGDJtpVL0NYRvQkJLnEIIGJCQuop6MiMkxxhBi9FUVmImwGNTM7OZQLs34VNES2Clq5TAlrUIoEZBJSp5KSYL7vk/jNAzbtmlOj9aOnUrZDtPVYUi5Z5689wSW8uQQYwh1dIsq6OFm7LZpf13l66ubq2e7y8W9t149/hEOB79sRdTjrPs00LmkSqVkREwpmWioQiBPaCzFg1ZNWK+a1bL+6NMXl/3u63e//D9vHvyd84uqTXd5UqfgonOeQtBCM7waiATRmJEYkUUF1FBSnqeQhBxrQAMT8jX5WFIau25K16+eP9VS7p+d/ti3vr65//r7zy4fP/3+7uYqVtVyfeKqpomRykCau2H0IawWbT/um4Zf7ftv/+t/VZEuqqqO3nzss/ZD+vi9d771xa+ebja/9t3fVhBmzirv/MHvvQMUg28cVI7VUFTR4VTs+z/88Ff/wd/+y//F/64+Oz09OUGwMpZf/dXvHt1/9Jd//hdXdXj3O/82mRYZh7FnZlXxIWyONl03vH7i3/nOr3G1eO/d7xwOu8Vic3J0YmDPXzz92ld+8mi1On39S1cvX5SSxrFTVef85uy145NTRDp57QvoXb+9MpOT4+UnNy+YcMzQ9aVuWnCO5hhETjruoGRgz6FysTbzmoaSshZRAMgTMhMhqHKshJBUHRqAmmRABnbsfc55ypncDGj1M+lUzDQlVYE8levnMB6IAKQYEcK1X96BUElJhIHa2ppjm/Yz/WZWiwE7kAKSQhUpzK+JnFJCACsJAM1H5QgqlkbQDLPBAAcgIheUHOICEcAF4IBgxM6YAVBMgUaarnUaQTO4CJYIQceMWmGlJqoqiI68J8cmCWZArAsQm7lHaQlxMvDBFaKTVXNTcOFj3Taxqk/rUPk1vXa367p916uIQVrf2TTN/cVyhc6pmRhKSePQ39zcpJTBeURkF5IZmuSc+6vrBMTMIUYiAiAR7XOWXBxCxcjMqchUspgZJKeWxjL4zsUoHMDUezdNGQwvr/ZFxIdwtjpuyKrAh34cciaCo9Vyl2UoChwdMBK5tq68q1pkdknVz50pxF3Wsc+Qd0O/W4TA3hcpZuaIur05FwBpvVp2WVIaSTWYrGo/FVu1rQt+yAJaHFFb19H74FxRBYAspSJSsDRNkQmJRbWUlMZegSZRQozeZSnETtQEyJD7PF50fR6nKU2HnJqmOfQ3Hk3Is/fLqgIpklPwTHULzpN3ijQZUKiG8w8/e/f7F88vhm4s17v67G5omjIwETvngCBLYSMgmFJmvvVFIigwIWIM4XbcwTwOQ2D36P7xogk/+PDVU67+7zcXzw32z+kXGw0yxSURO1MD5xEM2Kkomakau2Dk5iAyIrhZN6UiJUuevK/AZzWahn5/6M6fv+x2u7ffePitr30hnL7xsoOq3dy7S+vlkXfoXABmQJoyW2g9VZfb3bNXF2sPp9b98j/5Z8Or58fLitiJCMIU0FngMZcP3nvn7v2Hf+HP/Mnf+L3f++zpS3bzGwXEdD9ZnzUwOgRNUnt3vKp/+9vfXa7+2l/+X/9vP/vs/Le/81vf/re/6erNycMvxqrF8ZrbTRk+e/rpB94H57yIrFdLBHaEl88+6Ic8XDw7f/7pZnX05S9/48mTD5988LipG3bOOe9CvTo+m7qL3dW5qVZVc//Rm3fu3T/sdymNIimXpJLRHRs6BBGzKZUgue8POWdVKeNYxg6tBB9inSUnIE79DpBcqKJjMGMw59iIBZyUZCIGAERspgCTUlBj5xV5ZjUTM4KZqszspvlDevyAStE8zFhwAFDnkZwxiWRGFjSLS8BZ0T1P/4xcQPGjmQ0TzryzPPCsjCJPhA5EY22hBhXvyIAMREsCQwuzeoYBQctMlDQr2TSDJAODnAn9zKkgQNTRcpLhRg4OfYOhIUog2aRCQPOBDGBGSzKBKrCHaoEG7kfPL1lLE9yd9fLhujmk8tF5Z1rqqrre758+e5bFELGuIpqs27pp25QKg5naME1TKaFq1PlYVW3buKrRNJEMTCwcx2KHmQxpWAUfqpZraB07oiLFN9AAV1VUURKNaKg6pdR325JHlDKmslit6qqJde3IMSKpFhWumnXdAuI4DG1dMQVlzmaEKMw9E5EjAKHb1KyKpMPusLuZ0jiO06KuwVdgEqwsYrzMulqsg+d+dwCDfpg8Yxu9ItZ1yGAll9bTYbRXu9111zV1c7JYoKbLq8v3P/2kaVr2QQ03y2XTtmbEqh7KbpouupSLNFWom9b56JkDQpLkEU/bpg8RaQ3II3qoV4rgiSr+d315RfTemYFo8Wau7EsZ9ofu4uWFEpvB0arRYbs+u//qcA1z/YtARRBAJIOpZKnqepomAwsheM+l5BhjvVrmXIJjJFw31aIKqvYHnzx5aa/DuvpgLydPx598Pco0ECIxO+cVCV0sKZupobIL7CIys3e3CJAZUyUi05DTJOM0jdPV5dX58wu08h/8/M986af/eGmPtqOMU+q6PaoET2MuQxmnnExhnAYiij4q0WK5qJrFxfbyZ/+zvzLtb975x7/EZAUpSSHUxgVHsYf0/Omny9XqP/65n/rk2YvvfP/d/WEwBFBz3ovZWEDNGkdikgqqyMX55f/5//B//K3vfOfQ9/fuvf7ozZPU7Z++972r6/1bX/niu7/7b4lYTb3zr735Vtsury9eNpU/f/6saLl4dd62y5OT048fv3/+/DPvvZrtbi6oTKFdTSmfP3+y328JCYgunj1GGW8OfVs3/e5mnMZx6u8I1ot12r0ShW67X6tUzcIVRTRb6DyPBzBTnXUQFKrZhyQ55d2lTj0isvNcrzDUyGE+e7OjuQIopkQUaTaaoeEtiAPgFo6APoAPljPWrZWieYSUJM0GOQDkbECMTA59gNm+MEPTnScXHBMSFwOdDiwFJSMoUAEkUUEVY29qWQ0RHBFZnkVoJQ0EhqZmxQCBKwi1aSEwQEbfAhiBMoFzHgkhVoQGpiBgYICCRpAGQ8AyAqDNSmNWsIQcQMCA3Dj0kkvv3OX17mq7Xx4dx3aNoRkY16fL03sPRYwJxv6Qp1GKJCmxXbSWQeTuetmsj3yz9IGZmJkUSKXgLAHxseTJgYUQEEgMhpSmaWod9lPK08A+7lPe7/d5HCNaJCYRBCCCtm6C9y6E0LTgPALhXKVPud9v94d9n9Iw9Oq8Q2tXq9gufNMugnPOF+JBIGUTK3NJCczGEKpm4bxXgzJNqgWd76fpkNLJpjXmNoQmOAQ4WmMpEh3tp/GmTzkP3nHl+fnV1a7rDWDRLF8sutMmNqG+e/ZwFFMgILdTf7kbUy55msbh0B92zjkFcj4sF6mtIpqAFArxuG3OVstCVBQZkQjVlMyAKHpXFLKYImURZ+qIkBykw/bZ+1cX55fnL+p21V3vqqbqDp2ExYsnT3LBF90BjmERHYHNyUI1UNGcEiLGGInw1h0AYGrsaLFsTXXsutjUb75+H5F+78NPDnfe2n2j/e0/SGfr6u04FkBk9o3j2FDVWgRAdCFWTeN81FKwJBAxKZonUxZMWlyZBgIahuHq5cuo+Y/9qT+lZ2+9c9Hx1YimpWRAaqoqME4ibd1ueJlTMmmd91XThBjnz6jcf2SmwXvK6Ye//A/QjMyKFjLzHGrPBNYf9n/w++/cPTv7i3/qF7aH3UdPr956683f+M3fuNwexHDdNrnoIYsvednW16P96rf/jWM8Ojo5Or7Tj+PxRt793nePHrzxz//7v6V5Wi43/dAtlkdvf+kbh14e3H/4w9/9zazlxcvni7o5PjrebW9CCDFGZs6lbPfbw/ZmHIe+iImQC1IyIOapAJApbG9uZJhE1UqZxqldHo03LwDx6mbfvHg2KcSq5hDVDAAcRFMZh15KJmYDqmKlxcrYIVJsV4gIJWvqTUtsFiFWxUBU0czRTOu0nDOB2izQmy3aLtxCC0V06i1NyExSShoBDIgpLJ1jchWYgYrlySFQCCKGFAXARLNKAWMg1IwqzjtlRhcUWUpGnkVXCU0AwFIHpZBzLtTIHp3J1GHukQDMCBF5gT6wJskJTRgECRG8GDIGBMnqcBxwvADLSBWFCp0HX1tJoBk0QelADpC3gIj+BNzS3d2szs/PnePNvQexaWJwrx81RpSNyOy4DSnloT8smoibZTG83O76YejZWyooJYiw5GBcOSQtg8JhTEW1n6ZYhUPfpa4TgVg3CDgNXd/1knPVLhbLlU7lMJaU89j1DVMbKo+AYOi4ZiUyTGkQQ2AXY2hbBUxWEpEwh6Z1zaJerIgZUGY+RpfFAWUt+76fNXQUfBUo+tBWnoij51yKqZoUQ0qiKtmx65MagpoVlcOY9sOAAEPKgNTEgEjbsThfvXbaFpzBTvr80EtRMKhizKUgGJm0zrXOTYxHVdCjkxA8qAGjGeRpBFXvQ6zaq7Ec8u6N43UqMuWSchqnMeeiQFUVkd3MelNVUCklO4S7jbt6+vh6e5OyTKJEJFIIyYMEKOfnz6vFqu4LGxBqQ6SiM99TRNmR954IpZSSCwCUXHwMi+XCRPphNIPo+LUHJ8Mw/PDTT8y9nUb+weNXp4u7TUmuXtg4hMY5F6t2kbMAYhFkkHWg48WyqhyY6th3+8NhcD1I3110GRYev/mVN9/+1s9eh5NhTK4MaZwORc3HZrHOHF0IbWy8c4QwmR2mw7jbh/3h7vHRqm0Gka4fUkqMRnffOC/xBAciRLGSM6tEF9ExEaasz8+fPX/x4u7p0X/4Z/7y2z/xcz/1C3/m0x/81m//zm9+8PiZKhbFguCTPHn6dL3epGn0IeYsm5Y/fv8POLYfv/s7V5fPY1U3VXt69/Vv/vTP3lzt33jt7LP33vHt8sWTj5q6bduFqanKlOdZroiUYezYcJyGru/SsD90HSPmXA7d4eLF891uPw43aRz2+62W6ebq1dnJRgGagL2FKUufUirZDKBkH+IBEFVmADwxE3GfRis5DzsrBUNcnpxVzdKj5ZKnPAEAhzkHa3Iru0BhEJV50n8LpJ9x7DnhbNFWI0Jix7EBduhr8g5NRYulpFNvJSUASgmZXWw8k8kkZsxN5SEPaUrjlHtAQi8zmWPO2SIQhso5D7FRKYQWQnS+MjDnSadJFFCSaS4yl1XFTDV1ljtAQ99ybKkMcnODzs/XWwMC4Pkzy7oDK6aT2YDTFqWAFZNkOaHv3Z2To3unx+xD9ME3bcn5+tCzyZhyTgk27XYqL169UqC7Dx6EUBHR0WZztFwG5820LzKIpiROkMyGoc855ZK7oY+Oq3YF9ZGphlgB0nJ9mkvp+44QY9XEyh8TgFlOWZMwIIOBWhrHNAwvdztQKdMIJbd1vVivmkWTDKrVarImpTz2Hcm0PrnjYzXr56ZpPAxjXVW1Yxm7ECvmCKWMJU9F1LRyzocQQkDgw2E/5klT1jL5agGxEQABBIAmuCbWhuBjlYtMKUVvbdsUtbmYXkpyIqkMKZc0DexcDFUdwvGi9oTbqSpqRQTAQDSV/OrySvKomlW0Xayr5aabbDe8BLOh25ecsmrKyRNXMQKC88F7j0CiAmCOXSSplms35svzp1OWKWdQYMf9xfNHD1+btlvZdXsRSdRUDpkZjAnYIwJ6H5z3xOi8l1wkF3OGmaZxUlUzKznX7aKpm7ffuFfy0/fe/1CO3qqqw/vP4zcerXDsEUmlLnnk2JLzMvQ47E6r8sabZ/XbXzBqd9fdbiiH0PfDkOruy6+9Pu2vVw/eeLadng6GWWdeiBq2i1W9XPtY25xuMnOIdVW17eLs9CyXYqKM5mKIRcMmOCCQ/P/4e3//o5syBX3UEgJmhJyzqTK7QA4dIXIp+vTVtt7DO//oX2oZ7x0//Kv/xX/1yQc/unr2o2//zu+1i/VXvvrNT5483733g7g6ald3nOfu5tWQptLvLy7OnfM5pRFpc3y6ufPw3n364Ld/bXe4efrkw9OTu6plGIemWbat64aOkGYQmOTi2DsXYmUgWXZbNb28eNbUjcnoQ5WmAUxUi6iOaQRAUUy5qK/q03uuZEM0EZsmQ3AusnMqeT5Cl7Efx8HKBGIhNhiqaRimfkDv6rqpqmDk0JRNzQAJTCX3E5ohOyCaYe3oGAwIHQUPgKJtmb2utxAJIiaVMi9R0UVsPCFYHm3orIxp7MV7YAfkJYtCAkFlb1ojAIigjkw05wfNO0REKwDiTMAsp7HkAmAZiow9kJdpB9NhRssgAvkGKRqN6Cqr1qAJiLE+tjKgqwUMVDhUcBsdmIAboyUwwXELRWA8YOmV0Vzl+ilhKVWt037bP38aHZuU01WzCFWuY6cUm9XXvnHP2BEYO5cMmVxRFQB2DqAsomOHBEgGtaOS01hkuTwi70qRlp0jlFKcd1VVAzGcHLNz4BhyRsLDYb+/upiG0QE5JDQIsY51m3TvQCi4i64buu5mt11t1sWF4eKqz+Ww21LJSD48eXK03riqXjRV3SwcYkVQxRiXjYJldGA2TtmX7BDykMy0S/n84tUP3383p5TFvONFHckFZm6bpvHu9OgoQihGkkZS81qSApmaasrlpKljuwoEa3fUZ7kY0nYqLgTn/ah40Y83u72BEYCUIqZqtjvs++0FIMXYFOzm4cQ0DlO/Q7PV+oidPz46ioySUxKLMXr2BqYYYqzVoIL9TUrPzl/mXEpRZlYQKZDz9PKD94a+t5S8hgHItHimKjgmUjHvmYikFER27Hxd5TSJqIoMXe+CDyGUkqd+8CG0Tfv2mw+66fGnuyc/gjPYDlVsfuLtlSKY5NR3gaKP7aIOR+wfVFP12usFq2efXTy56adSyjQlySXlbVdAa/d4exA0MC17FQlV3SxXjh2amaS6qqL3nmc2HVeeDSDzbNoxNQwODDGl8vf/9t9+9w9+P6fp3etejqs3NtEhCWgpQqpI6skhOoSyT/hr//SXQrU4unP/7NHPnZfNb/7gU7l+8hf+s7/80fPh99/93mcf/dA5/8abX82Kq+CfPXtMTK/OnwNYkezRr5YbKeXlZ5+erOoupQ8+eLdp2pSmrt+fnZ61y6MHZ2efPP5RybnrdkzkY+XII6JzFTXq3LWUZFb6ft9U1XK5GUOVp845L5JL0RBqZtYyrY5aX9eQGAjRQKtKdQb8G3OcrYwh+NrWKlnSSFKmNMl4qJsFqqZhSP0AaJpHnAXvoKCmOSN7DMHHmpwDZvbRFECSQwsxNlWNvpYiKc8AVM4pmxS79XELaDEpKglAGI2cd4sjHyswMANjsFC55CGU4F0eehfCHB8JxIWciZSU0AqDIZGlXKQHBE09qKAZ5M7KiOxRheqFX6xAtUwekSwns1JyT+QBiVQNGX1tYJQGAkUkEwUCFLapEAIg2cxxlN4Nw2hq2370zGOattfX/X7PIMRM7IhxuVycHm/u379/587Z6XKVRXdTmZPIKeftzXWvetheUUnBu3XbbFYrBY9ElUP0vmqWiOAJCvms2g+jIhaANOVx7Pf7w36/H4dey9Qgr+o6sAdEZq4Wq3EcFnX11mtfKGbIFLwbhiHmdC/GMQ3jft8JilFYLQH0crd/3TOKdCYvc1HVJgZkh8ip5H5/c31zjT5WzWrM+dnTT0qaQtV459tm0QTfp9yXYkkVZby4yHi9aJo2+KO2rj3eTNonudkdLnf7Z6ptUyNT9EFLFiTzsRYpJROQGIhamgYzFTNiunNyevf0TslvIaALXlQd8YzkdUyMlosR2jAmQlif3a/r+va7GlEBplxySemzD55++mQakw9hTB0zqZmIEjJCWaRyJdLEGUqOWYQFCcE7JqJSSoyRiWf6pY+RRXIpPoQ5eRRiJSKkxQAXbf2lN+9N7z8977auWj97//psHV97eArsitl42EYk6q/WbVl++Vu4fHT55PrlITG7ihyGwOxUtR+nfd/tpomI6hjb5tQQ6hBDCNFxdG4SCd7NCXJCRAYyJWJAUzORWVCsQfUf/f3/7t/8i19mxjylouUHL3dFFm8d1Y5QFUpRpEzOHLEgTql0u+31xdOTB6+PSX7tn/3D73/nX6Sx++CT53W9uHr1xMCmachqq9V63G0fvf317/3WL5vq7CH0ITTt0kSvn374/e98ur253Gw2Fxcvh7EPIXgfQ92ePnqz64eqbZ9+9mGRUlXLOjTHZ69tdwfNzvuIAGBMyDmV5WpDvn757EdAjjiw8+uTe3VV3asFaVF5ryqzgIMdA9ItkdnUSi4ppXGAMoGqqeZpNABC108peHU+lFKccxwqM3ChMWR2fsYuMqPCLAnLkjozAaSxiO12SBSCZ2YTqaLHUJVUcF4yqsK4x6lnEPYVxEZKLiZQimhPCCjFkSJxKUigqslSl7rL0t8AEbkKLAGyiwsfKpE0966894YkWvRwgQgUa2PmUIe6ShBTEgAzVyMyaLLSE0YDIhfBBWZC9hgqyh5yD5oQnRmZTjSOEALECIVBEuTODeNUeecJFp7ONid050SQZgRNMWyqql4snI9oEpmGnPeTANA0JSm5G4bd1RWVZIjDoU/TFIkqz7nbOkBQa2O13iz3fe9CrE/vV3cejKVQqBSK845is2K/Ojo2wFxyIGxCCHh7NxaVTajEzEw9opn0w9CnnJF23SglKzrzbrVYFc2WJlc1L4uWLFCGYUpILqNXTTOoP1F98mDjvY+OD92e8U0mi/WirSsHYOQ8ASJE5xZtkxV3qcxqvZtcLnMh4GIQm8WG/DD0GaAu2QiJMKWpDnHsuu3QB+9NJTAunA+hqaqqrpuqabx3s0wNAcaccpGUctd1w9jJ1InacrEC5uBjmRFOhEyemJIURN9S+uj805ILMU25eO/HcZimJFlFxcjU8zJrARB0qlaKQsB5qkuOGQnAfPQzSZOIYl2FoiVndixFgA0ARTSEAGanx5svvSH7956OOXyR1/zDm/3J0eY4MlVaJjfctOVQ1ytbPjjcpOtBOFSRnZnmkopoMiDv75yc5pSY3aqtGWeKOyJhxejQmF3ORUDZ11kBAZIUADGwqUgR9d5try5/6W/8jR+9833nME1JpKiKAfzw5b6YvLluwRTBVKFMyTlmZBFJeWLnp93L7/2rf/Dkkx/5EJyPIrnvrr/09W/92f/0z/21/8t/nabp6sWz5XL18ukHAFBVzTgNdV2dHN/JabLcvf/x46vri6PNcS4l58l7H52v6ubo5F5c36mXL/o0laJFStMsV6uT9ck9810eYgifzruV5fpOIDazUC+rZu3clYq4UC+O71F9dHF9dbKst6/OBYB9MDMpeY7+z8VJNLJSmJiblSGAgVcVyQTgHM8lpIAUHCFILiqippJKgpwADYZsacjDDkStJAoRASm26CuVPKYEoN47dkQKCqil4GzpZifkSsmWJlTEEFEtpQImiIimkHvIA0iaadeOvUq2OdCbJ3SemrXVGyEWY9FkYlgGBHPOVyePVMUvm357yKUotobOyMgEiUALqEFcmYpjRh+Q0catjTecowGCDCgZYYC4wGZNcQHeWU4mgxmhc+7l+XmeBjOrYmiaxdHJnbt3z1br9Wa5AICrq8uS85SzEO8+eQLs+mEIjoNzF5eXu8NepQTCzclpu1yuV0suidlV602IVUFf1dWqictpHKQMWWgaTTOzVaEJwTnHxYiIc1E0rqoIZpYFsrFDAFfKOMPHqhAYafJusWxn2LQDzSVPWVMupXDvuBhQrOLCFdGm0UBUB48AbfSqWlQPUx7HlK2sAk7Bn1/fpKstykQlA7sQ/HFTbbsDsYPQ+HZ51C4JVDgQsY/VKjgHNk4JzG66/bTfqmZmDibpcHOYspkStc75drk0pKw2ZM3SlTTeXbXMnIoSYsmpTHldVScna7XVNKVSigAOabq5vrq4eKkGsW42i/Z4vQ6xiYH3zz7t9ttYx0Es5SHnLKI6X19VwWAAqapQEyWzsVifZNGii87MCLFZtpJLSXkWfs57zFgFZoL5w6PqQ4UE7F2eEhHdv3t6dn752cXzH7Gvb6L8zuM//x+tgWwZq5i7anPsj852nX56fTMmISbQwoieCQEcOyRUVYfBMY5DN01JDNC5RbtgcM6TR0SmLJjTmKYxl5SKhbqtfSglA2Hq+l/7V7/64fvvi5QiUkqe9RGzIfj9l92U7Y1NBaZqqGYpK5Kcntx7te9Sml4+f0Lkf+KP/OKXv/ljHz99uVwuL598ePHi4+/+xq8ichUb7935Z+/v95chhJLzen28XKyZHKO8//7390P3xqM31+3COIxj33X7onJ5eeHCR9OYJB26w0FUc8pVEx88ev3NL7190DBcv7h59Xy/v4IiZ4/eXFbN6cNH2z4fS7/fXo3Mm9O7R/cfHd+7d/7qR8Ssw8EQTXMWA1Uo4zglQOJYhaoFJGSHzM4xwBwU86pmZkSKqmoiGQnBI0ZPiE7VyYhaJq4DNdVUVYyqhuSr+YBj5EwKex+rCp035DElwkSABgpaAD23xzijfszQMQCqZgIy0+AdlKxSrCS5HbkBpcnyBCAIAOSNuIz7nNLnjTc0UzRUUw2R6xaJ6wUZWBpGgsmQ2fH8vxWdA80YHEi2rIoejIBrK4IIqIhcu1Dz+lQUc9+h9AhKqjBdgIJ7++E9ZL9cLtqqqquQgTBEBbwcRVWxOQKVPPX9fn91cZ5TMbOqaTfr1erk7PjeQ1IpUjyqcWDN3cXBBSyISI6btvg4eG+hhpyWTFkhjUXGfL19uTvsneOSi6SplLys4luvv6ZGBIg2n49h6tOYBldXpa4WlUP23TCFGBjB2CVyXZE+la7rhZ2ask1VRU1dIVLJaTdMU1HY93kaD/stGIYQvHfGy9VRu9ocd8MYGBfRD2nqx3SnDW+jXuz7i24ig34YUQtwQqI8djuwbNyN451Fe1rHa21HsUWo/Ab7XNZIy7ZFFxQAAZMUnjKaTP3h/Obw4qVNuSRRNRAVAHIxVlUTvR+mIU0DAx4tWhGtY+V8iFXd1JHAagcB5PzyWYjVdj9MQ5rGUUpOqQCic2Tmpykt25AKjsJr1ePXvphimG4+bpHAJKckpbBzYMpzxxiBAFXEBZ/HqUjx3hPP33k0G1eqiG8+OivpyafpstOlf3z1Mx8df/OnvlWji0ZhtYb2aFLvHNUks7jTsWMKJlpK6odhSmnou5ub6yK6WG7qdrFsIgFsu/5VyaZK7GIIhAiKzKFiQqJp9h4a/sH339lvt6fHRx9+fImzP2u2882AIYTH14dDSl84ah2JGaiCFL2etmbKTHW7/Nk//ZcChd2rDx/dO/nB7/3e0N2EevVbv/btR6+9dbyM11eXh8PW+5Bzruv63mtfWiyPr55/+MknHyWRo9WmbVdVVV9ebwm5lKJm+8O+2b3SokfrlZYcfBiGw+XFizYuPefiFpb7nKeSEwKoZh9jbCpOCqi5JAWNwZnp3bv36ENg76jdoJRYNw5McmZr44ocz15ImqcMKkJmJomZjD15BDNvkxSbxMh7dr4UmaYhDwfNk+XJxg5KT7ExjugcKmh3IOfgc62XI0zM3nsOVUoZyCHemnaQ1FlBHzk4RAQEETFAMCXvCdF8YGKOnsl2l1cldaAC8yW9TAA8r0iBeK6fG7uZLEnemauykWUtqZALzXIFKgBYbFYHmeURJRuiWUYdbBhAherVvM0wawjNIMvFx6aG5I0rAJTccR6Bonvy7FmaxllJWcWoiCFWVb04Oj1pYojR3T09wdXJTdsuj45R1RE550IVxCx33Z1VyGokBdIAaUA2zv3ZnQdbgd04dLvt0B9GwVwE0XJO3f4wD3FExDnWPB2vN+hCr/jB84sYY+t8reKRPNOijuyW4Gno+lfn1251JLFGSdGEvDNyuUh0fHz/bkHOZklExMYpiUHK+fLiFYE1ISyjj6tV8H61WoQQi1maRjBbVD7WTWDHoYq+H0RHo+QqZTnsd+1ydeg6Yl4u156clWxgJ0fHQ5JX4tr1WcNewaac0WvtGbx3SAaW0iSlDFOfcgbTxWpT+yAqV10vc4sYnUPbLBeOHWPbRG9GIQRmVwWvpo7QO+cQJtGSOgTtxymXkqZRRdKUTY09ajEAY6a6ijXg/noPi+Ozb3zt+UWXh5tp2i2WNQPllLim+UHG7KZp8BUTsxZxzt1Kp5AALQ3DnI30sXr94d1y2G9G+eHFrn+9/aXf+f7X3n7QPnjw8iq/dreJ7erZ9c0wiotVW1ee3JjLrktSSpEyFZGivl6eNitAqEIkoqyS0oQGqZRSxHsAQkJSs5JLkeK8b+rm8Tvf/9F7Pzp//qImG8e+5OK9+1xHdXsyAzBCfHVIfdLXN/XCo6qOBV5sX4gaEr16/vi93/zHZ2dn3/3Ob1XBL9Z3Tk7vvP21b427m+jw8Yfv7rvDMPab9ZFzoV1szu49HLub5+dPi2pgx0jbm2tclnHYA82OES0ldYetZKmcpGHH7FWl5KLIIpBLktQXLcM0kMHN1avWx5fP8PLy4vrlp0N/MNNpHK/Onx9v1oslOnLieNXWggRiaOaI5rWdRyKElJOo5lJMBCShCIUKERUIZts0USoGmNL+SqeDjAcysJJmUw5OBT2zzC8AZQIkQgNDSoaqKFMqhw6cD7FBZrLMCCWXLEWniQg51FKSGKCI824ZYzHKkhXQYYDgw1HIY29mt90SSURILmYFlQwi+DnOTgwUGLKy56wOmBBZxJgYAcvhQqY9IcLuKRLirDpzEdkRe+13xG4uIVjpFMQoAHvgCK6Z8yvmKyvJffXH/0gpaeh6NFmu14FvBe4qJaAtPbrUpZxrQu/x8vKmN6ibpjtId+hEyhXjqo7DlFqC/mbLCNvr68uuW917zZZH0Tuo6g1xyQm0XF++cpHvnBy/OIyb9SbWTZ8KzNJdgMO+x24MPj46Pa3QnKPFyXEQEFU62jSLtpATdgpQANmRdw7FxGAEGlPOaez6/rDboeYqxlVb39ssYtVWVeWdG3MmJGW+2G4lT7t+ErNlHRdqDtDKkIrU3t85PuZYXe4PDsE5bwDdlJNBVVWEJArOs4kaYFEJwTHisq4OQ0+mJDkbOkKvkrvrE1N/dIQATRXBbEy5ib6pmxg9I5no8bIxoizmEJHRgMQsi4wZB5EXu5vDzcWu688a7PfbcRxLmdEXikSOoJRCiGDgHM9ehXrZXPQH/9E77dmbN6v7w1VXZyNnIpKnpISA6EMEgDSOoWpUpeTMziFiTskHDwDMzjkfYzha1PnBaXu9dxX9hisvDP7uP/vuf/4Xf/Fgfrcfl2d0ud0JcJRcoYL3RNxUYSpMxTWNL1oAwDmeHSVEWBPOmhtYLhFx7pZJkVksZmAhVjdX19/+9W+ff/ZJ8H7y/tB1AEBE+jlJ4vZpZjab+w65vHuxe7BaomRFOjo+vri6nlJCxDTsDlvwntWw5LHfbp+89167PL66fhVjLCUVKX3f3X/45o/9kT/26Y/e+dG730tpJLAiOeWR2am1Ivk2XDxjdVIOvgzjRIQ+eCI3DIf99qpxpmEhucs5IaCoDEOfjSi07LqikFIiBGQei7z54M7R6xuNATj0UxaVlFPJGZHyNFi51Uiz8z4GJiQ0ZFdyxjwwuxArqhrnAzOLWlaoqnjbSwdD/fyZbzoDl5AZYXZeAwDN60FTQTBvOsenBdEQgJCYTMxyynksWcCQnQNy5v2QhRiYufLeAKYxmQHH2kz1NikNTLeaEAR3GzqbAQQla0oGqglmsYGBls/tRVgydBeaD6AFZQRiimtABiRjh8jzn5BNoYwWN+oCmhrXMy2DyIurjbN788EdFSmih34aStlvt9vr62kapmlkohi9gcUQ1qtlFWMIfuxGK6kKcdE0CBiqUHu/LAKKq9WdYb8P67sWfPY+CxiRiwHYT0rRw/pYH96/D76CY+wPh8OQEG2cRnIhxmpRLwAU2W9Fi3OqzEkCMcfoQU6P1lngcrd1aMvF6ubQ92kKde28l1LylFCVDc42ayYeh8Nuu18d3xGz691uHX1OkyEC+9pz0y43q1VRzKoI0k+T91GIBsarqZSxT0WKyGlLTCyiU0qsEmJdxVAH109ZzZhw6g+Vj1eH/fmrC4/UVkEJHblu7F+9eF6mwcfKxbZZrJGg73cyDgCA7KrgPLsqxhBr8m7ZLs+ONpsm7rruqutUrJ8Vub45PdvE/ZMX+900pqEfDYCZbB4uAEjOzKwixKiiTe33Y37y0UdfaBfV8jhNdw7D+aL2JakWCZ7JsTfvmMEgjWMpCRFExTs/L9CcY/beeWdSqKru3zvL03h0trn6wcv3XqRXdv7+D3/3jW/+8Rh9NitZ+jSOzu3H3LaNIdTMYFB7z1iMwBCZybPT28ijesasZoDMLACgFhwggCIi8asXL37zN7/Lc4N16p0FLUXNTE1EiW4xxLcdL4CZpQWGnfLN7nB0dLTg4Jhd07RV/eTps08/e7ZYLphIjQpY1hybBrZw/vxxLnn+JC1Xx5999N7jj95BU0fcpRERp5Q8T/1wsDmCimimYDoX9ZF92yz7lOuqSWm4unwq45Wgy2Xc72+mcQSw/fby/LMPoYyHvuv22ymNhDAc9ttXz7cnD19/48d2Q5e2l7VjI45Mra/RTILzPjBzykWk5Jx1mvI4iOQ0jUhMzoWiy8WqCoZgsw/X2DH7Wd44n75V1dSAEGcyrCkhIii72R+sYFSKEBCaKpACMDPYDFAzF0KE1ko2VQBjImQENVUUFUABANOihkiMpp4DMqsUQy4iSASA5CKCAaKBAXuQwiqINrcRyHRWUhqYeaD1a3p4Aakj18Ks3y0TajHJSEbNKdUrkLHwXeAKNQPMiglD9lASphEA3A9++F4RGadsZs67NI55HEpJ7Hy7XB+dnCKj5CQq+6Q2DTlnG8zhZMRV00bFXRkXbZP7HpkxNEJOmYaSVDOQ09QF71RKEhSBNEkNsqkqKZXl1EZuT49uMphzznkfghqAKYWA7PdFLPWQ0jISg/Vd3+fSjVM3fkJVnRCR/d3jNbMrScUKse9zRswuROeq0ei0rsNisXDW8KIAX02lCd7ArJhDrNk7hFTy3I+KxJFBgMg5xyxSGAl99CmLSkEa+mHX91PWrJZzykUAu5RGc4EcF+fMAIhc3d594wtsttteqyE7FzwvqzsBIUvZjamO0TObFnSuaRZ10/ZiNOZF8ONA131valXlmqqpPOxeXI/DpAB2+11ozrmSCyEas5rNC/VZALhexPOr7vlHH3zhp37+Zbqb8z6XgRFFtQgxyNj3IYZZ7DCfcECknwZ2QUpORE1dEQLVLTIvl4tVWxfEP/nTbz3959+/3Mrf+uUf/CVd/dRf/Z9szT2/2grhZnMcQhzE2qpWBJC0O3RDSjlPWgQBQuA2xvXmGJw3s8gISIxoRKkIAjFY7fmTp+e/9Ev/cNjesMl62Z4ftn0aEQwRxHQ+dHx+x7RbGQrM1Hvsh369XoHqi5fnqrperbzzrz96cP7i1dXVpXOOaLvfhaef/khFionOGkWk1Xrz8vnHl6/OEbEKnghmc72ImGnKxTOORZm4qCISkVtv7rTrs7tHm5dX12qyu3mZS1IJ4JyJ5DSJFgBUKfv9ZWBJCjkl1QKEY3/YXb746L3xy9/6SjfmV08/dUSxadvNsQuNgSK6YhCAwMcpSxZTQyFP7H1cAJALnpC6KfdDLzkRIxiEqkEfnI+AlHMhRL41YyB7VlMAYxDPvqiqSBU8k08AY7cdDvu2adrFMpmiimMGQJXkCH0VZt0PEc+5Vr39m4FSCiI6ZnZehUTNAUTnxDS4Wg1FMiCZFNNC6NATOIrseFZjGmgZJWc1I8cUKrAFt6cwHqx0hABaQMVAGTI3G+TahQrDWorKNAAENcCSZsY3wFwETm447LtxdC60wZmkRV2NYIKVGUiR8+fPZ5aLC26W8IpBiFVRrdtVyUl31+vlERjFxQLYg/fMrGCU0zh0QOh8BSpCLIjiKwEqgqVoXCwMEJ3HOh6zKyJ9343DGIOPziPSYRz6rkOzYHp1vRuuL3LfgXOT2pBTYa6P79BiMx2y98gEgX0dqyo475ypKjECZimBaZtMK8+EgcFrTob7rm+8K2X3ohuymRSZ0qgKTDCHKRCgjiE6but6FUJWTMjE5JnW7ACgqKQiRQrjhtk7MCAUs+CcZwJAEaFHDz1B14/XN1dd16UQwPmq8T5EVSPwVR0DQQ0yjtO21z1x9OHRSShqqqKWqN8fbi6LzTBXYGRjEBHCWTJ/+0NV5vtD5XnVhOurm4tPP3SLu9t9dlGaJuQiAIbozcSVYio4T/tV2DkFRLOSRgDsykS4ClWVc64rF2KlqRyfHP/sj739z3/9HVN97/c+HP/KlEbzMa7a1aKpmVlUCABAV3U8amoxUBVR9UwKpgKVZ/vcV5gkFxHvKBVTUwO83B/+7a/+6rS9oTyUkolw0dbzrpCJENDM/r0v93ZkdrsHAGAmAhinMeVMiOMwvPXaF8acDV6ZmaqCFQJfwICAFFWNHC2a9nDYpqGLjoeUD2NZVJUjVDU1nf96m3ZJkyDQods6JGZu2sZ5Xh9v9ofu6Pi0P1zXzQqZgObhDM77PQMMoSbi+ewMgMw+xoqcv7i82vkvt4++kpMO+5tx6KlqmrhQVUYsBlKyc8FVcY7sG5iUomZoxrOMzAWCSlNiZlM1Iod8Ky5xbGBaCqMhQi5FVAk5FektmRQw3W+TTIPmUaYBFPqr8+hYAXXqiQyJTQuZOR9CXZeS2VcutqFZcGyMHBE1nhK6qVhwbIxmRhhUlUCrGBAJIQx9Nw6D98GzgYqaQJ5ymkqeiB2H6BeLJGZazBTNTAuECn0wYkLFMmKZHGZCQA+gQlWkEOgmyzDOJmBgZxghBIKMuXefPXsuAk3b7BBzGp333oUQIxFzG9dNS8Q+eHJkObPzoWkQCBCKaB7IO66bBpGMSZ2byyVIhOx9uxbAImVKoxqw41BVyFyFColVhcCI4Pr6OudpGMeUVcxUoW7bWZzBxABqpTC69t5r66qKdUPOa5lMc103nfFkOKbkET0iaErTsNvnPKUhZxFBpOBDqGsfg0N2oF6mF7vu5cWF5slM0cXV0bGZ5iIu1M4HyLkv2A+dlky3MxqM0QPhZrXZrNeb1YoRPaCPwcyr2TClyNTEWNRExUqeRMeiY8r77c1ue+N8aNqFjyF4v3KuraIj1lKK5H6aPn72rO92JU3eeQQk7xixit75ivaf7bc3RVRV1ZRuLzs2U1xUjQhVZ84iGIIZbNqqH/NnH3wQxx/GIvnuuoQZ2XOrkRdVR94MiGnOehOh5GRgRIxIJSXLkxY/ZXPeWypDtp/61td/9/sfuEN58Gz89t/658f/yf/o/oMHoELEZrBaLKJHBFBDIqiYHMJUSlF0RAjgyRBRAAGdd0xFwcyzqJr3br991Vx+2owXB4hFxJIQcT9O/TgyMyLNX6Aq3J7FPpfezLw5kTJlTDkzkXOuSOnHjtjnPCc5jImY2Dnz3hFyyRkJU5qGYVg0zRwqHqZJRKsY+2FgpBCq1fLo7PT08uKibpppODCSqYxDx2GR1Jqmcs3rr148Wx/dXwbqi7FDvrpwLPOTd7k+PTo54WGyC6Obq7pdL1ZHrq4vzy8uhvygWR2vvJ6coAGFMNfCb3PCImWack7OOwCQXEw1VBEMQBRptsqCc8zMYASIKU0gk3NepRQRMBhyEjUtyUwgT5ozmM0iHiS2WWPOAR0BhCxiZVIxGTqaFRehSSWNQwZUwsHoBgHJM8UFxSV7H5vWhYrRhmkCmz1QoKqDCCJqzv1uW6SQGzVNSAgy6djBeABL5BuOTdycGHozm3OCJhOKaO7IudgsgMgvN8ZRczJNmItsr9B5dg5l0N0Lk4QIiGShVWSz4r709psx1otFE70bp5Tma6yPwI6IUhqlWN22TLg6jYwoRMOYCNEjxkVYNi2qpTy5WBXnCzISiiqZTTlv+0nJ7pzdAdMh5cP+ZkrThNwPkwJKKXns9vu9qKHzVbtsl0dNvajrxnuOIYTgCSBLCTN6iQkBi6kQMbU3peSSx1TGaZyHGmRiKnXdbNpFmzM6DwZTKV3XX19fSSnzrJHZoQ9TMeddXdepFGL2dbto1967EHxdRYfg0YhJDYmZPncXO56BKgZgNVFkpwi1cx5VDHLORiQKhhw9R+dWdXPv7CyGaGDzyVxEFbCAGdGiWW7Wq7TZIAIyZzE2USRC8uxkvPn0s9/rhmma8uHQzWkyFStFReZzFs4fZmY2lduCG9OmDYfDSGhI1N/0VHlsfEQGAETSedFFRGizctE5RwBFCqLNV8+p79oqIKOpGtBU9Liq//TPff03/tU7Ofj//p/9+s+dLF7/2o83wVXtclLohn4cLYSQBczMobWVr3wIjFltHnsTkYiNaZxSJjVkmoolKc3+5slv/Vqd9q/58elhf6nVkMrl1dWhPwR2olpmnSDA/AV+Ltz8d+e0IqJq8+2z5KImP3jvB+t2sWpqRPLBBxdWq816czRNk3e83908ef4kpQQAqkbeOcPoQyr5ZHNUSgEEM4nRE/sYnLKfQ3OERBwevP7W0b1HNYeLbnQ+1quT9bK1fqAOnK9KzvNqL03J+di6FUG5ePnMh/rs/ptJZJg++uzTz17/aTfmnIuYpFob75iYRdRK9swQvYthZlswOVMBg5zGEGsphRyzczV7xhkdYuoYAMkRiud5wghIOZkpOw8uQNQ5dUEuAgGxB0kGQOzmWQOYaCmaR0kjqIJmU0MkirFerJgduTDbmo38/MxlBTHzPoLpOE2eaUw5TROCSs4ghZwHX5GriBklZWADsKmTnBWmcvmKfEREco6IZzZRiEvnfTKylHM+IO6gJDDh2MoEyKyWEMTqjY0HnXZoAiJgigCu2RxZzmiaSwnOmSgTh+iT2bC7mrKMWXY31+ujo2nsApOa7ftxGgfLuTJxhui9omLdivPd9ppBRTWEiCG2qyNyYTtdLptqGqYW4dG9s6sEizUUtTwOppuzR0ENmF0xa+u6aRYiJTqqYgBEEQVTAmMEKGLMkiYxy4pSFE09WVw0VaxjjERERAq3EHJHBApIkHK+vLwc+p4Qh7FfLldn9+4ZICN4x1WI7AN7VzvnEQS5qHiee0Y0nwqYGAhADUwDIRCVklgLEU2lMOIkoGDGPCs14+ewRiZSQQXx7D0hIhKS3R6uNMmsKXWiyoQI5n00uF1zby8+6w43qjYOo6ipmJoJgKoSkarMfhlE8I5UMZdCRKrW1jF6HsZ8OBQRSNspVDGLIiL7MD+ZEQxM2TlVnVtEoDhfAlXKKGXs3DKQAWRByyWn8WS9Oi/93+nMUPp/+et/9QtfSfEoFkk5MzpAENE2OCKHxMVslxSRFKwUSaUggpVyc3O1vbkuOUkp5MMmcP74ve2nH8jY1UQPg6b9/vluKpKrECrPU5GpGxWsiMy+tH/vUGa3zzUDNb29bpsCACHuusOU/Hp9tN6cOHLtYrVZtZcXr56/eHpx8SqLzIvREKr1et33XWV6tb02s+BDLklyRjQM8fU339r1ZTjsrq9fGcL2+uLV08dXl1c1ydNnL6dpuvPgwdnp2QmH7fkn+6vLPZFIOb378OjoQbNc6kTOn9XNMjaL5d2HRcX/6PvXF9cheGOOBmiRCT0zAjrmCUFKAQTnAswvTSRE9sR1DFWMZjamVEoZRQkJUYkZgHDWx3pmAFAxtyAA1SNDNFEzQ5qZ2lZyIvKKRIR0S7WecWnoeQ1mjn1gUBFRM1UkZCZEAjMwZQZRMPLMKKKiwGQKWBQcO4qIzGCWSiEEycm0SBrZM4XK2ImroYxA3tBMCoCJ5JInIuRmqexLmmTYokxoCXztqjWSk5LBgMDYB5kGFUWO1twxUwQCE5Xs3v3R42a5kJR9DGbYNDUhjsOL2js0OUx53ulOr16FKqZxGIehrioCw5xC9MaBmBQ4GYlAXG6Gwy6XBA67XTcW7Yb+4tUFEk3DsFgsXnvrrdff/NKijlMqulwgggE5do5ZtETn6hCvt3sG8EjFQM0cOzNz7PZTT5NFtoboop9EJZVsau1yKTqLmZGREEzNEA1MeTZtONc8fDjz4UytmJlJZI8zFofIAJkITE1FVZbezQwBk1wHDwCIWRUZASTv9l1SOL/ZgkrrCc2Wy42SQyJmDs4TMyKhmZbs0LN3kaiYGThRCQ4ZIAkWNMcOVJoqmFnOOXp0HpGcqGkedy8+6buh6/qu70tRABORMk9MEch5UEVUhzjfOlXnk5oxETnvGgKDbp/SMO1vhuqsAfTzcUbN0JCdY6JSyvzah5mfh4QKPrhpHKTUSakgOcPc9482i5//0t1ff3zFr1cvuosqHUa+n1RO24ZDzIZdypMg5NRP0zBOIkLIBUzMTLXkHJgX7SLWzTROCob9Ln/w/ZtPPyjDnrSAZcdwr9ZhkpcUh1xUhZDmXa3MIHxARFIT/PyCOa81/3CcNgvu5o9uKuXq5moch7au97vLZ0/k6ua6H4b5iP2Hm4Tlcp2mkTgQYs6yWiwO3YGIEJk51u3q+YvH6EM39LMBJMYYq83p0XI6XIPJy+ePcRpO3/pCbFsfg/OeGTcnd07uPbr32p2X1730FGMVq7hcLV1TBx9E1DMCORGdUp7jE6YGzGImagagNuvgbB6KJVUEwJwYrQ5emYsKIqoaoiGhAkgphCQAIiIimhOwN1AiBgCG2yC08xEAXQhIICmzY7vNqhpoMQMxmQp4ZkZl70zVEEVVAFQMckFE74kc1TEMu21/+Sp1O7Ai02DkwuoOuuDrhfNuyiAA5r2xs5yROSzXrIvovDEzCCIVZFAB50TA0pByhwaEDusNNxsDgzyAAhGoSsrgq4VnyH1n3TWkgxEiR9Dizh4+8qEKnh1T3SxSTiVPR3fOxmEglQgaqqo7DAZICHW7OHEuON+2DeYcvUdiY06qh5xTTs55rldE7ENYIZY0teyOH30xODbTxWLpHDvvEdF7zmNBJM8wHG5205izFNEYQlU3m5OTjJhSnmdzZorsqlihSl033vu7i0IAOvPnZuYJUynFCBEgem+mUuanlqkVRAzsAqGqVI6ZGADAcMw5iVQhACgTErtgxgSilpMES6+2l9uxeOeQaEjl6ub6ars/Pj1j5984WaHKxXb7yePHN9u95qmuau9d3bRVUx8tV0ftoq68IhqCJ1RDM2K0yAwEDXksBTzXFaecG3crmiwiwcebq6e768sp5+7Q5yJgoCozosfMippDnF+rYoqiJsZIZf4XUD2S8z6EDJHSfupfXG1N3cMNTLmweEL0jjwTURV9zkjBl5S0FJViRA4cGKRxEItAToqMQ3/v7PS/+t//lf/y//Tfvtf19ytshk8vd3de9Yc09MG706PNWIRdYLBUBECbunGAqoJIPrQK6BjqunbEIXoY++//i29fPv6RpRGkqGZVIYDW+y8e4aqXjzu7OhQwCZ6zlNuZP96CPv8wbjb/899Ln/17oQ0EEdl3h313+MOfYuf+/f9wyunuwzdUNJUcw5WasK+LaknT8/OnWWDcr3bXL7KJqc6Tqf6wB3DdXvO0Ny3DYT9Wq+7y8vr6Yhi6cexV5PriVXCLOtJ224+78+6wRcfPP/no6P7DJPnxZ6/2N9fN5mQUMeSxpFGUANjMAMk5AFARAAihAhMDKKWUnMfJQO12jEs4O8glZ8dujmoFB1UMRUoRf9C5LmHzK07KXPQEpFtyFYoREbs58oWICILztQaRjFAVixQTiSF670ilAFIIEdQQgHj8/5P1J7G2tdt5HjaKr5jFKnZ1yr+6NQuREklVtCRSgmNFkQRZliIrSSOBgiSAe3YjCAIjSTdA0jXScsduOIocBFYKO04URVIo01KsSOTlveSt+N+/OP+pdrnWmnN+1RgjjXn+S1JunI2zsYC9gYW1vzm+d7zv8845He/T6diAKOzAjT5EdkHJlSZVjJzv42CtVTGMg4FCa/2m90zFrCZRBSRjIkfATErRhSvER2BgpgCAoBa3CGYtwfrHXCqTxn4jfW/HN6AN/cgxuifPHpdSWxMVqSW1lACk1rrpB/YBiLwP+yu3Wp8lp5oX50OZl8jkfUDvpnkmB5tIzrl+6FJ2SDQXCV2MZwMAmItZjRA2Xe955RZxFuFNbE2qtkPKtSoC+37ALvq+I0RAGhzFEGUt6SJq/p0Bb865SfMhOOQ1mqOq0ppzbqVrogkADjEgWBML5Lz3BGBgDR0RizTP6MgNgRltvWKpSFWcW1lEjqncT8vN7U3LOfZjjBFBjZxxfP7ebrfZ5FrnIpvon15cfv2Dj6poLtlUvI9mOvQ9EnfOmQm9y2WBR1xHwVkszfNxSQ/H6fru5mq/A+eZWVpbUhKVzsczfbssy+EwqxkT1dII0RCJKOcEhmAQ/BpMQUMCtnd6GdHqMAIj5xxsSQHK/fLw+g5VLj+4CMHV2hCAwGIIPnqKQUUUUQCYHTAhoEoDkRA8GeZSGGxelstvfvXP/uI3fvB//ifhg3F8/MGH+/NDGlboH5rsiYE9Em2IHfEQQxGtUk3VsUN20lpae6nuH17++t9//f3val7WtotaZV1EqBkh7gM8LbowHKsRQHRuXcWuLtl3BY34brD6/Y4zNcPfN6z9/pfWy9TvvQRoaKXmt9fXS6lVW1Mt81xbFZEmYseHcdh4IrBmIgDYVMjsdHxAQscirYDp3fWr3eZyyAXAMXs1MMCUM/kIFHzPUk+ADMCOgg/x4smzz35wNx0eGkcjR8TSzDmEVYdFYMfaBBFErImAKjEQUfBeDQ1UaiUkYDYwrQ3JmfMGpqK1lWoWvfOex7FvrTV9pxwYrJ8KECnSGiMhYSSFhxMMG0AiEWI0twbLzBExw2rzV1U0dUQMZCZNGjsPVRSgv3h6+fwjYDfPi9RiLbVSipDVYkQiWRcgk2KI65uvemqz5YkRnHP92aWiU9UlLc73PkRC0FocgbkA0lrOJorMxh6UAEykqZiUewMz3oJzII0KuOPhOAS3pAXA5brcvf6izlOupaYkqqHvY9d34whIecnWqoFGFy7PzrZDJ6VaiNX5U6qe4WwccnOGNqJddNx3FPvYTEXVyAUfKlJr0tQUwDN1jBkhNXzvvQ+JEA2bigIQgCkQ2hDC0qRJRaYEUGsFxNok+mCA07wMXQ+guZTWJITQr0txMAZTs2pQSg2es5oArQbXpTTFRoAOWnBNpQFQMUBVh2DEBrYJgV0c++Gjx1fBcUc4VSHCy6GbqubW9n1YVXwzbYbs2FTUOodUzBwYAWS1qkKmDi03bUCgpgj303I/nVTs7uEBRDl0GZjVjsspuKDMuZRox+n45nRaRHT9fBORmdbaRARhBYZaLVnUHL1TKJjZuXWvp+S9mUXyThQMVFXmdvcwmw9Pnl/0fXQEQCSqaEBMDokAiB0TNWmMykxKNGx2B62oFRE+//zl9b//d37r298JbH/m5z48u/yg+v6sH5pAbZWZnWNSXbeOqlBb9cHH0K8bhlLXJICHlj//r/7Rm+/9FpSEUlSKqoqtb6etFZapqUe4CGCKyTA4ePdWAKjaO8Mw/IFj6r/+7R84yH5PXVvfTFuJXa3Wm5vXIG1OqTU10xXspWa5lOPhHhE9WiuFHddcEazULK1O80wIgHy4e/O5gdZUAZY05ZwM7HB/83D3hmRp3Of52EQPh9vD8SEez3Iq3bifD4s7g5orszjvCUxVkRgBTJTQkL7cxBCqrqYsAzRQdc6FGHXt7CNenzprfSUaNbVymg3ReWb0Wpfl7tV8uOU4sg+RcbPdUNwYIhPVWs130KRpQWKpBijeB1HVmhGE2O+GQARLKmDmYxA1BAKDmhcEbK0+PNyBSF0mMy3zSfLJua7lhVChVWmFY+QwADpi0po0L2it64b++UdFKLeMhghccmJrqhUIVQhaQx+6Yawp1byg80CIoABk1hpFXJMK7MA1NHEs5fXbh5SrD50BdpszcLFOh+U0b7puvz8fhj50nfmoajF2XT92zIN3jhkAKITFtGOyJue7AdkdU12LvJpzjn3vXZOWGwBi57ioNAFFEG0b78bOH7Pk1tSI1geuiJllUwSqxqk2VIsxNpNUpItekeZcHLsq9jDNS04p1+iDHqfTaQrBsyMwYB824zB0/f3h6H0AaIgQogfRIq2J5lzUzDsXvQPiKgIATBqYTjUDUiBCM2IqqpvIpcHdnBjBRGqx3nkgWqpFQjJtpmJ0qpURBcyZriJsMmhiQH5pMi+J2XEIO9xG5vefPFklDzJzq2bfBSIoVdLdZz/44jv3D8fWVr87MlOt0mqrTZiQEU0VTRkIAU0FaZ1IEWBNNQEh1QZq4D332+Hk6/FU59upAH3to4swBgbzzgORiQIisQvM2hoTqSlSoG7rxr1LR1WcT/c/fvny//3/+0E1+8azy1/6o39Mz9+PSyUyimxC1UCMgJlRHRIg5qJgVlVE2uA9R18FoOYv/uk/eP3dfyFlRimg695JqmoRVVNVq2KlaVXzCBtWbJDMCM3eGc1MFPjL8+hfOrD+ZdXsD776k1lund0UQM1Ox/shRmltJVSbNVp9Wwi5pCXNvu9ryc45SAaIVRoi+jCeXVzd3R9Vb3OapsO1G7Y1zbUVU1kQDzdfQB6y2ny8n6YHJPjkR99xrA931/e31w83N0++9VNYqpmwCRObd6Jrgl6JAMxCCGBqost0AlDvPSPE2Bn79SQSkaZK3pmoM/FEzZjAiAne1YhUQ8K4iTvqhsEBEGij6NgTmnNETK0pgZJxYBIDMBBphEAxlCpSy8NJPBMhN2lSCiI54iWlVrLVxLg+I1BUSVs/brGP8/Fo2mqZ0VRrUalS2lqXR74L+20fAzlvYasi0Koxq6ktD4ebIxKxcyjF+UBhwNADALEHJEQFJWCEdcozQ2ux3yCaqrqz/X672c1iZqAizEzswMDHiKbDbidNHBMgppSDw23fsUqHREyKuCgwYURA8EWNQKJjEXTRBaLc2nHJfXBJQFLyzBS6LhIBHJZ22/IYw8AGQFUBDRShiKgoMTFYK7W2lkudlymEkIq21t6FStJ8FniZT/Ph1Lg7PDyUkkXBwFrO49ifnV9IiAskacoOANTUWmoAaIZNbRgG711t1lohMwJsIp6dAU6pOEbyDoyWOUVmi5hrW6r0wYHqSesR25KzW1ksRkygyMlIzZBJRJ1BCFyaIhNiMSDm1dEB59uNKhiCa0ZMVbSZIUKTVufUalpuXrz47Ivb22M/xPVW3qSVUphJTdFARM3MhXe5E4BVFQey9U/0S4fCuiUgdJ4HM6faDkt6lV6W/OG3noddz0wKxohgxs4BYVMLPqhp6CK4MfSbfhAQ+a1PXvzT3/xeFd1G/gt/9o+e/eFfuXmYVMQcE9smMLdGBLnp6XQE17F3ZDpGTyrs3FKbEGteXv2zf/j2t37DcmItZk21FdEiWlVLUzWook0sixTRqoaGXtLTD77aXz79L3/914mIEFVNQOndZfMPzF+rcKa/by77yYlGv6ejvRPX1hONaA2HAq0rHgJcpzyDJqJNDImZx3i+pOSQnPP99nx3/vTRxW5Jv5uzb1Kn6eGj9795d/Pm7v5m/RViKqred02aqjgOTaSkZbc/e/npj9Iy9x5XmL2sDILYA0LOBZFW+w1araVcf/Hi4fqtmQIiMw7j5vLJky4G4G7sO0FiZiBKy5LT0nI2E6lFDGI/un50zo+PHq3BiUAo0gwweBe8V9Ncm2eHJgGptQYARMCOg2MViY4JiFysrY1DD0S1ZGlSW2UEc06pL8vsCfvOx/5RUxU1R7odL1uaTWvJBVoGKc656Bm9c9wpuTKd6uk0nU7EZKbE7HxA14XzLSBhy5huLD+06S36gbut9ntQJCYkB1pBlQk5Bu+4LofVn+wiQiPm6Jvh+ulvVTh6T+hDl1MKDjt0a5rhzPktiaoyYxe4IZmatNqFDpGCSq41OL+05pRUbei7yNJqCQbQdac5BaqjCwiw7XypTczSmidRdd6RNmAm53NJX7x9qzV7QlXAVu6aqguK5Hx0wV+/flPy8uHzZ9v9OZArIgoYnOu7/jQvzntAyCJLWjaxFxXvAyISUa2NCB1zU7XaEMAxxRhMjQDVjBGpiwh2zHmaUwgBEU1VRItIa0VbLSmp6ul0VBFQQUSBtTA8bnbbq6ur87Nzh2hEnk1UmKg1UwA0RNXWpBrAyvIn8mgC+Obu4f7+bkrp2YZe//ZvffH5F2kpMbqi0pq0Wr5MYqOqNlHvnCgQERkQARCtSeImzVQQANkxIiORB4BWEcyhDbEd8+2bB2zywU+9Z7s+BiYERlwpgOzY1ACJXaTYk4tdF+f7u/H88Z/4pf7pxeYbX/vw5/74L//mx68FoO96IKpNTFtg50Poo9+MIyIuTRvQbcq9cx3hdtNZa9/+tX/05jv/XGsiySZVpBaRXDU1raJNrYoWETXMYkWgquVcPvzmT//Nf+d/9ub129/8zd+cDkfnHSKoChLT75vO1nfmD1w1f9+Mhv+1/cBP/h+7zX53luW21lJrBQIiRiAzY+e32/OzR0/HYWqIx+MdqiIhIsQYu3HDTMzeiAm9mT7+ys+mNN+++ZydZ98RB+OOXAfIBhj7sTZjH5B5Oh03wZMZsZPgVaSUTESeUA2cp3mRw2muOU3zosQq2NJS0zwdp+Nx2l5eDruLualKk7QgACG2mjXNraRpSSiyuXh0OW6lpjnVruuICMibGjG1WsnMee5DMLMG5J1zCV7/+Hun+1tA0ppUxIWIyN3+0pzLm2HoBvbBEfSbQWozhNZUtjuRhmD9MNQmpYmJIkK/v1Q0yRm1AYMU0dqQgRyneRYf2XdWFyMmDtaKASIHUUWoBub8znc7h1WOb9rd79qhR+eVHfoOgYhJrEG3a0CWJ2iLmblD0Twdh90uBBd8EGmzwuCZVEYWCy5Pc4eR2J7uYh9jdCwKzoSJcqto0DNGh6np4Gnru+BZeg+IxQzQgsdFKTjyIZ4PoSo8nObA7GO4n4WabPuuqhWD+XDaR7yIEcwqR370yNjfPzwAUc7Fiwx913Xx9jgvKZ8/fnJ/PL1eWidLjIHZOUcuBiDcjkMIzpNr2lY6oJoRGCAQo0MHZgDqiVZDearl/vZ6maZaGxCx80vOJmLIYggAwXvnvXccnDvbnw2OHLRcm+GzsevQTKTdp9YAnePtZhu9h3cFONhUg/cESKgqCoBZpGVTEU9oYFOpIu205JRybg2JXXn49OMfi6hjktoMLOVislplxTObGREhArMDUGRSVVIwNCVVVVoDSmDI3ERKlZRqSnXONZVmbNFAHub2o1fPP7zabkNA67sAaOyirVcI58BFpdhE9wwXH7z37A//aQDooz/Oy6//8DVQKDm54FXEezfuL54+ebzbDL2nwBSZNypLlQa+iFZ0mJbv/4P/++vf/k0smSWZ1abamua2HmS2VM2tGUBVa2JZNJVaqvzsL//pP//f/Vvq+w++9o1/+9/9X/1f/uP/w4++/zunw4GYBRRWyBbYmrcnIgMQkZ+cVrgeZD/xbax6Kvy+qU1ld355cXaZwJeWW62I4H1QVVMJPmzOLvpxf9b3t6fJO9dqcc6Fro+7s7Mnzx5dXxvi3d217zYOLY6X3/rFX/neP/sHOc/jxfPt0C8N9hdyd/NGTUK33V08aXdvun5TGi5paaItFXbsmV0Mq3dszpW9Q+/7zWY1eSBxSQlMvA9EnHMm78h7EWVi6rdoStZidLDdAWC/LDklF+M0J6mlthrq6jICdM45MjFtlZiRuI+uzKfj/V10sOl70P3d/UOt0nLBYn7YWG2M7ub64W19bVp96MaLq67rg2MDHKKH2DnvakomuumC984IpGkrmYfgw0bBUsqoUEXYuYC++g5MTAZkByoIQAAqFc1MKjrXFASJqA/nX/P70o7Xmg+Q74G9ASkS9pc1C1qxdLI2o6nbQH3/+XmqZqjHZe4d+7qgpv1mI4B3c3vxyecjCXl3Ohy7fhOc88zahPthVptMY+wC8el4OO/9xdWVJ1TV/dBxNzhmdOy8V4DcxNQY7dHZ9n7OuUrv3BCcJ4hEcXB1DNO8VLHoSKVebDeietFfrSU2O9aBpBmk7f4ouBg/vboEwi54RyTSHDt654O3VIvmqbV6zJUQjinneU7TBFr6cZOazsdjiKEpOOfENKcktSmR906lgTkX/Aq62253RAQqNaUKeDzcM0PvuOWs0i4uzs7Orh6d7x8xdcyz6lxE1ZqqiBKSghJgUQns2JGoMq7oOTjm0kQAwBN77wnxbL9zjufvf7KkpCLe+ybNOWbClNe4D4ms5WGgaiKNmZjWx5QCkOlqGUVibM1Kq7nU45RKqat2Hh1XsoR6anJze7ot+vNfO//w6UV3fsWh01Y2BCK1mFHoYzd8cDm+/9WvVdz91hezqKnZzsenjx83ETHru67zDsCQ427bd86B6lyKVAnBBwZWcdHVefruP/hP3/7wu06KWAGr0lppUtSKWhbNTbNIUauyimWSSr16/8M/85f++s//yX8FV3kb5Ctf/9q//T//d9+8fvWd3/zNX/t7/9nDy8+956wwV0F4ZxtG/D3v2O+/e9qqkcEf/AeABt45x+j9l74cJOdcLRmRAAgdK7rQBy/kfUfI3odhs+/GvWCnRk2N2D358OvvffTBxBvHMt2+un39WejH/dMPeoO7F+JcNFSOI3dDt9sjO/Rdmw6fffZpMx7PHnHw+/1uGxwwxsEbOEUMMRZxNm7YsZo5JpHWqpqqQ6srchdBDKSqGIMQE4xD8Az73dZ3sdbWJDYFBaw5I77jYbR3hEUAkfubu3K4K/NpNr0n7vqN317Fyw4AWs3MDAqtikqz0qRlZC9NlmWpPiJAU0Os3jtpamYIGbUZO1B1RE1VDdP1a2MOwyayq6UQIjunSsjunXZJBGbkuX8ZuwAA8JRJREFUHBNqmdn34KuB0mrVdWzDJY+PDN8F2Fb8hi0PWhKaAJIBuX/0//2NLvrN5SX7cH9/dEido2HwkW7csKFhK6H//O4hzfdg2NWM5T4C+Bjrze3tPM01iWjOyfsOEboYO+e8DzF2SQGYN5txt9koArnoEJtKcKjAm83m2dmOAnZdLyqkMiCcnY1IJAYwATI5woiqrjPRoNm5ziOcBf+MvSI3kZulPuTaWlGFKc8IcFiWnOq0LCJNpJW8tJy7bkgpxeDaMkvJz957j7c9mExLZudM7cTo2I19F/vOO+e9N3JJLPjYBa8iMfiSZmv1MC2qCiKxG4T40EinfL/kJjo6vDrbBXYC6tHEERMbUFVDYlUzBDEDwFIleLfxXkUNTEWWnM92O+9cj/njPPkQQ1tnCDCDVldQBMjqLXrXCYaoSIQqSo6RSJqtTu7WpDYAgzmXw7TU0jrP5/suBu+dK1WOU8rK43h19fjxz/zyL73/9a9ligq4d/jVM19uPv2Nb3+79hdff7z/2i98Q7ePrj+b33+6c4wK4AAZLC1LQWai0EWPMKd0nPONLNZqFzuRiik7F435+ObVi//i750++xFbBSnaaqmtNSmquWmpWsRy09w0NyuiKZc4bv61v/I3fvUv/ZXNdpuXxTGK41ostcrOffjBez/7za//2V/+Y3/73/vfvvzkk4L8Si03xfURYbZuQdTMTNcj61/acq50jZ+wNx49eXY+7kuXbt9+arUiUddv1tMu9pvLJx/srt4bNN3Nn5ZatdVS0svPf1wrnO5ujrdv7+9u8ny/nG5PN/GEDymdHu7ezMvp7s0nz9//6KOPPtpfnh3v39y/eXH57Nmzr35teLj97Pu/tb26cv24f/zckIr51trt/aF0HqW1ZZqXJK1FtqHvK/nT6Wiqm/25C9E57rquGQFoxyqAjkjYGWBpsiz5+jBpSr7rRsPoOZXaRMEAEYhZVVsthFjmh0qOAJiQh613vuWspc6nY02v2QeOwzrqcoih6xC8djj0F2EYmVBEvOMuhi6GVrM4BsLeew7O1hYKh+wCSjNt24vzqoZq3jOHAacMoOCCga0oFDDVWtY1NqFHpBXwGB35fsMotfVYllqTLJPWAmBiBCorl5OaGao7CRjGdH9suTSRYRwF4nRIreTgH8i0KQKAlExEVl2bJzGxCadlrjE2Q3Lx0fnVZuhjP+w247brmxrHTkXKO4NM3TiaSmlIq8dG1JroJ29umKDVOi+LqVbyiGAqTFxKQeeQPIGSo8Dcxb6L8XI3Rk6m5pj2nQe1VvW4FB8CsF9KRuY5n/abgbWBypxc/+QxszPis+12G72oxa6LDPsuujUZC6REc0qEOJfmnBs8e+9PTVJpnQuClqsE773jyyesptExmXbBLzmbQh8YkOfalLj3vOQyxDjnBWohpOhiBfiyNomGMapakxpCBIDWWm2t7yIiMogcrmtJ/TiWZq01U/1SkaW1cwwZCZGQ1ZSZV9+s1qaqgLgObSJam6ZScqnR0dV+tx3iuOkMcF4KD/03f/kX3vvat7ZXT5g9ET+AqgiCTcA3zX3w1Z//Y9vLF59+crmJ1m/TElI5Jm2gxqvfogkgTDkDYl/EWiXHBOCYkUlNgw8A4Ls4Xb/85B/+35bPf0zWRItprU1Lk6aWqi5VmlkWSU1SsyW3VOsf+oVf+pt/63/05IMPT0uSZQrMpWbvuBu6rrmHORlANnny9Mn/+H/6v/hP/qP/8J//+q/tPdwZr2ggA7NVI1svmPb7XLU/mcW+DAysfhcRvbl/WBTa+vYhEbMBmKiKHB4OS0GXj/e3b2opTQqAHg93m2EL0tJylFbmefr8k9/xVsdHH0k1Zldzfvvix99Vig7Vh77vXi2nN5/98L333x/HPjiHCE3l9vY+BN8UU1qY/avDXZ6n7e6s1ayIx+uXvXMX++049knx9euaawUV5wOFmB4eEMwPYwjRhbDb7foYzncjgNJurGZoRmCXu81pSbnmMk3UjUBsKgKK5Mt8QqLYD+iDNUFn7LoyTaKzLDPkJfQjD9t+HEXVb3YEpq3lXMl7IjdN08PNmzJPokbDpovdGCiGLqul+eQ9d8NuWZZWM6FprTUlJgJURKemiAA1YUvkHBKZqus2sKbfJZP30Kq0rGmyOHpC3j7qCCDPLS3akpXUSq5lXX0joXfvvf9BFx2aAvApF2kteB+Ca6UikUOrpbFjx5cpzZ1jIOv7jhwX8osZdcN2tw9d55nJeRNTMBBVwsVqQahWwVB8VNI55Trnw+evzYyJFKyUQoRmwMQ+RAM0AEIQaS5ENImxZ+eQ3Qj+VOWzt7cAto1x3Ay1SfB+txlj8KlJU8ulMtrZ/qy0Jsx9759c9WMXilgqlYJ7ezqJUTsuyzyl6WA5TUuaco7syDtg148b9sGz956HvheR3rvt2O+6gIin0jpv0XtVWIvKt9ErMIKZwb73BhiYi/q5WTOH7EW1ldZUQDXXWmsBM3znP1lt3u9GhmleYoxn6UZFlnlZj7kvR+oVVgieqErzziEiA634dluBgYBrg6Gq5NbSUlXtbBPP99thiM6RiB7mcv78q9/8o798/uQ5IVdV0UogIMVqA7Bk/uVDbqIfPf3Kh8BeHszIck1Ns0JkYsZAXNm11py33rnBczFJUkPwnUciYgAmIh/efvK73/7P/5Py9qUnMG26hjAVqmqqWkSzWKotNZmLnOY07s7+wn/v3/yVP//nYwz3Dw+eUEwZwBNAyae01FJ3sZvSfCuKxATwV/+H/9ZP/8Iv/Wf/x7+TPv18AnoXxDVQ1He7y3eAoHcn2h/wyuKa6Hf3Nzd1ThnwdDpIa0QkanmZwUwN7q9f+W72bW4lr0ZNAsjLMp3uc0mSTtqaqJbS7o+T0utDSnmZck4IdjzcLKeD64e8zICQ5vnzH37/2de/1qR9/vHvHr96+fE/+8eK4IfNw+11jP0yL90w1PnI7LaXj7phd7i9PhwPMURk2l48IheRXKuCmqo0a82IATnPR8nzvUkfw3Z3ZmZd8HOVudSLzehNl2VSg5wX7xwTAfvQDew9M7PzqBKJpDppbeh2uhtUGoWuiQBxadLS3GpR1diPrVWcp1bmlpLUoqb9/rIDzPMpn1REXL9DNNUiMKuhAOeUTEQVmqnWDO0AbdGamVnTEeuskgnMdVuKAzOjCwSG5BKz73e935qnNJ+wVcZ1V+h4cxZVFGCeZikLmLgQu6XkdXAdXai1dn3fj72VhkyMxI4dc/CBmcboURoCEFMxOJWagUqTvMxVFVSjc330HrDrd110Rnw4nlqDWuvb29tWxTlGomU6aavsfecDEZghe2cAa0Xb7vIqMB2myYehtpZT7gcWsNqkmEkup9OUXnwRQ3AhxBCGvjvb7ZqqmlUA57HrOs/QhQAr5Lq1t29fH2+ul5zKPBtAiOF4f7e6t4hpJvKxA6RW2/78bDvGoevHzeZqGKKDU9OHadn2fR+5iaBIM00CDbB3HD0VsVOuqoJoBOC9P6XcWltykVpBZduFwdNyfLh+mKqaIJUlA5GZsmcVkVqJ8PHZfs/T/c3tNC/47nIEIkqEovpO4jYzMEdUW1O1EDwYVJE1cd2aKkAuwozn22637XbbEQjvD9OS67d+/o+891M/EzYbLQu4HrQxgtbaalERQ1Dxxu5lK3OtZ5vzKzfku/zidtKyOAqpyM3D3X7sh802Ote5UEXmnGLwj0Nw3uemrVUgitp+8Ou/9tu/9vcpnwigNjWVXGpqImalShFIrU1FU21TLrnqT//RX/7X/tt/88OvfKXW6msJ3teU7uYEzkdHI6EYZHDWIAY/MlelQy6vj6f3//Af+1tf+6n//G//B3//H/5aMv7JmfXlcpPsy2QIffkS/H4yLbkmdUmnYrZ2oaupa1VVwKzWkpaDiKiVeUm27rMRoZVSFgV0SGpqZqfTg3dhOtxktbKcUprMVK6/eP3FZ+P+/Obtq5zz8fDgQ//2izcXV09bKobcXTw+PdwvS+l3VzUlYrecjul08N5Px9thd8GxDzF2230rZZoLe0Wz4Di6cP70ypkGx+l09D0oJHOR4nCXmtQafaPQxX4UtWKEcUO1OceqYgZd6AwgDgQAKmJAvh/6YWS0oQ/SpLZ2mnNpotKQOXS9mQBQq01Vynws0wGJOcYYoqkc33xuBt3uzFQ9KppJKX3Xbc7OxCCllOZFVcBUsrOMis0zITs3bAGASSU9WJ6QFAwJGrk+bM7csPfdxqSJkvfOtLWUiRygmYrUStZicBLOgNCtavGainZizjvvPZg5z57fzSZjcIOngLDbBinYcgKHxdSpfHZ9++LNTZ5Py7x4wnEYPVLfdxiDurDdjCHGR9vhbLfTp5ev7w8Fea5Sa3NkZ9sNk8vL0kcvAGdDPwRen5jR+dyk1lJEjL1nTk2yMYJimj9/+Sq6AZ27ONtfbHddFy6GPqsmWcPk/rRk50lVpdqyTD/83nfTMp8OMzuoOV2en//Uhx88+cU/fDoe3tzcXD9M5tzZbvv82XvjMLgQe8+d90DkALpAzDyXhsyDJ1OHgLkJqM65ESJAy6U+nCZAVLDT4YiEOWdSobWpV/TOOTLb9OFst0dQh3b02I2jKCiRKXTBPb66hHK4//6PXr167QlyLi6EJeUV6s+EiJByI8YVxEjEzO88imYIAGoqImLmmTa9343RB59LfX17PJ3mn/25b5xdnWurbTnxsG3l9E5LkmIlg1ZUa6oKJv0gNc8lHzf7bilvX7748cc/5uCBnCK/CaGLrjWNMQIAe2dNvEMy88HvdmdB8m/8F3//89/5TQ+iiNLEEJpIE8lNS9MqlprMpc1VT0vaXjz6a3/j3/y5P/ErjdzdcSIwtxmbVB/CVReZqDZRtbbM237UNUDvw6Ayxk40llplu/03/tb/ZHv5+O/+3f/rUhvRu1lsDeO+82qYrYW+7/abv5cNgJrTkk5NQaUaACpWqe8wka2m+QDggsNa8k9muhVCQKpGsPb1lpKrVI8GyMRMhCJYa769ftUFn/Kc0vLm9aevPv/hdn/x6ac/enr1RM02j98LZ4/yPNXawiBMePP6i7cvPtmM45KWaS6b/SVgq/e3Pg7o3OXV1TBuQghFNOeshMbUX27HPpxynYuaITnnYyetmSqbEa98J0aiJkI+gJpIXVeaAMYIBDjPU6sFVW8O7IIDVQRyIZo5ACDXSc0ibXtx2UTc1eO6nEpVadn7WNO0wrAQwXWDIYlUoHj7ME1L3m+Hoev3mwtClCZNBFpZuXskVWoyz9SaylWMHW+2oI1cBPJEqMaSlnKcoObT21spGcpCzhsSmGo5mSohx91lOLt0ZuKIvfOe2cC2Q+cJj1PyQ8/M96c5TdOLkjCXcejJzK/+666/T+nt4eHwcFiz9ezcIk2XPPhQbcFc5tpeXhN3w9Nnz9+j+NWLzbP9cNvgMOclJSSKIT7My36/CQhEqK064n0I0zzPDXPTJhqYY+d3fTcE/5BqErt8cv6LX/vgNpWbLAqmTYBoNuhj1NqmJbXcnHOguJwmRsxpDv12Li1u3cVuHDfbMG4mpocK+/Mnv/DNbwDBq4d0tt/MVUX1mMqSageW2TdpJtSQU8pzWrTkVUNVQAPLuX5eynw6llrQRxHbbUZViH3cbDZ1nqZ5XkqrKrvYH6epII8js/MK8PjR5mzsx3E00+C4tpbFoOqPb96WtMLtSFVXG6eosHOpFEAj4iamWkPwzvvWmpiowpr/ISbP7Bm7QM65pvry+nh9+/Ctjx6fne3QTEtp6JhneEfPUW255WytWS0tzUjY785olLkuOZ2GYffe86dpSR9/9nk/7sbdJtd8/fr1/HBjKsNmG4KvTbph2203fYu3v/Pt+x9+J51uwazoaiNHFa1NishSpIhV1bnIcclV6Rf/1K/+N//6f2c8O59zYu8cs4oeHw7Be99FMVuWkkoeu24zDKC1GSjStGRJs7Z2d3tbqhyOJyL45T/zKwjwd/5Pf3clavxEGVsT1msByu/tN81W+WwVChEJUH7SjAIG74Cuqjln9kujoAAuREgLIzFz12277dXgsQnmkqXVVWpzzpk5Imcm7FxtKiJPnn14vL+tJR0P95dXj1utX3z2u/l49LFbluRij9TUJC/zePbIxXE53XXd8OTZ04urJ8G7ptYHFmQcz+c037y9WQVARNJW2Ec6UD/0xPxl+R5yjIwIBsmQ7R3kjpmlVQUspTrn0rKM0SNirdl5JyqmhkS1KYiAZEEKsXOO0Jqp+BBKWhDdXBYiGvY7aAWJht2OwNYnqYqSc4BAxGiKiIcl23xEq4QIxC0lSSdNJ8mT5gNIBVMwQRPnAxG5EIbLD7aXlwy63N2WVqVUlkXLiXyEliSvihuAVjNTBHl7nF//0HV1+uDxZdhsjupuj9NxTmWeTtOspk0kH4+OGRD7fqOujV3nvVuOB/a+24wfnO1Hhpvbu2lO1SAv6XK3/ei9Z+z9lLP3tPXsx20z/Ozm4e3dPSEQUS3ZDLb7PcxZ1KaUc1qcc86Ht0s778OG8XJDrwzvk5qAa+22lrNorVXv/bHUE7msQIgqIEhVDIrU0oAwF021Eabf/eH3AODi8uLq7Oy950/P9/tpSb7rWlv9lDQ3tSq/+frhauzRAIilpVeH5bDUZZkIMTVhk+lwVDAFbLWWtBCzNCFGzwSq2+22EQN6MiLPFTB2Yeji8fBQRc832wsmFyK6UC/OgbiWXJog8Uno/n7qpyy19Q42fc8hHF9+8nB/v3L9ibi2CmqmAmi1VRVlJlMrtbLjJqKaVEHaenECInbrZgCMiBXg7pjeXt+9/2R/eXXO7KHVlqZAWK2uatFayaF5aTmbFANy7KWWkk7Uota8iNpmc35x/tnt0bybl9T3nW3ONmeXtZXWWuiGPvab3TYcbw/f/qfp9Y+pFUZaSpb2DtSjps1gKbo0zbWl0k6p7B+/96f/8l/91s//4SKNTgdkn4+n4Fw2KqVO6bDdbEDleH87Hw8lLa216f6OmVNaus3uo298c56W+7tbNtBW5iXdvL25ePLer/7qn/1//r/+XvBuPYx+ssD8l5Qy+/JQq9JiP8QkaG21p4EBM0tDeFeBFMbNxePHj/T1G9WWlnllMsYujtttgOZ9YKKGFruRyXy34dD541GXhcj1210c9ufkdy8/Pdzf5JKbyP7i0Rcff09EwzDEsTXVpqc6LdK0H8fd+Vnsvj449PsLQxJAZhJCaWKlMHPsIgAZMYCZRDMNMRI7RGDniGi1cDvvCUhNalNEFTVQBTQkx96ZoQFM8wzEiBAdUwiNvYGYQVFFHxmgpDlLY0biYIg+9rQiyU1BG4CSSiBWVTFjBEDQnMykqXR+bSkGQiqlSk4tHcvhWsoMOYE1Zg+I7AMAgJihkiRtjfzb+dWPWpkcIwD64cxMyXVGARnRTFsB50wFrRkgoDNQ9zs/+PEnn73ouyEb5VZbqaptJbjHGIOPPsa+H5x3nWNSCeyGi535sNntfAxXg4eP3hOkqYgBcKv3dw/kORe82I5IjERW24dX5+QQkXKVUnLsBlsLhMCamGxGQPLeK2BqbSnNFS1iQxeQKJWSa71RIyLJCRGAyBMxaPCeV6qYShWQUoPH25sbNXHOP3r6lBFl1ciJrx4/9iGIwdVu6B0P3rdWamuZPDHenuZdFx9t8dkZI16Y6SdfvDmVNjy6Oh4nYJ5SJh8RLTCWlHzoxs14Ng7eORfCYZpiCMh+XtJSq7nIILdT7oKrD6fOUz8M6AIZdiGIaG01eldrraKvbg7ojk83vrz6eJ4XACDCXFqtbX1IrvRhIiJClRUUQSLWTFe77+rL9Y4JQVSQmRyflnZ3Nz2+GB9d7vu+B9NWFofQErLzREyOVVRq0ZxEZBUWCIHYWxOtkzonarfX7tHVs298I6ytBjEE75yZLXm5vz/u97t2vL35zj+5/vi7WCZCRNSU8zG1XJuqREeGMFedsmSRZSkYh5//1X/1j/zKn3Oxe3tzz+zM1FTZBTSrIuzYe/7ke799//YNrL0KtXRd143bVuv26unTDz7anJ2HPvWbkVpxJlXFmrDzX//qh2l6+Ee//k/WXervj2SuxXQ/iQoQ0UpQ2p2dm4VGcPv2xYq72Z09Ot7fiBRGjDF0fbc7vzodjrLKq4hm1moxFUE1NPbeSgHyrus3l09zntM0A9wgou+Gp1//qeubm/1nPyy1IjpFDv2WXAjBWfTgLkC1dJ3uz8jx6e4mzadWSx3Gbk4xBjNgIfLvLla1ViACtRjj2rIMoK00ZZNcgdb5yy2H02TQjYOUSgxMRMitJhd7rZmJVMW5oIxqIjm3DDFGz2gK7L03qTVXIyISxSbgnAM1mWdYn5QqJi2fDqHrGK211m33xzeft5xNtabJhY68C8MOkbvtznUbH3vr+64fW5pqmus8GWjX9SpNapY2A3dNIIZRMJgfXRhwxRFqQxekZmj5nbmsZZPGPoiCiSBkMHTbi/MYY0Tbgk5JjoBqMQ6bbhgeP7qMMWqtu83QOeq6jthbbYQQoh87X3NqDY61HKf55jD7cdNOp9vr21zb3d1NtxkuznfB+1mITDuGYRycweX52Ra9d476vjQJnjvnEHCuNXrHaHPTVKVD80CHlKNzhOydU2uI1sdAzIc5JdUm2oXQTEyaY5e1gcEwbjbj+OyZm5YFifq+Y6b9mTGxZz4t83FeFseLbxHNWo3kAlMfow8+NFEzBzAE98e/+ZEAgdWcxQhPRXOTrgvesdWWc01ItRQKvjS9iF2rNQbfhSAiLjgHqIitFCKejwcRcJ5idIjEiKSNzSrytu8uhni57evhzXfvb5sYIOVaa2uiRmAIuHLrkckUSq2OHSGJqCGIrhIBv+tUJWJCYqhNlqWMkR9fbPu+BzCpVZxXm1WFOboQSBkUtLWVuE3EoPJOSdJmRCCCUm/vrjeb7ePLy4c5g4EhVREw67sB43T7nX96/cPfyrfX3qGJGGAp9bjUubQiMgRvCFMuxyxz09bsG3/kj/0rf/GvPv7wI22iqgBnZmKmtVQwXOfNUlvLqZRCzpei7L0fxu351bjd7s525/vdzcvPv/tPvp3nGQyWaQpD/+i9D3abYRyHovQX/vK//rufffHFixdrc+gfdJb93okGsO7b0TGSI3p3M0Uijl2/BGepwvrcYFSzGHzzfd/1CoqAIcSzy8dd302nOaVlmk7duHny5P1HX/+p2zevrc61zGBa0mnYjj/z/rP7lz9My8l3W3Rx3F/2wxD7XkDzdHLO932vYK1V1w2di877YbvzTNYKEabapFZTiZsNh+C8l1IRjZnNEMmH0FEI6TSZCRMhYux6ZDIAMQVwtLaLUWfvytxYmyxpAbAVOokuNMJpWVQUsZqptUbO98OmH7paW2uy5lzXohwkkqbkfFVLJYNJef15Ph0FwBRc2FCMplLmxF2/nE6whhQJESNthuE8MEhkA2nNsJWsUolIazapdb5HBdEGuo6E3mqBmtl7A9OaAYlibAqILNpAkV1w3/jZP4SqxC6YXO37syG+OrWDQisN2U3zBMQ3p6XlonkhNSnN0MyHY17NsFxrM7DaCuWGIpePnyzTfEql+HD36UsBdb5TWAkh4JnRh24t4I0xxBEAzvbbPgYjeu98dznEsy4o6iLwUNVa8Y6NYC6LitRcRVofArRqqnOD1kSJA6N3biCvZsTOO4+IZ7ttEyFCBgzeidmcy/FwUtHzi7NS6v3dLYDdPtz1seuH0XfDkpaxH47TnEoaPGFeWmsiuhkG7jpr7VSKKpq0oQ+ltWHcKwD76IMnpmVZQgzsPJp1jNFT5rA0O99t3Ip/ZGrkailLysfTSYCIOTp3nE7neoMuVLXazBRrFXZoCk2aiCKiIyi1rZh7ERFTIvIrrx1UVNBAVZ0jJG7NEGy37fqhQ0IDQCIzRXQmolBAQJVMVFWJWUXABJGZnEkFYiJGE6uFO3d3d3sZ90PfWS1sDZGWh7svvvftF9/7ren+znumVqUZEEXHzlnfByDshIkpVZ0aHXLZbve/+lf+2s/8yV9Vs/l48s4xc85L8I6IjCmlrOqIaYg+7LbjZjNPE4IS0bDZra0LjvGLTz/5zj/7r46311ILAF2c7z68uHo86uH4ytphO/TOD3/tv/UX/oP//d+eU0b8vaslAOhakmT26Orq/v6+tsrsnjx9j0M6pqMLkdkz8bg7X+bjSj10vr948sHu8fPOhQehMGzycjK1U0qH+7t57k7H+2meCLHrvPfIWqaH2+PhZjodAOyLH3//B9/+zW/+9LdQGxI9fv786r2P7u9uzs4euRCoCgKp1KrmvfMIj54+Jtc5MGm5GqRa7q6vn3zwIQJqkziOpWTnQ9f1a1mNcy66d/p+gUrM75BQ7AEpzVNGYyKQGhyi62tOIOIgDR2z6ytwMwVRI2pNnQ8YUVtb3a6quqRcagFpxBxib9qAGQHKdEJE9CHGgLu9lXy6fY0+khhFh0yiCFUMVGHJtRB9+TEE6YKncUtdv9zdNxeIGZG579EkOAak1m9NirW0Zo21ZgTDKK0sUGfqzgD92oqu1sBt2Hdg5rYMGQC03R7nuehLOk5rEZVqKXWZppRSTfn9b/7sw9sbnR6Y2Ds37redY47xfOiY3ZxzbsIiu2EIXdcdo3oWRNpuAHATeNeFLvjrh1OqImYAcL7dbvb752eb4N3Y9475LtXa2pIBTdTFm9N0PnbDtm+IMTrneiSac21NHOFZ5wghlTqlXBCboIp1fcg5R+dKbT5Gz2QI0nReplRyCLHU1kTmaXp4uJmPh5RziH1TY5831eiUxr57uL5lwlbb/cNcSybEcRxvru9zTtbasNs+3D+gSpkPngmRybk4jj72TCzSxs2uibCP0mQ/9k+uzrfjyBSqaAUAIIcIIZTaxu1OVbuuq00IoJ5EVI+HSURaEyJm5tLqWm9ra/XB6kddyY7kAFaKsqkKIRKTmREhIzapnrAL3gwQgJ0DRFEFlRX9pepU2socRVk98sropFXV5kK3JlilZAGYTHfzuQOud68//t53S9UvPvmxLCeH4AlRrVRBgkjs2ak0BPDOMWpTq2rHUr7+tQ//8v/g3+ovn5VclpQQsdZi5p1zq/veB8/Oi6h3rta0TMfY9UN/Ka12XVTV0zQD4u3D/e319fmTZ/1ml+dpuxk/eHJ5/ebl93/0ceg3yzztOj7rxpefff7e+f5Hr64BvyzSfBfHfHfrzDm7ELAUM725uXk4wZyPpgCMyOz70fmu0gKIrWnO+eXL1zAfru/u53leTgdAbCJMPnab1kpOSWp9/eJTrIJdtywzO4dIBlZyPhwf5qXUJmmZmUTKUss8dL4butNhYUc3r18uhyMj5rQAmI8xOtfF8Pjpk0cXTzbbLSA6x+Cd97ztttEhmzIhEaecoMp0PBITMqE4NZinU4wxlfbmzduW5lYyd0OI0ZCcD0CwPNzqfOy7Dp33sXOxC3FAQjZFJu8DuEDOmahzVEsxVOcdYxMCVFXVbhwccxXN08khOh+63eWsN60UFwdCZB/NzGomk37Txd6RYj9umiEhAruqoE2tVd9vAJSdB4O6GIeOvcpycP3A7AEAWkNGEHUtg4gxAyCyN61WMyKg661V9yzWudohmbvYNZG3t6fD/f08zczMlj27/Wbnzva73u2eP92E55uxd8x9F81HdUwmYhgJi5op7js3dKGVcpiPlfxBeSn1G/v4ZL8ptR2Xer+k60aHVPd9BB8WkWHoQwzR82YwaG0GfHlKc7pL81weYNyOs4K7uWGCo/lZsYk83N/dvH07Dj2YHQ4HNH385HHsR2IexxHZiSgS1VZSKv0wotn1zS05l9OyLBOTY0Yw5NAhub4P+7ML7/3t3Z1n6rtunicDY/Zx2w9Dv9tuVPThcKgl11piv/Wh63aXUhOCqbQslg5HQNxdXKgqu74PYfdo9ERVreZkMfbegfPHVKSJIRLT2bAlgOhYABzidKxvX72dp4mdd96JNVNjJufcUgsYAOK7npF1wU5ICK01M3COVxipERIjAjAT4uqeY+c8I6oIE9GXnJu1ZWe1+BCiSlVFFzp4B8yxVhISioosBWt6uHl5++kXbz/5+OHNKy3JEQXHtRREcIhDF1NrSy5oUmtbC+wICUAQcNfHv/iv/6Xu/LHW5D0ShSWV9eLMjoILq8ehSVvT8o5dyRm/xGGLiEqTkrt+OD8/i94T4nw6bcaBpLz+9JO0VOAOgAz5Ry+uT6ePr7Y7LXkb+VgF3vkz3h1k6+3y4XBY0xRg9sUnv7tomKaHaTlyds65u7evToe7vExrd+Lm9Qvkric93d+aShNBxFpTzlMTqXlprRhYzktOS5qXUmtrWmtZmwBf/PgHkfjs4sp7X1I+PNy/+Pj7/XIbrJIPogkouNjPx5lcPN7dtJvbvMwA1n2//+q3fuan/9DPso/RU80pzdMnL19AK2bQWiX2DVFqqyWhj87HPB1B2wqiYnYlZ0lH9s5VCaUyO3YpbvZxfzUrHlvDkuv9gX2HhC1Na08ZEfbbs/PHzzebvo9evB87N3QBzA7zIqW6NTUEouCL25SUpnl2Pgz7M98aKICZc4zMofcdA5iAFOe7vu8M/VzqsiQVYR/CMBIoCBAAM/G4LSk5NNeNlhdkr62hC0gE2Ezbu05UhLac3sUzDLFlIHb/4uUEyALoi+3H/moII23nIZa8WCmbLoTRs4+dpOFsA0Cnqmju7aGkfGrMRZSc8851BOfRP5zmpdT7lFOtTWrXjwJ6/bo8vTgfh/76OE9iHIdt388ikXXJtRh8/+X18XTM01RTQh8pBEIsKaVpfvT0cXAOAE6nU02pLsfd+Xmu9Xh/B1pPdzcqCuyNoBQVNTQlRy54FQCEYbt//PT5bhyGvsutjUO/326aggu+Sbs8O9sNg5oCIhgMnV/mueuCttKPY8p1sxmic2AyteqcV7VUhEM0QgTH2DOz98REV+f7q7Md+U6bxs6HEHIptFaiGZRSFmmuWa7FEfcxBscMRkiEsO2cSPv4s0/evr1mAhNBpi4GRFDTnJsjUjNGWgvWGIAYzFDBAMF7DoGJWYgdAYCtMGnHzOyYmZhMV8ZQY3bGRty9S434gAitZARExFaKC5GZzBoYIDlVaaV4hHk+Tjevrz/+gV8nQ20oZkjOu7OxC8xzSsdlvaw4VEHTlZgnCkwuNXf/8Y+wFu9RxM6fvDdsNlUEiUuuSOCcj0RqKk1MWmDOhwcFOz083Lx88flnH0MTH+Lh+DCfjqDy6L2P/uSf/JMvPvsEiX3Xd2ppnj758ceH03S526RWwrjpclakqVT80v2/NnSs0Mp3Fg2Dw/2NhU1ZTu9aucFqmqWWJhUAEfPp4RY5qMe8HEWaqqw/p+REteW8IJgaLPPp/uFWP9X7h0NOD6lm1QZgx/s3n/+uffVb36xVajMkz767fX1kZ3WZog9P3v/g7RcvT8d5OR4BQA3jsKnSKPSI8Pnnn/swpHxKtbVmYJbnYzrcO7cSUxx73407JK9qvutvX77oh5GIVCqYgPPsIxBjiN24RQRVi/0wnF/l6QSmbrCakqq4fitSl7SA6pyLIi9T3EbSnO+tpSIp5VxbWSYmbGbe+XF3zqahD+NmFyNLHA3AOVJgR2uBdJnnuSi1bAjH64cHZh63W+ciBmdI0sR5n8tcaiFbW6e1mrrYC7m2zM53AaymCcEAjENvxCAV2aspgAIwaAVT93opjsghDojXt7c3n3+SU2qt1VyQnSH58Nb74JD24/CN959e7nbUxwk6F8NJYDme2jxJq6ks9zmVKhJi9v7+cGytaqs+RgP60aubzWaz2e1D8PsBCU2lvfziOvbd8TodT8daqkhzzjnE4D2z22xG9+Sx976lxQy2m03rhwPB61evHdN8OkhJwC4MW0XMzQyhH8daSi0514V85x1vd/vtZozB7Z4/HYex1ppSGoderHU+MEJrLQuumuTFZkhjn4qWVEKIxOwIgmdpdr7Z7AbJtSqcSxPvue86RDwejvvNdhi61Y095QqeRPU4TaI6dISqTEhDV0s1U+ccqrVWTUUAvXPGfH9KWI+31288U6ut63yq7zLngEhoffQGaGrOkYqtqAwmNhNEQMY1Jc2OiFCattpMzQVa4dqqKtK8iwCm0oj8muP1IYJpq9VaA2IAJe9gLTdc0QWiWlurBcBKGMZN3wde0/iISACOuO9677ypjD727I8pNVUCZBMmcMqHZdoNg0d4OBzv3r5RbZvtltJpd35+tt8LOQ4ekE2XLvhZcCo25Xy8vX3z+ecANk3z/fWrVuvpcD9Np2EYAPH58yc//Yd++vb1i5xSNwz399evXr89naZUWpV2//BwnKau71Nrg/civLT2+4BlvxfPXL9My+SUcp6/DFfUkqaVqAaAVm1ZHpACBl/LYmAKsBaxgalqa7JyGnRaJmZmFDN6t3RWMtPWSm4VjC6fPLt+/SKl+XD39nA8zoe7NOurNw9PPvrw7NGj0MebVy/n+2NpBw7dkydP+hhOp/u761fXr18/+vBrT7/yDQ+GRLLblrPzKlJKrcvSalmWidlzCKEbLp5/dLq/A0RQQ/ab/QW4NWHoaslq5hyn40MruSxTTcmsMTtDRAqAHDd7bZWsvfnx96xmM2utgjZgcqEHAG0FTLzvQz9O8wRArt9096ddF4bttt/uPEVECJ76fijLgo4oi9vtW2tpmZAInSvpJK0hoBi24AMRMpdSiYjYOybHDrseJYCtOBlDIkO3PsYVEJnQEJuhY2lJ07Te6hXJzafjm88+SacHMCUkQnKo3Tj4riMDIj7m9sMXry8OpyXlLKrs/O78cDrm6WTL4kABSBAX0URohLU2NCyldl232e43u/35xYUZpNJyKsTc7c63mwH5UFpNSyq1UggCMGz33rk+uBA8gVnfqZrUTMQfPL5a8vL27n7YX1ZV7wMSbTbbGCMzxND1jlOttdXYdbHrEVBEiOjm7v7TTz4NXdfU9tsNM0u9i46L6vn+LJVS1EBPwTnP7tHFWdZGHKS1KZW1FbSPYbsZmAiRRCHVOi9L1/Wptds314y03fTesQG2ZmbahSBq0oQADGoXfHAMKqICzKUaIMwp5em02Z+TIbPz3gFQiP44H0oR59g5dkxr+wmAAjgzUGNVMwV512L9zmeAhk3X2cLIMQe/qjbr8q6V4ry3VQWrhRFqBiI0FRDV1SHKDRoiEYCqKjLVVtOcXBD2qfeui14MVkRPa0qOG0BT7b3fBK85d8GjmYksayoXbBPDOHblcNzunji41NaCo9sXn9+9fPlwdfX6ixdsGoP/9IuXX/3oQ3AunD3aP37CWs7PNiklSfboyZPY9Z1H1prSEjd7NH396e8e74/nT56l+ThPp8uLi29+7avLPC1LOs2nt29vSq0p11rKV549+uLu8HCa7Cc9dO++4DtDrIgHU2n2pYlDVlamrYsCbbX64FVFTZFWCRsNwZCc7/pOfdDj8d50rb4DJCb2TE5AkVgNXBiKwPOv/czLH/42gJ6Od/fH48vPPy90fpxO7eMfd8PY9f2zD74CH7pas7SaDvc3b14haJnnZ1/5xsWz99lMtKFRN46b3Q6ZUyqH2+vpeASz+XjcnJ37EAGgk7aautmt7TYKpjVnHzqQery/iyFIKQqG7E1gnmdml1MGUADSmgEQyGHnTaUbvUoD0NAN0pqIxhjgywo7Ju9cBKKb43yfxR+m3jsfguvGwIf9ZnDOBdFS8pITlyTs7k4H8r6lTAQKMIawtGqtaS1SU4gdjmM+PRA7kEpo5CNIA0FkJyKAtnaDIZiZQppKmiWdHDpGxDevXp1uromwGUqWcQg+REQMIa5Ou82wIaRSyzG1YbM7vH57e/1FtY+9d9aaI1ZEck58uD8cDtPy6L3nT589n+f07MOPhmEYul6amKqqhlWgAWuitw8PQ9e9//x53/XzktG73TjudyOIlVbmRWqr0iR6t9tswayKXFw9fv7ee10IpbWltLnUJZfb+wcm70MI0TfA0A8hRgTzjMF10lofwr1oXlIXgnNO1Zri8TAhc6q3JtJ1seu6ptC0emZrWlVMrakIEyMuafHOm+l2HGqppdbjkjzTMI7eh+idKYhhLSUE7zkaqLbWamHHona8m4fgrRZ0Dl3zTE0VgajfHObFafax6/qI1NbNS5Pm/FoxvCr6QMQiWkWZyTHm0lbqtqpVUVNdRw9CJOIYPDO/y/G8wz/gGj9UNQJspTgPAGyiIg1EjBhrUVJmBlVdW/lqW1LqiarUgXnKcreUpUoudanNkMa+68KyiX7fxY5wDH7Tx5KSGp+WtCJZhnHTbzdVGwVPQyy5jlfPlmn6jd/4jbIs2z7mZT7OE7Y6nF/+9PtfsXyC080ZqwTZXwwUujnN5ML1yzebiyvvw+l0bEq7y6tSsuTT1fl+s7tSIoz95pK2y/Lk+Ydoenv9utV0fXN7XvtS67TkL2NMBPBlBgDMzOKw53kiYgAjotANpVVIaW2hQ3Ycus3ZucJd07ZMJyRCpO3u0ocuH3Ep2Tm3dhgi+67ftRr86dSawHoqdENtOoyX28vHWrIPfW12d/22eElzKtP0ANfORx/9Zne2zPMQ3H47Xj76QwrIxOjYkGpKvt+S41SLA5aSx3G4CM8uHz8ztNPDwYXQDT05d/HosuRCzK3kNXHpY2RiMCNHpmoA6XRaptPp7rY1NQoAKFoBFIEMCJm1NHLeqJZamb3vOva+H30cNtqqinAIw/5ifR4QU6slT1NJ0/QwO3K+62PXPxxPgBBCHIdhHMfC1EoK/aCAyJWdh5prmk1bW078zmzU8nRCVW1l7a41LCtzAls1a+QiOzRDq8VEwCqTYYwupVzm08PDgxpyMw7RhZ6dE1XvnYgisHfB+857T8Om72If/QcfjVePHxPqdrMJiIdp+s63v/PNn/mmdt15bsL+7OKSiJeUVC30QzM0lLosisjMzjuHEAMtqaQq3vHTZ8+6GJdlWduVl7QY2GqR34z9mkiP3jfVuTZBEigOYBtd71D78GS/NURCdExnuy0iliaiyqie6P6UHl3snj9+lEpBQiZCwCIioillIDK0lgsTtVqHLgbndmNfxaZlPi2SS90MnaggEjER824bifAJEiGZyZSSqKFBYELHtZSGhYCqCDQprTnv1ez17X3fxYBMWpJZ33WeicC8966m1sp2uz2erl9f36lZE6mlqQgjAGIMfqVuOc+tiamJKBgAG6DV2kSUeS0ucZ3j6B0TO2Y1WVG6RIiEa/8zMtlaL4aIvE6aigA1F4AUYtdqa6JAkHOrTaKptpZK+93b6fo0L7V1ITx9dPGVD97f7XYhhmlOJS1LSlXadFrYQI0WAUEsgGee6JPvg0KrLW42290+PPvwze3Ds/qhD6ELcZmOtZYnT58BaJsPdw/3bT7Vzt0fHsY4uN3V7tHTPsbf+e3vvHh7G/thmpbrN6+GwCFEM/3gww8oehFhz9YEwZZlIpNa0+vXb5dcpdVnF9tPX7fchBEBFAHRdMWbqcH2/OrheONzQIQQurNHz4C45ISICNT122F3cXbxqOYqBPe3b3BFpGkzsM3Z+f0nP5RW1TTXepqPERAA2Dm3Xu5C3F487vtxc3bRwUcPb15dPn7++Sc/Sg2atXQ4gRURUQUfu7ZMj5893V5e1aYCwMS1NTQwLc59WdXctEiOXTwejq1WQuLgu+3GOcc+MCGoduOAACFGUJtPJ23i+pWXB0DEzPurR7vzi0fPnpeS5sPB1FQlpaXlTESByXU9EEktqjqM226zzTkvx/tcKjJR8OScthqGEYiZyIVuGDc5LdP9XTod8uFhergzs2G7ZXa3quN27z0zmHds7BxvnXMivWk1YSJuKamBDx2aKapzHZmYCgCpVhMBE9VmVXGawERbRedUmrVEhE7NpIlzXomlNlVlgNzEEXl0YdhE72Pst7vtbju6NSGRU/Dh8vKSERHM9/3YZP/sw+zDseSnXd/M0pIAIHTd4e5BVYN3xl7UiHBKeU45BI8GQx/Pui61Zqq11hhDa03BtrtNLs1yBsBSKzpuCnlOCtpyrqXUnJZpaikbiA/hbL8nNBEZx40Bdn3nY6y5iqkpAMDrN9daCjuO/eAdGbJj573vd2NubUkVkAggdiGXJLqS1HQI4Wwcc6uAWBZ0jn3wqFKq5VJVBUXHzbB1uO+7+zm1Voq0vCRBZqJaKpj5wAiw3Wy247jWNJRS0ryw94A4dh2CHd58tyxJFF7fHO8eZmJyTEyqhoLIjEuqTNT1wVTXVBCAiRispTuAaqZN1/uo8wzv3DfmkaU1RBRmrLLmb9Z2jzUsjUhAROwJCVBFdF6mdxBWNSJH7NSAwa5fvpXUnj179vjxo0fn+7HrQj+UUvphGIYtOZrnJS2pLvNxnksqMviUEwxd//gDGTelVn+xMx8n55fT6fzy/OLq8vNPf3zx7Nmw+2lTMm3H+9s8P7Dv3pzefPydT589e4/7ITT1y/T6sx8j8Xa33ezPuzGTc9Phnr0/u7hUhd/5rd8otW63u4fDQZoA2OF4fDgc+hhj8GbWWtv14WGpVRoCrhnMVWgUESIEs7XBxHnvfYghfkkLUkRj513Xx9hB7NYblgEi0+bs4tUnP5iXU1MDhFKWV68ngxerGxeBxuhH9sFmj0Hn+9LasN1KjP24bRYe5qpq05xM2zgMH773ZAj+cHyYUuq60QxKKUAUQkjziZ3bnl3FkQ1Ra6W+i/3oOyOi1pqa5pQxVyZWrc6HWnLoe0IKfed8YMfSCgIhQZqXBqWW4kLg0O8fj0xcS2GmEIKqtFZNAQj13bBJNZc4boftdjqeSlqI0HU9MrUmCIIhtlrZcezH0A0Iz8t0qqc7SZNJhbacnZ2Hjpx3XTeo2ZwLALZaTBuxA1TnA5MDqVYK9QPUsl5sPIGBAiERAzhUJ00VGhBRP4IaIwAPZuYcatiNu/0WmR1T9KHVBmKeXRdD9B7Uur6vOUvKceMc+2M+LYcleAci/XZbDO6nJavG87PaJMK7FpzSWozh8eNLAog+IMLY93PKrJpzNoPYdVXEtaZNFEFUNkOHYAiIqgjqEAXUpE6HnHJO06ks83w6Sq3OR98PahpjXE7pYUogUluBlZPJawEHbPdnRq7lpE3AjJikFtcShWChP9vuvPfb3WYYNtraotoPHQDX0hYARGBEQquleqKzoS81l2VhRAXLot65udW3X7xW1YtNv+k6NBm6btP19cv15Xrp88Sd49YaMPfBx01fN/2cFs/oCOrp5vqT7xPhJ59dv747RecBrIhSE0TwzE2UiLzHVKoZECGza9qcI6TfQwHpWvLqPCABgq3Cv6mt7WQAq4pP9E5jEwAkUhDvvZmWlERljWiu2WDnIxqy82v74us3Ny/e3P47/+of33zlp75IlEs6pjLuzhpCqdNuexH94rda83LlfFPwaF0Mfd8NZxe+G7A2dk7VIiCS1Zzykij2Syrq0vxw3/Udxn6zuxhy+vzVmzjuwA/UbwDhRz/4wecvXjnn9xyIaRxiWkJKgUP49LPPTKW2CgalNABUbTf39/O8DF1vAKLGjqsIMz672Ly8PRQRQlp9Z2ZA5M4fPbl+87mKIGK/vdg9/UiU3OvPHTl27uzx+xfv/9Tz995zgAnZ/7iTVg3t/v7mxWc/aqUgk6w+PkAi/okv12EtWfJRrr///1k224ubp+Pu4qYOz3/q558+fX7x6FE+wVU3bo8xdPH86pEL8eF4bMZQWisHH0M3bvvNVs1C1/nYiTQA2OzPtFUm8jGqNjPs+6HUrNIQEEzTqbTanONyPHTbHTERAyC2KoiKouwcIfkY87LUnJwLzSoxl9qOhwMzWpNWqphKy924YeelNsjZxy7Ejpl1zX8hETMgIBF5T4iAIK310Q8X5+OTyzFw8M4QXrx6+3D3kO8PANdiCNaYXIg9IBAhAZB3uH6jaLU4YiAEViMyExc6yUVraWnysWcfAMHEgNEwQE2+G90Xn3xCYpv9GTh/eXU+OOJxP+cGZuhDKpWYZZ4lZ+r7eph2e96dn2ltaZ7Bh2wCjaEfkPB+Wvq+r7XWFfGJJKK7cRMYfQzrdmzXd810CV4N2bEnX0si5CXlaVmOBz+djseHWyLXWs3LYghlXojZhzCfJhe8AQ7jFokM0LmwTIupxmEwYscu9iuj24PBagvU2vI05yWFYUilgkIxT0J7pm2gt8eH4F0wDT4M221ali74ods0kVyKY9cxzWYKqGD7zea4LLJSXVTMrN9trs72r67vGrpGbMC1GUFzCIAIhK1K8E5MW2u51tKap+308HYTPOUiYqJyfPlxOh1F2osv3qZUt5cdIbQqSARgTU3NUEEMsIpfuy8Jmbk1QYPaWm26HmQrGgjfQRlVRNZ2JmlNib8cfuO6YwNAQCHmWqsDaqWJKSAQM6g553yMrama5SWdPX/y5/70H//+9z/+9/7j/8d//1de/tKf+nOvn3w4gZPWgMx7/3B7fby7S+n0cDwx4tgNYM17V1vr+uHy8or7cdjsTQUJ+nHbmoZx3MdQUj7cH1qVMPoVNFZEP/rpn/vwmz9bSyHnHMI+F/FbJigp/fN/8S9Ox0Mr9fHjR7vt/tF7w9APNWfv+cmzJ59+8uLt9e3Vk3GZT8eHg9ZWSgWzZppqHbv45GL38ubQRN6Z7QCqtOk0n6bTel9DYgh9f/7Yuw7QDHieUri//6zKw5vXc63zstS66PpmevSO86qLGa6tKKuTLbK9t6HXM1zPTWyqKZXDbTL/0S/+N7TWj549vnryJHdNVYevfqUf+ynl1qRXM5U4bNhxiF0TCV0vquaDmQ6bkdgjoQI003o6xhidI6nJExmHFRwzbPc1La3WZT42g64fy5IoeoaVHJW6zdiaWHsHbV/miZyLPKLV2EVRAWRQ885z5el0ImIm140jvguuG3+5QamlSCuFXOi6pkIACLDkVJCmQ0GEuqTz891uv4/eHx7u8jzXWk6nh1KK7wZib1K9d3Gzc84zgHNcWwnd4EOU3KQWcr7Msws9GlCE2goBIBIgYq0r67wpuD/xrY8SeGU/dP5sOzrTUyMN2NSMnR9dF3xExOUo5MAo1VIrOu81BBdCroUYi7YqWEtuTYJ3yCilVbNhv717eIgxDmKeafQsJiDWETVVqfXt4eFwd+dDSCk7pvuc52kCZrOcUwIw5z11A4Ll1uJur7V6H0pTszb0vQ++5NRtt10I+912GPux6x2zEROxtlZang4n18ru+RPysdRSWy1ViQhqvj0usd+Gzflc5g0gAZLzTSCX6hD6EJpKqrbr+7FzYjA4yNrMx1N+N+D0Zpsudk8fVVOsFZCJyJgDg6r14qw3h+CZbu8PPeOzq13OlaOfioAL7Hxe5pqWXFJKGX6vbsOcf5clFJUmRqirRbaJMaGZrlbPWmWFUAbHMQbnSFUFhNkpQBNBgXEzqFlrTcUAV78Ar3QaIm4iaCaS1FYXmvpIKzm61WYGjt1S50fn+0DDX//ln/+tH332H/693/jH/+V3/sZf/LPf+uU/9aLbHquKaK1tqbmWWnJd3Xyt1SAiSqlOr16/vbx69Oyjr4YQvA/zvAAyqnofiJko8/oZ8F6tEOI4bjiGZZq0ianuHz07e8ZoBkhPPvpKy6WV2lqNMcSu64fewADw9u0Nx+Hxs0iEKaenIjktyzy31mot56qtFFuWs21/OKamiohgtOT51YsfLqejSEVEOty9/vEPrWku+Z0t481nau2eoqbD/d3bJZ1W0zITNzFZewYA1k6aFdPNAAR0vYAqMOFUoYoK4Nvj9CzXl5/++JtXQ2vWB8dMzoW0pJxKiD4OQy0lzRM79jFudnszja5X0VqSmq3GZmZSEQVQlVJaqxUA+nFDxNEHMQ1MgOi6IU+naZqG3UZrI+eQeNjvHZOwOh8AQaWtP6rk0mqV1gwsxg4QybHjwfejmVkT8s6FoK0JExOBqYgQE2FgF9bNCYioak0ZAYnZ1vacF68dQ2RgRlPpg3/04XMA4tDND7c3N3M9nVKaDJmYnPNdPyCApMlaamkiwDDuQ+yUPcQAaCpgrRCzipOaTNhawf/o3//fvbhfYvRj9FWxGRhS7OJhSYaMZqLmiDqywN7UQhcR0NTYsZgVE2Ne0lqQF+elVtVcCoXAxM7xOsiLGBNt+7jkFJ1jMGRkDvOylNamJd/f3uZldkStFAHyQ9eaaJNaMnkfY99ttl0/lJo9syMaulBK9sy7/X4YN47QO9bWAJCZ2DlVTalIq8TIACH25LjV4gmLkUiLPjjHJtrH0AzMmgOLMUqp1TRX01YGT+ZCKs2kOR+9tcfb3thPuXSOOu9MBAAULCmU0kpOTDxsNgYWCEttVS1NEzm31NI5d96HORWO8XQ8mVmV1scotx+//ME///GnL3/02VtR7LpYW1EBpHfytKg5jwgEZjE475gIwUDNcimICIDeUfSBCVb5a+ijY66teeZ+07cquZSVwBG8Z+aV0+18EFWRSujIMQKKKRE7x0TsYxc9T6mVXH7+61979cX1t7/3oz/+R3/h9sXrf/Ht7372409/4SvP/sKf+PmLD7+yPPvGm+NSa2ZiBj0dHlprzrn10nF3e+vZXzx71g9DNwzjuBGRWlsquaYECMy+idB6KBCJitbmQyi5LMs8bDbEbCLLNMWuV1MCAMdpXgih1Sa2ovkFwIL3iGsyuh0Oh9ev356Op5xTyTnnVHOdl6m1mpY8lbLGwlRluz2ruVSpCBi6YXf+BAAO1y9KSQAYYjw7uyp5OR3vU07MBPCu7kkNzBTeFXCsAhutgMOwhrzRxshoOlcExDmXn/sjv/z8vQ//jT/+/NlXvr6Az6kcsjzMqeSsavvL81IaI7Jz6+o89L0hIXrVqirIzgChtSYNibz3pmqqTRohheBBhX0EQseO3RpKk9ZERVcRFcG460vKpuqCRxUy4xCYmZhbrdPpaKt3et0IlaIinpmZWi3rBizEDomsCjpsKcWuVxAzzMtiTdhhXmYmMrUw9FKbtqoq8/2t866mKXZhM+42+/0wDpJmRlQDlbZMx6ZG7ImgplnzJGVpywlAw7CPu8s4bNCUAJRWZlq1Wnw3MCH+L//X/xsmxFod4dluIwBZbNd33lMFXprUJmoUHYcYeuahi1JLzmW/HYjpfi73ub6z66j5Lq7gZiPKpQqAiuWcOERGi97Pp1nMtmM/RE+EuVS33h9TrbXmWk6HoyLutlsXPCGejvOwGUOMXRfAIOe82wyB2DlWtVJySrnreyAys4hIqMQ8zamLgZ03EWJUUTFYvZ/StOs7JkQkZtQq7Kgp5JybioiNXRy6bsnltCQ0Dd577xFRagNC7xkRmSgCkpSpmZmlvCaTIDjene2YqJYSQJpCAySkUpsjQKTampqZqpXFhdjE2DmX71799j/+3vc/fjjl07wsy1qCDGtxFgI6ZiJQQ8/oPRORd+SI18KEddZ3jhgwxBVoSH3f0bsIp3rvmqiIEuNq2ida/xLRYJVU7f/P05/8WNZu+XnY6t5m79NERGZ++TW3qXtvdSRFWmwkAZRokRIEmzIsuBFAG7DlgQbWyAY88sCGRwZs2VP7v7DhmQcGPJAgCaJkyqRYVS6y6tat23xdZkZGxDln7/12ay0PdpI5zEECkbHPPu+71u/3PI6YpsQkqp8cuhJijDGir83c/fd//KN/+k/+/Jffv/+X/9bfQsUY4uOf/tP/+j//L37zy1//1sPx3/6X//Lv/J2/9Xz/4+eq6NZ1mOPoXVUf7o6AqMimjmCt9a3UEEIUISEzB7PhFnKOISDiXgFO8wyIOsb15QJuISft5qbuHqe89yslxFK2VlsrpfeRDrP15gDqbg7v3727vLysy/L88clUzXT3h9vQ3lstpY9RahtD1QzcU0ilV9gV5YczIrWytFb3DQARug/bfUYIiET7bweRmREJEQiRiEbvgsDk6ijMTMhMgmDuaq5mMR3/1b/2l/+l3/1ypcPd2y9QwtZ0Ot+Zac7T9XYLMQKSMGnvXQ0AjudTnI/WOzCFENy9lVJr633wLtYcHUnMTHX3e6bArO4iIiKEHkMY5qUU08HMMU2tbGN0lvCJewHe20DGIGHfWxAhII6hZV0AsV6uLhRETNXBYppijOYObm46+iBEIgwxIaBq77W0uvmwkLK7OXwCZI7RCbFvC8FQd5HYtyuh7P+PTBCmmYi0lb5crK7aNm0beXM3kpiOryQfJeYQEzGbdlNFMBsq756e0DmkYK3eXp4f3r69dnv/dAkxxcjuGFIy9GVZaquM/Op8jOC11tvT0/k0W8zaGyIBy7qVM+E5hXkKax/bGGMoieSUEDFPE7jd3Z1TFB29DiWEpn5bt9OJcs4xpTPTm1f34EjMQy0EuTudeCfkITBASKFvpZhN0xRCkMB38bhuxRENsLS+rYubHY/Hx5frNGUhyikgko1+dzgquwvkFNEMCfpQ9R0ApTGIVaBI7l5qRabz6eAO6KajA2FMUmptTQOLErxsNZOWrQBzqaOs9eHNnSFebwsRja4SOInUsgExInW1LGBqSBACh3wvzDvVw6PXbiHl0ExaH1r2lT8RAsAYaqq9KyC5cR8mgm7S6RODZR+fuSMwEhIRCTO4l1Z3ikkfA/b5AqATm1kfivgpmz7G2Ge3sEFK6OCmgzkBoLq13m3AF28e7mf+5vv36PD08SWn7ECvf/f3/95f/Ivf/eIXf//v/5f/l//oH/z4D/7kv/ev/ZWf/s7P3j38rCu8mkIjLDg/vSwOkIK4KyF28+kwL8u6rktZlsPd3fnuPCHGVntdfT4xo40GlkhCnPLhcKi1ltq25TnEZNpLqb12B8uHoxkYEYV4mGdwaK2a+zbs+eV6XbfSuyM4Qmk1iJh5b+MTxUcEAGJKaTrMx+N8OiNQCHy+vze1p48fvvv2ewBF4fzPXrKH4/FwPCFaiCmGAOBlK+42TVOMqW4rs/TWMrZXPLZSvn26ff1xRQQW2mvbAigxnM93PpZffPvhsT/nd8/5eOYQ7kqNacrqQUIbSlGGegw5ZTTV1vp0dM6p1Xa9XERknqa782kr9batCD7UTLvgnjdydxvqkpKO0XqzMWKQnOYpT0O7da3rojpCDAhIQUwN3ac5m5urgyowqWGKEd1pnpD5eJiHmUgopYBZlADgQwdKAN3RGlZLrbUhoLsi4Xw4melOkDJAHUNCCEwEyAi3x+96XYnIgRAbujOiauNrJCJhcm06xujNzMkIwb2Pcn2cWehwarUScwh7GNJAGP+D//X/dpRtjD7U+rrlaUIEcKTA5phiQIkGwERpmoAopTjHUMv6/HIz89dv3qjZblhtrdfWYs6ff/bq/pCH08fnCyDOhwMhEOJhmlSH7Mk6JzXtuhuhkIWZeZIgQQgscGi9m5uZphgZvJu1ruZAhMI0hg8zIjqmWFrlmITJ1dpO43I3pDknIQLEbSvCNOWUU0QHdSu1mhkDUghmFpljlForE+ac3X0MHQZqZg7kykymllIcvdfW9xAcmuUQlBABt9JL3bSP0+kQYhDiWpvqAKA8TwSOAIl5x5w7IIMTASGR0Dd/9J//wd//T4v6VurLy3UrHRERIcawC+V6b74H9gFVnXnXljsLgwMRiEiMIcWQhHhPHADUWkOIap8mayEI4j8Pu0MI8ol8j2QIMUZEijESoQSREB0I3H/n7eEY5PVnX/xXf/SL/9d/8o9utSGF3//h27/9N/+l8zS9/a2fPL1cjyH+43/8h//p//s//u5P/+y3T/L3/u7fPP2df3ujwzzJyza+e75ta9mZJa3WIPzm9V0O8bpsgFAvlw//9E/qy8vTt98i02d/+18nDsfTmRENgJjLVhC9D1vK5o6HeT6eTtt6G2Pkw2G0vpW6h3rJ/fLy1EbblFOe1qW8vDx9/PD9tq5gvl+qx2j7RZAQX795/bu///vnh4d1Lc6cRHw0ZAohiHB3Xktf14K2PwVovXGQ3lqe596aD0XCMUpdt2meCGBbFgMA7XdTOES6p/6Pfv71N08LEpoZOgwb0zQfz3fn4/l4OISUAGmMAYgppulwBKYpynycHz57G0LettWGnu7vhamPLiFGCX0PSSAjgqkyk4MLi5lt28YiZr1t1YaeHh4IIeY8+lBTQgLmUdsnJjWhsKQYFdFUR22AYAhojoimSsxRGMHNHZGBUHWvNgwYBrhP3juAhxj2BgkiJaFW2rJtqr2VNXBAZjA7HGZHcAfX4eAhhHq9PH/4bg+UoEOvSyt1zokIADFIcFcfXftmY6DbDjYS4ePDZzKd94QAovdlURshTfg//vf/56BmCPuMiT8tAXE+HQDI3fbALjOfcgwS1KG3WmpNOQtD78YiFBK6nVJ42upwDEFen+aY0rWU29bcsZZiZvd3JwKIgduwOSdm2VofY6QUkVAN0I0IACiFoDrM3AElMOgARAc8froegiDDjtPTkXN0g6G6E2zMQRDUTNXUFIlrV3Ko622ep/P51Ht3Iu3WeyUmQJjytEeQzIxFsoipxhja0DZs6P5L9CwkCEDU1RyglMKIOacUI7gPhxQCIfTWa9mGe0zJHUw1MquZEAlDChnBYwpl20S4Pv36P/t//t/ffbyGGM395eVatmLmIQoiAsKOAEUkd9g5hbuIgxkZCRBZOAjlGPIUQXVX+u0Gd1OTKAhoZsxMLPvXNhIFYQAHZAcw0/1CGWMiohCDRAEUVP03/8ZP73PSeP4P/6//t//65788zMcfffbqX/yLvytEh8N8nLMj3929Or160Lr9w3/wD58G/LW/8Dtf/uyneT6eZ8laH6/bz99dt61FBvn43Xd//Cfv3r27uheDk/fL9x9v19t1Ss+lvU3yo7/6L7Z1m1KQkO5Tmr766qLmqqfz3XDDkNw9CK/LtfURmThkZHbtOkbZNkRqOrqCOezzsrqtTDLPKU85xKStgFsvRUR+/OPfulwuqoNTOp1P27L2ruBuo4aY57sHByAJ7u5mIOH28ly3xZ0IoZZCiCwcU3K30dpe7pEgKScg1t6D65cz/vz721LbdJgkBABMzMfDnOfj64dzd7gttbW2bav2YdbruiK4mzHj+XR68+VXr7/8AXLYe2xm0FoPQVRHjEGC2BgsMQipqupwAxZurXU1dGQGQmQEYmq1swiHYGO00bT36XCwWgPhPE2B3IB3xcxSuw3dN0KjtRgD7U04JjBnkdG7mYE7gLuZmhHT3lcbY9gYSERmCEYhurkTah+9bDZ6iMEBQNVGOdy9ArDYNxzlZSnoY13rVgoH8TEQzM1E2Ha95ujMgowpxHQ4AzIQjbp5L0Ac84QkEqYppUzMgBRSFOb9GU9TdnM1q+va+8t8mMrtdjydU4zqxCG1PoZLrf0QEgE6QGA65XRpWoZ98/hynicSOk8psFwYnl6uj0/POafJMzFXNQatrSKi7K9RMEAspW615RgPh4Oz6zByNwcbQ4eWrd6djwiw3V7ynJ241jqVcMxpEqlDibD0jlEOTBzjc+m1dwQoY6ikBlyGzjkzYoXmKOiQU2y1ITM4uHktXUVNh5oBUo7BjNysmy7XNaQYBZIIIpwOD6Pr3oQJOSZEUxujxxRzTvu4AR1ab2spqkMNOczNB/VOjCFNOMq3//Qfla2oGqs5YgiinXdH3C4uIQDJCUmGDi8WmAxhT40ZOCKYmRuC+x4pMDMdigg7PXt05f3173uFzIMIuI+hBhAimdo+PHZ3QJAURAQo9NYm4SlKR1k6bsvtf/R3/82/8Zd+Z5SNgrj5+XR6WhvFmae52/hY+2d/5a98kebFxp8/Xeh6s1rz9eP09PWXd/e//PXz08vtP//Hf/DHv/zN1bURB/MT4RcpOeK1j9r617W+/Gf/RSBYJYC5XC8/+st/+ad/828h+mXd5uNJYiprUdM4n+eYJcj1+eJuo2uajoET+sA+oKsaIrWHV2+Ox0MMcr08A2LZltEqmn388J6IpxgdtLSqj+37Px/T+Xy8ezMc5sMp50QxlGXTWmOQ3tvxfH8+nXpObtZqCSlsa9uWlx3AO+W5lXJ+darbioCt9SnlKYVHh1c/fPWGaWdapBg/n8Pr0wyAH6ueU7473zlJG7qU8uH770dtxDwdDrWs33333eOHDz+r9ce//xdrqSGEoU4iHEIfvdSGtbhjSAAQ3H2ojW7Q2jRNIXIrm+oAkeXlkuZZRMAdAWIK05R6qa22Vgckebpc96PA/XG6P80EuNKw5iIiKZqahDBGZ0QgAFMfo9uwvVmvigg8cI86MjGQiQRwq2VFb8QsSGmeulCvdYyxk1dGa6O/J8YJ7TDnN68PMZIbXK/X2kbrfTjVbXXrrqP35goc+Pz2yxhn145uwEIkmA8UExJq3eT152/RUZhrrQCgfThAyIncT8cDijyHoGoKDkybY+sjxTBLvJ+Cur+XeGvNSmWAGEKe8inAZVlK1+d1zdOUgAjGOUfzkwGqGQAys6lNU94HQ631obrn3B1pnufRe2lNmFJK7mbqABRTZPRa6tYHAbBZYEkpL6WU1l8d56Gm7iKBOH5cFhHZUygSeE9e6VA3vy7bthZ1m3M+H9JwkBiHWW39/nRW09o7EO/5q2YWEIcBOD7c3ccUCGGY9tpHHTEKCX/CyZjtTMR9PV9bi0xTislJprnJqKO5WSll1AJbzSn3l+/rugCj6mjNJWViQkIGAgA3R3IWYgIWQg5gXrwZudm+QYP9LQXgw2zUPdYPgIDuoACAzND7CEFsv7juYGhAYASEMZSIkAX2tA4RAnKImCYFSpGJ8HbbfvwXfu8/+Hv/TpjOw/rD61cAMB0OmGe/bavStfWtbigizNeXRwBDZjc3s8Oc/vQf/fLv/yf/jx8c5o/P7ZmCnE5Zx9QHp/zAsC5lCC211TEIoGhFJmaeI72L0xenu8DkIQQSBYjAx/MZmJF49D6ahhgICfIMiBwzaGsRwn7+JLjd1lrX6+Nza0Nt1PXmNlxNYs4xfXj//es3r/pQDtP5PJXl2tva1dl6X17CdAhxmk8nNbfR3U3dQghA4e71Azq+vFyvMYQUCR2A7u7ulrXknGOIGObWh0wBx2AWFgHXOfAs/Hi9vbtup8NcIcqo4COmycwOSeYf/aB99hrAFSgEAe1pmiQEdz8eDiHGVsswMNPDYUYi7WNv/FYwJk4xaVv3ALAEDIfJAcwsvnpgJDQlNyRolxeKfDqewxyQhJnMxvNSt2FjtG1dUeIUYxK6rQ0IWaTU2moB4hQDAZJQsv2BcVdDwv2cAYTsioyunUKY5kPZVqEAaih2mOcmsleDe2uMd72sFGIpa+3MY4znYnWdUwjCISRCqgG6gY4ZbDCiCDEFR2SR/UCIRByS60BgZpbM3HojYDfbV7zMlOYpE90nOmV+HU/fL22pffcheu+fn5ITqXkQmgLV6qYGQZ7Xkh1yCA+nyU9zqX04uPtaK6h2w3UrccrgPmrNMd6WrdUaRHKOOWQHDyIOcLve8pznPOHe8VeXKa/LOrSCkBDdHQ9laGkVgCJRjnJIiQnVISMB0fW2Docco7YWUmxdGai1jrtZkvH+4bwvsIZZH7qu9XQ6HHKSQKM0Yc4hEEHXkUmQ0NwjSyBBAhsK4IAQYwhBAGDs83U3QGbhWtuw4eYapLUuxAQ+R46Sam0xZkBqt9u3v/qz5eO7oO5DwUFVl5crgKl5a2N3XnbhKLtPZDgR77RnM3dnFlUXIgnUh3rrDkBECIZIzIT7Zw8AkdScEcGdkADZfBDs/4Kqqog4kAOZGYrQdAzToTtNU2jXRWEWDC8Vnj6+d4dJ4PO3D8/ePn7/0oY1GzIdiXC01SUut6uZPz8+qVsQihMgp1/y3S8LTKTNOkv4HGCa80VCX24rmAFExyzEQELYQzwe8/mrH8zN318v/+A//o/+pb/1t3FOeZokBBZat0JsPgYKC+c+lJncIQZhmAI6mI3WMcYMECIS8Qz47ptv+gAmOd/fHY/H6/NHHUGm8+cPn398923Ztul4JOIoEYSG0tP7j2+/+mFmAYTp/BAY3ay1Os+hlhqZ37y+Px2Py7Ye5rwsRREPpzuBnnMuw46nGYhqadrbFHmtupRmQfj0EEWYCerYtu045ZTSMHh+fkH00TWmFFLsZgihF43qy21BlpgyuyFxLQuoAUCeJwfiGPqywTA/nfJhqrUsZcPGCM78iYEeg8Qg5BQIcJq6+rKsxyg58bpuc45f3h0HeNnWUjsCai3dBgCOoRwEiRjZiWrvgWgKGdjH/oy6MbAjCtMhhZDYHK+tg6sBpHm2Plqry/U5cDic71hCWW91WT5Vl3QTliyMhCNIRVj6QHP2Yb2iK3FgJuBIAH20DpXKpjaYhBGAEMDB1K3b6HJMUtCRieWobqqeQrw/zMH6UUBb064MsB83WEgNh8NnxxxDUPdb08Pp0NogEWYiolqrQDwcpihc2yitOXNzH675MAWiecq9DzXvve3COkTKKTgguPc+7u/PZuam6EYiHFiYbARr/XiYWGQM3S7L3d3dcHhebjlPZWgBbKWGKO6QcgpO19tW1yUdjiJSdSBynlJtrdQaRIR5jjLUWzeJYZgLkfZ+nKfRR2ltraOOMRIKk5mV1glaDpxEEosgGUJrnZmcMKdEbkJibvORzXyoNx1tjLUUG+N8PE4x9lokUAy5aLdXr+5fv/H68de/+rXZrau/XEvTAQ616VDd07JMezcTkVCYP+ECAZB017XUgTpU9pkKmfCOnN178Ajg5u4u9CmCgYhogA5gwwD3cx26g7srEqccpgNwdODjnP/pn/zi85/87uPT49s3d/X6q7svfhByXNSX69LGAISug3xwOlLMyOHtDw/E9PmP/XZ5+eUf/MG3l/qjL376P/y3P//mu3ePT8/f/Pqby9PHCLSBLa1aHwVAxuiOApDIlUN3649PerpTpLqt27DrVs93r4HZiT++XISl1y1KSMTLuqWchbm2ttZqZmM0GBaCdO11K+guwpePj4x+OBxce9lueY7pOKdwxym8f//u+++++62f/vb9lz843T0EibfbjUUePu/pMPnQGJARxxgShUNSMzfYxhi6Hg7HNzOj9tfp7qmO6oiURkjEKimAw+F4N8YgxHjw0boTlVpxtC4cQ3LHjnwrnTk081E3Rs7HuCxrDMHc67a5K1EAAKbNECTGutwEab1d0DqHgEzau/du2j//6svD+S5Px73W1hWRRUJwt7UUNk2bUZqvtZa6PYowCQDC+4+B8Bj57nQ0wOAjszsQkiRCEl6W1QFGH0jYTLEWCWHdapwymg8dO6Pl5XrDUVmicuhj5JR435sj1To2LW3o6Xw+Ho95ysttMffR2vL8vAbO00xEjhhzAnekICnrthCxu4IrSgASN++tIVHTRgDgTlCcSEdPkQV0MMAYen88iPDaxkRwD/Vlqx9WayTXAYAISKrqNo7zxCKt9SRyWdZlWSFPJDzGYIrmLhJqVy7tMMXEFAJvwx2ZzOZ5GrWNMcyc988/ERM19X5dcoqBOYVgbq22NgbqiCIxhKI7w1SGWh+NiQDdtLv5lHMUyYH7GBQCENdtI5EpcY7zyKmNQeA7VysKgpG77NrBbdie2OrqO67DwKwruOeUDvM8zBFxjLGW4QAsvPWxNg3CusdcdbALIrbRhBnBzI3A9mlVQBKJS+0h59r7tm3oqrYFptPdmWPsrfucjm++fLy1shYKgRyGqiMYQB2uZg5K8MlbLrRPJhF3KAjpDhakPWhAOzoM9iSnBPpn5FR08B1Iu4e2ANGYwPdhHzAjR04SkGNTj2qIENP87Xfvbx9u39VfXnr4G3/zb4VXb59Kf77durWBMEblmPPhoGBjNOSAOny03rybMqGjP97W2y9+9fJyMeKPl6WbgePNhhoXx20oI6CwCydEG6pbZTOM4cP3H8JxZpSvfvf3+NXrtZRufbst8/HMKTmCASpQyBkQbuvKEkpppr3Vfnp4MNPeWiktT1Nv9XK53m63FAO5p8jb5ZnSYbnc1tvLcPwr/8p/87O3byFE3cEsEsxsPp0AgWIEwF6K6dhTZnk+ttaiEAGU2jjAMWYfejjksQ1yS8TdfAxnwlrr6TCzj4B+ujszkQEsdVxKrbVzDGo++uCux8P0ie3rLsyOGJjj+WTqhyR3EVwSIAchtrOQ3eqbb95/eLosMEa/rfnhYZTtFz//c7Ru2lNOHBIgTtPExF+8fj3f3RdViaK92+gEvi6bpAnBtucXDOGdA3+4chJtI4RPPMQoDKObeTqfEwCDb9umBsE95rBHEU2HqoEE4ORGpkqubrSWNnpLIR6mWQBab9rH5eXleqUc5f7hjY+6u36Q2FURzUf/xC1x1NbcjeIUcray7sojJ2gr9qHMsidDtRUE4hB1NPw//If/x5zj81oRIEuoXWfGH95PHtI3l21tHdNUSkUJaH48TG/vDhPY/RQY4etr+7CWgdLUSikxJXSfpwkJooTzIc8xuNvWtKoPNQkcQvjEKQYzRyKMIiSybltgqqU7ces1hoBAXXuUYMstxtTBppxTCATW3bshM885CMBQ22pVxx0bjYAS9lkkqikRgVlKyRF760Iw5ZSIALyrvSzFAFJMvbUQJKUksOdZKbqe52yMaLY17e6tGwu6E4EigKs5uCEwcesjxGCqKSYdfagOHZE4SjCwbS2ltWEmRNba3f0pihCRtvXrP/vD//q//Acfn597H6P3PrqZmVrr3cx612EOO5UMcT9z7V7VT1ISphhYiPaMPYCnFBmRmQkBEAhJzdRcRFgYAZgIifad+k55ZJY0TSFNKGE+n0+v37TWl49PH3718y8+f/30dP2rf+2vf/nljzbzQWSqrr2ty+X9+/nhVZhmN7vdLstaVU2Yeu+tje+/f38UxtFNMiBJiJfLpdXym1/8so/x2Q9+PH/2ee/t/de/vr48i/ZgICSvf/CD86tXz+tSzaf7V7/1279zd3eSGBiJd9mU275A3LH9IkJM1g0Q8zy1XpelzHMW5t76tm2X27out9FHq1u9vhD489P7n/327z28fbvfrAHo/OrBTJlDrx3QEdAACFFbAdWQ8jB1t5DScrnkHImobqv2Eac5Smi9M0Kc5hDTcrvNOSIxMgdmsfEQ/PFaV46uZqbHKeac58BuJjFuqrXZMO99uFmKwRC3ZQW37F18zKTl+vzNt9/N5zOR4HQ8ne+mwD76w91xUFouz68yPV7L++fr01JYt1no43Ux8GWr27LkFGOafvDbv8cxpxgCszMPVQBYbwsRxpyFiETKugFCyLPWaq5gtjelYsocBAmFSFtfL7e71w8xxT36u2+c9kQBEgix6gDHWrY+ugCAjhDj+fUrM2/bOloLMZ7n9PnDgSS8vFyvW3MbvVWtDehTO91UR2scgqkBeM5p1IroUZBYDKAsq5nuIxdCGnWT989XtrHVkedJDvnukFvH7zuf0X77zd2U5NbtXT1+93I1UwTXPhSVMDsMIJrneWvdkTuzjhFD6KMjUR9j29bT4XCc8mlKs3sd1s0JcfS+KzPAXM0Jsdc6WldAA2BwdKi1IbMQtzGqWogxEE5JgpsitmoKMKwTQhZJIvdzasNfSiulsITRu9Ju6LCUWA3Wy42ZHg4zgR0Z5yiJERE/P06RQR3ePW6ufX1++fycIeYNyW63HwnIfFCKz7ftUsezQlcN5GaWCHKWNw93Y/StjrWPaj4A6rbqGJJSztmGDbfemoOfTkciUoDt+lLWdQGaxR6//cV/9V/+f14+PvcxwD3GAGCmYISA4EOjiIHbMHAw+NRcnnPctZa9m5vvof9PybVdksmgqmOXlJCb+lA1c1YjQg8CZkQkLMzCEhyhmbXaWd3XNczl8vHx63/6Tx7Oh+++/zDleTH/5ft3h9PJkR3USrk9fXx+ftpU0+m8XJfn50vTHQ4t63J1VS3r4XTEfJ7uHsI0IeDx8x8g4Wc//m0AwBCn0xmYfvJ7v+dq7kAAKBLzhMI/TbnVQgDCLISB2dV6707Sh0YJpi7uiKjmMccBwwG3ZTmcjzGm3XVStkVHz+zTm9fuXpbLx9Fjzm9/8IM3b96oupYbS8R0ABIAZBFzYKLWG5mHnN3BeTgicijLDZAP968QnFzb8ClTSHn0gQLqUMagIBJC7TrNwc2GuzBtqnfnE1Nqqn306u5tYCu7s299efn23QcEJCYh8hynaT6Cal3actu2a83pH//BP8FpTsWm47x8+/3hdI75WNab1fX1Od+f71oQcT+R3j1E6LDV9pf+8s9M9UPxb65N3dM0G7KOYYhGOMc5ErmpBNmWxVRL1wQg5Mf7V+bQEVRH2UovNeWJY+ylIKGxjGE8560UG32aM4EZAqKN2oHZzWvbiFlbQ9fT8dTq2nRoH+3DY45REEKQdV3qcoVWfvf3f/s8yeP3H65LuZXr3WE6352sFsl3GKb1trzcbqUU4tDMFIkRttrQapomIQAgFLHRwX0g4f/if/O/OwSpralh7eP+1QOYqoOP/nCcf/LZvfbxrsN12KjDzabjNAk/5PRmYkz5ca1r68NwKa32PUnsIYQ9U84shxiPc3JAQCCg4b7VWkqpXdetINE0zwieg8QYdxKLmbnZnLMiqFmr3c1Pc06BT0EOKTSHqnYttQ/bv5+j8BwDoJeqRhgY3WFPGmShAB6EA2ISPB0mc1jaAJKPj4+Xp49k45v3j4/Ldvfm9XK9kOsU8+V2c7MvTqff+fGXPznK/PoNu22t/+KbD24uKV0hLFsV9LFtD6/uX795hQC3bgoEROVTpYOGGaoDeRBx7cvtyjCe3n17fXm+fHz/4d2H5ba03m0MQAAwHap9qJup7a0p9J3cCQbuDlutZjAlQXdz36Gyh7y3yh12H8ouryZEIgfXoQCwt+2YSIIAuMheUgqSJ0lJncpWYp6Q2VWf372/vTwhYkrpd//KX4nz+XA6IaCOgciEULZtXbehQCHK4fDZ51/2WnurffhAw2HldultHO7u4zTr0JwTIaace29mgERm+/lVUxAw6rWe3r6JeVqXm/VBjCw82oghADrSnikl6yPGSEwSRN21m6CpKcUMDiGyqvbSurZWS60NAFUHOlyfn6bjiQGnw4GFtfW6XpDgsx/8NOZpjP3RBSbcq4iqujeTAEBVmbmVjkxjDAbotTIZEdteIXDnKPM01XXdNVT3U5iFbm2g23DMkYvxIWCy7mbPt5Xn6Xa9rqU+fvhw+fgohO7aypby5KMjejqc1+XKgKoqEtyd0VU1z3OaJnOEtp3nFPP8+jxvWz0G/kd/+MfX0r784s1/42c/enx8+vyzV6+/+OI3lzEodZk4Sq0VzKx3DjJPc8rTGM3dWu+j9d5bzrOr4q746l1SdAckBFURQUQ3q8sVwWWaD0HOSZLQtfS1VEVswzmwq4IDmhGh9lpNERjM+hijroEZiKCXOEqIadTy7t27pbXRCns9RhmjHY/nL7/86vXdnXGYppjPr94/3dZS2xgIHpj66KMWliASrBU3GL3j//7/9H/+0cNBwD9W22pjFiSUEJ5u2xTCm2Ma5ksdA7kDLGtVt8OUDzmFXn/r89enQ2pdl+HfXbdL1a462pAgwzSEkGKMOfEneAAcUxJBAWfhPRnQHd5fbmsbIYZERAAppmGj1EYOIYZ5nvc9LuGnU1tAOJ+P7GAAa6toqICGcIoiiGttIJyDDLNTSsdAvnMKzd1UXKOP2sevPjx/d7n+/E/+dLjlFK/Pl+PdWRjL9UYxkUgr5XA6pfmQXF+d7zLjD075y89fv5rTy8ttc7jcrt98XOY5O/J0Ombi+ylBnrrjVhswqgEQsatp224vy+V5vV23bW29r+u2XC86xk7muV6uAmg2mo5WmqqNMeJOvFBDAOJ9qsX7SeS2lt6Gu+2NS3MXJmE2cySgXT/IuC8N9omYCIcQ9vA3EoYQJIikHFIiCSRBHa4vL7Vpb2273cbwtz/+yenhs1evX7/64gtTDzEGCdZ7SqmVAuDdLB8Oy+UmKTx++21d12meJeY0T6Yap2m7rSIcpxkAAhMQ7fIBIpIgal62qqPneeq1hRhDkE/naREH77WmGACgj65mQjzGQPCUMofwyWcBNIbundN8OGzXBQmG6la21hog2lDcM3e9whhmFlIiZjQfvd+dD6dXn5mjueZpcnMmlMC9D0IAZELa9aNgNtTPk0TT5+u1Oy61kYRpyrsIpdV2vn+IUZbbrbV2iOHLYxLmrfWivmwlMCJg72NtvRuEENh7K/X9h3fEtC7Ly4f3vawxJgfXXpED6OAgEqK1prrP/nQ+nvI8sYQ3p9Rbv66btnpb1r/4W1+8PaTrup7vX49WV4jxcFqeXv7qv/Cz+/u7x4pfX9uOCQOkoTr6CDGMPnSMmCLtJRAAHapuO6QPzQGdiVtrrWwphvOUk9BxSjFGZoQx2A2JQkqU88f3H6/LtvSxtWFma9nQPeTsY5Tl1nu7vTxdPrxnoVE3dBOJ091ZexvbZV3W5XZLkVKMQpRiTCmLyDHHzz7/PE6HAaENdVciMRtt24iYQkTvIsHU5Lpuv3F7k+Scw8/uz2L9wzbel55Z5iSR7LMpnl5NyOHm9PXL9u1lbcOwjYnju+eFwV8f8g/vwptZ/uzj9t3L6gQhCAKjQ6u19h5EYhAAuKwLut3P6fPTnbi+nhMR/Fmkr6/1VltpjZm8g4jkaXI3ZtlqAzcCd2J1u5bq4N8+vZzmec7pboqR4f40dbWI3gwU43C6li6E17GWlJ5u623bLi/PZdtaKZcP77W3ZVu2ywUJD6ezNSHG5w8fgohM+XR3d7289N51jOePHyVEPp5Hnv9w0T/80/d/7fPTcx2XDjic8vGxW2C8PV7iYf664XnTE+uyXsp6DSEs27rerqOUrbUxrGwbpUjMOnqIEQDHGCGn2bxvm6r3bhJTQKy15CCq3dTMgNiIJMYEiBLkcOfX63J5vtTW9mmCmnV1AGdm2qtrSOaupkgYRPYDGgAMNVNUtByIgIq69raVS2/a1TkmycdXr758ePP2q9/6CYfEiIfjsbdqZhITpklEnENXvT4+ynQAkZByPp4Op3Oepp2K5qzCkg/zLiJGolLb8XwmFtMRYyThSDzUQ4oxRSIKMQEiWYVPzjdKOfpQB0gpD4cpxVrqGGOYjVqQKIiojjSnMbyNgaV+/PDy+vPP0oROzNGG6u35Yy+LBAGwVqvrLmlBd83TRASuI+SJOcGnK0topdTeT6dDjKGsW2sjxIhMU4RhNueU1a+3zZCDSK9dArvZznX8+PHFRvfRH5cV4X6O4eE8fZYS9Fpaf1e0iedoGZEIe2938/z08rys6/H+TZ7PH37zq1ZXJGpdA7Cpt7GNy5UQQwjYFFAMOKTp/pC37bb1jkyl9jzP71YF7y+r/vr24SHRosv1u/fzPL//+LFeLz7KF29+65tlMLiqUUhJ+M1xWmp/vlytVSOcgqQQPIiD71V57z2n0Ax0zmOl4Z6jAOCt9Km185wUaO2KWuvzlUJcy2Y2LrfS1eq2lXUZOrR3pF1MYcTihL13c+xtyBi1bSlGcnj96nXKR7VOOlqvZm0opHkezW/fPSW5nO7uYgwhBmT2bhKDmgP4GEPNEUFK7aZjXchD+P7p9qNjqI7X6uuwx+v1Q5D7Of3+6/nVEZPQBvXRx3DcliUe523g01Yft3bIMRImoeOctz7UDB32qpBI6GMgQJ7S6INCeO4Wr0sCR7PfenP6nVd5jvJUczfo+5NqNtRiigDAaCFNtamEXfgdd3y7AlSzx7WC++OyTjHlGJR46Ta0O/itqIO1x+dvvv6NjdrKtjw/l9u1LDcAb62ZOYcQp2n03raVYxy1zIFq3YTlzc9+B4nv7l+FGLdlRZa6XTmkP3jcxtBh6kDgOhFNr46R/BAxYoftw+PT+3cfn9c6DEDdy7YyAIaASBijxMQIHbH0oUOZaJpnRxlmUxSHLeXIjNOY0Hqv2FrDvW6Zc5zmAYRI2Ho6MpbOKG1Zzcz2yggho7KQMKs6s+/nWTXcmoJbCAGZEXF0azBsWfZzewjxix/+8Ec//HGejylPwnt7Orh7nqY+dG/o3pbl7u68lY2J0jyFS+xbZaayrvP5LMKn43kMHdpbbSJhOgizmHstFdL+PJMbScq1VmbiEHBHdcTQSs1TVlOJCRxaazmHocbMIUiSYKppSjKCg9vQNKVSaoiTCDPDlJO5vf3RFwjeWnNHCQEAJE0AdLs8m6owAkIbI8ac83Sc5zFqWV5CKxRiPp7ckZlSCuDe2nCzndfY+5imfQznT0VTym9Senx6EREGoBBqbcRg3qcc3eN6vaWUi4GM7hWI0REPx8MDtXJZh6sNV1cbPZ6OP/vt3/75z3/RWo/TfP78q/V60dGGQe/NhiMyB44piQRiIqTT/fmrLz8XHe9ad0U1k+NdYMbp8Mtl1eHaytMC14/vGCHG8Kc43PAXf/yH/92/+2+l+59suvPVvdTWh0amc4prqzZ81SYcbss1hHAQToIft9r6GA4ofDcflnX79uPCOVrvE/nj8wU5ABO6vX+51dZVbdvWsl60tV7LcrmUWuf5MB2PtRRGyFHK5fn28pxSTIwS5PWrux+8fX13mqfp2Eg+Xm4wOvZtqf2laD6e9tlKb/X55QUB5uMxSDQbIcYQk/XOLKU2RhTvbatjUavI3+r4U/B5mgZCqd0dXoSv1/DxI/744fz5w/npVnpXB05JBlhDeSp6nLOHeCll67Z7c1vvrbU8zZKTqnfVUrbldospAjO4r2tB8G9j2Pp4O8cvpyBu/+T7F0Ywwl6bxbyWKkynwOAqjLMQMwqxq3X0tbS1KTEFhkDyXNeU4vl4cHA1u63L9XJbXp4ev/tGTbVstWzr5cIxmEMrW2/d3amPOB+Red1qUCNhWsqXP3w4f/aWSHBvmI+ViNoYIU97AitliQAR8WHiNwHm2ce2vv/w/lffv3t8vqxbZREHI5b5/r4vWxvjmOfSOhHpp0tcPd2dynKbDtmJOca7h/tem4QENlT7NOfRdrQG61AiAhILeVm25XZdrouqXZctMEPMBE6wA2cYHKbjtLtsCcABJEhv4/pycbcMIgYchDDGeFRzwXiY59/6nd+9f/0Z58zICBBiNDcgNrO9RMyScgh124Z5G8bkkcb54X653tCAiIF4DKtDl+t6vDue8mRuwsIcgsh8NHPQ3hBR8lRbc8RaW0zB3JgEHW/bZSs1TQEAaq8ppaHGQUTEiRC8t8YxSYyINpgRSSTklLqOWjcwK6Wkabq8XGMMQBxikhBOpxOczuf7+9v1st1uQ0eM+Xg+xiBI6G6qvaz17u3nZubmQ9nNHLwstx6E9iWVg6kTIjKx8G4w3k03lOLoY6dIlW1D5BDDfJwkxDFGcfj+1m5r/dEXnzn499dt3bZa+7audV3PD/fbuKQYf/Sz317W9fHd+1YbUJjPxxCnsl133dHoI87HaZqISHsPKV3W+vL4kZhCnFB41FqXG5Y1pVgJJE3a6puvfrhtW7kujxqOdw+f/aW/fvGYBW14b03AW9nm+7ObIwuFpK0j4bJuSLJ1ZffT8ZQ8XLetDrWmZkBEknPtzXTUouu27Yh2BJQYQp5QdWIxt6U/1a5OolaGeiv1MOfzFOdAP3k1qX51nqa3n53jdCSSUdfr5ZYTP8zzIUUwf/v5w0D8+S/efXi+lFoBfJonQkCWMWz0FcH2eNon+hJR7x3/3f/Jv+cO7oCMTDSG+hjEDEw6hiAhM6ekZbub53A4Yspg7jpYJDBzkMNhfpUDgee7e+BwK61027ZtnqbD+dx6PwsExpelruplqKpRCIgwpZSYxPT3P7uLkf/hN8/bMGIGsBTSPr0mRCG6y+EuioTwodTnl4WjcAhraSjsbQATuR+Px22rt+X2/Py83q7L89P16f16vdR10VrjPLGk6XTkEEspvTYHsNEPpxMQ9a2OMZjxL/7Vv5YPx9u1HM7nu7vzMNXR21ZCzrvrZA7847v59QRYL4/vv3/38enx5fayrLUPcGt9sMg/V2OHPNU+BDzNh1LHdDwiACAkoRDjtt5SjKrmqugwRuvbtlwvTOgOu207kC3XFQhvSyuKj49POjoSS0wS03w6EeA0T0AkzCyRmPM86Rgxz3VdzO1wd9dq0WFlW4PIfD6f7x+AudfGLNM05eMhxVRrc3BtTZjNMaSduzkFIh0mQaZ5Uh07lEKHHk8n1aEOvXetLcQgQYhova0hRreuZkFinqbR1dG19WmeDUFYxhgAnnJGgNE7EbuZMA0dpfXIAmC1rK7Kwo4sIr21rhpjImJEc0R3n1N0xyDpZbmM1nprwNxK660iSZwmZkZzQASznIMO3epABCJDVd4v5KOe7x4kJB2j1iIhhhR2EFgMCQm1qwHFKDA05ohErdSYplI3HX06zODoYFHkdlu7obuBdkIBdCTU1rbb7fOH07/w06++fqnffHy5Xa/buo7RT+fzbqA5HI7H87lt5Xa7fv3LXyJhXcvoW12uMU/5cESEtm0xhfv7u+Px4EitdrMxzwcOYbTWyla3Zb0tRACurTQJMp+P03yXD6dpmtR0CnJ3OufDkZjX1kfvaO6jELETobmpqVk37737aOc55+PZ3NQBhbEW1/a0tN77erlMp4Mj22huFkOSIPNhJnR0F3DcnqtCGVDLpm2DUSODDntZ1mVZYwwIkIPsoW4YbS1rFjrM05vPPjvmsNxuP/ny7euvfnjZ+m1ZzCCGsK7Ly7a1bmRjXa7qCMigGqCnaerq+N/+d/77ALDPNYiJiEkE9iQvgITALCKhlm20FkNgEUA3cyKOUx61obCElFN+89lrBhvdjMVY3r55vZVi7rEsmVGmg82nAcTCiPh6zudIH5dSuxKSxKjMBK7qwyyIzCkw09rUxjgzAmEDilGYqA3lEFobA9zUhukYOvqovV8vL08fHrfLy+3x/Rijbmtv22iViNUs5sOXP/udbblNU57Pd3vdOqfsgHGaDsfD6e4eiVOMn04UIqWUWlvOmRBnts+izeP6q1/+2a+/f1xaB8TRxxij9ZZz6mq9jWmeQ57G6NPhcLtt8xQlzTshL89T2VaBMc1nB/ehW1mFxUYvZTMHZl6ul8t16+o6Btp4frki0bpVDJkkxiiAnNI0HY75eCAW7d3cXQeFGIQBcD4eOQTVDgDWjYOk6bBt6+l83quajt5KFwlMhELMAfaQRyl5nmKMvTZkJkIJaeioWzvMU0zBYUewERKB+yiVYxhjsLCI9FrdIcTYeyvLmo+HFELZtlIWAoopIRGBg7sZphQkBWEpWymlSJTeems6pSigBr77PftQQCLiNCVT1d6RKOa8y1oCkcRcW1PT0Xobo9R6eX4GszylvlwJnUJGN9D2+ssf3G6Fp0OvdYpyfzqnw2QOrmP0ziGM3pAIzIkw5hSYkdjN1q2WWnPKOUckAoMphqa91WqAqjrH+HA8mNulja20Vup0mMt6Q4QxtK6lbuubu8PPfvSVsvzm/dOHDx/HGILkBMwShdV8mucQYx+93G6j9ZfLtWxLTEmY19vtOKc3n7/dH3Tt4/r0IR8P5fISQkCJ+XQk4l4Kovc+WtOUJOY5TTO42bCHzz4XkfV2jXlKMfgYe/zl5fHDNCVAIo5tNHA3tT0nCGDIgjtDcdR1XdWst6a9997n0z0F0VoBPKZISDGIjs7gPvry+O3xdJdP973Xl48fnt6/L9tatqW0BvBJer9tawxxnrJE0d4QcJ6Ph+NBvPfej2n6yQ+/nM8P8xS//OK1EKrqspWldCF8fHq5LlvtXVt7fnoeDpJn/O/8u3/PzdwcPvH/6JMUNggzIZCpjdY5sLkK8Z4731dRpmZDHYBjSvPRVEercZpTnuI8789tTMnAg8hhng/zdD+lKadA+DrxjOPrS/nYXFnUPKe46+y6GSBNKZJZM8s570XlFORuzsL8stWylX0gNVSnnC+3dVtXYRyqpRQiBLUQ47ZurW4fP7xHgJTT519+9er1a5IwHw7uNsY4zJPp/oFBBwfznGJOQdVKrTrU++CcrPcvzimv77775ldff/f++XZTwNZ73aowE6OOwSEaESGd7u45RNOR5nld6nyYAIEAOIYQUt22IEQSltvym1/86s0PfyiAXXUrTVI+nO8+fHi/3FZ0zMcTMYWYJMieHQt5alvjGBCRGWtpo/eYc8qJiNxcmImpbGWakpCDw+W2iHBK2dV662Vb0zwjSa/tMM8xpz0GtVNqTdVVR68ppxAnc5sOh7JsihiEp5R67ynlXeRkZrfrVWLc3y+19ZhSCLItC3JAUCYKMW6l9NYQoJbibttyzTmTROvN9VMeGIny4TjG+PKLt0TYW69bQSYwu12v+Xi20d+8/Xy5XSUIuiMJc3AwIVcDc9/Wiky7+H1Z1u368vLh23e/+nPt/c0XX51fPQACmqtpyrOkacrpzdu3IGnnu7RapmlG8Mv1KhIYydyJIOcJEYHotqyt9ciERAY45yRChAhm5LYUnVLspkgUoyxLqbXMh7nV6oYppx1ru16fqZbzYZ5ev75t7eVyuXz8uJf/1mUJMc6nO3ADHXGa0b3WKiHEnATxMGdXBZbeu5ndXl7GvnJ+ekICEZqORwBiifP5DMCEULeVkGKekDildDxMpTYkKssKAIfDFFM0UwcSCUPH6D0GHn0M9dHr/v0E7sK43G47knsMHbWMrrjrJYchOqCDGhE5QLtd+2huY71cEMHNW7m5DtXRax2973143evoZjGnnNP5eDyc70+nkwGN0cFNJBIAEZ3Ox/vj/DrzMWEMIc6TY1iut9Hr09oQaStb6bo1xf/B//TfdwA33auREiIiGgAgutro3U1H7xKDm9Kn3A2A2W58ZpE0HWOMDmDmgEAsIU+n+3sknFLelZp5nqaUTklOTIdA4nZt9rjVzaDUzqBTysfToam1MRDIiXSMu+Ok5lNM4G5g6uAGOYiZXrfCIrV1YkoxCWOe8raW2oewSCA3ZyYCYsJhhjpsWwn8fDqmlJtbJHIbiuG6VSb87DznIOwqLEQwWhf6xNYBwo8fv3t+95tf/urX7z48GSIQbbX1Nphpnqda1iAhzAdiIRscUzqcEUBSGmbTPJmZ9gGAvXVgDjF98/V3777/AAY/+t3fEQl1q2k+5PNJQnSw0bv17qY7MR0IdfSybSGku1evr5erAzCLRDHVkPN+wQPQvm0f379DotP51NdFTdetTtMUUvz+V79hYUO6f/OWkO9eP8yH4+3lEqfISL12YWy9LpfrGP3+/r6PkaZJCF1trc1HyznFfEAEYZkOR5HwqZzfh6qt6xpTZtlZ/AHcAOG2LNtWx+itlnmegWi93qYopRR1Wy7P5Jjn+XQ83b+6d22jtZBybc3MAdGBR2uvv/zyw3ffCFNIh5hSCDxaR+JpngHcxliXZYyxK2zLVoaN5eVle3myPUnv3sqGbu59tPHx++/efvXVFz/84Rc/+pli2F+Ot8v1cJh2gNLuX/vw8elwnHPKxBKFEZGEfYzedauVkIgJkD67P/70Ln681da7pHzd2tbH1nXbNmZm5hQEcV8pxFa7mpatCHOKEgi30ilK3crttiDhfJgRERHNfQ+DmDkFAffR+uF0bKWyMBBJCHUrZSvEtLw8LZeXsq5vvvpBTJOODgaIzsxmyszHu7vR+vl03Kmu13Xtrbnpbr0CACQZvS8vTzHKdDhO85FFeq/gxiza1lpKqRUcOcZ1ucWURh/uDubr7SpRtA/rVUJGtF5rK+Xl8V1ZrhIjgIMZIpmp9tpqAQDTrsMQIU/T8Xh888UXd3cPDsAiPlTHcDDtnRBCCI6EplPASdi1FwUkmQKlKZXSgmAO9O5llek4qzoCxJzANeWJSdRUh/b+aZjBx8O61eFubswiIaqNEEJMKeWU5tMhZ05BJBB460MBQ4z0ieAFKSUCD20TkHzI92TXZs/Nngc44BDaqi2lPfchhGxOzByjjrFtTc36LuzCT2R608FMam5D9y/JOnQt4+W2ImBO0d1GU0BQhSjSat+28v79+zEGE+fDVRBbbXfHww9O6c3dec60dt2WZSDMQu+3/uFy3eqgVn77s+P9BN+/++6P/uwXj5db67pt204dQZJpTkioDkSc5oljdNVhvly3N/OJgyzrcnu+xHk2A1UrdaTjWQRpOB1e/fAvfBVTGr2DyJQPMU8hR3ZfLrfb86P2Rnsf3FBS3nVxrdVWq7ZNHeLxLEQhTzFGBG9l+fDrP3/++Hi9XOfjydprZL5dru5+fX6OKV6XWwgxpLSt6/39GV2fnp7Qxu37j8fzvY/eEXaoWUxp24r6XhLgVuvHdx/ADdDMEZBSzMf7e+F4//CQU3ThXYa0LSu5mQ0A66XEFLMwRC4+5DAh+Oh9Psy9bGYOSA+v37794otAlKdsptr7U/lwe3zctlJru3v92Xx37waPHx6BI+es7kPdfLQ+JGA0Y+Y4HxRwe/8+pTzUDalsdZhxyuzWer9dXhjMVEX4+vyc5kM6vQqHM0lY18bCrXZzc/Sh3loP0edpevPmzV7OV1VzDRx6bYc554lSDbUNAEf33vqfPOq2FdI+43L/cI4Ax/mgp8N1rdfbzc1yjsKUhc+BTjk0u9uuy9bViU732d2nPJ3Od0PNXSUwANRSkVDV63LTtYnEmNMwN4DI0segiPM0MRGKxJTu3r7VNspWEHE+n0OM5FCXVVJEQpFwmKedX2ajn+fE5wOD7+3d4d571xwCU+t9AJRSYoxExAznw9S7rMxq5s7qgChlrWnK7mA6OEZTRSJzdKbWuvaBwpKnYNrKlmJK84GEwWGaEzGLMLmN0UOIMUZiYZFtuanqNM2wh9qYTbWua11uAAjgNyJH8N53NrKpMgEhvjqfjodYtor//v/yfzXU1IyJSEg4hPBpWkSMvfa3x+m3PrsrY/zJtx+vpXXHGAUATg/3vXRTi/MchM9TJgBkvF5uHx6fdHQw3ZFhZkaEIchXb17/K3/hJz8+xa/X8Q+/vbxsLeWUc4pMBrjW1kcHg3w8aOuttxjSIccUwxiac0KH2puOAQClDhISYQ6x9N67igi7rrdbbzVKyPNctnUrpbVubimmlBMhQgiXl8uuyIHez4zH43xtOkjcjBC3Us1ttP7qIG9D/f5XP3/38Xk4IhES1tYBPrW4Y0oxpzEgMsQpj50EudXSATkASxs2FA8Pr9I0p/m4lXo4n9wciWLMIoIErVVGjimA+3a7aW+1bd//8pduTTjEwxEQGRkYAdDGYKH18pKm49svf3h+9ar3joTkHoJ8++s//+7Xv3z58Dif73/4s5+M1p4eH0Ftu73Md/dmhMLa+8ObN3f35zzPvQO41m2xVlKeJOWQJiDS1sZo2huCmWqptWwN3LflwiwhT4fDEZDy8Q5M94nq6XxiFpLgo67XCxNenz+OPiSkPM/DTZgRRXIKIe4eo3k+xJz3lffxMJuO3m2ff66luDpLkBCEaVmLmYYQibD3gQCqQ6IQ7mQWHEN7a09Pz8e7U91qK+Xpw/tyu86nQ0pxvS2jFjCLOccYPv/qq7df/nDoEKZSewgRwBAgpaSqIUR3c1MJQYdv29Z6Z6Ep51Z6zmGaJus9Cwv5F6f5bk5LqX/+/kVHy9aX4XMMd4fJOZxOp63UbYzlekspzgGPOTHLrer9ebZa1q4VUA2eb9s29DBNy7oCYggREIRFVZuOVpuZ1doICcHzNHUd3ruEAIj7S2foYOYxBiKaGxEx4Bgd/JPQOMRoOgJhShlc1ZwBgqACmNPOROjdDNB06Oh7bjZHDqBN3d37UEXqXYHYVc1cRxu9A4KqrrcLAQLRKJvVOuoS0F/fH1OK8XRvJMNBh45WGKnU0ktptTi4j97W1QFClBBjynMQ6WVzorYVJgBVtUHM7m46Wql7lQXB3Zw+qYQQEYWYyXezhZO5grHoUF/XTUJgplvpZa2/8+X9KaefP9enMmoptbUP331AxMP57Kaj2zSn8yRbHx+W6/r0OEZ3tyRyOJ2JGJAoJgjxF19/X+boKb8+HSCktbfR+wR0SnGSvI1o7hyktHZ3PBLh6AMAeq232y1PUwy0q6623gjkJAxjkOrYlkvZrs8v5s6MhJSuqbcmU5acxGyY2bqlaUK1PJ+IyUdrY7wr7TdPL9qa6UBTYJEQzOwYaQ7+//ujP7kuG4rsEp29QrDn5neoppv12uNpXpZtua7d2eM0nV/lu1f5eAoxEYnpCNOkvatfY5oYkYPsRWJ1R4QQZC9ajt4/fPuthBDn0+PXv0bCI4oDmup0PDAHQi5rCdNdOh7j6VxqR7DAoW4LQZzyVLbt8vQUUrp8fCzLqmbXxw+t1jifJEotG7q76eXx0Xp3G61s5bZqr4DgQMe7++kwl62a2bZcY47LbZXpePfZ59u6Jg5IJERbs8PdsesAd9MBvU+HWfsAqjZq2VYmNGQjLzq8VUIiicfzPTGBQ5rznJKbg+oUY2DuXVVNe2dmByaU06vzLt2cp2meZza/bRsyxxBySjpGryUloTDVsjnY3etXrx7uS+0lVJun+4f72+V6ujsL2LJuU06HwyHmHERGH4CIpqO342GurQeRsq4xJEaSwLUokwhzDMTCALhuZRikKY3eWq0++olDfbn+0fv3o3dCv6xrG87CY38fIKScP3v98MOH88Px9MUxC9rH63ZZ25uzoI3H67pWfb7enJiYRm2Uorkd5qm1TogkTIDElFPm00lVS2sI2EdDR0RHJieqWxEmcARzR4whCJK5I4O2Mec8hq5bMYfhLQRZW7ttBdEJHFB24HiICd2CkAgtWx2tIUJZV0DUEQ6CQKSqTBAYWR29DZbAwCn2zo7QSjk+3AfExNZKbOZlPeTjcYyhxM9r723p26q7a7o1IhytmY3dUw6IQ4cVb7WrGhPbGCiCRMjIMZKpqwGAxBTiNLT31vY6jGpHG4fMTCy9d3RwHbviApBVkWJAdgNEpJeuf/ayKsKrSX50lPsstxYupTaFZq5DL5ebjtGX5X4KATFGef3l564+dtkncqu11wpb7Xh9T/yHZgT++vWrME0A4L2xhO/fvUdCihlDIofjPF2X1d0NYL1ct3UFgJRLDIROAF5rQYTvfvnSS9ltuEjIImme3IFiNGIQ6bWN1s39cDoKUC01HUMIAoTXlwVJeDqQORLVZUFiR3QgRj1S+fkf//y2dvU9U797i5D3VpqjAwDismxm9u13azfJ54fp/k0+nY+nu2GWUg4pqY4gwkKMEh7ukSgyOSIKO34CYMCeRXc73t9JkPl4RJGXn/x0u1wkxjzNbfTT6YzoAdFMh8E85W1by+gxhG25gOnysoxW6lrqVkzH+9/8Jk4HCrENvbxcpvMtzbpcXmyMPE1MxCIfv/0mH2aSADHv3/C363VrXfJsSDjfgQhP4fBwPxTSdCKKIQZ3ZKJXr+6fX17mKROACEsMaDgc3n//nrUc5nlZlsNhlpiJOM+ThBRS6r2Z+raWKSZ1IyIzm3LcFR6meS/Hn6ZJAYjZRIKwmhnuTSzxf/YnTjOBjd4B964WglmKMuUwWmfhz9686q3v4axAmFIcQ+vttqsOtTcJ4lBNjYkdSAJvW23XdbfALctqaohICLfL8144G7UCAAf5ZpizOFjftjTlXnpMSQ0opIBUR9/Uvr+UX7+/HE/H05xPgY9TagZ9afcCCQcyyKu7bfhSq4s4wlYrIju4uKNZ6V1i6LWniKoKZo54Ph4BfPTBIZj5mKY+Rvtn61cRUjcCIGCJcWK/v59LTWvrTRWBNpBB0npzZkAA96HetyJMvasQCCJFGb3FyKP3UgYI3x/nN4fJR2fEyzDRvo6ahUdpgfCUQ5qn43E+n+Y58vNl/fXz9uG6rtUUeu21lnJ7fh6tINIYnZkIBIjcWccA0xAjEiI6mLVWU55Qwi6e3rY6ehdhQhra97GVmgHsmQuWnNj7wyFEYfx7/97/TIjnKPenwyFFYykYnETdbdeamUWhY5CM9uU5Z+/T8fRStatelS6X5ft1mOptW9tQtJFSjCkBiuQM7gguzK8SModu/sNzfuy4tM45t+7qJq663CqJDo15enj94O7CbGZC1NVKqQ5GMWpXdUADQNxaXS9P16envm1mFuZDiCFIlCAOPs2H490DoINqmlII2cFbbbX1EIKNQcJlWXQoENXrjZmQKUZBJBv6JuvzL//o2+/eG2DtioghhBACMezAIjMn4TH05eWqitP96/svvkrzSVJydwlRDabjMU+Tth6CTPOBhN2dwAFw6DgeDvt8BBF7G2N0QLDWD+fT7vWSKL1bDKHUom6n+WCjr7fbti6H0xlGXZcbhURIpq1shYViSk+PT7/+0z/t2wboknKaD+vttjw/SQjT8XB7fCzb+tVPfpZSOt7fbds2nc4hRrBxe3pxs/n+Ph1O8/0DA40xRh/MNE3TsmwANk35eDylGBjpcDysy8IhbMtStnW5XA6n0/HuYVm3QL68PPbWgrBLPJ7vU0puBkCACABlq0Sf3LKAlMI+gIc85XUrajrFqGpIpKoEwIHdofceYjJTQLQ+HBzGwF3q4b6uFREQnQBYsBsyUuu9bCXlDIiBqbSKQOttWUt58/pBRHKeai3Wez7MqmbqT8/Px+MxJR6919qQSFtttfbWSCjEoF3NzdTjPIlIX4u5pZx2oQkyC5K5pTSRBAdANwAKUVpr2qqZEyK5IzmObjokZzNk4TE6U4h5BkYmcrPWG5gj+DTPDmAOZoaAxPjw8OAOQdgd3Gyo1la1KUcRIhtjEnyVOQmfM5sjhVhqXZoZYhQ0zu9eluGDdwOJG6HXbWtDAT6R8IgI0RNYZAL3smy3oevo+3ixrZuZSYxJ+HQ8ZKFTks/O0xTDfuH47vH63dOlqb7c1uePj701MOAYzToC9bIBuANGkRAlhOA6dPSYwul0Hr23WojY3a7PT7UUVeu9pRhiShRCijGEgEillO16Ve1uiv/Wf+vvkrAIn+bDj18df/zmzGkC4m8qPq6t10LIqgPAhDmDoo435zkxHwjevnk4JfnF8/pyXb5Z9VrVwFlYWE6vP8vzrNviNl4dD77eDlM29y8PoR3uv71286Gqw5BD6OZJJAQxQHUstfXWYpTjlFPO5rZtxcyCsAIKiwOs67Zt2+16aaUBeJznsqwIOB0P8/EwT/PhOO9x7RQE4dOpbThcLsv1ejPVEKTXSsJ5noUFAMpyQ3Crqzz/8k/+8R9sahxiSIkDu9nu2lIzUx3m21avt5Lm4+n1m3w4pflw/+azfDwuLxdHMbe3P/gqhMhELMzEgBCYVW3oIABmUXNEF5FS6164a7UiM6CDOSIQBcS9GUemvWyl9w5mEoL1hgjq6g5jaO9jmicwc4fa6vtvvmMhJwoSOMrH776/PT+1dXl59z2F+Lt/7a+X6wXM7t68ceL5dE8E6+0WRCTk6XicjydhPh6P4D7NOYUgIqM3ZkHEPgYCmvvL5TrPWYRbbZfLS0rp/uHV6L23vm1LW2/Xp8f71697bXdvPg85M9G6ljzloQoOxDTGEGYdOk+ZCUZTjpGZ3EZtPUhgkbKsHKWVJjGw8L4sf3l+maccouxig1I2YiGCsjUHa+s1xEwiRDwM3I2DCJLEoKOb2p7jC8Ipht0fCDrUnGJYbguzCOO2bhIDAlyen9RtdAVEbVVEJBAiuhMAtq2kOTFL3YqbS2RwI8fD+WTDOEVhQebRS69K5CxBVVspksJ2ve7WrhBTrw0ZkSiF6P/cYmcmTHW5MYOrskSOiUjy8SAxigQGHK0RYYhBJIQYWm3M7Dqo1/tDZvBD4kPOzTwFmkRYSETC8e7PfvHrd9el9waOrQ9i2LY2H2ZzV8dP+gozETHtxEFEzHVXNLVSu/aUMrq5qgTebVWjK5p9eT//3tsjsXz9dP1w2W5d1bFt204vbrVq32u1lvOUApsORDQzMNtpJaVs8GnNqqOst8v15XJRd0YKOc2HA+ggQALoZmXb2rrkHPFv/51/I6QIzAiEAJHpbs53OY40P9eRmD6/P70s21KaCIurcWC0U4qK/Pbu/MPXp1FWHePXFbpM6mimpyk3EtQ+C8nhEBF92Gdv7nm9PpwPNc7v1vHy8rLcbiwBQo4pCmLKaZi/XG592LLcck4pSmDRMcwgzNN+5DkfJgIYpl2BmQghT4mQ1q3syGdmarWJiI6urQORtf7w8BACMRK4pSBjqANGocttCTE31bW155frqCXjWB9/8/M/+uM+bD6dQopluYWYkOj2/ASITtwUJB3uP//i/OpNPszz4RDSJCKHwwwObYyhGjgwooO7W2tdh0oKbgrugQUIdRgzIpHt/XEidxtqQl5qB4AYEhEwA6iVsoGOkKfeh41elnU6HlurYwwCRGEdivSJTtP72K7X0VrZFnNgCciyXV6e379/88MfgFmrxVUPd/ecMiHGlELMOYXT3d1hPoSQEFGEVDXnxMS9dyaapryum4SIAGq2ax9jDPsLWoeCgzCVWoMw2Lg+PbZtU/eHt1/EPO26AlX9tGjiTy9xBJQoo/eylDjlaZ4QQLW7/XOCLoyhiD66qeo0ZUdUsyiChK310eqOmTLAbSvDBjiEGJmJkWopxCQsIkxotQ4Jcb/e+j5UBmegEEIfAxGFuffuiG5qQy+X56aqw7alAGOKUtcFiXr3NE+jFEQLIXHMRBCFW9nGGESc8iRBoGuc0vF86upDlRG22225XhwxpcRx0t4cQJh7KxKklVq3lZhabTFnGCpJ6m2p65XBQ86qOs0zc3AiiWm0YWoShJmned6nyaec7ucohN0REA+BMuKbc54DO2HZqgJ0ta2M7y/L4219enxC1/nuLs+H03EWAlMb7ju2RM2ImRCnaW6ttK3simkH7/tSJSZCMB1A5IDkOoO9Pk4Ph/j1y3qpw4DLtpkrIbZP87KORKfTobVu2vfQCSCMVohExzAdptp7RbcYAiC8PL8Acc4Tu4r3LESI161yCHVd0B3/9b/zt3dxPCAikCNJTIgw5zyfzj94e//Vw+HxZX2qUMxba4iMLDHI8e6cUnxI8tOH+RC4q31Y2/frWBVBmxOvt9thyiTct1LVmHly/fLVnebDzamsqzrkKUvK2jozno7HKMHMulnpbVkKCbFE3VZkmY6HEAITImJikhjcXIjH6H2oyCecHiL01tW1dZUQEpObE+EhxXMkULsLfphyzOm5+lPpz5eFmDHIWpq57U7J223Z1lvKOabUau21AAsitnVVIMk55WmOAgiq2M0A3c3IMQQJwgMxIJmaI4w+JISdv3673hwhMGfh0up+V93TrZJmJETwPhTBXp7XPE+H40RIaJ0JL9drb+14OpVSR+/bsuR56rUAEAIOHSjcSo1TbusmMTLz5cOH5XLdtjXmNJ/uyvUCSOlwqMs1xBRzBoB4PMLQ4/H49osvDodD3TZwnw4TIl0v15QSIRKTAwjinhIABEZy99pqiJEQWMIYAwG0d0BMSepW123TXsv15XA8pMPRDJatBLBtOOy5OZL5MAOiDp0Pc9k7AyIUBA16ayknM2OEIMEAai3f/fnXJPTw2av5eGQJ2jshDVPGnbmHbioSDNHGADMMTEhg5u4ijMSI0PvYxy69D2R0sxiklp6miZm2dUspuiuAj9odiaMQYCsFERTwcJhv15u7o4R1WUcfpuN8f+dmjAhuo/d5ntS8toYso/WYou+08k+tC2tbHe4hhhQzII5W9+iVCJFwbd1URy3r9QUAidhsILi2ggRMJBIJqfdGLAhExPPdmZDcLOY0z3NCz0nmGG4grSsDBPQvTvlhisfMl+vSDdxMguTA3z69fP20Pt82J56n2XSkKDlIjBKDtNbb0OEISKYWyE1HVyUi0N5qzYEBSJGSBButj3E4TMtWehuTj3g83JYSYgACQgLEXmsQ7r2NobUUU1XthDRGB1MbLc9HN+u1IqOZEdI8T1HITRnxPIdR2sttaX2Y47puWy2td3DHf/Vf+1fdXYcSMQnHPLOIiMSUQkzn4xyCGDIQU8ruYEjah8S4nwbJ4Rz5p589/PUfvw5kf/yx/slLK20AQGnN3FtvzCGmuBcXdlft+eFeRBwBHXaoFRHt35tBJOWkDqX2oaru3RSAhCjIfopxRhDiFGKMclu3EEJg3rMaCm4GDkYkOSXXEYRH76A6sz89fvReBpAQNeR0Pt+d7ziGdaul97qVvtbjq3OpwxFyjIzQex9uw2B/cRgRDCXy9XK5f3ioXQ0Q3AAMHCWI6wgxn8+nbauA+18G7c0Ay1ZSSmZmOmKObgC7XoAwxChBVHcotiJRDHF/uwmiqbWyQZBeeopBwUYftaylVjePMbe2icTWGoC7WpA0HQ8SiHwneXueD6OW1sc0zXnKn/RyDr3WmFIIQdVSjKWUnJOpAuEutdvWgkSIHnauJBAyAfjhMBNAKY0/ef+cmeq2vby8MFrM+Xq59tFarYHQzWNKl2Vbb9dh6L2ElLfa5ynPp8P1cvvqBz/Yf/AcQk6xbGXvM7hpr+X+9euyVUSIh0NI2YaO2vI8jTEIYajllLT3/VeRghiAINVWACgEbq2LMCCp2pSzu5VSiVlE+hh1KwlBEZFlnqcxxrZtU44G4I5lq0yQpuzuvEdY910Hi4MT834uHqq9td46k7cxjofDYcqA2FrvrQWRwDRKUaTbWpTDsixBJB8PQfjy9JJzIiJV3R3mo4+yrr3X0UevxVvdBTYMYNpziCFnBDycjgowTTMiTFPKgArIRIyovVV1Q3SiPZuZRILbj18dXk30dN2+u2596HUpp4inKYUQIYRraQNlLR2ZUyAwhTE4cFfftsZC4E4ESLxtq6lZH46ehf/Cl3cphN+81AnGdSuH4yEgbG3kKFsfy1azoKOEENX1+flSSuljrOu63Za63UwVEW20HY09HybigIAhBGKKKeYUaIwU6NUh/fCz8/NSv/5wfXdZWcRUS9n6MHTbZWKOjBITh4Ash9MJzHfxyTZg1cEBhCCIs4jWbjqM2QYRIgcZIXy79f/vN5fffTMfp/hQ/Yq09MFEwng4Hg7T1EppYwBJKYVZ1m1DpJynEEPf24ytPjzczzmbe60tiLDrthVgijG0rgNcGzCRDVM3ME9phMCmXmsPQXZVoupw932MUstWtu3p6enl+Xno6Mtt1MY5U2B0CDEFfvfm7dvD+QSA6jgMl9Z5q8ISopRlvdyuSDTN2Q1qqcwhH6Zd93Z8/RkzTYIYWFtVcw7JeocQc8qAxPu9z2GfozJSF2m1MnOI6XCYW21uThPXdUNwBAwcYgiqhky99k++mP9/U3/WY1m2pulCXzfGmM1ay8ybaPbemVk9VUVJdQniXHODhGiEkOh0jgrED+QHcIPEDUdAQXV5yFNZmbn3jtbdzWw1c47ma7iYvktHCskj5KGQeZitOcf4vvd9HkAkWp6emLBSRQK0KLmsy6zuo5sIj7HkaYoAHypZAHC7XRlxXlcKZ+HRx+n53d4GMydiRKh1/9o5Y6w6nNK88BhqalPJAJBSMvRpPlx/qbev+clcSqtdhwpjzukgRCamodp6c7fr9ZXc7OuSBp0EKD59/jyvy/L8/Pbycr/f09DR+8tPf1zfPalGvb1NyxJITIR4nEWY901SYkl/+MPv69bSNH/4oFPZw9wCJSdhdlNXV9ZABIQylbZXHX2eJwskgmFuHgKYcsExau+MpObgbmZTyeV8en15zVMRYUQws5RTSgUgDvAUwldCsRKbqRBat0iBQF8RGnHMlVIg9bq32lnSsJhzIsI8T2Pb35/O3344TUKPbn+87vepBAQgsPD0/omJgVDHUDVBGq4bQ2sCAL0WtzEt8+l0IoBAXNc1J6l7m5ZpjDGGah/X19tRpjsv0/NpCnGZlmC5bvVaW+/jUvg8lR9e7m8bLwIBeO92tfj80vzztp6Wb9YpoV9O+bvTad/2X+8P4xRmYABu7hbdAJFFlpLcUqsdUybBrv5XP76ccnrZR05cGB1wFSxq4XHvdsRGdVQgdLPa9+vrVT0CY7hdrzfTwcxhZtqJeKillFgkHo+67wAeNhjw6d3z9vHD672WROvEeZOIyCW7KYQCslw+fgMBnNLxamWRXGYzA2JmJkkiUtbZh5GIOwDisa3rvR3OApT8pW8vt+2nx+nDeX1f6HmZXwZU89rH41F/ffslCVPOBLae1inlVHLK2dwDYGAARs4JEdVtytnNa2sl53kFABSRzCqSEPFQnvTWPWKeMwLEV2ot3GtNnCRxqEqS4f52u/34d7+/Xa8B4WZMDCmX9TT6IGZImVLK6+IIY6+OMJ/OnJ/fXq5zkjlytQEpj2E8jBHX9TSGHpocZAqEt+ttLjO7t9pP5xOl9Lbt5U/vWPMIh3mZAAAIiDnlGIThwIRt2wA5JdHwnBNFJAxiMNXH/YYePE9LOQEkZtExInyZV3K/Ph7mCASZWNXWtYw+5mlGogPXs7cqklMqeZoee51zSSTLkoYaMQUEiQijhSVJvbdR9wCSed62SkQs0twEBdXCfSqTgf/y0+dpSpfLfIhRoMRe98fWp5xymcBs7623HY5k//nUt+3TL78+ffgYAa8vb6d1YaLr9Tafn0Qyp1z3drimRhtSpn3bhnaWXLfHtCznD9+hyP3xsPEG7tZ6WpaEuLf+8nJ19/fffCTmo8s5l3z026ecj6uAA3p8bRaLiEgKj/vtfj6fo3eHkCTHXMwDS+ZpmQFpjHE4SRnJXJkFMdaliMhoo/Vq7toH5WTu7lBK6W2MMUrOAYoA4ZbnOYi62nFSYyLO2QE/bePlvstQIORpWs+TEJuqAxCmxNJafXo6gYe66dDLaYVwFEYggFAdGFBy3ltzt3DnLC9fXrf7fV7Wp/fPJPzydmUYnPgbms+X089froD0m3MRKDfit0cTomD54d7XjEQcyHMp67rc3m6tjddUiOjL59v7tXy3cMxyB+kKYwxiLvPa7w8WatqvrxUQ3a3u+6GGunpcqyE4MpZcfnndXsNJcK/bgK8Sw8de1aPVnREC0LWrOTiUeXUbwtJaS6Uc9BQiMh1brUPV3ELHPJVgeav+5fHA+CpCyyJuysy5oAjL6XKZ52Wa5sDYHxsScylI0lt1d2TGQ2GZ2NwQ6ZgySk6jj2meJcnoo7cqKe1DP1+vNxFMmXJ2i0xI64Tn1dzbUIpYTysBmlvbKxAicWGZS9HeBYE83J0QHcjM748tAnwYgH/77cdCuO0tizytEws7kJoHwFDbe9PaOykwzikJWdt2Ifzm24+lCATsrTtgzpMkfozb04cPp/MpZ/YIUN/6SMQ+1MPOSwZ3dUgpEVqkTATruoRDniynxEdRIyI9PxOzjZ5Lmefp0RoSEAARDrWUk4IDopvmzHttWRIT1j7MI8KW0+xugihJ9sdmOgK+Gmjqvk/hYFGW2cZoOnIut7e30ToCEDpqKIeO4aM70jRzuKdSPBw5eQCRzPNECCLy2HeAmKdFCFLKw0bdOxHtWlNKeVqXdY6Ix2OHMFeHlM1DR48IdZ+mMs8ZDxzosdhyA0Biud633MfptLgPG13dDMDVyrp++E12dXdtvUf4+en8+sNPo48+bN+ruTFJXs4sjObTenKPIwaxPWrbB4SrmuvI03L58GG9nJ+fn5fTGZAO+XGtjZPIlLs5jWF9mNmB6ycEizjC4McTjRJJkqEjIHLOEHGgutXMAyUlhyjTSkS11t7HxKX3nnPWMYQ4lwwExzhvmOWUR+8WruoirKaI5BGHyi/nst3vI5znhVJiomMzeHs0AEci0W2aMgaaux3Dp6GppNWjHBwuYdWx17rMM4sAQO9DiIaNMHPzZr2OUbctTWU+rYeRoLaOqup23/bfffP+dL78ctv++q9/YcTTeZ3nqQYViieOXocKA4SEZ8nT01oSh+Rtr4706dHuQ2aCPmo1M9NSplCLsPHYXCSlDMimIyfhPJecCXyM4W7usW9tJnt/mrmUR6IxBhDsA22ea++90TC93+5hOk1ThAIYRIArhYXp/TpE2IYhgbmnlC7z+bQu6zqfTqcxdIxhZmYKhrWrqrEQMweASMrPy5QYSQp5mNo6T4acCPsYLBkYEEE4hTsxmx3inxR+nOsBEXIpiHi/P27aex8okuel1z5N+XQ+lWnea+1DS8n7/ZZyCYgxTFJOiSRsYfaJo3UI7/uwgIB4tBEe6zylhEvJM8UMvdW7SLpQgqrn8yyn5THgFfwiuZwyQXyq2hFbrdu2tdbMNOVCAHk5yTwLUe8dPUpmM923Zqpq7g49cX8Zrh0Be+/5fE4pa+tlWcBxe2zhLqVcH9euyolyKhSopto7At0fW2vdh259uKkDBqCO8dMff56WKU9luz9ykvV8tjGW02kqMwLUfd9uNx217TunBMxZUgTknMPj9fXlpGOYUcqjDxyt9+Fu99e3AJBpOl3OAQgBOnp73O7X15ym+fJMhNMy2+hTFpakww5mCRF/ZeeXMvpATsxiYbW2cE+J96o2NHPSXi0gkBCh7TsLlZIBcWjsbXMbyzyvSwaAoXpwU0pOFAnIzNTciXj46K1TLvd97/riAeBY5uXy7oOOMc3z+enpfFqTCDI9blvYGKqItK6LMA81wiCWMVSEEWkqWc0OxKt57HvLWcpUMICmrxBwFl7KbHAsKOCYCyLicRdWs2PmlSUBITH33s39iOzP8ywsVJCI1UP9cMi4qULAsUCIAD4i6RAiTEyjD0npQPgGRK1tnuc+VIRdlafMJK03kgQY4La3se17KeUokEou5rFvdd/ru3cXMGUCJCRJW+3zQtq7mSJla+PzTz9zzu8+vkfhiFiWGQkOYH9OqakBEEr58hj3/e3y/hkA+xhBHDoS+Qz+L/7hN2+P+m/++OXL222/3xHgdFq+Oc9lQSE0i0vB22P7hIzgo7aIMIs8ld7t+npnkZzSej5FQBFJ3qaI371b17LUkPt9ezplGJ1NT6dET/nx2Bzirz9vd/ecJJZJzYmo9zr2hpROlycfo96umShkyiw6epAzQ855mZfz+XQ6rc+n8u2S98djV2JJ6GMYdI2ttm6+Nd1rFfaYwJZE17qX8HlJTwsrppewUI0wwtSbQvQk4oHuVqaCECJf8SNH16S2Hm697trG8vwMJPO6DB2/fvpCiNqqqkaEq07LvK6naZpO335o2/2+Pe6S7tvuAWmeDuUSQ7iDkVwf29M6G3DtOk20nM+z0Bo1oP3ZNE8LfNmVtV+WaRV428atBwAE4XR89kbHiAM0zsRfPn3po499f71fARB7T1Mp65pznuZZSExz6y2GHr3ZMk/m9th2M5eUYGtj9ESUo/T6cDck8mGIse9CTAg4VHWrESYpmweIBAuyTMu8zBNzMh19v7cdHrf7H//Tf5yX+dtvP3773TfD7dPPvzSL+XzudbMASrn1DoTQ6+i9PW4sycL++Df/7VC9vP/w+UccY5RpwZQfr59ef/rx/O7D+v4dc5qXlfP029/9biqeixCyEDmENys5+7AkSd3XZWmtPmo11Ry0rGuoeoSU5BbDw3TcbzeZ5+vbaynT+ekJgIbh0DHGSFmQ8OXldcoSGlLKOvFQteEzAj1fejdEvN3uCC6SkHmapnVdAYKIh6qrJmFiSc9JVXNOKUlCRITH3lkkXHVYyqkfbTYz32sSKUnGUECq2w6AeSp2xPRF1PwY7UfE4QPDCABURIg40hhjdGRC4iPFIszAoqrH6zkAcko2BgIO8yQcfzp2mdno3SGO3OVQTUkkAgCEBRDWldCdMJhoqIabB5QkLDzUbm+7u085FSGyURhz5lLO21aZYAztrU64HNnsSACB4ZBTqn1cb/fT5UzCpRQmDote9asfG/k3335Qh73WAHia07kwhaLQazVhFuKh/uPjsU7lt5fln3/7HCz386n2ziKbG+s4Jb6rIlAS3NURwRDWzOc5tXDIPM1FciLiCcMLF28L6OfN/3qv//0/e//NyrwPVhvqP77c91+uj8deW6NSalcDRCIILzmDCXgqa9IwNyVO0caBxpzmQgjCMk1SUm616ei993ob14YvX673Pr77cP7+qeQyk9ApnQPor396/flK+L/9P/yXCWMq5d7VzC9T/nBe1rlsDr9srbdR5hWIh6kkkZRM7askm6X3fhAcc04B0LcN3HOelstpXpZlykL4eOyf3976Vol49JZLef7wnhlVbbtvL59f8umUp6LmKSdiqvedhUtK63nN6xkBs1CEz0TnxOphph8KMkIMVY8aQMvZIFob133svTngelqKJA0HQACotbEQEl2vt9FHBDiQlFKY3r9/nudiQK11BChZRMTMWx9gLiXttSFizvn4bKgZmBJGLtNQc4s2GgEmlvU09W5ER2eTeutlmlLiMLu/bZJpqL78/PPby5dwBSQb2vbHX/yjf3C5XLa3FxF5PLba6nK+jN63t+vzd99Pl6f79Z4Q1tPyuL6+fXkNob/9D3/Z635+focEJDKtT/PlYqp//a//n+d379599507LKen999/t8wzAK6XdyycCT0iALJIOATB4/EoKYmk4dZqXZd5qIWbjz7UT+fTNE17rUfWfNsrEyah3jqxpCStNRHiVPatQQwmIkkl595q3TdtfVkXydP7D++OnhAyjaGPxwMgeh+SSwRsj0cRWdcllwThbkbMrba5ZJJU21DVdZ5LSULk4VvvECBEZt7NWY4lCR7qY48Yveecp5wjwtyJKNzXZQEAj0D3gDjOaAjhgXutJNJqA4RlKnbcCiDAw9wlCSKVLAhgHm4GhxWevqrq4Ot/yHWYuZkFgU1Mz0sh190AWKzVIyK/18oR82n1PpZMp7kE8qPblNPe+tv1GmAdEkpy8zBrrZ3P62Vdr7crhENEd+CUAtDGADNDevvyMp+XWbhtG4nU3r2Pp2X+7t0JiLvHLy+3x17nks19AIYbu5GbEhOifo0ogmtPLGWZKacj5xEQTLTdrt5rD1T1MToxPWf+Z9+sv39te28zGnK61zanfC58njhMHyMqpYfhbk7EKQm4qWrbW29tPa2HhVqYI0LHsMPQ4+E61tNKiHXbzRQiGOHjzBNFHzpLoI/f//J6735KdFqX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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "load checkpoint from https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model*_base_caption.pth\n", + "caption: a woman sitting on the beach with a dog\n" + ] + } + ], + "source": [ + "from models.blip import blip_decoder\n", + "\n", + "image_size = 384\n", + "image = load_demo_image(image_size=image_size, device=device)\n", + "\n", + "model_url = 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_capfilt_large.pth'\n", + " \n", + "model = blip_decoder(pretrained=model_url, image_size=image_size, vit='base')\n", + "model.eval()\n", + "model = model.to(device)\n", + "\n", + "with torch.no_grad():\n", + " # beam search\n", + " caption = model.generate(image, sample=False, num_beams=3, max_length=20, min_length=5) \n", + " # nucleus sampling\n", + " # caption = model.generate(image, sample=True, top_p=0.9, max_length=20, min_length=5) \n", + " print('caption: '+caption[0])" + ] + }, + { + "cell_type": "markdown", + "id": "fac320a2", + "metadata": {}, + "source": [ + "# VQA\n", + "Perform visual question answering using finetuned BLIP model" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "5e6f3fb1", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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sU7lt5fln3/7HCz386n2ziKbG+s4Jb6rIlAS3NURwRDWzOc5tXDIPM1FciLiCcMLF28L6OfN/3qv//0/e//NyrwPVhvqP77c91+uj8deW6NSalcDRCIILzmDCXgqa9IwNyVO0caBxpzmQgjCMk1SUm616ei993ob14YvX673Pr77cP7+qeQyk9ApnQPor396/flK+L/9P/yXCWMq5d7VzC9T/nBe1rlsDr9srbdR5hWIh6kkkZRM7askm6X3fhAcc04B0LcN3HOelstpXpZlykL4eOyf3976Vol49JZLef7wnhlVbbtvL59f8umUp6LmKSdiqvedhUtK63nN6xkBs1CEz0TnxOphph8KMkIMVY8aQMvZIFob133svTngelqKJA0HQACotbEQEl2vt9FHBDiQlFKY3r9/nudiQK11BChZRMTMWx9gLiXttSFizvn4bKgZmBJGLtNQc4s2GgEmlvU09W5ER2eTeutlmlLiMLu/bZJpqL78/PPby5dwBSQb2vbHX/yjf3C5XLa3FxF5PLba6nK+jN63t+vzd99Pl6f79Z4Q1tPyuL6+fXkNob/9D3/Z635+focEJDKtT/PlYqp//a//n+d379599507LKen999/t8wzAK6XdyycCT0iALJIOATB4/EoKYmk4dZqXZd5qIWbjz7UT+fTNE17rUfWfNsrEyah3jqxpCStNRHiVPatQQwmIkkl595q3TdtfVkXydP7D++OnhAyjaGPxwMgeh+SSwRsj0cRWdcllwThbkbMrba5ZJJU21DVdZ5LSULk4VvvECBEZt7NWY4lCR7qY48Yveecp5wjwtyJKNzXZQEAj0D3gDjOaAjhgXutJNJqA4RlKnbcCiDAw9wlCSKVLAhgHm4GhxWevqrq4Ot/yHWYuZkFgU1Mz0sh190AWKzVIyK/18oR82n1PpZMp7kE8qPblNPe+tv1GmAdEkpy8zBrrZ3P62Vdr7crhENEd+CUAtDGADNDevvyMp+XWbhtG4nU3r2Pp2X+7t0JiLvHLy+3x17nks19AIYbu5GbEhOifo0ogmtPLGWZKacj5xEQTLTdrt5rD1T1MToxPWf+Z9+sv39te28zGnK61zanfC58njhMHyMqpYfhbk7EKQm4qWrbW29tPa2HhVqYI0LHsMPQ4+E61tNKiHXbzRQiGOHjzBNFHzpLoI/f//J6735KdFqX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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "load checkpoint from https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model*_vqa.pth\n", + "answer: on beach\n" + ] + } + ], + "source": [ + "from models.blip_vqa import blip_vqa\n", + "\n", + "image_size = 480\n", + "image = load_demo_image(image_size=image_size, device=device) \n", + "\n", + "model_url = 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_vqa_capfilt_large.pth'\n", + " \n", + "model = blip_vqa(pretrained=model_url, image_size=image_size, vit='base')\n", + "model.eval()\n", + "model = model.to(device)\n", + "\n", + "question = 'where is the woman sitting?'\n", + "\n", + "with torch.no_grad():\n", + " answer = model(image, question, train=False, inference='generate') \n", + " print('answer: '+answer[0])" + ] + }, + { + "cell_type": "markdown", + "id": "6100e519", + "metadata": {}, + "source": [ + "# Feature Extraction" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "4f8f21ed", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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sU7lt5fln3/7HCz386n2ziKbG+s4Jb6rIlAS3NURwRDWzOc5tXDIPM1FciLiCcMLF28L6OfN/3qv//0/e//NyrwPVhvqP77c91+uj8deW6NSalcDRCIILzmDCXgqa9IwNyVO0caBxpzmQgjCMk1SUm616ei993ob14YvX673Pr77cP7+qeQyk9ApnQPor396/flK+L/9P/yXCWMq5d7VzC9T/nBe1rlsDr9srbdR5hWIh6kkkZRM7askm6X3fhAcc04B0LcN3HOelstpXpZlykL4eOyf3976Vol49JZLef7wnhlVbbtvL59f8umUp6LmKSdiqvedhUtK63nN6xkBs1CEz0TnxOphph8KMkIMVY8aQMvZIFob133svTngelqKJA0HQACotbEQEl2vt9FHBDiQlFKY3r9/nudiQK11BChZRMTMWx9gLiXttSFizvn4bKgZmBJGLtNQc4s2GgEmlvU09W5ER2eTeutlmlLiMLu/bZJpqL78/PPby5dwBSQb2vbHX/yjf3C5XLa3FxF5PLba6nK+jN63t+vzd99Pl6f79Z4Q1tPyuL6+fXkNob/9D3/Z635+focEJDKtT/PlYqp//a//n+d379599507LKen999/t8wzAK6XdyycCT0iALJIOATB4/EoKYmk4dZqXZd5qIWbjz7UT+fTNE17rUfWfNsrEyah3jqxpCStNRHiVPatQQwmIkkl595q3TdtfVkXydP7D++OnhAyjaGPxwMgeh+SSwRsj0cRWdcllwThbkbMrba5ZJJU21DVdZ5LSULk4VvvECBEZt7NWY4lCR7qY48Yveecp5wjwtyJKNzXZQEAj0D3gDjOaAjhgXutJNJqA4RlKnbcCiDAw9wlCSKVLAhgHm4GhxWevqrq4Ot/yHWYuZkFgU1Mz0sh190AWKzVIyK/18oR82n1PpZMp7kE8qPblNPe+tv1GmAdEkpy8zBrrZ3P62Vdr7crhENEd+CUAtDGADNDevvyMp+XWbhtG4nU3r2Pp2X+7t0JiLvHLy+3x17nks19AIYbu5GbEhOifo0ogmtPLGWZKacj5xEQTLTdrt5rD1T1MToxPWf+Z9+sv39te28zGnK61zanfC58njhMHyMqpYfhbk7EKQm4qWrbW29tPa2HhVqYI0LHsMPQ4+E61tNKiHXbzRQiGOHjzBNFHzpLoI/f//J6735KdFqX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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "load checkpoint from https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base.pth\n" + ] + } + ], + "source": [ + "from models.blip import blip_feature_extractor\n", + "\n", + "image_size = 224\n", + "image = load_demo_image(image_size=image_size, device=device) \n", + "\n", + "model_url = 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base.pth'\n", + " \n", + "model = blip_feature_extractor(pretrained=model_url, image_size=image_size, vit='base')\n", + "model.eval()\n", + "model = model.to(device)\n", + "\n", + "caption = 'a woman sitting on the beach with a dog'\n", + "\n", + "multimodal_feature = model(image, caption, mode='multimodal')[0,0]\n", + "image_feature = model(image, caption, mode='image')[0,0]\n", + "text_feature = model(image, caption, mode='text')[0,0]" + ] + }, + { + "cell_type": "markdown", + "id": "201e1146", + "metadata": {}, + "source": [ + "# Image-Text Matching" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "49ba5906", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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sU7lt5fln3/7HCz386n2ziKbG+s4Jb6rIlAS3NURwRDWzOc5tXDIPM1FciLiCcMLF28L6OfN/3qv//0/e//NyrwPVhvqP77c91+uj8deW6NSalcDRCIILzmDCXgqa9IwNyVO0caBxpzmQgjCMk1SUm616ei993ob14YvX673Pr77cP7+qeQyk9ApnQPor396/flK+L/9P/yXCWMq5d7VzC9T/nBe1rlsDr9srbdR5hWIh6kkkZRM7askm6X3fhAcc04B0LcN3HOelstpXpZlykL4eOyf3976Vol49JZLef7wnhlVbbtvL59f8umUp6LmKSdiqvedhUtK63nN6xkBs1CEz0TnxOphph8KMkIMVY8aQMvZIFob133svTngelqKJA0HQACotbEQEl2vt9FHBDiQlFKY3r9/nudiQK11BChZRMTMWx9gLiXttSFizvn4bKgZmBJGLtNQc4s2GgEmlvU09W5ER2eTeutlmlLiMLu/bZJpqL78/PPby5dwBSQb2vbHX/yjf3C5XLa3FxF5PLba6nK+jN63t+vzd99Pl6f79Z4Q1tPyuL6+fXkNob/9D3/Z635+focEJDKtT/PlYqp//a//n+d379599507LKen999/t8wzAK6XdyycCT0iALJIOATB4/EoKYmk4dZqXZd5qIWbjz7UT+fTNE17rUfWfNsrEyah3jqxpCStNRHiVPatQQwmIkkl595q3TdtfVkXydP7D++OnhAyjaGPxwMgeh+SSwRsj0cRWdcllwThbkbMrba5ZJJU21DVdZ5LSULk4VvvECBEZt7NWY4lCR7qY48Yveecp5wjwtyJKNzXZQEAj0D3gDjOaAjhgXutJNJqA4RlKnbcCiDAw9wlCSKVLAhgHm4GhxWevqrq4Ot/yHWYuZkFgU1Mz0sh190AWKzVIyK/18oR82n1PpZMp7kE8qPblNPe+tv1GmAdEkpy8zBrrZ3P62Vdr7crhENEd+CUAtDGADNDevvyMp+XWbhtG4nU3r2Pp2X+7t0JiLvHLy+3x17nks19AIYbu5GbEhOifo0ogmtPLGWZKacj5xEQTLTdrt5rD1T1MToxPWf+Z9+sv39te28zGnK61zanfC58njhMHyMqpYfhbk7EKQm4qWrbW29tPa2HhVqYI0LHsMPQ4+E61tNKiHXbzRQiGOHjzBNFHzpLoI/f//J6735KdFqX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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "load checkpoint from https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_retrieval_coco.pth\n", + "text: a woman sitting on the beach with a dog\n", + "The image and text is matched with a probability of 0.9960\n", + "The image feature and text feature has a cosine similarity of 0.5262\n" + ] + } + ], + "source": [ + "from models.blip_itm import blip_itm\n", + "\n", + "image_size = 384\n", + "image = load_demo_image(image_size=image_size,device=device)\n", + "\n", + "model_url = 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_retrieval_coco.pth'\n", + " \n", + "model = blip_itm(pretrained=model_url, image_size=image_size, vit='base')\n", + "model.eval()\n", + "model = model.to(device='cpu')\n", + "\n", + "caption = 'a woman sitting on the beach with a dog'\n", + "\n", + "print('text: %s' %caption)\n", + "\n", + "itm_output = model(image,caption,match_head='itm')\n", + "itm_score = torch.nn.functional.softmax(itm_output,dim=1)[:,1]\n", + "print('The image and text is matched with a probability of %.4f'%itm_score)\n", + "\n", + "itc_score = model(image,caption,match_head='itc')\n", + "print('The image feature and text feature has a cosine similarity of %.4f'%itc_score)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/repositories/blip/eval_nocaps.py b/repositories/blip/eval_nocaps.py new file mode 100644 index 000000000..3cbb09a8c --- /dev/null +++ b/repositories/blip/eval_nocaps.py @@ -0,0 +1,118 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.backends.cudnn as cudnn +import torch.distributed as dist +from torch.utils.data import DataLoader + +from models.blip import blip_decoder +import utils +from data import create_dataset, create_sampler, create_loader +from data.utils import save_result + +@torch.no_grad() +def evaluate(model, data_loader, device, config): + # evaluate + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Evaluation:' + print_freq = 10 + + result = [] + for image, image_id in metric_logger.log_every(data_loader, print_freq, header): + + image = image.to(device) + + captions = model.generate(image, sample=False, num_beams=config['num_beams'], max_length=config['max_length'], + min_length=config['min_length'], repetition_penalty=1.1) + + for caption, img_id in zip(captions, image_id): + result.append({"image_id": img_id.item(), "caption": caption}) + + return result + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating captioning dataset") + val_dataset, test_dataset = create_dataset('nocaps', config) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler([val_dataset,test_dataset], [False,False], num_tasks, global_rank) + else: + samplers = [None,None] + + val_loader, test_loader = create_loader([val_dataset, test_dataset],samplers, + batch_size=[config['batch_size']]*2,num_workers=[4,4], + is_trains=[False, False], collate_fns=[None,None]) + + #### Model #### + print("Creating model") + model = blip_decoder(pretrained=config['pretrained'], image_size=config['image_size'], vit=config['vit'], + prompt=config['prompt']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + val_result = evaluate(model_without_ddp, val_loader, device, config) + val_result_file = save_result(val_result, args.result_dir, 'val', remove_duplicate='image_id') + test_result = evaluate(model_without_ddp, test_loader, device, config) + test_result_file = save_result(test_result, args.result_dir, 'test', remove_duplicate='image_id') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/nocaps.yaml') + parser.add_argument('--output_dir', default='output/NoCaps') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + args.result_dir = os.path.join(args.output_dir, 'result') + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + Path(args.result_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/repositories/blip/eval_retrieval_video.py b/repositories/blip/eval_retrieval_video.py new file mode 100644 index 000000000..07ebab7f4 --- /dev/null +++ b/repositories/blip/eval_retrieval_video.py @@ -0,0 +1,250 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.backends.cudnn as cudnn +import torch.distributed as dist +from torch.utils.data import DataLoader + +from models.blip_retrieval import blip_retrieval +import utils +from data.video_dataset import VideoDataset + + +@torch.no_grad() +def evaluation(model, data_loader, tokenizer, device, config): + # test + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Evaluation:' + + print('Computing features for evaluation...') + start_time = time.time() + + texts = data_loader.dataset.text + num_text = len(texts) + text_bs = 256 + text_ids = [] + text_embeds = [] + text_atts = [] + for i in range(0, num_text, text_bs): + text = texts[i: min(num_text, i+text_bs)] + text_input = tokenizer(text, padding='max_length', truncation=True, max_length=35, return_tensors="pt").to(device) + text_output = model.text_encoder(text_input.input_ids, attention_mask = text_input.attention_mask, mode='text') + text_embed = F.normalize(model.text_proj(text_output.last_hidden_state[:,0,:])) + text_embeds.append(text_embed) + text_ids.append(text_input.input_ids) + text_atts.append(text_input.attention_mask) + + text_embeds = torch.cat(text_embeds,dim=0) + text_ids = torch.cat(text_ids,dim=0) + text_atts = torch.cat(text_atts,dim=0) + text_ids[:,0] = tokenizer.additional_special_tokens_ids[0] + + video_feats = [] + video_embeds = [] + for video, video_id in data_loader: + + B,N,C,W,H = video.size() + video = video.view(-1,C,W,H) + video = video.to(device,non_blocking=True) + video_feat = model.visual_encoder(video) + video_embed = model.vision_proj(video_feat[:,0,:]) + video_embed = video_embed.view(B,N,-1).mean(dim=1) + video_embed = F.normalize(video_embed,dim=-1) + + video_feat = video_feat.view(B,-1,video_feat.shape[-1]) + video_feats.append(video_feat.cpu()) + video_embeds.append(video_embed) + + video_feats = torch.cat(video_feats,dim=0) + video_embeds = torch.cat(video_embeds,dim=0) + + sims_matrix = video_embeds @ text_embeds.t() + score_matrix_v2t = torch.full((len(texts),len(texts)),-100.0).to(device) + + num_tasks = utils.get_world_size() + rank = utils.get_rank() + step = sims_matrix.size(0)//num_tasks + 1 + start = rank*step + end = min(sims_matrix.size(0),start+step) + + for i,sims in enumerate(metric_logger.log_every(sims_matrix[start:end], 50, header)): + topk_sim, topk_idx = sims.topk(k=config['k_test'], dim=0) + + encoder_output = video_feats[start+i].repeat(config['k_test'],1,1).to(device,non_blocking=True) + encoder_att = torch.ones(encoder_output.size()[:-1],dtype=torch.long).to(device,non_blocking=True) + output = model.text_encoder(text_ids[topk_idx], + attention_mask = text_atts[topk_idx], + encoder_hidden_states = encoder_output, + encoder_attention_mask = encoder_att, + return_dict = True, + ) + score = model.itm_head(output.last_hidden_state[:,0,:])[:,1] + score_matrix_v2t[start+i,topk_idx] = score + topk_sim + + sims_matrix = sims_matrix.t() + score_matrix_t2v = torch.full((len(texts),len(texts)),-100.0).to(device) + + step = sims_matrix.size(0)//num_tasks + 1 + start = rank*step + end = min(sims_matrix.size(0),start+step) + + for i,sims in enumerate(metric_logger.log_every(sims_matrix[start:end], 50, header)): + + topk_sim, topk_idx = sims.topk(k=config['k_test'], dim=0) + encoder_output = video_feats[topk_idx].to(device,non_blocking=True) + encoder_att = torch.ones(encoder_output.size()[:-1],dtype=torch.long).to(device,non_blocking=True) + output = model.text_encoder(text_ids[start+i].repeat(config['k_test'],1), + attention_mask = text_atts[start+i].repeat(config['k_test'],1), + encoder_hidden_states = encoder_output, + encoder_attention_mask = encoder_att, + return_dict = True, + ) + score = model.itm_head(output.last_hidden_state[:,0,:])[:,1] + score_matrix_t2v[start+i,topk_idx] = score + topk_sim + + if args.distributed: + dist.barrier() + torch.distributed.all_reduce(score_matrix_v2t, op=torch.distributed.ReduceOp.SUM) + torch.distributed.all_reduce(score_matrix_t2v, op=torch.distributed.ReduceOp.SUM) + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Evaluation time {}'.format(total_time_str)) + + return score_matrix_v2t.cpu().numpy(), score_matrix_t2v.cpu().numpy() + + + +@torch.no_grad() +def itm_eval(scores_v2t, scores_t2v, txt2vmg, vid2txt): + + #Video->Text + ranks = np.zeros(scores_v2t.shape[0]) + for index,score in enumerate(scores_v2t): + inds = np.argsort(score)[::-1] + ranks[index] = np.where(inds == vid2txt[index])[0][0] + + # Compute metrics + tr1 = 100.0 * len(np.where(ranks < 1)[0]) / len(ranks) + tr5 = 100.0 * len(np.where(ranks < 5)[0]) / len(ranks) + tr10 = 100.0 * len(np.where(ranks < 10)[0]) / len(ranks) + + #Text->Video + ranks = np.zeros(scores_t2v.shape[0]) + + for index,score in enumerate(scores_t2v): + inds = np.argsort(score)[::-1] + ranks[index] = np.where(inds == txt2vmg[index])[0][0] + + mdR = np.median(ranks+1) + + # Compute metrics + vr1 = 100.0 * len(np.where(ranks < 1)[0]) / len(ranks) + vr5 = 100.0 * len(np.where(ranks < 5)[0]) / len(ranks) + vr10 = 100.0 * len(np.where(ranks < 10)[0]) / len(ranks) + + tr_mean = (tr1 + tr5 + tr10) / 3 + vr_mean = (vr1 + vr5 + vr10) / 3 + r_mean = (tr_mean + vr_mean) / 2 + + eval_result = {'txt_r1': tr1, + 'txt_r5': tr5, + 'txt_r10': tr10, + 'txt_r_mean': tr_mean, + 'vid_r1': vr1, + 'vid_r5': vr5, + 'vid_r10': vr10, + 'vid_r_mean': vr_mean, + 'vid_mdR': mdR, + 'r_mean': r_mean} + return eval_result + + + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating retrieval dataset") + test_dataset = VideoDataset(config['video_root'],config['ann_root'],num_frm=config['num_frm_test'], + max_img_size=config['image_size'], frm_sampling_strategy='uniform') + + test_loader = DataLoader( + test_dataset, + batch_size=config['batch_size'], + num_workers=4, + pin_memory=True, + drop_last=False, + shuffle=False, + ) + + #### Model #### + print("Creating model") + model = blip_retrieval(pretrained=config['pretrained'], image_size=config['image_size'], vit=config['vit']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + score_v2t, score_t2v, = evaluation(model_without_ddp, test_loader, model_without_ddp.tokenizer, device, config) + + if utils.is_main_process(): + + test_result = itm_eval(score_v2t, score_t2v, test_loader.dataset.txt2video, test_loader.dataset.video2txt) + print(test_result) + + log_stats = {**{f'{k}': v for k, v in test_result.items()},} + with open(os.path.join(args.output_dir, "test_result.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/retrieval_msrvtt.yaml') + parser.add_argument('--output_dir', default='output/Retrieval_msrvtt') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/repositories/.placeholder b/repositories/blip/models/__init__.py similarity index 100% rename from repositories/.placeholder rename to repositories/blip/models/__init__.py diff --git a/repositories/blip/models/blip.py b/repositories/blip/models/blip.py new file mode 100644 index 000000000..38678f65e --- /dev/null +++ b/repositories/blip/models/blip.py @@ -0,0 +1,238 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import warnings +warnings.filterwarnings("ignore") + +from models.vit import VisionTransformer, interpolate_pos_embed +from models.med import BertConfig, BertModel, BertLMHeadModel +from transformers import BertTokenizer + +import torch +from torch import nn +import torch.nn.functional as F + +import os +from urllib.parse import urlparse +from timm.models.hub import download_cached_file + +class BLIP_Base(nn.Module): + def __init__(self, + med_config = 'configs/med_config.json', + image_size = 224, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer) + self.tokenizer = init_tokenizer() + med_config = BertConfig.from_json_file(med_config) + med_config.encoder_width = vision_width + self.text_encoder = BertModel(config=med_config, add_pooling_layer=False) + + + def forward(self, image, caption, mode): + + assert mode in ['image', 'text', 'multimodal'], "mode parameter must be image, text, or multimodal" + text = self.tokenizer(caption, return_tensors="pt").to(image.device) + + if mode=='image': + # return image features + image_embeds = self.visual_encoder(image) + return image_embeds + + elif mode=='text': + # return text features + text_output = self.text_encoder(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + return text_output.last_hidden_state + + elif mode=='multimodal': + # return multimodel features + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + + text.input_ids[:,0] = self.tokenizer.enc_token_id + output = self.text_encoder(text.input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True, + ) + return output.last_hidden_state + + + +class BLIP_Decoder(nn.Module): + def __init__(self, + med_config = 'configs/med_config.json', + image_size = 384, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + prompt = 'a picture of ', + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer) + self.tokenizer = init_tokenizer() + med_config = BertConfig.from_json_file(med_config) + med_config.encoder_width = vision_width + self.text_decoder = BertLMHeadModel(config=med_config) + + self.prompt = prompt + self.prompt_length = len(self.tokenizer(self.prompt).input_ids)-1 + + + def forward(self, image, caption): + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + + text = self.tokenizer(caption, padding='longest', truncation=True, max_length=40, return_tensors="pt").to(image.device) + + text.input_ids[:,0] = self.tokenizer.bos_token_id + + decoder_targets = text.input_ids.masked_fill(text.input_ids == self.tokenizer.pad_token_id, -100) + decoder_targets[:,:self.prompt_length] = -100 + + decoder_output = self.text_decoder(text.input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + labels = decoder_targets, + return_dict = True, + ) + loss_lm = decoder_output.loss + + return loss_lm + + def generate(self, image, sample=False, num_beams=3, max_length=30, min_length=10, top_p=0.9, repetition_penalty=1.0): + image_embeds = self.visual_encoder(image) + + if not sample: + image_embeds = image_embeds.repeat_interleave(num_beams,dim=0) + + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + model_kwargs = {"encoder_hidden_states": image_embeds, "encoder_attention_mask":image_atts} + + prompt = [self.prompt] * image.size(0) + input_ids = self.tokenizer(prompt, return_tensors="pt").input_ids.to(image.device) + input_ids[:,0] = self.tokenizer.bos_token_id + input_ids = input_ids[:, :-1] + + if sample: + #nucleus sampling + outputs = self.text_decoder.generate(input_ids=input_ids, + max_length=max_length, + min_length=min_length, + do_sample=True, + top_p=top_p, + num_return_sequences=1, + eos_token_id=self.tokenizer.sep_token_id, + pad_token_id=self.tokenizer.pad_token_id, + repetition_penalty=1.1, + **model_kwargs) + else: + #beam search + outputs = self.text_decoder.generate(input_ids=input_ids, + max_length=max_length, + min_length=min_length, + num_beams=num_beams, + eos_token_id=self.tokenizer.sep_token_id, + pad_token_id=self.tokenizer.pad_token_id, + repetition_penalty=repetition_penalty, + **model_kwargs) + + captions = [] + for output in outputs: + caption = self.tokenizer.decode(output, skip_special_tokens=True) + captions.append(caption[len(self.prompt):]) + return captions + + +def blip_decoder(pretrained='',**kwargs): + model = BLIP_Decoder(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) + assert(len(msg.missing_keys)==0) + return model + +def blip_feature_extractor(pretrained='',**kwargs): + model = BLIP_Base(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) + assert(len(msg.missing_keys)==0) + return model + +def init_tokenizer(): + tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') + tokenizer.add_special_tokens({'bos_token':'[DEC]'}) + tokenizer.add_special_tokens({'additional_special_tokens':['[ENC]']}) + tokenizer.enc_token_id = tokenizer.additional_special_tokens_ids[0] + return tokenizer + + +def create_vit(vit, image_size, use_grad_checkpointing=False, ckpt_layer=0, drop_path_rate=0): + + assert vit in ['base', 'large'], "vit parameter must be base or large" + if vit=='base': + vision_width = 768 + visual_encoder = VisionTransformer(img_size=image_size, patch_size=16, embed_dim=vision_width, depth=12, + num_heads=12, use_grad_checkpointing=use_grad_checkpointing, ckpt_layer=ckpt_layer, + drop_path_rate=0 or drop_path_rate + ) + elif vit=='large': + vision_width = 1024 + visual_encoder = VisionTransformer(img_size=image_size, patch_size=16, embed_dim=vision_width, depth=24, + num_heads=16, use_grad_checkpointing=use_grad_checkpointing, ckpt_layer=ckpt_layer, + drop_path_rate=0.1 or drop_path_rate + ) + return visual_encoder, vision_width + +def is_url(url_or_filename): + parsed = urlparse(url_or_filename) + return parsed.scheme in ("http", "https") + +def load_checkpoint(model,url_or_filename): + if is_url(url_or_filename): + cached_file = download_cached_file(url_or_filename, check_hash=False, progress=True) + checkpoint = torch.load(cached_file, map_location='cpu') + elif os.path.isfile(url_or_filename): + checkpoint = torch.load(url_or_filename, map_location='cpu') + else: + raise RuntimeError('checkpoint url or path is invalid') + + state_dict = checkpoint['model'] + + state_dict['visual_encoder.pos_embed'] = interpolate_pos_embed(state_dict['visual_encoder.pos_embed'],model.visual_encoder) + if 'visual_encoder_m.pos_embed' in model.state_dict().keys(): + state_dict['visual_encoder_m.pos_embed'] = interpolate_pos_embed(state_dict['visual_encoder_m.pos_embed'], + model.visual_encoder_m) + for key in model.state_dict().keys(): + if key in state_dict.keys(): + if state_dict[key].shape!=model.state_dict()[key].shape: + del state_dict[key] + + msg = model.load_state_dict(state_dict,strict=False) + print('load checkpoint from %s'%url_or_filename) + return model,msg + diff --git a/repositories/blip/models/blip_itm.py b/repositories/blip/models/blip_itm.py new file mode 100644 index 000000000..cf354c829 --- /dev/null +++ b/repositories/blip/models/blip_itm.py @@ -0,0 +1,76 @@ +from models.med import BertConfig, BertModel +from transformers import BertTokenizer + +import torch +from torch import nn +import torch.nn.functional as F + +from models.blip import create_vit, init_tokenizer, load_checkpoint + +class BLIP_ITM(nn.Module): + def __init__(self, + med_config = 'configs/med_config.json', + image_size = 384, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + embed_dim = 256, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer) + self.tokenizer = init_tokenizer() + med_config = BertConfig.from_json_file(med_config) + med_config.encoder_width = vision_width + self.text_encoder = BertModel(config=med_config, add_pooling_layer=False) + + text_width = self.text_encoder.config.hidden_size + + self.vision_proj = nn.Linear(vision_width, embed_dim) + self.text_proj = nn.Linear(text_width, embed_dim) + + self.itm_head = nn.Linear(text_width, 2) + + + def forward(self, image, caption, match_head='itm'): + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + + text = self.tokenizer(caption, padding='max_length', truncation=True, max_length=35, + return_tensors="pt").to(image.device) + + + if match_head=='itm': + output = self.text_encoder(text.input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True, + ) + itm_output = self.itm_head(output.last_hidden_state[:,0,:]) + return itm_output + + elif match_head=='itc': + text_output = self.text_encoder(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + image_feat = F.normalize(self.vision_proj(image_embeds[:,0,:]),dim=-1) + text_feat = F.normalize(self.text_proj(text_output.last_hidden_state[:,0,:]),dim=-1) + + sim = image_feat @ text_feat.t() + return sim + + +def blip_itm(pretrained='',**kwargs): + model = BLIP_ITM(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) + assert(len(msg.missing_keys)==0) + return model + \ No newline at end of file diff --git a/repositories/blip/models/blip_nlvr.py b/repositories/blip/models/blip_nlvr.py new file mode 100644 index 000000000..84837167b --- /dev/null +++ b/repositories/blip/models/blip_nlvr.py @@ -0,0 +1,103 @@ +from models.med import BertConfig +from models.nlvr_encoder import BertModel +from models.vit import interpolate_pos_embed +from models.blip import create_vit, init_tokenizer, is_url + +from timm.models.hub import download_cached_file + +import torch +from torch import nn +import torch.nn.functional as F +from transformers import BertTokenizer +import numpy as np + +class BLIP_NLVR(nn.Module): + def __init__(self, + med_config = 'configs/med_config.json', + image_size = 480, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer, drop_path_rate=0.1) + self.tokenizer = init_tokenizer() + med_config = BertConfig.from_json_file(med_config) + med_config.encoder_width = vision_width + self.text_encoder = BertModel(config=med_config, add_pooling_layer=False) + + self.cls_head = nn.Sequential( + nn.Linear(self.text_encoder.config.hidden_size, self.text_encoder.config.hidden_size), + nn.ReLU(), + nn.Linear(self.text_encoder.config.hidden_size, 2) + ) + + def forward(self, image, text, targets, train=True): + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + image0_embeds, image1_embeds = torch.split(image_embeds,targets.size(0)) + + text = self.tokenizer(text, padding='longest', return_tensors="pt").to(image.device) + text.input_ids[:,0] = self.tokenizer.enc_token_id + + output = self.text_encoder(text.input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = [image0_embeds,image1_embeds], + encoder_attention_mask = [image_atts[:image0_embeds.size(0)], + image_atts[image0_embeds.size(0):]], + return_dict = True, + ) + hidden_state = output.last_hidden_state[:,0,:] + prediction = self.cls_head(hidden_state) + + if train: + loss = F.cross_entropy(prediction, targets) + return loss + else: + return prediction + +def blip_nlvr(pretrained='',**kwargs): + model = BLIP_NLVR(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) + print("missing keys:") + print(msg.missing_keys) + return model + + +def load_checkpoint(model,url_or_filename): + if is_url(url_or_filename): + cached_file = download_cached_file(url_or_filename, check_hash=False, progress=True) + checkpoint = torch.load(cached_file, map_location='cpu') + elif os.path.isfile(url_or_filename): + checkpoint = torch.load(url_or_filename, map_location='cpu') + else: + raise RuntimeError('checkpoint url or path is invalid') + state_dict = checkpoint['model'] + + state_dict['visual_encoder.pos_embed'] = interpolate_pos_embed(state_dict['visual_encoder.pos_embed'],model.visual_encoder) + + for key in list(state_dict.keys()): + if 'crossattention.self.' in key: + new_key0 = key.replace('self','self0') + new_key1 = key.replace('self','self1') + state_dict[new_key0] = state_dict[key] + state_dict[new_key1] = state_dict[key] + elif 'crossattention.output.dense.' in key: + new_key0 = key.replace('dense','dense0') + new_key1 = key.replace('dense','dense1') + state_dict[new_key0] = state_dict[key] + state_dict[new_key1] = state_dict[key] + + msg = model.load_state_dict(state_dict,strict=False) + print('load checkpoint from %s'%url_or_filename) + return model,msg + \ No newline at end of file diff --git a/repositories/blip/models/blip_pretrain.py b/repositories/blip/models/blip_pretrain.py new file mode 100644 index 000000000..e42ce5f99 --- /dev/null +++ b/repositories/blip/models/blip_pretrain.py @@ -0,0 +1,339 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +from models.med import BertConfig, BertModel, BertLMHeadModel +from transformers import BertTokenizer +import transformers +transformers.logging.set_verbosity_error() + +import torch +from torch import nn +import torch.nn.functional as F + +from models.blip import create_vit, init_tokenizer, load_checkpoint + +class BLIP_Pretrain(nn.Module): + def __init__(self, + med_config = 'configs/bert_config.json', + image_size = 224, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + embed_dim = 256, + queue_size = 57600, + momentum = 0.995, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer, 0) + + if vit=='base': + checkpoint = torch.hub.load_state_dict_from_url( + url="https://dl.fbaipublicfiles.com/deit/deit_base_patch16_224-b5f2ef4d.pth", + map_location="cpu", check_hash=True) + state_dict = checkpoint["model"] + msg = self.visual_encoder.load_state_dict(state_dict,strict=False) + elif vit=='large': + from timm.models.helpers import load_custom_pretrained + from timm.models.vision_transformer import default_cfgs + load_custom_pretrained(self.visual_encoder,default_cfgs['vit_large_patch16_224_in21k']) + + self.tokenizer = init_tokenizer() + encoder_config = BertConfig.from_json_file(med_config) + encoder_config.encoder_width = vision_width + self.text_encoder = BertModel.from_pretrained('bert-base-uncased',config=encoder_config, add_pooling_layer=False) + self.text_encoder.resize_token_embeddings(len(self.tokenizer)) + + text_width = self.text_encoder.config.hidden_size + + self.vision_proj = nn.Linear(vision_width, embed_dim) + self.text_proj = nn.Linear(text_width, embed_dim) + + self.itm_head = nn.Linear(text_width, 2) + + # create momentum encoders + self.visual_encoder_m, vision_width = create_vit(vit,image_size) + self.vision_proj_m = nn.Linear(vision_width, embed_dim) + self.text_encoder_m = BertModel(config=encoder_config, add_pooling_layer=False) + self.text_proj_m = nn.Linear(text_width, embed_dim) + + self.model_pairs = [[self.visual_encoder,self.visual_encoder_m], + [self.vision_proj,self.vision_proj_m], + [self.text_encoder,self.text_encoder_m], + [self.text_proj,self.text_proj_m], + ] + self.copy_params() + + # create the queue + self.register_buffer("image_queue", torch.randn(embed_dim, queue_size)) + self.register_buffer("text_queue", torch.randn(embed_dim, queue_size)) + self.register_buffer("queue_ptr", torch.zeros(1, dtype=torch.long)) + + self.image_queue = nn.functional.normalize(self.image_queue, dim=0) + self.text_queue = nn.functional.normalize(self.text_queue, dim=0) + + self.queue_size = queue_size + self.momentum = momentum + self.temp = nn.Parameter(0.07*torch.ones([])) + + # create the decoder + decoder_config = BertConfig.from_json_file(med_config) + decoder_config.encoder_width = vision_width + self.text_decoder = BertLMHeadModel.from_pretrained('bert-base-uncased',config=decoder_config) + self.text_decoder.resize_token_embeddings(len(self.tokenizer)) + tie_encoder_decoder_weights(self.text_encoder,self.text_decoder.bert,'','/attention') + + + def forward(self, image, caption, alpha): + with torch.no_grad(): + self.temp.clamp_(0.001,0.5) + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + image_feat = F.normalize(self.vision_proj(image_embeds[:,0,:]),dim=-1) + + text = self.tokenizer(caption, padding='max_length', truncation=True, max_length=30, + return_tensors="pt").to(image.device) + text_output = self.text_encoder(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + text_feat = F.normalize(self.text_proj(text_output.last_hidden_state[:,0,:]),dim=-1) + + # get momentum features + with torch.no_grad(): + self._momentum_update() + image_embeds_m = self.visual_encoder_m(image) + image_feat_m = F.normalize(self.vision_proj_m(image_embeds_m[:,0,:]),dim=-1) + image_feat_all = torch.cat([image_feat_m.t(),self.image_queue.clone().detach()],dim=1) + + text_output_m = self.text_encoder_m(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + text_feat_m = F.normalize(self.text_proj_m(text_output_m.last_hidden_state[:,0,:]),dim=-1) + text_feat_all = torch.cat([text_feat_m.t(),self.text_queue.clone().detach()],dim=1) + + sim_i2t_m = image_feat_m @ text_feat_all / self.temp + sim_t2i_m = text_feat_m @ image_feat_all / self.temp + + sim_targets = torch.zeros(sim_i2t_m.size()).to(image.device) + sim_targets.fill_diagonal_(1) + + sim_i2t_targets = alpha * F.softmax(sim_i2t_m, dim=1) + (1 - alpha) * sim_targets + sim_t2i_targets = alpha * F.softmax(sim_t2i_m, dim=1) + (1 - alpha) * sim_targets + + sim_i2t = image_feat @ text_feat_all / self.temp + sim_t2i = text_feat @ image_feat_all / self.temp + + loss_i2t = -torch.sum(F.log_softmax(sim_i2t, dim=1)*sim_i2t_targets,dim=1).mean() + loss_t2i = -torch.sum(F.log_softmax(sim_t2i, dim=1)*sim_t2i_targets,dim=1).mean() + + loss_ita = (loss_i2t+loss_t2i)/2 + + self._dequeue_and_enqueue(image_feat_m, text_feat_m) + + ###============== Image-text Matching ===================### + encoder_input_ids = text.input_ids.clone() + encoder_input_ids[:,0] = self.tokenizer.enc_token_id + + # forward the positve image-text pair + bs = image.size(0) + output_pos = self.text_encoder(encoder_input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True, + ) + with torch.no_grad(): + weights_t2i = F.softmax(sim_t2i[:,:bs],dim=1)+1e-4 + weights_t2i.fill_diagonal_(0) + weights_i2t = F.softmax(sim_i2t[:,:bs],dim=1)+1e-4 + weights_i2t.fill_diagonal_(0) + + # select a negative image for each text + image_embeds_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_t2i[b], 1).item() + image_embeds_neg.append(image_embeds[neg_idx]) + image_embeds_neg = torch.stack(image_embeds_neg,dim=0) + + # select a negative text for each image + text_ids_neg = [] + text_atts_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_i2t[b], 1).item() + text_ids_neg.append(encoder_input_ids[neg_idx]) + text_atts_neg.append(text.attention_mask[neg_idx]) + + text_ids_neg = torch.stack(text_ids_neg,dim=0) + text_atts_neg = torch.stack(text_atts_neg,dim=0) + + text_ids_all = torch.cat([encoder_input_ids, text_ids_neg],dim=0) + text_atts_all = torch.cat([text.attention_mask, text_atts_neg],dim=0) + + image_embeds_all = torch.cat([image_embeds_neg,image_embeds],dim=0) + image_atts_all = torch.cat([image_atts,image_atts],dim=0) + + output_neg = self.text_encoder(text_ids_all, + attention_mask = text_atts_all, + encoder_hidden_states = image_embeds_all, + encoder_attention_mask = image_atts_all, + return_dict = True, + ) + + vl_embeddings = torch.cat([output_pos.last_hidden_state[:,0,:], output_neg.last_hidden_state[:,0,:]],dim=0) + vl_output = self.itm_head(vl_embeddings) + + itm_labels = torch.cat([torch.ones(bs,dtype=torch.long),torch.zeros(2*bs,dtype=torch.long)], + dim=0).to(image.device) + loss_itm = F.cross_entropy(vl_output, itm_labels) + + ##================= LM ========================## + decoder_input_ids = text.input_ids.clone() + decoder_input_ids[:,0] = self.tokenizer.bos_token_id + decoder_targets = decoder_input_ids.masked_fill(decoder_input_ids == self.tokenizer.pad_token_id, -100) + + decoder_output = self.text_decoder(decoder_input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + labels = decoder_targets, + return_dict = True, + ) + + loss_lm = decoder_output.loss + return loss_ita, loss_itm, loss_lm + + + + @torch.no_grad() + def copy_params(self): + for model_pair in self.model_pairs: + for param, param_m in zip(model_pair[0].parameters(), model_pair[1].parameters()): + param_m.data.copy_(param.data) # initialize + param_m.requires_grad = False # not update by gradient + + + @torch.no_grad() + def _momentum_update(self): + for model_pair in self.model_pairs: + for param, param_m in zip(model_pair[0].parameters(), model_pair[1].parameters()): + param_m.data = param_m.data * self.momentum + param.data * (1. - self.momentum) + + + @torch.no_grad() + def _dequeue_and_enqueue(self, image_feat, text_feat): + # gather keys before updating queue + image_feats = concat_all_gather(image_feat) + text_feats = concat_all_gather(text_feat) + + batch_size = image_feats.shape[0] + + ptr = int(self.queue_ptr) + assert self.queue_size % batch_size == 0 # for simplicity + + # replace the keys at ptr (dequeue and enqueue) + self.image_queue[:, ptr:ptr + batch_size] = image_feats.T + self.text_queue[:, ptr:ptr + batch_size] = text_feats.T + ptr = (ptr + batch_size) % self.queue_size # move pointer + + self.queue_ptr[0] = ptr + + +def blip_pretrain(**kwargs): + model = BLIP_Pretrain(**kwargs) + return model + + +@torch.no_grad() +def concat_all_gather(tensor): + """ + Performs all_gather operation on the provided tensors. + *** Warning ***: torch.distributed.all_gather has no gradient. + """ + tensors_gather = [torch.ones_like(tensor) + for _ in range(torch.distributed.get_world_size())] + torch.distributed.all_gather(tensors_gather, tensor, async_op=False) + + output = torch.cat(tensors_gather, dim=0) + return output + + +from typing import List +def tie_encoder_decoder_weights(encoder: nn.Module, decoder: nn.Module, base_model_prefix: str, skip_key:str): + uninitialized_encoder_weights: List[str] = [] + if decoder.__class__ != encoder.__class__: + logger.info( + f"{decoder.__class__} and {encoder.__class__} are not equal. In this case make sure that all encoder weights are correctly initialized." + ) + + def tie_encoder_to_decoder_recursively( + decoder_pointer: nn.Module, + encoder_pointer: nn.Module, + module_name: str, + uninitialized_encoder_weights: List[str], + skip_key: str, + depth=0, + ): + assert isinstance(decoder_pointer, nn.Module) and isinstance( + encoder_pointer, nn.Module + ), f"{decoder_pointer} and {encoder_pointer} have to be of type torch.nn.Module" + if hasattr(decoder_pointer, "weight") and skip_key not in module_name: + assert hasattr(encoder_pointer, "weight") + encoder_pointer.weight = decoder_pointer.weight + if hasattr(decoder_pointer, "bias"): + assert hasattr(encoder_pointer, "bias") + encoder_pointer.bias = decoder_pointer.bias + print(module_name+' is tied') + return + + encoder_modules = encoder_pointer._modules + decoder_modules = decoder_pointer._modules + if len(decoder_modules) > 0: + assert ( + len(encoder_modules) > 0 + ), f"Encoder module {encoder_pointer} does not match decoder module {decoder_pointer}" + + all_encoder_weights = set([module_name + "/" + sub_name for sub_name in encoder_modules.keys()]) + encoder_layer_pos = 0 + for name, module in decoder_modules.items(): + if name.isdigit(): + encoder_name = str(int(name) + encoder_layer_pos) + decoder_name = name + if not isinstance(decoder_modules[decoder_name], type(encoder_modules[encoder_name])) and len( + encoder_modules + ) != len(decoder_modules): + # this can happen if the name corresponds to the position in a list module list of layers + # in this case the decoder has added a cross-attention that the encoder does not have + # thus skip this step and subtract one layer pos from encoder + encoder_layer_pos -= 1 + continue + elif name not in encoder_modules: + continue + elif depth > 500: + raise ValueError( + "Max depth of recursive function `tie_encoder_to_decoder` reached. It seems that there is a circular dependency between two or more `nn.Modules` of your model." + ) + else: + decoder_name = encoder_name = name + tie_encoder_to_decoder_recursively( + decoder_modules[decoder_name], + encoder_modules[encoder_name], + module_name + "/" + name, + uninitialized_encoder_weights, + skip_key, + depth=depth + 1, + ) + all_encoder_weights.remove(module_name + "/" + encoder_name) + + uninitialized_encoder_weights += list(all_encoder_weights) + + # tie weights recursively + tie_encoder_to_decoder_recursively(decoder, encoder, base_model_prefix, uninitialized_encoder_weights, skip_key) diff --git a/repositories/blip/models/blip_retrieval.py b/repositories/blip/models/blip_retrieval.py new file mode 100644 index 000000000..1debe7e2e --- /dev/null +++ b/repositories/blip/models/blip_retrieval.py @@ -0,0 +1,319 @@ +from models.med import BertConfig, BertModel +from transformers import BertTokenizer + +import torch +from torch import nn +import torch.nn.functional as F + +from models.blip import create_vit, init_tokenizer, load_checkpoint + +class BLIP_Retrieval(nn.Module): + def __init__(self, + med_config = 'configs/med_config.json', + image_size = 384, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + embed_dim = 256, + queue_size = 57600, + momentum = 0.995, + negative_all_rank = False, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer) + self.tokenizer = init_tokenizer() + med_config = BertConfig.from_json_file(med_config) + med_config.encoder_width = vision_width + self.text_encoder = BertModel(config=med_config, add_pooling_layer=False) + + text_width = self.text_encoder.config.hidden_size + + self.vision_proj = nn.Linear(vision_width, embed_dim) + self.text_proj = nn.Linear(text_width, embed_dim) + + self.itm_head = nn.Linear(text_width, 2) + + # create momentum encoders + self.visual_encoder_m, vision_width = create_vit(vit,image_size) + self.vision_proj_m = nn.Linear(vision_width, embed_dim) + self.text_encoder_m = BertModel(config=med_config, add_pooling_layer=False) + self.text_proj_m = nn.Linear(text_width, embed_dim) + + self.model_pairs = [[self.visual_encoder,self.visual_encoder_m], + [self.vision_proj,self.vision_proj_m], + [self.text_encoder,self.text_encoder_m], + [self.text_proj,self.text_proj_m], + ] + self.copy_params() + + # create the queue + self.register_buffer("image_queue", torch.randn(embed_dim, queue_size)) + self.register_buffer("text_queue", torch.randn(embed_dim, queue_size)) + self.register_buffer("idx_queue", torch.full((1,queue_size),-100)) + self.register_buffer("ptr_queue", torch.zeros(1, dtype=torch.long)) + + self.image_queue = nn.functional.normalize(self.image_queue, dim=0) + self.text_queue = nn.functional.normalize(self.text_queue, dim=0) + + self.queue_size = queue_size + self.momentum = momentum + self.temp = nn.Parameter(0.07*torch.ones([])) + + self.negative_all_rank = negative_all_rank + + + def forward(self, image, caption, alpha, idx): + with torch.no_grad(): + self.temp.clamp_(0.001,0.5) + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + image_feat = F.normalize(self.vision_proj(image_embeds[:,0,:]),dim=-1) + + text = self.tokenizer(caption, padding='max_length', truncation=True, max_length=35, + return_tensors="pt").to(image.device) + + text_output = self.text_encoder(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + text_feat = F.normalize(self.text_proj(text_output.last_hidden_state[:,0,:]),dim=-1) + + ###============== Image-text Contrastive Learning ===================### + idx = idx.view(-1,1) + idx_all = torch.cat([idx.t(), self.idx_queue.clone().detach()],dim=1) + pos_idx = torch.eq(idx, idx_all).float() + sim_targets = pos_idx / pos_idx.sum(1,keepdim=True) + + # get momentum features + with torch.no_grad(): + self._momentum_update() + image_embeds_m = self.visual_encoder_m(image) + image_feat_m = F.normalize(self.vision_proj_m(image_embeds_m[:,0,:]),dim=-1) + image_feat_m_all = torch.cat([image_feat_m.t(),self.image_queue.clone().detach()],dim=1) + + text_output_m = self.text_encoder_m(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + text_feat_m = F.normalize(self.text_proj_m(text_output_m.last_hidden_state[:,0,:]),dim=-1) + text_feat_m_all = torch.cat([text_feat_m.t(),self.text_queue.clone().detach()],dim=1) + + sim_i2t_m = image_feat_m @ text_feat_m_all / self.temp + sim_t2i_m = text_feat_m @ image_feat_m_all / self.temp + + sim_i2t_targets = alpha * F.softmax(sim_i2t_m, dim=1) + (1 - alpha) * sim_targets + sim_t2i_targets = alpha * F.softmax(sim_t2i_m, dim=1) + (1 - alpha) * sim_targets + + sim_i2t = image_feat @ text_feat_m_all / self.temp + sim_t2i = text_feat @ image_feat_m_all / self.temp + + loss_i2t = -torch.sum(F.log_softmax(sim_i2t, dim=1)*sim_i2t_targets,dim=1).mean() + loss_t2i = -torch.sum(F.log_softmax(sim_t2i, dim=1)*sim_t2i_targets,dim=1).mean() + + loss_ita = (loss_i2t+loss_t2i)/2 + + idxs = concat_all_gather(idx) + self._dequeue_and_enqueue(image_feat_m, text_feat_m, idxs) + + ###============== Image-text Matching ===================### + encoder_input_ids = text.input_ids.clone() + encoder_input_ids[:,0] = self.tokenizer.enc_token_id + + # forward the positve image-text pair + bs = image.size(0) + output_pos = self.text_encoder(encoder_input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True, + ) + + + if self.negative_all_rank: + # compute sample similarity + with torch.no_grad(): + mask = torch.eq(idx, idxs.t()) + + image_feat_world = concat_all_gather(image_feat) + text_feat_world = concat_all_gather(text_feat) + + sim_i2t = image_feat @ text_feat_world.t() / self.temp + sim_t2i = text_feat @ image_feat_world.t() / self.temp + + weights_i2t = F.softmax(sim_i2t,dim=1) + weights_i2t.masked_fill_(mask, 0) + + weights_t2i = F.softmax(sim_t2i,dim=1) + weights_t2i.masked_fill_(mask, 0) + + image_embeds_world = all_gather_with_grad(image_embeds) + + # select a negative image (from all ranks) for each text + image_embeds_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_t2i[b], 1).item() + image_embeds_neg.append(image_embeds_world[neg_idx]) + image_embeds_neg = torch.stack(image_embeds_neg,dim=0) + + # select a negative text (from all ranks) for each image + input_ids_world = concat_all_gather(encoder_input_ids) + att_mask_world = concat_all_gather(text.attention_mask) + + text_ids_neg = [] + text_atts_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_i2t[b], 1).item() + text_ids_neg.append(input_ids_world[neg_idx]) + text_atts_neg.append(att_mask_world[neg_idx]) + + else: + with torch.no_grad(): + mask = torch.eq(idx, idx.t()) + + sim_i2t = image_feat @ text_feat.t() / self.temp + sim_t2i = text_feat @ image_feat.t() / self.temp + + weights_i2t = F.softmax(sim_i2t,dim=1) + weights_i2t.masked_fill_(mask, 0) + + weights_t2i = F.softmax(sim_t2i,dim=1) + weights_t2i.masked_fill_(mask, 0) + + # select a negative image (from same rank) for each text + image_embeds_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_t2i[b], 1).item() + image_embeds_neg.append(image_embeds[neg_idx]) + image_embeds_neg = torch.stack(image_embeds_neg,dim=0) + + # select a negative text (from same rank) for each image + text_ids_neg = [] + text_atts_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_i2t[b], 1).item() + text_ids_neg.append(encoder_input_ids[neg_idx]) + text_atts_neg.append(text.attention_mask[neg_idx]) + + text_ids_neg = torch.stack(text_ids_neg,dim=0) + text_atts_neg = torch.stack(text_atts_neg,dim=0) + + text_ids_all = torch.cat([encoder_input_ids, text_ids_neg],dim=0) + text_atts_all = torch.cat([text.attention_mask, text_atts_neg],dim=0) + + image_embeds_all = torch.cat([image_embeds_neg,image_embeds],dim=0) + image_atts_all = torch.cat([image_atts,image_atts],dim=0) + + output_neg = self.text_encoder(text_ids_all, + attention_mask = text_atts_all, + encoder_hidden_states = image_embeds_all, + encoder_attention_mask = image_atts_all, + return_dict = True, + ) + + + vl_embeddings = torch.cat([output_pos.last_hidden_state[:,0,:], output_neg.last_hidden_state[:,0,:]],dim=0) + vl_output = self.itm_head(vl_embeddings) + + itm_labels = torch.cat([torch.ones(bs,dtype=torch.long),torch.zeros(2*bs,dtype=torch.long)], + dim=0).to(image.device) + loss_itm = F.cross_entropy(vl_output, itm_labels) + + return loss_ita, loss_itm + + + @torch.no_grad() + def copy_params(self): + for model_pair in self.model_pairs: + for param, param_m in zip(model_pair[0].parameters(), model_pair[1].parameters()): + param_m.data.copy_(param.data) # initialize + param_m.requires_grad = False # not update by gradient + + + @torch.no_grad() + def _momentum_update(self): + for model_pair in self.model_pairs: + for param, param_m in zip(model_pair[0].parameters(), model_pair[1].parameters()): + param_m.data = param_m.data * self.momentum + param.data * (1. - self.momentum) + + + @torch.no_grad() + def _dequeue_and_enqueue(self, image_feat, text_feat, idxs): + # gather keys before updating queue + image_feats = concat_all_gather(image_feat) + text_feats = concat_all_gather(text_feat) + + + batch_size = image_feats.shape[0] + + ptr = int(self.ptr_queue) + assert self.queue_size % batch_size == 0 # for simplicity + + # replace the keys at ptr (dequeue and enqueue) + self.image_queue[:, ptr:ptr + batch_size] = image_feats.T + self.text_queue[:, ptr:ptr + batch_size] = text_feats.T + self.idx_queue[:, ptr:ptr + batch_size] = idxs.T + ptr = (ptr + batch_size) % self.queue_size # move pointer + + self.ptr_queue[0] = ptr + + +def blip_retrieval(pretrained='',**kwargs): + model = BLIP_Retrieval(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) + print("missing keys:") + print(msg.missing_keys) + return model + + +@torch.no_grad() +def concat_all_gather(tensor): + """ + Performs all_gather operation on the provided tensors. + *** Warning ***: torch.distributed.all_gather has no gradient. + """ + tensors_gather = [torch.ones_like(tensor) + for _ in range(torch.distributed.get_world_size())] + torch.distributed.all_gather(tensors_gather, tensor, async_op=False) + + output = torch.cat(tensors_gather, dim=0) + return output + + +class GatherLayer(torch.autograd.Function): + """ + Gather tensors from all workers with support for backward propagation: + This implementation does not cut the gradients as torch.distributed.all_gather does. + """ + + @staticmethod + def forward(ctx, x): + output = [torch.zeros_like(x) for _ in range(torch.distributed.get_world_size())] + torch.distributed.all_gather(output, x) + return tuple(output) + + @staticmethod + def backward(ctx, *grads): + all_gradients = torch.stack(grads) + torch.distributed.all_reduce(all_gradients) + return all_gradients[torch.distributed.get_rank()] + + +def all_gather_with_grad(tensors): + """ + Performs all_gather operation on the provided tensors. + Graph remains connected for backward grad computation. + """ + # Queue the gathered tensors + world_size = torch.distributed.get_world_size() + # There is no need for reduction in the single-proc case + if world_size == 1: + return tensors + + tensor_all = GatherLayer.apply(tensors) + + return torch.cat(tensor_all, dim=0) diff --git a/repositories/blip/models/blip_vqa.py b/repositories/blip/models/blip_vqa.py new file mode 100644 index 000000000..d4cb3688f --- /dev/null +++ b/repositories/blip/models/blip_vqa.py @@ -0,0 +1,186 @@ +from models.med import BertConfig, BertModel, BertLMHeadModel +from models.blip import create_vit, init_tokenizer, load_checkpoint + +import torch +from torch import nn +import torch.nn.functional as F +from transformers import BertTokenizer +import numpy as np + +class BLIP_VQA(nn.Module): + def __init__(self, + med_config = 'configs/med_config.json', + image_size = 480, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit, image_size, vit_grad_ckpt, vit_ckpt_layer, drop_path_rate=0.1) + self.tokenizer = init_tokenizer() + + encoder_config = BertConfig.from_json_file(med_config) + encoder_config.encoder_width = vision_width + self.text_encoder = BertModel(config=encoder_config, add_pooling_layer=False) + + decoder_config = BertConfig.from_json_file(med_config) + self.text_decoder = BertLMHeadModel(config=decoder_config) + + + def forward(self, image, question, answer=None, n=None, weights=None, train=True, inference='rank', k_test=128): + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + + question = self.tokenizer(question, padding='longest', truncation=True, max_length=35, + return_tensors="pt").to(image.device) + question.input_ids[:,0] = self.tokenizer.enc_token_id + + if train: + ''' + n: number of answers for each question + weights: weight for each answer + ''' + answer = self.tokenizer(answer, padding='longest', return_tensors="pt").to(image.device) + answer.input_ids[:,0] = self.tokenizer.bos_token_id + answer_targets = answer.input_ids.masked_fill(answer.input_ids == self.tokenizer.pad_token_id, -100) + + question_output = self.text_encoder(question.input_ids, + attention_mask = question.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True) + + question_states = [] + question_atts = [] + for b, n in enumerate(n): + question_states += [question_output.last_hidden_state[b]]*n + question_atts += [question.attention_mask[b]]*n + question_states = torch.stack(question_states,0) + question_atts = torch.stack(question_atts,0) + + answer_output = self.text_decoder(answer.input_ids, + attention_mask = answer.attention_mask, + encoder_hidden_states = question_states, + encoder_attention_mask = question_atts, + labels = answer_targets, + return_dict = True, + reduction = 'none', + ) + + loss = weights * answer_output.loss + loss = loss.sum()/image.size(0) + + return loss + + + else: + question_output = self.text_encoder(question.input_ids, + attention_mask = question.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True) + + if inference=='generate': + num_beams = 3 + question_states = question_output.last_hidden_state.repeat_interleave(num_beams,dim=0) + question_atts = torch.ones(question_states.size()[:-1],dtype=torch.long).to(question_states.device) + model_kwargs = {"encoder_hidden_states": question_states, "encoder_attention_mask":question_atts} + + bos_ids = torch.full((image.size(0),1),fill_value=self.tokenizer.bos_token_id,device=image.device) + + outputs = self.text_decoder.generate(input_ids=bos_ids, + max_length=10, + min_length=1, + num_beams=num_beams, + eos_token_id=self.tokenizer.sep_token_id, + pad_token_id=self.tokenizer.pad_token_id, + **model_kwargs) + + answers = [] + for output in outputs: + answer = self.tokenizer.decode(output, skip_special_tokens=True) + answers.append(answer) + return answers + + elif inference=='rank': + max_ids = self.rank_answer(question_output.last_hidden_state, question.attention_mask, + answer.input_ids, answer.attention_mask, k_test) + return max_ids + + + + def rank_answer(self, question_states, question_atts, answer_ids, answer_atts, k): + + num_ques = question_states.size(0) + start_ids = answer_ids[0,0].repeat(num_ques,1) # bos token + + start_output = self.text_decoder(start_ids, + encoder_hidden_states = question_states, + encoder_attention_mask = question_atts, + return_dict = True, + reduction = 'none') + logits = start_output.logits[:,0,:] # first token's logit + + # topk_probs: top-k probability + # topk_ids: [num_question, k] + answer_first_token = answer_ids[:,1] + prob_first_token = F.softmax(logits,dim=1).index_select(dim=1, index=answer_first_token) + topk_probs, topk_ids = prob_first_token.topk(k,dim=1) + + # answer input: [num_question*k, answer_len] + input_ids = [] + input_atts = [] + for b, topk_id in enumerate(topk_ids): + input_ids.append(answer_ids.index_select(dim=0, index=topk_id)) + input_atts.append(answer_atts.index_select(dim=0, index=topk_id)) + input_ids = torch.cat(input_ids,dim=0) + input_atts = torch.cat(input_atts,dim=0) + + targets_ids = input_ids.masked_fill(input_ids == self.tokenizer.pad_token_id, -100) + + # repeat encoder's output for top-k answers + question_states = tile(question_states, 0, k) + question_atts = tile(question_atts, 0, k) + + output = self.text_decoder(input_ids, + attention_mask = input_atts, + encoder_hidden_states = question_states, + encoder_attention_mask = question_atts, + labels = targets_ids, + return_dict = True, + reduction = 'none') + + log_probs_sum = -output.loss + log_probs_sum = log_probs_sum.view(num_ques,k) + + max_topk_ids = log_probs_sum.argmax(dim=1) + max_ids = topk_ids[max_topk_ids>=0,max_topk_ids] + + return max_ids + + +def blip_vqa(pretrained='',**kwargs): + model = BLIP_VQA(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) +# assert(len(msg.missing_keys)==0) + return model + + +def tile(x, dim, n_tile): + init_dim = x.size(dim) + repeat_idx = [1] * x.dim() + repeat_idx[dim] = n_tile + x = x.repeat(*(repeat_idx)) + order_index = torch.LongTensor(np.concatenate([init_dim * np.arange(n_tile) + i for i in range(init_dim)])) + return torch.index_select(x, dim, order_index.to(x.device)) + + \ No newline at end of file diff --git a/repositories/blip/models/med.py b/repositories/blip/models/med.py new file mode 100644 index 000000000..7b00a3545 --- /dev/null +++ b/repositories/blip/models/med.py @@ -0,0 +1,955 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li + * Based on huggingface code base + * https://github.com/huggingface/transformers/blob/v4.15.0/src/transformers/models/bert +''' + +import math +import os +import warnings +from dataclasses import dataclass +from typing import Optional, Tuple + +import torch +from torch import Tensor, device, dtype, nn +import torch.utils.checkpoint +from torch import nn +from torch.nn import CrossEntropyLoss +import torch.nn.functional as F + +from transformers.activations import ACT2FN +from transformers.file_utils import ( + ModelOutput, +) +from transformers.modeling_outputs import ( + BaseModelOutputWithPastAndCrossAttentions, + BaseModelOutputWithPoolingAndCrossAttentions, + CausalLMOutputWithCrossAttentions, + MaskedLMOutput, + MultipleChoiceModelOutput, + NextSentencePredictorOutput, + QuestionAnsweringModelOutput, + SequenceClassifierOutput, + TokenClassifierOutput, +) +from transformers.modeling_utils import ( + PreTrainedModel, + apply_chunking_to_forward, + find_pruneable_heads_and_indices, + prune_linear_layer, +) +from transformers.utils import logging +from transformers.models.bert.configuration_bert import BertConfig + + +logger = logging.get_logger(__name__) + + +class BertEmbeddings(nn.Module): + """Construct the embeddings from word and position embeddings.""" + + def __init__(self, config): + super().__init__() + self.word_embeddings = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=config.pad_token_id) + self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size) + + # self.LayerNorm is not snake-cased to stick with TensorFlow model variable name and be able to load + # any TensorFlow checkpoint file + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + # position_ids (1, len position emb) is contiguous in memory and exported when serialized + self.register_buffer("position_ids", torch.arange(config.max_position_embeddings).expand((1, -1))) + self.position_embedding_type = getattr(config, "position_embedding_type", "absolute") + + self.config = config + + def forward( + self, input_ids=None, position_ids=None, inputs_embeds=None, past_key_values_length=0 + ): + if input_ids is not None: + input_shape = input_ids.size() + else: + input_shape = inputs_embeds.size()[:-1] + + seq_length = input_shape[1] + + if position_ids is None: + position_ids = self.position_ids[:, past_key_values_length : seq_length + past_key_values_length] + + if inputs_embeds is None: + inputs_embeds = self.word_embeddings(input_ids) + + embeddings = inputs_embeds + + if self.position_embedding_type == "absolute": + position_embeddings = self.position_embeddings(position_ids) + embeddings += position_embeddings + embeddings = self.LayerNorm(embeddings) + embeddings = self.dropout(embeddings) + return embeddings + + +class BertSelfAttention(nn.Module): + def __init__(self, config, is_cross_attention): + super().__init__() + self.config = config + if config.hidden_size % config.num_attention_heads != 0 and not hasattr(config, "embedding_size"): + raise ValueError( + "The hidden size (%d) is not a multiple of the number of attention " + "heads (%d)" % (config.hidden_size, config.num_attention_heads) + ) + + self.num_attention_heads = config.num_attention_heads + self.attention_head_size = int(config.hidden_size / config.num_attention_heads) + self.all_head_size = self.num_attention_heads * self.attention_head_size + + self.query = nn.Linear(config.hidden_size, self.all_head_size) + if is_cross_attention: + self.key = nn.Linear(config.encoder_width, self.all_head_size) + self.value = nn.Linear(config.encoder_width, self.all_head_size) + else: + self.key = nn.Linear(config.hidden_size, self.all_head_size) + self.value = nn.Linear(config.hidden_size, self.all_head_size) + + self.dropout = nn.Dropout(config.attention_probs_dropout_prob) + self.position_embedding_type = getattr(config, "position_embedding_type", "absolute") + if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query": + self.max_position_embeddings = config.max_position_embeddings + self.distance_embedding = nn.Embedding(2 * config.max_position_embeddings - 1, self.attention_head_size) + self.save_attention = False + + def save_attn_gradients(self, attn_gradients): + self.attn_gradients = attn_gradients + + def get_attn_gradients(self): + return self.attn_gradients + + def save_attention_map(self, attention_map): + self.attention_map = attention_map + + def get_attention_map(self): + return self.attention_map + + def transpose_for_scores(self, x): + new_x_shape = x.size()[:-1] + (self.num_attention_heads, self.attention_head_size) + x = x.view(*new_x_shape) + return x.permute(0, 2, 1, 3) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + ): + mixed_query_layer = self.query(hidden_states) + + # If this is instantiated as a cross-attention module, the keys + # and values come from an encoder; the attention mask needs to be + # such that the encoder's padding tokens are not attended to. + is_cross_attention = encoder_hidden_states is not None + + if is_cross_attention: + key_layer = self.transpose_for_scores(self.key(encoder_hidden_states)) + value_layer = self.transpose_for_scores(self.value(encoder_hidden_states)) + attention_mask = encoder_attention_mask + elif past_key_value is not None: + key_layer = self.transpose_for_scores(self.key(hidden_states)) + value_layer = self.transpose_for_scores(self.value(hidden_states)) + key_layer = torch.cat([past_key_value[0], key_layer], dim=2) + value_layer = torch.cat([past_key_value[1], value_layer], dim=2) + else: + key_layer = self.transpose_for_scores(self.key(hidden_states)) + value_layer = self.transpose_for_scores(self.value(hidden_states)) + + query_layer = self.transpose_for_scores(mixed_query_layer) + + past_key_value = (key_layer, value_layer) + + # Take the dot product between "query" and "key" to get the raw attention scores. + attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2)) + + if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query": + seq_length = hidden_states.size()[1] + position_ids_l = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(-1, 1) + position_ids_r = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(1, -1) + distance = position_ids_l - position_ids_r + positional_embedding = self.distance_embedding(distance + self.max_position_embeddings - 1) + positional_embedding = positional_embedding.to(dtype=query_layer.dtype) # fp16 compatibility + + if self.position_embedding_type == "relative_key": + relative_position_scores = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding) + attention_scores = attention_scores + relative_position_scores + elif self.position_embedding_type == "relative_key_query": + relative_position_scores_query = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding) + relative_position_scores_key = torch.einsum("bhrd,lrd->bhlr", key_layer, positional_embedding) + attention_scores = attention_scores + relative_position_scores_query + relative_position_scores_key + + attention_scores = attention_scores / math.sqrt(self.attention_head_size) + if attention_mask is not None: + # Apply the attention mask is (precomputed for all layers in BertModel forward() function) + attention_scores = attention_scores + attention_mask + + # Normalize the attention scores to probabilities. + attention_probs = nn.Softmax(dim=-1)(attention_scores) + + if is_cross_attention and self.save_attention: + self.save_attention_map(attention_probs) + attention_probs.register_hook(self.save_attn_gradients) + + # This is actually dropping out entire tokens to attend to, which might + # seem a bit unusual, but is taken from the original Transformer paper. + attention_probs_dropped = self.dropout(attention_probs) + + # Mask heads if we want to + if head_mask is not None: + attention_probs_dropped = attention_probs_dropped * head_mask + + context_layer = torch.matmul(attention_probs_dropped, value_layer) + + context_layer = context_layer.permute(0, 2, 1, 3).contiguous() + new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,) + context_layer = context_layer.view(*new_context_layer_shape) + + outputs = (context_layer, attention_probs) if output_attentions else (context_layer,) + + outputs = outputs + (past_key_value,) + return outputs + + +class BertSelfOutput(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, hidden_states, input_tensor): + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertAttention(nn.Module): + def __init__(self, config, is_cross_attention=False): + super().__init__() + self.self = BertSelfAttention(config, is_cross_attention) + self.output = BertSelfOutput(config) + self.pruned_heads = set() + + def prune_heads(self, heads): + if len(heads) == 0: + return + heads, index = find_pruneable_heads_and_indices( + heads, self.self.num_attention_heads, self.self.attention_head_size, self.pruned_heads + ) + + # Prune linear layers + self.self.query = prune_linear_layer(self.self.query, index) + self.self.key = prune_linear_layer(self.self.key, index) + self.self.value = prune_linear_layer(self.self.value, index) + self.output.dense = prune_linear_layer(self.output.dense, index, dim=1) + + # Update hyper params and store pruned heads + self.self.num_attention_heads = self.self.num_attention_heads - len(heads) + self.self.all_head_size = self.self.attention_head_size * self.self.num_attention_heads + self.pruned_heads = self.pruned_heads.union(heads) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + ): + self_outputs = self.self( + hidden_states, + attention_mask, + head_mask, + encoder_hidden_states, + encoder_attention_mask, + past_key_value, + output_attentions, + ) + attention_output = self.output(self_outputs[0], hidden_states) + outputs = (attention_output,) + self_outputs[1:] # add attentions if we output them + return outputs + + +class BertIntermediate(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.intermediate_size) + if isinstance(config.hidden_act, str): + self.intermediate_act_fn = ACT2FN[config.hidden_act] + else: + self.intermediate_act_fn = config.hidden_act + + def forward(self, hidden_states): + hidden_states = self.dense(hidden_states) + hidden_states = self.intermediate_act_fn(hidden_states) + return hidden_states + + +class BertOutput(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.intermediate_size, config.hidden_size) + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, hidden_states, input_tensor): + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertLayer(nn.Module): + def __init__(self, config, layer_num): + super().__init__() + self.config = config + self.chunk_size_feed_forward = config.chunk_size_feed_forward + self.seq_len_dim = 1 + self.attention = BertAttention(config) + self.layer_num = layer_num + if self.config.add_cross_attention: + self.crossattention = BertAttention(config, is_cross_attention=self.config.add_cross_attention) + self.intermediate = BertIntermediate(config) + self.output = BertOutput(config) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + mode=None, + ): + # decoder uni-directional self-attention cached key/values tuple is at positions 1,2 + self_attn_past_key_value = past_key_value[:2] if past_key_value is not None else None + self_attention_outputs = self.attention( + hidden_states, + attention_mask, + head_mask, + output_attentions=output_attentions, + past_key_value=self_attn_past_key_value, + ) + attention_output = self_attention_outputs[0] + + outputs = self_attention_outputs[1:-1] + present_key_value = self_attention_outputs[-1] + + if mode=='multimodal': + assert encoder_hidden_states is not None, "encoder_hidden_states must be given for cross-attention layers" + + cross_attention_outputs = self.crossattention( + attention_output, + attention_mask, + head_mask, + encoder_hidden_states, + encoder_attention_mask, + output_attentions=output_attentions, + ) + attention_output = cross_attention_outputs[0] + outputs = outputs + cross_attention_outputs[1:-1] # add cross attentions if we output attention weights + layer_output = apply_chunking_to_forward( + self.feed_forward_chunk, self.chunk_size_feed_forward, self.seq_len_dim, attention_output + ) + outputs = (layer_output,) + outputs + + outputs = outputs + (present_key_value,) + + return outputs + + def feed_forward_chunk(self, attention_output): + intermediate_output = self.intermediate(attention_output) + layer_output = self.output(intermediate_output, attention_output) + return layer_output + + +class BertEncoder(nn.Module): + def __init__(self, config): + super().__init__() + self.config = config + self.layer = nn.ModuleList([BertLayer(config,i) for i in range(config.num_hidden_layers)]) + self.gradient_checkpointing = False + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_values=None, + use_cache=None, + output_attentions=False, + output_hidden_states=False, + return_dict=True, + mode='multimodal', + ): + all_hidden_states = () if output_hidden_states else None + all_self_attentions = () if output_attentions else None + all_cross_attentions = () if output_attentions and self.config.add_cross_attention else None + + next_decoder_cache = () if use_cache else None + + for i in range(self.config.num_hidden_layers): + layer_module = self.layer[i] + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states,) + + layer_head_mask = head_mask[i] if head_mask is not None else None + past_key_value = past_key_values[i] if past_key_values is not None else None + + if self.gradient_checkpointing and self.training: + + if use_cache: + logger.warn( + "`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`..." + ) + use_cache = False + + def create_custom_forward(module): + def custom_forward(*inputs): + return module(*inputs, past_key_value, output_attentions) + + return custom_forward + + layer_outputs = torch.utils.checkpoint.checkpoint( + create_custom_forward(layer_module), + hidden_states, + attention_mask, + layer_head_mask, + encoder_hidden_states, + encoder_attention_mask, + mode=mode, + ) + else: + layer_outputs = layer_module( + hidden_states, + attention_mask, + layer_head_mask, + encoder_hidden_states, + encoder_attention_mask, + past_key_value, + output_attentions, + mode=mode, + ) + + hidden_states = layer_outputs[0] + if use_cache: + next_decoder_cache += (layer_outputs[-1],) + if output_attentions: + all_self_attentions = all_self_attentions + (layer_outputs[1],) + + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states,) + + if not return_dict: + return tuple( + v + for v in [ + hidden_states, + next_decoder_cache, + all_hidden_states, + all_self_attentions, + all_cross_attentions, + ] + if v is not None + ) + return BaseModelOutputWithPastAndCrossAttentions( + last_hidden_state=hidden_states, + past_key_values=next_decoder_cache, + hidden_states=all_hidden_states, + attentions=all_self_attentions, + cross_attentions=all_cross_attentions, + ) + + +class BertPooler(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + self.activation = nn.Tanh() + + def forward(self, hidden_states): + # We "pool" the model by simply taking the hidden state corresponding + # to the first token. + first_token_tensor = hidden_states[:, 0] + pooled_output = self.dense(first_token_tensor) + pooled_output = self.activation(pooled_output) + return pooled_output + + +class BertPredictionHeadTransform(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + if isinstance(config.hidden_act, str): + self.transform_act_fn = ACT2FN[config.hidden_act] + else: + self.transform_act_fn = config.hidden_act + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + + def forward(self, hidden_states): + hidden_states = self.dense(hidden_states) + hidden_states = self.transform_act_fn(hidden_states) + hidden_states = self.LayerNorm(hidden_states) + return hidden_states + + +class BertLMPredictionHead(nn.Module): + def __init__(self, config): + super().__init__() + self.transform = BertPredictionHeadTransform(config) + + # The output weights are the same as the input embeddings, but there is + # an output-only bias for each token. + self.decoder = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + + self.bias = nn.Parameter(torch.zeros(config.vocab_size)) + + # Need a link between the two variables so that the bias is correctly resized with `resize_token_embeddings` + self.decoder.bias = self.bias + + def forward(self, hidden_states): + hidden_states = self.transform(hidden_states) + hidden_states = self.decoder(hidden_states) + return hidden_states + + +class BertOnlyMLMHead(nn.Module): + def __init__(self, config): + super().__init__() + self.predictions = BertLMPredictionHead(config) + + def forward(self, sequence_output): + prediction_scores = self.predictions(sequence_output) + return prediction_scores + + +class BertPreTrainedModel(PreTrainedModel): + """ + An abstract class to handle weights initialization and a simple interface for downloading and loading pretrained + models. + """ + + config_class = BertConfig + base_model_prefix = "bert" + _keys_to_ignore_on_load_missing = [r"position_ids"] + + def _init_weights(self, module): + """ Initialize the weights """ + if isinstance(module, (nn.Linear, nn.Embedding)): + # Slightly different from the TF version which uses truncated_normal for initialization + # cf https://github.com/pytorch/pytorch/pull/5617 + module.weight.data.normal_(mean=0.0, std=self.config.initializer_range) + elif isinstance(module, nn.LayerNorm): + module.bias.data.zero_() + module.weight.data.fill_(1.0) + if isinstance(module, nn.Linear) and module.bias is not None: + module.bias.data.zero_() + + +class BertModel(BertPreTrainedModel): + """ + The model can behave as an encoder (with only self-attention) as well as a decoder, in which case a layer of + cross-attention is added between the self-attention layers, following the architecture described in `Attention is + all you need `__ by Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, + Llion Jones, Aidan N. Gomez, Lukasz Kaiser and Illia Polosukhin. + argument and :obj:`add_cross_attention` set to :obj:`True`; an :obj:`encoder_hidden_states` is then expected as an + input to the forward pass. + """ + + def __init__(self, config, add_pooling_layer=True): + super().__init__(config) + self.config = config + + self.embeddings = BertEmbeddings(config) + + self.encoder = BertEncoder(config) + + self.pooler = BertPooler(config) if add_pooling_layer else None + + self.init_weights() + + + def get_input_embeddings(self): + return self.embeddings.word_embeddings + + def set_input_embeddings(self, value): + self.embeddings.word_embeddings = value + + def _prune_heads(self, heads_to_prune): + """ + Prunes heads of the model. heads_to_prune: dict of {layer_num: list of heads to prune in this layer} See base + class PreTrainedModel + """ + for layer, heads in heads_to_prune.items(): + self.encoder.layer[layer].attention.prune_heads(heads) + + + def get_extended_attention_mask(self, attention_mask: Tensor, input_shape: Tuple[int], device: device, is_decoder: bool) -> Tensor: + """ + Makes broadcastable attention and causal masks so that future and masked tokens are ignored. + + Arguments: + attention_mask (:obj:`torch.Tensor`): + Mask with ones indicating tokens to attend to, zeros for tokens to ignore. + input_shape (:obj:`Tuple[int]`): + The shape of the input to the model. + device: (:obj:`torch.device`): + The device of the input to the model. + + Returns: + :obj:`torch.Tensor` The extended attention mask, with a the same dtype as :obj:`attention_mask.dtype`. + """ + # We can provide a self-attention mask of dimensions [batch_size, from_seq_length, to_seq_length] + # ourselves in which case we just need to make it broadcastable to all heads. + if attention_mask.dim() == 3: + extended_attention_mask = attention_mask[:, None, :, :] + elif attention_mask.dim() == 2: + # Provided a padding mask of dimensions [batch_size, seq_length] + # - if the model is a decoder, apply a causal mask in addition to the padding mask + # - if the model is an encoder, make the mask broadcastable to [batch_size, num_heads, seq_length, seq_length] + if is_decoder: + batch_size, seq_length = input_shape + + seq_ids = torch.arange(seq_length, device=device) + causal_mask = seq_ids[None, None, :].repeat(batch_size, seq_length, 1) <= seq_ids[None, :, None] + # in case past_key_values are used we need to add a prefix ones mask to the causal mask + # causal and attention masks must have same type with pytorch version < 1.3 + causal_mask = causal_mask.to(attention_mask.dtype) + + if causal_mask.shape[1] < attention_mask.shape[1]: + prefix_seq_len = attention_mask.shape[1] - causal_mask.shape[1] + causal_mask = torch.cat( + [ + torch.ones((batch_size, seq_length, prefix_seq_len), device=device, dtype=causal_mask.dtype), + causal_mask, + ], + axis=-1, + ) + + extended_attention_mask = causal_mask[:, None, :, :] * attention_mask[:, None, None, :] + else: + extended_attention_mask = attention_mask[:, None, None, :] + else: + raise ValueError( + "Wrong shape for input_ids (shape {}) or attention_mask (shape {})".format( + input_shape, attention_mask.shape + ) + ) + + # Since attention_mask is 1.0 for positions we want to attend and 0.0 for + # masked positions, this operation will create a tensor which is 0.0 for + # positions we want to attend and -10000.0 for masked positions. + # Since we are adding it to the raw scores before the softmax, this is + # effectively the same as removing these entirely. + extended_attention_mask = extended_attention_mask.to(dtype=self.dtype) # fp16 compatibility + extended_attention_mask = (1.0 - extended_attention_mask) * -10000.0 + return extended_attention_mask + + def forward( + self, + input_ids=None, + attention_mask=None, + position_ids=None, + head_mask=None, + inputs_embeds=None, + encoder_embeds=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_values=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + is_decoder=False, + mode='multimodal', + ): + r""" + encoder_hidden_states (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`, `optional`): + Sequence of hidden-states at the output of the last layer of the encoder. Used in the cross-attention if + the model is configured as a decoder. + encoder_attention_mask (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Mask to avoid performing attention on the padding token indices of the encoder input. This mask is used in + the cross-attention if the model is configured as a decoder. Mask values selected in ``[0, 1]``: + - 1 for tokens that are **not masked**, + - 0 for tokens that are **masked**. + past_key_values (:obj:`tuple(tuple(torch.FloatTensor))` of length :obj:`config.n_layers` with each tuple having 4 tensors of shape :obj:`(batch_size, num_heads, sequence_length - 1, embed_size_per_head)`): + Contains precomputed key and value hidden states of the attention blocks. Can be used to speed up decoding. + If :obj:`past_key_values` are used, the user can optionally input only the last :obj:`decoder_input_ids` + (those that don't have their past key value states given to this model) of shape :obj:`(batch_size, 1)` + instead of all :obj:`decoder_input_ids` of shape :obj:`(batch_size, sequence_length)`. + use_cache (:obj:`bool`, `optional`): + If set to :obj:`True`, :obj:`past_key_values` key value states are returned and can be used to speed up + decoding (see :obj:`past_key_values`). + """ + output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions + output_hidden_states = ( + output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states + ) + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + if is_decoder: + use_cache = use_cache if use_cache is not None else self.config.use_cache + else: + use_cache = False + + if input_ids is not None and inputs_embeds is not None: + raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time") + elif input_ids is not None: + input_shape = input_ids.size() + batch_size, seq_length = input_shape + device = input_ids.device + elif inputs_embeds is not None: + input_shape = inputs_embeds.size()[:-1] + batch_size, seq_length = input_shape + device = inputs_embeds.device + elif encoder_embeds is not None: + input_shape = encoder_embeds.size()[:-1] + batch_size, seq_length = input_shape + device = encoder_embeds.device + else: + raise ValueError("You have to specify either input_ids or inputs_embeds or encoder_embeds") + + # past_key_values_length + past_key_values_length = past_key_values[0][0].shape[2] if past_key_values is not None else 0 + + if attention_mask is None: + attention_mask = torch.ones(((batch_size, seq_length + past_key_values_length)), device=device) + + # We can provide a self-attention mask of dimensions [batch_size, from_seq_length, to_seq_length] + # ourselves in which case we just need to make it broadcastable to all heads. + extended_attention_mask: torch.Tensor = self.get_extended_attention_mask(attention_mask, input_shape, + device, is_decoder) + + # If a 2D or 3D attention mask is provided for the cross-attention + # we need to make broadcastable to [batch_size, num_heads, seq_length, seq_length] + if encoder_hidden_states is not None: + if type(encoder_hidden_states) == list: + encoder_batch_size, encoder_sequence_length, _ = encoder_hidden_states[0].size() + else: + encoder_batch_size, encoder_sequence_length, _ = encoder_hidden_states.size() + encoder_hidden_shape = (encoder_batch_size, encoder_sequence_length) + + if type(encoder_attention_mask) == list: + encoder_extended_attention_mask = [self.invert_attention_mask(mask) for mask in encoder_attention_mask] + elif encoder_attention_mask is None: + encoder_attention_mask = torch.ones(encoder_hidden_shape, device=device) + encoder_extended_attention_mask = self.invert_attention_mask(encoder_attention_mask) + else: + encoder_extended_attention_mask = self.invert_attention_mask(encoder_attention_mask) + else: + encoder_extended_attention_mask = None + + # Prepare head mask if needed + # 1.0 in head_mask indicate we keep the head + # attention_probs has shape bsz x n_heads x N x N + # input head_mask has shape [num_heads] or [num_hidden_layers x num_heads] + # and head_mask is converted to shape [num_hidden_layers x batch x num_heads x seq_length x seq_length] + head_mask = self.get_head_mask(head_mask, self.config.num_hidden_layers) + + if encoder_embeds is None: + embedding_output = self.embeddings( + input_ids=input_ids, + position_ids=position_ids, + inputs_embeds=inputs_embeds, + past_key_values_length=past_key_values_length, + ) + else: + embedding_output = encoder_embeds + + encoder_outputs = self.encoder( + embedding_output, + attention_mask=extended_attention_mask, + head_mask=head_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_extended_attention_mask, + past_key_values=past_key_values, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + mode=mode, + ) + sequence_output = encoder_outputs[0] + pooled_output = self.pooler(sequence_output) if self.pooler is not None else None + + if not return_dict: + return (sequence_output, pooled_output) + encoder_outputs[1:] + + return BaseModelOutputWithPoolingAndCrossAttentions( + last_hidden_state=sequence_output, + pooler_output=pooled_output, + past_key_values=encoder_outputs.past_key_values, + hidden_states=encoder_outputs.hidden_states, + attentions=encoder_outputs.attentions, + cross_attentions=encoder_outputs.cross_attentions, + ) + + + +class BertLMHeadModel(BertPreTrainedModel): + + _keys_to_ignore_on_load_unexpected = [r"pooler"] + _keys_to_ignore_on_load_missing = [r"position_ids", r"predictions.decoder.bias"] + + def __init__(self, config): + super().__init__(config) + + self.bert = BertModel(config, add_pooling_layer=False) + self.cls = BertOnlyMLMHead(config) + + self.init_weights() + + def get_output_embeddings(self): + return self.cls.predictions.decoder + + def set_output_embeddings(self, new_embeddings): + self.cls.predictions.decoder = new_embeddings + + def forward( + self, + input_ids=None, + attention_mask=None, + position_ids=None, + head_mask=None, + inputs_embeds=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + labels=None, + past_key_values=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + return_logits=False, + is_decoder=True, + reduction='mean', + mode='multimodal', + ): + r""" + encoder_hidden_states (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`, `optional`): + Sequence of hidden-states at the output of the last layer of the encoder. Used in the cross-attention if + the model is configured as a decoder. + encoder_attention_mask (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Mask to avoid performing attention on the padding token indices of the encoder input. This mask is used in + the cross-attention if the model is configured as a decoder. Mask values selected in ``[0, 1]``: + - 1 for tokens that are **not masked**, + - 0 for tokens that are **masked**. + labels (:obj:`torch.LongTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Labels for computing the left-to-right language modeling loss (next word prediction). Indices should be in + ``[-100, 0, ..., config.vocab_size]`` (see ``input_ids`` docstring) Tokens with indices set to ``-100`` are + ignored (masked), the loss is only computed for the tokens with labels n ``[0, ..., config.vocab_size]`` + past_key_values (:obj:`tuple(tuple(torch.FloatTensor))` of length :obj:`config.n_layers` with each tuple having 4 tensors of shape :obj:`(batch_size, num_heads, sequence_length - 1, embed_size_per_head)`): + Contains precomputed key and value hidden states of the attention blocks. Can be used to speed up decoding. + If :obj:`past_key_values` are used, the user can optionally input only the last :obj:`decoder_input_ids` + (those that don't have their past key value states given to this model) of shape :obj:`(batch_size, 1)` + instead of all :obj:`decoder_input_ids` of shape :obj:`(batch_size, sequence_length)`. + use_cache (:obj:`bool`, `optional`): + If set to :obj:`True`, :obj:`past_key_values` key value states are returned and can be used to speed up + decoding (see :obj:`past_key_values`). + Returns: + Example:: + >>> from transformers import BertTokenizer, BertLMHeadModel, BertConfig + >>> import torch + >>> tokenizer = BertTokenizer.from_pretrained('bert-base-cased') + >>> config = BertConfig.from_pretrained("bert-base-cased") + >>> model = BertLMHeadModel.from_pretrained('bert-base-cased', config=config) + >>> inputs = tokenizer("Hello, my dog is cute", return_tensors="pt") + >>> outputs = model(**inputs) + >>> prediction_logits = outputs.logits + """ + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + if labels is not None: + use_cache = False + + outputs = self.bert( + input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + head_mask=head_mask, + inputs_embeds=inputs_embeds, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + past_key_values=past_key_values, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + is_decoder=is_decoder, + mode=mode, + ) + + sequence_output = outputs[0] + prediction_scores = self.cls(sequence_output) + + if return_logits: + return prediction_scores[:, :-1, :].contiguous() + + lm_loss = None + if labels is not None: + # we are doing next-token prediction; shift prediction scores and input ids by one + shifted_prediction_scores = prediction_scores[:, :-1, :].contiguous() + labels = labels[:, 1:].contiguous() + loss_fct = CrossEntropyLoss(reduction=reduction, label_smoothing=0.1) + lm_loss = loss_fct(shifted_prediction_scores.view(-1, self.config.vocab_size), labels.view(-1)) + if reduction=='none': + lm_loss = lm_loss.view(prediction_scores.size(0),-1).sum(1) + + if not return_dict: + output = (prediction_scores,) + outputs[2:] + return ((lm_loss,) + output) if lm_loss is not None else output + + return CausalLMOutputWithCrossAttentions( + loss=lm_loss, + logits=prediction_scores, + past_key_values=outputs.past_key_values, + hidden_states=outputs.hidden_states, + attentions=outputs.attentions, + cross_attentions=outputs.cross_attentions, + ) + + def prepare_inputs_for_generation(self, input_ids, past=None, attention_mask=None, **model_kwargs): + input_shape = input_ids.shape + # if model is used as a decoder in encoder-decoder model, the decoder attention mask is created on the fly + if attention_mask is None: + attention_mask = input_ids.new_ones(input_shape) + + # cut decoder_input_ids if past is used + if past is not None: + input_ids = input_ids[:, -1:] + + return { + "input_ids": input_ids, + "attention_mask": attention_mask, + "past_key_values": past, + "encoder_hidden_states": model_kwargs.get("encoder_hidden_states", None), + "encoder_attention_mask": model_kwargs.get("encoder_attention_mask", None), + "is_decoder": True, + } + + def _reorder_cache(self, past, beam_idx): + reordered_past = () + for layer_past in past: + reordered_past += (tuple(past_state.index_select(0, beam_idx) for past_state in layer_past),) + return reordered_past diff --git a/repositories/blip/models/nlvr_encoder.py b/repositories/blip/models/nlvr_encoder.py new file mode 100644 index 000000000..1946bb4a3 --- /dev/null +++ b/repositories/blip/models/nlvr_encoder.py @@ -0,0 +1,843 @@ +import math +import os +import warnings +from dataclasses import dataclass +from typing import Optional, Tuple + +import torch +from torch import Tensor, device, dtype, nn +import torch.utils.checkpoint +from torch import nn +from torch.nn import CrossEntropyLoss +import torch.nn.functional as F + +from transformers.activations import ACT2FN +from transformers.file_utils import ( + ModelOutput, +) +from transformers.modeling_outputs import ( + BaseModelOutputWithPastAndCrossAttentions, + BaseModelOutputWithPoolingAndCrossAttentions, + CausalLMOutputWithCrossAttentions, + MaskedLMOutput, + MultipleChoiceModelOutput, + NextSentencePredictorOutput, + QuestionAnsweringModelOutput, + SequenceClassifierOutput, + TokenClassifierOutput, +) +from transformers.modeling_utils import ( + PreTrainedModel, + apply_chunking_to_forward, + find_pruneable_heads_and_indices, + prune_linear_layer, +) +from transformers.utils import logging +from transformers.models.bert.configuration_bert import BertConfig + + +logger = logging.get_logger(__name__) + + +class BertEmbeddings(nn.Module): + """Construct the embeddings from word and position embeddings.""" + + def __init__(self, config): + super().__init__() + self.word_embeddings = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=config.pad_token_id) + self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size) + + # self.LayerNorm is not snake-cased to stick with TensorFlow model variable name and be able to load + # any TensorFlow checkpoint file + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + # position_ids (1, len position emb) is contiguous in memory and exported when serialized + self.register_buffer("position_ids", torch.arange(config.max_position_embeddings).expand((1, -1))) + self.position_embedding_type = getattr(config, "position_embedding_type", "absolute") + + self.config = config + + def forward( + self, input_ids=None, position_ids=None, inputs_embeds=None, past_key_values_length=0 + ): + if input_ids is not None: + input_shape = input_ids.size() + else: + input_shape = inputs_embeds.size()[:-1] + + seq_length = input_shape[1] + + if position_ids is None: + position_ids = self.position_ids[:, past_key_values_length : seq_length + past_key_values_length] + + if inputs_embeds is None: + inputs_embeds = self.word_embeddings(input_ids) + + embeddings = inputs_embeds + + if self.position_embedding_type == "absolute": + position_embeddings = self.position_embeddings(position_ids) + embeddings += position_embeddings + embeddings = self.LayerNorm(embeddings) + embeddings = self.dropout(embeddings) + return embeddings + + +class BertSelfAttention(nn.Module): + def __init__(self, config, is_cross_attention): + super().__init__() + self.config = config + if config.hidden_size % config.num_attention_heads != 0 and not hasattr(config, "embedding_size"): + raise ValueError( + "The hidden size (%d) is not a multiple of the number of attention " + "heads (%d)" % (config.hidden_size, config.num_attention_heads) + ) + + self.num_attention_heads = config.num_attention_heads + self.attention_head_size = int(config.hidden_size / config.num_attention_heads) + self.all_head_size = self.num_attention_heads * self.attention_head_size + + self.query = nn.Linear(config.hidden_size, self.all_head_size) + if is_cross_attention: + self.key = nn.Linear(config.encoder_width, self.all_head_size) + self.value = nn.Linear(config.encoder_width, self.all_head_size) + else: + self.key = nn.Linear(config.hidden_size, self.all_head_size) + self.value = nn.Linear(config.hidden_size, self.all_head_size) + + self.dropout = nn.Dropout(config.attention_probs_dropout_prob) + self.position_embedding_type = getattr(config, "position_embedding_type", "absolute") + if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query": + self.max_position_embeddings = config.max_position_embeddings + self.distance_embedding = nn.Embedding(2 * config.max_position_embeddings - 1, self.attention_head_size) + self.save_attention = False + + def save_attn_gradients(self, attn_gradients): + self.attn_gradients = attn_gradients + + def get_attn_gradients(self): + return self.attn_gradients + + def save_attention_map(self, attention_map): + self.attention_map = attention_map + + def get_attention_map(self): + return self.attention_map + + def transpose_for_scores(self, x): + new_x_shape = x.size()[:-1] + (self.num_attention_heads, self.attention_head_size) + x = x.view(*new_x_shape) + return x.permute(0, 2, 1, 3) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + ): + mixed_query_layer = self.query(hidden_states) + + # If this is instantiated as a cross-attention module, the keys + # and values come from an encoder; the attention mask needs to be + # such that the encoder's padding tokens are not attended to. + is_cross_attention = encoder_hidden_states is not None + + if is_cross_attention: + key_layer = self.transpose_for_scores(self.key(encoder_hidden_states)) + value_layer = self.transpose_for_scores(self.value(encoder_hidden_states)) + attention_mask = encoder_attention_mask + elif past_key_value is not None: + key_layer = self.transpose_for_scores(self.key(hidden_states)) + value_layer = self.transpose_for_scores(self.value(hidden_states)) + key_layer = torch.cat([past_key_value[0], key_layer], dim=2) + value_layer = torch.cat([past_key_value[1], value_layer], dim=2) + else: + key_layer = self.transpose_for_scores(self.key(hidden_states)) + value_layer = self.transpose_for_scores(self.value(hidden_states)) + + query_layer = self.transpose_for_scores(mixed_query_layer) + + past_key_value = (key_layer, value_layer) + + # Take the dot product between "query" and "key" to get the raw attention scores. + attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2)) + + if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query": + seq_length = hidden_states.size()[1] + position_ids_l = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(-1, 1) + position_ids_r = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(1, -1) + distance = position_ids_l - position_ids_r + positional_embedding = self.distance_embedding(distance + self.max_position_embeddings - 1) + positional_embedding = positional_embedding.to(dtype=query_layer.dtype) # fp16 compatibility + + if self.position_embedding_type == "relative_key": + relative_position_scores = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding) + attention_scores = attention_scores + relative_position_scores + elif self.position_embedding_type == "relative_key_query": + relative_position_scores_query = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding) + relative_position_scores_key = torch.einsum("bhrd,lrd->bhlr", key_layer, positional_embedding) + attention_scores = attention_scores + relative_position_scores_query + relative_position_scores_key + + attention_scores = attention_scores / math.sqrt(self.attention_head_size) + if attention_mask is not None: + # Apply the attention mask is (precomputed for all layers in BertModel forward() function) + attention_scores = attention_scores + attention_mask + + # Normalize the attention scores to probabilities. + attention_probs = nn.Softmax(dim=-1)(attention_scores) + + if is_cross_attention and self.save_attention: + self.save_attention_map(attention_probs) + attention_probs.register_hook(self.save_attn_gradients) + + # This is actually dropping out entire tokens to attend to, which might + # seem a bit unusual, but is taken from the original Transformer paper. + attention_probs_dropped = self.dropout(attention_probs) + + # Mask heads if we want to + if head_mask is not None: + attention_probs_dropped = attention_probs_dropped * head_mask + + context_layer = torch.matmul(attention_probs_dropped, value_layer) + + context_layer = context_layer.permute(0, 2, 1, 3).contiguous() + new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,) + context_layer = context_layer.view(*new_context_layer_shape) + + outputs = (context_layer, attention_probs) if output_attentions else (context_layer,) + + outputs = outputs + (past_key_value,) + return outputs + + +class BertSelfOutput(nn.Module): + def __init__(self, config, twin=False, merge=False): + super().__init__() + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + if twin: + self.dense0 = nn.Linear(config.hidden_size, config.hidden_size) + self.dense1 = nn.Linear(config.hidden_size, config.hidden_size) + else: + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + if merge: + self.act = ACT2FN[config.hidden_act] + self.merge_layer = nn.Linear(config.hidden_size * 2, config.hidden_size) + self.merge = True + else: + self.merge = False + + def forward(self, hidden_states, input_tensor): + if type(hidden_states) == list: + hidden_states0 = self.dense0(hidden_states[0]) + hidden_states1 = self.dense1(hidden_states[1]) + if self.merge: + #hidden_states = self.merge_layer(self.act(torch.cat([hidden_states0,hidden_states1],dim=-1))) + hidden_states = self.merge_layer(torch.cat([hidden_states0,hidden_states1],dim=-1)) + else: + hidden_states = (hidden_states0+hidden_states1)/2 + else: + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertAttention(nn.Module): + def __init__(self, config, is_cross_attention=False, layer_num=-1): + super().__init__() + if is_cross_attention: + self.self0 = BertSelfAttention(config, is_cross_attention) + self.self1 = BertSelfAttention(config, is_cross_attention) + else: + self.self = BertSelfAttention(config, is_cross_attention) + self.output = BertSelfOutput(config, twin=is_cross_attention, merge=(is_cross_attention and layer_num>=6)) + self.pruned_heads = set() + + def prune_heads(self, heads): + if len(heads) == 0: + return + heads, index = find_pruneable_heads_and_indices( + heads, self.self.num_attention_heads, self.self.attention_head_size, self.pruned_heads + ) + + # Prune linear layers + self.self.query = prune_linear_layer(self.self.query, index) + self.self.key = prune_linear_layer(self.self.key, index) + self.self.value = prune_linear_layer(self.self.value, index) + self.output.dense = prune_linear_layer(self.output.dense, index, dim=1) + + # Update hyper params and store pruned heads + self.self.num_attention_heads = self.self.num_attention_heads - len(heads) + self.self.all_head_size = self.self.attention_head_size * self.self.num_attention_heads + self.pruned_heads = self.pruned_heads.union(heads) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + ): + if type(encoder_hidden_states)==list: + self_outputs0 = self.self0( + hidden_states, + attention_mask, + head_mask, + encoder_hidden_states[0], + encoder_attention_mask[0], + past_key_value, + output_attentions, + ) + self_outputs1 = self.self1( + hidden_states, + attention_mask, + head_mask, + encoder_hidden_states[1], + encoder_attention_mask[1], + past_key_value, + output_attentions, + ) + attention_output = self.output([self_outputs0[0],self_outputs1[0]], hidden_states) + + outputs = (attention_output,) + self_outputs0[1:] # add attentions if we output them + else: + self_outputs = self.self( + hidden_states, + attention_mask, + head_mask, + encoder_hidden_states, + encoder_attention_mask, + past_key_value, + output_attentions, + ) + attention_output = self.output(self_outputs[0], hidden_states) + outputs = (attention_output,) + self_outputs[1:] # add attentions if we output them + return outputs + + +class BertIntermediate(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.intermediate_size) + if isinstance(config.hidden_act, str): + self.intermediate_act_fn = ACT2FN[config.hidden_act] + else: + self.intermediate_act_fn = config.hidden_act + + def forward(self, hidden_states): + hidden_states = self.dense(hidden_states) + hidden_states = self.intermediate_act_fn(hidden_states) + return hidden_states + + +class BertOutput(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.intermediate_size, config.hidden_size) + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, hidden_states, input_tensor): + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertLayer(nn.Module): + def __init__(self, config, layer_num): + super().__init__() + self.config = config + self.chunk_size_feed_forward = config.chunk_size_feed_forward + self.seq_len_dim = 1 + self.attention = BertAttention(config) + self.layer_num = layer_num + if self.config.add_cross_attention: + self.crossattention = BertAttention(config, is_cross_attention=self.config.add_cross_attention, layer_num=layer_num) + self.intermediate = BertIntermediate(config) + self.output = BertOutput(config) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + mode=None, + ): + # decoder uni-directional self-attention cached key/values tuple is at positions 1,2 + self_attn_past_key_value = past_key_value[:2] if past_key_value is not None else None + self_attention_outputs = self.attention( + hidden_states, + attention_mask, + head_mask, + output_attentions=output_attentions, + past_key_value=self_attn_past_key_value, + ) + attention_output = self_attention_outputs[0] + + outputs = self_attention_outputs[1:-1] + present_key_value = self_attention_outputs[-1] + + if mode=='multimodal': + assert encoder_hidden_states is not None, "encoder_hidden_states must be given for cross-attention layers" + cross_attention_outputs = self.crossattention( + attention_output, + attention_mask, + head_mask, + encoder_hidden_states, + encoder_attention_mask, + output_attentions=output_attentions, + ) + attention_output = cross_attention_outputs[0] + outputs = outputs + cross_attention_outputs[1:-1] # add cross attentions if we output attention weights + layer_output = apply_chunking_to_forward( + self.feed_forward_chunk, self.chunk_size_feed_forward, self.seq_len_dim, attention_output + ) + outputs = (layer_output,) + outputs + + outputs = outputs + (present_key_value,) + + return outputs + + def feed_forward_chunk(self, attention_output): + intermediate_output = self.intermediate(attention_output) + layer_output = self.output(intermediate_output, attention_output) + return layer_output + + +class BertEncoder(nn.Module): + def __init__(self, config): + super().__init__() + self.config = config + self.layer = nn.ModuleList([BertLayer(config,i) for i in range(config.num_hidden_layers)]) + self.gradient_checkpointing = False + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_values=None, + use_cache=None, + output_attentions=False, + output_hidden_states=False, + return_dict=True, + mode='multimodal', + ): + all_hidden_states = () if output_hidden_states else None + all_self_attentions = () if output_attentions else None + all_cross_attentions = () if output_attentions and self.config.add_cross_attention else None + + next_decoder_cache = () if use_cache else None + + for i in range(self.config.num_hidden_layers): + layer_module = self.layer[i] + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states,) + + layer_head_mask = head_mask[i] if head_mask is not None else None + past_key_value = past_key_values[i] if past_key_values is not None else None + + if self.gradient_checkpointing and self.training: + + if use_cache: + logger.warn( + "`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`..." + ) + use_cache = False + + def create_custom_forward(module): + def custom_forward(*inputs): + return module(*inputs, past_key_value, output_attentions) + + return custom_forward + + layer_outputs = torch.utils.checkpoint.checkpoint( + create_custom_forward(layer_module), + hidden_states, + attention_mask, + layer_head_mask, + encoder_hidden_states, + encoder_attention_mask, + mode=mode, + ) + else: + layer_outputs = layer_module( + hidden_states, + attention_mask, + layer_head_mask, + encoder_hidden_states, + encoder_attention_mask, + past_key_value, + output_attentions, + mode=mode, + ) + + hidden_states = layer_outputs[0] + if use_cache: + next_decoder_cache += (layer_outputs[-1],) + if output_attentions: + all_self_attentions = all_self_attentions + (layer_outputs[1],) + + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states,) + + if not return_dict: + return tuple( + v + for v in [ + hidden_states, + next_decoder_cache, + all_hidden_states, + all_self_attentions, + all_cross_attentions, + ] + if v is not None + ) + return BaseModelOutputWithPastAndCrossAttentions( + last_hidden_state=hidden_states, + past_key_values=next_decoder_cache, + hidden_states=all_hidden_states, + attentions=all_self_attentions, + cross_attentions=all_cross_attentions, + ) + + +class BertPooler(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + self.activation = nn.Tanh() + + def forward(self, hidden_states): + # We "pool" the model by simply taking the hidden state corresponding + # to the first token. + first_token_tensor = hidden_states[:, 0] + pooled_output = self.dense(first_token_tensor) + pooled_output = self.activation(pooled_output) + return pooled_output + + +class BertPredictionHeadTransform(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + if isinstance(config.hidden_act, str): + self.transform_act_fn = ACT2FN[config.hidden_act] + else: + self.transform_act_fn = config.hidden_act + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + + def forward(self, hidden_states): + hidden_states = self.dense(hidden_states) + hidden_states = self.transform_act_fn(hidden_states) + hidden_states = self.LayerNorm(hidden_states) + return hidden_states + + +class BertLMPredictionHead(nn.Module): + def __init__(self, config): + super().__init__() + self.transform = BertPredictionHeadTransform(config) + + # The output weights are the same as the input embeddings, but there is + # an output-only bias for each token. + self.decoder = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + + self.bias = nn.Parameter(torch.zeros(config.vocab_size)) + + # Need a link between the two variables so that the bias is correctly resized with `resize_token_embeddings` + self.decoder.bias = self.bias + + def forward(self, hidden_states): + hidden_states = self.transform(hidden_states) + hidden_states = self.decoder(hidden_states) + return hidden_states + + +class BertOnlyMLMHead(nn.Module): + def __init__(self, config): + super().__init__() + self.predictions = BertLMPredictionHead(config) + + def forward(self, sequence_output): + prediction_scores = self.predictions(sequence_output) + return prediction_scores + + +class BertPreTrainedModel(PreTrainedModel): + """ + An abstract class to handle weights initialization and a simple interface for downloading and loading pretrained + models. + """ + + config_class = BertConfig + base_model_prefix = "bert" + _keys_to_ignore_on_load_missing = [r"position_ids"] + + def _init_weights(self, module): + """ Initialize the weights """ + if isinstance(module, (nn.Linear, nn.Embedding)): + # Slightly different from the TF version which uses truncated_normal for initialization + # cf https://github.com/pytorch/pytorch/pull/5617 + module.weight.data.normal_(mean=0.0, std=self.config.initializer_range) + elif isinstance(module, nn.LayerNorm): + module.bias.data.zero_() + module.weight.data.fill_(1.0) + if isinstance(module, nn.Linear) and module.bias is not None: + module.bias.data.zero_() + + +class BertModel(BertPreTrainedModel): + """ + The model can behave as an encoder (with only self-attention) as well as a decoder, in which case a layer of + cross-attention is added between the self-attention layers, following the architecture described in `Attention is + all you need `__ by Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, + Llion Jones, Aidan N. Gomez, Lukasz Kaiser and Illia Polosukhin. + argument and :obj:`add_cross_attention` set to :obj:`True`; an :obj:`encoder_hidden_states` is then expected as an + input to the forward pass. + """ + + def __init__(self, config, add_pooling_layer=True): + super().__init__(config) + self.config = config + + self.embeddings = BertEmbeddings(config) + + self.encoder = BertEncoder(config) + + self.pooler = BertPooler(config) if add_pooling_layer else None + + self.init_weights() + + + def get_input_embeddings(self): + return self.embeddings.word_embeddings + + def set_input_embeddings(self, value): + self.embeddings.word_embeddings = value + + def _prune_heads(self, heads_to_prune): + """ + Prunes heads of the model. heads_to_prune: dict of {layer_num: list of heads to prune in this layer} See base + class PreTrainedModel + """ + for layer, heads in heads_to_prune.items(): + self.encoder.layer[layer].attention.prune_heads(heads) + + + def get_extended_attention_mask(self, attention_mask: Tensor, input_shape: Tuple[int], device: device, is_decoder: bool) -> Tensor: + """ + Makes broadcastable attention and causal masks so that future and masked tokens are ignored. + + Arguments: + attention_mask (:obj:`torch.Tensor`): + Mask with ones indicating tokens to attend to, zeros for tokens to ignore. + input_shape (:obj:`Tuple[int]`): + The shape of the input to the model. + device: (:obj:`torch.device`): + The device of the input to the model. + + Returns: + :obj:`torch.Tensor` The extended attention mask, with a the same dtype as :obj:`attention_mask.dtype`. + """ + # We can provide a self-attention mask of dimensions [batch_size, from_seq_length, to_seq_length] + # ourselves in which case we just need to make it broadcastable to all heads. + if attention_mask.dim() == 3: + extended_attention_mask = attention_mask[:, None, :, :] + elif attention_mask.dim() == 2: + # Provided a padding mask of dimensions [batch_size, seq_length] + # - if the model is a decoder, apply a causal mask in addition to the padding mask + # - if the model is an encoder, make the mask broadcastable to [batch_size, num_heads, seq_length, seq_length] + if is_decoder: + batch_size, seq_length = input_shape + + seq_ids = torch.arange(seq_length, device=device) + causal_mask = seq_ids[None, None, :].repeat(batch_size, seq_length, 1) <= seq_ids[None, :, None] + # in case past_key_values are used we need to add a prefix ones mask to the causal mask + # causal and attention masks must have same type with pytorch version < 1.3 + causal_mask = causal_mask.to(attention_mask.dtype) + + if causal_mask.shape[1] < attention_mask.shape[1]: + prefix_seq_len = attention_mask.shape[1] - causal_mask.shape[1] + causal_mask = torch.cat( + [ + torch.ones((batch_size, seq_length, prefix_seq_len), device=device, dtype=causal_mask.dtype), + causal_mask, + ], + axis=-1, + ) + + extended_attention_mask = causal_mask[:, None, :, :] * attention_mask[:, None, None, :] + else: + extended_attention_mask = attention_mask[:, None, None, :] + else: + raise ValueError( + "Wrong shape for input_ids (shape {}) or attention_mask (shape {})".format( + input_shape, attention_mask.shape + ) + ) + + # Since attention_mask is 1.0 for positions we want to attend and 0.0 for + # masked positions, this operation will create a tensor which is 0.0 for + # positions we want to attend and -10000.0 for masked positions. + # Since we are adding it to the raw scores before the softmax, this is + # effectively the same as removing these entirely. + extended_attention_mask = extended_attention_mask.to(dtype=self.dtype) # fp16 compatibility + extended_attention_mask = (1.0 - extended_attention_mask) * -10000.0 + return extended_attention_mask + + def forward( + self, + input_ids=None, + attention_mask=None, + position_ids=None, + head_mask=None, + inputs_embeds=None, + encoder_embeds=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_values=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + is_decoder=False, + mode='multimodal', + ): + r""" + encoder_hidden_states (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`, `optional`): + Sequence of hidden-states at the output of the last layer of the encoder. Used in the cross-attention if + the model is configured as a decoder. + encoder_attention_mask (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Mask to avoid performing attention on the padding token indices of the encoder input. This mask is used in + the cross-attention if the model is configured as a decoder. Mask values selected in ``[0, 1]``: + - 1 for tokens that are **not masked**, + - 0 for tokens that are **masked**. + past_key_values (:obj:`tuple(tuple(torch.FloatTensor))` of length :obj:`config.n_layers` with each tuple having 4 tensors of shape :obj:`(batch_size, num_heads, sequence_length - 1, embed_size_per_head)`): + Contains precomputed key and value hidden states of the attention blocks. Can be used to speed up decoding. + If :obj:`past_key_values` are used, the user can optionally input only the last :obj:`decoder_input_ids` + (those that don't have their past key value states given to this model) of shape :obj:`(batch_size, 1)` + instead of all :obj:`decoder_input_ids` of shape :obj:`(batch_size, sequence_length)`. + use_cache (:obj:`bool`, `optional`): + If set to :obj:`True`, :obj:`past_key_values` key value states are returned and can be used to speed up + decoding (see :obj:`past_key_values`). + """ + output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions + output_hidden_states = ( + output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states + ) + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + if is_decoder: + use_cache = use_cache if use_cache is not None else self.config.use_cache + else: + use_cache = False + + if input_ids is not None and inputs_embeds is not None: + raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time") + elif input_ids is not None: + input_shape = input_ids.size() + batch_size, seq_length = input_shape + device = input_ids.device + elif inputs_embeds is not None: + input_shape = inputs_embeds.size()[:-1] + batch_size, seq_length = input_shape + device = inputs_embeds.device + elif encoder_embeds is not None: + input_shape = encoder_embeds.size()[:-1] + batch_size, seq_length = input_shape + device = encoder_embeds.device + else: + raise ValueError("You have to specify either input_ids or inputs_embeds or encoder_embeds") + + # past_key_values_length + past_key_values_length = past_key_values[0][0].shape[2] if past_key_values is not None else 0 + + if attention_mask is None: + attention_mask = torch.ones(((batch_size, seq_length + past_key_values_length)), device=device) + + # We can provide a self-attention mask of dimensions [batch_size, from_seq_length, to_seq_length] + # ourselves in which case we just need to make it broadcastable to all heads. + extended_attention_mask: torch.Tensor = self.get_extended_attention_mask(attention_mask, input_shape, + device, is_decoder) + + # If a 2D or 3D attention mask is provided for the cross-attention + # we need to make broadcastable to [batch_size, num_heads, seq_length, seq_length] + if encoder_hidden_states is not None: + if type(encoder_hidden_states) == list: + encoder_batch_size, encoder_sequence_length, _ = encoder_hidden_states[0].size() + else: + encoder_batch_size, encoder_sequence_length, _ = encoder_hidden_states.size() + encoder_hidden_shape = (encoder_batch_size, encoder_sequence_length) + + if type(encoder_attention_mask) == list: + encoder_extended_attention_mask = [self.invert_attention_mask(mask) for mask in encoder_attention_mask] + elif encoder_attention_mask is None: + encoder_attention_mask = torch.ones(encoder_hidden_shape, device=device) + encoder_extended_attention_mask = self.invert_attention_mask(encoder_attention_mask) + else: + encoder_extended_attention_mask = self.invert_attention_mask(encoder_attention_mask) + else: + encoder_extended_attention_mask = None + + # Prepare head mask if needed + # 1.0 in head_mask indicate we keep the head + # attention_probs has shape bsz x n_heads x N x N + # input head_mask has shape [num_heads] or [num_hidden_layers x num_heads] + # and head_mask is converted to shape [num_hidden_layers x batch x num_heads x seq_length x seq_length] + head_mask = self.get_head_mask(head_mask, self.config.num_hidden_layers) + + if encoder_embeds is None: + embedding_output = self.embeddings( + input_ids=input_ids, + position_ids=position_ids, + inputs_embeds=inputs_embeds, + past_key_values_length=past_key_values_length, + ) + else: + embedding_output = encoder_embeds + + encoder_outputs = self.encoder( + embedding_output, + attention_mask=extended_attention_mask, + head_mask=head_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_extended_attention_mask, + past_key_values=past_key_values, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + mode=mode, + ) + sequence_output = encoder_outputs[0] + pooled_output = self.pooler(sequence_output) if self.pooler is not None else None + + if not return_dict: + return (sequence_output, pooled_output) + encoder_outputs[1:] + + return BaseModelOutputWithPoolingAndCrossAttentions( + last_hidden_state=sequence_output, + pooler_output=pooled_output, + past_key_values=encoder_outputs.past_key_values, + hidden_states=encoder_outputs.hidden_states, + attentions=encoder_outputs.attentions, + cross_attentions=encoder_outputs.cross_attentions, + ) + diff --git a/repositories/blip/models/vit.py b/repositories/blip/models/vit.py new file mode 100644 index 000000000..cec3d8e08 --- /dev/null +++ b/repositories/blip/models/vit.py @@ -0,0 +1,305 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li + * Based on timm code base + * https://github.com/rwightman/pytorch-image-models/tree/master/timm +''' + +import torch +import torch.nn as nn +import torch.nn.functional as F +from functools import partial + +from timm.models.vision_transformer import _cfg, PatchEmbed +from timm.models.registry import register_model +from timm.models.layers import trunc_normal_, DropPath +from timm.models.helpers import named_apply, adapt_input_conv + +from fairscale.nn.checkpoint.checkpoint_activations import checkpoint_wrapper + +class Mlp(nn.Module): + """ MLP as used in Vision Transformer, MLP-Mixer and related networks + """ + def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +class Attention(nn.Module): + def __init__(self, dim, num_heads=8, qkv_bias=False, qk_scale=None, attn_drop=0., proj_drop=0.): + super().__init__() + self.num_heads = num_heads + head_dim = dim // num_heads + # NOTE scale factor was wrong in my original version, can set manually to be compat with prev weights + self.scale = qk_scale or head_dim ** -0.5 + self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) + self.attn_drop = nn.Dropout(attn_drop) + self.proj = nn.Linear(dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + self.attn_gradients = None + self.attention_map = None + + def save_attn_gradients(self, attn_gradients): + self.attn_gradients = attn_gradients + + def get_attn_gradients(self): + return self.attn_gradients + + def save_attention_map(self, attention_map): + self.attention_map = attention_map + + def get_attention_map(self): + return self.attention_map + + def forward(self, x, register_hook=False): + B, N, C = x.shape + qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4) + q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple) + + attn = (q @ k.transpose(-2, -1)) * self.scale + attn = attn.softmax(dim=-1) + attn = self.attn_drop(attn) + + if register_hook: + self.save_attention_map(attn) + attn.register_hook(self.save_attn_gradients) + + x = (attn @ v).transpose(1, 2).reshape(B, N, C) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class Block(nn.Module): + + def __init__(self, dim, num_heads, mlp_ratio=4., qkv_bias=False, qk_scale=None, drop=0., attn_drop=0., + drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm, use_grad_checkpointing=False): + super().__init__() + self.norm1 = norm_layer(dim) + self.attn = Attention( + dim, num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop) + # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here + self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity() + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) + + if use_grad_checkpointing: + self.attn = checkpoint_wrapper(self.attn) + self.mlp = checkpoint_wrapper(self.mlp) + + def forward(self, x, register_hook=False): + x = x + self.drop_path(self.attn(self.norm1(x), register_hook=register_hook)) + x = x + self.drop_path(self.mlp(self.norm2(x))) + return x + + +class VisionTransformer(nn.Module): + """ Vision Transformer + A PyTorch impl of : `An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale` - + https://arxiv.org/abs/2010.11929 + """ + def __init__(self, img_size=224, patch_size=16, in_chans=3, num_classes=1000, embed_dim=768, depth=12, + num_heads=12, mlp_ratio=4., qkv_bias=True, qk_scale=None, representation_size=None, + drop_rate=0., attn_drop_rate=0., drop_path_rate=0., norm_layer=None, + use_grad_checkpointing=False, ckpt_layer=0): + """ + Args: + img_size (int, tuple): input image size + patch_size (int, tuple): patch size + in_chans (int): number of input channels + num_classes (int): number of classes for classification head + embed_dim (int): embedding dimension + depth (int): depth of transformer + num_heads (int): number of attention heads + mlp_ratio (int): ratio of mlp hidden dim to embedding dim + qkv_bias (bool): enable bias for qkv if True + qk_scale (float): override default qk scale of head_dim ** -0.5 if set + representation_size (Optional[int]): enable and set representation layer (pre-logits) to this value if set + drop_rate (float): dropout rate + attn_drop_rate (float): attention dropout rate + drop_path_rate (float): stochastic depth rate + norm_layer: (nn.Module): normalization layer + """ + super().__init__() + self.num_features = self.embed_dim = embed_dim # num_features for consistency with other models + norm_layer = norm_layer or partial(nn.LayerNorm, eps=1e-6) + + self.patch_embed = PatchEmbed( + img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim) + + num_patches = self.patch_embed.num_patches + + self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) + self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, embed_dim)) + self.pos_drop = nn.Dropout(p=drop_rate) + + dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] # stochastic depth decay rule + self.blocks = nn.ModuleList([ + Block( + dim=embed_dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, + drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[i], norm_layer=norm_layer, + use_grad_checkpointing=(use_grad_checkpointing and i>=depth-ckpt_layer) + ) + for i in range(depth)]) + self.norm = norm_layer(embed_dim) + + trunc_normal_(self.pos_embed, std=.02) + trunc_normal_(self.cls_token, std=.02) + self.apply(self._init_weights) + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight, std=.02) + if isinstance(m, nn.Linear) and m.bias is not None: + nn.init.constant_(m.bias, 0) + elif isinstance(m, nn.LayerNorm): + nn.init.constant_(m.bias, 0) + nn.init.constant_(m.weight, 1.0) + + @torch.jit.ignore + def no_weight_decay(self): + return {'pos_embed', 'cls_token'} + + def forward(self, x, register_blk=-1): + B = x.shape[0] + x = self.patch_embed(x) + + cls_tokens = self.cls_token.expand(B, -1, -1) # stole cls_tokens impl from Phil Wang, thanks + x = torch.cat((cls_tokens, x), dim=1) + + x = x + self.pos_embed[:,:x.size(1),:] + x = self.pos_drop(x) + + for i,blk in enumerate(self.blocks): + x = blk(x, register_blk==i) + x = self.norm(x) + + return x + + @torch.jit.ignore() + def load_pretrained(self, checkpoint_path, prefix=''): + _load_weights(self, checkpoint_path, prefix) + + +@torch.no_grad() +def _load_weights(model: VisionTransformer, checkpoint_path: str, prefix: str = ''): + """ Load weights from .npz checkpoints for official Google Brain Flax implementation + """ + import numpy as np + + def _n2p(w, t=True): + if w.ndim == 4 and w.shape[0] == w.shape[1] == w.shape[2] == 1: + w = w.flatten() + if t: + if w.ndim == 4: + w = w.transpose([3, 2, 0, 1]) + elif w.ndim == 3: + w = w.transpose([2, 0, 1]) + elif w.ndim == 2: + w = w.transpose([1, 0]) + return torch.from_numpy(w) + + w = np.load(checkpoint_path) + if not prefix and 'opt/target/embedding/kernel' in w: + prefix = 'opt/target/' + + if hasattr(model.patch_embed, 'backbone'): + # hybrid + backbone = model.patch_embed.backbone + stem_only = not hasattr(backbone, 'stem') + stem = backbone if stem_only else backbone.stem + stem.conv.weight.copy_(adapt_input_conv(stem.conv.weight.shape[1], _n2p(w[f'{prefix}conv_root/kernel']))) + stem.norm.weight.copy_(_n2p(w[f'{prefix}gn_root/scale'])) + stem.norm.bias.copy_(_n2p(w[f'{prefix}gn_root/bias'])) + if not stem_only: + for i, stage in enumerate(backbone.stages): + for j, block in enumerate(stage.blocks): + bp = f'{prefix}block{i + 1}/unit{j + 1}/' + for r in range(3): + getattr(block, f'conv{r + 1}').weight.copy_(_n2p(w[f'{bp}conv{r + 1}/kernel'])) + getattr(block, f'norm{r + 1}').weight.copy_(_n2p(w[f'{bp}gn{r + 1}/scale'])) + getattr(block, f'norm{r + 1}').bias.copy_(_n2p(w[f'{bp}gn{r + 1}/bias'])) + if block.downsample is not None: + block.downsample.conv.weight.copy_(_n2p(w[f'{bp}conv_proj/kernel'])) + block.downsample.norm.weight.copy_(_n2p(w[f'{bp}gn_proj/scale'])) + block.downsample.norm.bias.copy_(_n2p(w[f'{bp}gn_proj/bias'])) + embed_conv_w = _n2p(w[f'{prefix}embedding/kernel']) + else: + embed_conv_w = adapt_input_conv( + model.patch_embed.proj.weight.shape[1], _n2p(w[f'{prefix}embedding/kernel'])) + model.patch_embed.proj.weight.copy_(embed_conv_w) + model.patch_embed.proj.bias.copy_(_n2p(w[f'{prefix}embedding/bias'])) + model.cls_token.copy_(_n2p(w[f'{prefix}cls'], t=False)) + pos_embed_w = _n2p(w[f'{prefix}Transformer/posembed_input/pos_embedding'], t=False) + if pos_embed_w.shape != model.pos_embed.shape: + pos_embed_w = resize_pos_embed( # resize pos embedding when different size from pretrained weights + pos_embed_w, model.pos_embed, getattr(model, 'num_tokens', 1), model.patch_embed.grid_size) + model.pos_embed.copy_(pos_embed_w) + model.norm.weight.copy_(_n2p(w[f'{prefix}Transformer/encoder_norm/scale'])) + model.norm.bias.copy_(_n2p(w[f'{prefix}Transformer/encoder_norm/bias'])) +# if isinstance(model.head, nn.Linear) and model.head.bias.shape[0] == w[f'{prefix}head/bias'].shape[-1]: +# model.head.weight.copy_(_n2p(w[f'{prefix}head/kernel'])) +# model.head.bias.copy_(_n2p(w[f'{prefix}head/bias'])) +# if isinstance(getattr(model.pre_logits, 'fc', None), nn.Linear) and f'{prefix}pre_logits/bias' in w: +# model.pre_logits.fc.weight.copy_(_n2p(w[f'{prefix}pre_logits/kernel'])) +# model.pre_logits.fc.bias.copy_(_n2p(w[f'{prefix}pre_logits/bias'])) + for i, block in enumerate(model.blocks.children()): + block_prefix = f'{prefix}Transformer/encoderblock_{i}/' + mha_prefix = block_prefix + 'MultiHeadDotProductAttention_1/' + block.norm1.weight.copy_(_n2p(w[f'{block_prefix}LayerNorm_0/scale'])) + block.norm1.bias.copy_(_n2p(w[f'{block_prefix}LayerNorm_0/bias'])) + block.attn.qkv.weight.copy_(torch.cat([ + _n2p(w[f'{mha_prefix}{n}/kernel'], t=False).flatten(1).T for n in ('query', 'key', 'value')])) + block.attn.qkv.bias.copy_(torch.cat([ + _n2p(w[f'{mha_prefix}{n}/bias'], t=False).reshape(-1) for n in ('query', 'key', 'value')])) + block.attn.proj.weight.copy_(_n2p(w[f'{mha_prefix}out/kernel']).flatten(1)) + block.attn.proj.bias.copy_(_n2p(w[f'{mha_prefix}out/bias'])) + for r in range(2): + getattr(block.mlp, f'fc{r + 1}').weight.copy_(_n2p(w[f'{block_prefix}MlpBlock_3/Dense_{r}/kernel'])) + getattr(block.mlp, f'fc{r + 1}').bias.copy_(_n2p(w[f'{block_prefix}MlpBlock_3/Dense_{r}/bias'])) + block.norm2.weight.copy_(_n2p(w[f'{block_prefix}LayerNorm_2/scale'])) + block.norm2.bias.copy_(_n2p(w[f'{block_prefix}LayerNorm_2/bias'])) + + +def interpolate_pos_embed(pos_embed_checkpoint, visual_encoder): + # interpolate position embedding + embedding_size = pos_embed_checkpoint.shape[-1] + num_patches = visual_encoder.patch_embed.num_patches + num_extra_tokens = visual_encoder.pos_embed.shape[-2] - num_patches + # height (== width) for the checkpoint position embedding + orig_size = int((pos_embed_checkpoint.shape[-2] - num_extra_tokens) ** 0.5) + # height (== width) for the new position embedding + new_size = int(num_patches ** 0.5) + + if orig_size!=new_size: + # class_token and dist_token are kept unchanged + extra_tokens = pos_embed_checkpoint[:, :num_extra_tokens] + # only the position tokens are interpolated + pos_tokens = pos_embed_checkpoint[:, num_extra_tokens:] + pos_tokens = pos_tokens.reshape(-1, orig_size, orig_size, embedding_size).permute(0, 3, 1, 2) + pos_tokens = torch.nn.functional.interpolate( + pos_tokens, size=(new_size, new_size), mode='bicubic', align_corners=False) + pos_tokens = pos_tokens.permute(0, 2, 3, 1).flatten(1, 2) + new_pos_embed = torch.cat((extra_tokens, pos_tokens), dim=1) + print('reshape position embedding from %d to %d'%(orig_size ** 2,new_size ** 2)) + + return new_pos_embed + else: + return pos_embed_checkpoint \ No newline at end of file diff --git a/repositories/blip/predict.py b/repositories/blip/predict.py new file mode 100644 index 000000000..35426cadc --- /dev/null +++ b/repositories/blip/predict.py @@ -0,0 +1,98 @@ +""" +Download the weights in ./checkpoints beforehand for fast inference +wget https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model*_base_caption.pth +wget https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model*_vqa.pth +wget https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_retrieval_coco.pth +""" + +from pathlib import Path + +from PIL import Image +import torch +from torchvision import transforms +from torchvision.transforms.functional import InterpolationMode +import cog + +from models.blip import blip_decoder +from models.blip_vqa import blip_vqa +from models.blip_itm import blip_itm + + +class Predictor(cog.Predictor): + def setup(self): + self.device = "cuda:0" + + self.models = { + 'image_captioning': blip_decoder(pretrained='checkpoints/model*_base_caption.pth', + image_size=384, vit='base'), + 'visual_question_answering': blip_vqa(pretrained='checkpoints/model*_vqa.pth', + image_size=480, vit='base'), + 'image_text_matching': blip_itm(pretrained='checkpoints/model_base_retrieval_coco.pth', + image_size=384, vit='base') + } + + @cog.input( + "image", + type=Path, + help="input image", + ) + @cog.input( + "task", + type=str, + default='image_captioning', + options=['image_captioning', 'visual_question_answering', 'image_text_matching'], + help="Choose a task.", + ) + @cog.input( + "question", + type=str, + default=None, + help="Type question for the input image for visual question answering task.", + ) + @cog.input( + "caption", + type=str, + default=None, + help="Type caption for the input image for image text matching task.", + ) + def predict(self, image, task, question, caption): + if task == 'visual_question_answering': + assert question is not None, 'Please type a question for visual question answering task.' + if task == 'image_text_matching': + assert caption is not None, 'Please type a caption for mage text matching task.' + + im = load_image(image, image_size=480 if task == 'visual_question_answering' else 384, device=self.device) + model = self.models[task] + model.eval() + model = model.to(self.device) + + if task == 'image_captioning': + with torch.no_grad(): + caption = model.generate(im, sample=False, num_beams=3, max_length=20, min_length=5) + return 'Caption: ' + caption[0] + + if task == 'visual_question_answering': + with torch.no_grad(): + answer = model(im, question, train=False, inference='generate') + return 'Answer: ' + answer[0] + + # image_text_matching + itm_output = model(im, caption, match_head='itm') + itm_score = torch.nn.functional.softmax(itm_output, dim=1)[:, 1] + itc_score = model(im, caption, match_head='itc') + return f'The image and text is matched with a probability of {itm_score.item():.4f}.\n' \ + f'The image feature and text feature has a cosine similarity of {itc_score.item():.4f}.' + + +def load_image(image, image_size, device): + raw_image = Image.open(str(image)).convert('RGB') + + w, h = raw_image.size + + transform = transforms.Compose([ + transforms.Resize((image_size, image_size), interpolation=InterpolationMode.BICUBIC), + transforms.ToTensor(), + transforms.Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711)) + ]) + image = transform(raw_image).unsqueeze(0).to(device) + return image diff --git a/repositories/blip/pretrain.py b/repositories/blip/pretrain.py new file mode 100644 index 000000000..c9490ec8e --- /dev/null +++ b/repositories/blip/pretrain.py @@ -0,0 +1,173 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.backends.cudnn as cudnn +import torch.distributed as dist +from torch.utils.data import DataLoader + +from models.blip_pretrain import blip_pretrain +import utils +from utils import warmup_lr_schedule, step_lr_schedule +from data import create_dataset, create_sampler, create_loader + +def train(model, data_loader, optimizer, epoch, device, config): + # train + model.train() + + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=50, fmt='{value:.6f}')) + metric_logger.add_meter('loss_ita', utils.SmoothedValue(window_size=50, fmt='{value:.4f}')) + metric_logger.add_meter('loss_itm', utils.SmoothedValue(window_size=50, fmt='{value:.4f}')) + metric_logger.add_meter('loss_lm', utils.SmoothedValue(window_size=50, fmt='{value:.4f}')) + + header = 'Train Epoch: [{}]'.format(epoch) + print_freq = 50 + + if config['laion_path']: + data_loader.dataset.reload_laion(epoch) + + data_loader.sampler.set_epoch(epoch) + + for i, (image, caption) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + + if epoch==0: + warmup_lr_schedule(optimizer, i, config['warmup_steps'], config['warmup_lr'], config['init_lr']) + + optimizer.zero_grad() + + image = image.to(device,non_blocking=True) + + # ramp up alpha in the first 2 epochs + alpha = config['alpha']*min(1,(epoch*len(data_loader)+i)/(2*len(data_loader))) + + loss_ita, loss_itm, loss_lm = model(image, caption, alpha = alpha) + loss = loss_ita + loss_itm + loss_lm + + loss.backward() + optimizer.step() + + metric_logger.update(loss_ita=loss_ita.item()) + metric_logger.update(loss_itm=loss_itm.item()) + metric_logger.update(loss_lm=loss_lm.item()) + metric_logger.update(lr=optimizer.param_groups[0]["lr"]) + + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.3f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating dataset") + datasets = [create_dataset('pretrain', config, min_scale=0.2)] + print('number of training samples: %d'%len(datasets[0])) + + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler(datasets, [True], num_tasks, global_rank) + + data_loader = create_loader(datasets,samplers,batch_size=[config['batch_size']], num_workers=[4], is_trains=[True], collate_fns=[None])[0] + + #### Model #### + print("Creating model") + model = blip_pretrain(image_size=config['image_size'], vit=config['vit'], vit_grad_ckpt=config['vit_grad_ckpt'], + vit_ckpt_layer=config['vit_ckpt_layer'], queue_size=config['queue_size']) + + model = model.to(device) + + optimizer = torch.optim.AdamW(params=model.parameters(), lr=config['init_lr'], weight_decay=config['weight_decay']) + + start_epoch = 0 + if args.checkpoint: + checkpoint = torch.load(args.checkpoint, map_location='cpu') + state_dict = checkpoint['model'] + model.load_state_dict(state_dict) + + optimizer.load_state_dict(checkpoint['optimizer']) + start_epoch = checkpoint['epoch']+1 + print('resume checkpoint from %s'%args.checkpoint) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + print("Start training") + start_time = time.time() + for epoch in range(start_epoch, config['max_epoch']): + + step_lr_schedule(optimizer, epoch, config['init_lr'], config['min_lr'], config['lr_decay_rate']) + + train_stats = train(model, data_loader, optimizer, epoch, device, config) + if utils.is_main_process(): + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + 'epoch': epoch, + } + save_obj = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'config': config, + 'epoch': epoch, + } + torch.save(save_obj, os.path.join(args.output_dir, 'checkpoint_%02d.pth'%epoch)) + + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + dist.barrier() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/pretrain.yaml') + parser.add_argument('--output_dir', default='output/Pretrain') + parser.add_argument('--checkpoint', default='') + parser.add_argument('--evaluate', action='store_true') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/repositories/blip/requirements.txt b/repositories/blip/requirements.txt new file mode 100644 index 000000000..d897bc6a0 --- /dev/null +++ b/repositories/blip/requirements.txt @@ -0,0 +1,4 @@ +timm==0.4.12 +transformers==4.15.0 +fairscale==0.4.4 +pycocoevalcap diff --git a/repositories/blip/train_caption.py b/repositories/blip/train_caption.py new file mode 100644 index 000000000..7c639ac64 --- /dev/null +++ b/repositories/blip/train_caption.py @@ -0,0 +1,206 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.backends.cudnn as cudnn +import torch.distributed as dist +from torch.utils.data import DataLoader + +from models.blip import blip_decoder +import utils +from utils import cosine_lr_schedule +from data import create_dataset, create_sampler, create_loader +from data.utils import save_result, coco_caption_eval + +def train(model, data_loader, optimizer, epoch, device): + # train + model.train() + + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=1, fmt='{value:.6f}')) + metric_logger.add_meter('loss', utils.SmoothedValue(window_size=1, fmt='{value:.4f}')) + header = 'Train Caption Epoch: [{}]'.format(epoch) + print_freq = 50 + + for i, (image, caption, _) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + image = image.to(device) + + loss = model(image, caption) + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + metric_logger.update(loss=loss.item()) + metric_logger.update(lr=optimizer.param_groups[0]["lr"]) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.3f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + +@torch.no_grad() +def evaluate(model, data_loader, device, config): + # evaluate + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Caption generation:' + print_freq = 10 + + result = [] + for image, image_id in metric_logger.log_every(data_loader, print_freq, header): + + image = image.to(device) + + captions = model.generate(image, sample=False, num_beams=config['num_beams'], max_length=config['max_length'], + min_length=config['min_length']) + + for caption, img_id in zip(captions, image_id): + result.append({"image_id": img_id.item(), "caption": caption}) + + return result + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating captioning dataset") + train_dataset, val_dataset, test_dataset = create_dataset('caption_coco', config) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler([train_dataset,val_dataset,test_dataset], [True,False,False], num_tasks, global_rank) + else: + samplers = [None, None, None] + + train_loader, val_loader, test_loader = create_loader([train_dataset, val_dataset, test_dataset],samplers, + batch_size=[config['batch_size']]*3,num_workers=[4,4,4], + is_trains=[True, False, False], collate_fns=[None,None,None]) + + #### Model #### + print("Creating model") + model = blip_decoder(pretrained=config['pretrained'], image_size=config['image_size'], vit=config['vit'], + vit_grad_ckpt=config['vit_grad_ckpt'], vit_ckpt_layer=config['vit_ckpt_layer'], + prompt=config['prompt']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + optimizer = torch.optim.AdamW(params=model.parameters(), lr=config['init_lr'], weight_decay=config['weight_decay']) + + best = 0 + best_epoch = 0 + + print("Start training") + start_time = time.time() + for epoch in range(0, config['max_epoch']): + if not args.evaluate: + if args.distributed: + train_loader.sampler.set_epoch(epoch) + + cosine_lr_schedule(optimizer, epoch, config['max_epoch'], config['init_lr'], config['min_lr']) + + train_stats = train(model, train_loader, optimizer, epoch, device) + + val_result = evaluate(model_without_ddp, val_loader, device, config) + val_result_file = save_result(val_result, args.result_dir, 'val_epoch%d'%epoch, remove_duplicate='image_id') + + test_result = evaluate(model_without_ddp, test_loader, device, config) + test_result_file = save_result(test_result, args.result_dir, 'test_epoch%d'%epoch, remove_duplicate='image_id') + + if utils.is_main_process(): + coco_val = coco_caption_eval(config['coco_gt_root'],val_result_file,'val') + coco_test = coco_caption_eval(config['coco_gt_root'],test_result_file,'test') + + if args.evaluate: + log_stats = {**{f'val_{k}': v for k, v in coco_val.eval.items()}, + **{f'test_{k}': v for k, v in coco_test.eval.items()}, + } + with open(os.path.join(args.output_dir, "evaluate.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + else: + save_obj = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'config': config, + 'epoch': epoch, + } + + if coco_val.eval['CIDEr'] + coco_val.eval['Bleu_4'] > best: + best = coco_val.eval['CIDEr'] + coco_val.eval['Bleu_4'] + best_epoch = epoch + torch.save(save_obj, os.path.join(args.output_dir, 'checkpoint_best.pth')) + + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + **{f'val_{k}': v for k, v in coco_val.eval.items()}, + **{f'test_{k}': v for k, v in coco_test.eval.items()}, + 'epoch': epoch, + 'best_epoch': best_epoch, + } + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + if args.evaluate: + break + dist.barrier() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/caption_coco.yaml') + parser.add_argument('--output_dir', default='output/Caption_coco') + parser.add_argument('--evaluate', action='store_true') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + args.result_dir = os.path.join(args.output_dir, 'result') + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + Path(args.result_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/repositories/blip/train_nlvr.py b/repositories/blip/train_nlvr.py new file mode 100644 index 000000000..84b247bda --- /dev/null +++ b/repositories/blip/train_nlvr.py @@ -0,0 +1,213 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path +import json +import pickle + +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch.utils.data import DataLoader +import torch.backends.cudnn as cudnn +import torch.distributed as dist + +from models.blip_nlvr import blip_nlvr + +import utils +from utils import cosine_lr_schedule, warmup_lr_schedule +from data import create_dataset, create_sampler, create_loader + +def train(model, data_loader, optimizer, epoch, device, config): + # train + model.train() + + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=50, fmt='{value:.6f}')) + metric_logger.add_meter('loss', utils.SmoothedValue(window_size=50, fmt='{value:.4f}')) + + header = 'Train Epoch: [{}]'.format(epoch) + print_freq = 50 + step_size = 10 + + for i,(image0, image1, text, targets) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + + images = torch.cat([image0, image1], dim=0) + images, targets = images.to(device), targets.to(device) + + loss = model(images, text, targets=targets, train=True) + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + metric_logger.update(lr=optimizer.param_groups[0]["lr"]) + metric_logger.update(loss=loss.item()) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.4f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + +@torch.no_grad() +def evaluate(model, data_loader, device, config): + # test + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + + header = 'Evaluation:' + print_freq = 50 + + for image0, image1, text, targets in metric_logger.log_every(data_loader, print_freq, header): + images = torch.cat([image0, image1], dim=0) + images, targets = images.to(device), targets.to(device) + + prediction = model(images, text, targets=targets, train=False) + + _, pred_class = prediction.max(1) + accuracy = (targets==pred_class).sum() / targets.size(0) + + metric_logger.meters['acc'].update(accuracy.item(), n=image0.size(0)) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.4f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating dataset") + datasets = create_dataset('nlvr', config) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler(datasets, [True,False,False], num_tasks, global_rank) + else: + samplers = [None, None, None] + + batch_size=[config['batch_size_train'],config['batch_size_test'],config['batch_size_test']] + train_loader, val_loader, test_loader = create_loader(datasets,samplers,batch_size=batch_size, + num_workers=[4,4,4],is_trains=[True,False,False], + collate_fns=[None,None,None]) + + #### Model #### + print("Creating model") + model = blip_nlvr(pretrained=config['pretrained'], image_size=config['image_size'], + vit=config['vit'], vit_grad_ckpt=config['vit_grad_ckpt'], vit_ckpt_layer=config['vit_ckpt_layer']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + optimizer = torch.optim.AdamW(params=model.parameters(), lr=config['init_lr'], weight_decay=config['weight_decay']) + + print("Start training") + start_time = time.time() + best = 0 + best_epoch = 0 + + for epoch in range(0, config['max_epoch']): + if not args.evaluate: + if args.distributed: + train_loader.sampler.set_epoch(epoch) + + cosine_lr_schedule(optimizer, epoch, config['max_epoch'], config['init_lr'], config['min_lr']) + + train_stats = train(model, train_loader, optimizer, epoch, device, config) + + val_stats = evaluate(model, val_loader, device, config) + test_stats = evaluate(model, test_loader, device, config) + + if utils.is_main_process(): + if args.evaluate: + log_stats = {**{f'val_{k}': v for k, v in val_stats.items()}, + **{f'test_{k}': v for k, v in test_stats.items()}, + } + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + else: + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + **{f'val_{k}': v for k, v in val_stats.items()}, + **{f'test_{k}': v for k, v in test_stats.items()}, + 'epoch': epoch, + } + + if float(val_stats['acc'])>best: + save_obj = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'config': config, + 'epoch': epoch, + } + torch.save(save_obj, os.path.join(args.output_dir, 'checkpoint_best.pth')) + best = float(val_stats['acc']) + best_epoch = epoch + + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + if args.evaluate: + break + + dist.barrier() + + if utils.is_main_process(): + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write("best epoch: %d"%best_epoch) + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/nlvr.yaml') + parser.add_argument('--output_dir', default='output/NLVR') + parser.add_argument('--evaluate', action='store_true') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/repositories/blip/train_retrieval.py b/repositories/blip/train_retrieval.py new file mode 100644 index 000000000..574f03382 --- /dev/null +++ b/repositories/blip/train_retrieval.py @@ -0,0 +1,345 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.backends.cudnn as cudnn +import torch.distributed as dist +from torch.utils.data import DataLoader + +from models.blip_retrieval import blip_retrieval +import utils +from utils import cosine_lr_schedule +from data import create_dataset, create_sampler, create_loader + + +def train(model, data_loader, optimizer, epoch, device, config): + # train + model.train() + + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=1, fmt='{value:.6f}')) + metric_logger.add_meter('loss_itm', utils.SmoothedValue(window_size=1, fmt='{value:.4f}')) + metric_logger.add_meter('loss_ita', utils.SmoothedValue(window_size=1, fmt='{value:.4f}')) + header = 'Train Epoch: [{}]'.format(epoch) + print_freq = 50 + + for i,(image, caption, idx) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + image = image.to(device,non_blocking=True) + idx = idx.to(device,non_blocking=True) + + if epoch>0: + alpha = config['alpha'] + else: + alpha = config['alpha']*min(1,i/len(data_loader)) + + loss_ita, loss_itm = model(image, caption, alpha=alpha, idx=idx) + loss = loss_ita + loss_itm + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + metric_logger.update(loss_itm=loss_itm.item()) + metric_logger.update(loss_ita=loss_ita.item()) + metric_logger.update(lr=optimizer.param_groups[0]["lr"]) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.3f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + +@torch.no_grad() +def evaluation(model, data_loader, device, config): + # test + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Evaluation:' + + print('Computing features for evaluation...') + start_time = time.time() + + texts = data_loader.dataset.text + num_text = len(texts) + text_bs = 256 + text_ids = [] + text_embeds = [] + text_atts = [] + for i in range(0, num_text, text_bs): + text = texts[i: min(num_text, i+text_bs)] + text_input = model.tokenizer(text, padding='max_length', truncation=True, max_length=35, return_tensors="pt").to(device) + text_output = model.text_encoder(text_input.input_ids, attention_mask = text_input.attention_mask, mode='text') + text_embed = F.normalize(model.text_proj(text_output.last_hidden_state[:,0,:])) + text_embeds.append(text_embed) + text_ids.append(text_input.input_ids) + text_atts.append(text_input.attention_mask) + + text_embeds = torch.cat(text_embeds,dim=0) + text_ids = torch.cat(text_ids,dim=0) + text_atts = torch.cat(text_atts,dim=0) + text_ids[:,0] = model.tokenizer.enc_token_id + + image_feats = [] + image_embeds = [] + for image, img_id in data_loader: + image = image.to(device) + image_feat = model.visual_encoder(image) + image_embed = model.vision_proj(image_feat[:,0,:]) + image_embed = F.normalize(image_embed,dim=-1) + + image_feats.append(image_feat.cpu()) + image_embeds.append(image_embed) + + image_feats = torch.cat(image_feats,dim=0) + image_embeds = torch.cat(image_embeds,dim=0) + + sims_matrix = image_embeds @ text_embeds.t() + score_matrix_i2t = torch.full((len(data_loader.dataset.image),len(texts)),-100.0).to(device) + + num_tasks = utils.get_world_size() + rank = utils.get_rank() + step = sims_matrix.size(0)//num_tasks + 1 + start = rank*step + end = min(sims_matrix.size(0),start+step) + + for i,sims in enumerate(metric_logger.log_every(sims_matrix[start:end], 50, header)): + topk_sim, topk_idx = sims.topk(k=config['k_test'], dim=0) + + encoder_output = image_feats[start+i].repeat(config['k_test'],1,1).to(device) + encoder_att = torch.ones(encoder_output.size()[:-1],dtype=torch.long).to(device) + output = model.text_encoder(text_ids[topk_idx], + attention_mask = text_atts[topk_idx], + encoder_hidden_states = encoder_output, + encoder_attention_mask = encoder_att, + return_dict = True, + ) + score = model.itm_head(output.last_hidden_state[:,0,:])[:,1] + score_matrix_i2t[start+i,topk_idx] = score + topk_sim + + sims_matrix = sims_matrix.t() + score_matrix_t2i = torch.full((len(texts),len(data_loader.dataset.image)),-100.0).to(device) + + step = sims_matrix.size(0)//num_tasks + 1 + start = rank*step + end = min(sims_matrix.size(0),start+step) + + for i,sims in enumerate(metric_logger.log_every(sims_matrix[start:end], 50, header)): + + topk_sim, topk_idx = sims.topk(k=config['k_test'], dim=0) + encoder_output = image_feats[topk_idx].to(device) + encoder_att = torch.ones(encoder_output.size()[:-1],dtype=torch.long).to(device) + output = model.text_encoder(text_ids[start+i].repeat(config['k_test'],1), + attention_mask = text_atts[start+i].repeat(config['k_test'],1), + encoder_hidden_states = encoder_output, + encoder_attention_mask = encoder_att, + return_dict = True, + ) + score = model.itm_head(output.last_hidden_state[:,0,:])[:,1] + score_matrix_t2i[start+i,topk_idx] = score + topk_sim + + if args.distributed: + dist.barrier() + torch.distributed.all_reduce(score_matrix_i2t, op=torch.distributed.ReduceOp.SUM) + torch.distributed.all_reduce(score_matrix_t2i, op=torch.distributed.ReduceOp.SUM) + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Evaluation time {}'.format(total_time_str)) + + return score_matrix_i2t.cpu().numpy(), score_matrix_t2i.cpu().numpy() + + + +@torch.no_grad() +def itm_eval(scores_i2t, scores_t2i, txt2img, img2txt): + + #Images->Text + ranks = np.zeros(scores_i2t.shape[0]) + for index,score in enumerate(scores_i2t): + inds = np.argsort(score)[::-1] + # Score + rank = 1e20 + for i in img2txt[index]: + tmp = np.where(inds == i)[0][0] + if tmp < rank: + rank = tmp + ranks[index] = rank + + # Compute metrics + tr1 = 100.0 * len(np.where(ranks < 1)[0]) / len(ranks) + tr5 = 100.0 * len(np.where(ranks < 5)[0]) / len(ranks) + tr10 = 100.0 * len(np.where(ranks < 10)[0]) / len(ranks) + + #Text->Images + ranks = np.zeros(scores_t2i.shape[0]) + + for index,score in enumerate(scores_t2i): + inds = np.argsort(score)[::-1] + ranks[index] = np.where(inds == txt2img[index])[0][0] + + # Compute metrics + ir1 = 100.0 * len(np.where(ranks < 1)[0]) / len(ranks) + ir5 = 100.0 * len(np.where(ranks < 5)[0]) / len(ranks) + ir10 = 100.0 * len(np.where(ranks < 10)[0]) / len(ranks) + + tr_mean = (tr1 + tr5 + tr10) / 3 + ir_mean = (ir1 + ir5 + ir10) / 3 + r_mean = (tr_mean + ir_mean) / 2 + + eval_result = {'txt_r1': tr1, + 'txt_r5': tr5, + 'txt_r10': tr10, + 'txt_r_mean': tr_mean, + 'img_r1': ir1, + 'img_r5': ir5, + 'img_r10': ir10, + 'img_r_mean': ir_mean, + 'r_mean': r_mean} + return eval_result + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating retrieval dataset") + train_dataset, val_dataset, test_dataset = create_dataset('retrieval_%s'%config['dataset'], config) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler([train_dataset], [True], num_tasks, global_rank) + [None, None] + else: + samplers = [None, None, None] + + train_loader, val_loader, test_loader = create_loader([train_dataset, val_dataset, test_dataset],samplers, + batch_size=[config['batch_size_train']]+[config['batch_size_test']]*2, + num_workers=[4,4,4], + is_trains=[True, False, False], + collate_fns=[None,None,None]) + + + #### Model #### + print("Creating model") + model = blip_retrieval(pretrained=config['pretrained'], image_size=config['image_size'], vit=config['vit'], + vit_grad_ckpt=config['vit_grad_ckpt'], vit_ckpt_layer=config['vit_ckpt_layer'], + queue_size=config['queue_size'], negative_all_rank=config['negative_all_rank']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + optimizer = torch.optim.AdamW(params=model.parameters(), lr=config['init_lr'], weight_decay=config['weight_decay']) + + best = 0 + best_epoch = 0 + + print("Start training") + start_time = time.time() + + for epoch in range(0, config['max_epoch']): + if not args.evaluate: + if args.distributed: + train_loader.sampler.set_epoch(epoch) + + cosine_lr_schedule(optimizer, epoch, config['max_epoch'], config['init_lr'], config['min_lr']) + + train_stats = train(model, train_loader, optimizer, epoch, device, config) + + score_val_i2t, score_val_t2i, = evaluation(model_without_ddp, val_loader, device, config) + score_test_i2t, score_test_t2i = evaluation(model_without_ddp, test_loader, device, config) + + if utils.is_main_process(): + + val_result = itm_eval(score_val_i2t, score_val_t2i, val_loader.dataset.txt2img, val_loader.dataset.img2txt) + print(val_result) + + if val_result['r_mean']>best: + save_obj = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'config': config, + 'epoch': epoch, + } + torch.save(save_obj, os.path.join(args.output_dir, 'checkpoint_best.pth')) + best = val_result['r_mean'] + best_epoch = epoch + + test_result = itm_eval(score_test_i2t, score_test_t2i, test_loader.dataset.txt2img, test_loader.dataset.img2txt) + print(test_result) + + if args.evaluate: + log_stats = {**{f'val_{k}': v for k, v in val_result.items()}, + **{f'test_{k}': v for k, v in test_result.items()}, + } + with open(os.path.join(args.output_dir, "evaluate.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + else: + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + **{f'val_{k}': v for k, v in val_result.items()}, + **{f'test_{k}': v for k, v in test_result.items()}, + 'epoch': epoch, + 'best_epoch': best_epoch, + } + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + if args.evaluate: + break + + dist.barrier() + torch.cuda.empty_cache() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/retrieval_flickr.yaml') + parser.add_argument('--output_dir', default='output/Retrieval_flickr') + parser.add_argument('--evaluate', action='store_true') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/repositories/blip/train_vqa.py b/repositories/blip/train_vqa.py new file mode 100644 index 000000000..89eb74908 --- /dev/null +++ b/repositories/blip/train_vqa.py @@ -0,0 +1,202 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch.utils.data import DataLoader +import torch.backends.cudnn as cudnn +import torch.distributed as dist + +from models.blip_vqa import blip_vqa +import utils +from utils import cosine_lr_schedule +from data import create_dataset, create_sampler, create_loader +from data.vqa_dataset import vqa_collate_fn +from data.utils import save_result + + +def train(model, data_loader, optimizer, epoch, device): + # train + model.train() + + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=1, fmt='{value:.6f}')) + metric_logger.add_meter('loss', utils.SmoothedValue(window_size=1, fmt='{value:.4f}')) + + header = 'Train Epoch: [{}]'.format(epoch) + print_freq = 50 + + for i,(image, question, answer, weights, n) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + image, weights = image.to(device,non_blocking=True), weights.to(device,non_blocking=True) + + loss = model(image, question, answer, train=True, n=n, weights=weights) + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + metric_logger.update(loss=loss.item()) + metric_logger.update(lr=optimizer.param_groups[0]["lr"]) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.3f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + +@torch.no_grad() +def evaluation(model, data_loader, device, config) : + # test + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Generate VQA test result:' + print_freq = 50 + + result = [] + + if config['inference']=='rank': + answer_list = data_loader.dataset.answer_list + answer_candidates = model.tokenizer(answer_list, padding='longest', return_tensors='pt').to(device) + answer_candidates.input_ids[:,0] = model.tokenizer.bos_token_id + + for n, (image, question, question_id) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + image = image.to(device,non_blocking=True) + + if config['inference']=='generate': + answers = model(image, question, train=False, inference='generate') + + for answer, ques_id in zip(answers, question_id): + ques_id = int(ques_id.item()) + result.append({"question_id":ques_id, "answer":answer}) + + elif config['inference']=='rank': + answer_ids = model(image, question, answer_candidates, train=False, inference='rank', k_test=config['k_test']) + + for ques_id, answer_id in zip(question_id, answer_ids): + result.append({"question_id":int(ques_id.item()), "answer":answer_list[answer_id]}) + + return result + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating vqa datasets") + datasets = create_dataset('vqa', config) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler(datasets, [True, False], num_tasks, global_rank) + else: + samplers = [None, None] + + train_loader, test_loader = create_loader(datasets,samplers, + batch_size=[config['batch_size_train'],config['batch_size_test']], + num_workers=[4,4],is_trains=[True, False], + collate_fns=[vqa_collate_fn,None]) + #### Model #### + print("Creating model") + model = blip_vqa(pretrained=config['pretrained'], image_size=config['image_size'], + vit=config['vit'], vit_grad_ckpt=config['vit_grad_ckpt'], vit_ckpt_layer=config['vit_ckpt_layer']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + optimizer = torch.optim.AdamW(params=model.parameters(), lr=config['init_lr'], weight_decay=config['weight_decay']) + + best = 0 + best_epoch = 0 + + print("Start training") + start_time = time.time() + for epoch in range(0, config['max_epoch']): + if not args.evaluate: + if args.distributed: + train_loader.sampler.set_epoch(epoch) + + cosine_lr_schedule(optimizer, epoch, config['max_epoch'], config['init_lr'], config['min_lr']) + + train_stats = train(model, train_loader, optimizer, epoch, device) + + else: + break + + if utils.is_main_process(): + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + 'epoch': epoch, + } + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + save_obj = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'config': config, + 'epoch': epoch, + } + torch.save(save_obj, os.path.join(args.output_dir, 'checkpoint_%02d.pth'%epoch)) + + dist.barrier() + + vqa_result = evaluation(model_without_ddp, test_loader, device, config) + result_file = save_result(vqa_result, args.result_dir, 'vqa_result') + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/vqa.yaml') + parser.add_argument('--output_dir', default='output/VQA') + parser.add_argument('--evaluate', action='store_true') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + args.result_dir = os.path.join(args.output_dir, 'result') + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + Path(args.result_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/repositories/blip/transform/randaugment.py b/repositories/blip/transform/randaugment.py new file mode 100644 index 000000000..094d9f4ca --- /dev/null +++ b/repositories/blip/transform/randaugment.py @@ -0,0 +1,340 @@ +import cv2 +import numpy as np + + +## aug functions +def identity_func(img): + return img + + +def autocontrast_func(img, cutoff=0): + ''' + same output as PIL.ImageOps.autocontrast + ''' + n_bins = 256 + + def tune_channel(ch): + n = ch.size + cut = cutoff * n // 100 + if cut == 0: + high, low = ch.max(), ch.min() + else: + hist = cv2.calcHist([ch], [0], None, [n_bins], [0, n_bins]) + low = np.argwhere(np.cumsum(hist) > cut) + low = 0 if low.shape[0] == 0 else low[0] + high = np.argwhere(np.cumsum(hist[::-1]) > cut) + high = n_bins - 1 if high.shape[0] == 0 else n_bins - 1 - high[0] + if high <= low: + table = np.arange(n_bins) + else: + scale = (n_bins - 1) / (high - low) + offset = -low * scale + table = np.arange(n_bins) * scale + offset + table[table < 0] = 0 + table[table > n_bins - 1] = n_bins - 1 + table = table.clip(0, 255).astype(np.uint8) + return table[ch] + + channels = [tune_channel(ch) for ch in cv2.split(img)] + out = cv2.merge(channels) + return out + + +def equalize_func(img): + ''' + same output as PIL.ImageOps.equalize + PIL's implementation is different from cv2.equalize + ''' + n_bins = 256 + + def tune_channel(ch): + hist = cv2.calcHist([ch], [0], None, [n_bins], [0, n_bins]) + non_zero_hist = hist[hist != 0].reshape(-1) + step = np.sum(non_zero_hist[:-1]) // (n_bins - 1) + if step == 0: return ch + n = np.empty_like(hist) + n[0] = step // 2 + n[1:] = hist[:-1] + table = (np.cumsum(n) // step).clip(0, 255).astype(np.uint8) + return table[ch] + + channels = [tune_channel(ch) for ch in cv2.split(img)] + out = cv2.merge(channels) + return out + + +def rotate_func(img, degree, fill=(0, 0, 0)): + ''' + like PIL, rotate by degree, not radians + ''' + H, W = img.shape[0], img.shape[1] + center = W / 2, H / 2 + M = cv2.getRotationMatrix2D(center, degree, 1) + out = cv2.warpAffine(img, M, (W, H), borderValue=fill) + return out + + +def solarize_func(img, thresh=128): + ''' + same output as PIL.ImageOps.posterize + ''' + table = np.array([el if el < thresh else 255 - el for el in range(256)]) + table = table.clip(0, 255).astype(np.uint8) + out = table[img] + return out + + +def color_func(img, factor): + ''' + same output as PIL.ImageEnhance.Color + ''' + ## implementation according to PIL definition, quite slow + # degenerate = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)[:, :, np.newaxis] + # out = blend(degenerate, img, factor) + # M = ( + # np.eye(3) * factor + # + np.float32([0.114, 0.587, 0.299]).reshape(3, 1) * (1. - factor) + # )[np.newaxis, np.newaxis, :] + M = ( + np.float32([ + [0.886, -0.114, -0.114], + [-0.587, 0.413, -0.587], + [-0.299, -0.299, 0.701]]) * factor + + np.float32([[0.114], [0.587], [0.299]]) + ) + out = np.matmul(img, M).clip(0, 255).astype(np.uint8) + return out + + +def contrast_func(img, factor): + """ + same output as PIL.ImageEnhance.Contrast + """ + mean = np.sum(np.mean(img, axis=(0, 1)) * np.array([0.114, 0.587, 0.299])) + table = np.array([( + el - mean) * factor + mean + for el in range(256) + ]).clip(0, 255).astype(np.uint8) + out = table[img] + return out + + +def brightness_func(img, factor): + ''' + same output as PIL.ImageEnhance.Contrast + ''' + table = (np.arange(256, dtype=np.float32) * factor).clip(0, 255).astype(np.uint8) + out = table[img] + return out + + +def sharpness_func(img, factor): + ''' + The differences the this result and PIL are all on the 4 boundaries, the center + areas are same + ''' + kernel = np.ones((3, 3), dtype=np.float32) + kernel[1][1] = 5 + kernel /= 13 + degenerate = cv2.filter2D(img, -1, kernel) + if factor == 0.0: + out = degenerate + elif factor == 1.0: + out = img + else: + out = img.astype(np.float32) + degenerate = degenerate.astype(np.float32)[1:-1, 1:-1, :] + out[1:-1, 1:-1, :] = degenerate + factor * (out[1:-1, 1:-1, :] - degenerate) + out = out.astype(np.uint8) + return out + + +def shear_x_func(img, factor, fill=(0, 0, 0)): + H, W = img.shape[0], img.shape[1] + M = np.float32([[1, factor, 0], [0, 1, 0]]) + out = cv2.warpAffine(img, M, (W, H), borderValue=fill, flags=cv2.INTER_LINEAR).astype(np.uint8) + return out + + +def translate_x_func(img, offset, fill=(0, 0, 0)): + ''' + same output as PIL.Image.transform + ''' + H, W = img.shape[0], img.shape[1] + M = np.float32([[1, 0, -offset], [0, 1, 0]]) + out = cv2.warpAffine(img, M, (W, H), borderValue=fill, flags=cv2.INTER_LINEAR).astype(np.uint8) + return out + + +def translate_y_func(img, offset, fill=(0, 0, 0)): + ''' + same output as PIL.Image.transform + ''' + H, W = img.shape[0], img.shape[1] + M = np.float32([[1, 0, 0], [0, 1, -offset]]) + out = cv2.warpAffine(img, M, (W, H), borderValue=fill, flags=cv2.INTER_LINEAR).astype(np.uint8) + return out + + +def posterize_func(img, bits): + ''' + same output as PIL.ImageOps.posterize + ''' + out = np.bitwise_and(img, np.uint8(255 << (8 - bits))) + return out + + +def shear_y_func(img, factor, fill=(0, 0, 0)): + H, W = img.shape[0], img.shape[1] + M = np.float32([[1, 0, 0], [factor, 1, 0]]) + out = cv2.warpAffine(img, M, (W, H), borderValue=fill, flags=cv2.INTER_LINEAR).astype(np.uint8) + return out + + +def cutout_func(img, pad_size, replace=(0, 0, 0)): + replace = np.array(replace, dtype=np.uint8) + H, W = img.shape[0], img.shape[1] + rh, rw = np.random.random(2) + pad_size = pad_size // 2 + ch, cw = int(rh * H), int(rw * W) + x1, x2 = max(ch - pad_size, 0), min(ch + pad_size, H) + y1, y2 = max(cw - pad_size, 0), min(cw + pad_size, W) + out = img.copy() + out[x1:x2, y1:y2, :] = replace + return out + + +### level to args +def enhance_level_to_args(MAX_LEVEL): + def level_to_args(level): + return ((level / MAX_LEVEL) * 1.8 + 0.1,) + return level_to_args + + +def shear_level_to_args(MAX_LEVEL, replace_value): + def level_to_args(level): + level = (level / MAX_LEVEL) * 0.3 + if np.random.random() > 0.5: level = -level + return (level, replace_value) + + return level_to_args + + +def translate_level_to_args(translate_const, MAX_LEVEL, replace_value): + def level_to_args(level): + level = (level / MAX_LEVEL) * float(translate_const) + if np.random.random() > 0.5: level = -level + return (level, replace_value) + + return level_to_args + + +def cutout_level_to_args(cutout_const, MAX_LEVEL, replace_value): + def level_to_args(level): + level = int((level / MAX_LEVEL) * cutout_const) + return (level, replace_value) + + return level_to_args + + +def solarize_level_to_args(MAX_LEVEL): + def level_to_args(level): + level = int((level / MAX_LEVEL) * 256) + return (level, ) + return level_to_args + + +def none_level_to_args(level): + return () + + +def posterize_level_to_args(MAX_LEVEL): + def level_to_args(level): + level = int((level / MAX_LEVEL) * 4) + return (level, ) + return level_to_args + + +def rotate_level_to_args(MAX_LEVEL, replace_value): + def level_to_args(level): + level = (level / MAX_LEVEL) * 30 + if np.random.random() < 0.5: + level = -level + return (level, replace_value) + + return level_to_args + + +func_dict = { + 'Identity': identity_func, + 'AutoContrast': autocontrast_func, + 'Equalize': equalize_func, + 'Rotate': rotate_func, + 'Solarize': solarize_func, + 'Color': color_func, + 'Contrast': contrast_func, + 'Brightness': brightness_func, + 'Sharpness': sharpness_func, + 'ShearX': shear_x_func, + 'TranslateX': translate_x_func, + 'TranslateY': translate_y_func, + 'Posterize': posterize_func, + 'ShearY': shear_y_func, +} + +translate_const = 10 +MAX_LEVEL = 10 +replace_value = (128, 128, 128) +arg_dict = { + 'Identity': none_level_to_args, + 'AutoContrast': none_level_to_args, + 'Equalize': none_level_to_args, + 'Rotate': rotate_level_to_args(MAX_LEVEL, replace_value), + 'Solarize': solarize_level_to_args(MAX_LEVEL), + 'Color': enhance_level_to_args(MAX_LEVEL), + 'Contrast': enhance_level_to_args(MAX_LEVEL), + 'Brightness': enhance_level_to_args(MAX_LEVEL), + 'Sharpness': enhance_level_to_args(MAX_LEVEL), + 'ShearX': shear_level_to_args(MAX_LEVEL, replace_value), + 'TranslateX': translate_level_to_args( + translate_const, MAX_LEVEL, replace_value + ), + 'TranslateY': translate_level_to_args( + translate_const, MAX_LEVEL, replace_value + ), + 'Posterize': posterize_level_to_args(MAX_LEVEL), + 'ShearY': shear_level_to_args(MAX_LEVEL, replace_value), +} + + +class RandomAugment(object): + + def __init__(self, N=2, M=10, isPIL=False, augs=[]): + self.N = N + self.M = M + self.isPIL = isPIL + if augs: + self.augs = augs + else: + self.augs = list(arg_dict.keys()) + + def get_random_ops(self): + sampled_ops = np.random.choice(self.augs, self.N) + return [(op, 0.5, self.M) for op in sampled_ops] + + def __call__(self, img): + if self.isPIL: + img = np.array(img) + ops = self.get_random_ops() + for name, prob, level in ops: + if np.random.random() > prob: + continue + args = arg_dict[name](level) + img = func_dict[name](img, *args) + return img + + +if __name__ == '__main__': + a = RandomAugment() + img = np.random.randn(32, 32, 3) + a(img) \ No newline at end of file diff --git a/repositories/blip/utils.py b/repositories/blip/utils.py new file mode 100644 index 000000000..ebe0e1dc2 --- /dev/null +++ b/repositories/blip/utils.py @@ -0,0 +1,278 @@ +import math +def cosine_lr_schedule(optimizer, epoch, max_epoch, init_lr, min_lr): + """Decay the learning rate""" + lr = (init_lr - min_lr) * 0.5 * (1. + math.cos(math.pi * epoch / max_epoch)) + min_lr + for param_group in optimizer.param_groups: + param_group['lr'] = lr + +def warmup_lr_schedule(optimizer, step, max_step, init_lr, max_lr): + """Warmup the learning rate""" + lr = min(max_lr, init_lr + (max_lr - init_lr) * step / max_step) + for param_group in optimizer.param_groups: + param_group['lr'] = lr + +def step_lr_schedule(optimizer, epoch, init_lr, min_lr, decay_rate): + """Decay the learning rate""" + lr = max(min_lr, init_lr * (decay_rate**epoch)) + for param_group in optimizer.param_groups: + param_group['lr'] = lr + +import numpy as np +import io +import os +import time +from collections import defaultdict, deque +import datetime + +import torch +import torch.distributed as dist + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size=20, fmt=None): + if fmt is None: + fmt = "{median:.4f} ({global_avg:.4f})" + self.deque = deque(maxlen=window_size) + self.total = 0.0 + self.count = 0 + self.fmt = fmt + + def update(self, value, n=1): + self.deque.append(value) + self.count += n + self.total += value * n + + def synchronize_between_processes(self): + """ + Warning: does not synchronize the deque! + """ + if not is_dist_avail_and_initialized(): + return + t = torch.tensor([self.count, self.total], dtype=torch.float64, device='cuda') + dist.barrier() + dist.all_reduce(t) + t = t.tolist() + self.count = int(t[0]) + self.total = t[1] + + @property + def median(self): + d = torch.tensor(list(self.deque)) + return d.median().item() + + @property + def avg(self): + d = torch.tensor(list(self.deque), dtype=torch.float32) + return d.mean().item() + + @property + def global_avg(self): + return self.total / self.count + + @property + def max(self): + return max(self.deque) + + @property + def value(self): + return self.deque[-1] + + def __str__(self): + return self.fmt.format( + median=self.median, + avg=self.avg, + global_avg=self.global_avg, + max=self.max, + value=self.value) + + +class MetricLogger(object): + def __init__(self, delimiter="\t"): + self.meters = defaultdict(SmoothedValue) + self.delimiter = delimiter + + def update(self, **kwargs): + for k, v in kwargs.items(): + if isinstance(v, torch.Tensor): + v = v.item() + assert isinstance(v, (float, int)) + self.meters[k].update(v) + + def __getattr__(self, attr): + if attr in self.meters: + return self.meters[attr] + if attr in self.__dict__: + return self.__dict__[attr] + raise AttributeError("'{}' object has no attribute '{}'".format( + type(self).__name__, attr)) + + def __str__(self): + loss_str = [] + for name, meter in self.meters.items(): + loss_str.append( + "{}: {}".format(name, str(meter)) + ) + return self.delimiter.join(loss_str) + + def global_avg(self): + loss_str = [] + for name, meter in self.meters.items(): + loss_str.append( + "{}: {:.4f}".format(name, meter.global_avg) + ) + return self.delimiter.join(loss_str) + + def synchronize_between_processes(self): + for meter in self.meters.values(): + meter.synchronize_between_processes() + + def add_meter(self, name, meter): + self.meters[name] = meter + + def log_every(self, iterable, print_freq, header=None): + i = 0 + if not header: + header = '' + start_time = time.time() + end = time.time() + iter_time = SmoothedValue(fmt='{avg:.4f}') + data_time = SmoothedValue(fmt='{avg:.4f}') + space_fmt = ':' + str(len(str(len(iterable)))) + 'd' + log_msg = [ + header, + '[{0' + space_fmt + '}/{1}]', + 'eta: {eta}', + '{meters}', + 'time: {time}', + 'data: {data}' + ] + if torch.cuda.is_available(): + log_msg.append('max mem: {memory:.0f}') + log_msg = self.delimiter.join(log_msg) + MB = 1024.0 * 1024.0 + for obj in iterable: + data_time.update(time.time() - end) + yield obj + iter_time.update(time.time() - end) + if i % print_freq == 0 or i == len(iterable) - 1: + eta_seconds = iter_time.global_avg * (len(iterable) - i) + eta_string = str(datetime.timedelta(seconds=int(eta_seconds))) + if torch.cuda.is_available(): + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time), + memory=torch.cuda.max_memory_allocated() / MB)) + else: + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time))) + i += 1 + end = time.time() + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('{} Total time: {} ({:.4f} s / it)'.format( + header, total_time_str, total_time / len(iterable))) + + +class AttrDict(dict): + def __init__(self, *args, **kwargs): + super(AttrDict, self).__init__(*args, **kwargs) + self.__dict__ = self + + +def compute_acc(logits, label, reduction='mean'): + ret = (torch.argmax(logits, dim=1) == label).float() + if reduction == 'none': + return ret.detach() + elif reduction == 'mean': + return ret.mean().item() + +def compute_n_params(model, return_str=True): + tot = 0 + for p in model.parameters(): + w = 1 + for x in p.shape: + w *= x + tot += w + if return_str: + if tot >= 1e6: + return '{:.1f}M'.format(tot / 1e6) + else: + return '{:.1f}K'.format(tot / 1e3) + else: + return tot + +def setup_for_distributed(is_master): + """ + This function disables printing when not in master process + """ + import builtins as __builtin__ + builtin_print = __builtin__.print + + def print(*args, **kwargs): + force = kwargs.pop('force', False) + if is_master or force: + builtin_print(*args, **kwargs) + + __builtin__.print = print + + +def is_dist_avail_and_initialized(): + if not dist.is_available(): + return False + if not dist.is_initialized(): + return False + return True + + +def get_world_size(): + if not is_dist_avail_and_initialized(): + return 1 + return dist.get_world_size() + + +def get_rank(): + if not is_dist_avail_and_initialized(): + return 0 + return dist.get_rank() + + +def is_main_process(): + return get_rank() == 0 + + +def save_on_master(*args, **kwargs): + if is_main_process(): + torch.save(*args, **kwargs) + + +def init_distributed_mode(args): + if 'RANK' in os.environ and 'WORLD_SIZE' in os.environ: + args.rank = int(os.environ["RANK"]) + args.world_size = int(os.environ['WORLD_SIZE']) + args.gpu = int(os.environ['LOCAL_RANK']) + elif 'SLURM_PROCID' in os.environ: + args.rank = int(os.environ['SLURM_PROCID']) + args.gpu = args.rank % torch.cuda.device_count() + else: + print('Not using distributed mode') + args.distributed = False + return + + args.distributed = True + + torch.cuda.set_device(args.gpu) + args.dist_backend = 'nccl' + print('| distributed init (rank {}, word {}): {}'.format( + args.rank, args.world_size, args.dist_url), flush=True) + torch.distributed.init_process_group(backend=args.dist_backend, init_method=args.dist_url, + world_size=args.world_size, rank=args.rank) + torch.distributed.barrier() + setup_for_distributed(args.rank == 0) + + \ No newline at end of file diff --git a/repositories/codeformer/LICENSE b/repositories/codeformer/LICENSE new file mode 100644 index 000000000..44bf750a2 --- /dev/null +++ b/repositories/codeformer/LICENSE @@ -0,0 +1,35 @@ +S-Lab License 1.0 + +Copyright 2022 S-Lab + +Redistribution and use for non-commercial purpose in source and +binary forms, with or without modification, are permitted provided +that the following conditions are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +In the event that redistribution and/or use for commercial purpose in +source or binary forms, with or without modification is required, +please contact the contributor(s) of the work. \ No newline at end of file diff --git a/repositories/codeformer/README.md b/repositories/codeformer/README.md new file mode 100644 index 000000000..f75785393 --- /dev/null +++ b/repositories/codeformer/README.md @@ -0,0 +1,149 @@ +

+ +

+ +## Towards Robust Blind Face Restoration with Codebook Lookup Transformer (NeurIPS 2022) + +[Paper](https://arxiv.org/abs/2206.11253) | [Project Page](https://shangchenzhou.com/projects/CodeFormer/) | [Video](https://youtu.be/d3VDpkXlueI) + + +google colab logo [![Hugging Face](https://img.shields.io/badge/Demo-%F0%9F%A4%97%20Hugging%20Face-blue)](https://huggingface.co/spaces/sczhou/CodeFormer) [![Replicate](https://img.shields.io/badge/Demo-%F0%9F%9A%80%20Replicate-blue)](https://replicate.com/sczhou/codeformer) ![visitors](https://visitor-badge-sczhou.glitch.me/badge?page_id=sczhou/CodeFormer) + + + +[Shangchen Zhou](https://shangchenzhou.com/), [Kelvin C.K. Chan](https://ckkelvinchan.github.io/), [Chongyi Li](https://li-chongyi.github.io/), [Chen Change Loy](https://www.mmlab-ntu.com/person/ccloy/) + +S-Lab, Nanyang Technological University + + + + +:star: If CodeFormer is helpful to your images or projects, please help star this repo. Thanks! :hugs: + +**[News]**: :whale: *Due to copyright issues, we have to delay the release of the training code (expected by the end of this year). Please star and stay tuned for our future updates!* +### Update +- **2022.10.05**: Support video input `--input_path [YOUR_VIDOE.mp4]`. Try it to enhance your videos! :clapper: +- **2022.09.14**: Integrated to :hugs: [Hugging Face](https://huggingface.co/spaces). Try out online demo! [![Hugging Face](https://img.shields.io/badge/Demo-%F0%9F%A4%97%20Hugging%20Face-blue)](https://huggingface.co/spaces/sczhou/CodeFormer) +- **2022.09.09**: Integrated to :rocket: [Replicate](https://replicate.com/explore). Try out online demo! [![Replicate](https://img.shields.io/badge/Demo-%F0%9F%9A%80%20Replicate-blue)](https://replicate.com/sczhou/codeformer) +- **2022.09.04**: Add face upsampling `--face_upsample` for high-resolution AI-created face enhancement. +- **2022.08.23**: Some modifications on face detection and fusion for better AI-created face enhancement. +- **2022.08.07**: Integrate [Real-ESRGAN](https://github.com/xinntao/Real-ESRGAN) to support background image enhancement. +- **2022.07.29**: Integrate new face detectors of `['RetinaFace'(default), 'YOLOv5']`. +- **2022.07.17**: Add Colab demo of CodeFormer. google colab logo +- **2022.07.16**: Release inference code for face restoration. :blush: +- **2022.06.21**: This repo is created. + +### TODO +- [ ] Add checkpoint for face inpainting +- [ ] Add checkpoint for face colorization +- [ ] Add training code and config files +- [x] ~~Add background image enhancement~~ + +#### :panda_face: Try Enhancing Old Photos / Fixing AI-arts +[](https://imgsli.com/MTI3NTE2) [](https://imgsli.com/MTI3NTE1) [](https://imgsli.com/MTI3NTIw) + +#### Face Restoration + + + + +#### Face Color Enhancement and Restoration + + + +#### Face Inpainting + + + + + +### Dependencies and Installation + +- Pytorch >= 1.7.1 +- CUDA >= 10.1 +- Other required packages in `requirements.txt` +``` +# git clone this repository +git clone https://github.com/sczhou/CodeFormer +cd CodeFormer + +# create new anaconda env +conda create -n codeformer python=3.8 -y +conda activate codeformer + +# install python dependencies +pip3 install -r requirements.txt +python basicsr/setup.py develop +``` + + +### Quick Inference + +#### Download Pre-trained Models: +Download the facelib pretrained models from [[Google Drive](https://drive.google.com/drive/folders/1b_3qwrzY_kTQh0-SnBoGBgOrJ_PLZSKm?usp=sharing) | [OneDrive](https://entuedu-my.sharepoint.com/:f:/g/personal/s200094_e_ntu_edu_sg/EvDxR7FcAbZMp_MA9ouq7aQB8XTppMb3-T0uGZ_2anI2mg?e=DXsJFo)] to the `weights/facelib` folder. You can manually download the pretrained models OR download by running the following command. +``` +python scripts/download_pretrained_models.py facelib +``` + +Download the CodeFormer pretrained models from [[Google Drive](https://drive.google.com/drive/folders/1CNNByjHDFt0b95q54yMVp6Ifo5iuU6QS?usp=sharing) | [OneDrive](https://entuedu-my.sharepoint.com/:f:/g/personal/s200094_e_ntu_edu_sg/EoKFj4wo8cdIn2-TY2IV6CYBhZ0pIG4kUOeHdPR_A5nlbg?e=AO8UN9)] to the `weights/CodeFormer` folder. You can manually download the pretrained models OR download by running the following command. +``` +python scripts/download_pretrained_models.py CodeFormer +``` + +#### Prepare Testing Data: +You can put the testing images in the `inputs/TestWhole` folder. If you would like to test on cropped and aligned faces, you can put them in the `inputs/cropped_faces` folder. + + +#### Testing on Face Restoration: +[Note] If you want to compare CodeFormer in your paper, please run the following command indicating `--has_aligned` (for cropped and aligned face), as the command for the whole image will involve a process of face-background fusion that may damage hair texture on the boundary, which leads to unfair comparison. + +🧑🏻 Face Restoration (cropped and aligned face) +``` +# For cropped and aligned faces +python inference_codeformer.py -w 0.5 --has_aligned --input_path [image folder]|[image path] +``` + +:framed_picture: Whole Image Enhancement +``` +# For whole image +# Add '--bg_upsampler realesrgan' to enhance the background regions with Real-ESRGAN +# Add '--face_upsample' to further upsample restorated face with Real-ESRGAN +python inference_codeformer.py -w 0.7 --input_path [image folder]|[image path] +``` + +:clapper: Video Enhancement +``` +# For Windows/Mac users, please install ffmpeg first +conda install -c conda-forge ffmpeg +``` +``` +# For video clips +# video path should end with '.mp4'|'.mov'|'.avi' +python inference_codeformer.py --bg_upsampler realesrgan --face_upsample -w 1.0 --input_path [video path] +``` + + +Fidelity weight *w* lays in [0, 1]. Generally, smaller *w* tends to produce a higher-quality result, while larger *w* yields a higher-fidelity result. + +The results will be saved in the `results` folder. + +### Citation +If our work is useful for your research, please consider citing: + + @inproceedings{zhou2022codeformer, + author = {Zhou, Shangchen and Chan, Kelvin C.K. and Li, Chongyi and Loy, Chen Change}, + title = {Towards Robust Blind Face Restoration with Codebook Lookup TransFormer}, + booktitle = {NeurIPS}, + year = {2022} + } + +### License + +This project is licensed under NTU S-Lab License 1.0. Redistribution and use should follow this license. + +### Acknowledgement + +This project is based on [BasicSR](https://github.com/XPixelGroup/BasicSR). Some codes are brought from [Unleashing Transformers](https://github.com/samb-t/unleashing-transformers), [YOLOv5-face](https://github.com/deepcam-cn/yolov5-face), and [FaceXLib](https://github.com/xinntao/facexlib). We also adopt [Real-ESRGAN](https://github.com/xinntao/Real-ESRGAN) to support background image enhancement. Thanks for their awesome works. + +### Contact +If you have any question, please feel free to reach me out at `shangchenzhou@gmail.com`. diff --git a/repositories/codeformer/basicsr/VERSION b/repositories/codeformer/basicsr/VERSION new file mode 100644 index 000000000..1892b9267 --- /dev/null +++ b/repositories/codeformer/basicsr/VERSION @@ -0,0 +1 @@ +1.3.2 diff --git a/repositories/codeformer/basicsr/__init__.py b/repositories/codeformer/basicsr/__init__.py new file mode 100644 index 000000000..c7ffcccd7 --- /dev/null +++ b/repositories/codeformer/basicsr/__init__.py @@ -0,0 +1,11 @@ +# https://github.com/xinntao/BasicSR +# flake8: noqa +from .archs import * +from .data import * +from .losses import * +from .metrics import * +from .models import * +from .ops import * +from .train import * +from .utils import * +from .version import __gitsha__, __version__ diff --git a/repositories/codeformer/basicsr/archs/__init__.py b/repositories/codeformer/basicsr/archs/__init__.py new file mode 100644 index 000000000..cfb1e4d7b --- /dev/null +++ b/repositories/codeformer/basicsr/archs/__init__.py @@ -0,0 +1,25 @@ +import importlib +from copy import deepcopy +from os import path as osp + +from basicsr.utils import get_root_logger, scandir +from basicsr.utils.registry import ARCH_REGISTRY + +__all__ = ['build_network'] + +# automatically scan and import arch modules for registry +# scan all the files under the 'archs' folder and collect files ending with +# '_arch.py' +arch_folder = osp.dirname(osp.abspath(__file__)) +arch_filenames = [osp.splitext(osp.basename(v))[0] for v in scandir(arch_folder) if v.endswith('_arch.py')] +# import all the arch modules +_arch_modules = [importlib.import_module(f'basicsr.archs.{file_name}') for file_name in arch_filenames] + + +def build_network(opt): + opt = deepcopy(opt) + network_type = opt.pop('type') + net = ARCH_REGISTRY.get(network_type)(**opt) + logger = get_root_logger() + logger.info(f'Network [{net.__class__.__name__}] is created.') + return net diff --git a/repositories/codeformer/basicsr/archs/arcface_arch.py b/repositories/codeformer/basicsr/archs/arcface_arch.py new file mode 100644 index 000000000..fe5afb7bd --- /dev/null +++ b/repositories/codeformer/basicsr/archs/arcface_arch.py @@ -0,0 +1,245 @@ +import torch.nn as nn +from basicsr.utils.registry import ARCH_REGISTRY + + +def conv3x3(inplanes, outplanes, stride=1): + """A simple wrapper for 3x3 convolution with padding. + + Args: + inplanes (int): Channel number of inputs. + outplanes (int): Channel number of outputs. + stride (int): Stride in convolution. Default: 1. + """ + return nn.Conv2d(inplanes, outplanes, kernel_size=3, stride=stride, padding=1, bias=False) + + +class BasicBlock(nn.Module): + """Basic residual block used in the ResNetArcFace architecture. + + Args: + inplanes (int): Channel number of inputs. + planes (int): Channel number of outputs. + stride (int): Stride in convolution. Default: 1. + downsample (nn.Module): The downsample module. Default: None. + """ + expansion = 1 # output channel expansion ratio + + def __init__(self, inplanes, planes, stride=1, downsample=None): + super(BasicBlock, self).__init__() + self.conv1 = conv3x3(inplanes, planes, stride) + self.bn1 = nn.BatchNorm2d(planes) + self.relu = nn.ReLU(inplace=True) + self.conv2 = conv3x3(planes, planes) + self.bn2 = nn.BatchNorm2d(planes) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class IRBlock(nn.Module): + """Improved residual block (IR Block) used in the ResNetArcFace architecture. + + Args: + inplanes (int): Channel number of inputs. + planes (int): Channel number of outputs. + stride (int): Stride in convolution. Default: 1. + downsample (nn.Module): The downsample module. Default: None. + use_se (bool): Whether use the SEBlock (squeeze and excitation block). Default: True. + """ + expansion = 1 # output channel expansion ratio + + def __init__(self, inplanes, planes, stride=1, downsample=None, use_se=True): + super(IRBlock, self).__init__() + self.bn0 = nn.BatchNorm2d(inplanes) + self.conv1 = conv3x3(inplanes, inplanes) + self.bn1 = nn.BatchNorm2d(inplanes) + self.prelu = nn.PReLU() + self.conv2 = conv3x3(inplanes, planes, stride) + self.bn2 = nn.BatchNorm2d(planes) + self.downsample = downsample + self.stride = stride + self.use_se = use_se + if self.use_se: + self.se = SEBlock(planes) + + def forward(self, x): + residual = x + out = self.bn0(x) + out = self.conv1(out) + out = self.bn1(out) + out = self.prelu(out) + + out = self.conv2(out) + out = self.bn2(out) + if self.use_se: + out = self.se(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.prelu(out) + + return out + + +class Bottleneck(nn.Module): + """Bottleneck block used in the ResNetArcFace architecture. + + Args: + inplanes (int): Channel number of inputs. + planes (int): Channel number of outputs. + stride (int): Stride in convolution. Default: 1. + downsample (nn.Module): The downsample module. Default: None. + """ + expansion = 4 # output channel expansion ratio + + def __init__(self, inplanes, planes, stride=1, downsample=None): + super(Bottleneck, self).__init__() + self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) + self.bn1 = nn.BatchNorm2d(planes) + self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False) + self.bn2 = nn.BatchNorm2d(planes) + self.conv3 = nn.Conv2d(planes, planes * self.expansion, kernel_size=1, bias=False) + self.bn3 = nn.BatchNorm2d(planes * self.expansion) + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + residual = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + if self.downsample is not None: + residual = self.downsample(x) + + out += residual + out = self.relu(out) + + return out + + +class SEBlock(nn.Module): + """The squeeze-and-excitation block (SEBlock) used in the IRBlock. + + Args: + channel (int): Channel number of inputs. + reduction (int): Channel reduction ration. Default: 16. + """ + + def __init__(self, channel, reduction=16): + super(SEBlock, self).__init__() + self.avg_pool = nn.AdaptiveAvgPool2d(1) # pool to 1x1 without spatial information + self.fc = nn.Sequential( + nn.Linear(channel, channel // reduction), nn.PReLU(), nn.Linear(channel // reduction, channel), + nn.Sigmoid()) + + def forward(self, x): + b, c, _, _ = x.size() + y = self.avg_pool(x).view(b, c) + y = self.fc(y).view(b, c, 1, 1) + return x * y + + +@ARCH_REGISTRY.register() +class ResNetArcFace(nn.Module): + """ArcFace with ResNet architectures. + + Ref: ArcFace: Additive Angular Margin Loss for Deep Face Recognition. + + Args: + block (str): Block used in the ArcFace architecture. + layers (tuple(int)): Block numbers in each layer. + use_se (bool): Whether use the SEBlock (squeeze and excitation block). Default: True. + """ + + def __init__(self, block, layers, use_se=True): + if block == 'IRBlock': + block = IRBlock + self.inplanes = 64 + self.use_se = use_se + super(ResNetArcFace, self).__init__() + + self.conv1 = nn.Conv2d(1, 64, kernel_size=3, padding=1, bias=False) + self.bn1 = nn.BatchNorm2d(64) + self.prelu = nn.PReLU() + self.maxpool = nn.MaxPool2d(kernel_size=2, stride=2) + self.layer1 = self._make_layer(block, 64, layers[0]) + self.layer2 = self._make_layer(block, 128, layers[1], stride=2) + self.layer3 = self._make_layer(block, 256, layers[2], stride=2) + self.layer4 = self._make_layer(block, 512, layers[3], stride=2) + self.bn4 = nn.BatchNorm2d(512) + self.dropout = nn.Dropout() + self.fc5 = nn.Linear(512 * 8 * 8, 512) + self.bn5 = nn.BatchNorm1d(512) + + # initialization + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.xavier_normal_(m.weight) + elif isinstance(m, nn.BatchNorm2d) or isinstance(m, nn.BatchNorm1d): + nn.init.constant_(m.weight, 1) + nn.init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + nn.init.xavier_normal_(m.weight) + nn.init.constant_(m.bias, 0) + + def _make_layer(self, block, planes, num_blocks, stride=1): + downsample = None + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + nn.Conv2d(self.inplanes, planes * block.expansion, kernel_size=1, stride=stride, bias=False), + nn.BatchNorm2d(planes * block.expansion), + ) + layers = [] + layers.append(block(self.inplanes, planes, stride, downsample, use_se=self.use_se)) + self.inplanes = planes + for _ in range(1, num_blocks): + layers.append(block(self.inplanes, planes, use_se=self.use_se)) + + return nn.Sequential(*layers) + + def forward(self, x): + x = self.conv1(x) + x = self.bn1(x) + x = self.prelu(x) + x = self.maxpool(x) + + x = self.layer1(x) + x = self.layer2(x) + x = self.layer3(x) + x = self.layer4(x) + x = self.bn4(x) + x = self.dropout(x) + x = x.view(x.size(0), -1) + x = self.fc5(x) + x = self.bn5(x) + + return x \ No newline at end of file diff --git a/repositories/codeformer/basicsr/archs/arch_util.py b/repositories/codeformer/basicsr/archs/arch_util.py new file mode 100644 index 000000000..bad45ab34 --- /dev/null +++ b/repositories/codeformer/basicsr/archs/arch_util.py @@ -0,0 +1,318 @@ +import collections.abc +import math +import torch +import torchvision +import warnings +from distutils.version import LooseVersion +from itertools import repeat +from torch import nn as nn +from torch.nn import functional as F +from torch.nn import init as init +from torch.nn.modules.batchnorm import _BatchNorm + +from basicsr.ops.dcn import ModulatedDeformConvPack, modulated_deform_conv +from basicsr.utils import get_root_logger + + +@torch.no_grad() +def default_init_weights(module_list, scale=1, bias_fill=0, **kwargs): + """Initialize network weights. + + Args: + module_list (list[nn.Module] | nn.Module): Modules to be initialized. + scale (float): Scale initialized weights, especially for residual + blocks. Default: 1. + bias_fill (float): The value to fill bias. Default: 0 + kwargs (dict): Other arguments for initialization function. + """ + if not isinstance(module_list, list): + module_list = [module_list] + for module in module_list: + for m in module.modules(): + if isinstance(m, nn.Conv2d): + init.kaiming_normal_(m.weight, **kwargs) + m.weight.data *= scale + if m.bias is not None: + m.bias.data.fill_(bias_fill) + elif isinstance(m, nn.Linear): + init.kaiming_normal_(m.weight, **kwargs) + m.weight.data *= scale + if m.bias is not None: + m.bias.data.fill_(bias_fill) + elif isinstance(m, _BatchNorm): + init.constant_(m.weight, 1) + if m.bias is not None: + m.bias.data.fill_(bias_fill) + + +def make_layer(basic_block, num_basic_block, **kwarg): + """Make layers by stacking the same blocks. + + Args: + basic_block (nn.module): nn.module class for basic block. + num_basic_block (int): number of blocks. + + Returns: + nn.Sequential: Stacked blocks in nn.Sequential. + """ + layers = [] + for _ in range(num_basic_block): + layers.append(basic_block(**kwarg)) + return nn.Sequential(*layers) + + +class ResidualBlockNoBN(nn.Module): + """Residual block without BN. + + It has a style of: + ---Conv-ReLU-Conv-+- + |________________| + + Args: + num_feat (int): Channel number of intermediate features. + Default: 64. + res_scale (float): Residual scale. Default: 1. + pytorch_init (bool): If set to True, use pytorch default init, + otherwise, use default_init_weights. Default: False. + """ + + def __init__(self, num_feat=64, res_scale=1, pytorch_init=False): + super(ResidualBlockNoBN, self).__init__() + self.res_scale = res_scale + self.conv1 = nn.Conv2d(num_feat, num_feat, 3, 1, 1, bias=True) + self.conv2 = nn.Conv2d(num_feat, num_feat, 3, 1, 1, bias=True) + self.relu = nn.ReLU(inplace=True) + + if not pytorch_init: + default_init_weights([self.conv1, self.conv2], 0.1) + + def forward(self, x): + identity = x + out = self.conv2(self.relu(self.conv1(x))) + return identity + out * self.res_scale + + +class Upsample(nn.Sequential): + """Upsample module. + + Args: + scale (int): Scale factor. Supported scales: 2^n and 3. + num_feat (int): Channel number of intermediate features. + """ + + def __init__(self, scale, num_feat): + m = [] + if (scale & (scale - 1)) == 0: # scale = 2^n + for _ in range(int(math.log(scale, 2))): + m.append(nn.Conv2d(num_feat, 4 * num_feat, 3, 1, 1)) + m.append(nn.PixelShuffle(2)) + elif scale == 3: + m.append(nn.Conv2d(num_feat, 9 * num_feat, 3, 1, 1)) + m.append(nn.PixelShuffle(3)) + else: + raise ValueError(f'scale {scale} is not supported. Supported scales: 2^n and 3.') + super(Upsample, self).__init__(*m) + + +def flow_warp(x, flow, interp_mode='bilinear', padding_mode='zeros', align_corners=True): + """Warp an image or feature map with optical flow. + + Args: + x (Tensor): Tensor with size (n, c, h, w). + flow (Tensor): Tensor with size (n, h, w, 2), normal value. + interp_mode (str): 'nearest' or 'bilinear'. Default: 'bilinear'. + padding_mode (str): 'zeros' or 'border' or 'reflection'. + Default: 'zeros'. + align_corners (bool): Before pytorch 1.3, the default value is + align_corners=True. After pytorch 1.3, the default value is + align_corners=False. Here, we use the True as default. + + Returns: + Tensor: Warped image or feature map. + """ + assert x.size()[-2:] == flow.size()[1:3] + _, _, h, w = x.size() + # create mesh grid + grid_y, grid_x = torch.meshgrid(torch.arange(0, h).type_as(x), torch.arange(0, w).type_as(x)) + grid = torch.stack((grid_x, grid_y), 2).float() # W(x), H(y), 2 + grid.requires_grad = False + + vgrid = grid + flow + # scale grid to [-1,1] + vgrid_x = 2.0 * vgrid[:, :, :, 0] / max(w - 1, 1) - 1.0 + vgrid_y = 2.0 * vgrid[:, :, :, 1] / max(h - 1, 1) - 1.0 + vgrid_scaled = torch.stack((vgrid_x, vgrid_y), dim=3) + output = F.grid_sample(x, vgrid_scaled, mode=interp_mode, padding_mode=padding_mode, align_corners=align_corners) + + # TODO, what if align_corners=False + return output + + +def resize_flow(flow, size_type, sizes, interp_mode='bilinear', align_corners=False): + """Resize a flow according to ratio or shape. + + Args: + flow (Tensor): Precomputed flow. shape [N, 2, H, W]. + size_type (str): 'ratio' or 'shape'. + sizes (list[int | float]): the ratio for resizing or the final output + shape. + 1) The order of ratio should be [ratio_h, ratio_w]. For + downsampling, the ratio should be smaller than 1.0 (i.e., ratio + < 1.0). For upsampling, the ratio should be larger than 1.0 (i.e., + ratio > 1.0). + 2) The order of output_size should be [out_h, out_w]. + interp_mode (str): The mode of interpolation for resizing. + Default: 'bilinear'. + align_corners (bool): Whether align corners. Default: False. + + Returns: + Tensor: Resized flow. + """ + _, _, flow_h, flow_w = flow.size() + if size_type == 'ratio': + output_h, output_w = int(flow_h * sizes[0]), int(flow_w * sizes[1]) + elif size_type == 'shape': + output_h, output_w = sizes[0], sizes[1] + else: + raise ValueError(f'Size type should be ratio or shape, but got type {size_type}.') + + input_flow = flow.clone() + ratio_h = output_h / flow_h + ratio_w = output_w / flow_w + input_flow[:, 0, :, :] *= ratio_w + input_flow[:, 1, :, :] *= ratio_h + resized_flow = F.interpolate( + input=input_flow, size=(output_h, output_w), mode=interp_mode, align_corners=align_corners) + return resized_flow + + +# TODO: may write a cpp file +def pixel_unshuffle(x, scale): + """ Pixel unshuffle. + + Args: + x (Tensor): Input feature with shape (b, c, hh, hw). + scale (int): Downsample ratio. + + Returns: + Tensor: the pixel unshuffled feature. + """ + b, c, hh, hw = x.size() + out_channel = c * (scale**2) + assert hh % scale == 0 and hw % scale == 0 + h = hh // scale + w = hw // scale + x_view = x.view(b, c, h, scale, w, scale) + return x_view.permute(0, 1, 3, 5, 2, 4).reshape(b, out_channel, h, w) + + +class DCNv2Pack(ModulatedDeformConvPack): + """Modulated deformable conv for deformable alignment. + + Different from the official DCNv2Pack, which generates offsets and masks + from the preceding features, this DCNv2Pack takes another different + features to generate offsets and masks. + + Ref: + Delving Deep into Deformable Alignment in Video Super-Resolution. + """ + + def forward(self, x, feat): + out = self.conv_offset(feat) + o1, o2, mask = torch.chunk(out, 3, dim=1) + offset = torch.cat((o1, o2), dim=1) + mask = torch.sigmoid(mask) + + offset_absmean = torch.mean(torch.abs(offset)) + if offset_absmean > 50: + logger = get_root_logger() + logger.warning(f'Offset abs mean is {offset_absmean}, larger than 50.') + + if LooseVersion(torchvision.__version__) >= LooseVersion('0.9.0'): + return torchvision.ops.deform_conv2d(x, offset, self.weight, self.bias, self.stride, self.padding, + self.dilation, mask) + else: + return modulated_deform_conv(x, offset, mask, self.weight, self.bias, self.stride, self.padding, + self.dilation, self.groups, self.deformable_groups) + + +def _no_grad_trunc_normal_(tensor, mean, std, a, b): + # From: https://github.com/rwightman/pytorch-image-models/blob/master/timm/models/layers/weight_init.py + # Cut & paste from PyTorch official master until it's in a few official releases - RW + # Method based on https://people.sc.fsu.edu/~jburkardt/presentations/truncated_normal.pdf + def norm_cdf(x): + # Computes standard normal cumulative distribution function + return (1. + math.erf(x / math.sqrt(2.))) / 2. + + if (mean < a - 2 * std) or (mean > b + 2 * std): + warnings.warn( + 'mean is more than 2 std from [a, b] in nn.init.trunc_normal_. ' + 'The distribution of values may be incorrect.', + stacklevel=2) + + with torch.no_grad(): + # Values are generated by using a truncated uniform distribution and + # then using the inverse CDF for the normal distribution. + # Get upper and lower cdf values + low = norm_cdf((a - mean) / std) + up = norm_cdf((b - mean) / std) + + # Uniformly fill tensor with values from [low, up], then translate to + # [2l-1, 2u-1]. + tensor.uniform_(2 * low - 1, 2 * up - 1) + + # Use inverse cdf transform for normal distribution to get truncated + # standard normal + tensor.erfinv_() + + # Transform to proper mean, std + tensor.mul_(std * math.sqrt(2.)) + tensor.add_(mean) + + # Clamp to ensure it's in the proper range + tensor.clamp_(min=a, max=b) + return tensor + + +def trunc_normal_(tensor, mean=0., std=1., a=-2., b=2.): + r"""Fills the input Tensor with values drawn from a truncated + normal distribution. + + From: https://github.com/rwightman/pytorch-image-models/blob/master/timm/models/layers/weight_init.py + + The values are effectively drawn from the + normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` + with values outside :math:`[a, b]` redrawn until they are within + the bounds. The method used for generating the random values works + best when :math:`a \leq \text{mean} \leq b`. + + Args: + tensor: an n-dimensional `torch.Tensor` + mean: the mean of the normal distribution + std: the standard deviation of the normal distribution + a: the minimum cutoff value + b: the maximum cutoff value + + Examples: + >>> w = torch.empty(3, 5) + >>> nn.init.trunc_normal_(w) + """ + return _no_grad_trunc_normal_(tensor, mean, std, a, b) + + +# From PyTorch +def _ntuple(n): + + def parse(x): + if isinstance(x, collections.abc.Iterable): + return x + return tuple(repeat(x, n)) + + return parse + + +to_1tuple = _ntuple(1) +to_2tuple = _ntuple(2) +to_3tuple = _ntuple(3) +to_4tuple = _ntuple(4) +to_ntuple = _ntuple \ No newline at end of file diff --git a/repositories/codeformer/basicsr/archs/codeformer_arch.py b/repositories/codeformer/basicsr/archs/codeformer_arch.py new file mode 100644 index 000000000..4d0d8027c --- /dev/null +++ b/repositories/codeformer/basicsr/archs/codeformer_arch.py @@ -0,0 +1,276 @@ +import math +import numpy as np +import torch +from torch import nn, Tensor +import torch.nn.functional as F +from typing import Optional, List + +from basicsr.archs.vqgan_arch import * +from basicsr.utils import get_root_logger +from basicsr.utils.registry import ARCH_REGISTRY + +def calc_mean_std(feat, eps=1e-5): + """Calculate mean and std for adaptive_instance_normalization. + + Args: + feat (Tensor): 4D tensor. + eps (float): A small value added to the variance to avoid + divide-by-zero. Default: 1e-5. + """ + size = feat.size() + assert len(size) == 4, 'The input feature should be 4D tensor.' + b, c = size[:2] + feat_var = feat.view(b, c, -1).var(dim=2) + eps + feat_std = feat_var.sqrt().view(b, c, 1, 1) + feat_mean = feat.view(b, c, -1).mean(dim=2).view(b, c, 1, 1) + return feat_mean, feat_std + + +def adaptive_instance_normalization(content_feat, style_feat): + """Adaptive instance normalization. + + Adjust the reference features to have the similar color and illuminations + as those in the degradate features. + + Args: + content_feat (Tensor): The reference feature. + style_feat (Tensor): The degradate features. + """ + size = content_feat.size() + style_mean, style_std = calc_mean_std(style_feat) + content_mean, content_std = calc_mean_std(content_feat) + normalized_feat = (content_feat - content_mean.expand(size)) / content_std.expand(size) + return normalized_feat * style_std.expand(size) + style_mean.expand(size) + + +class PositionEmbeddingSine(nn.Module): + """ + This is a more standard version of the position embedding, very similar to the one + used by the Attention is all you need paper, generalized to work on images. + """ + + def __init__(self, num_pos_feats=64, temperature=10000, normalize=False, scale=None): + super().__init__() + self.num_pos_feats = num_pos_feats + self.temperature = temperature + self.normalize = normalize + if scale is not None and normalize is False: + raise ValueError("normalize should be True if scale is passed") + if scale is None: + scale = 2 * math.pi + self.scale = scale + + def forward(self, x, mask=None): + if mask is None: + mask = torch.zeros((x.size(0), x.size(2), x.size(3)), device=x.device, dtype=torch.bool) + not_mask = ~mask + y_embed = not_mask.cumsum(1, dtype=torch.float32) + x_embed = not_mask.cumsum(2, dtype=torch.float32) + if self.normalize: + eps = 1e-6 + y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale + x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale + + dim_t = torch.arange(self.num_pos_feats, dtype=torch.float32, device=x.device) + dim_t = self.temperature ** (2 * (dim_t // 2) / self.num_pos_feats) + + pos_x = x_embed[:, :, :, None] / dim_t + pos_y = y_embed[:, :, :, None] / dim_t + pos_x = torch.stack( + (pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4 + ).flatten(3) + pos_y = torch.stack( + (pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4 + ).flatten(3) + pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2) + return pos + +def _get_activation_fn(activation): + """Return an activation function given a string""" + if activation == "relu": + return F.relu + if activation == "gelu": + return F.gelu + if activation == "glu": + return F.glu + raise RuntimeError(F"activation should be relu/gelu, not {activation}.") + + +class TransformerSALayer(nn.Module): + def __init__(self, embed_dim, nhead=8, dim_mlp=2048, dropout=0.0, activation="gelu"): + super().__init__() + self.self_attn = nn.MultiheadAttention(embed_dim, nhead, dropout=dropout) + # Implementation of Feedforward model - MLP + self.linear1 = nn.Linear(embed_dim, dim_mlp) + self.dropout = nn.Dropout(dropout) + self.linear2 = nn.Linear(dim_mlp, embed_dim) + + self.norm1 = nn.LayerNorm(embed_dim) + self.norm2 = nn.LayerNorm(embed_dim) + self.dropout1 = nn.Dropout(dropout) + self.dropout2 = nn.Dropout(dropout) + + self.activation = _get_activation_fn(activation) + + def with_pos_embed(self, tensor, pos: Optional[Tensor]): + return tensor if pos is None else tensor + pos + + def forward(self, tgt, + tgt_mask: Optional[Tensor] = None, + tgt_key_padding_mask: Optional[Tensor] = None, + query_pos: Optional[Tensor] = None): + + # self attention + tgt2 = self.norm1(tgt) + q = k = self.with_pos_embed(tgt2, query_pos) + tgt2 = self.self_attn(q, k, value=tgt2, attn_mask=tgt_mask, + key_padding_mask=tgt_key_padding_mask)[0] + tgt = tgt + self.dropout1(tgt2) + + # ffn + tgt2 = self.norm2(tgt) + tgt2 = self.linear2(self.dropout(self.activation(self.linear1(tgt2)))) + tgt = tgt + self.dropout2(tgt2) + return tgt + +class Fuse_sft_block(nn.Module): + def __init__(self, in_ch, out_ch): + super().__init__() + self.encode_enc = ResBlock(2*in_ch, out_ch) + + self.scale = nn.Sequential( + nn.Conv2d(in_ch, out_ch, kernel_size=3, padding=1), + nn.LeakyReLU(0.2, True), + nn.Conv2d(out_ch, out_ch, kernel_size=3, padding=1)) + + self.shift = nn.Sequential( + nn.Conv2d(in_ch, out_ch, kernel_size=3, padding=1), + nn.LeakyReLU(0.2, True), + nn.Conv2d(out_ch, out_ch, kernel_size=3, padding=1)) + + def forward(self, enc_feat, dec_feat, w=1): + enc_feat = self.encode_enc(torch.cat([enc_feat, dec_feat], dim=1)) + scale = self.scale(enc_feat) + shift = self.shift(enc_feat) + residual = w * (dec_feat * scale + shift) + out = dec_feat + residual + return out + + +@ARCH_REGISTRY.register() +class CodeFormer(VQAutoEncoder): + def __init__(self, dim_embd=512, n_head=8, n_layers=9, + codebook_size=1024, latent_size=256, + connect_list=['32', '64', '128', '256'], + fix_modules=['quantize','generator']): + super(CodeFormer, self).__init__(512, 64, [1, 2, 2, 4, 4, 8], 'nearest',2, [16], codebook_size) + + if fix_modules is not None: + for module in fix_modules: + for param in getattr(self, module).parameters(): + param.requires_grad = False + + self.connect_list = connect_list + self.n_layers = n_layers + self.dim_embd = dim_embd + self.dim_mlp = dim_embd*2 + + self.position_emb = nn.Parameter(torch.zeros(latent_size, self.dim_embd)) + self.feat_emb = nn.Linear(256, self.dim_embd) + + # transformer + self.ft_layers = nn.Sequential(*[TransformerSALayer(embed_dim=dim_embd, nhead=n_head, dim_mlp=self.dim_mlp, dropout=0.0) + for _ in range(self.n_layers)]) + + # logits_predict head + self.idx_pred_layer = nn.Sequential( + nn.LayerNorm(dim_embd), + nn.Linear(dim_embd, codebook_size, bias=False)) + + self.channels = { + '16': 512, + '32': 256, + '64': 256, + '128': 128, + '256': 128, + '512': 64, + } + + # after second residual block for > 16, before attn layer for ==16 + self.fuse_encoder_block = {'512':2, '256':5, '128':8, '64':11, '32':14, '16':18} + # after first residual block for > 16, before attn layer for ==16 + self.fuse_generator_block = {'16':6, '32': 9, '64':12, '128':15, '256':18, '512':21} + + # fuse_convs_dict + self.fuse_convs_dict = nn.ModuleDict() + for f_size in self.connect_list: + in_ch = self.channels[f_size] + self.fuse_convs_dict[f_size] = Fuse_sft_block(in_ch, in_ch) + + def _init_weights(self, module): + if isinstance(module, (nn.Linear, nn.Embedding)): + module.weight.data.normal_(mean=0.0, std=0.02) + if isinstance(module, nn.Linear) and module.bias is not None: + module.bias.data.zero_() + elif isinstance(module, nn.LayerNorm): + module.bias.data.zero_() + module.weight.data.fill_(1.0) + + def forward(self, x, w=0, detach_16=True, code_only=False, adain=False): + # ################### Encoder ##################### + enc_feat_dict = {} + out_list = [self.fuse_encoder_block[f_size] for f_size in self.connect_list] + for i, block in enumerate(self.encoder.blocks): + x = block(x) + if i in out_list: + enc_feat_dict[str(x.shape[-1])] = x.clone() + + lq_feat = x + # ################# Transformer ################### + # quant_feat, codebook_loss, quant_stats = self.quantize(lq_feat) + pos_emb = self.position_emb.unsqueeze(1).repeat(1,x.shape[0],1) + # BCHW -> BC(HW) -> (HW)BC + feat_emb = self.feat_emb(lq_feat.flatten(2).permute(2,0,1)) + query_emb = feat_emb + # Transformer encoder + for layer in self.ft_layers: + query_emb = layer(query_emb, query_pos=pos_emb) + + # output logits + logits = self.idx_pred_layer(query_emb) # (hw)bn + logits = logits.permute(1,0,2) # (hw)bn -> b(hw)n + + if code_only: # for training stage II + # logits doesn't need softmax before cross_entropy loss + return logits, lq_feat + + # ################# Quantization ################### + # if self.training: + # quant_feat = torch.einsum('btn,nc->btc', [soft_one_hot, self.quantize.embedding.weight]) + # # b(hw)c -> bc(hw) -> bchw + # quant_feat = quant_feat.permute(0,2,1).view(lq_feat.shape) + # ------------ + soft_one_hot = F.softmax(logits, dim=2) + _, top_idx = torch.topk(soft_one_hot, 1, dim=2) + quant_feat = self.quantize.get_codebook_feat(top_idx, shape=[x.shape[0],16,16,256]) + # preserve gradients + # quant_feat = lq_feat + (quant_feat - lq_feat).detach() + + if detach_16: + quant_feat = quant_feat.detach() # for training stage III + if adain: + quant_feat = adaptive_instance_normalization(quant_feat, lq_feat) + + # ################## Generator #################### + x = quant_feat + fuse_list = [self.fuse_generator_block[f_size] for f_size in self.connect_list] + + for i, block in enumerate(self.generator.blocks): + x = block(x) + if i in fuse_list: # fuse after i-th block + f_size = str(x.shape[-1]) + if w>0: + x = self.fuse_convs_dict[f_size](enc_feat_dict[f_size].detach(), x, w) + out = x + # logits doesn't need softmax before cross_entropy loss + return out, logits, lq_feat \ No newline at end of file diff --git a/repositories/codeformer/basicsr/archs/rrdbnet_arch.py b/repositories/codeformer/basicsr/archs/rrdbnet_arch.py new file mode 100644 index 000000000..49a2d6c20 --- /dev/null +++ b/repositories/codeformer/basicsr/archs/rrdbnet_arch.py @@ -0,0 +1,119 @@ +import torch +from torch import nn as nn +from torch.nn import functional as F + +from basicsr.utils.registry import ARCH_REGISTRY +from .arch_util import default_init_weights, make_layer, pixel_unshuffle + + +class ResidualDenseBlock(nn.Module): + """Residual Dense Block. + + Used in RRDB block in ESRGAN. + + Args: + num_feat (int): Channel number of intermediate features. + num_grow_ch (int): Channels for each growth. + """ + + def __init__(self, num_feat=64, num_grow_ch=32): + super(ResidualDenseBlock, self).__init__() + self.conv1 = nn.Conv2d(num_feat, num_grow_ch, 3, 1, 1) + self.conv2 = nn.Conv2d(num_feat + num_grow_ch, num_grow_ch, 3, 1, 1) + self.conv3 = nn.Conv2d(num_feat + 2 * num_grow_ch, num_grow_ch, 3, 1, 1) + self.conv4 = nn.Conv2d(num_feat + 3 * num_grow_ch, num_grow_ch, 3, 1, 1) + self.conv5 = nn.Conv2d(num_feat + 4 * num_grow_ch, num_feat, 3, 1, 1) + + self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True) + + # initialization + default_init_weights([self.conv1, self.conv2, self.conv3, self.conv4, self.conv5], 0.1) + + def forward(self, x): + x1 = self.lrelu(self.conv1(x)) + x2 = self.lrelu(self.conv2(torch.cat((x, x1), 1))) + x3 = self.lrelu(self.conv3(torch.cat((x, x1, x2), 1))) + x4 = self.lrelu(self.conv4(torch.cat((x, x1, x2, x3), 1))) + x5 = self.conv5(torch.cat((x, x1, x2, x3, x4), 1)) + # Emperically, we use 0.2 to scale the residual for better performance + return x5 * 0.2 + x + + +class RRDB(nn.Module): + """Residual in Residual Dense Block. + + Used in RRDB-Net in ESRGAN. + + Args: + num_feat (int): Channel number of intermediate features. + num_grow_ch (int): Channels for each growth. + """ + + def __init__(self, num_feat, num_grow_ch=32): + super(RRDB, self).__init__() + self.rdb1 = ResidualDenseBlock(num_feat, num_grow_ch) + self.rdb2 = ResidualDenseBlock(num_feat, num_grow_ch) + self.rdb3 = ResidualDenseBlock(num_feat, num_grow_ch) + + def forward(self, x): + out = self.rdb1(x) + out = self.rdb2(out) + out = self.rdb3(out) + # Emperically, we use 0.2 to scale the residual for better performance + return out * 0.2 + x + + +@ARCH_REGISTRY.register() +class RRDBNet(nn.Module): + """Networks consisting of Residual in Residual Dense Block, which is used + in ESRGAN. + + ESRGAN: Enhanced Super-Resolution Generative Adversarial Networks. + + We extend ESRGAN for scale x2 and scale x1. + Note: This is one option for scale 1, scale 2 in RRDBNet. + We first employ the pixel-unshuffle (an inverse operation of pixelshuffle to reduce the spatial size + and enlarge the channel size before feeding inputs into the main ESRGAN architecture. + + Args: + num_in_ch (int): Channel number of inputs. + num_out_ch (int): Channel number of outputs. + num_feat (int): Channel number of intermediate features. + Default: 64 + num_block (int): Block number in the trunk network. Defaults: 23 + num_grow_ch (int): Channels for each growth. Default: 32. + """ + + def __init__(self, num_in_ch, num_out_ch, scale=4, num_feat=64, num_block=23, num_grow_ch=32): + super(RRDBNet, self).__init__() + self.scale = scale + if scale == 2: + num_in_ch = num_in_ch * 4 + elif scale == 1: + num_in_ch = num_in_ch * 16 + self.conv_first = nn.Conv2d(num_in_ch, num_feat, 3, 1, 1) + self.body = make_layer(RRDB, num_block, num_feat=num_feat, num_grow_ch=num_grow_ch) + self.conv_body = nn.Conv2d(num_feat, num_feat, 3, 1, 1) + # upsample + self.conv_up1 = nn.Conv2d(num_feat, num_feat, 3, 1, 1) + self.conv_up2 = nn.Conv2d(num_feat, num_feat, 3, 1, 1) + self.conv_hr = nn.Conv2d(num_feat, num_feat, 3, 1, 1) + self.conv_last = nn.Conv2d(num_feat, num_out_ch, 3, 1, 1) + + self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True) + + def forward(self, x): + if self.scale == 2: + feat = pixel_unshuffle(x, scale=2) + elif self.scale == 1: + feat = pixel_unshuffle(x, scale=4) + else: + feat = x + feat = self.conv_first(feat) + body_feat = self.conv_body(self.body(feat)) + feat = feat + body_feat + # upsample + feat = self.lrelu(self.conv_up1(F.interpolate(feat, scale_factor=2, mode='nearest'))) + feat = self.lrelu(self.conv_up2(F.interpolate(feat, scale_factor=2, mode='nearest'))) + out = self.conv_last(self.lrelu(self.conv_hr(feat))) + return out \ No newline at end of file diff --git a/repositories/codeformer/basicsr/archs/vgg_arch.py b/repositories/codeformer/basicsr/archs/vgg_arch.py new file mode 100644 index 000000000..23bb0103c --- /dev/null +++ b/repositories/codeformer/basicsr/archs/vgg_arch.py @@ -0,0 +1,161 @@ +import os +import torch +from collections import OrderedDict +from torch import nn as nn +from torchvision.models import vgg as vgg + +from basicsr.utils.registry import ARCH_REGISTRY + +VGG_PRETRAIN_PATH = 'experiments/pretrained_models/vgg19-dcbb9e9d.pth' +NAMES = { + 'vgg11': [ + 'conv1_1', 'relu1_1', 'pool1', 'conv2_1', 'relu2_1', 'pool2', 'conv3_1', 'relu3_1', 'conv3_2', 'relu3_2', + 'pool3', 'conv4_1', 'relu4_1', 'conv4_2', 'relu4_2', 'pool4', 'conv5_1', 'relu5_1', 'conv5_2', 'relu5_2', + 'pool5' + ], + 'vgg13': [ + 'conv1_1', 'relu1_1', 'conv1_2', 'relu1_2', 'pool1', 'conv2_1', 'relu2_1', 'conv2_2', 'relu2_2', 'pool2', + 'conv3_1', 'relu3_1', 'conv3_2', 'relu3_2', 'pool3', 'conv4_1', 'relu4_1', 'conv4_2', 'relu4_2', 'pool4', + 'conv5_1', 'relu5_1', 'conv5_2', 'relu5_2', 'pool5' + ], + 'vgg16': [ + 'conv1_1', 'relu1_1', 'conv1_2', 'relu1_2', 'pool1', 'conv2_1', 'relu2_1', 'conv2_2', 'relu2_2', 'pool2', + 'conv3_1', 'relu3_1', 'conv3_2', 'relu3_2', 'conv3_3', 'relu3_3', 'pool3', 'conv4_1', 'relu4_1', 'conv4_2', + 'relu4_2', 'conv4_3', 'relu4_3', 'pool4', 'conv5_1', 'relu5_1', 'conv5_2', 'relu5_2', 'conv5_3', 'relu5_3', + 'pool5' + ], + 'vgg19': [ + 'conv1_1', 'relu1_1', 'conv1_2', 'relu1_2', 'pool1', 'conv2_1', 'relu2_1', 'conv2_2', 'relu2_2', 'pool2', + 'conv3_1', 'relu3_1', 'conv3_2', 'relu3_2', 'conv3_3', 'relu3_3', 'conv3_4', 'relu3_4', 'pool3', 'conv4_1', + 'relu4_1', 'conv4_2', 'relu4_2', 'conv4_3', 'relu4_3', 'conv4_4', 'relu4_4', 'pool4', 'conv5_1', 'relu5_1', + 'conv5_2', 'relu5_2', 'conv5_3', 'relu5_3', 'conv5_4', 'relu5_4', 'pool5' + ] +} + + +def insert_bn(names): + """Insert bn layer after each conv. + + Args: + names (list): The list of layer names. + + Returns: + list: The list of layer names with bn layers. + """ + names_bn = [] + for name in names: + names_bn.append(name) + if 'conv' in name: + position = name.replace('conv', '') + names_bn.append('bn' + position) + return names_bn + + +@ARCH_REGISTRY.register() +class VGGFeatureExtractor(nn.Module): + """VGG network for feature extraction. + + In this implementation, we allow users to choose whether use normalization + in the input feature and the type of vgg network. Note that the pretrained + path must fit the vgg type. + + Args: + layer_name_list (list[str]): Forward function returns the corresponding + features according to the layer_name_list. + Example: {'relu1_1', 'relu2_1', 'relu3_1'}. + vgg_type (str): Set the type of vgg network. Default: 'vgg19'. + use_input_norm (bool): If True, normalize the input image. Importantly, + the input feature must in the range [0, 1]. Default: True. + range_norm (bool): If True, norm images with range [-1, 1] to [0, 1]. + Default: False. + requires_grad (bool): If true, the parameters of VGG network will be + optimized. Default: False. + remove_pooling (bool): If true, the max pooling operations in VGG net + will be removed. Default: False. + pooling_stride (int): The stride of max pooling operation. Default: 2. + """ + + def __init__(self, + layer_name_list, + vgg_type='vgg19', + use_input_norm=True, + range_norm=False, + requires_grad=False, + remove_pooling=False, + pooling_stride=2): + super(VGGFeatureExtractor, self).__init__() + + self.layer_name_list = layer_name_list + self.use_input_norm = use_input_norm + self.range_norm = range_norm + + self.names = NAMES[vgg_type.replace('_bn', '')] + if 'bn' in vgg_type: + self.names = insert_bn(self.names) + + # only borrow layers that will be used to avoid unused params + max_idx = 0 + for v in layer_name_list: + idx = self.names.index(v) + if idx > max_idx: + max_idx = idx + + if os.path.exists(VGG_PRETRAIN_PATH): + vgg_net = getattr(vgg, vgg_type)(pretrained=False) + state_dict = torch.load(VGG_PRETRAIN_PATH, map_location=lambda storage, loc: storage) + vgg_net.load_state_dict(state_dict) + else: + vgg_net = getattr(vgg, vgg_type)(pretrained=True) + + features = vgg_net.features[:max_idx + 1] + + modified_net = OrderedDict() + for k, v in zip(self.names, features): + if 'pool' in k: + # if remove_pooling is true, pooling operation will be removed + if remove_pooling: + continue + else: + # in some cases, we may want to change the default stride + modified_net[k] = nn.MaxPool2d(kernel_size=2, stride=pooling_stride) + else: + modified_net[k] = v + + self.vgg_net = nn.Sequential(modified_net) + + if not requires_grad: + self.vgg_net.eval() + for param in self.parameters(): + param.requires_grad = False + else: + self.vgg_net.train() + for param in self.parameters(): + param.requires_grad = True + + if self.use_input_norm: + # the mean is for image with range [0, 1] + self.register_buffer('mean', torch.Tensor([0.485, 0.456, 0.406]).view(1, 3, 1, 1)) + # the std is for image with range [0, 1] + self.register_buffer('std', torch.Tensor([0.229, 0.224, 0.225]).view(1, 3, 1, 1)) + + def forward(self, x): + """Forward function. + + Args: + x (Tensor): Input tensor with shape (n, c, h, w). + + Returns: + Tensor: Forward results. + """ + if self.range_norm: + x = (x + 1) / 2 + if self.use_input_norm: + x = (x - self.mean) / self.std + output = {} + + for key, layer in self.vgg_net._modules.items(): + x = layer(x) + if key in self.layer_name_list: + output[key] = x.clone() + + return output diff --git a/repositories/codeformer/basicsr/archs/vqgan_arch.py b/repositories/codeformer/basicsr/archs/vqgan_arch.py new file mode 100644 index 000000000..5ac692633 --- /dev/null +++ b/repositories/codeformer/basicsr/archs/vqgan_arch.py @@ -0,0 +1,434 @@ +''' +VQGAN code, adapted from the original created by the Unleashing Transformers authors: +https://github.com/samb-t/unleashing-transformers/blob/master/models/vqgan.py + +''' +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F +import copy +from basicsr.utils import get_root_logger +from basicsr.utils.registry import ARCH_REGISTRY + +def normalize(in_channels): + return torch.nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True) + + +@torch.jit.script +def swish(x): + return x*torch.sigmoid(x) + + +# Define VQVAE classes +class VectorQuantizer(nn.Module): + def __init__(self, codebook_size, emb_dim, beta): + super(VectorQuantizer, self).__init__() + self.codebook_size = codebook_size # number of embeddings + self.emb_dim = emb_dim # dimension of embedding + self.beta = beta # commitment cost used in loss term, beta * ||z_e(x)-sg[e]||^2 + self.embedding = nn.Embedding(self.codebook_size, self.emb_dim) + self.embedding.weight.data.uniform_(-1.0 / self.codebook_size, 1.0 / self.codebook_size) + + def forward(self, z): + # reshape z -> (batch, height, width, channel) and flatten + z = z.permute(0, 2, 3, 1).contiguous() + z_flattened = z.view(-1, self.emb_dim) + + # distances from z to embeddings e_j (z - e)^2 = z^2 + e^2 - 2 e * z + d = (z_flattened ** 2).sum(dim=1, keepdim=True) + (self.embedding.weight**2).sum(1) - \ + 2 * torch.matmul(z_flattened, self.embedding.weight.t()) + + mean_distance = torch.mean(d) + # find closest encodings + min_encoding_indices = torch.argmin(d, dim=1).unsqueeze(1) + # min_encoding_scores, min_encoding_indices = torch.topk(d, 1, dim=1, largest=False) + # [0-1], higher score, higher confidence + # min_encoding_scores = torch.exp(-min_encoding_scores/10) + + min_encodings = torch.zeros(min_encoding_indices.shape[0], self.codebook_size).to(z) + min_encodings.scatter_(1, min_encoding_indices, 1) + + # get quantized latent vectors + z_q = torch.matmul(min_encodings, self.embedding.weight).view(z.shape) + # compute loss for embedding + loss = torch.mean((z_q.detach()-z)**2) + self.beta * torch.mean((z_q - z.detach()) ** 2) + # preserve gradients + z_q = z + (z_q - z).detach() + + # perplexity + e_mean = torch.mean(min_encodings, dim=0) + perplexity = torch.exp(-torch.sum(e_mean * torch.log(e_mean + 1e-10))) + # reshape back to match original input shape + z_q = z_q.permute(0, 3, 1, 2).contiguous() + + return z_q, loss, { + "perplexity": perplexity, + "min_encodings": min_encodings, + "min_encoding_indices": min_encoding_indices, + "mean_distance": mean_distance + } + + def get_codebook_feat(self, indices, shape): + # input indices: batch*token_num -> (batch*token_num)*1 + # shape: batch, height, width, channel + indices = indices.view(-1,1) + min_encodings = torch.zeros(indices.shape[0], self.codebook_size).to(indices) + min_encodings.scatter_(1, indices, 1) + # get quantized latent vectors + z_q = torch.matmul(min_encodings.float(), self.embedding.weight) + + if shape is not None: # reshape back to match original input shape + z_q = z_q.view(shape).permute(0, 3, 1, 2).contiguous() + + return z_q + + +class GumbelQuantizer(nn.Module): + def __init__(self, codebook_size, emb_dim, num_hiddens, straight_through=False, kl_weight=5e-4, temp_init=1.0): + super().__init__() + self.codebook_size = codebook_size # number of embeddings + self.emb_dim = emb_dim # dimension of embedding + self.straight_through = straight_through + self.temperature = temp_init + self.kl_weight = kl_weight + self.proj = nn.Conv2d(num_hiddens, codebook_size, 1) # projects last encoder layer to quantized logits + self.embed = nn.Embedding(codebook_size, emb_dim) + + def forward(self, z): + hard = self.straight_through if self.training else True + + logits = self.proj(z) + + soft_one_hot = F.gumbel_softmax(logits, tau=self.temperature, dim=1, hard=hard) + + z_q = torch.einsum("b n h w, n d -> b d h w", soft_one_hot, self.embed.weight) + + # + kl divergence to the prior loss + qy = F.softmax(logits, dim=1) + diff = self.kl_weight * torch.sum(qy * torch.log(qy * self.codebook_size + 1e-10), dim=1).mean() + min_encoding_indices = soft_one_hot.argmax(dim=1) + + return z_q, diff, { + "min_encoding_indices": min_encoding_indices + } + + +class Downsample(nn.Module): + def __init__(self, in_channels): + super().__init__() + self.conv = torch.nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=2, padding=0) + + def forward(self, x): + pad = (0, 1, 0, 1) + x = torch.nn.functional.pad(x, pad, mode="constant", value=0) + x = self.conv(x) + return x + + +class Upsample(nn.Module): + def __init__(self, in_channels): + super().__init__() + self.conv = nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=1, padding=1) + + def forward(self, x): + x = F.interpolate(x, scale_factor=2.0, mode="nearest") + x = self.conv(x) + + return x + + +class ResBlock(nn.Module): + def __init__(self, in_channels, out_channels=None): + super(ResBlock, self).__init__() + self.in_channels = in_channels + self.out_channels = in_channels if out_channels is None else out_channels + self.norm1 = normalize(in_channels) + self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=1, padding=1) + self.norm2 = normalize(out_channels) + self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1) + if self.in_channels != self.out_channels: + self.conv_out = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, padding=0) + + def forward(self, x_in): + x = x_in + x = self.norm1(x) + x = swish(x) + x = self.conv1(x) + x = self.norm2(x) + x = swish(x) + x = self.conv2(x) + if self.in_channels != self.out_channels: + x_in = self.conv_out(x_in) + + return x + x_in + + +class AttnBlock(nn.Module): + def __init__(self, in_channels): + super().__init__() + self.in_channels = in_channels + + self.norm = normalize(in_channels) + self.q = torch.nn.Conv2d( + in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0 + ) + self.k = torch.nn.Conv2d( + in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0 + ) + self.v = torch.nn.Conv2d( + in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0 + ) + self.proj_out = torch.nn.Conv2d( + in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0 + ) + + def forward(self, x): + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + + # compute attention + b, c, h, w = q.shape + q = q.reshape(b, c, h*w) + q = q.permute(0, 2, 1) + k = k.reshape(b, c, h*w) + w_ = torch.bmm(q, k) + w_ = w_ * (int(c)**(-0.5)) + w_ = F.softmax(w_, dim=2) + + # attend to values + v = v.reshape(b, c, h*w) + w_ = w_.permute(0, 2, 1) + h_ = torch.bmm(v, w_) + h_ = h_.reshape(b, c, h, w) + + h_ = self.proj_out(h_) + + return x+h_ + + +class Encoder(nn.Module): + def __init__(self, in_channels, nf, emb_dim, ch_mult, num_res_blocks, resolution, attn_resolutions): + super().__init__() + self.nf = nf + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.attn_resolutions = attn_resolutions + + curr_res = self.resolution + in_ch_mult = (1,)+tuple(ch_mult) + + blocks = [] + # initial convultion + blocks.append(nn.Conv2d(in_channels, nf, kernel_size=3, stride=1, padding=1)) + + # residual and downsampling blocks, with attention on smaller res (16x16) + for i in range(self.num_resolutions): + block_in_ch = nf * in_ch_mult[i] + block_out_ch = nf * ch_mult[i] + for _ in range(self.num_res_blocks): + blocks.append(ResBlock(block_in_ch, block_out_ch)) + block_in_ch = block_out_ch + if curr_res in attn_resolutions: + blocks.append(AttnBlock(block_in_ch)) + + if i != self.num_resolutions - 1: + blocks.append(Downsample(block_in_ch)) + curr_res = curr_res // 2 + + # non-local attention block + blocks.append(ResBlock(block_in_ch, block_in_ch)) + blocks.append(AttnBlock(block_in_ch)) + blocks.append(ResBlock(block_in_ch, block_in_ch)) + + # normalise and convert to latent size + blocks.append(normalize(block_in_ch)) + blocks.append(nn.Conv2d(block_in_ch, emb_dim, kernel_size=3, stride=1, padding=1)) + self.blocks = nn.ModuleList(blocks) + + def forward(self, x): + for block in self.blocks: + x = block(x) + + return x + + +class Generator(nn.Module): + def __init__(self, nf, emb_dim, ch_mult, res_blocks, img_size, attn_resolutions): + super().__init__() + self.nf = nf + self.ch_mult = ch_mult + self.num_resolutions = len(self.ch_mult) + self.num_res_blocks = res_blocks + self.resolution = img_size + self.attn_resolutions = attn_resolutions + self.in_channels = emb_dim + self.out_channels = 3 + block_in_ch = self.nf * self.ch_mult[-1] + curr_res = self.resolution // 2 ** (self.num_resolutions-1) + + blocks = [] + # initial conv + blocks.append(nn.Conv2d(self.in_channels, block_in_ch, kernel_size=3, stride=1, padding=1)) + + # non-local attention block + blocks.append(ResBlock(block_in_ch, block_in_ch)) + blocks.append(AttnBlock(block_in_ch)) + blocks.append(ResBlock(block_in_ch, block_in_ch)) + + for i in reversed(range(self.num_resolutions)): + block_out_ch = self.nf * self.ch_mult[i] + + for _ in range(self.num_res_blocks): + blocks.append(ResBlock(block_in_ch, block_out_ch)) + block_in_ch = block_out_ch + + if curr_res in self.attn_resolutions: + blocks.append(AttnBlock(block_in_ch)) + + if i != 0: + blocks.append(Upsample(block_in_ch)) + curr_res = curr_res * 2 + + blocks.append(normalize(block_in_ch)) + blocks.append(nn.Conv2d(block_in_ch, self.out_channels, kernel_size=3, stride=1, padding=1)) + + self.blocks = nn.ModuleList(blocks) + + + def forward(self, x): + for block in self.blocks: + x = block(x) + + return x + + +@ARCH_REGISTRY.register() +class VQAutoEncoder(nn.Module): + def __init__(self, img_size, nf, ch_mult, quantizer="nearest", res_blocks=2, attn_resolutions=[16], codebook_size=1024, emb_dim=256, + beta=0.25, gumbel_straight_through=False, gumbel_kl_weight=1e-8, model_path=None): + super().__init__() + logger = get_root_logger() + self.in_channels = 3 + self.nf = nf + self.n_blocks = res_blocks + self.codebook_size = codebook_size + self.embed_dim = emb_dim + self.ch_mult = ch_mult + self.resolution = img_size + self.attn_resolutions = attn_resolutions + self.quantizer_type = quantizer + self.encoder = Encoder( + self.in_channels, + self.nf, + self.embed_dim, + self.ch_mult, + self.n_blocks, + self.resolution, + self.attn_resolutions + ) + if self.quantizer_type == "nearest": + self.beta = beta #0.25 + self.quantize = VectorQuantizer(self.codebook_size, self.embed_dim, self.beta) + elif self.quantizer_type == "gumbel": + self.gumbel_num_hiddens = emb_dim + self.straight_through = gumbel_straight_through + self.kl_weight = gumbel_kl_weight + self.quantize = GumbelQuantizer( + self.codebook_size, + self.embed_dim, + self.gumbel_num_hiddens, + self.straight_through, + self.kl_weight + ) + self.generator = Generator( + self.nf, + self.embed_dim, + self.ch_mult, + self.n_blocks, + self.resolution, + self.attn_resolutions + ) + + if model_path is not None: + chkpt = torch.load(model_path, map_location='cpu') + if 'params_ema' in chkpt: + self.load_state_dict(torch.load(model_path, map_location='cpu')['params_ema']) + logger.info(f'vqgan is loaded from: {model_path} [params_ema]') + elif 'params' in chkpt: + self.load_state_dict(torch.load(model_path, map_location='cpu')['params']) + logger.info(f'vqgan is loaded from: {model_path} [params]') + else: + raise ValueError(f'Wrong params!') + + + def forward(self, x): + x = self.encoder(x) + quant, codebook_loss, quant_stats = self.quantize(x) + x = self.generator(quant) + return x, codebook_loss, quant_stats + + + +# patch based discriminator +@ARCH_REGISTRY.register() +class VQGANDiscriminator(nn.Module): + def __init__(self, nc=3, ndf=64, n_layers=4, model_path=None): + super().__init__() + + layers = [nn.Conv2d(nc, ndf, kernel_size=4, stride=2, padding=1), nn.LeakyReLU(0.2, True)] + ndf_mult = 1 + ndf_mult_prev = 1 + for n in range(1, n_layers): # gradually increase the number of filters + ndf_mult_prev = ndf_mult + ndf_mult = min(2 ** n, 8) + layers += [ + nn.Conv2d(ndf * ndf_mult_prev, ndf * ndf_mult, kernel_size=4, stride=2, padding=1, bias=False), + nn.BatchNorm2d(ndf * ndf_mult), + nn.LeakyReLU(0.2, True) + ] + + ndf_mult_prev = ndf_mult + ndf_mult = min(2 ** n_layers, 8) + + layers += [ + nn.Conv2d(ndf * ndf_mult_prev, ndf * ndf_mult, kernel_size=4, stride=1, padding=1, bias=False), + nn.BatchNorm2d(ndf * ndf_mult), + nn.LeakyReLU(0.2, True) + ] + + layers += [ + nn.Conv2d(ndf * ndf_mult, 1, kernel_size=4, stride=1, padding=1)] # output 1 channel prediction map + self.main = nn.Sequential(*layers) + + if model_path is not None: + chkpt = torch.load(model_path, map_location='cpu') + if 'params_d' in chkpt: + self.load_state_dict(torch.load(model_path, map_location='cpu')['params_d']) + elif 'params' in chkpt: + self.load_state_dict(torch.load(model_path, map_location='cpu')['params']) + else: + raise ValueError(f'Wrong params!') + + def forward(self, x): + return self.main(x) \ No newline at end of file diff --git a/repositories/codeformer/basicsr/data/__init__.py b/repositories/codeformer/basicsr/data/__init__.py new file mode 100644 index 000000000..c6adb4bb6 --- /dev/null +++ b/repositories/codeformer/basicsr/data/__init__.py @@ -0,0 +1,100 @@ +import importlib +import numpy as np +import random +import torch +import torch.utils.data +from copy import deepcopy +from functools import partial +from os import path as osp + +from basicsr.data.prefetch_dataloader import PrefetchDataLoader +from basicsr.utils import get_root_logger, scandir +from basicsr.utils.dist_util import get_dist_info +from basicsr.utils.registry import DATASET_REGISTRY + +__all__ = ['build_dataset', 'build_dataloader'] + +# automatically scan and import dataset modules for registry +# scan all the files under the data folder with '_dataset' in file names +data_folder = osp.dirname(osp.abspath(__file__)) +dataset_filenames = [osp.splitext(osp.basename(v))[0] for v in scandir(data_folder) if v.endswith('_dataset.py')] +# import all the dataset modules +_dataset_modules = [importlib.import_module(f'basicsr.data.{file_name}') for file_name in dataset_filenames] + + +def build_dataset(dataset_opt): + """Build dataset from options. + + Args: + dataset_opt (dict): Configuration for dataset. It must constain: + name (str): Dataset name. + type (str): Dataset type. + """ + dataset_opt = deepcopy(dataset_opt) + dataset = DATASET_REGISTRY.get(dataset_opt['type'])(dataset_opt) + logger = get_root_logger() + logger.info(f'Dataset [{dataset.__class__.__name__}] - {dataset_opt["name"]} ' 'is built.') + return dataset + + +def build_dataloader(dataset, dataset_opt, num_gpu=1, dist=False, sampler=None, seed=None): + """Build dataloader. + + Args: + dataset (torch.utils.data.Dataset): Dataset. + dataset_opt (dict): Dataset options. It contains the following keys: + phase (str): 'train' or 'val'. + num_worker_per_gpu (int): Number of workers for each GPU. + batch_size_per_gpu (int): Training batch size for each GPU. + num_gpu (int): Number of GPUs. Used only in the train phase. + Default: 1. + dist (bool): Whether in distributed training. Used only in the train + phase. Default: False. + sampler (torch.utils.data.sampler): Data sampler. Default: None. + seed (int | None): Seed. Default: None + """ + phase = dataset_opt['phase'] + rank, _ = get_dist_info() + if phase == 'train': + if dist: # distributed training + batch_size = dataset_opt['batch_size_per_gpu'] + num_workers = dataset_opt['num_worker_per_gpu'] + else: # non-distributed training + multiplier = 1 if num_gpu == 0 else num_gpu + batch_size = dataset_opt['batch_size_per_gpu'] * multiplier + num_workers = dataset_opt['num_worker_per_gpu'] * multiplier + dataloader_args = dict( + dataset=dataset, + batch_size=batch_size, + shuffle=False, + num_workers=num_workers, + sampler=sampler, + drop_last=True) + if sampler is None: + dataloader_args['shuffle'] = True + dataloader_args['worker_init_fn'] = partial( + worker_init_fn, num_workers=num_workers, rank=rank, seed=seed) if seed is not None else None + elif phase in ['val', 'test']: # validation + dataloader_args = dict(dataset=dataset, batch_size=1, shuffle=False, num_workers=0) + else: + raise ValueError(f'Wrong dataset phase: {phase}. ' "Supported ones are 'train', 'val' and 'test'.") + + dataloader_args['pin_memory'] = dataset_opt.get('pin_memory', False) + + prefetch_mode = dataset_opt.get('prefetch_mode') + if prefetch_mode == 'cpu': # CPUPrefetcher + num_prefetch_queue = dataset_opt.get('num_prefetch_queue', 1) + logger = get_root_logger() + logger.info(f'Use {prefetch_mode} prefetch dataloader: ' f'num_prefetch_queue = {num_prefetch_queue}') + return PrefetchDataLoader(num_prefetch_queue=num_prefetch_queue, **dataloader_args) + else: + # prefetch_mode=None: Normal dataloader + # prefetch_mode='cuda': dataloader for CUDAPrefetcher + return torch.utils.data.DataLoader(**dataloader_args) + + +def worker_init_fn(worker_id, num_workers, rank, seed): + # Set the worker seed to num_workers * rank + worker_id + seed + worker_seed = num_workers * rank + worker_id + seed + np.random.seed(worker_seed) + random.seed(worker_seed) diff --git a/repositories/codeformer/basicsr/data/data_sampler.py b/repositories/codeformer/basicsr/data/data_sampler.py new file mode 100644 index 000000000..575452d9f --- /dev/null +++ b/repositories/codeformer/basicsr/data/data_sampler.py @@ -0,0 +1,48 @@ +import math +import torch +from torch.utils.data.sampler import Sampler + + +class EnlargedSampler(Sampler): + """Sampler that restricts data loading to a subset of the dataset. + + Modified from torch.utils.data.distributed.DistributedSampler + Support enlarging the dataset for iteration-based training, for saving + time when restart the dataloader after each epoch + + Args: + dataset (torch.utils.data.Dataset): Dataset used for sampling. + num_replicas (int | None): Number of processes participating in + the training. It is usually the world_size. + rank (int | None): Rank of the current process within num_replicas. + ratio (int): Enlarging ratio. Default: 1. + """ + + def __init__(self, dataset, num_replicas, rank, ratio=1): + self.dataset = dataset + self.num_replicas = num_replicas + self.rank = rank + self.epoch = 0 + self.num_samples = math.ceil(len(self.dataset) * ratio / self.num_replicas) + self.total_size = self.num_samples * self.num_replicas + + def __iter__(self): + # deterministically shuffle based on epoch + g = torch.Generator() + g.manual_seed(self.epoch) + indices = torch.randperm(self.total_size, generator=g).tolist() + + dataset_size = len(self.dataset) + indices = [v % dataset_size for v in indices] + + # subsample + indices = indices[self.rank:self.total_size:self.num_replicas] + assert len(indices) == self.num_samples + + return iter(indices) + + def __len__(self): + return self.num_samples + + def set_epoch(self, epoch): + self.epoch = epoch diff --git a/repositories/codeformer/basicsr/data/data_util.py b/repositories/codeformer/basicsr/data/data_util.py new file mode 100644 index 000000000..63b1bce8e --- /dev/null +++ b/repositories/codeformer/basicsr/data/data_util.py @@ -0,0 +1,305 @@ +import cv2 +import numpy as np +import torch +from os import path as osp +from torch.nn import functional as F + +from basicsr.data.transforms import mod_crop +from basicsr.utils import img2tensor, scandir + + +def read_img_seq(path, require_mod_crop=False, scale=1): + """Read a sequence of images from a given folder path. + + Args: + path (list[str] | str): List of image paths or image folder path. + require_mod_crop (bool): Require mod crop for each image. + Default: False. + scale (int): Scale factor for mod_crop. Default: 1. + + Returns: + Tensor: size (t, c, h, w), RGB, [0, 1]. + """ + if isinstance(path, list): + img_paths = path + else: + img_paths = sorted(list(scandir(path, full_path=True))) + imgs = [cv2.imread(v).astype(np.float32) / 255. for v in img_paths] + if require_mod_crop: + imgs = [mod_crop(img, scale) for img in imgs] + imgs = img2tensor(imgs, bgr2rgb=True, float32=True) + imgs = torch.stack(imgs, dim=0) + return imgs + + +def generate_frame_indices(crt_idx, max_frame_num, num_frames, padding='reflection'): + """Generate an index list for reading `num_frames` frames from a sequence + of images. + + Args: + crt_idx (int): Current center index. + max_frame_num (int): Max number of the sequence of images (from 1). + num_frames (int): Reading num_frames frames. + padding (str): Padding mode, one of + 'replicate' | 'reflection' | 'reflection_circle' | 'circle' + Examples: current_idx = 0, num_frames = 5 + The generated frame indices under different padding mode: + replicate: [0, 0, 0, 1, 2] + reflection: [2, 1, 0, 1, 2] + reflection_circle: [4, 3, 0, 1, 2] + circle: [3, 4, 0, 1, 2] + + Returns: + list[int]: A list of indices. + """ + assert num_frames % 2 == 1, 'num_frames should be an odd number.' + assert padding in ('replicate', 'reflection', 'reflection_circle', 'circle'), f'Wrong padding mode: {padding}.' + + max_frame_num = max_frame_num - 1 # start from 0 + num_pad = num_frames // 2 + + indices = [] + for i in range(crt_idx - num_pad, crt_idx + num_pad + 1): + if i < 0: + if padding == 'replicate': + pad_idx = 0 + elif padding == 'reflection': + pad_idx = -i + elif padding == 'reflection_circle': + pad_idx = crt_idx + num_pad - i + else: + pad_idx = num_frames + i + elif i > max_frame_num: + if padding == 'replicate': + pad_idx = max_frame_num + elif padding == 'reflection': + pad_idx = max_frame_num * 2 - i + elif padding == 'reflection_circle': + pad_idx = (crt_idx - num_pad) - (i - max_frame_num) + else: + pad_idx = i - num_frames + else: + pad_idx = i + indices.append(pad_idx) + return indices + + +def paired_paths_from_lmdb(folders, keys): + """Generate paired paths from lmdb files. + + Contents of lmdb. Taking the `lq.lmdb` for example, the file structure is: + + lq.lmdb + ├── data.mdb + ├── lock.mdb + ├── meta_info.txt + + The data.mdb and lock.mdb are standard lmdb files and you can refer to + https://lmdb.readthedocs.io/en/release/ for more details. + + The meta_info.txt is a specified txt file to record the meta information + of our datasets. It will be automatically created when preparing + datasets by our provided dataset tools. + Each line in the txt file records + 1)image name (with extension), + 2)image shape, + 3)compression level, separated by a white space. + Example: `baboon.png (120,125,3) 1` + + We use the image name without extension as the lmdb key. + Note that we use the same key for the corresponding lq and gt images. + + Args: + folders (list[str]): A list of folder path. The order of list should + be [input_folder, gt_folder]. + keys (list[str]): A list of keys identifying folders. The order should + be in consistent with folders, e.g., ['lq', 'gt']. + Note that this key is different from lmdb keys. + + Returns: + list[str]: Returned path list. + """ + assert len(folders) == 2, ('The len of folders should be 2 with [input_folder, gt_folder]. ' + f'But got {len(folders)}') + assert len(keys) == 2, ('The len of keys should be 2 with [input_key, gt_key]. ' f'But got {len(keys)}') + input_folder, gt_folder = folders + input_key, gt_key = keys + + if not (input_folder.endswith('.lmdb') and gt_folder.endswith('.lmdb')): + raise ValueError(f'{input_key} folder and {gt_key} folder should both in lmdb ' + f'formats. But received {input_key}: {input_folder}; ' + f'{gt_key}: {gt_folder}') + # ensure that the two meta_info files are the same + with open(osp.join(input_folder, 'meta_info.txt')) as fin: + input_lmdb_keys = [line.split('.')[0] for line in fin] + with open(osp.join(gt_folder, 'meta_info.txt')) as fin: + gt_lmdb_keys = [line.split('.')[0] for line in fin] + if set(input_lmdb_keys) != set(gt_lmdb_keys): + raise ValueError(f'Keys in {input_key}_folder and {gt_key}_folder are different.') + else: + paths = [] + for lmdb_key in sorted(input_lmdb_keys): + paths.append(dict([(f'{input_key}_path', lmdb_key), (f'{gt_key}_path', lmdb_key)])) + return paths + + +def paired_paths_from_meta_info_file(folders, keys, meta_info_file, filename_tmpl): + """Generate paired paths from an meta information file. + + Each line in the meta information file contains the image names and + image shape (usually for gt), separated by a white space. + + Example of an meta information file: + ``` + 0001_s001.png (480,480,3) + 0001_s002.png (480,480,3) + ``` + + Args: + folders (list[str]): A list of folder path. The order of list should + be [input_folder, gt_folder]. + keys (list[str]): A list of keys identifying folders. The order should + be in consistent with folders, e.g., ['lq', 'gt']. + meta_info_file (str): Path to the meta information file. + filename_tmpl (str): Template for each filename. Note that the + template excludes the file extension. Usually the filename_tmpl is + for files in the input folder. + + Returns: + list[str]: Returned path list. + """ + assert len(folders) == 2, ('The len of folders should be 2 with [input_folder, gt_folder]. ' + f'But got {len(folders)}') + assert len(keys) == 2, ('The len of keys should be 2 with [input_key, gt_key]. ' f'But got {len(keys)}') + input_folder, gt_folder = folders + input_key, gt_key = keys + + with open(meta_info_file, 'r') as fin: + gt_names = [line.split(' ')[0] for line in fin] + + paths = [] + for gt_name in gt_names: + basename, ext = osp.splitext(osp.basename(gt_name)) + input_name = f'{filename_tmpl.format(basename)}{ext}' + input_path = osp.join(input_folder, input_name) + gt_path = osp.join(gt_folder, gt_name) + paths.append(dict([(f'{input_key}_path', input_path), (f'{gt_key}_path', gt_path)])) + return paths + + +def paired_paths_from_folder(folders, keys, filename_tmpl): + """Generate paired paths from folders. + + Args: + folders (list[str]): A list of folder path. The order of list should + be [input_folder, gt_folder]. + keys (list[str]): A list of keys identifying folders. The order should + be in consistent with folders, e.g., ['lq', 'gt']. + filename_tmpl (str): Template for each filename. Note that the + template excludes the file extension. Usually the filename_tmpl is + for files in the input folder. + + Returns: + list[str]: Returned path list. + """ + assert len(folders) == 2, ('The len of folders should be 2 with [input_folder, gt_folder]. ' + f'But got {len(folders)}') + assert len(keys) == 2, ('The len of keys should be 2 with [input_key, gt_key]. ' f'But got {len(keys)}') + input_folder, gt_folder = folders + input_key, gt_key = keys + + input_paths = list(scandir(input_folder)) + gt_paths = list(scandir(gt_folder)) + assert len(input_paths) == len(gt_paths), (f'{input_key} and {gt_key} datasets have different number of images: ' + f'{len(input_paths)}, {len(gt_paths)}.') + paths = [] + for gt_path in gt_paths: + basename, ext = osp.splitext(osp.basename(gt_path)) + input_name = f'{filename_tmpl.format(basename)}{ext}' + input_path = osp.join(input_folder, input_name) + assert input_name in input_paths, (f'{input_name} is not in ' f'{input_key}_paths.') + gt_path = osp.join(gt_folder, gt_path) + paths.append(dict([(f'{input_key}_path', input_path), (f'{gt_key}_path', gt_path)])) + return paths + + +def paths_from_folder(folder): + """Generate paths from folder. + + Args: + folder (str): Folder path. + + Returns: + list[str]: Returned path list. + """ + + paths = list(scandir(folder)) + paths = [osp.join(folder, path) for path in paths] + return paths + + +def paths_from_lmdb(folder): + """Generate paths from lmdb. + + Args: + folder (str): Folder path. + + Returns: + list[str]: Returned path list. + """ + if not folder.endswith('.lmdb'): + raise ValueError(f'Folder {folder}folder should in lmdb format.') + with open(osp.join(folder, 'meta_info.txt')) as fin: + paths = [line.split('.')[0] for line in fin] + return paths + + +def generate_gaussian_kernel(kernel_size=13, sigma=1.6): + """Generate Gaussian kernel used in `duf_downsample`. + + Args: + kernel_size (int): Kernel size. Default: 13. + sigma (float): Sigma of the Gaussian kernel. Default: 1.6. + + Returns: + np.array: The Gaussian kernel. + """ + from scipy.ndimage import filters as filters + kernel = np.zeros((kernel_size, kernel_size)) + # set element at the middle to one, a dirac delta + kernel[kernel_size // 2, kernel_size // 2] = 1 + # gaussian-smooth the dirac, resulting in a gaussian filter + return filters.gaussian_filter(kernel, sigma) + + +def duf_downsample(x, kernel_size=13, scale=4): + """Downsamping with Gaussian kernel used in the DUF official code. + + Args: + x (Tensor): Frames to be downsampled, with shape (b, t, c, h, w). + kernel_size (int): Kernel size. Default: 13. + scale (int): Downsampling factor. Supported scale: (2, 3, 4). + Default: 4. + + Returns: + Tensor: DUF downsampled frames. + """ + assert scale in (2, 3, 4), f'Only support scale (2, 3, 4), but got {scale}.' + + squeeze_flag = False + if x.ndim == 4: + squeeze_flag = True + x = x.unsqueeze(0) + b, t, c, h, w = x.size() + x = x.view(-1, 1, h, w) + pad_w, pad_h = kernel_size // 2 + scale * 2, kernel_size // 2 + scale * 2 + x = F.pad(x, (pad_w, pad_w, pad_h, pad_h), 'reflect') + + gaussian_filter = generate_gaussian_kernel(kernel_size, 0.4 * scale) + gaussian_filter = torch.from_numpy(gaussian_filter).type_as(x).unsqueeze(0).unsqueeze(0) + x = F.conv2d(x, gaussian_filter, stride=scale) + x = x[:, :, 2:-2, 2:-2] + x = x.view(b, t, c, x.size(2), x.size(3)) + if squeeze_flag: + x = x.squeeze(0) + return x diff --git a/repositories/codeformer/basicsr/data/prefetch_dataloader.py b/repositories/codeformer/basicsr/data/prefetch_dataloader.py new file mode 100644 index 000000000..508842505 --- /dev/null +++ b/repositories/codeformer/basicsr/data/prefetch_dataloader.py @@ -0,0 +1,125 @@ +import queue as Queue +import threading +import torch +from torch.utils.data import DataLoader + + +class PrefetchGenerator(threading.Thread): + """A general prefetch generator. + + Ref: + https://stackoverflow.com/questions/7323664/python-generator-pre-fetch + + Args: + generator: Python generator. + num_prefetch_queue (int): Number of prefetch queue. + """ + + def __init__(self, generator, num_prefetch_queue): + threading.Thread.__init__(self) + self.queue = Queue.Queue(num_prefetch_queue) + self.generator = generator + self.daemon = True + self.start() + + def run(self): + for item in self.generator: + self.queue.put(item) + self.queue.put(None) + + def __next__(self): + next_item = self.queue.get() + if next_item is None: + raise StopIteration + return next_item + + def __iter__(self): + return self + + +class PrefetchDataLoader(DataLoader): + """Prefetch version of dataloader. + + Ref: + https://github.com/IgorSusmelj/pytorch-styleguide/issues/5# + + TODO: + Need to test on single gpu and ddp (multi-gpu). There is a known issue in + ddp. + + Args: + num_prefetch_queue (int): Number of prefetch queue. + kwargs (dict): Other arguments for dataloader. + """ + + def __init__(self, num_prefetch_queue, **kwargs): + self.num_prefetch_queue = num_prefetch_queue + super(PrefetchDataLoader, self).__init__(**kwargs) + + def __iter__(self): + return PrefetchGenerator(super().__iter__(), self.num_prefetch_queue) + + +class CPUPrefetcher(): + """CPU prefetcher. + + Args: + loader: Dataloader. + """ + + def __init__(self, loader): + self.ori_loader = loader + self.loader = iter(loader) + + def next(self): + try: + return next(self.loader) + except StopIteration: + return None + + def reset(self): + self.loader = iter(self.ori_loader) + + +class CUDAPrefetcher(): + """CUDA prefetcher. + + Ref: + https://github.com/NVIDIA/apex/issues/304# + + It may consums more GPU memory. + + Args: + loader: Dataloader. + opt (dict): Options. + """ + + def __init__(self, loader, opt): + self.ori_loader = loader + self.loader = iter(loader) + self.opt = opt + self.stream = torch.cuda.Stream() + self.device = torch.device('cuda' if opt['num_gpu'] != 0 else 'cpu') + self.preload() + + def preload(self): + try: + self.batch = next(self.loader) # self.batch is a dict + except StopIteration: + self.batch = None + return None + # put tensors to gpu + with torch.cuda.stream(self.stream): + for k, v in self.batch.items(): + if torch.is_tensor(v): + self.batch[k] = self.batch[k].to(device=self.device, non_blocking=True) + + def next(self): + torch.cuda.current_stream().wait_stream(self.stream) + batch = self.batch + self.preload() + return batch + + def reset(self): + self.loader = iter(self.ori_loader) + self.preload() diff --git a/repositories/codeformer/basicsr/data/transforms.py b/repositories/codeformer/basicsr/data/transforms.py new file mode 100644 index 000000000..aead9dc73 --- /dev/null +++ b/repositories/codeformer/basicsr/data/transforms.py @@ -0,0 +1,165 @@ +import cv2 +import random + + +def mod_crop(img, scale): + """Mod crop images, used during testing. + + Args: + img (ndarray): Input image. + scale (int): Scale factor. + + Returns: + ndarray: Result image. + """ + img = img.copy() + if img.ndim in (2, 3): + h, w = img.shape[0], img.shape[1] + h_remainder, w_remainder = h % scale, w % scale + img = img[:h - h_remainder, :w - w_remainder, ...] + else: + raise ValueError(f'Wrong img ndim: {img.ndim}.') + return img + + +def paired_random_crop(img_gts, img_lqs, gt_patch_size, scale, gt_path): + """Paired random crop. + + It crops lists of lq and gt images with corresponding locations. + + Args: + img_gts (list[ndarray] | ndarray): GT images. Note that all images + should have the same shape. If the input is an ndarray, it will + be transformed to a list containing itself. + img_lqs (list[ndarray] | ndarray): LQ images. Note that all images + should have the same shape. If the input is an ndarray, it will + be transformed to a list containing itself. + gt_patch_size (int): GT patch size. + scale (int): Scale factor. + gt_path (str): Path to ground-truth. + + Returns: + list[ndarray] | ndarray: GT images and LQ images. If returned results + only have one element, just return ndarray. + """ + + if not isinstance(img_gts, list): + img_gts = [img_gts] + if not isinstance(img_lqs, list): + img_lqs = [img_lqs] + + h_lq, w_lq, _ = img_lqs[0].shape + h_gt, w_gt, _ = img_gts[0].shape + lq_patch_size = gt_patch_size // scale + + if h_gt != h_lq * scale or w_gt != w_lq * scale: + raise ValueError(f'Scale mismatches. GT ({h_gt}, {w_gt}) is not {scale}x ', + f'multiplication of LQ ({h_lq}, {w_lq}).') + if h_lq < lq_patch_size or w_lq < lq_patch_size: + raise ValueError(f'LQ ({h_lq}, {w_lq}) is smaller than patch size ' + f'({lq_patch_size}, {lq_patch_size}). ' + f'Please remove {gt_path}.') + + # randomly choose top and left coordinates for lq patch + top = random.randint(0, h_lq - lq_patch_size) + left = random.randint(0, w_lq - lq_patch_size) + + # crop lq patch + img_lqs = [v[top:top + lq_patch_size, left:left + lq_patch_size, ...] for v in img_lqs] + + # crop corresponding gt patch + top_gt, left_gt = int(top * scale), int(left * scale) + img_gts = [v[top_gt:top_gt + gt_patch_size, left_gt:left_gt + gt_patch_size, ...] for v in img_gts] + if len(img_gts) == 1: + img_gts = img_gts[0] + if len(img_lqs) == 1: + img_lqs = img_lqs[0] + return img_gts, img_lqs + + +def augment(imgs, hflip=True, rotation=True, flows=None, return_status=False): + """Augment: horizontal flips OR rotate (0, 90, 180, 270 degrees). + + We use vertical flip and transpose for rotation implementation. + All the images in the list use the same augmentation. + + Args: + imgs (list[ndarray] | ndarray): Images to be augmented. If the input + is an ndarray, it will be transformed to a list. + hflip (bool): Horizontal flip. Default: True. + rotation (bool): Ratotation. Default: True. + flows (list[ndarray]: Flows to be augmented. If the input is an + ndarray, it will be transformed to a list. + Dimension is (h, w, 2). Default: None. + return_status (bool): Return the status of flip and rotation. + Default: False. + + Returns: + list[ndarray] | ndarray: Augmented images and flows. If returned + results only have one element, just return ndarray. + + """ + hflip = hflip and random.random() < 0.5 + vflip = rotation and random.random() < 0.5 + rot90 = rotation and random.random() < 0.5 + + def _augment(img): + if hflip: # horizontal + cv2.flip(img, 1, img) + if vflip: # vertical + cv2.flip(img, 0, img) + if rot90: + img = img.transpose(1, 0, 2) + return img + + def _augment_flow(flow): + if hflip: # horizontal + cv2.flip(flow, 1, flow) + flow[:, :, 0] *= -1 + if vflip: # vertical + cv2.flip(flow, 0, flow) + flow[:, :, 1] *= -1 + if rot90: + flow = flow.transpose(1, 0, 2) + flow = flow[:, :, [1, 0]] + return flow + + if not isinstance(imgs, list): + imgs = [imgs] + imgs = [_augment(img) for img in imgs] + if len(imgs) == 1: + imgs = imgs[0] + + if flows is not None: + if not isinstance(flows, list): + flows = [flows] + flows = [_augment_flow(flow) for flow in flows] + if len(flows) == 1: + flows = flows[0] + return imgs, flows + else: + if return_status: + return imgs, (hflip, vflip, rot90) + else: + return imgs + + +def img_rotate(img, angle, center=None, scale=1.0): + """Rotate image. + + Args: + img (ndarray): Image to be rotated. + angle (float): Rotation angle in degrees. Positive values mean + counter-clockwise rotation. + center (tuple[int]): Rotation center. If the center is None, + initialize it as the center of the image. Default: None. + scale (float): Isotropic scale factor. Default: 1.0. + """ + (h, w) = img.shape[:2] + + if center is None: + center = (w // 2, h // 2) + + matrix = cv2.getRotationMatrix2D(center, angle, scale) + rotated_img = cv2.warpAffine(img, matrix, (w, h)) + return rotated_img diff --git a/repositories/codeformer/basicsr/losses/__init__.py b/repositories/codeformer/basicsr/losses/__init__.py new file mode 100644 index 000000000..2b184e74c --- /dev/null +++ b/repositories/codeformer/basicsr/losses/__init__.py @@ -0,0 +1,26 @@ +from copy import deepcopy + +from basicsr.utils import get_root_logger +from basicsr.utils.registry import LOSS_REGISTRY +from .losses import (CharbonnierLoss, GANLoss, L1Loss, MSELoss, PerceptualLoss, WeightedTVLoss, g_path_regularize, + gradient_penalty_loss, r1_penalty) + +__all__ = [ + 'L1Loss', 'MSELoss', 'CharbonnierLoss', 'WeightedTVLoss', 'PerceptualLoss', 'GANLoss', 'gradient_penalty_loss', + 'r1_penalty', 'g_path_regularize' +] + + +def build_loss(opt): + """Build loss from options. + + Args: + opt (dict): Configuration. It must constain: + type (str): Model type. + """ + opt = deepcopy(opt) + loss_type = opt.pop('type') + loss = LOSS_REGISTRY.get(loss_type)(**opt) + logger = get_root_logger() + logger.info(f'Loss [{loss.__class__.__name__}] is created.') + return loss diff --git a/repositories/codeformer/basicsr/losses/loss_util.py b/repositories/codeformer/basicsr/losses/loss_util.py new file mode 100644 index 000000000..744eeb46d --- /dev/null +++ b/repositories/codeformer/basicsr/losses/loss_util.py @@ -0,0 +1,95 @@ +import functools +from torch.nn import functional as F + + +def reduce_loss(loss, reduction): + """Reduce loss as specified. + + Args: + loss (Tensor): Elementwise loss tensor. + reduction (str): Options are 'none', 'mean' and 'sum'. + + Returns: + Tensor: Reduced loss tensor. + """ + reduction_enum = F._Reduction.get_enum(reduction) + # none: 0, elementwise_mean:1, sum: 2 + if reduction_enum == 0: + return loss + elif reduction_enum == 1: + return loss.mean() + else: + return loss.sum() + + +def weight_reduce_loss(loss, weight=None, reduction='mean'): + """Apply element-wise weight and reduce loss. + + Args: + loss (Tensor): Element-wise loss. + weight (Tensor): Element-wise weights. Default: None. + reduction (str): Same as built-in losses of PyTorch. Options are + 'none', 'mean' and 'sum'. Default: 'mean'. + + Returns: + Tensor: Loss values. + """ + # if weight is specified, apply element-wise weight + if weight is not None: + assert weight.dim() == loss.dim() + assert weight.size(1) == 1 or weight.size(1) == loss.size(1) + loss = loss * weight + + # if weight is not specified or reduction is sum, just reduce the loss + if weight is None or reduction == 'sum': + loss = reduce_loss(loss, reduction) + # if reduction is mean, then compute mean over weight region + elif reduction == 'mean': + if weight.size(1) > 1: + weight = weight.sum() + else: + weight = weight.sum() * loss.size(1) + loss = loss.sum() / weight + + return loss + + +def weighted_loss(loss_func): + """Create a weighted version of a given loss function. + + To use this decorator, the loss function must have the signature like + `loss_func(pred, target, **kwargs)`. The function only needs to compute + element-wise loss without any reduction. This decorator will add weight + and reduction arguments to the function. The decorated function will have + the signature like `loss_func(pred, target, weight=None, reduction='mean', + **kwargs)`. + + :Example: + + >>> import torch + >>> @weighted_loss + >>> def l1_loss(pred, target): + >>> return (pred - target).abs() + + >>> pred = torch.Tensor([0, 2, 3]) + >>> target = torch.Tensor([1, 1, 1]) + >>> weight = torch.Tensor([1, 0, 1]) + + >>> l1_loss(pred, target) + tensor(1.3333) + >>> l1_loss(pred, target, weight) + tensor(1.5000) + >>> l1_loss(pred, target, reduction='none') + tensor([1., 1., 2.]) + >>> l1_loss(pred, target, weight, reduction='sum') + tensor(3.) + """ + + @functools.wraps(loss_func) + def wrapper(pred, target, weight=None, reduction='mean', **kwargs): + # get element-wise loss + loss = loss_func(pred, target, **kwargs) + loss = weight_reduce_loss(loss, weight, reduction) + return loss + + return wrapper diff --git a/repositories/codeformer/basicsr/losses/losses.py b/repositories/codeformer/basicsr/losses/losses.py new file mode 100644 index 000000000..1bcf272cf --- /dev/null +++ b/repositories/codeformer/basicsr/losses/losses.py @@ -0,0 +1,455 @@ +import math +import lpips +import torch +from torch import autograd as autograd +from torch import nn as nn +from torch.nn import functional as F + +from basicsr.archs.vgg_arch import VGGFeatureExtractor +from basicsr.utils.registry import LOSS_REGISTRY +from .loss_util import weighted_loss + +_reduction_modes = ['none', 'mean', 'sum'] + + +@weighted_loss +def l1_loss(pred, target): + return F.l1_loss(pred, target, reduction='none') + + +@weighted_loss +def mse_loss(pred, target): + return F.mse_loss(pred, target, reduction='none') + + +@weighted_loss +def charbonnier_loss(pred, target, eps=1e-12): + return torch.sqrt((pred - target)**2 + eps) + + +@LOSS_REGISTRY.register() +class L1Loss(nn.Module): + """L1 (mean absolute error, MAE) loss. + + Args: + loss_weight (float): Loss weight for L1 loss. Default: 1.0. + reduction (str): Specifies the reduction to apply to the output. + Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. + """ + + def __init__(self, loss_weight=1.0, reduction='mean'): + super(L1Loss, self).__init__() + if reduction not in ['none', 'mean', 'sum']: + raise ValueError(f'Unsupported reduction mode: {reduction}. ' f'Supported ones are: {_reduction_modes}') + + self.loss_weight = loss_weight + self.reduction = reduction + + def forward(self, pred, target, weight=None, **kwargs): + """ + Args: + pred (Tensor): of shape (N, C, H, W). Predicted tensor. + target (Tensor): of shape (N, C, H, W). Ground truth tensor. + weight (Tensor, optional): of shape (N, C, H, W). Element-wise + weights. Default: None. + """ + return self.loss_weight * l1_loss(pred, target, weight, reduction=self.reduction) + + +@LOSS_REGISTRY.register() +class MSELoss(nn.Module): + """MSE (L2) loss. + + Args: + loss_weight (float): Loss weight for MSE loss. Default: 1.0. + reduction (str): Specifies the reduction to apply to the output. + Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. + """ + + def __init__(self, loss_weight=1.0, reduction='mean'): + super(MSELoss, self).__init__() + if reduction not in ['none', 'mean', 'sum']: + raise ValueError(f'Unsupported reduction mode: {reduction}. ' f'Supported ones are: {_reduction_modes}') + + self.loss_weight = loss_weight + self.reduction = reduction + + def forward(self, pred, target, weight=None, **kwargs): + """ + Args: + pred (Tensor): of shape (N, C, H, W). Predicted tensor. + target (Tensor): of shape (N, C, H, W). Ground truth tensor. + weight (Tensor, optional): of shape (N, C, H, W). Element-wise + weights. Default: None. + """ + return self.loss_weight * mse_loss(pred, target, weight, reduction=self.reduction) + + +@LOSS_REGISTRY.register() +class CharbonnierLoss(nn.Module): + """Charbonnier loss (one variant of Robust L1Loss, a differentiable + variant of L1Loss). + + Described in "Deep Laplacian Pyramid Networks for Fast and Accurate + Super-Resolution". + + Args: + loss_weight (float): Loss weight for L1 loss. Default: 1.0. + reduction (str): Specifies the reduction to apply to the output. + Supported choices are 'none' | 'mean' | 'sum'. Default: 'mean'. + eps (float): A value used to control the curvature near zero. + Default: 1e-12. + """ + + def __init__(self, loss_weight=1.0, reduction='mean', eps=1e-12): + super(CharbonnierLoss, self).__init__() + if reduction not in ['none', 'mean', 'sum']: + raise ValueError(f'Unsupported reduction mode: {reduction}. ' f'Supported ones are: {_reduction_modes}') + + self.loss_weight = loss_weight + self.reduction = reduction + self.eps = eps + + def forward(self, pred, target, weight=None, **kwargs): + """ + Args: + pred (Tensor): of shape (N, C, H, W). Predicted tensor. + target (Tensor): of shape (N, C, H, W). Ground truth tensor. + weight (Tensor, optional): of shape (N, C, H, W). Element-wise + weights. Default: None. + """ + return self.loss_weight * charbonnier_loss(pred, target, weight, eps=self.eps, reduction=self.reduction) + + +@LOSS_REGISTRY.register() +class WeightedTVLoss(L1Loss): + """Weighted TV loss. + + Args: + loss_weight (float): Loss weight. Default: 1.0. + """ + + def __init__(self, loss_weight=1.0): + super(WeightedTVLoss, self).__init__(loss_weight=loss_weight) + + def forward(self, pred, weight=None): + y_diff = super(WeightedTVLoss, self).forward(pred[:, :, :-1, :], pred[:, :, 1:, :], weight=weight[:, :, :-1, :]) + x_diff = super(WeightedTVLoss, self).forward(pred[:, :, :, :-1], pred[:, :, :, 1:], weight=weight[:, :, :, :-1]) + + loss = x_diff + y_diff + + return loss + + +@LOSS_REGISTRY.register() +class PerceptualLoss(nn.Module): + """Perceptual loss with commonly used style loss. + + Args: + layer_weights (dict): The weight for each layer of vgg feature. + Here is an example: {'conv5_4': 1.}, which means the conv5_4 + feature layer (before relu5_4) will be extracted with weight + 1.0 in calculting losses. + vgg_type (str): The type of vgg network used as feature extractor. + Default: 'vgg19'. + use_input_norm (bool): If True, normalize the input image in vgg. + Default: True. + range_norm (bool): If True, norm images with range [-1, 1] to [0, 1]. + Default: False. + perceptual_weight (float): If `perceptual_weight > 0`, the perceptual + loss will be calculated and the loss will multiplied by the + weight. Default: 1.0. + style_weight (float): If `style_weight > 0`, the style loss will be + calculated and the loss will multiplied by the weight. + Default: 0. + criterion (str): Criterion used for perceptual loss. Default: 'l1'. + """ + + def __init__(self, + layer_weights, + vgg_type='vgg19', + use_input_norm=True, + range_norm=False, + perceptual_weight=1.0, + style_weight=0., + criterion='l1'): + super(PerceptualLoss, self).__init__() + self.perceptual_weight = perceptual_weight + self.style_weight = style_weight + self.layer_weights = layer_weights + self.vgg = VGGFeatureExtractor( + layer_name_list=list(layer_weights.keys()), + vgg_type=vgg_type, + use_input_norm=use_input_norm, + range_norm=range_norm) + + self.criterion_type = criterion + if self.criterion_type == 'l1': + self.criterion = torch.nn.L1Loss() + elif self.criterion_type == 'l2': + self.criterion = torch.nn.L2loss() + elif self.criterion_type == 'mse': + self.criterion = torch.nn.MSELoss(reduction='mean') + elif self.criterion_type == 'fro': + self.criterion = None + else: + raise NotImplementedError(f'{criterion} criterion has not been supported.') + + def forward(self, x, gt): + """Forward function. + + Args: + x (Tensor): Input tensor with shape (n, c, h, w). + gt (Tensor): Ground-truth tensor with shape (n, c, h, w). + + Returns: + Tensor: Forward results. + """ + # extract vgg features + x_features = self.vgg(x) + gt_features = self.vgg(gt.detach()) + + # calculate perceptual loss + if self.perceptual_weight > 0: + percep_loss = 0 + for k in x_features.keys(): + if self.criterion_type == 'fro': + percep_loss += torch.norm(x_features[k] - gt_features[k], p='fro') * self.layer_weights[k] + else: + percep_loss += self.criterion(x_features[k], gt_features[k]) * self.layer_weights[k] + percep_loss *= self.perceptual_weight + else: + percep_loss = None + + # calculate style loss + if self.style_weight > 0: + style_loss = 0 + for k in x_features.keys(): + if self.criterion_type == 'fro': + style_loss += torch.norm( + self._gram_mat(x_features[k]) - self._gram_mat(gt_features[k]), p='fro') * self.layer_weights[k] + else: + style_loss += self.criterion(self._gram_mat(x_features[k]), self._gram_mat( + gt_features[k])) * self.layer_weights[k] + style_loss *= self.style_weight + else: + style_loss = None + + return percep_loss, style_loss + + def _gram_mat(self, x): + """Calculate Gram matrix. + + Args: + x (torch.Tensor): Tensor with shape of (n, c, h, w). + + Returns: + torch.Tensor: Gram matrix. + """ + n, c, h, w = x.size() + features = x.view(n, c, w * h) + features_t = features.transpose(1, 2) + gram = features.bmm(features_t) / (c * h * w) + return gram + + +@LOSS_REGISTRY.register() +class LPIPSLoss(nn.Module): + def __init__(self, + loss_weight=1.0, + use_input_norm=True, + range_norm=False,): + super(LPIPSLoss, self).__init__() + self.perceptual = lpips.LPIPS(net="vgg", spatial=False).eval() + self.loss_weight = loss_weight + self.use_input_norm = use_input_norm + self.range_norm = range_norm + + if self.use_input_norm: + # the mean is for image with range [0, 1] + self.register_buffer('mean', torch.Tensor([0.485, 0.456, 0.406]).view(1, 3, 1, 1)) + # the std is for image with range [0, 1] + self.register_buffer('std', torch.Tensor([0.229, 0.224, 0.225]).view(1, 3, 1, 1)) + + def forward(self, pred, target): + if self.range_norm: + pred = (pred + 1) / 2 + target = (target + 1) / 2 + if self.use_input_norm: + pred = (pred - self.mean) / self.std + target = (target - self.mean) / self.std + lpips_loss = self.perceptual(target.contiguous(), pred.contiguous()) + return self.loss_weight * lpips_loss.mean() + + +@LOSS_REGISTRY.register() +class GANLoss(nn.Module): + """Define GAN loss. + + Args: + gan_type (str): Support 'vanilla', 'lsgan', 'wgan', 'hinge'. + real_label_val (float): The value for real label. Default: 1.0. + fake_label_val (float): The value for fake label. Default: 0.0. + loss_weight (float): Loss weight. Default: 1.0. + Note that loss_weight is only for generators; and it is always 1.0 + for discriminators. + """ + + def __init__(self, gan_type, real_label_val=1.0, fake_label_val=0.0, loss_weight=1.0): + super(GANLoss, self).__init__() + self.gan_type = gan_type + self.loss_weight = loss_weight + self.real_label_val = real_label_val + self.fake_label_val = fake_label_val + + if self.gan_type == 'vanilla': + self.loss = nn.BCEWithLogitsLoss() + elif self.gan_type == 'lsgan': + self.loss = nn.MSELoss() + elif self.gan_type == 'wgan': + self.loss = self._wgan_loss + elif self.gan_type == 'wgan_softplus': + self.loss = self._wgan_softplus_loss + elif self.gan_type == 'hinge': + self.loss = nn.ReLU() + else: + raise NotImplementedError(f'GAN type {self.gan_type} is not implemented.') + + def _wgan_loss(self, input, target): + """wgan loss. + + Args: + input (Tensor): Input tensor. + target (bool): Target label. + + Returns: + Tensor: wgan loss. + """ + return -input.mean() if target else input.mean() + + def _wgan_softplus_loss(self, input, target): + """wgan loss with soft plus. softplus is a smooth approximation to the + ReLU function. + + In StyleGAN2, it is called: + Logistic loss for discriminator; + Non-saturating loss for generator. + + Args: + input (Tensor): Input tensor. + target (bool): Target label. + + Returns: + Tensor: wgan loss. + """ + return F.softplus(-input).mean() if target else F.softplus(input).mean() + + def get_target_label(self, input, target_is_real): + """Get target label. + + Args: + input (Tensor): Input tensor. + target_is_real (bool): Whether the target is real or fake. + + Returns: + (bool | Tensor): Target tensor. Return bool for wgan, otherwise, + return Tensor. + """ + + if self.gan_type in ['wgan', 'wgan_softplus']: + return target_is_real + target_val = (self.real_label_val if target_is_real else self.fake_label_val) + return input.new_ones(input.size()) * target_val + + def forward(self, input, target_is_real, is_disc=False): + """ + Args: + input (Tensor): The input for the loss module, i.e., the network + prediction. + target_is_real (bool): Whether the targe is real or fake. + is_disc (bool): Whether the loss for discriminators or not. + Default: False. + + Returns: + Tensor: GAN loss value. + """ + if self.gan_type == 'hinge': + if is_disc: # for discriminators in hinge-gan + input = -input if target_is_real else input + loss = self.loss(1 + input).mean() + else: # for generators in hinge-gan + loss = -input.mean() + else: # other gan types + target_label = self.get_target_label(input, target_is_real) + loss = self.loss(input, target_label) + + # loss_weight is always 1.0 for discriminators + return loss if is_disc else loss * self.loss_weight + + +def r1_penalty(real_pred, real_img): + """R1 regularization for discriminator. The core idea is to + penalize the gradient on real data alone: when the + generator distribution produces the true data distribution + and the discriminator is equal to 0 on the data manifold, the + gradient penalty ensures that the discriminator cannot create + a non-zero gradient orthogonal to the data manifold without + suffering a loss in the GAN game. + + Ref: + Eq. 9 in Which training methods for GANs do actually converge. + """ + grad_real = autograd.grad(outputs=real_pred.sum(), inputs=real_img, create_graph=True)[0] + grad_penalty = grad_real.pow(2).view(grad_real.shape[0], -1).sum(1).mean() + return grad_penalty + + +def g_path_regularize(fake_img, latents, mean_path_length, decay=0.01): + noise = torch.randn_like(fake_img) / math.sqrt(fake_img.shape[2] * fake_img.shape[3]) + grad = autograd.grad(outputs=(fake_img * noise).sum(), inputs=latents, create_graph=True)[0] + path_lengths = torch.sqrt(grad.pow(2).sum(2).mean(1)) + + path_mean = mean_path_length + decay * (path_lengths.mean() - mean_path_length) + + path_penalty = (path_lengths - path_mean).pow(2).mean() + + return path_penalty, path_lengths.detach().mean(), path_mean.detach() + + +def gradient_penalty_loss(discriminator, real_data, fake_data, weight=None): + """Calculate gradient penalty for wgan-gp. + + Args: + discriminator (nn.Module): Network for the discriminator. + real_data (Tensor): Real input data. + fake_data (Tensor): Fake input data. + weight (Tensor): Weight tensor. Default: None. + + Returns: + Tensor: A tensor for gradient penalty. + """ + + batch_size = real_data.size(0) + alpha = real_data.new_tensor(torch.rand(batch_size, 1, 1, 1)) + + # interpolate between real_data and fake_data + interpolates = alpha * real_data + (1. - alpha) * fake_data + interpolates = autograd.Variable(interpolates, requires_grad=True) + + disc_interpolates = discriminator(interpolates) + gradients = autograd.grad( + outputs=disc_interpolates, + inputs=interpolates, + grad_outputs=torch.ones_like(disc_interpolates), + create_graph=True, + retain_graph=True, + only_inputs=True)[0] + + if weight is not None: + gradients = gradients * weight + + gradients_penalty = ((gradients.norm(2, dim=1) - 1)**2).mean() + if weight is not None: + gradients_penalty /= torch.mean(weight) + + return gradients_penalty diff --git a/repositories/codeformer/basicsr/metrics/__init__.py b/repositories/codeformer/basicsr/metrics/__init__.py new file mode 100644 index 000000000..19d55cc83 --- /dev/null +++ b/repositories/codeformer/basicsr/metrics/__init__.py @@ -0,0 +1,19 @@ +from copy import deepcopy + +from basicsr.utils.registry import METRIC_REGISTRY +from .psnr_ssim import calculate_psnr, calculate_ssim + +__all__ = ['calculate_psnr', 'calculate_ssim'] + + +def calculate_metric(data, opt): + """Calculate metric from data and options. + + Args: + opt (dict): Configuration. It must constain: + type (str): Model type. + """ + opt = deepcopy(opt) + metric_type = opt.pop('type') + metric = METRIC_REGISTRY.get(metric_type)(**data, **opt) + return metric diff --git a/repositories/codeformer/basicsr/metrics/metric_util.py b/repositories/codeformer/basicsr/metrics/metric_util.py new file mode 100644 index 000000000..4d18f0f78 --- /dev/null +++ b/repositories/codeformer/basicsr/metrics/metric_util.py @@ -0,0 +1,45 @@ +import numpy as np + +from basicsr.utils.matlab_functions import bgr2ycbcr + + +def reorder_image(img, input_order='HWC'): + """Reorder images to 'HWC' order. + + If the input_order is (h, w), return (h, w, 1); + If the input_order is (c, h, w), return (h, w, c); + If the input_order is (h, w, c), return as it is. + + Args: + img (ndarray): Input image. + input_order (str): Whether the input order is 'HWC' or 'CHW'. + If the input image shape is (h, w), input_order will not have + effects. Default: 'HWC'. + + Returns: + ndarray: reordered image. + """ + + if input_order not in ['HWC', 'CHW']: + raise ValueError(f'Wrong input_order {input_order}. Supported input_orders are ' "'HWC' and 'CHW'") + if len(img.shape) == 2: + img = img[..., None] + if input_order == 'CHW': + img = img.transpose(1, 2, 0) + return img + + +def to_y_channel(img): + """Change to Y channel of YCbCr. + + Args: + img (ndarray): Images with range [0, 255]. + + Returns: + (ndarray): Images with range [0, 255] (float type) without round. + """ + img = img.astype(np.float32) / 255. + if img.ndim == 3 and img.shape[2] == 3: + img = bgr2ycbcr(img, y_only=True) + img = img[..., None] + return img * 255. diff --git a/repositories/codeformer/basicsr/metrics/psnr_ssim.py b/repositories/codeformer/basicsr/metrics/psnr_ssim.py new file mode 100644 index 000000000..bbd950699 --- /dev/null +++ b/repositories/codeformer/basicsr/metrics/psnr_ssim.py @@ -0,0 +1,128 @@ +import cv2 +import numpy as np + +from basicsr.metrics.metric_util import reorder_image, to_y_channel +from basicsr.utils.registry import METRIC_REGISTRY + + +@METRIC_REGISTRY.register() +def calculate_psnr(img1, img2, crop_border, input_order='HWC', test_y_channel=False): + """Calculate PSNR (Peak Signal-to-Noise Ratio). + + Ref: https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio + + Args: + img1 (ndarray): Images with range [0, 255]. + img2 (ndarray): Images with range [0, 255]. + crop_border (int): Cropped pixels in each edge of an image. These + pixels are not involved in the PSNR calculation. + input_order (str): Whether the input order is 'HWC' or 'CHW'. + Default: 'HWC'. + test_y_channel (bool): Test on Y channel of YCbCr. Default: False. + + Returns: + float: psnr result. + """ + + assert img1.shape == img2.shape, (f'Image shapes are differnet: {img1.shape}, {img2.shape}.') + if input_order not in ['HWC', 'CHW']: + raise ValueError(f'Wrong input_order {input_order}. Supported input_orders are ' '"HWC" and "CHW"') + img1 = reorder_image(img1, input_order=input_order) + img2 = reorder_image(img2, input_order=input_order) + img1 = img1.astype(np.float64) + img2 = img2.astype(np.float64) + + if crop_border != 0: + img1 = img1[crop_border:-crop_border, crop_border:-crop_border, ...] + img2 = img2[crop_border:-crop_border, crop_border:-crop_border, ...] + + if test_y_channel: + img1 = to_y_channel(img1) + img2 = to_y_channel(img2) + + mse = np.mean((img1 - img2)**2) + if mse == 0: + return float('inf') + return 20. * np.log10(255. / np.sqrt(mse)) + + +def _ssim(img1, img2): + """Calculate SSIM (structural similarity) for one channel images. + + It is called by func:`calculate_ssim`. + + Args: + img1 (ndarray): Images with range [0, 255] with order 'HWC'. + img2 (ndarray): Images with range [0, 255] with order 'HWC'. + + Returns: + float: ssim result. + """ + + 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] + 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() + + +@METRIC_REGISTRY.register() +def calculate_ssim(img1, img2, crop_border, input_order='HWC', test_y_channel=False): + """Calculate SSIM (structural similarity). + + Ref: + Image quality assessment: From error visibility to structural similarity + + The results are the same as that of the official released MATLAB code in + https://ece.uwaterloo.ca/~z70wang/research/ssim/. + + For three-channel images, SSIM is calculated for each channel and then + averaged. + + Args: + img1 (ndarray): Images with range [0, 255]. + img2 (ndarray): Images with range [0, 255]. + crop_border (int): Cropped pixels in each edge of an image. These + pixels are not involved in the SSIM calculation. + input_order (str): Whether the input order is 'HWC' or 'CHW'. + Default: 'HWC'. + test_y_channel (bool): Test on Y channel of YCbCr. Default: False. + + Returns: + float: ssim result. + """ + + assert img1.shape == img2.shape, (f'Image shapes are differnet: {img1.shape}, {img2.shape}.') + if input_order not in ['HWC', 'CHW']: + raise ValueError(f'Wrong input_order {input_order}. Supported input_orders are ' '"HWC" and "CHW"') + img1 = reorder_image(img1, input_order=input_order) + img2 = reorder_image(img2, input_order=input_order) + img1 = img1.astype(np.float64) + img2 = img2.astype(np.float64) + + if crop_border != 0: + img1 = img1[crop_border:-crop_border, crop_border:-crop_border, ...] + img2 = img2[crop_border:-crop_border, crop_border:-crop_border, ...] + + if test_y_channel: + img1 = to_y_channel(img1) + img2 = to_y_channel(img2) + + ssims = [] + for i in range(img1.shape[2]): + ssims.append(_ssim(img1[..., i], img2[..., i])) + return np.array(ssims).mean() diff --git a/repositories/codeformer/basicsr/models/__init__.py b/repositories/codeformer/basicsr/models/__init__.py new file mode 100644 index 000000000..00bde45f0 --- /dev/null +++ b/repositories/codeformer/basicsr/models/__init__.py @@ -0,0 +1,30 @@ +import importlib +from copy import deepcopy +from os import path as osp + +from basicsr.utils import get_root_logger, scandir +from basicsr.utils.registry import MODEL_REGISTRY + +__all__ = ['build_model'] + +# automatically scan and import model modules for registry +# scan all the files under the 'models' folder and collect files ending with +# '_model.py' +model_folder = osp.dirname(osp.abspath(__file__)) +model_filenames = [osp.splitext(osp.basename(v))[0] for v in scandir(model_folder) if v.endswith('_model.py')] +# import all the model modules +_model_modules = [importlib.import_module(f'basicsr.models.{file_name}') for file_name in model_filenames] + + +def build_model(opt): + """Build model from options. + + Args: + opt (dict): Configuration. It must constain: + model_type (str): Model type. + """ + opt = deepcopy(opt) + model = MODEL_REGISTRY.get(opt['model_type'])(opt) + logger = get_root_logger() + logger.info(f'Model [{model.__class__.__name__}] is created.') + return model diff --git a/repositories/codeformer/basicsr/ops/__init__.py b/repositories/codeformer/basicsr/ops/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/codeformer/basicsr/ops/dcn/__init__.py b/repositories/codeformer/basicsr/ops/dcn/__init__.py new file mode 100644 index 000000000..32e3592f8 --- /dev/null +++ b/repositories/codeformer/basicsr/ops/dcn/__init__.py @@ -0,0 +1,7 @@ +from .deform_conv import (DeformConv, DeformConvPack, ModulatedDeformConv, ModulatedDeformConvPack, deform_conv, + modulated_deform_conv) + +__all__ = [ + 'DeformConv', 'DeformConvPack', 'ModulatedDeformConv', 'ModulatedDeformConvPack', 'deform_conv', + 'modulated_deform_conv' +] diff --git a/repositories/codeformer/basicsr/ops/dcn/deform_conv.py b/repositories/codeformer/basicsr/ops/dcn/deform_conv.py new file mode 100644 index 000000000..734154f9e --- /dev/null +++ b/repositories/codeformer/basicsr/ops/dcn/deform_conv.py @@ -0,0 +1,377 @@ +import math +import torch +from torch import nn as nn +from torch.autograd import Function +from torch.autograd.function import once_differentiable +from torch.nn import functional as F +from torch.nn.modules.utils import _pair, _single + +try: + from . import deform_conv_ext +except ImportError: + import os + BASICSR_JIT = os.getenv('BASICSR_JIT') + if BASICSR_JIT == 'True': + from torch.utils.cpp_extension import load + module_path = os.path.dirname(__file__) + deform_conv_ext = load( + 'deform_conv', + sources=[ + os.path.join(module_path, 'src', 'deform_conv_ext.cpp'), + os.path.join(module_path, 'src', 'deform_conv_cuda.cpp'), + os.path.join(module_path, 'src', 'deform_conv_cuda_kernel.cu'), + ], + ) + + +class DeformConvFunction(Function): + + @staticmethod + def forward(ctx, + input, + offset, + weight, + stride=1, + padding=0, + dilation=1, + groups=1, + deformable_groups=1, + im2col_step=64): + if input is not None and input.dim() != 4: + raise ValueError(f'Expected 4D tensor as input, got {input.dim()}' 'D tensor instead.') + ctx.stride = _pair(stride) + ctx.padding = _pair(padding) + ctx.dilation = _pair(dilation) + ctx.groups = groups + ctx.deformable_groups = deformable_groups + ctx.im2col_step = im2col_step + + ctx.save_for_backward(input, offset, weight) + + output = input.new_empty(DeformConvFunction._output_size(input, weight, ctx.padding, ctx.dilation, ctx.stride)) + + ctx.bufs_ = [input.new_empty(0), input.new_empty(0)] # columns, ones + + if not input.is_cuda: + raise NotImplementedError + else: + cur_im2col_step = min(ctx.im2col_step, input.shape[0]) + assert (input.shape[0] % cur_im2col_step) == 0, 'im2col step must divide batchsize' + deform_conv_ext.deform_conv_forward(input, weight, + offset, output, ctx.bufs_[0], ctx.bufs_[1], weight.size(3), + weight.size(2), ctx.stride[1], ctx.stride[0], ctx.padding[1], + ctx.padding[0], ctx.dilation[1], ctx.dilation[0], ctx.groups, + ctx.deformable_groups, cur_im2col_step) + return output + + @staticmethod + @once_differentiable + def backward(ctx, grad_output): + input, offset, weight = ctx.saved_tensors + + grad_input = grad_offset = grad_weight = None + + if not grad_output.is_cuda: + raise NotImplementedError + else: + cur_im2col_step = min(ctx.im2col_step, input.shape[0]) + assert (input.shape[0] % cur_im2col_step) == 0, 'im2col step must divide batchsize' + + if ctx.needs_input_grad[0] or ctx.needs_input_grad[1]: + grad_input = torch.zeros_like(input) + grad_offset = torch.zeros_like(offset) + deform_conv_ext.deform_conv_backward_input(input, offset, grad_output, grad_input, + grad_offset, weight, ctx.bufs_[0], weight.size(3), + weight.size(2), ctx.stride[1], ctx.stride[0], ctx.padding[1], + ctx.padding[0], ctx.dilation[1], ctx.dilation[0], ctx.groups, + ctx.deformable_groups, cur_im2col_step) + + if ctx.needs_input_grad[2]: + grad_weight = torch.zeros_like(weight) + deform_conv_ext.deform_conv_backward_parameters(input, offset, grad_output, grad_weight, + ctx.bufs_[0], ctx.bufs_[1], weight.size(3), + weight.size(2), ctx.stride[1], ctx.stride[0], + ctx.padding[1], ctx.padding[0], ctx.dilation[1], + ctx.dilation[0], ctx.groups, ctx.deformable_groups, 1, + cur_im2col_step) + + return (grad_input, grad_offset, grad_weight, None, None, None, None, None) + + @staticmethod + def _output_size(input, weight, padding, dilation, stride): + channels = weight.size(0) + output_size = (input.size(0), channels) + for d in range(input.dim() - 2): + in_size = input.size(d + 2) + pad = padding[d] + kernel = dilation[d] * (weight.size(d + 2) - 1) + 1 + stride_ = stride[d] + output_size += ((in_size + (2 * pad) - kernel) // stride_ + 1, ) + if not all(map(lambda s: s > 0, output_size)): + raise ValueError('convolution input is too small (output would be ' f'{"x".join(map(str, output_size))})') + return output_size + + +class ModulatedDeformConvFunction(Function): + + @staticmethod + def forward(ctx, + input, + offset, + mask, + weight, + bias=None, + stride=1, + padding=0, + dilation=1, + groups=1, + deformable_groups=1): + ctx.stride = stride + ctx.padding = padding + ctx.dilation = dilation + ctx.groups = groups + ctx.deformable_groups = deformable_groups + ctx.with_bias = bias is not None + if not ctx.with_bias: + bias = input.new_empty(1) # fake tensor + if not input.is_cuda: + raise NotImplementedError + if weight.requires_grad or mask.requires_grad or offset.requires_grad \ + or input.requires_grad: + ctx.save_for_backward(input, offset, mask, weight, bias) + output = input.new_empty(ModulatedDeformConvFunction._infer_shape(ctx, input, weight)) + ctx._bufs = [input.new_empty(0), input.new_empty(0)] + deform_conv_ext.modulated_deform_conv_forward(input, weight, bias, ctx._bufs[0], offset, mask, output, + ctx._bufs[1], weight.shape[2], weight.shape[3], ctx.stride, + ctx.stride, ctx.padding, ctx.padding, ctx.dilation, ctx.dilation, + ctx.groups, ctx.deformable_groups, ctx.with_bias) + return output + + @staticmethod + @once_differentiable + def backward(ctx, grad_output): + if not grad_output.is_cuda: + raise NotImplementedError + input, offset, mask, weight, bias = ctx.saved_tensors + grad_input = torch.zeros_like(input) + grad_offset = torch.zeros_like(offset) + grad_mask = torch.zeros_like(mask) + grad_weight = torch.zeros_like(weight) + grad_bias = torch.zeros_like(bias) + deform_conv_ext.modulated_deform_conv_backward(input, weight, bias, ctx._bufs[0], offset, mask, ctx._bufs[1], + grad_input, grad_weight, grad_bias, grad_offset, grad_mask, + grad_output, weight.shape[2], weight.shape[3], ctx.stride, + ctx.stride, ctx.padding, ctx.padding, ctx.dilation, ctx.dilation, + ctx.groups, ctx.deformable_groups, ctx.with_bias) + if not ctx.with_bias: + grad_bias = None + + return (grad_input, grad_offset, grad_mask, grad_weight, grad_bias, None, None, None, None, None) + + @staticmethod + def _infer_shape(ctx, input, weight): + n = input.size(0) + channels_out = weight.size(0) + height, width = input.shape[2:4] + kernel_h, kernel_w = weight.shape[2:4] + height_out = (height + 2 * ctx.padding - (ctx.dilation * (kernel_h - 1) + 1)) // ctx.stride + 1 + width_out = (width + 2 * ctx.padding - (ctx.dilation * (kernel_w - 1) + 1)) // ctx.stride + 1 + return n, channels_out, height_out, width_out + + +deform_conv = DeformConvFunction.apply +modulated_deform_conv = ModulatedDeformConvFunction.apply + + +class DeformConv(nn.Module): + + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + deformable_groups=1, + bias=False): + super(DeformConv, self).__init__() + + assert not bias + assert in_channels % groups == 0, \ + f'in_channels {in_channels} is not divisible by groups {groups}' + assert out_channels % groups == 0, \ + f'out_channels {out_channels} is not divisible ' \ + f'by groups {groups}' + + self.in_channels = in_channels + self.out_channels = out_channels + self.kernel_size = _pair(kernel_size) + self.stride = _pair(stride) + self.padding = _pair(padding) + self.dilation = _pair(dilation) + self.groups = groups + self.deformable_groups = deformable_groups + # enable compatibility with nn.Conv2d + self.transposed = False + self.output_padding = _single(0) + + self.weight = nn.Parameter(torch.Tensor(out_channels, in_channels // self.groups, *self.kernel_size)) + + self.reset_parameters() + + def reset_parameters(self): + n = self.in_channels + for k in self.kernel_size: + n *= k + stdv = 1. / math.sqrt(n) + self.weight.data.uniform_(-stdv, stdv) + + def forward(self, x, offset): + # To fix an assert error in deform_conv_cuda.cpp:128 + # input image is smaller than kernel + input_pad = (x.size(2) < self.kernel_size[0] or x.size(3) < self.kernel_size[1]) + if input_pad: + pad_h = max(self.kernel_size[0] - x.size(2), 0) + pad_w = max(self.kernel_size[1] - x.size(3), 0) + x = F.pad(x, (0, pad_w, 0, pad_h), 'constant', 0).contiguous() + offset = F.pad(offset, (0, pad_w, 0, pad_h), 'constant', 0).contiguous() + out = deform_conv(x, offset, self.weight, self.stride, self.padding, self.dilation, self.groups, + self.deformable_groups) + if input_pad: + out = out[:, :, :out.size(2) - pad_h, :out.size(3) - pad_w].contiguous() + return out + + +class DeformConvPack(DeformConv): + """A Deformable Conv Encapsulation that acts as normal Conv layers. + + Args: + in_channels (int): Same as nn.Conv2d. + out_channels (int): Same as nn.Conv2d. + kernel_size (int or tuple[int]): Same as nn.Conv2d. + stride (int or tuple[int]): Same as nn.Conv2d. + padding (int or tuple[int]): Same as nn.Conv2d. + dilation (int or tuple[int]): Same as nn.Conv2d. + groups (int): Same as nn.Conv2d. + bias (bool or str): If specified as `auto`, it will be decided by the + norm_cfg. Bias will be set as True if norm_cfg is None, otherwise + False. + """ + + _version = 2 + + def __init__(self, *args, **kwargs): + super(DeformConvPack, self).__init__(*args, **kwargs) + + self.conv_offset = nn.Conv2d( + self.in_channels, + self.deformable_groups * 2 * self.kernel_size[0] * self.kernel_size[1], + kernel_size=self.kernel_size, + stride=_pair(self.stride), + padding=_pair(self.padding), + dilation=_pair(self.dilation), + bias=True) + self.init_offset() + + def init_offset(self): + self.conv_offset.weight.data.zero_() + self.conv_offset.bias.data.zero_() + + def forward(self, x): + offset = self.conv_offset(x) + return deform_conv(x, offset, self.weight, self.stride, self.padding, self.dilation, self.groups, + self.deformable_groups) + + +class ModulatedDeformConv(nn.Module): + + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + deformable_groups=1, + bias=True): + super(ModulatedDeformConv, self).__init__() + self.in_channels = in_channels + self.out_channels = out_channels + self.kernel_size = _pair(kernel_size) + self.stride = stride + self.padding = padding + self.dilation = dilation + self.groups = groups + self.deformable_groups = deformable_groups + self.with_bias = bias + # enable compatibility with nn.Conv2d + self.transposed = False + self.output_padding = _single(0) + + self.weight = nn.Parameter(torch.Tensor(out_channels, in_channels // groups, *self.kernel_size)) + if bias: + self.bias = nn.Parameter(torch.Tensor(out_channels)) + else: + self.register_parameter('bias', None) + self.init_weights() + + def init_weights(self): + n = self.in_channels + for k in self.kernel_size: + n *= k + stdv = 1. / math.sqrt(n) + self.weight.data.uniform_(-stdv, stdv) + if self.bias is not None: + self.bias.data.zero_() + + def forward(self, x, offset, mask): + return modulated_deform_conv(x, offset, mask, self.weight, self.bias, self.stride, self.padding, self.dilation, + self.groups, self.deformable_groups) + + +class ModulatedDeformConvPack(ModulatedDeformConv): + """A ModulatedDeformable Conv Encapsulation that acts as normal Conv layers. + + Args: + in_channels (int): Same as nn.Conv2d. + out_channels (int): Same as nn.Conv2d. + kernel_size (int or tuple[int]): Same as nn.Conv2d. + stride (int or tuple[int]): Same as nn.Conv2d. + padding (int or tuple[int]): Same as nn.Conv2d. + dilation (int or tuple[int]): Same as nn.Conv2d. + groups (int): Same as nn.Conv2d. + bias (bool or str): If specified as `auto`, it will be decided by the + norm_cfg. Bias will be set as True if norm_cfg is None, otherwise + False. + """ + + _version = 2 + + def __init__(self, *args, **kwargs): + super(ModulatedDeformConvPack, self).__init__(*args, **kwargs) + + self.conv_offset = nn.Conv2d( + self.in_channels, + self.deformable_groups * 3 * self.kernel_size[0] * self.kernel_size[1], + kernel_size=self.kernel_size, + stride=_pair(self.stride), + padding=_pair(self.padding), + dilation=_pair(self.dilation), + bias=True) + self.init_weights() + + def init_weights(self): + super(ModulatedDeformConvPack, self).init_weights() + if hasattr(self, 'conv_offset'): + self.conv_offset.weight.data.zero_() + self.conv_offset.bias.data.zero_() + + def forward(self, x): + out = self.conv_offset(x) + o1, o2, mask = torch.chunk(out, 3, dim=1) + offset = torch.cat((o1, o2), dim=1) + mask = torch.sigmoid(mask) + return modulated_deform_conv(x, offset, mask, self.weight, self.bias, self.stride, self.padding, self.dilation, + self.groups, self.deformable_groups) diff --git a/repositories/codeformer/basicsr/ops/dcn/src/deform_conv_cuda.cpp b/repositories/codeformer/basicsr/ops/dcn/src/deform_conv_cuda.cpp new file mode 100644 index 000000000..5d9424908 --- /dev/null +++ b/repositories/codeformer/basicsr/ops/dcn/src/deform_conv_cuda.cpp @@ -0,0 +1,685 @@ +// modify from +// https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/blob/mmdetection/mmdet/ops/dcn/src/deform_conv_cuda.c + +#include +#include + +#include +#include + +void deformable_im2col(const at::Tensor data_im, const at::Tensor data_offset, + const int channels, const int height, const int width, + const int ksize_h, const int ksize_w, const int pad_h, + const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int parallel_imgs, const int deformable_group, + at::Tensor data_col); + +void deformable_col2im(const at::Tensor data_col, const at::Tensor data_offset, + const int channels, const int height, const int width, + const int ksize_h, const int ksize_w, const int pad_h, + const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int parallel_imgs, const int deformable_group, + at::Tensor grad_im); + +void deformable_col2im_coord( + const at::Tensor data_col, const at::Tensor data_im, + const at::Tensor data_offset, const int channels, const int height, + const int width, const int ksize_h, const int ksize_w, const int pad_h, + const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, const int parallel_imgs, + const int deformable_group, at::Tensor grad_offset); + +void modulated_deformable_im2col_cuda( + const at::Tensor data_im, const at::Tensor data_offset, + const at::Tensor data_mask, const int batch_size, const int channels, + const int height_im, const int width_im, const int height_col, + const int width_col, const int kernel_h, const int kenerl_w, + const int pad_h, const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, const int deformable_group, + at::Tensor data_col); + +void modulated_deformable_col2im_cuda( + const at::Tensor data_col, const at::Tensor data_offset, + const at::Tensor data_mask, const int batch_size, const int channels, + const int height_im, const int width_im, const int height_col, + const int width_col, const int kernel_h, const int kenerl_w, + const int pad_h, const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, const int deformable_group, + at::Tensor grad_im); + +void modulated_deformable_col2im_coord_cuda( + const at::Tensor data_col, const at::Tensor data_im, + const at::Tensor data_offset, const at::Tensor data_mask, + const int batch_size, const int channels, const int height_im, + const int width_im, const int height_col, const int width_col, + const int kernel_h, const int kenerl_w, const int pad_h, const int pad_w, + const int stride_h, const int stride_w, const int dilation_h, + const int dilation_w, const int deformable_group, at::Tensor grad_offset, + at::Tensor grad_mask); + +void shape_check(at::Tensor input, at::Tensor offset, at::Tensor *gradOutput, + at::Tensor weight, int kH, int kW, int dH, int dW, int padH, + int padW, int dilationH, int dilationW, int group, + int deformable_group) { + TORCH_CHECK(weight.ndimension() == 4, + "4D weight tensor (nOutputPlane,nInputPlane,kH,kW) expected, " + "but got: %s", + weight.ndimension()); + + TORCH_CHECK(weight.is_contiguous(), "weight tensor has to be contiguous"); + + TORCH_CHECK(kW > 0 && kH > 0, + "kernel size should be greater than zero, but got kH: %d kW: %d", kH, + kW); + + TORCH_CHECK((weight.size(2) == kH && weight.size(3) == kW), + "kernel size should be consistent with weight, ", + "but got kH: %d kW: %d weight.size(2): %d, weight.size(3): %d", kH, + kW, weight.size(2), weight.size(3)); + + TORCH_CHECK(dW > 0 && dH > 0, + "stride should be greater than zero, but got dH: %d dW: %d", dH, dW); + + TORCH_CHECK( + dilationW > 0 && dilationH > 0, + "dilation should be greater than 0, but got dilationH: %d dilationW: %d", + dilationH, dilationW); + + int ndim = input.ndimension(); + int dimf = 0; + int dimh = 1; + int dimw = 2; + + if (ndim == 4) { + dimf++; + dimh++; + dimw++; + } + + TORCH_CHECK(ndim == 3 || ndim == 4, "3D or 4D input tensor expected but got: %s", + ndim); + + long nInputPlane = weight.size(1) * group; + long inputHeight = input.size(dimh); + long inputWidth = input.size(dimw); + long nOutputPlane = weight.size(0); + long outputHeight = + (inputHeight + 2 * padH - (dilationH * (kH - 1) + 1)) / dH + 1; + long outputWidth = + (inputWidth + 2 * padW - (dilationW * (kW - 1) + 1)) / dW + 1; + + TORCH_CHECK(nInputPlane % deformable_group == 0, + "input channels must divide deformable group size"); + + if (outputWidth < 1 || outputHeight < 1) + AT_ERROR( + "Given input size: (%ld x %ld x %ld). " + "Calculated output size: (%ld x %ld x %ld). Output size is too small", + nInputPlane, inputHeight, inputWidth, nOutputPlane, outputHeight, + outputWidth); + + TORCH_CHECK(input.size(1) == nInputPlane, + "invalid number of input planes, expected: %d, but got: %d", + nInputPlane, input.size(1)); + + TORCH_CHECK((inputHeight >= kH && inputWidth >= kW), + "input image is smaller than kernel"); + + TORCH_CHECK((offset.size(2) == outputHeight && offset.size(3) == outputWidth), + "invalid spatial size of offset, expected height: %d width: %d, but " + "got height: %d width: %d", + outputHeight, outputWidth, offset.size(2), offset.size(3)); + + TORCH_CHECK((offset.size(1) == deformable_group * 2 * kH * kW), + "invalid number of channels of offset"); + + if (gradOutput != NULL) { + TORCH_CHECK(gradOutput->size(dimf) == nOutputPlane, + "invalid number of gradOutput planes, expected: %d, but got: %d", + nOutputPlane, gradOutput->size(dimf)); + + TORCH_CHECK((gradOutput->size(dimh) == outputHeight && + gradOutput->size(dimw) == outputWidth), + "invalid size of gradOutput, expected height: %d width: %d , but " + "got height: %d width: %d", + outputHeight, outputWidth, gradOutput->size(dimh), + gradOutput->size(dimw)); + } +} + +int deform_conv_forward_cuda(at::Tensor input, at::Tensor weight, + at::Tensor offset, at::Tensor output, + at::Tensor columns, at::Tensor ones, int kW, + int kH, int dW, int dH, int padW, int padH, + int dilationW, int dilationH, int group, + int deformable_group, int im2col_step) { + // todo: resize columns to include im2col: done + // todo: add im2col_step as input + // todo: add new output buffer and transpose it to output (or directly + // transpose output) todo: possibly change data indexing because of + // parallel_imgs + + shape_check(input, offset, NULL, weight, kH, kW, dH, dW, padH, padW, + dilationH, dilationW, group, deformable_group); + at::DeviceGuard guard(input.device()); + + input = input.contiguous(); + offset = offset.contiguous(); + weight = weight.contiguous(); + + int batch = 1; + if (input.ndimension() == 3) { + // Force batch + batch = 0; + input.unsqueeze_(0); + offset.unsqueeze_(0); + } + + // todo: assert batchsize dividable by im2col_step + + long batchSize = input.size(0); + long nInputPlane = input.size(1); + long inputHeight = input.size(2); + long inputWidth = input.size(3); + + long nOutputPlane = weight.size(0); + + long outputWidth = + (inputWidth + 2 * padW - (dilationW * (kW - 1) + 1)) / dW + 1; + long outputHeight = + (inputHeight + 2 * padH - (dilationH * (kH - 1) + 1)) / dH + 1; + + TORCH_CHECK((offset.size(0) == batchSize), "invalid batch size of offset"); + + output = output.view({batchSize / im2col_step, im2col_step, nOutputPlane, + outputHeight, outputWidth}); + columns = at::zeros( + {nInputPlane * kW * kH, im2col_step * outputHeight * outputWidth}, + input.options()); + + if (ones.ndimension() != 2 || + ones.size(0) * ones.size(1) < outputHeight * outputWidth) { + ones = at::ones({outputHeight, outputWidth}, input.options()); + } + + input = input.view({batchSize / im2col_step, im2col_step, nInputPlane, + inputHeight, inputWidth}); + offset = + offset.view({batchSize / im2col_step, im2col_step, + deformable_group * 2 * kH * kW, outputHeight, outputWidth}); + + at::Tensor output_buffer = + at::zeros({batchSize / im2col_step, nOutputPlane, + im2col_step * outputHeight, outputWidth}, + output.options()); + + output_buffer = output_buffer.view( + {output_buffer.size(0), group, output_buffer.size(1) / group, + output_buffer.size(2), output_buffer.size(3)}); + + for (int elt = 0; elt < batchSize / im2col_step; elt++) { + deformable_im2col(input[elt], offset[elt], nInputPlane, inputHeight, + inputWidth, kH, kW, padH, padW, dH, dW, dilationH, + dilationW, im2col_step, deformable_group, columns); + + columns = columns.view({group, columns.size(0) / group, columns.size(1)}); + weight = weight.view({group, weight.size(0) / group, weight.size(1), + weight.size(2), weight.size(3)}); + + for (int g = 0; g < group; g++) { + output_buffer[elt][g] = output_buffer[elt][g] + .flatten(1) + .addmm_(weight[g].flatten(1), columns[g]) + .view_as(output_buffer[elt][g]); + } + } + + output_buffer = output_buffer.view( + {output_buffer.size(0), output_buffer.size(1) * output_buffer.size(2), + output_buffer.size(3), output_buffer.size(4)}); + + output_buffer = output_buffer.view({batchSize / im2col_step, nOutputPlane, + im2col_step, outputHeight, outputWidth}); + output_buffer.transpose_(1, 2); + output.copy_(output_buffer); + output = output.view({batchSize, nOutputPlane, outputHeight, outputWidth}); + + input = input.view({batchSize, nInputPlane, inputHeight, inputWidth}); + offset = offset.view( + {batchSize, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); + + if (batch == 0) { + output = output.view({nOutputPlane, outputHeight, outputWidth}); + input = input.view({nInputPlane, inputHeight, inputWidth}); + offset = offset.view({offset.size(1), offset.size(2), offset.size(3)}); + } + + return 1; +} + +int deform_conv_backward_input_cuda(at::Tensor input, at::Tensor offset, + at::Tensor gradOutput, at::Tensor gradInput, + at::Tensor gradOffset, at::Tensor weight, + at::Tensor columns, int kW, int kH, int dW, + int dH, int padW, int padH, int dilationW, + int dilationH, int group, + int deformable_group, int im2col_step) { + shape_check(input, offset, &gradOutput, weight, kH, kW, dH, dW, padH, padW, + dilationH, dilationW, group, deformable_group); + at::DeviceGuard guard(input.device()); + + input = input.contiguous(); + offset = offset.contiguous(); + gradOutput = gradOutput.contiguous(); + weight = weight.contiguous(); + + int batch = 1; + + if (input.ndimension() == 3) { + // Force batch + batch = 0; + input = input.view({1, input.size(0), input.size(1), input.size(2)}); + offset = offset.view({1, offset.size(0), offset.size(1), offset.size(2)}); + gradOutput = gradOutput.view( + {1, gradOutput.size(0), gradOutput.size(1), gradOutput.size(2)}); + } + + long batchSize = input.size(0); + long nInputPlane = input.size(1); + long inputHeight = input.size(2); + long inputWidth = input.size(3); + + long nOutputPlane = weight.size(0); + + long outputWidth = + (inputWidth + 2 * padW - (dilationW * (kW - 1) + 1)) / dW + 1; + long outputHeight = + (inputHeight + 2 * padH - (dilationH * (kH - 1) + 1)) / dH + 1; + + TORCH_CHECK((offset.size(0) == batchSize), 3, "invalid batch size of offset"); + gradInput = gradInput.view({batchSize, nInputPlane, inputHeight, inputWidth}); + columns = at::zeros( + {nInputPlane * kW * kH, im2col_step * outputHeight * outputWidth}, + input.options()); + + // change order of grad output + gradOutput = gradOutput.view({batchSize / im2col_step, im2col_step, + nOutputPlane, outputHeight, outputWidth}); + gradOutput.transpose_(1, 2); + + gradInput = gradInput.view({batchSize / im2col_step, im2col_step, nInputPlane, + inputHeight, inputWidth}); + input = input.view({batchSize / im2col_step, im2col_step, nInputPlane, + inputHeight, inputWidth}); + gradOffset = gradOffset.view({batchSize / im2col_step, im2col_step, + deformable_group * 2 * kH * kW, outputHeight, + outputWidth}); + offset = + offset.view({batchSize / im2col_step, im2col_step, + deformable_group * 2 * kH * kW, outputHeight, outputWidth}); + + for (int elt = 0; elt < batchSize / im2col_step; elt++) { + // divide into groups + columns = columns.view({group, columns.size(0) / group, columns.size(1)}); + weight = weight.view({group, weight.size(0) / group, weight.size(1), + weight.size(2), weight.size(3)}); + gradOutput = gradOutput.view( + {gradOutput.size(0), group, gradOutput.size(1) / group, + gradOutput.size(2), gradOutput.size(3), gradOutput.size(4)}); + + for (int g = 0; g < group; g++) { + columns[g] = columns[g].addmm_(weight[g].flatten(1).transpose(0, 1), + gradOutput[elt][g].flatten(1), 0.0f, 1.0f); + } + + columns = + columns.view({columns.size(0) * columns.size(1), columns.size(2)}); + gradOutput = gradOutput.view( + {gradOutput.size(0), gradOutput.size(1) * gradOutput.size(2), + gradOutput.size(3), gradOutput.size(4), gradOutput.size(5)}); + + deformable_col2im_coord(columns, input[elt], offset[elt], nInputPlane, + inputHeight, inputWidth, kH, kW, padH, padW, dH, dW, + dilationH, dilationW, im2col_step, deformable_group, + gradOffset[elt]); + + deformable_col2im(columns, offset[elt], nInputPlane, inputHeight, + inputWidth, kH, kW, padH, padW, dH, dW, dilationH, + dilationW, im2col_step, deformable_group, gradInput[elt]); + } + + gradOutput.transpose_(1, 2); + gradOutput = + gradOutput.view({batchSize, nOutputPlane, outputHeight, outputWidth}); + + gradInput = gradInput.view({batchSize, nInputPlane, inputHeight, inputWidth}); + input = input.view({batchSize, nInputPlane, inputHeight, inputWidth}); + gradOffset = gradOffset.view( + {batchSize, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); + offset = offset.view( + {batchSize, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); + + if (batch == 0) { + gradOutput = gradOutput.view({nOutputPlane, outputHeight, outputWidth}); + input = input.view({nInputPlane, inputHeight, inputWidth}); + gradInput = gradInput.view({nInputPlane, inputHeight, inputWidth}); + offset = offset.view({offset.size(1), offset.size(2), offset.size(3)}); + gradOffset = + gradOffset.view({offset.size(1), offset.size(2), offset.size(3)}); + } + + return 1; +} + +int deform_conv_backward_parameters_cuda( + at::Tensor input, at::Tensor offset, at::Tensor gradOutput, + at::Tensor gradWeight, // at::Tensor gradBias, + at::Tensor columns, at::Tensor ones, int kW, int kH, int dW, int dH, + int padW, int padH, int dilationW, int dilationH, int group, + int deformable_group, float scale, int im2col_step) { + // todo: transpose and reshape outGrad + // todo: reshape columns + // todo: add im2col_step as input + + shape_check(input, offset, &gradOutput, gradWeight, kH, kW, dH, dW, padH, + padW, dilationH, dilationW, group, deformable_group); + at::DeviceGuard guard(input.device()); + + input = input.contiguous(); + offset = offset.contiguous(); + gradOutput = gradOutput.contiguous(); + + int batch = 1; + + if (input.ndimension() == 3) { + // Force batch + batch = 0; + input = input.view( + at::IntList({1, input.size(0), input.size(1), input.size(2)})); + gradOutput = gradOutput.view( + {1, gradOutput.size(0), gradOutput.size(1), gradOutput.size(2)}); + } + + long batchSize = input.size(0); + long nInputPlane = input.size(1); + long inputHeight = input.size(2); + long inputWidth = input.size(3); + + long nOutputPlane = gradWeight.size(0); + + long outputWidth = + (inputWidth + 2 * padW - (dilationW * (kW - 1) + 1)) / dW + 1; + long outputHeight = + (inputHeight + 2 * padH - (dilationH * (kH - 1) + 1)) / dH + 1; + + TORCH_CHECK((offset.size(0) == batchSize), "invalid batch size of offset"); + + columns = at::zeros( + {nInputPlane * kW * kH, im2col_step * outputHeight * outputWidth}, + input.options()); + + gradOutput = gradOutput.view({batchSize / im2col_step, im2col_step, + nOutputPlane, outputHeight, outputWidth}); + gradOutput.transpose_(1, 2); + + at::Tensor gradOutputBuffer = at::zeros_like(gradOutput); + gradOutputBuffer = + gradOutputBuffer.view({batchSize / im2col_step, nOutputPlane, im2col_step, + outputHeight, outputWidth}); + gradOutputBuffer.copy_(gradOutput); + gradOutputBuffer = + gradOutputBuffer.view({batchSize / im2col_step, nOutputPlane, + im2col_step * outputHeight, outputWidth}); + + gradOutput.transpose_(1, 2); + gradOutput = + gradOutput.view({batchSize, nOutputPlane, outputHeight, outputWidth}); + + input = input.view({batchSize / im2col_step, im2col_step, nInputPlane, + inputHeight, inputWidth}); + offset = + offset.view({batchSize / im2col_step, im2col_step, + deformable_group * 2 * kH * kW, outputHeight, outputWidth}); + + for (int elt = 0; elt < batchSize / im2col_step; elt++) { + deformable_im2col(input[elt], offset[elt], nInputPlane, inputHeight, + inputWidth, kH, kW, padH, padW, dH, dW, dilationH, + dilationW, im2col_step, deformable_group, columns); + + // divide into group + gradOutputBuffer = gradOutputBuffer.view( + {gradOutputBuffer.size(0), group, gradOutputBuffer.size(1) / group, + gradOutputBuffer.size(2), gradOutputBuffer.size(3)}); + columns = columns.view({group, columns.size(0) / group, columns.size(1)}); + gradWeight = + gradWeight.view({group, gradWeight.size(0) / group, gradWeight.size(1), + gradWeight.size(2), gradWeight.size(3)}); + + for (int g = 0; g < group; g++) { + gradWeight[g] = gradWeight[g] + .flatten(1) + .addmm_(gradOutputBuffer[elt][g].flatten(1), + columns[g].transpose(1, 0), 1.0, scale) + .view_as(gradWeight[g]); + } + gradOutputBuffer = gradOutputBuffer.view( + {gradOutputBuffer.size(0), + gradOutputBuffer.size(1) * gradOutputBuffer.size(2), + gradOutputBuffer.size(3), gradOutputBuffer.size(4)}); + columns = + columns.view({columns.size(0) * columns.size(1), columns.size(2)}); + gradWeight = gradWeight.view({gradWeight.size(0) * gradWeight.size(1), + gradWeight.size(2), gradWeight.size(3), + gradWeight.size(4)}); + } + + input = input.view({batchSize, nInputPlane, inputHeight, inputWidth}); + offset = offset.view( + {batchSize, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); + + if (batch == 0) { + gradOutput = gradOutput.view({nOutputPlane, outputHeight, outputWidth}); + input = input.view({nInputPlane, inputHeight, inputWidth}); + } + + return 1; +} + +void modulated_deform_conv_cuda_forward( + at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, + at::Tensor offset, at::Tensor mask, at::Tensor output, at::Tensor columns, + int kernel_h, int kernel_w, const int stride_h, const int stride_w, + const int pad_h, const int pad_w, const int dilation_h, + const int dilation_w, const int group, const int deformable_group, + const bool with_bias) { + TORCH_CHECK(input.is_contiguous(), "input tensor has to be contiguous"); + TORCH_CHECK(weight.is_contiguous(), "weight tensor has to be contiguous"); + at::DeviceGuard guard(input.device()); + + const int batch = input.size(0); + const int channels = input.size(1); + const int height = input.size(2); + const int width = input.size(3); + + const int channels_out = weight.size(0); + const int channels_kernel = weight.size(1); + const int kernel_h_ = weight.size(2); + const int kernel_w_ = weight.size(3); + + if (kernel_h_ != kernel_h || kernel_w_ != kernel_w) + AT_ERROR("Input shape and kernel shape wont match: (%d x %d vs %d x %d).", + kernel_h_, kernel_w, kernel_h_, kernel_w_); + if (channels != channels_kernel * group) + AT_ERROR("Input shape and kernel channels wont match: (%d vs %d).", + channels, channels_kernel * group); + + const int height_out = + (height + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1; + const int width_out = + (width + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1; + + if (ones.ndimension() != 2 || + ones.size(0) * ones.size(1) < height_out * width_out) { + // Resize plane and fill with ones... + ones = at::ones({height_out, width_out}, input.options()); + } + + // resize output + output = output.view({batch, channels_out, height_out, width_out}).zero_(); + // resize temporary columns + columns = + at::zeros({channels * kernel_h * kernel_w, 1 * height_out * width_out}, + input.options()); + + output = output.view({output.size(0), group, output.size(1) / group, + output.size(2), output.size(3)}); + + for (int b = 0; b < batch; b++) { + modulated_deformable_im2col_cuda( + input[b], offset[b], mask[b], 1, channels, height, width, height_out, + width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, + dilation_h, dilation_w, deformable_group, columns); + + // divide into group + weight = weight.view({group, weight.size(0) / group, weight.size(1), + weight.size(2), weight.size(3)}); + columns = columns.view({group, columns.size(0) / group, columns.size(1)}); + + for (int g = 0; g < group; g++) { + output[b][g] = output[b][g] + .flatten(1) + .addmm_(weight[g].flatten(1), columns[g]) + .view_as(output[b][g]); + } + + weight = weight.view({weight.size(0) * weight.size(1), weight.size(2), + weight.size(3), weight.size(4)}); + columns = + columns.view({columns.size(0) * columns.size(1), columns.size(2)}); + } + + output = output.view({output.size(0), output.size(1) * output.size(2), + output.size(3), output.size(4)}); + + if (with_bias) { + output += bias.view({1, bias.size(0), 1, 1}); + } +} + +void modulated_deform_conv_cuda_backward( + at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, + at::Tensor offset, at::Tensor mask, at::Tensor columns, + at::Tensor grad_input, at::Tensor grad_weight, at::Tensor grad_bias, + at::Tensor grad_offset, at::Tensor grad_mask, at::Tensor grad_output, + int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h, + int pad_w, int dilation_h, int dilation_w, int group, int deformable_group, + const bool with_bias) { + TORCH_CHECK(input.is_contiguous(), "input tensor has to be contiguous"); + TORCH_CHECK(weight.is_contiguous(), "weight tensor has to be contiguous"); + at::DeviceGuard guard(input.device()); + + const int batch = input.size(0); + const int channels = input.size(1); + const int height = input.size(2); + const int width = input.size(3); + + const int channels_kernel = weight.size(1); + const int kernel_h_ = weight.size(2); + const int kernel_w_ = weight.size(3); + if (kernel_h_ != kernel_h || kernel_w_ != kernel_w) + AT_ERROR("Input shape and kernel shape wont match: (%d x %d vs %d x %d).", + kernel_h_, kernel_w, kernel_h_, kernel_w_); + if (channels != channels_kernel * group) + AT_ERROR("Input shape and kernel channels wont match: (%d vs %d).", + channels, channels_kernel * group); + + const int height_out = + (height + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1; + const int width_out = + (width + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1; + + if (ones.ndimension() != 2 || + ones.size(0) * ones.size(1) < height_out * width_out) { + // Resize plane and fill with ones... + ones = at::ones({height_out, width_out}, input.options()); + } + + grad_input = grad_input.view({batch, channels, height, width}); + columns = at::zeros({channels * kernel_h * kernel_w, height_out * width_out}, + input.options()); + + grad_output = + grad_output.view({grad_output.size(0), group, grad_output.size(1) / group, + grad_output.size(2), grad_output.size(3)}); + + for (int b = 0; b < batch; b++) { + // divide int group + columns = columns.view({group, columns.size(0) / group, columns.size(1)}); + weight = weight.view({group, weight.size(0) / group, weight.size(1), + weight.size(2), weight.size(3)}); + + for (int g = 0; g < group; g++) { + columns[g].addmm_(weight[g].flatten(1).transpose(0, 1), + grad_output[b][g].flatten(1), 0.0f, 1.0f); + } + + columns = + columns.view({columns.size(0) * columns.size(1), columns.size(2)}); + weight = weight.view({weight.size(0) * weight.size(1), weight.size(2), + weight.size(3), weight.size(4)}); + + // gradient w.r.t. input coordinate data + modulated_deformable_col2im_coord_cuda( + columns, input[b], offset[b], mask[b], 1, channels, height, width, + height_out, width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, + stride_w, dilation_h, dilation_w, deformable_group, grad_offset[b], + grad_mask[b]); + // gradient w.r.t. input data + modulated_deformable_col2im_cuda( + columns, offset[b], mask[b], 1, channels, height, width, height_out, + width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, + dilation_h, dilation_w, deformable_group, grad_input[b]); + + // gradient w.r.t. weight, dWeight should accumulate across the batch and + // group + modulated_deformable_im2col_cuda( + input[b], offset[b], mask[b], 1, channels, height, width, height_out, + width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, + dilation_h, dilation_w, deformable_group, columns); + + columns = columns.view({group, columns.size(0) / group, columns.size(1)}); + grad_weight = grad_weight.view({group, grad_weight.size(0) / group, + grad_weight.size(1), grad_weight.size(2), + grad_weight.size(3)}); + if (with_bias) + grad_bias = grad_bias.view({group, grad_bias.size(0) / group}); + + for (int g = 0; g < group; g++) { + grad_weight[g] = + grad_weight[g] + .flatten(1) + .addmm_(grad_output[b][g].flatten(1), columns[g].transpose(0, 1)) + .view_as(grad_weight[g]); + if (with_bias) { + grad_bias[g] = + grad_bias[g] + .view({-1, 1}) + .addmm_(grad_output[b][g].flatten(1), ones.view({-1, 1})) + .view(-1); + } + } + + columns = + columns.view({columns.size(0) * columns.size(1), columns.size(2)}); + grad_weight = grad_weight.view({grad_weight.size(0) * grad_weight.size(1), + grad_weight.size(2), grad_weight.size(3), + grad_weight.size(4)}); + if (with_bias) + grad_bias = grad_bias.view({grad_bias.size(0) * grad_bias.size(1)}); + } + grad_output = grad_output.view({grad_output.size(0) * grad_output.size(1), + grad_output.size(2), grad_output.size(3), + grad_output.size(4)}); +} diff --git a/repositories/codeformer/basicsr/ops/dcn/src/deform_conv_cuda_kernel.cu b/repositories/codeformer/basicsr/ops/dcn/src/deform_conv_cuda_kernel.cu new file mode 100644 index 000000000..98752dccf --- /dev/null +++ b/repositories/codeformer/basicsr/ops/dcn/src/deform_conv_cuda_kernel.cu @@ -0,0 +1,867 @@ +/*! + ******************* BEGIN Caffe Copyright Notice and Disclaimer **************** + * + * COPYRIGHT + * + * All contributions by the University of California: + * Copyright (c) 2014-2017 The Regents of the University of California (Regents) + * All rights reserved. + * + * All other contributions: + * Copyright (c) 2014-2017, the respective contributors + * All rights reserved. + * + * Caffe uses a shared copyright model: each contributor holds copyright over + * their contributions to Caffe. The project versioning records all such + * contribution and copyright details. If a contributor wants to further mark + * their specific copyright on a particular contribution, they should indicate + * their copyright solely in the commit message of the change when it is + * committed. + * + * LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * CONTRIBUTION AGREEMENT + * + * By contributing to the BVLC/caffe repository through pull-request, comment, + * or otherwise, the contributor releases their content to the + * license and copyright terms herein. + * + ***************** END Caffe Copyright Notice and Disclaimer ******************** + * + * Copyright (c) 2018 Microsoft + * Licensed under The MIT License [see LICENSE for details] + * \file modulated_deformable_im2col.cuh + * \brief Function definitions of converting an image to + * column matrix based on kernel, padding, dilation, and offset. + * These functions are mainly used in deformable convolution operators. + * \ref: https://arxiv.org/abs/1703.06211 + * \author Yuwen Xiong, Haozhi Qi, Jifeng Dai, Xizhou Zhu, Han Hu, Dazhi Cheng + */ + +// modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/blob/mmdetection/mmdet/ops/dcn/src/deform_conv_cuda_kernel.cu + +#include +#include +#include +#include +#include +#include + +using namespace at; + +#define CUDA_KERNEL_LOOP(i, n) \ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ + i += blockDim.x * gridDim.x) + +const int CUDA_NUM_THREADS = 1024; +const int kMaxGridNum = 65535; + +inline int GET_BLOCKS(const int N) +{ + return std::min(kMaxGridNum, (N + CUDA_NUM_THREADS - 1) / CUDA_NUM_THREADS); +} + +template +__device__ scalar_t deformable_im2col_bilinear(const scalar_t *bottom_data, const int data_width, + const int height, const int width, scalar_t h, scalar_t w) +{ + + int h_low = floor(h); + int w_low = floor(w); + int h_high = h_low + 1; + int w_high = w_low + 1; + + scalar_t lh = h - h_low; + scalar_t lw = w - w_low; + scalar_t hh = 1 - lh, hw = 1 - lw; + + scalar_t v1 = 0; + if (h_low >= 0 && w_low >= 0) + v1 = bottom_data[h_low * data_width + w_low]; + scalar_t v2 = 0; + if (h_low >= 0 && w_high <= width - 1) + v2 = bottom_data[h_low * data_width + w_high]; + scalar_t v3 = 0; + if (h_high <= height - 1 && w_low >= 0) + v3 = bottom_data[h_high * data_width + w_low]; + scalar_t v4 = 0; + if (h_high <= height - 1 && w_high <= width - 1) + v4 = bottom_data[h_high * data_width + w_high]; + + scalar_t w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; + + scalar_t val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); + return val; +} + +template +__device__ scalar_t get_gradient_weight(scalar_t argmax_h, scalar_t argmax_w, + const int h, const int w, const int height, const int width) +{ + + if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || argmax_w >= width) + { + //empty + return 0; + } + + int argmax_h_low = floor(argmax_h); + int argmax_w_low = floor(argmax_w); + int argmax_h_high = argmax_h_low + 1; + int argmax_w_high = argmax_w_low + 1; + + scalar_t weight = 0; + if (h == argmax_h_low && w == argmax_w_low) + weight = (h + 1 - argmax_h) * (w + 1 - argmax_w); + if (h == argmax_h_low && w == argmax_w_high) + weight = (h + 1 - argmax_h) * (argmax_w + 1 - w); + if (h == argmax_h_high && w == argmax_w_low) + weight = (argmax_h + 1 - h) * (w + 1 - argmax_w); + if (h == argmax_h_high && w == argmax_w_high) + weight = (argmax_h + 1 - h) * (argmax_w + 1 - w); + return weight; +} + +template +__device__ scalar_t get_coordinate_weight(scalar_t argmax_h, scalar_t argmax_w, + const int height, const int width, const scalar_t *im_data, + const int data_width, const int bp_dir) +{ + + if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || argmax_w >= width) + { + //empty + return 0; + } + + int argmax_h_low = floor(argmax_h); + int argmax_w_low = floor(argmax_w); + int argmax_h_high = argmax_h_low + 1; + int argmax_w_high = argmax_w_low + 1; + + scalar_t weight = 0; + + if (bp_dir == 0) + { + if (argmax_h_low >= 0 && argmax_w_low >= 0) + weight += -1 * (argmax_w_low + 1 - argmax_w) * im_data[argmax_h_low * data_width + argmax_w_low]; + if (argmax_h_low >= 0 && argmax_w_high <= width - 1) + weight += -1 * (argmax_w - argmax_w_low) * im_data[argmax_h_low * data_width + argmax_w_high]; + if (argmax_h_high <= height - 1 && argmax_w_low >= 0) + weight += (argmax_w_low + 1 - argmax_w) * im_data[argmax_h_high * data_width + argmax_w_low]; + if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) + weight += (argmax_w - argmax_w_low) * im_data[argmax_h_high * data_width + argmax_w_high]; + } + else if (bp_dir == 1) + { + if (argmax_h_low >= 0 && argmax_w_low >= 0) + weight += -1 * (argmax_h_low + 1 - argmax_h) * im_data[argmax_h_low * data_width + argmax_w_low]; + if (argmax_h_low >= 0 && argmax_w_high <= width - 1) + weight += (argmax_h_low + 1 - argmax_h) * im_data[argmax_h_low * data_width + argmax_w_high]; + if (argmax_h_high <= height - 1 && argmax_w_low >= 0) + weight += -1 * (argmax_h - argmax_h_low) * im_data[argmax_h_high * data_width + argmax_w_low]; + if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) + weight += (argmax_h - argmax_h_low) * im_data[argmax_h_high * data_width + argmax_w_high]; + } + + return weight; +} + +template +__global__ void deformable_im2col_gpu_kernel(const int n, const scalar_t *data_im, const scalar_t *data_offset, + const int height, const int width, const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, const int channel_per_deformable_group, + const int batch_size, const int num_channels, const int deformable_group, + const int height_col, const int width_col, + scalar_t *data_col) +{ + CUDA_KERNEL_LOOP(index, n) + { + // index index of output matrix + const int w_col = index % width_col; + const int h_col = (index / width_col) % height_col; + const int b_col = (index / width_col / height_col) % batch_size; + const int c_im = (index / width_col / height_col) / batch_size; + const int c_col = c_im * kernel_h * kernel_w; + + // compute deformable group index + const int deformable_group_index = c_im / channel_per_deformable_group; + + const int h_in = h_col * stride_h - pad_h; + const int w_in = w_col * stride_w - pad_w; + scalar_t *data_col_ptr = data_col + ((c_col * batch_size + b_col) * height_col + h_col) * width_col + w_col; + //const scalar_t* data_im_ptr = data_im + ((b_col * num_channels + c_im) * height + h_in) * width + w_in; + const scalar_t *data_im_ptr = data_im + (b_col * num_channels + c_im) * height * width; + const scalar_t *data_offset_ptr = data_offset + (b_col * deformable_group + deformable_group_index) * 2 * kernel_h * kernel_w * height_col * width_col; + + for (int i = 0; i < kernel_h; ++i) + { + for (int j = 0; j < kernel_w; ++j) + { + const int data_offset_h_ptr = ((2 * (i * kernel_w + j)) * height_col + h_col) * width_col + w_col; + const int data_offset_w_ptr = ((2 * (i * kernel_w + j) + 1) * height_col + h_col) * width_col + w_col; + const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; + const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; + scalar_t val = static_cast(0); + const scalar_t h_im = h_in + i * dilation_h + offset_h; + const scalar_t w_im = w_in + j * dilation_w + offset_w; + if (h_im > -1 && w_im > -1 && h_im < height && w_im < width) + { + //const scalar_t map_h = i * dilation_h + offset_h; + //const scalar_t map_w = j * dilation_w + offset_w; + //const int cur_height = height - h_in; + //const int cur_width = width - w_in; + //val = deformable_im2col_bilinear(data_im_ptr, width, cur_height, cur_width, map_h, map_w); + val = deformable_im2col_bilinear(data_im_ptr, width, height, width, h_im, w_im); + } + *data_col_ptr = val; + data_col_ptr += batch_size * height_col * width_col; + } + } + } +} + +void deformable_im2col( + const at::Tensor data_im, const at::Tensor data_offset, const int channels, + const int height, const int width, const int ksize_h, const int ksize_w, + const int pad_h, const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, const int parallel_imgs, + const int deformable_group, at::Tensor data_col) +{ + // num_axes should be smaller than block size + // todo: check parallel_imgs is correctly passed in + int height_col = (height + 2 * pad_h - (dilation_h * (ksize_h - 1) + 1)) / stride_h + 1; + int width_col = (width + 2 * pad_w - (dilation_w * (ksize_w - 1) + 1)) / stride_w + 1; + int num_kernels = channels * height_col * width_col * parallel_imgs; + int channel_per_deformable_group = channels / deformable_group; + + AT_DISPATCH_FLOATING_TYPES_AND_HALF( + data_im.scalar_type(), "deformable_im2col_gpu", ([&] { + const scalar_t *data_im_ = data_im.data_ptr(); + const scalar_t *data_offset_ = data_offset.data_ptr(); + scalar_t *data_col_ = data_col.data_ptr(); + + deformable_im2col_gpu_kernel<<>>( + num_kernels, data_im_, data_offset_, height, width, ksize_h, ksize_w, + pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w, + channel_per_deformable_group, parallel_imgs, channels, deformable_group, + height_col, width_col, data_col_); + })); + + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) + { + printf("error in deformable_im2col: %s\n", cudaGetErrorString(err)); + } +} + +template +__global__ void deformable_col2im_gpu_kernel( + const int n, const scalar_t *data_col, const scalar_t *data_offset, + const int channels, const int height, const int width, + const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, + const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int channel_per_deformable_group, + const int batch_size, const int deformable_group, + const int height_col, const int width_col, + scalar_t *grad_im) +{ + CUDA_KERNEL_LOOP(index, n) + { + const int j = (index / width_col / height_col / batch_size) % kernel_w; + const int i = (index / width_col / height_col / batch_size / kernel_w) % kernel_h; + const int c = index / width_col / height_col / batch_size / kernel_w / kernel_h; + // compute the start and end of the output + + const int deformable_group_index = c / channel_per_deformable_group; + + int w_out = index % width_col; + int h_out = (index / width_col) % height_col; + int b = (index / width_col / height_col) % batch_size; + int w_in = w_out * stride_w - pad_w; + int h_in = h_out * stride_h - pad_h; + + const scalar_t *data_offset_ptr = data_offset + (b * deformable_group + deformable_group_index) * + 2 * kernel_h * kernel_w * height_col * width_col; + const int data_offset_h_ptr = ((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out; + const int data_offset_w_ptr = ((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out; + const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; + const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; + const scalar_t cur_inv_h_data = h_in + i * dilation_h + offset_h; + const scalar_t cur_inv_w_data = w_in + j * dilation_w + offset_w; + + const scalar_t cur_top_grad = data_col[index]; + const int cur_h = (int)cur_inv_h_data; + const int cur_w = (int)cur_inv_w_data; + for (int dy = -2; dy <= 2; dy++) + { + for (int dx = -2; dx <= 2; dx++) + { + if (cur_h + dy >= 0 && cur_h + dy < height && + cur_w + dx >= 0 && cur_w + dx < width && + abs(cur_inv_h_data - (cur_h + dy)) < 1 && + abs(cur_inv_w_data - (cur_w + dx)) < 1) + { + int cur_bottom_grad_pos = ((b * channels + c) * height + cur_h + dy) * width + cur_w + dx; + scalar_t weight = get_gradient_weight(cur_inv_h_data, cur_inv_w_data, cur_h + dy, cur_w + dx, height, width); + atomicAdd(grad_im + cur_bottom_grad_pos, weight * cur_top_grad); + } + } + } + } +} + +void deformable_col2im( + const at::Tensor data_col, const at::Tensor data_offset, const int channels, + const int height, const int width, const int ksize_h, + const int ksize_w, const int pad_h, const int pad_w, + const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int parallel_imgs, const int deformable_group, + at::Tensor grad_im) +{ + + // todo: make sure parallel_imgs is passed in correctly + int height_col = (height + 2 * pad_h - (dilation_h * (ksize_h - 1) + 1)) / stride_h + 1; + int width_col = (width + 2 * pad_w - (dilation_w * (ksize_w - 1) + 1)) / stride_w + 1; + int num_kernels = channels * ksize_h * ksize_w * height_col * width_col * parallel_imgs; + int channel_per_deformable_group = channels / deformable_group; + + AT_DISPATCH_FLOATING_TYPES_AND_HALF( + data_col.scalar_type(), "deformable_col2im_gpu", ([&] { + const scalar_t *data_col_ = data_col.data_ptr(); + const scalar_t *data_offset_ = data_offset.data_ptr(); + scalar_t *grad_im_ = grad_im.data_ptr(); + + deformable_col2im_gpu_kernel<<>>( + num_kernels, data_col_, data_offset_, channels, height, width, ksize_h, + ksize_w, pad_h, pad_w, stride_h, stride_w, + dilation_h, dilation_w, channel_per_deformable_group, + parallel_imgs, deformable_group, height_col, width_col, grad_im_); + })); + + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) + { + printf("error in deformable_col2im: %s\n", cudaGetErrorString(err)); + } +} + +template +__global__ void deformable_col2im_coord_gpu_kernel(const int n, const scalar_t *data_col, + const scalar_t *data_im, const scalar_t *data_offset, + const int channels, const int height, const int width, + const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, + const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int channel_per_deformable_group, + const int batch_size, const int offset_channels, const int deformable_group, + const int height_col, const int width_col, scalar_t *grad_offset) +{ + CUDA_KERNEL_LOOP(index, n) + { + scalar_t val = 0; + int w = index % width_col; + int h = (index / width_col) % height_col; + int c = (index / width_col / height_col) % offset_channels; + int b = (index / width_col / height_col) / offset_channels; + // compute the start and end of the output + + const int deformable_group_index = c / (2 * kernel_h * kernel_w); + const int col_step = kernel_h * kernel_w; + int cnt = 0; + const scalar_t *data_col_ptr = data_col + deformable_group_index * channel_per_deformable_group * + batch_size * width_col * height_col; + const scalar_t *data_im_ptr = data_im + (b * deformable_group + deformable_group_index) * + channel_per_deformable_group / kernel_h / kernel_w * height * width; + const scalar_t *data_offset_ptr = data_offset + (b * deformable_group + deformable_group_index) * 2 * + kernel_h * kernel_w * height_col * width_col; + + const int offset_c = c - deformable_group_index * 2 * kernel_h * kernel_w; + + for (int col_c = (offset_c / 2); col_c < channel_per_deformable_group; col_c += col_step) + { + const int col_pos = (((col_c * batch_size + b) * height_col) + h) * width_col + w; + const int bp_dir = offset_c % 2; + + int j = (col_pos / width_col / height_col / batch_size) % kernel_w; + int i = (col_pos / width_col / height_col / batch_size / kernel_w) % kernel_h; + int w_out = col_pos % width_col; + int h_out = (col_pos / width_col) % height_col; + int w_in = w_out * stride_w - pad_w; + int h_in = h_out * stride_h - pad_h; + const int data_offset_h_ptr = (((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out); + const int data_offset_w_ptr = (((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out); + const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; + const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; + scalar_t inv_h = h_in + i * dilation_h + offset_h; + scalar_t inv_w = w_in + j * dilation_w + offset_w; + if (inv_h <= -1 || inv_w <= -1 || inv_h >= height || inv_w >= width) + { + inv_h = inv_w = -2; + } + const scalar_t weight = get_coordinate_weight( + inv_h, inv_w, + height, width, data_im_ptr + cnt * height * width, width, bp_dir); + val += weight * data_col_ptr[col_pos]; + cnt += 1; + } + + grad_offset[index] = val; + } +} + +void deformable_col2im_coord( + const at::Tensor data_col, const at::Tensor data_im, const at::Tensor data_offset, + const int channels, const int height, const int width, const int ksize_h, + const int ksize_w, const int pad_h, const int pad_w, const int stride_h, + const int stride_w, const int dilation_h, const int dilation_w, + const int parallel_imgs, const int deformable_group, at::Tensor grad_offset) +{ + + int height_col = (height + 2 * pad_h - (dilation_h * (ksize_h - 1) + 1)) / stride_h + 1; + int width_col = (width + 2 * pad_w - (dilation_w * (ksize_w - 1) + 1)) / stride_w + 1; + int num_kernels = height_col * width_col * 2 * ksize_h * ksize_w * deformable_group * parallel_imgs; + int channel_per_deformable_group = channels * ksize_h * ksize_w / deformable_group; + + AT_DISPATCH_FLOATING_TYPES_AND_HALF( + data_col.scalar_type(), "deformable_col2im_coord_gpu", ([&] { + const scalar_t *data_col_ = data_col.data_ptr(); + const scalar_t *data_im_ = data_im.data_ptr(); + const scalar_t *data_offset_ = data_offset.data_ptr(); + scalar_t *grad_offset_ = grad_offset.data_ptr(); + + deformable_col2im_coord_gpu_kernel<<>>( + num_kernels, data_col_, data_im_, data_offset_, channels, height, width, + ksize_h, ksize_w, pad_h, pad_w, stride_h, stride_w, + dilation_h, dilation_w, channel_per_deformable_group, + parallel_imgs, 2 * ksize_h * ksize_w * deformable_group, deformable_group, + height_col, width_col, grad_offset_); + })); +} + +template +__device__ scalar_t dmcn_im2col_bilinear(const scalar_t *bottom_data, const int data_width, + const int height, const int width, scalar_t h, scalar_t w) +{ + int h_low = floor(h); + int w_low = floor(w); + int h_high = h_low + 1; + int w_high = w_low + 1; + + scalar_t lh = h - h_low; + scalar_t lw = w - w_low; + scalar_t hh = 1 - lh, hw = 1 - lw; + + scalar_t v1 = 0; + if (h_low >= 0 && w_low >= 0) + v1 = bottom_data[h_low * data_width + w_low]; + scalar_t v2 = 0; + if (h_low >= 0 && w_high <= width - 1) + v2 = bottom_data[h_low * data_width + w_high]; + scalar_t v3 = 0; + if (h_high <= height - 1 && w_low >= 0) + v3 = bottom_data[h_high * data_width + w_low]; + scalar_t v4 = 0; + if (h_high <= height - 1 && w_high <= width - 1) + v4 = bottom_data[h_high * data_width + w_high]; + + scalar_t w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; + + scalar_t val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); + return val; +} + +template +__device__ scalar_t dmcn_get_gradient_weight(scalar_t argmax_h, scalar_t argmax_w, + const int h, const int w, const int height, const int width) +{ + if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || argmax_w >= width) + { + //empty + return 0; + } + + int argmax_h_low = floor(argmax_h); + int argmax_w_low = floor(argmax_w); + int argmax_h_high = argmax_h_low + 1; + int argmax_w_high = argmax_w_low + 1; + + scalar_t weight = 0; + if (h == argmax_h_low && w == argmax_w_low) + weight = (h + 1 - argmax_h) * (w + 1 - argmax_w); + if (h == argmax_h_low && w == argmax_w_high) + weight = (h + 1 - argmax_h) * (argmax_w + 1 - w); + if (h == argmax_h_high && w == argmax_w_low) + weight = (argmax_h + 1 - h) * (w + 1 - argmax_w); + if (h == argmax_h_high && w == argmax_w_high) + weight = (argmax_h + 1 - h) * (argmax_w + 1 - w); + return weight; +} + +template +__device__ scalar_t dmcn_get_coordinate_weight(scalar_t argmax_h, scalar_t argmax_w, + const int height, const int width, const scalar_t *im_data, + const int data_width, const int bp_dir) +{ + if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || argmax_w >= width) + { + //empty + return 0; + } + + int argmax_h_low = floor(argmax_h); + int argmax_w_low = floor(argmax_w); + int argmax_h_high = argmax_h_low + 1; + int argmax_w_high = argmax_w_low + 1; + + scalar_t weight = 0; + + if (bp_dir == 0) + { + if (argmax_h_low >= 0 && argmax_w_low >= 0) + weight += -1 * (argmax_w_low + 1 - argmax_w) * im_data[argmax_h_low * data_width + argmax_w_low]; + if (argmax_h_low >= 0 && argmax_w_high <= width - 1) + weight += -1 * (argmax_w - argmax_w_low) * im_data[argmax_h_low * data_width + argmax_w_high]; + if (argmax_h_high <= height - 1 && argmax_w_low >= 0) + weight += (argmax_w_low + 1 - argmax_w) * im_data[argmax_h_high * data_width + argmax_w_low]; + if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) + weight += (argmax_w - argmax_w_low) * im_data[argmax_h_high * data_width + argmax_w_high]; + } + else if (bp_dir == 1) + { + if (argmax_h_low >= 0 && argmax_w_low >= 0) + weight += -1 * (argmax_h_low + 1 - argmax_h) * im_data[argmax_h_low * data_width + argmax_w_low]; + if (argmax_h_low >= 0 && argmax_w_high <= width - 1) + weight += (argmax_h_low + 1 - argmax_h) * im_data[argmax_h_low * data_width + argmax_w_high]; + if (argmax_h_high <= height - 1 && argmax_w_low >= 0) + weight += -1 * (argmax_h - argmax_h_low) * im_data[argmax_h_high * data_width + argmax_w_low]; + if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) + weight += (argmax_h - argmax_h_low) * im_data[argmax_h_high * data_width + argmax_w_high]; + } + + return weight; +} + +template +__global__ void modulated_deformable_im2col_gpu_kernel(const int n, + const scalar_t *data_im, const scalar_t *data_offset, const scalar_t *data_mask, + const int height, const int width, const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, + const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int channel_per_deformable_group, + const int batch_size, const int num_channels, const int deformable_group, + const int height_col, const int width_col, + scalar_t *data_col) +{ + CUDA_KERNEL_LOOP(index, n) + { + // index index of output matrix + const int w_col = index % width_col; + const int h_col = (index / width_col) % height_col; + const int b_col = (index / width_col / height_col) % batch_size; + const int c_im = (index / width_col / height_col) / batch_size; + const int c_col = c_im * kernel_h * kernel_w; + + // compute deformable group index + const int deformable_group_index = c_im / channel_per_deformable_group; + + const int h_in = h_col * stride_h - pad_h; + const int w_in = w_col * stride_w - pad_w; + + scalar_t *data_col_ptr = data_col + ((c_col * batch_size + b_col) * height_col + h_col) * width_col + w_col; + //const float* data_im_ptr = data_im + ((b_col * num_channels + c_im) * height + h_in) * width + w_in; + const scalar_t *data_im_ptr = data_im + (b_col * num_channels + c_im) * height * width; + const scalar_t *data_offset_ptr = data_offset + (b_col * deformable_group + deformable_group_index) * 2 * kernel_h * kernel_w * height_col * width_col; + + const scalar_t *data_mask_ptr = data_mask + (b_col * deformable_group + deformable_group_index) * kernel_h * kernel_w * height_col * width_col; + + for (int i = 0; i < kernel_h; ++i) + { + for (int j = 0; j < kernel_w; ++j) + { + const int data_offset_h_ptr = ((2 * (i * kernel_w + j)) * height_col + h_col) * width_col + w_col; + const int data_offset_w_ptr = ((2 * (i * kernel_w + j) + 1) * height_col + h_col) * width_col + w_col; + const int data_mask_hw_ptr = ((i * kernel_w + j) * height_col + h_col) * width_col + w_col; + const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; + const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; + const scalar_t mask = data_mask_ptr[data_mask_hw_ptr]; + scalar_t val = static_cast(0); + const scalar_t h_im = h_in + i * dilation_h + offset_h; + const scalar_t w_im = w_in + j * dilation_w + offset_w; + //if (h_im >= 0 && w_im >= 0 && h_im < height && w_im < width) { + if (h_im > -1 && w_im > -1 && h_im < height && w_im < width) + { + //const float map_h = i * dilation_h + offset_h; + //const float map_w = j * dilation_w + offset_w; + //const int cur_height = height - h_in; + //const int cur_width = width - w_in; + //val = dmcn_im2col_bilinear(data_im_ptr, width, cur_height, cur_width, map_h, map_w); + val = dmcn_im2col_bilinear(data_im_ptr, width, height, width, h_im, w_im); + } + *data_col_ptr = val * mask; + data_col_ptr += batch_size * height_col * width_col; + //data_col_ptr += height_col * width_col; + } + } + } +} + +template +__global__ void modulated_deformable_col2im_gpu_kernel(const int n, + const scalar_t *data_col, const scalar_t *data_offset, const scalar_t *data_mask, + const int channels, const int height, const int width, + const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, + const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int channel_per_deformable_group, + const int batch_size, const int deformable_group, + const int height_col, const int width_col, + scalar_t *grad_im) +{ + CUDA_KERNEL_LOOP(index, n) + { + const int j = (index / width_col / height_col / batch_size) % kernel_w; + const int i = (index / width_col / height_col / batch_size / kernel_w) % kernel_h; + const int c = index / width_col / height_col / batch_size / kernel_w / kernel_h; + // compute the start and end of the output + + const int deformable_group_index = c / channel_per_deformable_group; + + int w_out = index % width_col; + int h_out = (index / width_col) % height_col; + int b = (index / width_col / height_col) % batch_size; + int w_in = w_out * stride_w - pad_w; + int h_in = h_out * stride_h - pad_h; + + const scalar_t *data_offset_ptr = data_offset + (b * deformable_group + deformable_group_index) * 2 * kernel_h * kernel_w * height_col * width_col; + const scalar_t *data_mask_ptr = data_mask + (b * deformable_group + deformable_group_index) * kernel_h * kernel_w * height_col * width_col; + const int data_offset_h_ptr = ((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out; + const int data_offset_w_ptr = ((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out; + const int data_mask_hw_ptr = ((i * kernel_w + j) * height_col + h_out) * width_col + w_out; + const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; + const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; + const scalar_t mask = data_mask_ptr[data_mask_hw_ptr]; + const scalar_t cur_inv_h_data = h_in + i * dilation_h + offset_h; + const scalar_t cur_inv_w_data = w_in + j * dilation_w + offset_w; + + const scalar_t cur_top_grad = data_col[index] * mask; + const int cur_h = (int)cur_inv_h_data; + const int cur_w = (int)cur_inv_w_data; + for (int dy = -2; dy <= 2; dy++) + { + for (int dx = -2; dx <= 2; dx++) + { + if (cur_h + dy >= 0 && cur_h + dy < height && + cur_w + dx >= 0 && cur_w + dx < width && + abs(cur_inv_h_data - (cur_h + dy)) < 1 && + abs(cur_inv_w_data - (cur_w + dx)) < 1) + { + int cur_bottom_grad_pos = ((b * channels + c) * height + cur_h + dy) * width + cur_w + dx; + scalar_t weight = dmcn_get_gradient_weight(cur_inv_h_data, cur_inv_w_data, cur_h + dy, cur_w + dx, height, width); + atomicAdd(grad_im + cur_bottom_grad_pos, weight * cur_top_grad); + } + } + } + } +} + +template +__global__ void modulated_deformable_col2im_coord_gpu_kernel(const int n, + const scalar_t *data_col, const scalar_t *data_im, + const scalar_t *data_offset, const scalar_t *data_mask, + const int channels, const int height, const int width, + const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, + const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int channel_per_deformable_group, + const int batch_size, const int offset_channels, const int deformable_group, + const int height_col, const int width_col, + scalar_t *grad_offset, scalar_t *grad_mask) +{ + CUDA_KERNEL_LOOP(index, n) + { + scalar_t val = 0, mval = 0; + int w = index % width_col; + int h = (index / width_col) % height_col; + int c = (index / width_col / height_col) % offset_channels; + int b = (index / width_col / height_col) / offset_channels; + // compute the start and end of the output + + const int deformable_group_index = c / (2 * kernel_h * kernel_w); + const int col_step = kernel_h * kernel_w; + int cnt = 0; + const scalar_t *data_col_ptr = data_col + deformable_group_index * channel_per_deformable_group * batch_size * width_col * height_col; + const scalar_t *data_im_ptr = data_im + (b * deformable_group + deformable_group_index) * channel_per_deformable_group / kernel_h / kernel_w * height * width; + const scalar_t *data_offset_ptr = data_offset + (b * deformable_group + deformable_group_index) * 2 * kernel_h * kernel_w * height_col * width_col; + const scalar_t *data_mask_ptr = data_mask + (b * deformable_group + deformable_group_index) * kernel_h * kernel_w * height_col * width_col; + + const int offset_c = c - deformable_group_index * 2 * kernel_h * kernel_w; + + for (int col_c = (offset_c / 2); col_c < channel_per_deformable_group; col_c += col_step) + { + const int col_pos = (((col_c * batch_size + b) * height_col) + h) * width_col + w; + const int bp_dir = offset_c % 2; + + int j = (col_pos / width_col / height_col / batch_size) % kernel_w; + int i = (col_pos / width_col / height_col / batch_size / kernel_w) % kernel_h; + int w_out = col_pos % width_col; + int h_out = (col_pos / width_col) % height_col; + int w_in = w_out * stride_w - pad_w; + int h_in = h_out * stride_h - pad_h; + const int data_offset_h_ptr = (((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out); + const int data_offset_w_ptr = (((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out); + const int data_mask_hw_ptr = (((i * kernel_w + j) * height_col + h_out) * width_col + w_out); + const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; + const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; + const scalar_t mask = data_mask_ptr[data_mask_hw_ptr]; + scalar_t inv_h = h_in + i * dilation_h + offset_h; + scalar_t inv_w = w_in + j * dilation_w + offset_w; + if (inv_h <= -1 || inv_w <= -1 || inv_h >= height || inv_w >= width) + { + inv_h = inv_w = -2; + } + else + { + mval += data_col_ptr[col_pos] * dmcn_im2col_bilinear(data_im_ptr + cnt * height * width, width, height, width, inv_h, inv_w); + } + const scalar_t weight = dmcn_get_coordinate_weight( + inv_h, inv_w, + height, width, data_im_ptr + cnt * height * width, width, bp_dir); + val += weight * data_col_ptr[col_pos] * mask; + cnt += 1; + } + // KERNEL_ASSIGN(grad_offset[index], offset_req, val); + grad_offset[index] = val; + if (offset_c % 2 == 0) + // KERNEL_ASSIGN(grad_mask[(((b * deformable_group + deformable_group_index) * kernel_h * kernel_w + offset_c / 2) * height_col + h) * width_col + w], mask_req, mval); + grad_mask[(((b * deformable_group + deformable_group_index) * kernel_h * kernel_w + offset_c / 2) * height_col + h) * width_col + w] = mval; + } +} + +void modulated_deformable_im2col_cuda( + const at::Tensor data_im, const at::Tensor data_offset, const at::Tensor data_mask, + const int batch_size, const int channels, const int height_im, const int width_im, + const int height_col, const int width_col, const int kernel_h, const int kenerl_w, + const int pad_h, const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int deformable_group, at::Tensor data_col) +{ + // num_axes should be smaller than block size + const int channel_per_deformable_group = channels / deformable_group; + const int num_kernels = channels * batch_size * height_col * width_col; + + AT_DISPATCH_FLOATING_TYPES_AND_HALF( + data_im.scalar_type(), "modulated_deformable_im2col_gpu", ([&] { + const scalar_t *data_im_ = data_im.data_ptr(); + const scalar_t *data_offset_ = data_offset.data_ptr(); + const scalar_t *data_mask_ = data_mask.data_ptr(); + scalar_t *data_col_ = data_col.data_ptr(); + + modulated_deformable_im2col_gpu_kernel<<>>( + num_kernels, data_im_, data_offset_, data_mask_, height_im, width_im, kernel_h, kenerl_w, + pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w, channel_per_deformable_group, + batch_size, channels, deformable_group, height_col, width_col, data_col_); + })); + + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) + { + printf("error in modulated_deformable_im2col_cuda: %s\n", cudaGetErrorString(err)); + } +} + +void modulated_deformable_col2im_cuda( + const at::Tensor data_col, const at::Tensor data_offset, const at::Tensor data_mask, + const int batch_size, const int channels, const int height_im, const int width_im, + const int height_col, const int width_col, const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int deformable_group, at::Tensor grad_im) +{ + + const int channel_per_deformable_group = channels / deformable_group; + const int num_kernels = channels * kernel_h * kernel_w * batch_size * height_col * width_col; + + AT_DISPATCH_FLOATING_TYPES_AND_HALF( + data_col.scalar_type(), "modulated_deformable_col2im_gpu", ([&] { + const scalar_t *data_col_ = data_col.data_ptr(); + const scalar_t *data_offset_ = data_offset.data_ptr(); + const scalar_t *data_mask_ = data_mask.data_ptr(); + scalar_t *grad_im_ = grad_im.data_ptr(); + + modulated_deformable_col2im_gpu_kernel<<>>( + num_kernels, data_col_, data_offset_, data_mask_, channels, height_im, width_im, + kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, + dilation_h, dilation_w, channel_per_deformable_group, + batch_size, deformable_group, height_col, width_col, grad_im_); + })); + + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) + { + printf("error in modulated_deformable_col2im_cuda: %s\n", cudaGetErrorString(err)); + } +} + +void modulated_deformable_col2im_coord_cuda( + const at::Tensor data_col, const at::Tensor data_im, const at::Tensor data_offset, const at::Tensor data_mask, + const int batch_size, const int channels, const int height_im, const int width_im, + const int height_col, const int width_col, const int kernel_h, const int kernel_w, + const int pad_h, const int pad_w, const int stride_h, const int stride_w, + const int dilation_h, const int dilation_w, + const int deformable_group, + at::Tensor grad_offset, at::Tensor grad_mask) +{ + const int num_kernels = batch_size * height_col * width_col * 2 * kernel_h * kernel_w * deformable_group; + const int channel_per_deformable_group = channels * kernel_h * kernel_w / deformable_group; + + AT_DISPATCH_FLOATING_TYPES_AND_HALF( + data_col.scalar_type(), "modulated_deformable_col2im_coord_gpu", ([&] { + const scalar_t *data_col_ = data_col.data_ptr(); + const scalar_t *data_im_ = data_im.data_ptr(); + const scalar_t *data_offset_ = data_offset.data_ptr(); + const scalar_t *data_mask_ = data_mask.data_ptr(); + scalar_t *grad_offset_ = grad_offset.data_ptr(); + scalar_t *grad_mask_ = grad_mask.data_ptr(); + + modulated_deformable_col2im_coord_gpu_kernel<<>>( + num_kernels, data_col_, data_im_, data_offset_, data_mask_, channels, height_im, width_im, + kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, + dilation_h, dilation_w, channel_per_deformable_group, + batch_size, 2 * kernel_h * kernel_w * deformable_group, deformable_group, height_col, width_col, + grad_offset_, grad_mask_); + })); + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) + { + printf("error in modulated_deformable_col2im_coord_cuda: %s\n", cudaGetErrorString(err)); + } +} diff --git a/repositories/codeformer/basicsr/ops/dcn/src/deform_conv_ext.cpp b/repositories/codeformer/basicsr/ops/dcn/src/deform_conv_ext.cpp new file mode 100644 index 000000000..41c6df6f7 --- /dev/null +++ b/repositories/codeformer/basicsr/ops/dcn/src/deform_conv_ext.cpp @@ -0,0 +1,164 @@ +// modify from +// https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/blob/mmdetection/mmdet/ops/dcn/src/deform_conv_cuda.c + +#include +#include + +#include +#include + +#define WITH_CUDA // always use cuda +#ifdef WITH_CUDA +int deform_conv_forward_cuda(at::Tensor input, at::Tensor weight, + at::Tensor offset, at::Tensor output, + at::Tensor columns, at::Tensor ones, int kW, + int kH, int dW, int dH, int padW, int padH, + int dilationW, int dilationH, int group, + int deformable_group, int im2col_step); + +int deform_conv_backward_input_cuda(at::Tensor input, at::Tensor offset, + at::Tensor gradOutput, at::Tensor gradInput, + at::Tensor gradOffset, at::Tensor weight, + at::Tensor columns, int kW, int kH, int dW, + int dH, int padW, int padH, int dilationW, + int dilationH, int group, + int deformable_group, int im2col_step); + +int deform_conv_backward_parameters_cuda( + at::Tensor input, at::Tensor offset, at::Tensor gradOutput, + at::Tensor gradWeight, // at::Tensor gradBias, + at::Tensor columns, at::Tensor ones, int kW, int kH, int dW, int dH, + int padW, int padH, int dilationW, int dilationH, int group, + int deformable_group, float scale, int im2col_step); + +void modulated_deform_conv_cuda_forward( + at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, + at::Tensor offset, at::Tensor mask, at::Tensor output, at::Tensor columns, + int kernel_h, int kernel_w, const int stride_h, const int stride_w, + const int pad_h, const int pad_w, const int dilation_h, + const int dilation_w, const int group, const int deformable_group, + const bool with_bias); + +void modulated_deform_conv_cuda_backward( + at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, + at::Tensor offset, at::Tensor mask, at::Tensor columns, + at::Tensor grad_input, at::Tensor grad_weight, at::Tensor grad_bias, + at::Tensor grad_offset, at::Tensor grad_mask, at::Tensor grad_output, + int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h, + int pad_w, int dilation_h, int dilation_w, int group, int deformable_group, + const bool with_bias); +#endif + +int deform_conv_forward(at::Tensor input, at::Tensor weight, + at::Tensor offset, at::Tensor output, + at::Tensor columns, at::Tensor ones, int kW, + int kH, int dW, int dH, int padW, int padH, + int dilationW, int dilationH, int group, + int deformable_group, int im2col_step) { + if (input.device().is_cuda()) { +#ifdef WITH_CUDA + return deform_conv_forward_cuda(input, weight, offset, output, columns, + ones, kW, kH, dW, dH, padW, padH, dilationW, dilationH, group, + deformable_group, im2col_step); +#else + AT_ERROR("deform conv is not compiled with GPU support"); +#endif + } + AT_ERROR("deform conv is not implemented on CPU"); +} + +int deform_conv_backward_input(at::Tensor input, at::Tensor offset, + at::Tensor gradOutput, at::Tensor gradInput, + at::Tensor gradOffset, at::Tensor weight, + at::Tensor columns, int kW, int kH, int dW, + int dH, int padW, int padH, int dilationW, + int dilationH, int group, + int deformable_group, int im2col_step) { + if (input.device().is_cuda()) { +#ifdef WITH_CUDA + return deform_conv_backward_input_cuda(input, offset, gradOutput, + gradInput, gradOffset, weight, columns, kW, kH, dW, dH, padW, padH, + dilationW, dilationH, group, deformable_group, im2col_step); +#else + AT_ERROR("deform conv is not compiled with GPU support"); +#endif + } + AT_ERROR("deform conv is not implemented on CPU"); +} + +int deform_conv_backward_parameters( + at::Tensor input, at::Tensor offset, at::Tensor gradOutput, + at::Tensor gradWeight, // at::Tensor gradBias, + at::Tensor columns, at::Tensor ones, int kW, int kH, int dW, int dH, + int padW, int padH, int dilationW, int dilationH, int group, + int deformable_group, float scale, int im2col_step) { + if (input.device().is_cuda()) { +#ifdef WITH_CUDA + return deform_conv_backward_parameters_cuda(input, offset, gradOutput, + gradWeight, columns, ones, kW, kH, dW, dH, padW, padH, dilationW, + dilationH, group, deformable_group, scale, im2col_step); +#else + AT_ERROR("deform conv is not compiled with GPU support"); +#endif + } + AT_ERROR("deform conv is not implemented on CPU"); +} + +void modulated_deform_conv_forward( + at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, + at::Tensor offset, at::Tensor mask, at::Tensor output, at::Tensor columns, + int kernel_h, int kernel_w, const int stride_h, const int stride_w, + const int pad_h, const int pad_w, const int dilation_h, + const int dilation_w, const int group, const int deformable_group, + const bool with_bias) { + if (input.device().is_cuda()) { +#ifdef WITH_CUDA + return modulated_deform_conv_cuda_forward(input, weight, bias, ones, + offset, mask, output, columns, kernel_h, kernel_w, stride_h, + stride_w, pad_h, pad_w, dilation_h, dilation_w, group, + deformable_group, with_bias); +#else + AT_ERROR("modulated deform conv is not compiled with GPU support"); +#endif + } + AT_ERROR("modulated deform conv is not implemented on CPU"); +} + +void modulated_deform_conv_backward( + at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, + at::Tensor offset, at::Tensor mask, at::Tensor columns, + at::Tensor grad_input, at::Tensor grad_weight, at::Tensor grad_bias, + at::Tensor grad_offset, at::Tensor grad_mask, at::Tensor grad_output, + int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h, + int pad_w, int dilation_h, int dilation_w, int group, int deformable_group, + const bool with_bias) { + if (input.device().is_cuda()) { +#ifdef WITH_CUDA + return modulated_deform_conv_cuda_backward(input, weight, bias, ones, + offset, mask, columns, grad_input, grad_weight, grad_bias, grad_offset, + grad_mask, grad_output, kernel_h, kernel_w, stride_h, stride_w, + pad_h, pad_w, dilation_h, dilation_w, group, deformable_group, + with_bias); +#else + AT_ERROR("modulated deform conv is not compiled with GPU support"); +#endif + } + AT_ERROR("modulated deform conv is not implemented on CPU"); +} + + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("deform_conv_forward", &deform_conv_forward, + "deform forward"); + m.def("deform_conv_backward_input", &deform_conv_backward_input, + "deform_conv_backward_input"); + m.def("deform_conv_backward_parameters", + &deform_conv_backward_parameters, + "deform_conv_backward_parameters"); + m.def("modulated_deform_conv_forward", + &modulated_deform_conv_forward, + "modulated deform conv forward"); + m.def("modulated_deform_conv_backward", + &modulated_deform_conv_backward, + "modulated deform conv backward"); +} diff --git a/repositories/codeformer/basicsr/ops/fused_act/__init__.py b/repositories/codeformer/basicsr/ops/fused_act/__init__.py new file mode 100644 index 000000000..241dc0754 --- /dev/null +++ b/repositories/codeformer/basicsr/ops/fused_act/__init__.py @@ -0,0 +1,3 @@ +from .fused_act import FusedLeakyReLU, fused_leaky_relu + +__all__ = ['FusedLeakyReLU', 'fused_leaky_relu'] diff --git a/repositories/codeformer/basicsr/ops/fused_act/fused_act.py b/repositories/codeformer/basicsr/ops/fused_act/fused_act.py new file mode 100644 index 000000000..588f815e5 --- /dev/null +++ b/repositories/codeformer/basicsr/ops/fused_act/fused_act.py @@ -0,0 +1,89 @@ +# modify from https://github.com/rosinality/stylegan2-pytorch/blob/master/op/fused_act.py # noqa:E501 + +import torch +from torch import nn +from torch.autograd import Function + +try: + from . import fused_act_ext +except ImportError: + import os + BASICSR_JIT = os.getenv('BASICSR_JIT') + if BASICSR_JIT == 'True': + from torch.utils.cpp_extension import load + module_path = os.path.dirname(__file__) + fused_act_ext = load( + 'fused', + sources=[ + os.path.join(module_path, 'src', 'fused_bias_act.cpp'), + os.path.join(module_path, 'src', 'fused_bias_act_kernel.cu'), + ], + ) + + +class FusedLeakyReLUFunctionBackward(Function): + + @staticmethod + def forward(ctx, grad_output, out, negative_slope, scale): + ctx.save_for_backward(out) + ctx.negative_slope = negative_slope + ctx.scale = scale + + empty = grad_output.new_empty(0) + + grad_input = fused_act_ext.fused_bias_act(grad_output, empty, out, 3, 1, negative_slope, scale) + + dim = [0] + + if grad_input.ndim > 2: + dim += list(range(2, grad_input.ndim)) + + grad_bias = grad_input.sum(dim).detach() + + return grad_input, grad_bias + + @staticmethod + def backward(ctx, gradgrad_input, gradgrad_bias): + out, = ctx.saved_tensors + gradgrad_out = fused_act_ext.fused_bias_act(gradgrad_input, gradgrad_bias, out, 3, 1, ctx.negative_slope, + ctx.scale) + + return gradgrad_out, None, None, None + + +class FusedLeakyReLUFunction(Function): + + @staticmethod + def forward(ctx, input, bias, negative_slope, scale): + empty = input.new_empty(0) + out = fused_act_ext.fused_bias_act(input, bias, empty, 3, 0, negative_slope, scale) + ctx.save_for_backward(out) + ctx.negative_slope = negative_slope + ctx.scale = scale + + return out + + @staticmethod + def backward(ctx, grad_output): + out, = ctx.saved_tensors + + grad_input, grad_bias = FusedLeakyReLUFunctionBackward.apply(grad_output, out, ctx.negative_slope, ctx.scale) + + return grad_input, grad_bias, None, None + + +class FusedLeakyReLU(nn.Module): + + def __init__(self, channel, negative_slope=0.2, scale=2**0.5): + super().__init__() + + self.bias = nn.Parameter(torch.zeros(channel)) + self.negative_slope = negative_slope + self.scale = scale + + def forward(self, input): + return fused_leaky_relu(input, self.bias, self.negative_slope, self.scale) + + +def fused_leaky_relu(input, bias, negative_slope=0.2, scale=2**0.5): + return FusedLeakyReLUFunction.apply(input, bias, negative_slope, scale) diff --git a/repositories/codeformer/basicsr/ops/fused_act/src/fused_bias_act.cpp b/repositories/codeformer/basicsr/ops/fused_act/src/fused_bias_act.cpp new file mode 100644 index 000000000..85ed0a79f --- /dev/null +++ b/repositories/codeformer/basicsr/ops/fused_act/src/fused_bias_act.cpp @@ -0,0 +1,26 @@ +// from https://github.com/rosinality/stylegan2-pytorch/blob/master/op/fused_bias_act.cpp +#include + + +torch::Tensor fused_bias_act_op(const torch::Tensor& input, + const torch::Tensor& bias, + const torch::Tensor& refer, + int act, int grad, float alpha, float scale); + +#define CHECK_CUDA(x) TORCH_CHECK(x.type().is_cuda(), #x " must be a CUDA tensor") +#define CHECK_CONTIGUOUS(x) TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") +#define CHECK_INPUT(x) CHECK_CUDA(x); CHECK_CONTIGUOUS(x) + +torch::Tensor fused_bias_act(const torch::Tensor& input, + const torch::Tensor& bias, + const torch::Tensor& refer, + int act, int grad, float alpha, float scale) { + CHECK_CUDA(input); + CHECK_CUDA(bias); + + return fused_bias_act_op(input, bias, refer, act, grad, alpha, scale); +} + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("fused_bias_act", &fused_bias_act, "fused bias act (CUDA)"); +} diff --git a/repositories/codeformer/basicsr/ops/fused_act/src/fused_bias_act_kernel.cu b/repositories/codeformer/basicsr/ops/fused_act/src/fused_bias_act_kernel.cu new file mode 100644 index 000000000..54c7ff53c --- /dev/null +++ b/repositories/codeformer/basicsr/ops/fused_act/src/fused_bias_act_kernel.cu @@ -0,0 +1,100 @@ +// from https://github.com/rosinality/stylegan2-pytorch/blob/master/op/fused_bias_act_kernel.cu +// Copyright (c) 2019, NVIDIA Corporation. All rights reserved. +// +// This work is made available under the Nvidia Source Code License-NC. +// To view a copy of this license, visit +// https://nvlabs.github.io/stylegan2/license.html + +#include + +#include +#include +#include +#include + +#include +#include + + +template +static __global__ void fused_bias_act_kernel(scalar_t* out, const scalar_t* p_x, const scalar_t* p_b, const scalar_t* p_ref, + int act, int grad, scalar_t alpha, scalar_t scale, int loop_x, int size_x, int step_b, int size_b, int use_bias, int use_ref) { + int xi = blockIdx.x * loop_x * blockDim.x + threadIdx.x; + + scalar_t zero = 0.0; + + for (int loop_idx = 0; loop_idx < loop_x && xi < size_x; loop_idx++, xi += blockDim.x) { + scalar_t x = p_x[xi]; + + if (use_bias) { + x += p_b[(xi / step_b) % size_b]; + } + + scalar_t ref = use_ref ? p_ref[xi] : zero; + + scalar_t y; + + switch (act * 10 + grad) { + default: + case 10: y = x; break; + case 11: y = x; break; + case 12: y = 0.0; break; + + case 30: y = (x > 0.0) ? x : x * alpha; break; + case 31: y = (ref > 0.0) ? x : x * alpha; break; + case 32: y = 0.0; break; + } + + out[xi] = y * scale; + } +} + + +torch::Tensor fused_bias_act_op(const torch::Tensor& input, const torch::Tensor& bias, const torch::Tensor& refer, + int act, int grad, float alpha, float scale) { + int curDevice = -1; + cudaGetDevice(&curDevice); + cudaStream_t stream = at::cuda::getCurrentCUDAStream(curDevice); + + auto x = input.contiguous(); + auto b = bias.contiguous(); + auto ref = refer.contiguous(); + + int use_bias = b.numel() ? 1 : 0; + int use_ref = ref.numel() ? 1 : 0; + + int size_x = x.numel(); + int size_b = b.numel(); + int step_b = 1; + + for (int i = 1 + 1; i < x.dim(); i++) { + step_b *= x.size(i); + } + + int loop_x = 4; + int block_size = 4 * 32; + int grid_size = (size_x - 1) / (loop_x * block_size) + 1; + + auto y = torch::empty_like(x); + + AT_DISPATCH_FLOATING_TYPES_AND_HALF(x.scalar_type(), "fused_bias_act_kernel", [&] { + fused_bias_act_kernel<<>>( + y.data_ptr(), + x.data_ptr(), + b.data_ptr(), + ref.data_ptr(), + act, + grad, + alpha, + scale, + loop_x, + size_x, + step_b, + size_b, + use_bias, + use_ref + ); + }); + + return y; +} diff --git a/repositories/codeformer/basicsr/ops/upfirdn2d/__init__.py b/repositories/codeformer/basicsr/ops/upfirdn2d/__init__.py new file mode 100644 index 000000000..397e85bea --- /dev/null +++ b/repositories/codeformer/basicsr/ops/upfirdn2d/__init__.py @@ -0,0 +1,3 @@ +from .upfirdn2d import upfirdn2d + +__all__ = ['upfirdn2d'] diff --git a/repositories/codeformer/basicsr/ops/upfirdn2d/src/upfirdn2d.cpp b/repositories/codeformer/basicsr/ops/upfirdn2d/src/upfirdn2d.cpp new file mode 100644 index 000000000..43d0b6783 --- /dev/null +++ b/repositories/codeformer/basicsr/ops/upfirdn2d/src/upfirdn2d.cpp @@ -0,0 +1,24 @@ +// from https://github.com/rosinality/stylegan2-pytorch/blob/master/op/upfirdn2d.cpp +#include + + +torch::Tensor upfirdn2d_op(const torch::Tensor& input, const torch::Tensor& kernel, + int up_x, int up_y, int down_x, int down_y, + int pad_x0, int pad_x1, int pad_y0, int pad_y1); + +#define CHECK_CUDA(x) TORCH_CHECK(x.type().is_cuda(), #x " must be a CUDA tensor") +#define CHECK_CONTIGUOUS(x) TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") +#define CHECK_INPUT(x) CHECK_CUDA(x); CHECK_CONTIGUOUS(x) + +torch::Tensor upfirdn2d(const torch::Tensor& input, const torch::Tensor& kernel, + int up_x, int up_y, int down_x, int down_y, + int pad_x0, int pad_x1, int pad_y0, int pad_y1) { + CHECK_CUDA(input); + CHECK_CUDA(kernel); + + return upfirdn2d_op(input, kernel, up_x, up_y, down_x, down_y, pad_x0, pad_x1, pad_y0, pad_y1); +} + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("upfirdn2d", &upfirdn2d, "upfirdn2d (CUDA)"); +} diff --git a/repositories/codeformer/basicsr/ops/upfirdn2d/src/upfirdn2d_kernel.cu b/repositories/codeformer/basicsr/ops/upfirdn2d/src/upfirdn2d_kernel.cu new file mode 100644 index 000000000..8870063ba --- /dev/null +++ b/repositories/codeformer/basicsr/ops/upfirdn2d/src/upfirdn2d_kernel.cu @@ -0,0 +1,370 @@ +// from https://github.com/rosinality/stylegan2-pytorch/blob/master/op/upfirdn2d_kernel.cu +// Copyright (c) 2019, NVIDIA Corporation. All rights reserved. +// +// This work is made available under the Nvidia Source Code License-NC. +// To view a copy of this license, visit +// https://nvlabs.github.io/stylegan2/license.html + +#include + +#include +#include +#include +#include + +#include +#include + +static __host__ __device__ __forceinline__ int floor_div(int a, int b) { + int c = a / b; + + if (c * b > a) { + c--; + } + + return c; +} + +struct UpFirDn2DKernelParams { + int up_x; + int up_y; + int down_x; + int down_y; + int pad_x0; + int pad_x1; + int pad_y0; + int pad_y1; + + int major_dim; + int in_h; + int in_w; + int minor_dim; + int kernel_h; + int kernel_w; + int out_h; + int out_w; + int loop_major; + int loop_x; +}; + +template +__global__ void upfirdn2d_kernel_large(scalar_t *out, const scalar_t *input, + const scalar_t *kernel, + const UpFirDn2DKernelParams p) { + int minor_idx = blockIdx.x * blockDim.x + threadIdx.x; + int out_y = minor_idx / p.minor_dim; + minor_idx -= out_y * p.minor_dim; + int out_x_base = blockIdx.y * p.loop_x * blockDim.y + threadIdx.y; + int major_idx_base = blockIdx.z * p.loop_major; + + if (out_x_base >= p.out_w || out_y >= p.out_h || + major_idx_base >= p.major_dim) { + return; + } + + int mid_y = out_y * p.down_y + p.up_y - 1 - p.pad_y0; + int in_y = min(max(floor_div(mid_y, p.up_y), 0), p.in_h); + int h = min(max(floor_div(mid_y + p.kernel_h, p.up_y), 0), p.in_h) - in_y; + int kernel_y = mid_y + p.kernel_h - (in_y + 1) * p.up_y; + + for (int loop_major = 0, major_idx = major_idx_base; + loop_major < p.loop_major && major_idx < p.major_dim; + loop_major++, major_idx++) { + for (int loop_x = 0, out_x = out_x_base; + loop_x < p.loop_x && out_x < p.out_w; loop_x++, out_x += blockDim.y) { + int mid_x = out_x * p.down_x + p.up_x - 1 - p.pad_x0; + int in_x = min(max(floor_div(mid_x, p.up_x), 0), p.in_w); + int w = min(max(floor_div(mid_x + p.kernel_w, p.up_x), 0), p.in_w) - in_x; + int kernel_x = mid_x + p.kernel_w - (in_x + 1) * p.up_x; + + const scalar_t *x_p = + &input[((major_idx * p.in_h + in_y) * p.in_w + in_x) * p.minor_dim + + minor_idx]; + const scalar_t *k_p = &kernel[kernel_y * p.kernel_w + kernel_x]; + int x_px = p.minor_dim; + int k_px = -p.up_x; + int x_py = p.in_w * p.minor_dim; + int k_py = -p.up_y * p.kernel_w; + + scalar_t v = 0.0f; + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + v += static_cast(*x_p) * static_cast(*k_p); + x_p += x_px; + k_p += k_px; + } + + x_p += x_py - w * x_px; + k_p += k_py - w * k_px; + } + + out[((major_idx * p.out_h + out_y) * p.out_w + out_x) * p.minor_dim + + minor_idx] = v; + } + } +} + +template +__global__ void upfirdn2d_kernel(scalar_t *out, const scalar_t *input, + const scalar_t *kernel, + const UpFirDn2DKernelParams p) { + const int tile_in_h = ((tile_out_h - 1) * down_y + kernel_h - 1) / up_y + 1; + const int tile_in_w = ((tile_out_w - 1) * down_x + kernel_w - 1) / up_x + 1; + + __shared__ volatile float sk[kernel_h][kernel_w]; + __shared__ volatile float sx[tile_in_h][tile_in_w]; + + int minor_idx = blockIdx.x; + int tile_out_y = minor_idx / p.minor_dim; + minor_idx -= tile_out_y * p.minor_dim; + tile_out_y *= tile_out_h; + int tile_out_x_base = blockIdx.y * p.loop_x * tile_out_w; + int major_idx_base = blockIdx.z * p.loop_major; + + if (tile_out_x_base >= p.out_w | tile_out_y >= p.out_h | + major_idx_base >= p.major_dim) { + return; + } + + for (int tap_idx = threadIdx.x; tap_idx < kernel_h * kernel_w; + tap_idx += blockDim.x) { + int ky = tap_idx / kernel_w; + int kx = tap_idx - ky * kernel_w; + scalar_t v = 0.0; + + if (kx < p.kernel_w & ky < p.kernel_h) { + v = kernel[(p.kernel_h - 1 - ky) * p.kernel_w + (p.kernel_w - 1 - kx)]; + } + + sk[ky][kx] = v; + } + + for (int loop_major = 0, major_idx = major_idx_base; + loop_major < p.loop_major & major_idx < p.major_dim; + loop_major++, major_idx++) { + for (int loop_x = 0, tile_out_x = tile_out_x_base; + loop_x < p.loop_x & tile_out_x < p.out_w; + loop_x++, tile_out_x += tile_out_w) { + int tile_mid_x = tile_out_x * down_x + up_x - 1 - p.pad_x0; + int tile_mid_y = tile_out_y * down_y + up_y - 1 - p.pad_y0; + int tile_in_x = floor_div(tile_mid_x, up_x); + int tile_in_y = floor_div(tile_mid_y, up_y); + + __syncthreads(); + + for (int in_idx = threadIdx.x; in_idx < tile_in_h * tile_in_w; + in_idx += blockDim.x) { + int rel_in_y = in_idx / tile_in_w; + int rel_in_x = in_idx - rel_in_y * tile_in_w; + int in_x = rel_in_x + tile_in_x; + int in_y = rel_in_y + tile_in_y; + + scalar_t v = 0.0; + + if (in_x >= 0 & in_y >= 0 & in_x < p.in_w & in_y < p.in_h) { + v = input[((major_idx * p.in_h + in_y) * p.in_w + in_x) * + p.minor_dim + + minor_idx]; + } + + sx[rel_in_y][rel_in_x] = v; + } + + __syncthreads(); + for (int out_idx = threadIdx.x; out_idx < tile_out_h * tile_out_w; + out_idx += blockDim.x) { + int rel_out_y = out_idx / tile_out_w; + int rel_out_x = out_idx - rel_out_y * tile_out_w; + int out_x = rel_out_x + tile_out_x; + int out_y = rel_out_y + tile_out_y; + + int mid_x = tile_mid_x + rel_out_x * down_x; + int mid_y = tile_mid_y + rel_out_y * down_y; + int in_x = floor_div(mid_x, up_x); + int in_y = floor_div(mid_y, up_y); + int rel_in_x = in_x - tile_in_x; + int rel_in_y = in_y - tile_in_y; + int kernel_x = (in_x + 1) * up_x - mid_x - 1; + int kernel_y = (in_y + 1) * up_y - mid_y - 1; + + scalar_t v = 0.0; + +#pragma unroll + for (int y = 0; y < kernel_h / up_y; y++) +#pragma unroll + for (int x = 0; x < kernel_w / up_x; x++) + v += sx[rel_in_y + y][rel_in_x + x] * + sk[kernel_y + y * up_y][kernel_x + x * up_x]; + + if (out_x < p.out_w & out_y < p.out_h) { + out[((major_idx * p.out_h + out_y) * p.out_w + out_x) * p.minor_dim + + minor_idx] = v; + } + } + } + } +} + +torch::Tensor upfirdn2d_op(const torch::Tensor &input, + const torch::Tensor &kernel, int up_x, int up_y, + int down_x, int down_y, int pad_x0, int pad_x1, + int pad_y0, int pad_y1) { + int curDevice = -1; + cudaGetDevice(&curDevice); + cudaStream_t stream = at::cuda::getCurrentCUDAStream(curDevice); + + UpFirDn2DKernelParams p; + + auto x = input.contiguous(); + auto k = kernel.contiguous(); + + p.major_dim = x.size(0); + p.in_h = x.size(1); + p.in_w = x.size(2); + p.minor_dim = x.size(3); + p.kernel_h = k.size(0); + p.kernel_w = k.size(1); + p.up_x = up_x; + p.up_y = up_y; + p.down_x = down_x; + p.down_y = down_y; + p.pad_x0 = pad_x0; + p.pad_x1 = pad_x1; + p.pad_y0 = pad_y0; + p.pad_y1 = pad_y1; + + p.out_h = (p.in_h * p.up_y + p.pad_y0 + p.pad_y1 - p.kernel_h + p.down_y) / + p.down_y; + p.out_w = (p.in_w * p.up_x + p.pad_x0 + p.pad_x1 - p.kernel_w + p.down_x) / + p.down_x; + + auto out = + at::empty({p.major_dim, p.out_h, p.out_w, p.minor_dim}, x.options()); + + int mode = -1; + + int tile_out_h = -1; + int tile_out_w = -1; + + if (p.up_x == 1 && p.up_y == 1 && p.down_x == 1 && p.down_y == 1 && + p.kernel_h <= 4 && p.kernel_w <= 4) { + mode = 1; + tile_out_h = 16; + tile_out_w = 64; + } + + if (p.up_x == 1 && p.up_y == 1 && p.down_x == 1 && p.down_y == 1 && + p.kernel_h <= 3 && p.kernel_w <= 3) { + mode = 2; + tile_out_h = 16; + tile_out_w = 64; + } + + if (p.up_x == 2 && p.up_y == 2 && p.down_x == 1 && p.down_y == 1 && + p.kernel_h <= 4 && p.kernel_w <= 4) { + mode = 3; + tile_out_h = 16; + tile_out_w = 64; + } + + if (p.up_x == 2 && p.up_y == 2 && p.down_x == 1 && p.down_y == 1 && + p.kernel_h <= 2 && p.kernel_w <= 2) { + mode = 4; + tile_out_h = 16; + tile_out_w = 64; + } + + if (p.up_x == 1 && p.up_y == 1 && p.down_x == 2 && p.down_y == 2 && + p.kernel_h <= 4 && p.kernel_w <= 4) { + mode = 5; + tile_out_h = 8; + tile_out_w = 32; + } + + if (p.up_x == 1 && p.up_y == 1 && p.down_x == 2 && p.down_y == 2 && + p.kernel_h <= 2 && p.kernel_w <= 2) { + mode = 6; + tile_out_h = 8; + tile_out_w = 32; + } + + dim3 block_size; + dim3 grid_size; + + if (tile_out_h > 0 && tile_out_w > 0) { + p.loop_major = (p.major_dim - 1) / 16384 + 1; + p.loop_x = 1; + block_size = dim3(32 * 8, 1, 1); + grid_size = dim3(((p.out_h - 1) / tile_out_h + 1) * p.minor_dim, + (p.out_w - 1) / (p.loop_x * tile_out_w) + 1, + (p.major_dim - 1) / p.loop_major + 1); + } else { + p.loop_major = (p.major_dim - 1) / 16384 + 1; + p.loop_x = 4; + block_size = dim3(4, 32, 1); + grid_size = dim3((p.out_h * p.minor_dim - 1) / block_size.x + 1, + (p.out_w - 1) / (p.loop_x * block_size.y) + 1, + (p.major_dim - 1) / p.loop_major + 1); + } + + AT_DISPATCH_FLOATING_TYPES_AND_HALF(x.scalar_type(), "upfirdn2d_cuda", [&] { + switch (mode) { + case 1: + upfirdn2d_kernel + <<>>(out.data_ptr(), + x.data_ptr(), + k.data_ptr(), p); + + break; + + case 2: + upfirdn2d_kernel + <<>>(out.data_ptr(), + x.data_ptr(), + k.data_ptr(), p); + + break; + + case 3: + upfirdn2d_kernel + <<>>(out.data_ptr(), + x.data_ptr(), + k.data_ptr(), p); + + break; + + case 4: + upfirdn2d_kernel + <<>>(out.data_ptr(), + x.data_ptr(), + k.data_ptr(), p); + + break; + + case 5: + upfirdn2d_kernel + <<>>(out.data_ptr(), + x.data_ptr(), + k.data_ptr(), p); + + break; + + case 6: + upfirdn2d_kernel + <<>>(out.data_ptr(), + x.data_ptr(), + k.data_ptr(), p); + + break; + + default: + upfirdn2d_kernel_large<<>>( + out.data_ptr(), x.data_ptr(), + k.data_ptr(), p); + } + }); + + return out; +} diff --git a/repositories/codeformer/basicsr/ops/upfirdn2d/upfirdn2d.py b/repositories/codeformer/basicsr/ops/upfirdn2d/upfirdn2d.py new file mode 100644 index 000000000..667f96e1d --- /dev/null +++ b/repositories/codeformer/basicsr/ops/upfirdn2d/upfirdn2d.py @@ -0,0 +1,186 @@ +# modify from https://github.com/rosinality/stylegan2-pytorch/blob/master/op/upfirdn2d.py # noqa:E501 + +import torch +from torch.autograd import Function +from torch.nn import functional as F + +try: + from . import upfirdn2d_ext +except ImportError: + import os + BASICSR_JIT = os.getenv('BASICSR_JIT') + if BASICSR_JIT == 'True': + from torch.utils.cpp_extension import load + module_path = os.path.dirname(__file__) + upfirdn2d_ext = load( + 'upfirdn2d', + sources=[ + os.path.join(module_path, 'src', 'upfirdn2d.cpp'), + os.path.join(module_path, 'src', 'upfirdn2d_kernel.cu'), + ], + ) + + +class UpFirDn2dBackward(Function): + + @staticmethod + def forward(ctx, grad_output, kernel, grad_kernel, up, down, pad, g_pad, in_size, out_size): + + up_x, up_y = up + down_x, down_y = down + g_pad_x0, g_pad_x1, g_pad_y0, g_pad_y1 = g_pad + + grad_output = grad_output.reshape(-1, out_size[0], out_size[1], 1) + + grad_input = upfirdn2d_ext.upfirdn2d( + grad_output, + grad_kernel, + down_x, + down_y, + up_x, + up_y, + g_pad_x0, + g_pad_x1, + g_pad_y0, + g_pad_y1, + ) + grad_input = grad_input.view(in_size[0], in_size[1], in_size[2], in_size[3]) + + ctx.save_for_backward(kernel) + + pad_x0, pad_x1, pad_y0, pad_y1 = pad + + ctx.up_x = up_x + ctx.up_y = up_y + ctx.down_x = down_x + ctx.down_y = down_y + ctx.pad_x0 = pad_x0 + ctx.pad_x1 = pad_x1 + ctx.pad_y0 = pad_y0 + ctx.pad_y1 = pad_y1 + ctx.in_size = in_size + ctx.out_size = out_size + + return grad_input + + @staticmethod + def backward(ctx, gradgrad_input): + kernel, = ctx.saved_tensors + + gradgrad_input = gradgrad_input.reshape(-1, ctx.in_size[2], ctx.in_size[3], 1) + + gradgrad_out = upfirdn2d_ext.upfirdn2d( + gradgrad_input, + kernel, + ctx.up_x, + ctx.up_y, + ctx.down_x, + ctx.down_y, + ctx.pad_x0, + ctx.pad_x1, + ctx.pad_y0, + ctx.pad_y1, + ) + # gradgrad_out = gradgrad_out.view(ctx.in_size[0], ctx.out_size[0], + # ctx.out_size[1], ctx.in_size[3]) + gradgrad_out = gradgrad_out.view(ctx.in_size[0], ctx.in_size[1], ctx.out_size[0], ctx.out_size[1]) + + return gradgrad_out, None, None, None, None, None, None, None, None + + +class UpFirDn2d(Function): + + @staticmethod + def forward(ctx, input, kernel, up, down, pad): + up_x, up_y = up + down_x, down_y = down + pad_x0, pad_x1, pad_y0, pad_y1 = pad + + kernel_h, kernel_w = kernel.shape + batch, channel, in_h, in_w = input.shape + ctx.in_size = input.shape + + input = input.reshape(-1, in_h, in_w, 1) + + ctx.save_for_backward(kernel, torch.flip(kernel, [0, 1])) + + out_h = (in_h * up_y + pad_y0 + pad_y1 - kernel_h) // down_y + 1 + out_w = (in_w * up_x + pad_x0 + pad_x1 - kernel_w) // down_x + 1 + ctx.out_size = (out_h, out_w) + + ctx.up = (up_x, up_y) + ctx.down = (down_x, down_y) + ctx.pad = (pad_x0, pad_x1, pad_y0, pad_y1) + + g_pad_x0 = kernel_w - pad_x0 - 1 + g_pad_y0 = kernel_h - pad_y0 - 1 + g_pad_x1 = in_w * up_x - out_w * down_x + pad_x0 - up_x + 1 + g_pad_y1 = in_h * up_y - out_h * down_y + pad_y0 - up_y + 1 + + ctx.g_pad = (g_pad_x0, g_pad_x1, g_pad_y0, g_pad_y1) + + out = upfirdn2d_ext.upfirdn2d(input, kernel, up_x, up_y, down_x, down_y, pad_x0, pad_x1, pad_y0, pad_y1) + # out = out.view(major, out_h, out_w, minor) + out = out.view(-1, channel, out_h, out_w) + + return out + + @staticmethod + def backward(ctx, grad_output): + kernel, grad_kernel = ctx.saved_tensors + + grad_input = UpFirDn2dBackward.apply( + grad_output, + kernel, + grad_kernel, + ctx.up, + ctx.down, + ctx.pad, + ctx.g_pad, + ctx.in_size, + ctx.out_size, + ) + + return grad_input, None, None, None, None + + +def upfirdn2d(input, kernel, up=1, down=1, pad=(0, 0)): + if input.device.type == 'cpu': + out = upfirdn2d_native(input, kernel, up, up, down, down, pad[0], pad[1], pad[0], pad[1]) + else: + out = UpFirDn2d.apply(input, kernel, (up, up), (down, down), (pad[0], pad[1], pad[0], pad[1])) + + return out + + +def upfirdn2d_native(input, kernel, up_x, up_y, down_x, down_y, pad_x0, pad_x1, pad_y0, pad_y1): + _, channel, in_h, in_w = input.shape + input = input.reshape(-1, in_h, in_w, 1) + + _, in_h, in_w, minor = input.shape + kernel_h, kernel_w = kernel.shape + + out = input.view(-1, in_h, 1, in_w, 1, minor) + out = F.pad(out, [0, 0, 0, up_x - 1, 0, 0, 0, up_y - 1]) + out = out.view(-1, in_h * up_y, in_w * up_x, minor) + + out = F.pad(out, [0, 0, max(pad_x0, 0), max(pad_x1, 0), max(pad_y0, 0), max(pad_y1, 0)]) + out = out[:, max(-pad_y0, 0):out.shape[1] - max(-pad_y1, 0), max(-pad_x0, 0):out.shape[2] - max(-pad_x1, 0), :, ] + + out = out.permute(0, 3, 1, 2) + out = out.reshape([-1, 1, in_h * up_y + pad_y0 + pad_y1, in_w * up_x + pad_x0 + pad_x1]) + w = torch.flip(kernel, [0, 1]).view(1, 1, kernel_h, kernel_w) + out = F.conv2d(out, w) + out = out.reshape( + -1, + minor, + in_h * up_y + pad_y0 + pad_y1 - kernel_h + 1, + in_w * up_x + pad_x0 + pad_x1 - kernel_w + 1, + ) + out = out.permute(0, 2, 3, 1) + out = out[:, ::down_y, ::down_x, :] + + out_h = (in_h * up_y + pad_y0 + pad_y1 - kernel_h) // down_y + 1 + out_w = (in_w * up_x + pad_x0 + pad_x1 - kernel_w) // down_x + 1 + + return out.view(-1, channel, out_h, out_w) diff --git a/repositories/codeformer/basicsr/setup.py b/repositories/codeformer/basicsr/setup.py new file mode 100644 index 000000000..382a2aa10 --- /dev/null +++ b/repositories/codeformer/basicsr/setup.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python + +from setuptools import find_packages, setup + +import os +import subprocess +import sys +import time +import torch +from torch.utils.cpp_extension import BuildExtension, CppExtension, CUDAExtension + +version_file = './basicsr/version.py' + + +def readme(): + with open('README.md', encoding='utf-8') as f: + content = f.read() + return content + + +def get_git_hash(): + + def _minimal_ext_cmd(cmd): + # construct minimal environment + env = {} + for k in ['SYSTEMROOT', 'PATH', 'HOME']: + v = os.environ.get(k) + if v is not None: + env[k] = v + # LANGUAGE is used on win32 + env['LANGUAGE'] = 'C' + env['LANG'] = 'C' + env['LC_ALL'] = 'C' + out = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=env).communicate()[0] + return out + + try: + out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) + sha = out.strip().decode('ascii') + except OSError: + sha = 'unknown' + + return sha + + +def get_hash(): + if os.path.exists('.git'): + sha = get_git_hash()[:7] + elif os.path.exists(version_file): + try: + from version import __version__ + sha = __version__.split('+')[-1] + except ImportError: + raise ImportError('Unable to get git version') + else: + sha = 'unknown' + + return sha + + +def write_version_py(): + content = """# GENERATED VERSION FILE +# TIME: {} +__version__ = '{}' +__gitsha__ = '{}' +version_info = ({}) +""" + sha = get_hash() + with open('./basicsr/VERSION', 'r') as f: + SHORT_VERSION = f.read().strip() + VERSION_INFO = ', '.join([x if x.isdigit() else f'"{x}"' for x in SHORT_VERSION.split('.')]) + + version_file_str = content.format(time.asctime(), SHORT_VERSION, sha, VERSION_INFO) + with open(version_file, 'w') as f: + f.write(version_file_str) + + +def get_version(): + with open(version_file, 'r') as f: + exec(compile(f.read(), version_file, 'exec')) + return locals()['__version__'] + + +def make_cuda_ext(name, module, sources, sources_cuda=None): + if sources_cuda is None: + sources_cuda = [] + define_macros = [] + extra_compile_args = {'cxx': []} + + if torch.cuda.is_available() or os.getenv('FORCE_CUDA', '0') == '1': + define_macros += [('WITH_CUDA', None)] + extension = CUDAExtension + extra_compile_args['nvcc'] = [ + '-D__CUDA_NO_HALF_OPERATORS__', + '-D__CUDA_NO_HALF_CONVERSIONS__', + '-D__CUDA_NO_HALF2_OPERATORS__', + ] + sources += sources_cuda + else: + print(f'Compiling {name} without CUDA') + extension = CppExtension + + return extension( + name=f'{module}.{name}', + sources=[os.path.join(*module.split('.'), p) for p in sources], + define_macros=define_macros, + extra_compile_args=extra_compile_args) + + +def get_requirements(filename='requirements.txt'): + with open(os.path.join('.', filename), 'r') as f: + requires = [line.replace('\n', '') for line in f.readlines()] + return requires + + +if __name__ == '__main__': + if '--cuda_ext' in sys.argv: + ext_modules = [ + make_cuda_ext( + name='deform_conv_ext', + module='ops.dcn', + sources=['src/deform_conv_ext.cpp'], + sources_cuda=['src/deform_conv_cuda.cpp', 'src/deform_conv_cuda_kernel.cu']), + make_cuda_ext( + name='fused_act_ext', + module='ops.fused_act', + sources=['src/fused_bias_act.cpp'], + sources_cuda=['src/fused_bias_act_kernel.cu']), + make_cuda_ext( + name='upfirdn2d_ext', + module='ops.upfirdn2d', + sources=['src/upfirdn2d.cpp'], + sources_cuda=['src/upfirdn2d_kernel.cu']), + ] + sys.argv.remove('--cuda_ext') + else: + ext_modules = [] + + write_version_py() + setup( + name='basicsr', + version=get_version(), + description='Open Source Image and Video Super-Resolution Toolbox', + long_description=readme(), + long_description_content_type='text/markdown', + author='Xintao Wang', + author_email='xintao.wang@outlook.com', + keywords='computer vision, restoration, super resolution', + url='https://github.com/xinntao/BasicSR', + include_package_data=True, + packages=find_packages(exclude=('options', 'datasets', 'experiments', 'results', 'tb_logger', 'wandb')), + classifiers=[ + 'Development Status :: 4 - Beta', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: OS Independent', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + ], + license='Apache License 2.0', + setup_requires=['cython', 'numpy'], + install_requires=get_requirements(), + ext_modules=ext_modules, + cmdclass={'build_ext': BuildExtension}, + zip_safe=False) diff --git a/repositories/codeformer/basicsr/train.py b/repositories/codeformer/basicsr/train.py new file mode 100644 index 000000000..a01c0dfcc --- /dev/null +++ b/repositories/codeformer/basicsr/train.py @@ -0,0 +1,225 @@ +import argparse +import datetime +import logging +import math +import copy +import random +import time +import torch +from os import path as osp + +from basicsr.data import build_dataloader, build_dataset +from basicsr.data.data_sampler import EnlargedSampler +from basicsr.data.prefetch_dataloader import CPUPrefetcher, CUDAPrefetcher +from basicsr.models import build_model +from basicsr.utils import (MessageLogger, check_resume, get_env_info, get_root_logger, init_tb_logger, + init_wandb_logger, make_exp_dirs, mkdir_and_rename, set_random_seed) +from basicsr.utils.dist_util import get_dist_info, init_dist +from basicsr.utils.options import dict2str, parse + +import warnings +# ignore UserWarning: Detected call of `lr_scheduler.step()` before `optimizer.step()`. +warnings.filterwarnings("ignore", category=UserWarning) + +def parse_options(root_path, is_train=True): + parser = argparse.ArgumentParser() + parser.add_argument('-opt', type=str, required=True, help='Path to option YAML file.') + parser.add_argument('--launcher', choices=['none', 'pytorch', 'slurm'], default='none', help='job launcher') + parser.add_argument('--local_rank', type=int, default=0) + args = parser.parse_args() + opt = parse(args.opt, root_path, is_train=is_train) + + # distributed settings + if args.launcher == 'none': + opt['dist'] = False + print('Disable distributed.', flush=True) + else: + opt['dist'] = True + if args.launcher == 'slurm' and 'dist_params' in opt: + init_dist(args.launcher, **opt['dist_params']) + else: + init_dist(args.launcher) + + opt['rank'], opt['world_size'] = get_dist_info() + + # random seed + seed = opt.get('manual_seed') + if seed is None: + seed = random.randint(1, 10000) + opt['manual_seed'] = seed + set_random_seed(seed + opt['rank']) + + return opt + + +def init_loggers(opt): + log_file = osp.join(opt['path']['log'], f"train_{opt['name']}.log") + logger = get_root_logger(logger_name='basicsr', log_level=logging.INFO, log_file=log_file) + logger.info(get_env_info()) + logger.info(dict2str(opt)) + + # initialize wandb logger before tensorboard logger to allow proper sync: + if (opt['logger'].get('wandb') is not None) and (opt['logger']['wandb'].get('project') is not None): + assert opt['logger'].get('use_tb_logger') is True, ('should turn on tensorboard when using wandb') + init_wandb_logger(opt) + tb_logger = None + if opt['logger'].get('use_tb_logger'): + tb_logger = init_tb_logger(log_dir=osp.join('tb_logger', opt['name'])) + return logger, tb_logger + + +def create_train_val_dataloader(opt, logger): + # create train and val dataloaders + train_loader, val_loader = None, None + for phase, dataset_opt in opt['datasets'].items(): + if phase == 'train': + dataset_enlarge_ratio = dataset_opt.get('dataset_enlarge_ratio', 1) + train_set = build_dataset(dataset_opt) + train_sampler = EnlargedSampler(train_set, opt['world_size'], opt['rank'], dataset_enlarge_ratio) + train_loader = build_dataloader( + train_set, + dataset_opt, + num_gpu=opt['num_gpu'], + dist=opt['dist'], + sampler=train_sampler, + seed=opt['manual_seed']) + + num_iter_per_epoch = math.ceil( + len(train_set) * dataset_enlarge_ratio / (dataset_opt['batch_size_per_gpu'] * opt['world_size'])) + total_iters = int(opt['train']['total_iter']) + total_epochs = math.ceil(total_iters / (num_iter_per_epoch)) + logger.info('Training statistics:' + f'\n\tNumber of train images: {len(train_set)}' + f'\n\tDataset enlarge ratio: {dataset_enlarge_ratio}' + f'\n\tBatch size per gpu: {dataset_opt["batch_size_per_gpu"]}' + f'\n\tWorld size (gpu number): {opt["world_size"]}' + f'\n\tRequire iter number per epoch: {num_iter_per_epoch}' + f'\n\tTotal epochs: {total_epochs}; iters: {total_iters}.') + + elif phase == 'val': + val_set = build_dataset(dataset_opt) + val_loader = build_dataloader( + val_set, dataset_opt, num_gpu=opt['num_gpu'], dist=opt['dist'], sampler=None, seed=opt['manual_seed']) + logger.info(f'Number of val images/folders in {dataset_opt["name"]}: ' f'{len(val_set)}') + else: + raise ValueError(f'Dataset phase {phase} is not recognized.') + + return train_loader, train_sampler, val_loader, total_epochs, total_iters + + +def train_pipeline(root_path): + # parse options, set distributed setting, set ramdom seed + opt = parse_options(root_path, is_train=True) + + torch.backends.cudnn.benchmark = True + # torch.backends.cudnn.deterministic = True + + # load resume states if necessary + if opt['path'].get('resume_state'): + device_id = torch.cuda.current_device() + resume_state = torch.load( + opt['path']['resume_state'], map_location=lambda storage, loc: storage.cuda(device_id)) + else: + resume_state = None + + # mkdir for experiments and logger + if resume_state is None: + make_exp_dirs(opt) + if opt['logger'].get('use_tb_logger') and opt['rank'] == 0: + mkdir_and_rename(osp.join('tb_logger', opt['name'])) + + # initialize loggers + logger, tb_logger = init_loggers(opt) + + # create train and validation dataloaders + result = create_train_val_dataloader(opt, logger) + train_loader, train_sampler, val_loader, total_epochs, total_iters = result + + # create model + if resume_state: # resume training + check_resume(opt, resume_state['iter']) + model = build_model(opt) + model.resume_training(resume_state) # handle optimizers and schedulers + logger.info(f"Resuming training from epoch: {resume_state['epoch']}, " f"iter: {resume_state['iter']}.") + start_epoch = resume_state['epoch'] + current_iter = resume_state['iter'] + else: + model = build_model(opt) + start_epoch = 0 + current_iter = 0 + + # create message logger (formatted outputs) + msg_logger = MessageLogger(opt, current_iter, tb_logger) + + # dataloader prefetcher + prefetch_mode = opt['datasets']['train'].get('prefetch_mode') + if prefetch_mode is None or prefetch_mode == 'cpu': + prefetcher = CPUPrefetcher(train_loader) + elif prefetch_mode == 'cuda': + prefetcher = CUDAPrefetcher(train_loader, opt) + logger.info(f'Use {prefetch_mode} prefetch dataloader') + if opt['datasets']['train'].get('pin_memory') is not True: + raise ValueError('Please set pin_memory=True for CUDAPrefetcher.') + else: + raise ValueError(f'Wrong prefetch_mode {prefetch_mode}.' "Supported ones are: None, 'cuda', 'cpu'.") + + # training + logger.info(f'Start training from epoch: {start_epoch}, iter: {current_iter+1}') + data_time, iter_time = time.time(), time.time() + start_time = time.time() + + for epoch in range(start_epoch, total_epochs + 1): + train_sampler.set_epoch(epoch) + prefetcher.reset() + train_data = prefetcher.next() + + while train_data is not None: + data_time = time.time() - data_time + + current_iter += 1 + if current_iter > total_iters: + break + # update learning rate + model.update_learning_rate(current_iter, warmup_iter=opt['train'].get('warmup_iter', -1)) + # training + model.feed_data(train_data) + model.optimize_parameters(current_iter) + iter_time = time.time() - iter_time + # log + if current_iter % opt['logger']['print_freq'] == 0: + log_vars = {'epoch': epoch, 'iter': current_iter} + log_vars.update({'lrs': model.get_current_learning_rate()}) + log_vars.update({'time': iter_time, 'data_time': data_time}) + log_vars.update(model.get_current_log()) + msg_logger(log_vars) + + # save models and training states + if current_iter % opt['logger']['save_checkpoint_freq'] == 0: + logger.info('Saving models and training states.') + model.save(epoch, current_iter) + + # validation + if opt.get('val') is not None and opt['datasets'].get('val') is not None \ + and (current_iter % opt['val']['val_freq'] == 0): + model.validation(val_loader, current_iter, tb_logger, opt['val']['save_img']) + + data_time = time.time() + iter_time = time.time() + train_data = prefetcher.next() + # end of iter + + # end of epoch + + consumed_time = str(datetime.timedelta(seconds=int(time.time() - start_time))) + logger.info(f'End of training. Time consumed: {consumed_time}') + logger.info('Save the latest model.') + model.save(epoch=-1, current_iter=-1) # -1 stands for the latest + if opt.get('val') is not None and opt['datasets'].get('val'): + model.validation(val_loader, current_iter, tb_logger, opt['val']['save_img']) + if tb_logger: + tb_logger.close() + + +if __name__ == '__main__': + root_path = osp.abspath(osp.join(__file__, osp.pardir, osp.pardir)) + train_pipeline(root_path) diff --git a/repositories/codeformer/basicsr/utils/__init__.py b/repositories/codeformer/basicsr/utils/__init__.py new file mode 100644 index 000000000..5fcc1d540 --- /dev/null +++ b/repositories/codeformer/basicsr/utils/__init__.py @@ -0,0 +1,29 @@ +from .file_client import FileClient +from .img_util import crop_border, imfrombytes, img2tensor, imwrite, tensor2img +from .logger import MessageLogger, get_env_info, get_root_logger, init_tb_logger, init_wandb_logger +from .misc import check_resume, get_time_str, make_exp_dirs, mkdir_and_rename, scandir, set_random_seed, sizeof_fmt + +__all__ = [ + # file_client.py + 'FileClient', + # img_util.py + 'img2tensor', + 'tensor2img', + 'imfrombytes', + 'imwrite', + 'crop_border', + # logger.py + 'MessageLogger', + 'init_tb_logger', + 'init_wandb_logger', + 'get_root_logger', + 'get_env_info', + # misc.py + 'set_random_seed', + 'get_time_str', + 'mkdir_and_rename', + 'make_exp_dirs', + 'scandir', + 'check_resume', + 'sizeof_fmt' +] diff --git a/repositories/codeformer/basicsr/utils/dist_util.py b/repositories/codeformer/basicsr/utils/dist_util.py new file mode 100644 index 000000000..0fab887b2 --- /dev/null +++ b/repositories/codeformer/basicsr/utils/dist_util.py @@ -0,0 +1,82 @@ +# Modified from https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/dist_utils.py # noqa: E501 +import functools +import os +import subprocess +import torch +import torch.distributed as dist +import torch.multiprocessing as mp + + +def init_dist(launcher, backend='nccl', **kwargs): + if mp.get_start_method(allow_none=True) is None: + mp.set_start_method('spawn') + if launcher == 'pytorch': + _init_dist_pytorch(backend, **kwargs) + elif launcher == 'slurm': + _init_dist_slurm(backend, **kwargs) + else: + raise ValueError(f'Invalid launcher type: {launcher}') + + +def _init_dist_pytorch(backend, **kwargs): + rank = int(os.environ['RANK']) + num_gpus = torch.cuda.device_count() + torch.cuda.set_device(rank % num_gpus) + dist.init_process_group(backend=backend, **kwargs) + + +def _init_dist_slurm(backend, port=None): + """Initialize slurm distributed training environment. + + If argument ``port`` is not specified, then the master port will be system + environment variable ``MASTER_PORT``. If ``MASTER_PORT`` is not in system + environment variable, then a default port ``29500`` will be used. + + Args: + backend (str): Backend of torch.distributed. + port (int, optional): Master port. Defaults to None. + """ + proc_id = int(os.environ['SLURM_PROCID']) + ntasks = int(os.environ['SLURM_NTASKS']) + node_list = os.environ['SLURM_NODELIST'] + num_gpus = torch.cuda.device_count() + torch.cuda.set_device(proc_id % num_gpus) + addr = subprocess.getoutput(f'scontrol show hostname {node_list} | head -n1') + # specify master port + if port is not None: + os.environ['MASTER_PORT'] = str(port) + elif 'MASTER_PORT' in os.environ: + pass # use MASTER_PORT in the environment variable + else: + # 29500 is torch.distributed default port + os.environ['MASTER_PORT'] = '29500' + os.environ['MASTER_ADDR'] = addr + os.environ['WORLD_SIZE'] = str(ntasks) + os.environ['LOCAL_RANK'] = str(proc_id % num_gpus) + os.environ['RANK'] = str(proc_id) + dist.init_process_group(backend=backend) + + +def get_dist_info(): + if dist.is_available(): + initialized = dist.is_initialized() + else: + initialized = False + if initialized: + rank = dist.get_rank() + world_size = dist.get_world_size() + else: + rank = 0 + world_size = 1 + return rank, world_size + + +def master_only(func): + + @functools.wraps(func) + def wrapper(*args, **kwargs): + rank, _ = get_dist_info() + if rank == 0: + return func(*args, **kwargs) + + return wrapper diff --git a/repositories/codeformer/basicsr/utils/download_util.py b/repositories/codeformer/basicsr/utils/download_util.py new file mode 100644 index 000000000..2a2679157 --- /dev/null +++ b/repositories/codeformer/basicsr/utils/download_util.py @@ -0,0 +1,95 @@ +import math +import os +import requests +from torch.hub import download_url_to_file, get_dir +from tqdm import tqdm +from urllib.parse import urlparse + +from .misc import sizeof_fmt + + +def download_file_from_google_drive(file_id, save_path): + """Download files from google drive. + Ref: + https://stackoverflow.com/questions/25010369/wget-curl-large-file-from-google-drive # noqa E501 + Args: + file_id (str): File id. + save_path (str): Save path. + """ + + session = requests.Session() + URL = 'https://docs.google.com/uc?export=download' + params = {'id': file_id} + + response = session.get(URL, params=params, stream=True) + token = get_confirm_token(response) + if token: + params['confirm'] = token + response = session.get(URL, params=params, stream=True) + + # get file size + response_file_size = session.get(URL, params=params, stream=True, headers={'Range': 'bytes=0-2'}) + print(response_file_size) + if 'Content-Range' in response_file_size.headers: + file_size = int(response_file_size.headers['Content-Range'].split('/')[1]) + else: + file_size = None + + save_response_content(response, save_path, file_size) + + +def get_confirm_token(response): + for key, value in response.cookies.items(): + if key.startswith('download_warning'): + return value + return None + + +def save_response_content(response, destination, file_size=None, chunk_size=32768): + if file_size is not None: + pbar = tqdm(total=math.ceil(file_size / chunk_size), unit='chunk') + + readable_file_size = sizeof_fmt(file_size) + else: + pbar = None + + with open(destination, 'wb') as f: + downloaded_size = 0 + for chunk in response.iter_content(chunk_size): + downloaded_size += chunk_size + if pbar is not None: + pbar.update(1) + pbar.set_description(f'Download {sizeof_fmt(downloaded_size)} / {readable_file_size}') + if chunk: # filter out keep-alive new chunks + f.write(chunk) + if pbar is not None: + pbar.close() + + +def load_file_from_url(url, model_dir=None, progress=True, file_name=None): + """Load file form http url, will download models if necessary. + Ref:https://github.com/1adrianb/face-alignment/blob/master/face_alignment/utils.py + Args: + url (str): URL to be downloaded. + model_dir (str): The path to save the downloaded model. Should be a full path. If None, use pytorch hub_dir. + Default: None. + progress (bool): Whether to show the download progress. Default: True. + file_name (str): The downloaded file name. If None, use the file name in the url. Default: None. + Returns: + str: The path to the downloaded file. + """ + if model_dir is None: # use the pytorch hub_dir + hub_dir = get_dir() + model_dir = os.path.join(hub_dir, 'checkpoints') + + os.makedirs(model_dir, exist_ok=True) + + parts = urlparse(url) + filename = os.path.basename(parts.path) + if file_name is not None: + filename = file_name + cached_file = os.path.abspath(os.path.join(model_dir, filename)) + if not os.path.exists(cached_file): + print(f'Downloading: "{url}" to {cached_file}\n') + download_url_to_file(url, cached_file, hash_prefix=None, progress=progress) + return cached_file \ No newline at end of file diff --git a/repositories/codeformer/basicsr/utils/file_client.py b/repositories/codeformer/basicsr/utils/file_client.py new file mode 100644 index 000000000..7f38d9796 --- /dev/null +++ b/repositories/codeformer/basicsr/utils/file_client.py @@ -0,0 +1,167 @@ +# Modified from https://github.com/open-mmlab/mmcv/blob/master/mmcv/fileio/file_client.py # noqa: E501 +from abc import ABCMeta, abstractmethod + + +class BaseStorageBackend(metaclass=ABCMeta): + """Abstract class of storage backends. + + All backends need to implement two apis: ``get()`` and ``get_text()``. + ``get()`` reads the file as a byte stream and ``get_text()`` reads the file + as texts. + """ + + @abstractmethod + def get(self, filepath): + pass + + @abstractmethod + def get_text(self, filepath): + pass + + +class MemcachedBackend(BaseStorageBackend): + """Memcached storage backend. + + Attributes: + server_list_cfg (str): Config file for memcached server list. + client_cfg (str): Config file for memcached client. + sys_path (str | None): Additional path to be appended to `sys.path`. + Default: None. + """ + + def __init__(self, server_list_cfg, client_cfg, sys_path=None): + if sys_path is not None: + import sys + sys.path.append(sys_path) + try: + import mc + except ImportError: + raise ImportError('Please install memcached to enable MemcachedBackend.') + + self.server_list_cfg = server_list_cfg + self.client_cfg = client_cfg + self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, self.client_cfg) + # mc.pyvector servers as a point which points to a memory cache + self._mc_buffer = mc.pyvector() + + def get(self, filepath): + filepath = str(filepath) + import mc + self._client.Get(filepath, self._mc_buffer) + value_buf = mc.ConvertBuffer(self._mc_buffer) + return value_buf + + def get_text(self, filepath): + raise NotImplementedError + + +class HardDiskBackend(BaseStorageBackend): + """Raw hard disks storage backend.""" + + def get(self, filepath): + filepath = str(filepath) + with open(filepath, 'rb') as f: + value_buf = f.read() + return value_buf + + def get_text(self, filepath): + filepath = str(filepath) + with open(filepath, 'r') as f: + value_buf = f.read() + return value_buf + + +class LmdbBackend(BaseStorageBackend): + """Lmdb storage backend. + + Args: + db_paths (str | list[str]): Lmdb database paths. + client_keys (str | list[str]): Lmdb client keys. Default: 'default'. + readonly (bool, optional): Lmdb environment parameter. If True, + disallow any write operations. Default: True. + lock (bool, optional): Lmdb environment parameter. If False, when + concurrent access occurs, do not lock the database. Default: False. + readahead (bool, optional): Lmdb environment parameter. If False, + disable the OS filesystem readahead mechanism, which may improve + random read performance when a database is larger than RAM. + Default: False. + + Attributes: + db_paths (list): Lmdb database path. + _client (list): A list of several lmdb envs. + """ + + def __init__(self, db_paths, client_keys='default', readonly=True, lock=False, readahead=False, **kwargs): + try: + import lmdb + except ImportError: + raise ImportError('Please install lmdb to enable LmdbBackend.') + + if isinstance(client_keys, str): + client_keys = [client_keys] + + if isinstance(db_paths, list): + self.db_paths = [str(v) for v in db_paths] + elif isinstance(db_paths, str): + self.db_paths = [str(db_paths)] + assert len(client_keys) == len(self.db_paths), ('client_keys and db_paths should have the same length, ' + f'but received {len(client_keys)} and {len(self.db_paths)}.') + + self._client = {} + for client, path in zip(client_keys, self.db_paths): + self._client[client] = lmdb.open(path, readonly=readonly, lock=lock, readahead=readahead, **kwargs) + + def get(self, filepath, client_key): + """Get values according to the filepath from one lmdb named client_key. + + Args: + filepath (str | obj:`Path`): Here, filepath is the lmdb key. + client_key (str): Used for distinguishing differnet lmdb envs. + """ + filepath = str(filepath) + assert client_key in self._client, (f'client_key {client_key} is not ' 'in lmdb clients.') + client = self._client[client_key] + with client.begin(write=False) as txn: + value_buf = txn.get(filepath.encode('ascii')) + return value_buf + + def get_text(self, filepath): + raise NotImplementedError + + +class FileClient(object): + """A general file client to access files in different backend. + + The client loads a file or text in a specified backend from its path + and return it as a binary file. it can also register other backend + accessor with a given name and backend class. + + Attributes: + backend (str): The storage backend type. Options are "disk", + "memcached" and "lmdb". + client (:obj:`BaseStorageBackend`): The backend object. + """ + + _backends = { + 'disk': HardDiskBackend, + 'memcached': MemcachedBackend, + 'lmdb': LmdbBackend, + } + + def __init__(self, backend='disk', **kwargs): + if backend not in self._backends: + raise ValueError(f'Backend {backend} is not supported. Currently supported ones' + f' are {list(self._backends.keys())}') + self.backend = backend + self.client = self._backends[backend](**kwargs) + + def get(self, filepath, client_key='default'): + # client_key is used only for lmdb, where different fileclients have + # different lmdb environments. + if self.backend == 'lmdb': + return self.client.get(filepath, client_key) + else: + return self.client.get(filepath) + + def get_text(self, filepath): + return self.client.get_text(filepath) diff --git a/repositories/codeformer/basicsr/utils/img_util.py b/repositories/codeformer/basicsr/utils/img_util.py new file mode 100644 index 000000000..5aba82ce0 --- /dev/null +++ b/repositories/codeformer/basicsr/utils/img_util.py @@ -0,0 +1,171 @@ +import cv2 +import math +import numpy as np +import os +import torch +from torchvision.utils import make_grid + + +def img2tensor(imgs, bgr2rgb=True, float32=True): + """Numpy array to tensor. + + Args: + imgs (list[ndarray] | ndarray): Input images. + bgr2rgb (bool): Whether to change bgr to rgb. + float32 (bool): Whether to change to float32. + + Returns: + list[tensor] | tensor: Tensor images. If returned results only have + one element, just return tensor. + """ + + def _totensor(img, bgr2rgb, float32): + if img.shape[2] == 3 and bgr2rgb: + if img.dtype == 'float64': + img = img.astype('float32') + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + img = torch.from_numpy(img.transpose(2, 0, 1)) + if float32: + img = img.float() + return img + + if isinstance(imgs, list): + return [_totensor(img, bgr2rgb, float32) for img in imgs] + else: + return _totensor(imgs, bgr2rgb, float32) + + +def tensor2img(tensor, rgb2bgr=True, out_type=np.uint8, min_max=(0, 1)): + """Convert torch Tensors into image numpy arrays. + + After clamping to [min, max], values will be normalized to [0, 1]. + + Args: + tensor (Tensor or list[Tensor]): Accept shapes: + 1) 4D mini-batch Tensor of shape (B x 3/1 x H x W); + 2) 3D Tensor of shape (3/1 x H x W); + 3) 2D Tensor of shape (H x W). + Tensor channel should be in RGB order. + rgb2bgr (bool): Whether to change rgb to bgr. + out_type (numpy type): output types. If ``np.uint8``, transform outputs + to uint8 type with range [0, 255]; otherwise, float type with + range [0, 1]. Default: ``np.uint8``. + min_max (tuple[int]): min and max values for clamp. + + Returns: + (Tensor or list): 3D ndarray of shape (H x W x C) OR 2D ndarray of + shape (H x W). The channel order is BGR. + """ + if not (torch.is_tensor(tensor) or (isinstance(tensor, list) and all(torch.is_tensor(t) for t in tensor))): + raise TypeError(f'tensor or list of tensors expected, got {type(tensor)}') + + if torch.is_tensor(tensor): + tensor = [tensor] + result = [] + for _tensor in tensor: + _tensor = _tensor.squeeze(0).float().detach().cpu().clamp_(*min_max) + _tensor = (_tensor - min_max[0]) / (min_max[1] - min_max[0]) + + n_dim = _tensor.dim() + if n_dim == 4: + img_np = make_grid(_tensor, nrow=int(math.sqrt(_tensor.size(0))), normalize=False).numpy() + img_np = img_np.transpose(1, 2, 0) + if rgb2bgr: + img_np = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR) + elif n_dim == 3: + img_np = _tensor.numpy() + img_np = img_np.transpose(1, 2, 0) + if img_np.shape[2] == 1: # gray image + img_np = np.squeeze(img_np, axis=2) + else: + if rgb2bgr: + img_np = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR) + elif n_dim == 2: + img_np = _tensor.numpy() + else: + raise TypeError('Only support 4D, 3D or 2D tensor. ' f'But received with dimension: {n_dim}') + if out_type == np.uint8: + # Unlike MATLAB, numpy.unit8() WILL NOT round by default. + img_np = (img_np * 255.0).round() + img_np = img_np.astype(out_type) + result.append(img_np) + if len(result) == 1: + result = result[0] + return result + + +def tensor2img_fast(tensor, rgb2bgr=True, min_max=(0, 1)): + """This implementation is slightly faster than tensor2img. + It now only supports torch tensor with shape (1, c, h, w). + + Args: + tensor (Tensor): Now only support torch tensor with (1, c, h, w). + rgb2bgr (bool): Whether to change rgb to bgr. Default: True. + min_max (tuple[int]): min and max values for clamp. + """ + output = tensor.squeeze(0).detach().clamp_(*min_max).permute(1, 2, 0) + output = (output - min_max[0]) / (min_max[1] - min_max[0]) * 255 + output = output.type(torch.uint8).cpu().numpy() + if rgb2bgr: + output = cv2.cvtColor(output, cv2.COLOR_RGB2BGR) + return output + + +def imfrombytes(content, flag='color', float32=False): + """Read an image from bytes. + + Args: + content (bytes): Image bytes got from files or other streams. + flag (str): Flags specifying the color type of a loaded image, + candidates are `color`, `grayscale` and `unchanged`. + float32 (bool): Whether to change to float32., If True, will also norm + to [0, 1]. Default: False. + + Returns: + ndarray: Loaded image array. + """ + img_np = np.frombuffer(content, np.uint8) + imread_flags = {'color': cv2.IMREAD_COLOR, 'grayscale': cv2.IMREAD_GRAYSCALE, 'unchanged': cv2.IMREAD_UNCHANGED} + img = cv2.imdecode(img_np, imread_flags[flag]) + if float32: + img = img.astype(np.float32) / 255. + return img + + +def imwrite(img, file_path, params=None, auto_mkdir=True): + """Write image to file. + + Args: + img (ndarray): Image array to be written. + file_path (str): Image file path. + params (None or list): Same as opencv's :func:`imwrite` interface. + auto_mkdir (bool): If the parent folder of `file_path` does not exist, + whether to create it automatically. + + Returns: + bool: Successful or not. + """ + if auto_mkdir: + dir_name = os.path.abspath(os.path.dirname(file_path)) + os.makedirs(dir_name, exist_ok=True) + return cv2.imwrite(file_path, img, params) + + +def crop_border(imgs, crop_border): + """Crop borders of images. + + Args: + imgs (list[ndarray] | ndarray): Images with shape (h, w, c). + crop_border (int): Crop border for each end of height and weight. + + Returns: + list[ndarray]: Cropped images. + """ + if crop_border == 0: + return imgs + else: + if isinstance(imgs, list): + return [v[crop_border:-crop_border, crop_border:-crop_border, ...] for v in imgs] + else: + return imgs[crop_border:-crop_border, crop_border:-crop_border, ...] + \ No newline at end of file diff --git a/repositories/codeformer/basicsr/utils/lmdb_util.py b/repositories/codeformer/basicsr/utils/lmdb_util.py new file mode 100644 index 000000000..e0a10f60f --- /dev/null +++ b/repositories/codeformer/basicsr/utils/lmdb_util.py @@ -0,0 +1,196 @@ +import cv2 +import lmdb +import sys +from multiprocessing import Pool +from os import path as osp +from tqdm import tqdm + + +def make_lmdb_from_imgs(data_path, + lmdb_path, + img_path_list, + keys, + batch=5000, + compress_level=1, + multiprocessing_read=False, + n_thread=40, + map_size=None): + """Make lmdb from images. + + Contents of lmdb. The file structure is: + example.lmdb + ├── data.mdb + ├── lock.mdb + ├── meta_info.txt + + The data.mdb and lock.mdb are standard lmdb files and you can refer to + https://lmdb.readthedocs.io/en/release/ for more details. + + The meta_info.txt is a specified txt file to record the meta information + of our datasets. It will be automatically created when preparing + datasets by our provided dataset tools. + Each line in the txt file records 1)image name (with extension), + 2)image shape, and 3)compression level, separated by a white space. + + For example, the meta information could be: + `000_00000000.png (720,1280,3) 1`, which means: + 1) image name (with extension): 000_00000000.png; + 2) image shape: (720,1280,3); + 3) compression level: 1 + + We use the image name without extension as the lmdb key. + + If `multiprocessing_read` is True, it will read all the images to memory + using multiprocessing. Thus, your server needs to have enough memory. + + Args: + data_path (str): Data path for reading images. + lmdb_path (str): Lmdb save path. + img_path_list (str): Image path list. + keys (str): Used for lmdb keys. + batch (int): After processing batch images, lmdb commits. + Default: 5000. + compress_level (int): Compress level when encoding images. Default: 1. + multiprocessing_read (bool): Whether use multiprocessing to read all + the images to memory. Default: False. + n_thread (int): For multiprocessing. + map_size (int | None): Map size for lmdb env. If None, use the + estimated size from images. Default: None + """ + + assert len(img_path_list) == len(keys), ('img_path_list and keys should have the same length, ' + f'but got {len(img_path_list)} and {len(keys)}') + print(f'Create lmdb for {data_path}, save to {lmdb_path}...') + print(f'Totoal images: {len(img_path_list)}') + if not lmdb_path.endswith('.lmdb'): + raise ValueError("lmdb_path must end with '.lmdb'.") + if osp.exists(lmdb_path): + print(f'Folder {lmdb_path} already exists. Exit.') + sys.exit(1) + + if multiprocessing_read: + # read all the images to memory (multiprocessing) + dataset = {} # use dict to keep the order for multiprocessing + shapes = {} + print(f'Read images with multiprocessing, #thread: {n_thread} ...') + pbar = tqdm(total=len(img_path_list), unit='image') + + def callback(arg): + """get the image data and update pbar.""" + key, dataset[key], shapes[key] = arg + pbar.update(1) + pbar.set_description(f'Read {key}') + + pool = Pool(n_thread) + for path, key in zip(img_path_list, keys): + pool.apply_async(read_img_worker, args=(osp.join(data_path, path), key, compress_level), callback=callback) + pool.close() + pool.join() + pbar.close() + print(f'Finish reading {len(img_path_list)} images.') + + # create lmdb environment + if map_size is None: + # obtain data size for one image + img = cv2.imread(osp.join(data_path, img_path_list[0]), cv2.IMREAD_UNCHANGED) + _, img_byte = cv2.imencode('.png', img, [cv2.IMWRITE_PNG_COMPRESSION, compress_level]) + data_size_per_img = img_byte.nbytes + print('Data size per image is: ', data_size_per_img) + data_size = data_size_per_img * len(img_path_list) + map_size = data_size * 10 + + env = lmdb.open(lmdb_path, map_size=map_size) + + # write data to lmdb + pbar = tqdm(total=len(img_path_list), unit='chunk') + txn = env.begin(write=True) + txt_file = open(osp.join(lmdb_path, 'meta_info.txt'), 'w') + for idx, (path, key) in enumerate(zip(img_path_list, keys)): + pbar.update(1) + pbar.set_description(f'Write {key}') + key_byte = key.encode('ascii') + if multiprocessing_read: + img_byte = dataset[key] + h, w, c = shapes[key] + else: + _, img_byte, img_shape = read_img_worker(osp.join(data_path, path), key, compress_level) + h, w, c = img_shape + + txn.put(key_byte, img_byte) + # write meta information + txt_file.write(f'{key}.png ({h},{w},{c}) {compress_level}\n') + if idx % batch == 0: + txn.commit() + txn = env.begin(write=True) + pbar.close() + txn.commit() + env.close() + txt_file.close() + print('\nFinish writing lmdb.') + + +def read_img_worker(path, key, compress_level): + """Read image worker. + + Args: + path (str): Image path. + key (str): Image key. + compress_level (int): Compress level when encoding images. + + Returns: + str: Image key. + byte: Image byte. + tuple[int]: Image shape. + """ + + img = cv2.imread(path, cv2.IMREAD_UNCHANGED) + if img.ndim == 2: + h, w = img.shape + c = 1 + else: + h, w, c = img.shape + _, img_byte = cv2.imencode('.png', img, [cv2.IMWRITE_PNG_COMPRESSION, compress_level]) + return (key, img_byte, (h, w, c)) + + +class LmdbMaker(): + """LMDB Maker. + + Args: + lmdb_path (str): Lmdb save path. + map_size (int): Map size for lmdb env. Default: 1024 ** 4, 1TB. + batch (int): After processing batch images, lmdb commits. + Default: 5000. + compress_level (int): Compress level when encoding images. Default: 1. + """ + + def __init__(self, lmdb_path, map_size=1024**4, batch=5000, compress_level=1): + if not lmdb_path.endswith('.lmdb'): + raise ValueError("lmdb_path must end with '.lmdb'.") + if osp.exists(lmdb_path): + print(f'Folder {lmdb_path} already exists. Exit.') + sys.exit(1) + + self.lmdb_path = lmdb_path + self.batch = batch + self.compress_level = compress_level + self.env = lmdb.open(lmdb_path, map_size=map_size) + self.txn = self.env.begin(write=True) + self.txt_file = open(osp.join(lmdb_path, 'meta_info.txt'), 'w') + self.counter = 0 + + def put(self, img_byte, key, img_shape): + self.counter += 1 + key_byte = key.encode('ascii') + self.txn.put(key_byte, img_byte) + # write meta information + h, w, c = img_shape + self.txt_file.write(f'{key}.png ({h},{w},{c}) {self.compress_level}\n') + if self.counter % self.batch == 0: + self.txn.commit() + self.txn = self.env.begin(write=True) + + def close(self): + self.txn.commit() + self.env.close() + self.txt_file.close() diff --git a/repositories/codeformer/basicsr/utils/logger.py b/repositories/codeformer/basicsr/utils/logger.py new file mode 100644 index 000000000..9714bf59c --- /dev/null +++ b/repositories/codeformer/basicsr/utils/logger.py @@ -0,0 +1,169 @@ +import datetime +import logging +import time + +from .dist_util import get_dist_info, master_only + +initialized_logger = {} + + +class MessageLogger(): + """Message logger for printing. + Args: + opt (dict): Config. It contains the following keys: + name (str): Exp name. + logger (dict): Contains 'print_freq' (str) for logger interval. + train (dict): Contains 'total_iter' (int) for total iters. + use_tb_logger (bool): Use tensorboard logger. + start_iter (int): Start iter. Default: 1. + tb_logger (obj:`tb_logger`): Tensorboard logger. Default: None. + """ + + def __init__(self, opt, start_iter=1, tb_logger=None): + self.exp_name = opt['name'] + self.interval = opt['logger']['print_freq'] + self.start_iter = start_iter + self.max_iters = opt['train']['total_iter'] + self.use_tb_logger = opt['logger']['use_tb_logger'] + self.tb_logger = tb_logger + self.start_time = time.time() + self.logger = get_root_logger() + + @master_only + def __call__(self, log_vars): + """Format logging message. + Args: + log_vars (dict): It contains the following keys: + epoch (int): Epoch number. + iter (int): Current iter. + lrs (list): List for learning rates. + time (float): Iter time. + data_time (float): Data time for each iter. + """ + # epoch, iter, learning rates + epoch = log_vars.pop('epoch') + current_iter = log_vars.pop('iter') + lrs = log_vars.pop('lrs') + + message = (f'[{self.exp_name[:5]}..][epoch:{epoch:3d}, ' f'iter:{current_iter:8,d}, lr:(') + for v in lrs: + message += f'{v:.3e},' + message += ')] ' + + # time and estimated time + if 'time' in log_vars.keys(): + iter_time = log_vars.pop('time') + data_time = log_vars.pop('data_time') + + total_time = time.time() - self.start_time + time_sec_avg = total_time / (current_iter - self.start_iter + 1) + eta_sec = time_sec_avg * (self.max_iters - current_iter - 1) + eta_str = str(datetime.timedelta(seconds=int(eta_sec))) + message += f'[eta: {eta_str}, ' + message += f'time (data): {iter_time:.3f} ({data_time:.3f})] ' + + # other items, especially losses + for k, v in log_vars.items(): + message += f'{k}: {v:.4e} ' + # tensorboard logger + if self.use_tb_logger: + if k.startswith('l_'): + self.tb_logger.add_scalar(f'losses/{k}', v, current_iter) + else: + self.tb_logger.add_scalar(k, v, current_iter) + self.logger.info(message) + + +@master_only +def init_tb_logger(log_dir): + from torch.utils.tensorboard import SummaryWriter + tb_logger = SummaryWriter(log_dir=log_dir) + return tb_logger + + +@master_only +def init_wandb_logger(opt): + """We now only use wandb to sync tensorboard log.""" + import wandb + logger = logging.getLogger('basicsr') + + project = opt['logger']['wandb']['project'] + resume_id = opt['logger']['wandb'].get('resume_id') + if resume_id: + wandb_id = resume_id + resume = 'allow' + logger.warning(f'Resume wandb logger with id={wandb_id}.') + else: + wandb_id = wandb.util.generate_id() + resume = 'never' + + wandb.init(id=wandb_id, resume=resume, name=opt['name'], config=opt, project=project, sync_tensorboard=True) + + logger.info(f'Use wandb logger with id={wandb_id}; project={project}.') + + +def get_root_logger(logger_name='basicsr', log_level=logging.INFO, log_file=None): + """Get the root logger. + The logger will be initialized if it has not been initialized. By default a + StreamHandler will be added. If `log_file` is specified, a FileHandler will + also be added. + Args: + logger_name (str): root logger name. Default: 'basicsr'. + log_file (str | None): The log filename. If specified, a FileHandler + will be added to the root logger. + log_level (int): The root logger level. Note that only the process of + rank 0 is affected, while other processes will set the level to + "Error" and be silent most of the time. + Returns: + logging.Logger: The root logger. + """ + logger = logging.getLogger(logger_name) + # if the logger has been initialized, just return it + if logger_name in initialized_logger: + return logger + + format_str = '%(asctime)s %(levelname)s: %(message)s' + stream_handler = logging.StreamHandler() + stream_handler.setFormatter(logging.Formatter(format_str)) + logger.addHandler(stream_handler) + logger.propagate = False + rank, _ = get_dist_info() + if rank != 0: + logger.setLevel('ERROR') + elif log_file is not None: + logger.setLevel(log_level) + # add file handler + # file_handler = logging.FileHandler(log_file, 'w') + file_handler = logging.FileHandler(log_file, 'a') #Shangchen: keep the previous log + file_handler.setFormatter(logging.Formatter(format_str)) + file_handler.setLevel(log_level) + logger.addHandler(file_handler) + initialized_logger[logger_name] = True + return logger + + +def get_env_info(): + """Get environment information. + Currently, only log the software version. + """ + import torch + import torchvision + + from basicsr.version import __version__ + msg = r""" + ____ _ _____ ____ + / __ ) ____ _ _____ (_)_____/ ___/ / __ \ + / __ |/ __ `// ___// // ___/\__ \ / /_/ / + / /_/ // /_/ /(__ )/ // /__ ___/ // _, _/ + /_____/ \__,_//____//_/ \___//____//_/ |_| + ______ __ __ __ __ + / ____/____ ____ ____/ / / / __ __ _____ / /__ / / + / / __ / __ \ / __ \ / __ / / / / / / // ___// //_/ / / + / /_/ // /_/ // /_/ // /_/ / / /___/ /_/ // /__ / /< /_/ + \____/ \____/ \____/ \____/ /_____/\____/ \___//_/|_| (_) + """ + msg += ('\nVersion Information: ' + f'\n\tBasicSR: {__version__}' + f'\n\tPyTorch: {torch.__version__}' + f'\n\tTorchVision: {torchvision.__version__}') + return msg \ No newline at end of file diff --git a/repositories/codeformer/basicsr/utils/matlab_functions.py b/repositories/codeformer/basicsr/utils/matlab_functions.py new file mode 100644 index 000000000..c6ce1004a --- /dev/null +++ b/repositories/codeformer/basicsr/utils/matlab_functions.py @@ -0,0 +1,347 @@ +import math +import numpy as np +import torch + + +def cubic(x): + """cubic function used for calculate_weights_indices.""" + absx = torch.abs(x) + absx2 = absx**2 + absx3 = absx**3 + return (1.5 * absx3 - 2.5 * absx2 + 1) * ( + (absx <= 1).type_as(absx)) + (-0.5 * absx3 + 2.5 * absx2 - 4 * absx + 2) * (((absx > 1) * + (absx <= 2)).type_as(absx)) + + +def calculate_weights_indices(in_length, out_length, scale, kernel, kernel_width, antialiasing): + """Calculate weights and indices, used for imresize function. + + Args: + in_length (int): Input length. + out_length (int): Output length. + scale (float): Scale factor. + kernel_width (int): Kernel width. + antialisaing (bool): Whether to apply anti-aliasing when downsampling. + """ + + if (scale < 1) and antialiasing: + # Use a modified kernel (larger kernel width) to simultaneously + # interpolate and antialias + kernel_width = kernel_width / scale + + # Output-space coordinates + x = torch.linspace(1, out_length, out_length) + + # Input-space coordinates. Calculate the inverse mapping such that 0.5 + # in output space maps to 0.5 in input space, and 0.5 + scale in output + # space maps to 1.5 in input space. + u = x / scale + 0.5 * (1 - 1 / scale) + + # What is the left-most pixel that can be involved in the computation? + left = torch.floor(u - kernel_width / 2) + + # What is the maximum number of pixels that can be involved in the + # computation? Note: it's OK to use an extra pixel here; if the + # corresponding weights are all zero, it will be eliminated at the end + # of this function. + p = math.ceil(kernel_width) + 2 + + # The indices of the input pixels involved in computing the k-th output + # pixel are in row k of the indices matrix. + indices = left.view(out_length, 1).expand(out_length, p) + torch.linspace(0, p - 1, p).view(1, p).expand( + out_length, p) + + # The weights used to compute the k-th output pixel are in row k of the + # weights matrix. + distance_to_center = u.view(out_length, 1).expand(out_length, p) - indices + + # apply cubic kernel + if (scale < 1) and antialiasing: + weights = scale * cubic(distance_to_center * scale) + else: + weights = cubic(distance_to_center) + + # Normalize the weights matrix so that each row sums to 1. + weights_sum = torch.sum(weights, 1).view(out_length, 1) + weights = weights / weights_sum.expand(out_length, p) + + # If a column in weights is all zero, get rid of it. only consider the + # first and last column. + weights_zero_tmp = torch.sum((weights == 0), 0) + if not math.isclose(weights_zero_tmp[0], 0, rel_tol=1e-6): + indices = indices.narrow(1, 1, p - 2) + weights = weights.narrow(1, 1, p - 2) + if not math.isclose(weights_zero_tmp[-1], 0, rel_tol=1e-6): + indices = indices.narrow(1, 0, p - 2) + weights = weights.narrow(1, 0, p - 2) + weights = weights.contiguous() + indices = indices.contiguous() + sym_len_s = -indices.min() + 1 + sym_len_e = indices.max() - in_length + indices = indices + sym_len_s - 1 + return weights, indices, int(sym_len_s), int(sym_len_e) + + +@torch.no_grad() +def imresize(img, scale, antialiasing=True): + """imresize function same as MATLAB. + + It now only supports bicubic. + The same scale applies for both height and width. + + Args: + img (Tensor | Numpy array): + Tensor: Input image with shape (c, h, w), [0, 1] range. + Numpy: Input image with shape (h, w, c), [0, 1] range. + scale (float): Scale factor. The same scale applies for both height + and width. + antialisaing (bool): Whether to apply anti-aliasing when downsampling. + Default: True. + + Returns: + Tensor: Output image with shape (c, h, w), [0, 1] range, w/o round. + """ + if type(img).__module__ == np.__name__: # numpy type + numpy_type = True + img = torch.from_numpy(img.transpose(2, 0, 1)).float() + else: + numpy_type = False + + in_c, in_h, in_w = img.size() + out_h, out_w = math.ceil(in_h * scale), math.ceil(in_w * scale) + kernel_width = 4 + kernel = 'cubic' + + # get weights and indices + weights_h, indices_h, sym_len_hs, sym_len_he = calculate_weights_indices(in_h, out_h, scale, kernel, kernel_width, + antialiasing) + weights_w, indices_w, sym_len_ws, sym_len_we = calculate_weights_indices(in_w, out_w, scale, kernel, kernel_width, + antialiasing) + # process H dimension + # symmetric copying + img_aug = torch.FloatTensor(in_c, in_h + sym_len_hs + sym_len_he, in_w) + img_aug.narrow(1, sym_len_hs, in_h).copy_(img) + + sym_patch = img[:, :sym_len_hs, :] + inv_idx = torch.arange(sym_patch.size(1) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(1, inv_idx) + img_aug.narrow(1, 0, sym_len_hs).copy_(sym_patch_inv) + + sym_patch = img[:, -sym_len_he:, :] + inv_idx = torch.arange(sym_patch.size(1) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(1, inv_idx) + img_aug.narrow(1, sym_len_hs + in_h, sym_len_he).copy_(sym_patch_inv) + + out_1 = torch.FloatTensor(in_c, out_h, in_w) + kernel_width = weights_h.size(1) + for i in range(out_h): + idx = int(indices_h[i][0]) + for j in range(in_c): + out_1[j, i, :] = img_aug[j, idx:idx + kernel_width, :].transpose(0, 1).mv(weights_h[i]) + + # process W dimension + # symmetric copying + out_1_aug = torch.FloatTensor(in_c, out_h, in_w + sym_len_ws + sym_len_we) + out_1_aug.narrow(2, sym_len_ws, in_w).copy_(out_1) + + sym_patch = out_1[:, :, :sym_len_ws] + inv_idx = torch.arange(sym_patch.size(2) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(2, inv_idx) + out_1_aug.narrow(2, 0, sym_len_ws).copy_(sym_patch_inv) + + sym_patch = out_1[:, :, -sym_len_we:] + inv_idx = torch.arange(sym_patch.size(2) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(2, inv_idx) + out_1_aug.narrow(2, sym_len_ws + in_w, sym_len_we).copy_(sym_patch_inv) + + out_2 = torch.FloatTensor(in_c, out_h, out_w) + kernel_width = weights_w.size(1) + for i in range(out_w): + idx = int(indices_w[i][0]) + for j in range(in_c): + out_2[j, :, i] = out_1_aug[j, :, idx:idx + kernel_width].mv(weights_w[i]) + + if numpy_type: + out_2 = out_2.numpy().transpose(1, 2, 0) + return out_2 + + +def rgb2ycbcr(img, y_only=False): + """Convert a RGB image to YCbCr image. + + This function produces the same results as Matlab's `rgb2ycbcr` function. + It implements the ITU-R BT.601 conversion for standard-definition + television. See more details in + https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. + + It differs from a similar function in cv2.cvtColor: `RGB <-> YCrCb`. + In OpenCV, it implements a JPEG conversion. See more details in + https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. + + Args: + img (ndarray): The input image. It accepts: + 1. np.uint8 type with range [0, 255]; + 2. np.float32 type with range [0, 1]. + y_only (bool): Whether to only return Y channel. Default: False. + + Returns: + ndarray: The converted YCbCr image. The output image has the same type + and range as input image. + """ + img_type = img.dtype + img = _convert_input_type_range(img) + if y_only: + out_img = np.dot(img, [65.481, 128.553, 24.966]) + 16.0 + else: + out_img = np.matmul( + img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], [24.966, 112.0, -18.214]]) + [16, 128, 128] + out_img = _convert_output_type_range(out_img, img_type) + return out_img + + +def bgr2ycbcr(img, y_only=False): + """Convert a BGR image to YCbCr image. + + The bgr version of rgb2ycbcr. + It implements the ITU-R BT.601 conversion for standard-definition + television. See more details in + https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. + + It differs from a similar function in cv2.cvtColor: `BGR <-> YCrCb`. + In OpenCV, it implements a JPEG conversion. See more details in + https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. + + Args: + img (ndarray): The input image. It accepts: + 1. np.uint8 type with range [0, 255]; + 2. np.float32 type with range [0, 1]. + y_only (bool): Whether to only return Y channel. Default: False. + + Returns: + ndarray: The converted YCbCr image. The output image has the same type + and range as input image. + """ + img_type = img.dtype + img = _convert_input_type_range(img) + if y_only: + out_img = np.dot(img, [24.966, 128.553, 65.481]) + 16.0 + else: + out_img = np.matmul( + img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], [65.481, -37.797, 112.0]]) + [16, 128, 128] + out_img = _convert_output_type_range(out_img, img_type) + return out_img + + +def ycbcr2rgb(img): + """Convert a YCbCr image to RGB image. + + This function produces the same results as Matlab's ycbcr2rgb function. + It implements the ITU-R BT.601 conversion for standard-definition + television. See more details in + https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. + + It differs from a similar function in cv2.cvtColor: `YCrCb <-> RGB`. + In OpenCV, it implements a JPEG conversion. See more details in + https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. + + Args: + img (ndarray): The input image. It accepts: + 1. np.uint8 type with range [0, 255]; + 2. np.float32 type with range [0, 1]. + + Returns: + ndarray: The converted RGB image. The output image has the same type + and range as input image. + """ + img_type = img.dtype + img = _convert_input_type_range(img) * 255 + out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], [0, -0.00153632, 0.00791071], + [0.00625893, -0.00318811, 0]]) * 255.0 + [-222.921, 135.576, -276.836] # noqa: E126 + out_img = _convert_output_type_range(out_img, img_type) + return out_img + + +def ycbcr2bgr(img): + """Convert a YCbCr image to BGR image. + + The bgr version of ycbcr2rgb. + It implements the ITU-R BT.601 conversion for standard-definition + television. See more details in + https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion. + + It differs from a similar function in cv2.cvtColor: `YCrCb <-> BGR`. + In OpenCV, it implements a JPEG conversion. See more details in + https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion. + + Args: + img (ndarray): The input image. It accepts: + 1. np.uint8 type with range [0, 255]; + 2. np.float32 type with range [0, 1]. + + Returns: + ndarray: The converted BGR image. The output image has the same type + and range as input image. + """ + img_type = img.dtype + img = _convert_input_type_range(img) * 255 + out_img = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], [0.00791071, -0.00153632, 0], + [0, -0.00318811, 0.00625893]]) * 255.0 + [-276.836, 135.576, -222.921] # noqa: E126 + out_img = _convert_output_type_range(out_img, img_type) + return out_img + + +def _convert_input_type_range(img): + """Convert the type and range of the input image. + + It converts the input image to np.float32 type and range of [0, 1]. + It is mainly used for pre-processing the input image in colorspace + convertion functions such as rgb2ycbcr and ycbcr2rgb. + + Args: + img (ndarray): The input image. It accepts: + 1. np.uint8 type with range [0, 255]; + 2. np.float32 type with range [0, 1]. + + Returns: + (ndarray): The converted image with type of np.float32 and range of + [0, 1]. + """ + img_type = img.dtype + img = img.astype(np.float32) + if img_type == np.float32: + pass + elif img_type == np.uint8: + img /= 255. + else: + raise TypeError('The img type should be np.float32 or np.uint8, ' f'but got {img_type}') + return img + + +def _convert_output_type_range(img, dst_type): + """Convert the type and range of the image according to dst_type. + + It converts the image to desired type and range. If `dst_type` is np.uint8, + images will be converted to np.uint8 type with range [0, 255]. If + `dst_type` is np.float32, it converts the image to np.float32 type with + range [0, 1]. + It is mainly used for post-processing images in colorspace convertion + functions such as rgb2ycbcr and ycbcr2rgb. + + Args: + img (ndarray): The image to be converted with np.float32 type and + range [0, 255]. + dst_type (np.uint8 | np.float32): If dst_type is np.uint8, it + converts the image to np.uint8 type with range [0, 255]. If + dst_type is np.float32, it converts the image to np.float32 type + with range [0, 1]. + + Returns: + (ndarray): The converted image with desired type and range. + """ + if dst_type not in (np.uint8, np.float32): + raise TypeError('The dst_type should be np.float32 or np.uint8, ' f'but got {dst_type}') + if dst_type == np.uint8: + img = img.round() + else: + img /= 255. + return img.astype(dst_type) diff --git a/repositories/codeformer/basicsr/utils/misc.py b/repositories/codeformer/basicsr/utils/misc.py new file mode 100644 index 000000000..3b444ff3b --- /dev/null +++ b/repositories/codeformer/basicsr/utils/misc.py @@ -0,0 +1,134 @@ +import numpy as np +import os +import random +import time +import torch +from os import path as osp + +from .dist_util import master_only +from .logger import get_root_logger + + +def set_random_seed(seed): + """Set random seeds.""" + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + + +def get_time_str(): + return time.strftime('%Y%m%d_%H%M%S', time.localtime()) + + +def mkdir_and_rename(path): + """mkdirs. If path exists, rename it with timestamp and create a new one. + + Args: + path (str): Folder path. + """ + if osp.exists(path): + new_name = path + '_archived_' + get_time_str() + print(f'Path already exists. Rename it to {new_name}', flush=True) + os.rename(path, new_name) + os.makedirs(path, exist_ok=True) + + +@master_only +def make_exp_dirs(opt): + """Make dirs for experiments.""" + path_opt = opt['path'].copy() + if opt['is_train']: + mkdir_and_rename(path_opt.pop('experiments_root')) + else: + mkdir_and_rename(path_opt.pop('results_root')) + for key, path in path_opt.items(): + if ('strict_load' not in key) and ('pretrain_network' not in key) and ('resume' not in key): + os.makedirs(path, exist_ok=True) + + +def scandir(dir_path, suffix=None, recursive=False, full_path=False): + """Scan a directory to find the interested files. + + Args: + dir_path (str): Path of the directory. + suffix (str | tuple(str), optional): File suffix that we are + interested in. Default: None. + recursive (bool, optional): If set to True, recursively scan the + directory. Default: False. + full_path (bool, optional): If set to True, include the dir_path. + Default: False. + + Returns: + A generator for all the interested files with relative pathes. + """ + + if (suffix is not None) and not isinstance(suffix, (str, tuple)): + raise TypeError('"suffix" must be a string or tuple of strings') + + root = dir_path + + def _scandir(dir_path, suffix, recursive): + for entry in os.scandir(dir_path): + if not entry.name.startswith('.') and entry.is_file(): + if full_path: + return_path = entry.path + else: + return_path = osp.relpath(entry.path, root) + + if suffix is None: + yield return_path + elif return_path.endswith(suffix): + yield return_path + else: + if recursive: + yield from _scandir(entry.path, suffix=suffix, recursive=recursive) + else: + continue + + return _scandir(dir_path, suffix=suffix, recursive=recursive) + + +def check_resume(opt, resume_iter): + """Check resume states and pretrain_network paths. + + Args: + opt (dict): Options. + resume_iter (int): Resume iteration. + """ + logger = get_root_logger() + if opt['path']['resume_state']: + # get all the networks + networks = [key for key in opt.keys() if key.startswith('network_')] + flag_pretrain = False + for network in networks: + if opt['path'].get(f'pretrain_{network}') is not None: + flag_pretrain = True + if flag_pretrain: + logger.warning('pretrain_network path will be ignored during resuming.') + # set pretrained model paths + for network in networks: + name = f'pretrain_{network}' + basename = network.replace('network_', '') + if opt['path'].get('ignore_resume_networks') is None or (basename + not in opt['path']['ignore_resume_networks']): + opt['path'][name] = osp.join(opt['path']['models'], f'net_{basename}_{resume_iter}.pth') + logger.info(f"Set {name} to {opt['path'][name]}") + + +def sizeof_fmt(size, suffix='B'): + """Get human readable file size. + + Args: + size (int): File size. + suffix (str): Suffix. Default: 'B'. + + Return: + str: Formated file siz. + """ + for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']: + if abs(size) < 1024.0: + return f'{size:3.1f} {unit}{suffix}' + size /= 1024.0 + return f'{size:3.1f} Y{suffix}' diff --git a/repositories/codeformer/basicsr/utils/options.py b/repositories/codeformer/basicsr/utils/options.py new file mode 100644 index 000000000..db490e4aa --- /dev/null +++ b/repositories/codeformer/basicsr/utils/options.py @@ -0,0 +1,108 @@ +import yaml +import time +from collections import OrderedDict +from os import path as osp +from basicsr.utils.misc import get_time_str + +def ordered_yaml(): + """Support OrderedDict for yaml. + + Returns: + yaml Loader and Dumper. + """ + try: + from yaml import CDumper as Dumper + from yaml import CLoader as Loader + except ImportError: + from yaml import Dumper, Loader + + _mapping_tag = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG + + def dict_representer(dumper, data): + return dumper.represent_dict(data.items()) + + def dict_constructor(loader, node): + return OrderedDict(loader.construct_pairs(node)) + + Dumper.add_representer(OrderedDict, dict_representer) + Loader.add_constructor(_mapping_tag, dict_constructor) + return Loader, Dumper + + +def parse(opt_path, root_path, is_train=True): + """Parse option file. + + Args: + opt_path (str): Option file path. + is_train (str): Indicate whether in training or not. Default: True. + + Returns: + (dict): Options. + """ + with open(opt_path, mode='r') as f: + Loader, _ = ordered_yaml() + opt = yaml.load(f, Loader=Loader) + + opt['is_train'] = is_train + + # opt['name'] = f"{get_time_str()}_{opt['name']}" + if opt['path'].get('resume_state', None): # Shangchen added + resume_state_path = opt['path'].get('resume_state') + opt['name'] = resume_state_path.split("/")[-3] + else: + opt['name'] = f"{get_time_str()}_{opt['name']}" + + + # datasets + for phase, dataset in opt['datasets'].items(): + # for several datasets, e.g., test_1, test_2 + phase = phase.split('_')[0] + dataset['phase'] = phase + if 'scale' in opt: + dataset['scale'] = opt['scale'] + if dataset.get('dataroot_gt') is not None: + dataset['dataroot_gt'] = osp.expanduser(dataset['dataroot_gt']) + if dataset.get('dataroot_lq') is not None: + dataset['dataroot_lq'] = osp.expanduser(dataset['dataroot_lq']) + + # paths + for key, val in opt['path'].items(): + if (val is not None) and ('resume_state' in key or 'pretrain_network' in key): + opt['path'][key] = osp.expanduser(val) + + if is_train: + experiments_root = osp.join(root_path, 'experiments', opt['name']) + opt['path']['experiments_root'] = experiments_root + opt['path']['models'] = osp.join(experiments_root, 'models') + opt['path']['training_states'] = osp.join(experiments_root, 'training_states') + opt['path']['log'] = experiments_root + opt['path']['visualization'] = osp.join(experiments_root, 'visualization') + + else: # test + results_root = osp.join(root_path, 'results', opt['name']) + opt['path']['results_root'] = results_root + opt['path']['log'] = results_root + opt['path']['visualization'] = osp.join(results_root, 'visualization') + + return opt + + +def dict2str(opt, indent_level=1): + """dict to string for printing options. + + Args: + opt (dict): Option dict. + indent_level (int): Indent level. Default: 1. + + Return: + (str): Option string for printing. + """ + msg = '\n' + for k, v in opt.items(): + if isinstance(v, dict): + msg += ' ' * (indent_level * 2) + k + ':[' + msg += dict2str(v, indent_level + 1) + msg += ' ' * (indent_level * 2) + ']\n' + else: + msg += ' ' * (indent_level * 2) + k + ': ' + str(v) + '\n' + return msg diff --git a/repositories/codeformer/basicsr/utils/realesrgan_utils.py b/repositories/codeformer/basicsr/utils/realesrgan_utils.py new file mode 100644 index 000000000..6b7a8b460 --- /dev/null +++ b/repositories/codeformer/basicsr/utils/realesrgan_utils.py @@ -0,0 +1,299 @@ +import cv2 +import math +import numpy as np +import os +import queue +import threading +import torch +from basicsr.utils.download_util import load_file_from_url +from torch.nn import functional as F + +# ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +class RealESRGANer(): + """A helper class for upsampling images with RealESRGAN. + + Args: + scale (int): Upsampling scale factor used in the networks. It is usually 2 or 4. + model_path (str): The path to the pretrained model. It can be urls (will first download it automatically). + model (nn.Module): The defined network. Default: None. + tile (int): As too large images result in the out of GPU memory issue, so this tile option will first crop + input images into tiles, and then process each of them. Finally, they will be merged into one image. + 0 denotes for do not use tile. Default: 0. + tile_pad (int): The pad size for each tile, to remove border artifacts. Default: 10. + pre_pad (int): Pad the input images to avoid border artifacts. Default: 10. + half (float): Whether to use half precision during inference. Default: False. + """ + + def __init__(self, + scale, + model_path, + model=None, + tile=0, + tile_pad=10, + pre_pad=10, + half=False, + device=None, + gpu_id=None): + self.scale = scale + self.tile_size = tile + self.tile_pad = tile_pad + self.pre_pad = pre_pad + self.mod_scale = None + self.half = half + + # initialize model + if gpu_id: + self.device = torch.device( + f'cuda:{gpu_id}' if torch.cuda.is_available() else 'cpu') if device is None else device + else: + self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') if device is None else device + # if the model_path starts with https, it will first download models to the folder: realesrgan/weights + if model_path.startswith('https://'): + model_path = load_file_from_url( + url=model_path, model_dir=os.path.join('weights/realesrgan'), progress=True, file_name=None) + loadnet = torch.load(model_path, map_location=torch.device('cpu')) + # prefer to use params_ema + if 'params_ema' in loadnet: + keyname = 'params_ema' + else: + keyname = 'params' + model.load_state_dict(loadnet[keyname], strict=True) + model.eval() + self.model = model.to(self.device) + if self.half: + self.model = self.model.half() + + def pre_process(self, img): + """Pre-process, such as pre-pad and mod pad, so that the images can be divisible + """ + img = torch.from_numpy(np.transpose(img, (2, 0, 1))).float() + self.img = img.unsqueeze(0).to(self.device) + if self.half: + self.img = self.img.half() + + # pre_pad + if self.pre_pad != 0: + self.img = F.pad(self.img, (0, self.pre_pad, 0, self.pre_pad), 'reflect') + # mod pad for divisible borders + if self.scale == 2: + self.mod_scale = 2 + elif self.scale == 1: + self.mod_scale = 4 + if self.mod_scale is not None: + self.mod_pad_h, self.mod_pad_w = 0, 0 + _, _, h, w = self.img.size() + if (h % self.mod_scale != 0): + self.mod_pad_h = (self.mod_scale - h % self.mod_scale) + if (w % self.mod_scale != 0): + self.mod_pad_w = (self.mod_scale - w % self.mod_scale) + self.img = F.pad(self.img, (0, self.mod_pad_w, 0, self.mod_pad_h), 'reflect') + + def process(self): + # model inference + self.output = self.model(self.img) + + def tile_process(self): + """It will first crop input images to tiles, and then process each tile. + Finally, all the processed tiles are merged into one images. + + Modified from: https://github.com/ata4/esrgan-launcher + """ + batch, channel, height, width = self.img.shape + output_height = height * self.scale + output_width = width * self.scale + output_shape = (batch, channel, output_height, output_width) + + # start with black image + self.output = self.img.new_zeros(output_shape) + tiles_x = math.ceil(width / self.tile_size) + tiles_y = math.ceil(height / self.tile_size) + + # loop over all tiles + for y in range(tiles_y): + for x in range(tiles_x): + # extract tile from input image + ofs_x = x * self.tile_size + ofs_y = y * self.tile_size + # input tile area on total image + input_start_x = ofs_x + input_end_x = min(ofs_x + self.tile_size, width) + input_start_y = ofs_y + input_end_y = min(ofs_y + self.tile_size, height) + + # input tile area on total image with padding + input_start_x_pad = max(input_start_x - self.tile_pad, 0) + input_end_x_pad = min(input_end_x + self.tile_pad, width) + input_start_y_pad = max(input_start_y - self.tile_pad, 0) + input_end_y_pad = min(input_end_y + self.tile_pad, height) + + # input tile dimensions + input_tile_width = input_end_x - input_start_x + input_tile_height = input_end_y - input_start_y + tile_idx = y * tiles_x + x + 1 + input_tile = self.img[:, :, input_start_y_pad:input_end_y_pad, input_start_x_pad:input_end_x_pad] + + # upscale tile + try: + with torch.no_grad(): + output_tile = self.model(input_tile) + except RuntimeError as error: + print('Error', error) + # print(f'\tTile {tile_idx}/{tiles_x * tiles_y}') + + # output tile area on total image + output_start_x = input_start_x * self.scale + output_end_x = input_end_x * self.scale + output_start_y = input_start_y * self.scale + output_end_y = input_end_y * self.scale + + # output tile area without padding + output_start_x_tile = (input_start_x - input_start_x_pad) * self.scale + output_end_x_tile = output_start_x_tile + input_tile_width * self.scale + output_start_y_tile = (input_start_y - input_start_y_pad) * self.scale + output_end_y_tile = output_start_y_tile + input_tile_height * self.scale + + # put tile into output image + self.output[:, :, output_start_y:output_end_y, + output_start_x:output_end_x] = output_tile[:, :, output_start_y_tile:output_end_y_tile, + output_start_x_tile:output_end_x_tile] + + def post_process(self): + # remove extra pad + if self.mod_scale is not None: + _, _, h, w = self.output.size() + self.output = self.output[:, :, 0:h - self.mod_pad_h * self.scale, 0:w - self.mod_pad_w * self.scale] + # remove prepad + if self.pre_pad != 0: + _, _, h, w = self.output.size() + self.output = self.output[:, :, 0:h - self.pre_pad * self.scale, 0:w - self.pre_pad * self.scale] + return self.output + + @torch.no_grad() + def enhance(self, img, outscale=None, alpha_upsampler='realesrgan'): + h_input, w_input = img.shape[0:2] + # img: numpy + img = img.astype(np.float32) + if np.max(img) > 256: # 16-bit image + max_range = 65535 + print('\tInput is a 16-bit image') + else: + max_range = 255 + img = img / max_range + if len(img.shape) == 2: # gray image + img_mode = 'L' + img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) + elif img.shape[2] == 4: # RGBA image with alpha channel + img_mode = 'RGBA' + alpha = img[:, :, 3] + img = img[:, :, 0:3] + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + if alpha_upsampler == 'realesrgan': + alpha = cv2.cvtColor(alpha, cv2.COLOR_GRAY2RGB) + else: + img_mode = 'RGB' + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # ------------------- process image (without the alpha channel) ------------------- # + try: + with torch.no_grad(): + self.pre_process(img) + if self.tile_size > 0: + self.tile_process() + else: + self.process() + output_img_t = self.post_process() + output_img = output_img_t.data.squeeze().float().cpu().clamp_(0, 1).numpy() + output_img = np.transpose(output_img[[2, 1, 0], :, :], (1, 2, 0)) + if img_mode == 'L': + output_img = cv2.cvtColor(output_img, cv2.COLOR_BGR2GRAY) + del output_img_t + torch.cuda.empty_cache() + except RuntimeError as error: + print(f"Failed inference for RealESRGAN: {error}") + + # ------------------- process the alpha channel if necessary ------------------- # + if img_mode == 'RGBA': + if alpha_upsampler == 'realesrgan': + self.pre_process(alpha) + if self.tile_size > 0: + self.tile_process() + else: + self.process() + output_alpha = self.post_process() + output_alpha = output_alpha.data.squeeze().float().cpu().clamp_(0, 1).numpy() + output_alpha = np.transpose(output_alpha[[2, 1, 0], :, :], (1, 2, 0)) + output_alpha = cv2.cvtColor(output_alpha, cv2.COLOR_BGR2GRAY) + else: # use the cv2 resize for alpha channel + h, w = alpha.shape[0:2] + output_alpha = cv2.resize(alpha, (w * self.scale, h * self.scale), interpolation=cv2.INTER_LINEAR) + + # merge the alpha channel + output_img = cv2.cvtColor(output_img, cv2.COLOR_BGR2BGRA) + output_img[:, :, 3] = output_alpha + + # ------------------------------ return ------------------------------ # + if max_range == 65535: # 16-bit image + output = (output_img * 65535.0).round().astype(np.uint16) + else: + output = (output_img * 255.0).round().astype(np.uint8) + + if outscale is not None and outscale != float(self.scale): + output = cv2.resize( + output, ( + int(w_input * outscale), + int(h_input * outscale), + ), interpolation=cv2.INTER_LANCZOS4) + + return output, img_mode + + +class PrefetchReader(threading.Thread): + """Prefetch images. + + Args: + img_list (list[str]): A image list of image paths to be read. + num_prefetch_queue (int): Number of prefetch queue. + """ + + def __init__(self, img_list, num_prefetch_queue): + super().__init__() + self.que = queue.Queue(num_prefetch_queue) + self.img_list = img_list + + def run(self): + for img_path in self.img_list: + img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED) + self.que.put(img) + + self.que.put(None) + + def __next__(self): + next_item = self.que.get() + if next_item is None: + raise StopIteration + return next_item + + def __iter__(self): + return self + + +class IOConsumer(threading.Thread): + + def __init__(self, opt, que, qid): + super().__init__() + self._queue = que + self.qid = qid + self.opt = opt + + def run(self): + while True: + msg = self._queue.get() + if isinstance(msg, str) and msg == 'quit': + break + + output = msg['output'] + save_path = msg['save_path'] + cv2.imwrite(save_path, output) + print(f'IO worker {self.qid} is done.') \ No newline at end of file diff --git a/repositories/codeformer/basicsr/utils/registry.py b/repositories/codeformer/basicsr/utils/registry.py new file mode 100644 index 000000000..655753b3b --- /dev/null +++ b/repositories/codeformer/basicsr/utils/registry.py @@ -0,0 +1,82 @@ +# Modified from: https://github.com/facebookresearch/fvcore/blob/master/fvcore/common/registry.py # noqa: E501 + + +class Registry(): + """ + The registry that provides name -> object mapping, to support third-party + users' custom modules. + + To create a registry (e.g. a backbone registry): + + .. code-block:: python + + BACKBONE_REGISTRY = Registry('BACKBONE') + + To register an object: + + .. code-block:: python + + @BACKBONE_REGISTRY.register() + class MyBackbone(): + ... + + Or: + + .. code-block:: python + + BACKBONE_REGISTRY.register(MyBackbone) + """ + + def __init__(self, name): + """ + Args: + name (str): the name of this registry + """ + self._name = name + self._obj_map = {} + + def _do_register(self, name, obj): + assert (name not in self._obj_map), (f"An object named '{name}' was already registered " + f"in '{self._name}' registry!") + self._obj_map[name] = obj + + def register(self, obj=None): + """ + Register the given object under the the name `obj.__name__`. + Can be used as either a decorator or not. + See docstring of this class for usage. + """ + if obj is None: + # used as a decorator + def deco(func_or_class): + name = func_or_class.__name__ + self._do_register(name, func_or_class) + return func_or_class + + return deco + + # used as a function call + name = obj.__name__ + self._do_register(name, obj) + + def get(self, name): + ret = self._obj_map.get(name) + if ret is None: + raise KeyError(f"No object named '{name}' found in '{self._name}' registry!") + return ret + + def __contains__(self, name): + return name in self._obj_map + + def __iter__(self): + return iter(self._obj_map.items()) + + def keys(self): + return self._obj_map.keys() + + +DATASET_REGISTRY = Registry('dataset') +ARCH_REGISTRY = Registry('arch') +MODEL_REGISTRY = Registry('model') +LOSS_REGISTRY = Registry('loss') +METRIC_REGISTRY = Registry('metric') diff --git a/repositories/codeformer/basicsr/utils/video_util.py b/repositories/codeformer/basicsr/utils/video_util.py new file mode 100644 index 000000000..20a2ff14c --- /dev/null +++ b/repositories/codeformer/basicsr/utils/video_util.py @@ -0,0 +1,125 @@ +''' +The code is modified from the Real-ESRGAN: +https://github.com/xinntao/Real-ESRGAN/blob/master/inference_realesrgan_video.py + +''' +import cv2 +import sys +import numpy as np + +try: + import ffmpeg +except ImportError: + import pip + pip.main(['install', '--user', 'ffmpeg-python']) + import ffmpeg + +def get_video_meta_info(video_path): + ret = {} + probe = ffmpeg.probe(video_path) + video_streams = [stream for stream in probe['streams'] if stream['codec_type'] == 'video'] + has_audio = any(stream['codec_type'] == 'audio' for stream in probe['streams']) + ret['width'] = video_streams[0]['width'] + ret['height'] = video_streams[0]['height'] + ret['fps'] = eval(video_streams[0]['avg_frame_rate']) + ret['audio'] = ffmpeg.input(video_path).audio if has_audio else None + ret['nb_frames'] = int(video_streams[0]['nb_frames']) + return ret + +class VideoReader: + def __init__(self, video_path): + self.paths = [] # for image&folder type + self.audio = None + try: + self.stream_reader = ( + ffmpeg.input(video_path).output('pipe:', format='rawvideo', pix_fmt='bgr24', + loglevel='error').run_async( + pipe_stdin=True, pipe_stdout=True, cmd='ffmpeg')) + except FileNotFoundError: + print('Please install ffmpeg (not ffmpeg-python) by running\n', + '\t$ conda install -c conda-forge ffmpeg') + sys.exit(0) + + meta = get_video_meta_info(video_path) + self.width = meta['width'] + self.height = meta['height'] + self.input_fps = meta['fps'] + self.audio = meta['audio'] + self.nb_frames = meta['nb_frames'] + + self.idx = 0 + + def get_resolution(self): + return self.height, self.width + + def get_fps(self): + if self.input_fps is not None: + return self.input_fps + return 24 + + def get_audio(self): + return self.audio + + def __len__(self): + return self.nb_frames + + def get_frame_from_stream(self): + img_bytes = self.stream_reader.stdout.read(self.width * self.height * 3) # 3 bytes for one pixel + if not img_bytes: + return None + img = np.frombuffer(img_bytes, np.uint8).reshape([self.height, self.width, 3]) + return img + + def get_frame_from_list(self): + if self.idx >= self.nb_frames: + return None + img = cv2.imread(self.paths[self.idx]) + self.idx += 1 + return img + + def get_frame(self): + return self.get_frame_from_stream() + + + def close(self): + self.stream_reader.stdin.close() + self.stream_reader.wait() + + +class VideoWriter: + def __init__(self, video_save_path, height, width, fps, audio): + if height > 2160: + print('You are generating video that is larger than 4K, which will be very slow due to IO speed.', + 'We highly recommend to decrease the outscale(aka, -s).') + if audio is not None: + self.stream_writer = ( + ffmpeg.input('pipe:', format='rawvideo', pix_fmt='bgr24', s=f'{width}x{height}', + framerate=fps).output( + audio, + video_save_path, + pix_fmt='yuv420p', + vcodec='libx264', + loglevel='error', + acodec='copy').overwrite_output().run_async( + pipe_stdin=True, pipe_stdout=True, cmd='ffmpeg')) + else: + self.stream_writer = ( + ffmpeg.input('pipe:', format='rawvideo', pix_fmt='bgr24', s=f'{width}x{height}', + framerate=fps).output( + video_save_path, pix_fmt='yuv420p', vcodec='libx264', + loglevel='error').overwrite_output().run_async( + pipe_stdin=True, pipe_stdout=True, cmd='ffmpeg')) + + def write_frame(self, frame): + try: + frame = frame.astype(np.uint8).tobytes() + self.stream_writer.stdin.write(frame) + except BrokenPipeError: + print('Please re-install ffmpeg and libx264 by running\n', + '\t$ conda install -c conda-forge ffmpeg\n', + '\t$ conda install -c conda-forge x264') + sys.exit(0) + + def close(self): + self.stream_writer.stdin.close() + self.stream_writer.wait() \ No newline at end of file diff --git a/repositories/codeformer/facelib/detection/__init__.py b/repositories/codeformer/facelib/detection/__init__.py new file mode 100644 index 000000000..5d1f8fc21 --- /dev/null +++ b/repositories/codeformer/facelib/detection/__init__.py @@ -0,0 +1,100 @@ +import os +import torch +from torch import nn +from copy import deepcopy + +from facelib.utils import load_file_from_url +from facelib.utils import download_pretrained_models +from facelib.detection.yolov5face.models.common import Conv + +from .retinaface.retinaface import RetinaFace +from .yolov5face.face_detector import YoloDetector + + +def init_detection_model(model_name, half=False, device='cuda'): + if 'retinaface' in model_name: + model = init_retinaface_model(model_name, half, device) + elif 'YOLOv5' in model_name: + model = init_yolov5face_model(model_name, device) + else: + raise NotImplementedError(f'{model_name} is not implemented.') + + return model + + +def init_retinaface_model(model_name, half=False, device='cuda'): + if model_name == 'retinaface_resnet50': + model = RetinaFace(network_name='resnet50', half=half) + model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/detection_Resnet50_Final.pth' + elif model_name == 'retinaface_mobile0.25': + model = RetinaFace(network_name='mobile0.25', half=half) + model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/detection_mobilenet0.25_Final.pth' + else: + raise NotImplementedError(f'{model_name} is not implemented.') + + model_path = load_file_from_url(url=model_url, model_dir='weights/facelib', progress=True, file_name=None) + load_net = torch.load(model_path, map_location=lambda storage, loc: storage) + # remove unnecessary 'module.' + for k, v in deepcopy(load_net).items(): + if k.startswith('module.'): + load_net[k[7:]] = v + load_net.pop(k) + model.load_state_dict(load_net, strict=True) + model.eval() + model = model.to(device) + + return model + + +def init_yolov5face_model(model_name, device='cuda'): + if model_name == 'YOLOv5l': + model = YoloDetector(config_name='facelib/detection/yolov5face/models/yolov5l.yaml', device=device) + model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/yolov5l-face.pth' + elif model_name == 'YOLOv5n': + model = YoloDetector(config_name='facelib/detection/yolov5face/models/yolov5n.yaml', device=device) + model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/yolov5n-face.pth' + else: + raise NotImplementedError(f'{model_name} is not implemented.') + + model_path = load_file_from_url(url=model_url, model_dir='weights/facelib', progress=True, file_name=None) + load_net = torch.load(model_path, map_location=lambda storage, loc: storage) + model.detector.load_state_dict(load_net, strict=True) + model.detector.eval() + model.detector = model.detector.to(device).float() + + for m in model.detector.modules(): + if type(m) in [nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU]: + m.inplace = True # pytorch 1.7.0 compatibility + elif isinstance(m, Conv): + m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility + + return model + + +# Download from Google Drive +# def init_yolov5face_model(model_name, device='cuda'): +# if model_name == 'YOLOv5l': +# model = YoloDetector(config_name='facelib/detection/yolov5face/models/yolov5l.yaml', device=device) +# f_id = {'yolov5l-face.pth': '131578zMA6B2x8VQHyHfa6GEPtulMCNzV'} +# elif model_name == 'YOLOv5n': +# model = YoloDetector(config_name='facelib/detection/yolov5face/models/yolov5n.yaml', device=device) +# f_id = {'yolov5n-face.pth': '1fhcpFvWZqghpGXjYPIne2sw1Fy4yhw6o'} +# else: +# raise NotImplementedError(f'{model_name} is not implemented.') + +# model_path = os.path.join('weights/facelib', list(f_id.keys())[0]) +# if not os.path.exists(model_path): +# download_pretrained_models(file_ids=f_id, save_path_root='weights/facelib') + +# load_net = torch.load(model_path, map_location=lambda storage, loc: storage) +# model.detector.load_state_dict(load_net, strict=True) +# model.detector.eval() +# model.detector = model.detector.to(device).float() + +# for m in model.detector.modules(): +# if type(m) in [nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU]: +# m.inplace = True # pytorch 1.7.0 compatibility +# elif isinstance(m, Conv): +# m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility + +# return model \ No newline at end of file diff --git a/repositories/codeformer/facelib/detection/align_trans.py b/repositories/codeformer/facelib/detection/align_trans.py new file mode 100644 index 000000000..07f1eb365 --- /dev/null +++ b/repositories/codeformer/facelib/detection/align_trans.py @@ -0,0 +1,219 @@ +import cv2 +import numpy as np + +from .matlab_cp2tform import get_similarity_transform_for_cv2 + +# reference facial points, a list of coordinates (x,y) +REFERENCE_FACIAL_POINTS = [[30.29459953, 51.69630051], [65.53179932, 51.50139999], [48.02519989, 71.73660278], + [33.54930115, 92.3655014], [62.72990036, 92.20410156]] + +DEFAULT_CROP_SIZE = (96, 112) + + +class FaceWarpException(Exception): + + def __str__(self): + return 'In File {}:{}'.format(__file__, super.__str__(self)) + + +def get_reference_facial_points(output_size=None, inner_padding_factor=0.0, outer_padding=(0, 0), default_square=False): + """ + Function: + ---------- + get reference 5 key points according to crop settings: + 0. Set default crop_size: + if default_square: + crop_size = (112, 112) + else: + crop_size = (96, 112) + 1. Pad the crop_size by inner_padding_factor in each side; + 2. Resize crop_size into (output_size - outer_padding*2), + pad into output_size with outer_padding; + 3. Output reference_5point; + Parameters: + ---------- + @output_size: (w, h) or None + size of aligned face image + @inner_padding_factor: (w_factor, h_factor) + padding factor for inner (w, h) + @outer_padding: (w_pad, h_pad) + each row is a pair of coordinates (x, y) + @default_square: True or False + if True: + default crop_size = (112, 112) + else: + default crop_size = (96, 112); + !!! make sure, if output_size is not None: + (output_size - outer_padding) + = some_scale * (default crop_size * (1.0 + + inner_padding_factor)) + Returns: + ---------- + @reference_5point: 5x2 np.array + each row is a pair of transformed coordinates (x, y) + """ + + tmp_5pts = np.array(REFERENCE_FACIAL_POINTS) + tmp_crop_size = np.array(DEFAULT_CROP_SIZE) + + # 0) make the inner region a square + if default_square: + size_diff = max(tmp_crop_size) - tmp_crop_size + tmp_5pts += size_diff / 2 + tmp_crop_size += size_diff + + if (output_size and output_size[0] == tmp_crop_size[0] and output_size[1] == tmp_crop_size[1]): + + return tmp_5pts + + if (inner_padding_factor == 0 and outer_padding == (0, 0)): + if output_size is None: + return tmp_5pts + else: + raise FaceWarpException('No paddings to do, output_size must be None or {}'.format(tmp_crop_size)) + + # check output size + if not (0 <= inner_padding_factor <= 1.0): + raise FaceWarpException('Not (0 <= inner_padding_factor <= 1.0)') + + if ((inner_padding_factor > 0 or outer_padding[0] > 0 or outer_padding[1] > 0) and output_size is None): + output_size = tmp_crop_size * \ + (1 + inner_padding_factor * 2).astype(np.int32) + output_size += np.array(outer_padding) + if not (outer_padding[0] < output_size[0] and outer_padding[1] < output_size[1]): + raise FaceWarpException('Not (outer_padding[0] < output_size[0] and outer_padding[1] < output_size[1])') + + # 1) pad the inner region according inner_padding_factor + if inner_padding_factor > 0: + size_diff = tmp_crop_size * inner_padding_factor * 2 + tmp_5pts += size_diff / 2 + tmp_crop_size += np.round(size_diff).astype(np.int32) + + # 2) resize the padded inner region + size_bf_outer_pad = np.array(output_size) - np.array(outer_padding) * 2 + + if size_bf_outer_pad[0] * tmp_crop_size[1] != size_bf_outer_pad[1] * tmp_crop_size[0]: + raise FaceWarpException('Must have (output_size - outer_padding)' + '= some_scale * (crop_size * (1.0 + inner_padding_factor)') + + scale_factor = size_bf_outer_pad[0].astype(np.float32) / tmp_crop_size[0] + tmp_5pts = tmp_5pts * scale_factor + # size_diff = tmp_crop_size * (scale_factor - min(scale_factor)) + # tmp_5pts = tmp_5pts + size_diff / 2 + tmp_crop_size = size_bf_outer_pad + + # 3) add outer_padding to make output_size + reference_5point = tmp_5pts + np.array(outer_padding) + tmp_crop_size = output_size + + return reference_5point + + +def get_affine_transform_matrix(src_pts, dst_pts): + """ + Function: + ---------- + get affine transform matrix 'tfm' from src_pts to dst_pts + Parameters: + ---------- + @src_pts: Kx2 np.array + source points matrix, each row is a pair of coordinates (x, y) + @dst_pts: Kx2 np.array + destination points matrix, each row is a pair of coordinates (x, y) + Returns: + ---------- + @tfm: 2x3 np.array + transform matrix from src_pts to dst_pts + """ + + tfm = np.float32([[1, 0, 0], [0, 1, 0]]) + n_pts = src_pts.shape[0] + ones = np.ones((n_pts, 1), src_pts.dtype) + src_pts_ = np.hstack([src_pts, ones]) + dst_pts_ = np.hstack([dst_pts, ones]) + + A, res, rank, s = np.linalg.lstsq(src_pts_, dst_pts_) + + if rank == 3: + tfm = np.float32([[A[0, 0], A[1, 0], A[2, 0]], [A[0, 1], A[1, 1], A[2, 1]]]) + elif rank == 2: + tfm = np.float32([[A[0, 0], A[1, 0], 0], [A[0, 1], A[1, 1], 0]]) + + return tfm + + +def warp_and_crop_face(src_img, facial_pts, reference_pts=None, crop_size=(96, 112), align_type='smilarity'): + """ + Function: + ---------- + apply affine transform 'trans' to uv + Parameters: + ---------- + @src_img: 3x3 np.array + input image + @facial_pts: could be + 1)a list of K coordinates (x,y) + or + 2) Kx2 or 2xK np.array + each row or col is a pair of coordinates (x, y) + @reference_pts: could be + 1) a list of K coordinates (x,y) + or + 2) Kx2 or 2xK np.array + each row or col is a pair of coordinates (x, y) + or + 3) None + if None, use default reference facial points + @crop_size: (w, h) + output face image size + @align_type: transform type, could be one of + 1) 'similarity': use similarity transform + 2) 'cv2_affine': use the first 3 points to do affine transform, + by calling cv2.getAffineTransform() + 3) 'affine': use all points to do affine transform + Returns: + ---------- + @face_img: output face image with size (w, h) = @crop_size + """ + + if reference_pts is None: + if crop_size[0] == 96 and crop_size[1] == 112: + reference_pts = REFERENCE_FACIAL_POINTS + else: + default_square = False + inner_padding_factor = 0 + outer_padding = (0, 0) + output_size = crop_size + + reference_pts = get_reference_facial_points(output_size, inner_padding_factor, outer_padding, + default_square) + + ref_pts = np.float32(reference_pts) + ref_pts_shp = ref_pts.shape + if max(ref_pts_shp) < 3 or min(ref_pts_shp) != 2: + raise FaceWarpException('reference_pts.shape must be (K,2) or (2,K) and K>2') + + if ref_pts_shp[0] == 2: + ref_pts = ref_pts.T + + src_pts = np.float32(facial_pts) + src_pts_shp = src_pts.shape + if max(src_pts_shp) < 3 or min(src_pts_shp) != 2: + raise FaceWarpException('facial_pts.shape must be (K,2) or (2,K) and K>2') + + if src_pts_shp[0] == 2: + src_pts = src_pts.T + + if src_pts.shape != ref_pts.shape: + raise FaceWarpException('facial_pts and reference_pts must have the same shape') + + if align_type == 'cv2_affine': + tfm = cv2.getAffineTransform(src_pts[0:3], ref_pts[0:3]) + elif align_type == 'affine': + tfm = get_affine_transform_matrix(src_pts, ref_pts) + else: + tfm = get_similarity_transform_for_cv2(src_pts, ref_pts) + + face_img = cv2.warpAffine(src_img, tfm, (crop_size[0], crop_size[1])) + + return face_img diff --git a/repositories/codeformer/facelib/detection/matlab_cp2tform.py b/repositories/codeformer/facelib/detection/matlab_cp2tform.py new file mode 100644 index 000000000..b2a8b54a9 --- /dev/null +++ b/repositories/codeformer/facelib/detection/matlab_cp2tform.py @@ -0,0 +1,317 @@ +import numpy as np +from numpy.linalg import inv, lstsq +from numpy.linalg import matrix_rank as rank +from numpy.linalg import norm + + +class MatlabCp2tormException(Exception): + + def __str__(self): + return 'In File {}:{}'.format(__file__, super.__str__(self)) + + +def tformfwd(trans, uv): + """ + Function: + ---------- + apply affine transform 'trans' to uv + + Parameters: + ---------- + @trans: 3x3 np.array + transform matrix + @uv: Kx2 np.array + each row is a pair of coordinates (x, y) + + Returns: + ---------- + @xy: Kx2 np.array + each row is a pair of transformed coordinates (x, y) + """ + uv = np.hstack((uv, np.ones((uv.shape[0], 1)))) + xy = np.dot(uv, trans) + xy = xy[:, 0:-1] + return xy + + +def tforminv(trans, uv): + """ + Function: + ---------- + apply the inverse of affine transform 'trans' to uv + + Parameters: + ---------- + @trans: 3x3 np.array + transform matrix + @uv: Kx2 np.array + each row is a pair of coordinates (x, y) + + Returns: + ---------- + @xy: Kx2 np.array + each row is a pair of inverse-transformed coordinates (x, y) + """ + Tinv = inv(trans) + xy = tformfwd(Tinv, uv) + return xy + + +def findNonreflectiveSimilarity(uv, xy, options=None): + options = {'K': 2} + + K = options['K'] + M = xy.shape[0] + x = xy[:, 0].reshape((-1, 1)) # use reshape to keep a column vector + y = xy[:, 1].reshape((-1, 1)) # use reshape to keep a column vector + + tmp1 = np.hstack((x, y, np.ones((M, 1)), np.zeros((M, 1)))) + tmp2 = np.hstack((y, -x, np.zeros((M, 1)), np.ones((M, 1)))) + X = np.vstack((tmp1, tmp2)) + + u = uv[:, 0].reshape((-1, 1)) # use reshape to keep a column vector + v = uv[:, 1].reshape((-1, 1)) # use reshape to keep a column vector + U = np.vstack((u, v)) + + # We know that X * r = U + if rank(X) >= 2 * K: + r, _, _, _ = lstsq(X, U, rcond=-1) + r = np.squeeze(r) + else: + raise Exception('cp2tform:twoUniquePointsReq') + sc = r[0] + ss = r[1] + tx = r[2] + ty = r[3] + + Tinv = np.array([[sc, -ss, 0], [ss, sc, 0], [tx, ty, 1]]) + T = inv(Tinv) + T[:, 2] = np.array([0, 0, 1]) + + return T, Tinv + + +def findSimilarity(uv, xy, options=None): + options = {'K': 2} + + # uv = np.array(uv) + # xy = np.array(xy) + + # Solve for trans1 + trans1, trans1_inv = findNonreflectiveSimilarity(uv, xy, options) + + # Solve for trans2 + + # manually reflect the xy data across the Y-axis + xyR = xy + xyR[:, 0] = -1 * xyR[:, 0] + + trans2r, trans2r_inv = findNonreflectiveSimilarity(uv, xyR, options) + + # manually reflect the tform to undo the reflection done on xyR + TreflectY = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, 1]]) + + trans2 = np.dot(trans2r, TreflectY) + + # Figure out if trans1 or trans2 is better + xy1 = tformfwd(trans1, uv) + norm1 = norm(xy1 - xy) + + xy2 = tformfwd(trans2, uv) + norm2 = norm(xy2 - xy) + + if norm1 <= norm2: + return trans1, trans1_inv + else: + trans2_inv = inv(trans2) + return trans2, trans2_inv + + +def get_similarity_transform(src_pts, dst_pts, reflective=True): + """ + Function: + ---------- + Find Similarity Transform Matrix 'trans': + u = src_pts[:, 0] + v = src_pts[:, 1] + x = dst_pts[:, 0] + y = dst_pts[:, 1] + [x, y, 1] = [u, v, 1] * trans + + Parameters: + ---------- + @src_pts: Kx2 np.array + source points, each row is a pair of coordinates (x, y) + @dst_pts: Kx2 np.array + destination points, each row is a pair of transformed + coordinates (x, y) + @reflective: True or False + if True: + use reflective similarity transform + else: + use non-reflective similarity transform + + Returns: + ---------- + @trans: 3x3 np.array + transform matrix from uv to xy + trans_inv: 3x3 np.array + inverse of trans, transform matrix from xy to uv + """ + + if reflective: + trans, trans_inv = findSimilarity(src_pts, dst_pts) + else: + trans, trans_inv = findNonreflectiveSimilarity(src_pts, dst_pts) + + return trans, trans_inv + + +def cvt_tform_mat_for_cv2(trans): + """ + Function: + ---------- + Convert Transform Matrix 'trans' into 'cv2_trans' which could be + directly used by cv2.warpAffine(): + u = src_pts[:, 0] + v = src_pts[:, 1] + x = dst_pts[:, 0] + y = dst_pts[:, 1] + [x, y].T = cv_trans * [u, v, 1].T + + Parameters: + ---------- + @trans: 3x3 np.array + transform matrix from uv to xy + + Returns: + ---------- + @cv2_trans: 2x3 np.array + transform matrix from src_pts to dst_pts, could be directly used + for cv2.warpAffine() + """ + cv2_trans = trans[:, 0:2].T + + return cv2_trans + + +def get_similarity_transform_for_cv2(src_pts, dst_pts, reflective=True): + """ + Function: + ---------- + Find Similarity Transform Matrix 'cv2_trans' which could be + directly used by cv2.warpAffine(): + u = src_pts[:, 0] + v = src_pts[:, 1] + x = dst_pts[:, 0] + y = dst_pts[:, 1] + [x, y].T = cv_trans * [u, v, 1].T + + Parameters: + ---------- + @src_pts: Kx2 np.array + source points, each row is a pair of coordinates (x, y) + @dst_pts: Kx2 np.array + destination points, each row is a pair of transformed + coordinates (x, y) + reflective: True or False + if True: + use reflective similarity transform + else: + use non-reflective similarity transform + + Returns: + ---------- + @cv2_trans: 2x3 np.array + transform matrix from src_pts to dst_pts, could be directly used + for cv2.warpAffine() + """ + trans, trans_inv = get_similarity_transform(src_pts, dst_pts, reflective) + cv2_trans = cvt_tform_mat_for_cv2(trans) + + return cv2_trans + + +if __name__ == '__main__': + """ + u = [0, 6, -2] + v = [0, 3, 5] + x = [-1, 0, 4] + y = [-1, -10, 4] + + # In Matlab, run: + # + # uv = [u'; v']; + # xy = [x'; y']; + # tform_sim=cp2tform(uv,xy,'similarity'); + # + # trans = tform_sim.tdata.T + # ans = + # -0.0764 -1.6190 0 + # 1.6190 -0.0764 0 + # -3.2156 0.0290 1.0000 + # trans_inv = tform_sim.tdata.Tinv + # ans = + # + # -0.0291 0.6163 0 + # -0.6163 -0.0291 0 + # -0.0756 1.9826 1.0000 + # xy_m=tformfwd(tform_sim, u,v) + # + # xy_m = + # + # -3.2156 0.0290 + # 1.1833 -9.9143 + # 5.0323 2.8853 + # uv_m=tforminv(tform_sim, x,y) + # + # uv_m = + # + # 0.5698 1.3953 + # 6.0872 2.2733 + # -2.6570 4.3314 + """ + u = [0, 6, -2] + v = [0, 3, 5] + x = [-1, 0, 4] + y = [-1, -10, 4] + + uv = np.array((u, v)).T + xy = np.array((x, y)).T + + print('\n--->uv:') + print(uv) + print('\n--->xy:') + print(xy) + + trans, trans_inv = get_similarity_transform(uv, xy) + + print('\n--->trans matrix:') + print(trans) + + print('\n--->trans_inv matrix:') + print(trans_inv) + + print('\n---> apply transform to uv') + print('\nxy_m = uv_augmented * trans') + uv_aug = np.hstack((uv, np.ones((uv.shape[0], 1)))) + xy_m = np.dot(uv_aug, trans) + print(xy_m) + + print('\nxy_m = tformfwd(trans, uv)') + xy_m = tformfwd(trans, uv) + print(xy_m) + + print('\n---> apply inverse transform to xy') + print('\nuv_m = xy_augmented * trans_inv') + xy_aug = np.hstack((xy, np.ones((xy.shape[0], 1)))) + uv_m = np.dot(xy_aug, trans_inv) + print(uv_m) + + print('\nuv_m = tformfwd(trans_inv, xy)') + uv_m = tformfwd(trans_inv, xy) + print(uv_m) + + uv_m = tforminv(trans, xy) + print('\nuv_m = tforminv(trans, xy)') + print(uv_m) diff --git a/repositories/codeformer/facelib/detection/retinaface/retinaface.py b/repositories/codeformer/facelib/detection/retinaface/retinaface.py new file mode 100644 index 000000000..02593556d --- /dev/null +++ b/repositories/codeformer/facelib/detection/retinaface/retinaface.py @@ -0,0 +1,370 @@ +import cv2 +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F +from PIL import Image +from torchvision.models._utils import IntermediateLayerGetter as IntermediateLayerGetter + +from facelib.detection.align_trans import get_reference_facial_points, warp_and_crop_face +from facelib.detection.retinaface.retinaface_net import FPN, SSH, MobileNetV1, make_bbox_head, make_class_head, make_landmark_head +from facelib.detection.retinaface.retinaface_utils import (PriorBox, batched_decode, batched_decode_landm, decode, decode_landm, + py_cpu_nms) + +device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + + +def generate_config(network_name): + + cfg_mnet = { + 'name': 'mobilenet0.25', + 'min_sizes': [[16, 32], [64, 128], [256, 512]], + 'steps': [8, 16, 32], + 'variance': [0.1, 0.2], + 'clip': False, + 'loc_weight': 2.0, + 'gpu_train': True, + 'batch_size': 32, + 'ngpu': 1, + 'epoch': 250, + 'decay1': 190, + 'decay2': 220, + 'image_size': 640, + 'return_layers': { + 'stage1': 1, + 'stage2': 2, + 'stage3': 3 + }, + 'in_channel': 32, + 'out_channel': 64 + } + + cfg_re50 = { + 'name': 'Resnet50', + 'min_sizes': [[16, 32], [64, 128], [256, 512]], + 'steps': [8, 16, 32], + 'variance': [0.1, 0.2], + 'clip': False, + 'loc_weight': 2.0, + 'gpu_train': True, + 'batch_size': 24, + 'ngpu': 4, + 'epoch': 100, + 'decay1': 70, + 'decay2': 90, + 'image_size': 840, + 'return_layers': { + 'layer2': 1, + 'layer3': 2, + 'layer4': 3 + }, + 'in_channel': 256, + 'out_channel': 256 + } + + if network_name == 'mobile0.25': + return cfg_mnet + elif network_name == 'resnet50': + return cfg_re50 + else: + raise NotImplementedError(f'network_name={network_name}') + + +class RetinaFace(nn.Module): + + def __init__(self, network_name='resnet50', half=False, phase='test'): + super(RetinaFace, self).__init__() + self.half_inference = half + cfg = generate_config(network_name) + self.backbone = cfg['name'] + + self.model_name = f'retinaface_{network_name}' + self.cfg = cfg + self.phase = phase + self.target_size, self.max_size = 1600, 2150 + self.resize, self.scale, self.scale1 = 1., None, None + self.mean_tensor = torch.tensor([[[[104.]], [[117.]], [[123.]]]]).to(device) + self.reference = get_reference_facial_points(default_square=True) + # Build network. + backbone = None + if cfg['name'] == 'mobilenet0.25': + backbone = MobileNetV1() + self.body = IntermediateLayerGetter(backbone, cfg['return_layers']) + elif cfg['name'] == 'Resnet50': + import torchvision.models as models + backbone = models.resnet50(pretrained=False) + self.body = IntermediateLayerGetter(backbone, cfg['return_layers']) + + in_channels_stage2 = cfg['in_channel'] + in_channels_list = [ + in_channels_stage2 * 2, + in_channels_stage2 * 4, + in_channels_stage2 * 8, + ] + + out_channels = cfg['out_channel'] + self.fpn = FPN(in_channels_list, out_channels) + self.ssh1 = SSH(out_channels, out_channels) + self.ssh2 = SSH(out_channels, out_channels) + self.ssh3 = SSH(out_channels, out_channels) + + self.ClassHead = make_class_head(fpn_num=3, inchannels=cfg['out_channel']) + self.BboxHead = make_bbox_head(fpn_num=3, inchannels=cfg['out_channel']) + self.LandmarkHead = make_landmark_head(fpn_num=3, inchannels=cfg['out_channel']) + + self.to(device) + self.eval() + if self.half_inference: + self.half() + + def forward(self, inputs): + out = self.body(inputs) + + if self.backbone == 'mobilenet0.25' or self.backbone == 'Resnet50': + out = list(out.values()) + # FPN + fpn = self.fpn(out) + + # SSH + feature1 = self.ssh1(fpn[0]) + feature2 = self.ssh2(fpn[1]) + feature3 = self.ssh3(fpn[2]) + features = [feature1, feature2, feature3] + + bbox_regressions = torch.cat([self.BboxHead[i](feature) for i, feature in enumerate(features)], dim=1) + classifications = torch.cat([self.ClassHead[i](feature) for i, feature in enumerate(features)], dim=1) + tmp = [self.LandmarkHead[i](feature) for i, feature in enumerate(features)] + ldm_regressions = (torch.cat(tmp, dim=1)) + + if self.phase == 'train': + output = (bbox_regressions, classifications, ldm_regressions) + else: + output = (bbox_regressions, F.softmax(classifications, dim=-1), ldm_regressions) + return output + + def __detect_faces(self, inputs): + # get scale + height, width = inputs.shape[2:] + self.scale = torch.tensor([width, height, width, height], dtype=torch.float32).to(device) + tmp = [width, height, width, height, width, height, width, height, width, height] + self.scale1 = torch.tensor(tmp, dtype=torch.float32).to(device) + + # forawrd + inputs = inputs.to(device) + if self.half_inference: + inputs = inputs.half() + loc, conf, landmarks = self(inputs) + + # get priorbox + priorbox = PriorBox(self.cfg, image_size=inputs.shape[2:]) + priors = priorbox.forward().to(device) + + return loc, conf, landmarks, priors + + # single image detection + def transform(self, image, use_origin_size): + # convert to opencv format + if isinstance(image, Image.Image): + image = cv2.cvtColor(np.asarray(image), cv2.COLOR_RGB2BGR) + image = image.astype(np.float32) + + # testing scale + im_size_min = np.min(image.shape[0:2]) + im_size_max = np.max(image.shape[0:2]) + resize = float(self.target_size) / float(im_size_min) + + # prevent bigger axis from being more than max_size + if np.round(resize * im_size_max) > self.max_size: + resize = float(self.max_size) / float(im_size_max) + resize = 1 if use_origin_size else resize + + # resize + if resize != 1: + image = cv2.resize(image, None, None, fx=resize, fy=resize, interpolation=cv2.INTER_LINEAR) + + # convert to torch.tensor format + # image -= (104, 117, 123) + image = image.transpose(2, 0, 1) + image = torch.from_numpy(image).unsqueeze(0) + + return image, resize + + def detect_faces( + self, + image, + conf_threshold=0.8, + nms_threshold=0.4, + use_origin_size=True, + ): + """ + Params: + imgs: BGR image + """ + image, self.resize = self.transform(image, use_origin_size) + image = image.to(device) + if self.half_inference: + image = image.half() + image = image - self.mean_tensor + + loc, conf, landmarks, priors = self.__detect_faces(image) + + boxes = decode(loc.data.squeeze(0), priors.data, self.cfg['variance']) + boxes = boxes * self.scale / self.resize + boxes = boxes.cpu().numpy() + + scores = conf.squeeze(0).data.cpu().numpy()[:, 1] + + landmarks = decode_landm(landmarks.squeeze(0), priors, self.cfg['variance']) + landmarks = landmarks * self.scale1 / self.resize + landmarks = landmarks.cpu().numpy() + + # ignore low scores + inds = np.where(scores > conf_threshold)[0] + boxes, landmarks, scores = boxes[inds], landmarks[inds], scores[inds] + + # sort + order = scores.argsort()[::-1] + boxes, landmarks, scores = boxes[order], landmarks[order], scores[order] + + # do NMS + bounding_boxes = np.hstack((boxes, scores[:, np.newaxis])).astype(np.float32, copy=False) + keep = py_cpu_nms(bounding_boxes, nms_threshold) + bounding_boxes, landmarks = bounding_boxes[keep, :], landmarks[keep] + # self.t['forward_pass'].toc() + # print(self.t['forward_pass'].average_time) + # import sys + # sys.stdout.flush() + return np.concatenate((bounding_boxes, landmarks), axis=1) + + def __align_multi(self, image, boxes, landmarks, limit=None): + + if len(boxes) < 1: + return [], [] + + if limit: + boxes = boxes[:limit] + landmarks = landmarks[:limit] + + faces = [] + for landmark in landmarks: + facial5points = [[landmark[2 * j], landmark[2 * j + 1]] for j in range(5)] + + warped_face = warp_and_crop_face(np.array(image), facial5points, self.reference, crop_size=(112, 112)) + faces.append(warped_face) + + return np.concatenate((boxes, landmarks), axis=1), faces + + def align_multi(self, img, conf_threshold=0.8, limit=None): + + rlt = self.detect_faces(img, conf_threshold=conf_threshold) + boxes, landmarks = rlt[:, 0:5], rlt[:, 5:] + + return self.__align_multi(img, boxes, landmarks, limit) + + # batched detection + def batched_transform(self, frames, use_origin_size): + """ + Arguments: + frames: a list of PIL.Image, or torch.Tensor(shape=[n, h, w, c], + type=np.float32, BGR format). + use_origin_size: whether to use origin size. + """ + from_PIL = True if isinstance(frames[0], Image.Image) else False + + # convert to opencv format + if from_PIL: + frames = [cv2.cvtColor(np.asarray(frame), cv2.COLOR_RGB2BGR) for frame in frames] + frames = np.asarray(frames, dtype=np.float32) + + # testing scale + im_size_min = np.min(frames[0].shape[0:2]) + im_size_max = np.max(frames[0].shape[0:2]) + resize = float(self.target_size) / float(im_size_min) + + # prevent bigger axis from being more than max_size + if np.round(resize * im_size_max) > self.max_size: + resize = float(self.max_size) / float(im_size_max) + resize = 1 if use_origin_size else resize + + # resize + if resize != 1: + if not from_PIL: + frames = F.interpolate(frames, scale_factor=resize) + else: + frames = [ + cv2.resize(frame, None, None, fx=resize, fy=resize, interpolation=cv2.INTER_LINEAR) + for frame in frames + ] + + # convert to torch.tensor format + if not from_PIL: + frames = frames.transpose(1, 2).transpose(1, 3).contiguous() + else: + frames = frames.transpose((0, 3, 1, 2)) + frames = torch.from_numpy(frames) + + return frames, resize + + def batched_detect_faces(self, frames, conf_threshold=0.8, nms_threshold=0.4, use_origin_size=True): + """ + Arguments: + frames: a list of PIL.Image, or np.array(shape=[n, h, w, c], + type=np.uint8, BGR format). + conf_threshold: confidence threshold. + nms_threshold: nms threshold. + use_origin_size: whether to use origin size. + Returns: + final_bounding_boxes: list of np.array ([n_boxes, 5], + type=np.float32). + final_landmarks: list of np.array ([n_boxes, 10], type=np.float32). + """ + # self.t['forward_pass'].tic() + frames, self.resize = self.batched_transform(frames, use_origin_size) + frames = frames.to(device) + frames = frames - self.mean_tensor + + b_loc, b_conf, b_landmarks, priors = self.__detect_faces(frames) + + final_bounding_boxes, final_landmarks = [], [] + + # decode + priors = priors.unsqueeze(0) + b_loc = batched_decode(b_loc, priors, self.cfg['variance']) * self.scale / self.resize + b_landmarks = batched_decode_landm(b_landmarks, priors, self.cfg['variance']) * self.scale1 / self.resize + b_conf = b_conf[:, :, 1] + + # index for selection + b_indice = b_conf > conf_threshold + + # concat + b_loc_and_conf = torch.cat((b_loc, b_conf.unsqueeze(-1)), dim=2).float() + + for pred, landm, inds in zip(b_loc_and_conf, b_landmarks, b_indice): + + # ignore low scores + pred, landm = pred[inds, :], landm[inds, :] + if pred.shape[0] == 0: + final_bounding_boxes.append(np.array([], dtype=np.float32)) + final_landmarks.append(np.array([], dtype=np.float32)) + continue + + # sort + # order = score.argsort(descending=True) + # box, landm, score = box[order], landm[order], score[order] + + # to CPU + bounding_boxes, landm = pred.cpu().numpy(), landm.cpu().numpy() + + # NMS + keep = py_cpu_nms(bounding_boxes, nms_threshold) + bounding_boxes, landmarks = bounding_boxes[keep, :], landm[keep] + + # append + final_bounding_boxes.append(bounding_boxes) + final_landmarks.append(landmarks) + # self.t['forward_pass'].toc(average=True) + # self.batch_time += self.t['forward_pass'].diff + # self.total_frame += len(frames) + # print(self.batch_time / self.total_frame) + + return final_bounding_boxes, final_landmarks diff --git a/repositories/codeformer/facelib/detection/retinaface/retinaface_net.py b/repositories/codeformer/facelib/detection/retinaface/retinaface_net.py new file mode 100644 index 000000000..ab6aa82d3 --- /dev/null +++ b/repositories/codeformer/facelib/detection/retinaface/retinaface_net.py @@ -0,0 +1,196 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + + +def conv_bn(inp, oup, stride=1, leaky=0): + return nn.Sequential( + nn.Conv2d(inp, oup, 3, stride, 1, bias=False), nn.BatchNorm2d(oup), + nn.LeakyReLU(negative_slope=leaky, inplace=True)) + + +def conv_bn_no_relu(inp, oup, stride): + return nn.Sequential( + nn.Conv2d(inp, oup, 3, stride, 1, bias=False), + nn.BatchNorm2d(oup), + ) + + +def conv_bn1X1(inp, oup, stride, leaky=0): + return nn.Sequential( + nn.Conv2d(inp, oup, 1, stride, padding=0, bias=False), nn.BatchNorm2d(oup), + nn.LeakyReLU(negative_slope=leaky, inplace=True)) + + +def conv_dw(inp, oup, stride, leaky=0.1): + return nn.Sequential( + nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False), + nn.BatchNorm2d(inp), + nn.LeakyReLU(negative_slope=leaky, inplace=True), + nn.Conv2d(inp, oup, 1, 1, 0, bias=False), + nn.BatchNorm2d(oup), + nn.LeakyReLU(negative_slope=leaky, inplace=True), + ) + + +class SSH(nn.Module): + + def __init__(self, in_channel, out_channel): + super(SSH, self).__init__() + assert out_channel % 4 == 0 + leaky = 0 + if (out_channel <= 64): + leaky = 0.1 + self.conv3X3 = conv_bn_no_relu(in_channel, out_channel // 2, stride=1) + + self.conv5X5_1 = conv_bn(in_channel, out_channel // 4, stride=1, leaky=leaky) + self.conv5X5_2 = conv_bn_no_relu(out_channel // 4, out_channel // 4, stride=1) + + self.conv7X7_2 = conv_bn(out_channel // 4, out_channel // 4, stride=1, leaky=leaky) + self.conv7x7_3 = conv_bn_no_relu(out_channel // 4, out_channel // 4, stride=1) + + def forward(self, input): + conv3X3 = self.conv3X3(input) + + conv5X5_1 = self.conv5X5_1(input) + conv5X5 = self.conv5X5_2(conv5X5_1) + + conv7X7_2 = self.conv7X7_2(conv5X5_1) + conv7X7 = self.conv7x7_3(conv7X7_2) + + out = torch.cat([conv3X3, conv5X5, conv7X7], dim=1) + out = F.relu(out) + return out + + +class FPN(nn.Module): + + def __init__(self, in_channels_list, out_channels): + super(FPN, self).__init__() + leaky = 0 + if (out_channels <= 64): + leaky = 0.1 + self.output1 = conv_bn1X1(in_channels_list[0], out_channels, stride=1, leaky=leaky) + self.output2 = conv_bn1X1(in_channels_list[1], out_channels, stride=1, leaky=leaky) + self.output3 = conv_bn1X1(in_channels_list[2], out_channels, stride=1, leaky=leaky) + + self.merge1 = conv_bn(out_channels, out_channels, leaky=leaky) + self.merge2 = conv_bn(out_channels, out_channels, leaky=leaky) + + def forward(self, input): + # names = list(input.keys()) + # input = list(input.values()) + + output1 = self.output1(input[0]) + output2 = self.output2(input[1]) + output3 = self.output3(input[2]) + + up3 = F.interpolate(output3, size=[output2.size(2), output2.size(3)], mode='nearest') + output2 = output2 + up3 + output2 = self.merge2(output2) + + up2 = F.interpolate(output2, size=[output1.size(2), output1.size(3)], mode='nearest') + output1 = output1 + up2 + output1 = self.merge1(output1) + + out = [output1, output2, output3] + return out + + +class MobileNetV1(nn.Module): + + def __init__(self): + super(MobileNetV1, self).__init__() + self.stage1 = nn.Sequential( + conv_bn(3, 8, 2, leaky=0.1), # 3 + conv_dw(8, 16, 1), # 7 + conv_dw(16, 32, 2), # 11 + conv_dw(32, 32, 1), # 19 + conv_dw(32, 64, 2), # 27 + conv_dw(64, 64, 1), # 43 + ) + self.stage2 = nn.Sequential( + conv_dw(64, 128, 2), # 43 + 16 = 59 + conv_dw(128, 128, 1), # 59 + 32 = 91 + conv_dw(128, 128, 1), # 91 + 32 = 123 + conv_dw(128, 128, 1), # 123 + 32 = 155 + conv_dw(128, 128, 1), # 155 + 32 = 187 + conv_dw(128, 128, 1), # 187 + 32 = 219 + ) + self.stage3 = nn.Sequential( + conv_dw(128, 256, 2), # 219 +3 2 = 241 + conv_dw(256, 256, 1), # 241 + 64 = 301 + ) + self.avg = nn.AdaptiveAvgPool2d((1, 1)) + self.fc = nn.Linear(256, 1000) + + def forward(self, x): + x = self.stage1(x) + x = self.stage2(x) + x = self.stage3(x) + x = self.avg(x) + # x = self.model(x) + x = x.view(-1, 256) + x = self.fc(x) + return x + + +class ClassHead(nn.Module): + + def __init__(self, inchannels=512, num_anchors=3): + super(ClassHead, self).__init__() + self.num_anchors = num_anchors + self.conv1x1 = nn.Conv2d(inchannels, self.num_anchors * 2, kernel_size=(1, 1), stride=1, padding=0) + + def forward(self, x): + out = self.conv1x1(x) + out = out.permute(0, 2, 3, 1).contiguous() + + return out.view(out.shape[0], -1, 2) + + +class BboxHead(nn.Module): + + def __init__(self, inchannels=512, num_anchors=3): + super(BboxHead, self).__init__() + self.conv1x1 = nn.Conv2d(inchannels, num_anchors * 4, kernel_size=(1, 1), stride=1, padding=0) + + def forward(self, x): + out = self.conv1x1(x) + out = out.permute(0, 2, 3, 1).contiguous() + + return out.view(out.shape[0], -1, 4) + + +class LandmarkHead(nn.Module): + + def __init__(self, inchannels=512, num_anchors=3): + super(LandmarkHead, self).__init__() + self.conv1x1 = nn.Conv2d(inchannels, num_anchors * 10, kernel_size=(1, 1), stride=1, padding=0) + + def forward(self, x): + out = self.conv1x1(x) + out = out.permute(0, 2, 3, 1).contiguous() + + return out.view(out.shape[0], -1, 10) + + +def make_class_head(fpn_num=3, inchannels=64, anchor_num=2): + classhead = nn.ModuleList() + for i in range(fpn_num): + classhead.append(ClassHead(inchannels, anchor_num)) + return classhead + + +def make_bbox_head(fpn_num=3, inchannels=64, anchor_num=2): + bboxhead = nn.ModuleList() + for i in range(fpn_num): + bboxhead.append(BboxHead(inchannels, anchor_num)) + return bboxhead + + +def make_landmark_head(fpn_num=3, inchannels=64, anchor_num=2): + landmarkhead = nn.ModuleList() + for i in range(fpn_num): + landmarkhead.append(LandmarkHead(inchannels, anchor_num)) + return landmarkhead diff --git a/repositories/codeformer/facelib/detection/retinaface/retinaface_utils.py b/repositories/codeformer/facelib/detection/retinaface/retinaface_utils.py new file mode 100644 index 000000000..8c3577577 --- /dev/null +++ b/repositories/codeformer/facelib/detection/retinaface/retinaface_utils.py @@ -0,0 +1,421 @@ +import numpy as np +import torch +import torchvision +from itertools import product as product +from math import ceil + + +class PriorBox(object): + + def __init__(self, cfg, image_size=None, phase='train'): + super(PriorBox, self).__init__() + self.min_sizes = cfg['min_sizes'] + self.steps = cfg['steps'] + self.clip = cfg['clip'] + self.image_size = image_size + self.feature_maps = [[ceil(self.image_size[0] / step), ceil(self.image_size[1] / step)] for step in self.steps] + self.name = 's' + + def forward(self): + anchors = [] + for k, f in enumerate(self.feature_maps): + min_sizes = self.min_sizes[k] + for i, j in product(range(f[0]), range(f[1])): + for min_size in min_sizes: + s_kx = min_size / self.image_size[1] + s_ky = min_size / self.image_size[0] + dense_cx = [x * self.steps[k] / self.image_size[1] for x in [j + 0.5]] + dense_cy = [y * self.steps[k] / self.image_size[0] for y in [i + 0.5]] + for cy, cx in product(dense_cy, dense_cx): + anchors += [cx, cy, s_kx, s_ky] + + # back to torch land + output = torch.Tensor(anchors).view(-1, 4) + if self.clip: + output.clamp_(max=1, min=0) + return output + + +def py_cpu_nms(dets, thresh): + """Pure Python NMS baseline.""" + keep = torchvision.ops.nms( + boxes=torch.Tensor(dets[:, :4]), + scores=torch.Tensor(dets[:, 4]), + iou_threshold=thresh, + ) + + return list(keep) + + +def point_form(boxes): + """ Convert prior_boxes to (xmin, ymin, xmax, ymax) + representation for comparison to point form ground truth data. + Args: + boxes: (tensor) center-size default boxes from priorbox layers. + Return: + boxes: (tensor) Converted xmin, ymin, xmax, ymax form of boxes. + """ + return torch.cat( + ( + boxes[:, :2] - boxes[:, 2:] / 2, # xmin, ymin + boxes[:, :2] + boxes[:, 2:] / 2), + 1) # xmax, ymax + + +def center_size(boxes): + """ Convert prior_boxes to (cx, cy, w, h) + representation for comparison to center-size form ground truth data. + Args: + boxes: (tensor) point_form boxes + Return: + boxes: (tensor) Converted xmin, ymin, xmax, ymax form of boxes. + """ + return torch.cat( + (boxes[:, 2:] + boxes[:, :2]) / 2, # cx, cy + boxes[:, 2:] - boxes[:, :2], + 1) # w, h + + +def intersect(box_a, box_b): + """ We resize both tensors to [A,B,2] without new malloc: + [A,2] -> [A,1,2] -> [A,B,2] + [B,2] -> [1,B,2] -> [A,B,2] + Then we compute the area of intersect between box_a and box_b. + Args: + box_a: (tensor) bounding boxes, Shape: [A,4]. + box_b: (tensor) bounding boxes, Shape: [B,4]. + Return: + (tensor) intersection area, Shape: [A,B]. + """ + A = box_a.size(0) + B = box_b.size(0) + max_xy = torch.min(box_a[:, 2:].unsqueeze(1).expand(A, B, 2), box_b[:, 2:].unsqueeze(0).expand(A, B, 2)) + min_xy = torch.max(box_a[:, :2].unsqueeze(1).expand(A, B, 2), box_b[:, :2].unsqueeze(0).expand(A, B, 2)) + inter = torch.clamp((max_xy - min_xy), min=0) + return inter[:, :, 0] * inter[:, :, 1] + + +def jaccard(box_a, box_b): + """Compute the jaccard overlap of two sets of boxes. The jaccard overlap + is simply the intersection over union of two boxes. Here we operate on + ground truth boxes and default boxes. + E.g.: + A ∩ B / A ∪ B = A ∩ B / (area(A) + area(B) - A ∩ B) + Args: + box_a: (tensor) Ground truth bounding boxes, Shape: [num_objects,4] + box_b: (tensor) Prior boxes from priorbox layers, Shape: [num_priors,4] + Return: + jaccard overlap: (tensor) Shape: [box_a.size(0), box_b.size(0)] + """ + inter = intersect(box_a, box_b) + area_a = ((box_a[:, 2] - box_a[:, 0]) * (box_a[:, 3] - box_a[:, 1])).unsqueeze(1).expand_as(inter) # [A,B] + area_b = ((box_b[:, 2] - box_b[:, 0]) * (box_b[:, 3] - box_b[:, 1])).unsqueeze(0).expand_as(inter) # [A,B] + union = area_a + area_b - inter + return inter / union # [A,B] + + +def matrix_iou(a, b): + """ + return iou of a and b, numpy version for data augenmentation + """ + lt = np.maximum(a[:, np.newaxis, :2], b[:, :2]) + rb = np.minimum(a[:, np.newaxis, 2:], b[:, 2:]) + + area_i = np.prod(rb - lt, axis=2) * (lt < rb).all(axis=2) + area_a = np.prod(a[:, 2:] - a[:, :2], axis=1) + area_b = np.prod(b[:, 2:] - b[:, :2], axis=1) + return area_i / (area_a[:, np.newaxis] + area_b - area_i) + + +def matrix_iof(a, b): + """ + return iof of a and b, numpy version for data augenmentation + """ + lt = np.maximum(a[:, np.newaxis, :2], b[:, :2]) + rb = np.minimum(a[:, np.newaxis, 2:], b[:, 2:]) + + area_i = np.prod(rb - lt, axis=2) * (lt < rb).all(axis=2) + area_a = np.prod(a[:, 2:] - a[:, :2], axis=1) + return area_i / np.maximum(area_a[:, np.newaxis], 1) + + +def match(threshold, truths, priors, variances, labels, landms, loc_t, conf_t, landm_t, idx): + """Match each prior box with the ground truth box of the highest jaccard + overlap, encode the bounding boxes, then return the matched indices + corresponding to both confidence and location preds. + Args: + threshold: (float) The overlap threshold used when matching boxes. + truths: (tensor) Ground truth boxes, Shape: [num_obj, 4]. + priors: (tensor) Prior boxes from priorbox layers, Shape: [n_priors,4]. + variances: (tensor) Variances corresponding to each prior coord, + Shape: [num_priors, 4]. + labels: (tensor) All the class labels for the image, Shape: [num_obj]. + landms: (tensor) Ground truth landms, Shape [num_obj, 10]. + loc_t: (tensor) Tensor to be filled w/ encoded location targets. + conf_t: (tensor) Tensor to be filled w/ matched indices for conf preds. + landm_t: (tensor) Tensor to be filled w/ encoded landm targets. + idx: (int) current batch index + Return: + The matched indices corresponding to 1)location 2)confidence + 3)landm preds. + """ + # jaccard index + overlaps = jaccard(truths, point_form(priors)) + # (Bipartite Matching) + # [1,num_objects] best prior for each ground truth + best_prior_overlap, best_prior_idx = overlaps.max(1, keepdim=True) + + # ignore hard gt + valid_gt_idx = best_prior_overlap[:, 0] >= 0.2 + best_prior_idx_filter = best_prior_idx[valid_gt_idx, :] + if best_prior_idx_filter.shape[0] <= 0: + loc_t[idx] = 0 + conf_t[idx] = 0 + return + + # [1,num_priors] best ground truth for each prior + best_truth_overlap, best_truth_idx = overlaps.max(0, keepdim=True) + best_truth_idx.squeeze_(0) + best_truth_overlap.squeeze_(0) + best_prior_idx.squeeze_(1) + best_prior_idx_filter.squeeze_(1) + best_prior_overlap.squeeze_(1) + best_truth_overlap.index_fill_(0, best_prior_idx_filter, 2) # ensure best prior + # TODO refactor: index best_prior_idx with long tensor + # ensure every gt matches with its prior of max overlap + for j in range(best_prior_idx.size(0)): # 判别此anchor是预测哪一个boxes + best_truth_idx[best_prior_idx[j]] = j + matches = truths[best_truth_idx] # Shape: [num_priors,4] 此处为每一个anchor对应的bbox取出来 + conf = labels[best_truth_idx] # Shape: [num_priors] 此处为每一个anchor对应的label取出来 + conf[best_truth_overlap < threshold] = 0 # label as background overlap<0.35的全部作为负样本 + loc = encode(matches, priors, variances) + + matches_landm = landms[best_truth_idx] + landm = encode_landm(matches_landm, priors, variances) + loc_t[idx] = loc # [num_priors,4] encoded offsets to learn + conf_t[idx] = conf # [num_priors] top class label for each prior + landm_t[idx] = landm + + +def encode(matched, priors, variances): + """Encode the variances from the priorbox layers into the ground truth boxes + we have matched (based on jaccard overlap) with the prior boxes. + Args: + matched: (tensor) Coords of ground truth for each prior in point-form + Shape: [num_priors, 4]. + priors: (tensor) Prior boxes in center-offset form + Shape: [num_priors,4]. + variances: (list[float]) Variances of priorboxes + Return: + encoded boxes (tensor), Shape: [num_priors, 4] + """ + + # dist b/t match center and prior's center + g_cxcy = (matched[:, :2] + matched[:, 2:]) / 2 - priors[:, :2] + # encode variance + g_cxcy /= (variances[0] * priors[:, 2:]) + # match wh / prior wh + g_wh = (matched[:, 2:] - matched[:, :2]) / priors[:, 2:] + g_wh = torch.log(g_wh) / variances[1] + # return target for smooth_l1_loss + return torch.cat([g_cxcy, g_wh], 1) # [num_priors,4] + + +def encode_landm(matched, priors, variances): + """Encode the variances from the priorbox layers into the ground truth boxes + we have matched (based on jaccard overlap) with the prior boxes. + Args: + matched: (tensor) Coords of ground truth for each prior in point-form + Shape: [num_priors, 10]. + priors: (tensor) Prior boxes in center-offset form + Shape: [num_priors,4]. + variances: (list[float]) Variances of priorboxes + Return: + encoded landm (tensor), Shape: [num_priors, 10] + """ + + # dist b/t match center and prior's center + matched = torch.reshape(matched, (matched.size(0), 5, 2)) + priors_cx = priors[:, 0].unsqueeze(1).expand(matched.size(0), 5).unsqueeze(2) + priors_cy = priors[:, 1].unsqueeze(1).expand(matched.size(0), 5).unsqueeze(2) + priors_w = priors[:, 2].unsqueeze(1).expand(matched.size(0), 5).unsqueeze(2) + priors_h = priors[:, 3].unsqueeze(1).expand(matched.size(0), 5).unsqueeze(2) + priors = torch.cat([priors_cx, priors_cy, priors_w, priors_h], dim=2) + g_cxcy = matched[:, :, :2] - priors[:, :, :2] + # encode variance + g_cxcy /= (variances[0] * priors[:, :, 2:]) + # g_cxcy /= priors[:, :, 2:] + g_cxcy = g_cxcy.reshape(g_cxcy.size(0), -1) + # return target for smooth_l1_loss + return g_cxcy + + +# Adapted from https://github.com/Hakuyume/chainer-ssd +def decode(loc, priors, variances): + """Decode locations from predictions using priors to undo + the encoding we did for offset regression at train time. + Args: + loc (tensor): location predictions for loc layers, + Shape: [num_priors,4] + priors (tensor): Prior boxes in center-offset form. + Shape: [num_priors,4]. + variances: (list[float]) Variances of priorboxes + Return: + decoded bounding box predictions + """ + + boxes = torch.cat((priors[:, :2] + loc[:, :2] * variances[0] * priors[:, 2:], + priors[:, 2:] * torch.exp(loc[:, 2:] * variances[1])), 1) + boxes[:, :2] -= boxes[:, 2:] / 2 + boxes[:, 2:] += boxes[:, :2] + return boxes + + +def decode_landm(pre, priors, variances): + """Decode landm from predictions using priors to undo + the encoding we did for offset regression at train time. + Args: + pre (tensor): landm predictions for loc layers, + Shape: [num_priors,10] + priors (tensor): Prior boxes in center-offset form. + Shape: [num_priors,4]. + variances: (list[float]) Variances of priorboxes + Return: + decoded landm predictions + """ + tmp = ( + priors[:, :2] + pre[:, :2] * variances[0] * priors[:, 2:], + priors[:, :2] + pre[:, 2:4] * variances[0] * priors[:, 2:], + priors[:, :2] + pre[:, 4:6] * variances[0] * priors[:, 2:], + priors[:, :2] + pre[:, 6:8] * variances[0] * priors[:, 2:], + priors[:, :2] + pre[:, 8:10] * variances[0] * priors[:, 2:], + ) + landms = torch.cat(tmp, dim=1) + return landms + + +def batched_decode(b_loc, priors, variances): + """Decode locations from predictions using priors to undo + the encoding we did for offset regression at train time. + Args: + b_loc (tensor): location predictions for loc layers, + Shape: [num_batches,num_priors,4] + priors (tensor): Prior boxes in center-offset form. + Shape: [1,num_priors,4]. + variances: (list[float]) Variances of priorboxes + Return: + decoded bounding box predictions + """ + boxes = ( + priors[:, :, :2] + b_loc[:, :, :2] * variances[0] * priors[:, :, 2:], + priors[:, :, 2:] * torch.exp(b_loc[:, :, 2:] * variances[1]), + ) + boxes = torch.cat(boxes, dim=2) + + boxes[:, :, :2] -= boxes[:, :, 2:] / 2 + boxes[:, :, 2:] += boxes[:, :, :2] + return boxes + + +def batched_decode_landm(pre, priors, variances): + """Decode landm from predictions using priors to undo + the encoding we did for offset regression at train time. + Args: + pre (tensor): landm predictions for loc layers, + Shape: [num_batches,num_priors,10] + priors (tensor): Prior boxes in center-offset form. + Shape: [1,num_priors,4]. + variances: (list[float]) Variances of priorboxes + Return: + decoded landm predictions + """ + landms = ( + priors[:, :, :2] + pre[:, :, :2] * variances[0] * priors[:, :, 2:], + priors[:, :, :2] + pre[:, :, 2:4] * variances[0] * priors[:, :, 2:], + priors[:, :, :2] + pre[:, :, 4:6] * variances[0] * priors[:, :, 2:], + priors[:, :, :2] + pre[:, :, 6:8] * variances[0] * priors[:, :, 2:], + priors[:, :, :2] + pre[:, :, 8:10] * variances[0] * priors[:, :, 2:], + ) + landms = torch.cat(landms, dim=2) + return landms + + +def log_sum_exp(x): + """Utility function for computing log_sum_exp while determining + This will be used to determine unaveraged confidence loss across + all examples in a batch. + Args: + x (Variable(tensor)): conf_preds from conf layers + """ + x_max = x.data.max() + return torch.log(torch.sum(torch.exp(x - x_max), 1, keepdim=True)) + x_max + + +# Original author: Francisco Massa: +# https://github.com/fmassa/object-detection.torch +# Ported to PyTorch by Max deGroot (02/01/2017) +def nms(boxes, scores, overlap=0.5, top_k=200): + """Apply non-maximum suppression at test time to avoid detecting too many + overlapping bounding boxes for a given object. + Args: + boxes: (tensor) The location preds for the img, Shape: [num_priors,4]. + scores: (tensor) The class predscores for the img, Shape:[num_priors]. + overlap: (float) The overlap thresh for suppressing unnecessary boxes. + top_k: (int) The Maximum number of box preds to consider. + Return: + The indices of the kept boxes with respect to num_priors. + """ + + keep = torch.Tensor(scores.size(0)).fill_(0).long() + if boxes.numel() == 0: + return keep + x1 = boxes[:, 0] + y1 = boxes[:, 1] + x2 = boxes[:, 2] + y2 = boxes[:, 3] + area = torch.mul(x2 - x1, y2 - y1) + v, idx = scores.sort(0) # sort in ascending order + # I = I[v >= 0.01] + idx = idx[-top_k:] # indices of the top-k largest vals + xx1 = boxes.new() + yy1 = boxes.new() + xx2 = boxes.new() + yy2 = boxes.new() + w = boxes.new() + h = boxes.new() + + # keep = torch.Tensor() + count = 0 + while idx.numel() > 0: + i = idx[-1] # index of current largest val + # keep.append(i) + keep[count] = i + count += 1 + if idx.size(0) == 1: + break + idx = idx[:-1] # remove kept element from view + # load bboxes of next highest vals + torch.index_select(x1, 0, idx, out=xx1) + torch.index_select(y1, 0, idx, out=yy1) + torch.index_select(x2, 0, idx, out=xx2) + torch.index_select(y2, 0, idx, out=yy2) + # store element-wise max with next highest score + xx1 = torch.clamp(xx1, min=x1[i]) + yy1 = torch.clamp(yy1, min=y1[i]) + xx2 = torch.clamp(xx2, max=x2[i]) + yy2 = torch.clamp(yy2, max=y2[i]) + w.resize_as_(xx2) + h.resize_as_(yy2) + w = xx2 - xx1 + h = yy2 - yy1 + # check sizes of xx1 and xx2.. after each iteration + w = torch.clamp(w, min=0.0) + h = torch.clamp(h, min=0.0) + inter = w * h + # IoU = i / (area(a) + area(b) - i) + rem_areas = torch.index_select(area, 0, idx) # load remaining areas) + union = (rem_areas - inter) + area[i] + IoU = inter / union # store result in iou + # keep only elements with an IoU <= overlap + idx = idx[IoU.le(overlap)] + return keep, count diff --git a/repositories/codeformer/facelib/detection/yolov5face/__init__.py b/repositories/codeformer/facelib/detection/yolov5face/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/codeformer/facelib/detection/yolov5face/face_detector.py b/repositories/codeformer/facelib/detection/yolov5face/face_detector.py new file mode 100644 index 000000000..79fdba0c9 --- /dev/null +++ b/repositories/codeformer/facelib/detection/yolov5face/face_detector.py @@ -0,0 +1,142 @@ +import copy +import os +from pathlib import Path + +import cv2 +import numpy as np +import torch +from torch import nn + +from facelib.detection.yolov5face.models.common import Conv +from facelib.detection.yolov5face.models.yolo import Model +from facelib.detection.yolov5face.utils.datasets import letterbox +from facelib.detection.yolov5face.utils.general import ( + check_img_size, + non_max_suppression_face, + scale_coords, + scale_coords_landmarks, +) + +IS_HIGH_VERSION = tuple(map(int, torch.__version__.split('+')[0].split('.')[:2])) >= (1, 9, 0) + + +def isListempty(inList): + if isinstance(inList, list): # Is a list + return all(map(isListempty, inList)) + return False # Not a list + +class YoloDetector: + def __init__( + self, + config_name, + min_face=10, + target_size=None, + device='cuda', + ): + """ + config_name: name of .yaml config with network configuration from models/ folder. + min_face : minimal face size in pixels. + target_size : target size of smaller image axis (choose lower for faster work). e.g. 480, 720, 1080. + None for original resolution. + """ + self._class_path = Path(__file__).parent.absolute() + self.target_size = target_size + self.min_face = min_face + self.detector = Model(cfg=config_name) + self.device = device + + + def _preprocess(self, imgs): + """ + Preprocessing image before passing through the network. Resize and conversion to torch tensor. + """ + pp_imgs = [] + for img in imgs: + h0, w0 = img.shape[:2] # orig hw + if self.target_size: + r = self.target_size / min(h0, w0) # resize image to img_size + if r < 1: + img = cv2.resize(img, (int(w0 * r), int(h0 * r)), interpolation=cv2.INTER_LINEAR) + + imgsz = check_img_size(max(img.shape[:2]), s=self.detector.stride.max()) # check img_size + img = letterbox(img, new_shape=imgsz)[0] + pp_imgs.append(img) + pp_imgs = np.array(pp_imgs) + pp_imgs = pp_imgs.transpose(0, 3, 1, 2) + pp_imgs = torch.from_numpy(pp_imgs).to(self.device) + pp_imgs = pp_imgs.float() # uint8 to fp16/32 + return pp_imgs / 255.0 # 0 - 255 to 0.0 - 1.0 + + def _postprocess(self, imgs, origimgs, pred, conf_thres, iou_thres): + """ + Postprocessing of raw pytorch model output. + Returns: + bboxes: list of arrays with 4 coordinates of bounding boxes with format x1,y1,x2,y2. + points: list of arrays with coordinates of 5 facial keypoints (eyes, nose, lips corners). + """ + bboxes = [[] for _ in range(len(origimgs))] + landmarks = [[] for _ in range(len(origimgs))] + + pred = non_max_suppression_face(pred, conf_thres, iou_thres) + + for image_id, origimg in enumerate(origimgs): + img_shape = origimg.shape + image_height, image_width = img_shape[:2] + gn = torch.tensor(img_shape)[[1, 0, 1, 0]] # normalization gain whwh + gn_lks = torch.tensor(img_shape)[[1, 0, 1, 0, 1, 0, 1, 0, 1, 0]] # normalization gain landmarks + det = pred[image_id].cpu() + scale_coords(imgs[image_id].shape[1:], det[:, :4], img_shape).round() + scale_coords_landmarks(imgs[image_id].shape[1:], det[:, 5:15], img_shape).round() + + for j in range(det.size()[0]): + box = (det[j, :4].view(1, 4) / gn).view(-1).tolist() + box = list( + map(int, [box[0] * image_width, box[1] * image_height, box[2] * image_width, box[3] * image_height]) + ) + if box[3] - box[1] < self.min_face: + continue + lm = (det[j, 5:15].view(1, 10) / gn_lks).view(-1).tolist() + lm = list(map(int, [i * image_width if j % 2 == 0 else i * image_height for j, i in enumerate(lm)])) + lm = [lm[i : i + 2] for i in range(0, len(lm), 2)] + bboxes[image_id].append(box) + landmarks[image_id].append(lm) + return bboxes, landmarks + + def detect_faces(self, imgs, conf_thres=0.7, iou_thres=0.5): + """ + Get bbox coordinates and keypoints of faces on original image. + Params: + imgs: image or list of images to detect faces on with BGR order (convert to RGB order for inference) + conf_thres: confidence threshold for each prediction + iou_thres: threshold for NMS (filter of intersecting bboxes) + Returns: + bboxes: list of arrays with 4 coordinates of bounding boxes with format x1,y1,x2,y2. + points: list of arrays with coordinates of 5 facial keypoints (eyes, nose, lips corners). + """ + # Pass input images through face detector + images = imgs if isinstance(imgs, list) else [imgs] + images = [cv2.cvtColor(img, cv2.COLOR_BGR2RGB) for img in images] + origimgs = copy.deepcopy(images) + + images = self._preprocess(images) + + if IS_HIGH_VERSION: + with torch.inference_mode(): # for pytorch>=1.9 + pred = self.detector(images)[0] + else: + with torch.no_grad(): # for pytorch<1.9 + pred = self.detector(images)[0] + + bboxes, points = self._postprocess(images, origimgs, pred, conf_thres, iou_thres) + + # return bboxes, points + if not isListempty(points): + bboxes = np.array(bboxes).reshape(-1,4) + points = np.array(points).reshape(-1,10) + padding = bboxes[:,0].reshape(-1,1) + return np.concatenate((bboxes, padding, points), axis=1) + else: + return None + + def __call__(self, *args): + return self.predict(*args) diff --git a/repositories/codeformer/facelib/detection/yolov5face/models/__init__.py b/repositories/codeformer/facelib/detection/yolov5face/models/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/codeformer/facelib/detection/yolov5face/models/common.py b/repositories/codeformer/facelib/detection/yolov5face/models/common.py new file mode 100644 index 000000000..497a00444 --- /dev/null +++ b/repositories/codeformer/facelib/detection/yolov5face/models/common.py @@ -0,0 +1,299 @@ +# This file contains modules common to various models + +import math + +import numpy as np +import torch +from torch import nn + +from facelib.detection.yolov5face.utils.datasets import letterbox +from facelib.detection.yolov5face.utils.general import ( + make_divisible, + non_max_suppression, + scale_coords, + xyxy2xywh, +) + + +def autopad(k, p=None): # kernel, padding + # Pad to 'same' + if p is None: + p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad + return p + + +def channel_shuffle(x, groups): + batchsize, num_channels, height, width = x.data.size() + channels_per_group = torch.div(num_channels, groups, rounding_mode="trunc") + + # reshape + x = x.view(batchsize, groups, channels_per_group, height, width) + x = torch.transpose(x, 1, 2).contiguous() + + # flatten + return x.view(batchsize, -1, height, width) + + +def DWConv(c1, c2, k=1, s=1, act=True): + # Depthwise convolution + return Conv(c1, c2, k, s, g=math.gcd(c1, c2), act=act) + + +class Conv(nn.Module): + # Standard convolution + def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups + super().__init__() + self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False) + self.bn = nn.BatchNorm2d(c2) + self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity()) + + def forward(self, x): + return self.act(self.bn(self.conv(x))) + + def fuseforward(self, x): + return self.act(self.conv(x)) + + +class StemBlock(nn.Module): + def __init__(self, c1, c2, k=3, s=2, p=None, g=1, act=True): + super().__init__() + self.stem_1 = Conv(c1, c2, k, s, p, g, act) + self.stem_2a = Conv(c2, c2 // 2, 1, 1, 0) + self.stem_2b = Conv(c2 // 2, c2, 3, 2, 1) + self.stem_2p = nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True) + self.stem_3 = Conv(c2 * 2, c2, 1, 1, 0) + + def forward(self, x): + stem_1_out = self.stem_1(x) + stem_2a_out = self.stem_2a(stem_1_out) + stem_2b_out = self.stem_2b(stem_2a_out) + stem_2p_out = self.stem_2p(stem_1_out) + return self.stem_3(torch.cat((stem_2b_out, stem_2p_out), 1)) + + +class Bottleneck(nn.Module): + # Standard bottleneck + def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion + super().__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c_, c2, 3, 1, g=g) + self.add = shortcut and c1 == c2 + + def forward(self, x): + return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x)) + + +class BottleneckCSP(nn.Module): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False) + self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False) + self.cv4 = Conv(2 * c_, c2, 1, 1) + self.bn = nn.BatchNorm2d(2 * c_) # applied to cat(cv2, cv3) + self.act = nn.LeakyReLU(0.1, inplace=True) + self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n))) + + def forward(self, x): + y1 = self.cv3(self.m(self.cv1(x))) + y2 = self.cv2(x) + return self.cv4(self.act(self.bn(torch.cat((y1, y2), dim=1)))) + + +class C3(nn.Module): + # CSP Bottleneck with 3 convolutions + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c1, c_, 1, 1) + self.cv3 = Conv(2 * c_, c2, 1) # act=FReLU(c2) + self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n))) + + def forward(self, x): + return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1)) + + +class ShuffleV2Block(nn.Module): + def __init__(self, inp, oup, stride): + super().__init__() + + if not 1 <= stride <= 3: + raise ValueError("illegal stride value") + self.stride = stride + + branch_features = oup // 2 + + if self.stride > 1: + self.branch1 = nn.Sequential( + self.depthwise_conv(inp, inp, kernel_size=3, stride=self.stride, padding=1), + nn.BatchNorm2d(inp), + nn.Conv2d(inp, branch_features, kernel_size=1, stride=1, padding=0, bias=False), + nn.BatchNorm2d(branch_features), + nn.SiLU(), + ) + else: + self.branch1 = nn.Sequential() + + self.branch2 = nn.Sequential( + nn.Conv2d( + inp if (self.stride > 1) else branch_features, + branch_features, + kernel_size=1, + stride=1, + padding=0, + bias=False, + ), + nn.BatchNorm2d(branch_features), + nn.SiLU(), + self.depthwise_conv(branch_features, branch_features, kernel_size=3, stride=self.stride, padding=1), + nn.BatchNorm2d(branch_features), + nn.Conv2d(branch_features, branch_features, kernel_size=1, stride=1, padding=0, bias=False), + nn.BatchNorm2d(branch_features), + nn.SiLU(), + ) + + @staticmethod + def depthwise_conv(i, o, kernel_size, stride=1, padding=0, bias=False): + return nn.Conv2d(i, o, kernel_size, stride, padding, bias=bias, groups=i) + + def forward(self, x): + if self.stride == 1: + x1, x2 = x.chunk(2, dim=1) + out = torch.cat((x1, self.branch2(x2)), dim=1) + else: + out = torch.cat((self.branch1(x), self.branch2(x)), dim=1) + out = channel_shuffle(out, 2) + return out + + +class SPP(nn.Module): + # Spatial pyramid pooling layer used in YOLOv3-SPP + def __init__(self, c1, c2, k=(5, 9, 13)): + super().__init__() + c_ = c1 // 2 # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1) + self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k]) + + def forward(self, x): + x = self.cv1(x) + return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1)) + + +class Focus(nn.Module): + # Focus wh information into c-space + def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups + super().__init__() + self.conv = Conv(c1 * 4, c2, k, s, p, g, act) + + def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2) + return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1)) + + +class Concat(nn.Module): + # Concatenate a list of tensors along dimension + def __init__(self, dimension=1): + super().__init__() + self.d = dimension + + def forward(self, x): + return torch.cat(x, self.d) + + +class NMS(nn.Module): + # Non-Maximum Suppression (NMS) module + conf = 0.25 # confidence threshold + iou = 0.45 # IoU threshold + classes = None # (optional list) filter by class + + def forward(self, x): + return non_max_suppression(x[0], conf_thres=self.conf, iou_thres=self.iou, classes=self.classes) + + +class AutoShape(nn.Module): + # input-robust model wrapper for passing cv2/np/PIL/torch inputs. Includes preprocessing, inference and NMS + img_size = 640 # inference size (pixels) + conf = 0.25 # NMS confidence threshold + iou = 0.45 # NMS IoU threshold + classes = None # (optional list) filter by class + + def __init__(self, model): + super().__init__() + self.model = model.eval() + + def autoshape(self): + print("autoShape already enabled, skipping... ") # model already converted to model.autoshape() + return self + + def forward(self, imgs, size=640, augment=False, profile=False): + # Inference from various sources. For height=720, width=1280, RGB images example inputs are: + # OpenCV: = cv2.imread('image.jpg')[:,:,::-1] # HWC BGR to RGB x(720,1280,3) + # PIL: = Image.open('image.jpg') # HWC x(720,1280,3) + # numpy: = np.zeros((720,1280,3)) # HWC + # torch: = torch.zeros(16,3,720,1280) # BCHW + # multiple: = [Image.open('image1.jpg'), Image.open('image2.jpg'), ...] # list of images + + p = next(self.model.parameters()) # for device and type + if isinstance(imgs, torch.Tensor): # torch + return self.model(imgs.to(p.device).type_as(p), augment, profile) # inference + + # Pre-process + n, imgs = (len(imgs), imgs) if isinstance(imgs, list) else (1, [imgs]) # number of images, list of images + shape0, shape1 = [], [] # image and inference shapes + for i, im in enumerate(imgs): + im = np.array(im) # to numpy + if im.shape[0] < 5: # image in CHW + im = im.transpose((1, 2, 0)) # reverse dataloader .transpose(2, 0, 1) + im = im[:, :, :3] if im.ndim == 3 else np.tile(im[:, :, None], 3) # enforce 3ch input + s = im.shape[:2] # HWC + shape0.append(s) # image shape + g = size / max(s) # gain + shape1.append([y * g for y in s]) + imgs[i] = im # update + shape1 = [make_divisible(x, int(self.stride.max())) for x in np.stack(shape1, 0).max(0)] # inference shape + x = [letterbox(im, new_shape=shape1, auto=False)[0] for im in imgs] # pad + x = np.stack(x, 0) if n > 1 else x[0][None] # stack + x = np.ascontiguousarray(x.transpose((0, 3, 1, 2))) # BHWC to BCHW + x = torch.from_numpy(x).to(p.device).type_as(p) / 255.0 # uint8 to fp16/32 + + # Inference + with torch.no_grad(): + y = self.model(x, augment, profile)[0] # forward + y = non_max_suppression(y, conf_thres=self.conf, iou_thres=self.iou, classes=self.classes) # NMS + + # Post-process + for i in range(n): + scale_coords(shape1, y[i][:, :4], shape0[i]) + + return Detections(imgs, y, self.names) + + +class Detections: + # detections class for YOLOv5 inference results + def __init__(self, imgs, pred, names=None): + super().__init__() + d = pred[0].device # device + gn = [torch.tensor([*(im.shape[i] for i in [1, 0, 1, 0]), 1.0, 1.0], device=d) for im in imgs] # normalizations + self.imgs = imgs # list of images as numpy arrays + self.pred = pred # list of tensors pred[0] = (xyxy, conf, cls) + self.names = names # class names + self.xyxy = pred # xyxy pixels + self.xywh = [xyxy2xywh(x) for x in pred] # xywh pixels + self.xyxyn = [x / g for x, g in zip(self.xyxy, gn)] # xyxy normalized + self.xywhn = [x / g for x, g in zip(self.xywh, gn)] # xywh normalized + self.n = len(self.pred) + + def __len__(self): + return self.n + + def tolist(self): + # return a list of Detections objects, i.e. 'for result in results.tolist():' + x = [Detections([self.imgs[i]], [self.pred[i]], self.names) for i in range(self.n)] + for d in x: + for k in ["imgs", "pred", "xyxy", "xyxyn", "xywh", "xywhn"]: + setattr(d, k, getattr(d, k)[0]) # pop out of list + return x diff --git a/repositories/codeformer/facelib/detection/yolov5face/models/experimental.py b/repositories/codeformer/facelib/detection/yolov5face/models/experimental.py new file mode 100644 index 000000000..37ba4c442 --- /dev/null +++ b/repositories/codeformer/facelib/detection/yolov5face/models/experimental.py @@ -0,0 +1,45 @@ +# # This file contains experimental modules + +import numpy as np +import torch +from torch import nn + +from facelib.detection.yolov5face.models.common import Conv + + +class CrossConv(nn.Module): + # Cross Convolution Downsample + def __init__(self, c1, c2, k=3, s=1, g=1, e=1.0, shortcut=False): + # ch_in, ch_out, kernel, stride, groups, expansion, shortcut + super().__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, (1, k), (1, s)) + self.cv2 = Conv(c_, c2, (k, 1), (s, 1), g=g) + self.add = shortcut and c1 == c2 + + def forward(self, x): + return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x)) + + +class MixConv2d(nn.Module): + # Mixed Depthwise Conv https://arxiv.org/abs/1907.09595 + def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True): + super().__init__() + groups = len(k) + if equal_ch: # equal c_ per group + i = torch.linspace(0, groups - 1e-6, c2).floor() # c2 indices + c_ = [(i == g).sum() for g in range(groups)] # intermediate channels + else: # equal weight.numel() per group + b = [c2] + [0] * groups + a = np.eye(groups + 1, groups, k=-1) + a -= np.roll(a, 1, axis=1) + a *= np.array(k) ** 2 + a[0] = 1 + c_ = np.linalg.lstsq(a, b, rcond=None)[0].round() # solve for equal weight indices, ax = b + + self.m = nn.ModuleList([nn.Conv2d(c1, int(c_[g]), k[g], s, k[g] // 2, bias=False) for g in range(groups)]) + self.bn = nn.BatchNorm2d(c2) + self.act = nn.LeakyReLU(0.1, inplace=True) + + def forward(self, x): + return x + self.act(self.bn(torch.cat([m(x) for m in self.m], 1))) diff --git a/repositories/codeformer/facelib/detection/yolov5face/models/yolo.py b/repositories/codeformer/facelib/detection/yolov5face/models/yolo.py new file mode 100644 index 000000000..70845d972 --- /dev/null +++ b/repositories/codeformer/facelib/detection/yolov5face/models/yolo.py @@ -0,0 +1,235 @@ +import math +from copy import deepcopy +from pathlib import Path + +import torch +import yaml # for torch hub +from torch import nn + +from facelib.detection.yolov5face.models.common import ( + C3, + NMS, + SPP, + AutoShape, + Bottleneck, + BottleneckCSP, + Concat, + Conv, + DWConv, + Focus, + ShuffleV2Block, + StemBlock, +) +from facelib.detection.yolov5face.models.experimental import CrossConv, MixConv2d +from facelib.detection.yolov5face.utils.autoanchor import check_anchor_order +from facelib.detection.yolov5face.utils.general import make_divisible +from facelib.detection.yolov5face.utils.torch_utils import copy_attr, fuse_conv_and_bn + + +class Detect(nn.Module): + stride = None # strides computed during build + export = False # onnx export + + def __init__(self, nc=80, anchors=(), ch=()): # detection layer + super().__init__() + self.nc = nc # number of classes + self.no = nc + 5 + 10 # number of outputs per anchor + + self.nl = len(anchors) # number of detection layers + self.na = len(anchors[0]) // 2 # number of anchors + self.grid = [torch.zeros(1)] * self.nl # init grid + a = torch.tensor(anchors).float().view(self.nl, -1, 2) + self.register_buffer("anchors", a) # shape(nl,na,2) + self.register_buffer("anchor_grid", a.clone().view(self.nl, 1, -1, 1, 1, 2)) # shape(nl,1,na,1,1,2) + self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv + + def forward(self, x): + z = [] # inference output + if self.export: + for i in range(self.nl): + x[i] = self.m[i](x[i]) + return x + for i in range(self.nl): + x[i] = self.m[i](x[i]) # conv + bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85) + x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous() + + if not self.training: # inference + if self.grid[i].shape[2:4] != x[i].shape[2:4]: + self.grid[i] = self._make_grid(nx, ny).to(x[i].device) + + y = torch.full_like(x[i], 0) + y[..., [0, 1, 2, 3, 4, 15]] = x[i][..., [0, 1, 2, 3, 4, 15]].sigmoid() + y[..., 5:15] = x[i][..., 5:15] + + y[..., 0:2] = (y[..., 0:2] * 2.0 - 0.5 + self.grid[i].to(x[i].device)) * self.stride[i] # xy + y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh + + y[..., 5:7] = ( + y[..., 5:7] * self.anchor_grid[i] + self.grid[i].to(x[i].device) * self.stride[i] + ) # landmark x1 y1 + y[..., 7:9] = ( + y[..., 7:9] * self.anchor_grid[i] + self.grid[i].to(x[i].device) * self.stride[i] + ) # landmark x2 y2 + y[..., 9:11] = ( + y[..., 9:11] * self.anchor_grid[i] + self.grid[i].to(x[i].device) * self.stride[i] + ) # landmark x3 y3 + y[..., 11:13] = ( + y[..., 11:13] * self.anchor_grid[i] + self.grid[i].to(x[i].device) * self.stride[i] + ) # landmark x4 y4 + y[..., 13:15] = ( + y[..., 13:15] * self.anchor_grid[i] + self.grid[i].to(x[i].device) * self.stride[i] + ) # landmark x5 y5 + + z.append(y.view(bs, -1, self.no)) + + return x if self.training else (torch.cat(z, 1), x) + + @staticmethod + def _make_grid(nx=20, ny=20): + # yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)], indexing="ij") # for pytorch>=1.10 + yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)]) + return torch.stack((xv, yv), 2).view((1, 1, ny, nx, 2)).float() + + +class Model(nn.Module): + def __init__(self, cfg="yolov5s.yaml", ch=3, nc=None): # model, input channels, number of classes + super().__init__() + self.yaml_file = Path(cfg).name + with Path(cfg).open(encoding="utf8") as f: + self.yaml = yaml.safe_load(f) # model dict + + # Define model + ch = self.yaml["ch"] = self.yaml.get("ch", ch) # input channels + if nc and nc != self.yaml["nc"]: + self.yaml["nc"] = nc # override yaml value + + self.model, self.save = parse_model(deepcopy(self.yaml), ch=[ch]) # model, savelist + self.names = [str(i) for i in range(self.yaml["nc"])] # default names + + # Build strides, anchors + m = self.model[-1] # Detect() + if isinstance(m, Detect): + s = 128 # 2x min stride + m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))]) # forward + m.anchors /= m.stride.view(-1, 1, 1) + check_anchor_order(m) + self.stride = m.stride + self._initialize_biases() # only run once + + def forward(self, x): + return self.forward_once(x) # single-scale inference, train + + def forward_once(self, x): + y = [] # outputs + for m in self.model: + if m.f != -1: # if not from previous layer + x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f] # from earlier layers + + x = m(x) # run + y.append(x if m.i in self.save else None) # save output + + return x + + def _initialize_biases(self, cf=None): # initialize biases into Detect(), cf is class frequency + # https://arxiv.org/abs/1708.02002 section 3.3 + m = self.model[-1] # Detect() module + for mi, s in zip(m.m, m.stride): # from + b = mi.bias.view(m.na, -1) # conv.bias(255) to (3,85) + b.data[:, 4] += math.log(8 / (640 / s) ** 2) # obj (8 objects per 640 image) + b.data[:, 5:] += math.log(0.6 / (m.nc - 0.99)) if cf is None else torch.log(cf / cf.sum()) # cls + mi.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + + def _print_biases(self): + m = self.model[-1] # Detect() module + for mi in m.m: # from + b = mi.bias.detach().view(m.na, -1).T # conv.bias(255) to (3,85) + print(("%6g Conv2d.bias:" + "%10.3g" * 6) % (mi.weight.shape[1], *b[:5].mean(1).tolist(), b[5:].mean())) + + def fuse(self): # fuse model Conv2d() + BatchNorm2d() layers + print("Fusing layers... ") + for m in self.model.modules(): + if isinstance(m, Conv) and hasattr(m, "bn"): + m.conv = fuse_conv_and_bn(m.conv, m.bn) # update conv + delattr(m, "bn") # remove batchnorm + m.forward = m.fuseforward # update forward + elif type(m) is nn.Upsample: + m.recompute_scale_factor = None # torch 1.11.0 compatibility + return self + + def nms(self, mode=True): # add or remove NMS module + present = isinstance(self.model[-1], NMS) # last layer is NMS + if mode and not present: + print("Adding NMS... ") + m = NMS() # module + m.f = -1 # from + m.i = self.model[-1].i + 1 # index + self.model.add_module(name=str(m.i), module=m) # add + self.eval() + elif not mode and present: + print("Removing NMS... ") + self.model = self.model[:-1] # remove + return self + + def autoshape(self): # add autoShape module + print("Adding autoShape... ") + m = AutoShape(self) # wrap model + copy_attr(m, self, include=("yaml", "nc", "hyp", "names", "stride"), exclude=()) # copy attributes + return m + + +def parse_model(d, ch): # model_dict, input_channels(3) + anchors, nc, gd, gw = d["anchors"], d["nc"], d["depth_multiple"], d["width_multiple"] + na = (len(anchors[0]) // 2) if isinstance(anchors, list) else anchors # number of anchors + no = na * (nc + 5) # number of outputs = anchors * (classes + 5) + + layers, save, c2 = [], [], ch[-1] # layers, savelist, ch out + for i, (f, n, m, args) in enumerate(d["backbone"] + d["head"]): # from, number, module, args + m = eval(m) if isinstance(m, str) else m # eval strings + for j, a in enumerate(args): + try: + args[j] = eval(a) if isinstance(a, str) else a # eval strings + except: + pass + + n = max(round(n * gd), 1) if n > 1 else n # depth gain + if m in [ + Conv, + Bottleneck, + SPP, + DWConv, + MixConv2d, + Focus, + CrossConv, + BottleneckCSP, + C3, + ShuffleV2Block, + StemBlock, + ]: + c1, c2 = ch[f], args[0] + + c2 = make_divisible(c2 * gw, 8) if c2 != no else c2 + + args = [c1, c2, *args[1:]] + if m in [BottleneckCSP, C3]: + args.insert(2, n) + n = 1 + elif m is nn.BatchNorm2d: + args = [ch[f]] + elif m is Concat: + c2 = sum(ch[-1 if x == -1 else x + 1] for x in f) + elif m is Detect: + args.append([ch[x + 1] for x in f]) + if isinstance(args[1], int): # number of anchors + args[1] = [list(range(args[1] * 2))] * len(f) + else: + c2 = ch[f] + + m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args) # module + t = str(m)[8:-2].replace("__main__.", "") # module type + np = sum(x.numel() for x in m_.parameters()) # number params + m_.i, m_.f, m_.type, m_.np = i, f, t, np # attach index, 'from' index, type, number params + save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist + layers.append(m_) + ch.append(c2) + return nn.Sequential(*layers), sorted(save) diff --git a/repositories/codeformer/facelib/detection/yolov5face/models/yolov5l.yaml b/repositories/codeformer/facelib/detection/yolov5face/models/yolov5l.yaml new file mode 100644 index 000000000..0532b0e22 --- /dev/null +++ b/repositories/codeformer/facelib/detection/yolov5face/models/yolov5l.yaml @@ -0,0 +1,47 @@ +# parameters +nc: 1 # number of classes +depth_multiple: 1.0 # model depth multiple +width_multiple: 1.0 # layer channel multiple + +# anchors +anchors: + - [4,5, 8,10, 13,16] # P3/8 + - [23,29, 43,55, 73,105] # P4/16 + - [146,217, 231,300, 335,433] # P5/32 + +# YOLOv5 backbone +backbone: + # [from, number, module, args] + [[-1, 1, StemBlock, [64, 3, 2]], # 0-P1/2 + [-1, 3, C3, [128]], + [-1, 1, Conv, [256, 3, 2]], # 2-P3/8 + [-1, 9, C3, [256]], + [-1, 1, Conv, [512, 3, 2]], # 4-P4/16 + [-1, 9, C3, [512]], + [-1, 1, Conv, [1024, 3, 2]], # 6-P5/32 + [-1, 1, SPP, [1024, [3,5,7]]], + [-1, 3, C3, [1024, False]], # 8 + ] + +# YOLOv5 head +head: + [[-1, 1, Conv, [512, 1, 1]], + [-1, 1, nn.Upsample, [None, 2, 'nearest']], + [[-1, 5], 1, Concat, [1]], # cat backbone P4 + [-1, 3, C3, [512, False]], # 12 + + [-1, 1, Conv, [256, 1, 1]], + [-1, 1, nn.Upsample, [None, 2, 'nearest']], + [[-1, 3], 1, Concat, [1]], # cat backbone P3 + [-1, 3, C3, [256, False]], # 16 (P3/8-small) + + [-1, 1, Conv, [256, 3, 2]], + [[-1, 13], 1, Concat, [1]], # cat head P4 + [-1, 3, C3, [512, False]], # 19 (P4/16-medium) + + [-1, 1, Conv, [512, 3, 2]], + [[-1, 9], 1, Concat, [1]], # cat head P5 + [-1, 3, C3, [1024, False]], # 22 (P5/32-large) + + [[16, 19, 22], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) + ] \ No newline at end of file diff --git a/repositories/codeformer/facelib/detection/yolov5face/models/yolov5n.yaml b/repositories/codeformer/facelib/detection/yolov5face/models/yolov5n.yaml new file mode 100644 index 000000000..caba6bed6 --- /dev/null +++ b/repositories/codeformer/facelib/detection/yolov5face/models/yolov5n.yaml @@ -0,0 +1,45 @@ +# parameters +nc: 1 # number of classes +depth_multiple: 1.0 # model depth multiple +width_multiple: 1.0 # layer channel multiple + +# anchors +anchors: + - [4,5, 8,10, 13,16] # P3/8 + - [23,29, 43,55, 73,105] # P4/16 + - [146,217, 231,300, 335,433] # P5/32 + +# YOLOv5 backbone +backbone: + # [from, number, module, args] + [[-1, 1, StemBlock, [32, 3, 2]], # 0-P2/4 + [-1, 1, ShuffleV2Block, [128, 2]], # 1-P3/8 + [-1, 3, ShuffleV2Block, [128, 1]], # 2 + [-1, 1, ShuffleV2Block, [256, 2]], # 3-P4/16 + [-1, 7, ShuffleV2Block, [256, 1]], # 4 + [-1, 1, ShuffleV2Block, [512, 2]], # 5-P5/32 + [-1, 3, ShuffleV2Block, [512, 1]], # 6 + ] + +# YOLOv5 head +head: + [[-1, 1, Conv, [128, 1, 1]], + [-1, 1, nn.Upsample, [None, 2, 'nearest']], + [[-1, 4], 1, Concat, [1]], # cat backbone P4 + [-1, 1, C3, [128, False]], # 10 + + [-1, 1, Conv, [128, 1, 1]], + [-1, 1, nn.Upsample, [None, 2, 'nearest']], + [[-1, 2], 1, Concat, [1]], # cat backbone P3 + [-1, 1, C3, [128, False]], # 14 (P3/8-small) + + [-1, 1, Conv, [128, 3, 2]], + [[-1, 11], 1, Concat, [1]], # cat head P4 + [-1, 1, C3, [128, False]], # 17 (P4/16-medium) + + [-1, 1, Conv, [128, 3, 2]], + [[-1, 7], 1, Concat, [1]], # cat head P5 + [-1, 1, C3, [128, False]], # 20 (P5/32-large) + + [[14, 17, 20], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) + ] diff --git a/repositories/codeformer/facelib/detection/yolov5face/utils/__init__.py b/repositories/codeformer/facelib/detection/yolov5face/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/codeformer/facelib/detection/yolov5face/utils/autoanchor.py b/repositories/codeformer/facelib/detection/yolov5face/utils/autoanchor.py new file mode 100644 index 000000000..a4eba3e94 --- /dev/null +++ b/repositories/codeformer/facelib/detection/yolov5face/utils/autoanchor.py @@ -0,0 +1,12 @@ +# Auto-anchor utils + + +def check_anchor_order(m): + # Check anchor order against stride order for YOLOv5 Detect() module m, and correct if necessary + a = m.anchor_grid.prod(-1).view(-1) # anchor area + da = a[-1] - a[0] # delta a + ds = m.stride[-1] - m.stride[0] # delta s + if da.sign() != ds.sign(): # same order + print("Reversing anchor order") + m.anchors[:] = m.anchors.flip(0) + m.anchor_grid[:] = m.anchor_grid.flip(0) diff --git a/repositories/codeformer/facelib/detection/yolov5face/utils/datasets.py b/repositories/codeformer/facelib/detection/yolov5face/utils/datasets.py new file mode 100755 index 000000000..e672b136f --- /dev/null +++ b/repositories/codeformer/facelib/detection/yolov5face/utils/datasets.py @@ -0,0 +1,35 @@ +import cv2 +import numpy as np + + +def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scale_fill=False, scaleup=True): + # Resize image to a 32-pixel-multiple rectangle https://github.com/ultralytics/yolov3/issues/232 + shape = img.shape[:2] # current shape [height, width] + if isinstance(new_shape, int): + new_shape = (new_shape, new_shape) + + # Scale ratio (new / old) + r = min(new_shape[0] / shape[0], new_shape[1] / shape[1]) + if not scaleup: # only scale down, do not scale up (for better test mAP) + r = min(r, 1.0) + + # Compute padding + ratio = r, r # width, height ratios + new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r)) + dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding + if auto: # minimum rectangle + dw, dh = np.mod(dw, 64), np.mod(dh, 64) # wh padding + elif scale_fill: # stretch + dw, dh = 0.0, 0.0 + new_unpad = (new_shape[1], new_shape[0]) + ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] # width, height ratios + + dw /= 2 # divide padding into 2 sides + dh /= 2 + + if shape[::-1] != new_unpad: # resize + img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR) + top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1)) + left, right = int(round(dw - 0.1)), int(round(dw + 0.1)) + img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border + return img, ratio, (dw, dh) diff --git a/repositories/codeformer/facelib/detection/yolov5face/utils/extract_ckpt.py b/repositories/codeformer/facelib/detection/yolov5face/utils/extract_ckpt.py new file mode 100644 index 000000000..4b8b63134 --- /dev/null +++ b/repositories/codeformer/facelib/detection/yolov5face/utils/extract_ckpt.py @@ -0,0 +1,5 @@ +import torch +import sys +sys.path.insert(0,'./facelib/detection/yolov5face') +model = torch.load('facelib/detection/yolov5face/yolov5n-face.pt', map_location='cpu')['model'] +torch.save(model.state_dict(),'weights/facelib/yolov5n-face.pth') \ No newline at end of file diff --git a/repositories/codeformer/facelib/detection/yolov5face/utils/general.py b/repositories/codeformer/facelib/detection/yolov5face/utils/general.py new file mode 100755 index 000000000..1c8e14f56 --- /dev/null +++ b/repositories/codeformer/facelib/detection/yolov5face/utils/general.py @@ -0,0 +1,271 @@ +import math +import time + +import numpy as np +import torch +import torchvision + + +def check_img_size(img_size, s=32): + # Verify img_size is a multiple of stride s + new_size = make_divisible(img_size, int(s)) # ceil gs-multiple + # if new_size != img_size: + # print(f"WARNING: --img-size {img_size:g} must be multiple of max stride {s:g}, updating to {new_size:g}") + return new_size + + +def make_divisible(x, divisor): + # Returns x evenly divisible by divisor + return math.ceil(x / divisor) * divisor + + +def xyxy2xywh(x): + # Convert nx4 boxes from [x1, y1, x2, y2] to [x, y, w, h] where xy1=top-left, xy2=bottom-right + y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) + y[:, 0] = (x[:, 0] + x[:, 2]) / 2 # x center + y[:, 1] = (x[:, 1] + x[:, 3]) / 2 # y center + y[:, 2] = x[:, 2] - x[:, 0] # width + y[:, 3] = x[:, 3] - x[:, 1] # height + return y + + +def xywh2xyxy(x): + # Convert nx4 boxes from [x, y, w, h] to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right + y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) + y[:, 0] = x[:, 0] - x[:, 2] / 2 # top left x + y[:, 1] = x[:, 1] - x[:, 3] / 2 # top left y + y[:, 2] = x[:, 0] + x[:, 2] / 2 # bottom right x + y[:, 3] = x[:, 1] + x[:, 3] / 2 # bottom right y + return y + + +def scale_coords(img1_shape, coords, img0_shape, ratio_pad=None): + # Rescale coords (xyxy) from img1_shape to img0_shape + if ratio_pad is None: # calculate from img0_shape + gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]) # gain = old / new + pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2 # wh padding + else: + gain = ratio_pad[0][0] + pad = ratio_pad[1] + + coords[:, [0, 2]] -= pad[0] # x padding + coords[:, [1, 3]] -= pad[1] # y padding + coords[:, :4] /= gain + clip_coords(coords, img0_shape) + return coords + + +def clip_coords(boxes, img_shape): + # Clip bounding xyxy bounding boxes to image shape (height, width) + boxes[:, 0].clamp_(0, img_shape[1]) # x1 + boxes[:, 1].clamp_(0, img_shape[0]) # y1 + boxes[:, 2].clamp_(0, img_shape[1]) # x2 + boxes[:, 3].clamp_(0, img_shape[0]) # y2 + + +def box_iou(box1, box2): + # https://github.com/pytorch/vision/blob/master/torchvision/ops/boxes.py + """ + Return intersection-over-union (Jaccard index) of boxes. + Both sets of boxes are expected to be in (x1, y1, x2, y2) format. + Arguments: + box1 (Tensor[N, 4]) + box2 (Tensor[M, 4]) + Returns: + iou (Tensor[N, M]): the NxM matrix containing the pairwise + IoU values for every element in boxes1 and boxes2 + """ + + def box_area(box): + return (box[2] - box[0]) * (box[3] - box[1]) + + area1 = box_area(box1.T) + area2 = box_area(box2.T) + + inter = (torch.min(box1[:, None, 2:], box2[:, 2:]) - torch.max(box1[:, None, :2], box2[:, :2])).clamp(0).prod(2) + return inter / (area1[:, None] + area2 - inter) + + +def non_max_suppression_face(prediction, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, labels=()): + """Performs Non-Maximum Suppression (NMS) on inference results + Returns: + detections with shape: nx6 (x1, y1, x2, y2, conf, cls) + """ + + nc = prediction.shape[2] - 15 # number of classes + xc = prediction[..., 4] > conf_thres # candidates + + # Settings + # (pixels) maximum box width and height + max_wh = 4096 + time_limit = 10.0 # seconds to quit after + redundant = True # require redundant detections + multi_label = nc > 1 # multiple labels per box (adds 0.5ms/img) + merge = False # use merge-NMS + + t = time.time() + output = [torch.zeros((0, 16), device=prediction.device)] * prediction.shape[0] + for xi, x in enumerate(prediction): # image index, image inference + # Apply constraints + x = x[xc[xi]] # confidence + + # Cat apriori labels if autolabelling + if labels and len(labels[xi]): + label = labels[xi] + v = torch.zeros((len(label), nc + 15), device=x.device) + v[:, :4] = label[:, 1:5] # box + v[:, 4] = 1.0 # conf + v[range(len(label)), label[:, 0].long() + 15] = 1.0 # cls + x = torch.cat((x, v), 0) + + # If none remain process next image + if not x.shape[0]: + continue + + # Compute conf + x[:, 15:] *= x[:, 4:5] # conf = obj_conf * cls_conf + + # Box (center x, center y, width, height) to (x1, y1, x2, y2) + box = xywh2xyxy(x[:, :4]) + + # Detections matrix nx6 (xyxy, conf, landmarks, cls) + if multi_label: + i, j = (x[:, 15:] > conf_thres).nonzero(as_tuple=False).T + x = torch.cat((box[i], x[i, j + 15, None], x[:, 5:15], j[:, None].float()), 1) + else: # best class only + conf, j = x[:, 15:].max(1, keepdim=True) + x = torch.cat((box, conf, x[:, 5:15], j.float()), 1)[conf.view(-1) > conf_thres] + + # Filter by class + if classes is not None: + x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)] + + # If none remain process next image + n = x.shape[0] # number of boxes + if not n: + continue + + # Batched NMS + c = x[:, 15:16] * (0 if agnostic else max_wh) # classes + boxes, scores = x[:, :4] + c, x[:, 4] # boxes (offset by class), scores + i = torchvision.ops.nms(boxes, scores, iou_thres) # NMS + + if merge and (1 < n < 3e3): # Merge NMS (boxes merged using weighted mean) + # update boxes as boxes(i,4) = weights(i,n) * boxes(n,4) + iou = box_iou(boxes[i], boxes) > iou_thres # iou matrix + weights = iou * scores[None] # box weights + x[i, :4] = torch.mm(weights, x[:, :4]).float() / weights.sum(1, keepdim=True) # merged boxes + if redundant: + i = i[iou.sum(1) > 1] # require redundancy + + output[xi] = x[i] + if (time.time() - t) > time_limit: + break # time limit exceeded + + return output + + +def non_max_suppression(prediction, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, labels=()): + """Performs Non-Maximum Suppression (NMS) on inference results + + Returns: + detections with shape: nx6 (x1, y1, x2, y2, conf, cls) + """ + + nc = prediction.shape[2] - 5 # number of classes + xc = prediction[..., 4] > conf_thres # candidates + + # Settings + # (pixels) maximum box width and height + max_wh = 4096 + time_limit = 10.0 # seconds to quit after + redundant = True # require redundant detections + multi_label = nc > 1 # multiple labels per box (adds 0.5ms/img) + merge = False # use merge-NMS + + t = time.time() + output = [torch.zeros((0, 6), device=prediction.device)] * prediction.shape[0] + for xi, x in enumerate(prediction): # image index, image inference + x = x[xc[xi]] # confidence + + # Cat apriori labels if autolabelling + if labels and len(labels[xi]): + label_id = labels[xi] + v = torch.zeros((len(label_id), nc + 5), device=x.device) + v[:, :4] = label_id[:, 1:5] # box + v[:, 4] = 1.0 # conf + v[range(len(label_id)), label_id[:, 0].long() + 5] = 1.0 # cls + x = torch.cat((x, v), 0) + + # If none remain process next image + if not x.shape[0]: + continue + + # Compute conf + x[:, 5:] *= x[:, 4:5] # conf = obj_conf * cls_conf + + # Box (center x, center y, width, height) to (x1, y1, x2, y2) + box = xywh2xyxy(x[:, :4]) + + # Detections matrix nx6 (xyxy, conf, cls) + if multi_label: + i, j = (x[:, 5:] > conf_thres).nonzero(as_tuple=False).T + x = torch.cat((box[i], x[i, j + 5, None], j[:, None].float()), 1) + else: # best class only + conf, j = x[:, 5:].max(1, keepdim=True) + x = torch.cat((box, conf, j.float()), 1)[conf.view(-1) > conf_thres] + + # Filter by class + if classes is not None: + x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)] + + # Check shape + n = x.shape[0] # number of boxes + if not n: # no boxes + continue + + x = x[x[:, 4].argsort(descending=True)] # sort by confidence + + # Batched NMS + c = x[:, 5:6] * (0 if agnostic else max_wh) # classes + boxes, scores = x[:, :4] + c, x[:, 4] # boxes (offset by class), scores + i = torchvision.ops.nms(boxes, scores, iou_thres) # NMS + if merge and (1 < n < 3e3): # Merge NMS (boxes merged using weighted mean) + # update boxes as boxes(i,4) = weights(i,n) * boxes(n,4) + iou = box_iou(boxes[i], boxes) > iou_thres # iou matrix + weights = iou * scores[None] # box weights + x[i, :4] = torch.mm(weights, x[:, :4]).float() / weights.sum(1, keepdim=True) # merged boxes + if redundant: + i = i[iou.sum(1) > 1] # require redundancy + + output[xi] = x[i] + if (time.time() - t) > time_limit: + print(f"WARNING: NMS time limit {time_limit}s exceeded") + break # time limit exceeded + + return output + + +def scale_coords_landmarks(img1_shape, coords, img0_shape, ratio_pad=None): + # Rescale coords (xyxy) from img1_shape to img0_shape + if ratio_pad is None: # calculate from img0_shape + gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]) # gain = old / new + pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2 # wh padding + else: + gain = ratio_pad[0][0] + pad = ratio_pad[1] + + coords[:, [0, 2, 4, 6, 8]] -= pad[0] # x padding + coords[:, [1, 3, 5, 7, 9]] -= pad[1] # y padding + coords[:, :10] /= gain + coords[:, 0].clamp_(0, img0_shape[1]) # x1 + coords[:, 1].clamp_(0, img0_shape[0]) # y1 + coords[:, 2].clamp_(0, img0_shape[1]) # x2 + coords[:, 3].clamp_(0, img0_shape[0]) # y2 + coords[:, 4].clamp_(0, img0_shape[1]) # x3 + coords[:, 5].clamp_(0, img0_shape[0]) # y3 + coords[:, 6].clamp_(0, img0_shape[1]) # x4 + coords[:, 7].clamp_(0, img0_shape[0]) # y4 + coords[:, 8].clamp_(0, img0_shape[1]) # x5 + coords[:, 9].clamp_(0, img0_shape[0]) # y5 + return coords diff --git a/repositories/codeformer/facelib/detection/yolov5face/utils/torch_utils.py b/repositories/codeformer/facelib/detection/yolov5face/utils/torch_utils.py new file mode 100644 index 000000000..af2d06587 --- /dev/null +++ b/repositories/codeformer/facelib/detection/yolov5face/utils/torch_utils.py @@ -0,0 +1,40 @@ +import torch +from torch import nn + + +def fuse_conv_and_bn(conv, bn): + # Fuse convolution and batchnorm layers https://tehnokv.com/posts/fusing-batchnorm-and-conv/ + fusedconv = ( + nn.Conv2d( + conv.in_channels, + conv.out_channels, + kernel_size=conv.kernel_size, + stride=conv.stride, + padding=conv.padding, + groups=conv.groups, + bias=True, + ) + .requires_grad_(False) + .to(conv.weight.device) + ) + + # prepare filters + w_conv = conv.weight.clone().view(conv.out_channels, -1) + w_bn = torch.diag(bn.weight.div(torch.sqrt(bn.eps + bn.running_var))) + fusedconv.weight.copy_(torch.mm(w_bn, w_conv).view(fusedconv.weight.size())) + + # prepare spatial bias + b_conv = torch.zeros(conv.weight.size(0), device=conv.weight.device) if conv.bias is None else conv.bias + b_bn = bn.bias - bn.weight.mul(bn.running_mean).div(torch.sqrt(bn.running_var + bn.eps)) + fusedconv.bias.copy_(torch.mm(w_bn, b_conv.reshape(-1, 1)).reshape(-1) + b_bn) + + return fusedconv + + +def copy_attr(a, b, include=(), exclude=()): + # Copy attributes from b to a, options to only include [...] and to exclude [...] + for k, v in b.__dict__.items(): + if (include and k not in include) or k.startswith("_") or k in exclude: + continue + + setattr(a, k, v) diff --git a/repositories/codeformer/facelib/parsing/__init__.py b/repositories/codeformer/facelib/parsing/__init__.py new file mode 100644 index 000000000..72656e4b5 --- /dev/null +++ b/repositories/codeformer/facelib/parsing/__init__.py @@ -0,0 +1,23 @@ +import torch + +from facelib.utils import load_file_from_url +from .bisenet import BiSeNet +from .parsenet import ParseNet + + +def init_parsing_model(model_name='bisenet', half=False, device='cuda'): + if model_name == 'bisenet': + model = BiSeNet(num_class=19) + model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/parsing_bisenet.pth' + elif model_name == 'parsenet': + model = ParseNet(in_size=512, out_size=512, parsing_ch=19) + model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/parsing_parsenet.pth' + else: + raise NotImplementedError(f'{model_name} is not implemented.') + + model_path = load_file_from_url(url=model_url, model_dir='weights/facelib', progress=True, file_name=None) + load_net = torch.load(model_path, map_location=lambda storage, loc: storage) + model.load_state_dict(load_net, strict=True) + model.eval() + model = model.to(device) + return model diff --git a/repositories/codeformer/facelib/parsing/bisenet.py b/repositories/codeformer/facelib/parsing/bisenet.py new file mode 100644 index 000000000..3898cab76 --- /dev/null +++ b/repositories/codeformer/facelib/parsing/bisenet.py @@ -0,0 +1,140 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + +from .resnet import ResNet18 + + +class ConvBNReLU(nn.Module): + + def __init__(self, in_chan, out_chan, ks=3, stride=1, padding=1): + super(ConvBNReLU, self).__init__() + self.conv = nn.Conv2d(in_chan, out_chan, kernel_size=ks, stride=stride, padding=padding, bias=False) + self.bn = nn.BatchNorm2d(out_chan) + + def forward(self, x): + x = self.conv(x) + x = F.relu(self.bn(x)) + return x + + +class BiSeNetOutput(nn.Module): + + def __init__(self, in_chan, mid_chan, num_class): + super(BiSeNetOutput, self).__init__() + self.conv = ConvBNReLU(in_chan, mid_chan, ks=3, stride=1, padding=1) + self.conv_out = nn.Conv2d(mid_chan, num_class, kernel_size=1, bias=False) + + def forward(self, x): + feat = self.conv(x) + out = self.conv_out(feat) + return out, feat + + +class AttentionRefinementModule(nn.Module): + + def __init__(self, in_chan, out_chan): + super(AttentionRefinementModule, self).__init__() + self.conv = ConvBNReLU(in_chan, out_chan, ks=3, stride=1, padding=1) + self.conv_atten = nn.Conv2d(out_chan, out_chan, kernel_size=1, bias=False) + self.bn_atten = nn.BatchNorm2d(out_chan) + self.sigmoid_atten = nn.Sigmoid() + + def forward(self, x): + feat = self.conv(x) + atten = F.avg_pool2d(feat, feat.size()[2:]) + atten = self.conv_atten(atten) + atten = self.bn_atten(atten) + atten = self.sigmoid_atten(atten) + out = torch.mul(feat, atten) + return out + + +class ContextPath(nn.Module): + + def __init__(self): + super(ContextPath, self).__init__() + self.resnet = ResNet18() + self.arm16 = AttentionRefinementModule(256, 128) + self.arm32 = AttentionRefinementModule(512, 128) + self.conv_head32 = ConvBNReLU(128, 128, ks=3, stride=1, padding=1) + self.conv_head16 = ConvBNReLU(128, 128, ks=3, stride=1, padding=1) + self.conv_avg = ConvBNReLU(512, 128, ks=1, stride=1, padding=0) + + def forward(self, x): + feat8, feat16, feat32 = self.resnet(x) + h8, w8 = feat8.size()[2:] + h16, w16 = feat16.size()[2:] + h32, w32 = feat32.size()[2:] + + avg = F.avg_pool2d(feat32, feat32.size()[2:]) + avg = self.conv_avg(avg) + avg_up = F.interpolate(avg, (h32, w32), mode='nearest') + + feat32_arm = self.arm32(feat32) + feat32_sum = feat32_arm + avg_up + feat32_up = F.interpolate(feat32_sum, (h16, w16), mode='nearest') + feat32_up = self.conv_head32(feat32_up) + + feat16_arm = self.arm16(feat16) + feat16_sum = feat16_arm + feat32_up + feat16_up = F.interpolate(feat16_sum, (h8, w8), mode='nearest') + feat16_up = self.conv_head16(feat16_up) + + return feat8, feat16_up, feat32_up # x8, x8, x16 + + +class FeatureFusionModule(nn.Module): + + def __init__(self, in_chan, out_chan): + super(FeatureFusionModule, self).__init__() + self.convblk = ConvBNReLU(in_chan, out_chan, ks=1, stride=1, padding=0) + self.conv1 = nn.Conv2d(out_chan, out_chan // 4, kernel_size=1, stride=1, padding=0, bias=False) + self.conv2 = nn.Conv2d(out_chan // 4, out_chan, kernel_size=1, stride=1, padding=0, bias=False) + self.relu = nn.ReLU(inplace=True) + self.sigmoid = nn.Sigmoid() + + def forward(self, fsp, fcp): + fcat = torch.cat([fsp, fcp], dim=1) + feat = self.convblk(fcat) + atten = F.avg_pool2d(feat, feat.size()[2:]) + atten = self.conv1(atten) + atten = self.relu(atten) + atten = self.conv2(atten) + atten = self.sigmoid(atten) + feat_atten = torch.mul(feat, atten) + feat_out = feat_atten + feat + return feat_out + + +class BiSeNet(nn.Module): + + def __init__(self, num_class): + super(BiSeNet, self).__init__() + self.cp = ContextPath() + self.ffm = FeatureFusionModule(256, 256) + self.conv_out = BiSeNetOutput(256, 256, num_class) + self.conv_out16 = BiSeNetOutput(128, 64, num_class) + self.conv_out32 = BiSeNetOutput(128, 64, num_class) + + def forward(self, x, return_feat=False): + h, w = x.size()[2:] + feat_res8, feat_cp8, feat_cp16 = self.cp(x) # return res3b1 feature + feat_sp = feat_res8 # replace spatial path feature with res3b1 feature + feat_fuse = self.ffm(feat_sp, feat_cp8) + + out, feat = self.conv_out(feat_fuse) + out16, feat16 = self.conv_out16(feat_cp8) + out32, feat32 = self.conv_out32(feat_cp16) + + out = F.interpolate(out, (h, w), mode='bilinear', align_corners=True) + out16 = F.interpolate(out16, (h, w), mode='bilinear', align_corners=True) + out32 = F.interpolate(out32, (h, w), mode='bilinear', align_corners=True) + + if return_feat: + feat = F.interpolate(feat, (h, w), mode='bilinear', align_corners=True) + feat16 = F.interpolate(feat16, (h, w), mode='bilinear', align_corners=True) + feat32 = F.interpolate(feat32, (h, w), mode='bilinear', align_corners=True) + return out, out16, out32, feat, feat16, feat32 + else: + return out, out16, out32 diff --git a/repositories/codeformer/facelib/parsing/parsenet.py b/repositories/codeformer/facelib/parsing/parsenet.py new file mode 100644 index 000000000..e178ebe43 --- /dev/null +++ b/repositories/codeformer/facelib/parsing/parsenet.py @@ -0,0 +1,194 @@ +"""Modified from https://github.com/chaofengc/PSFRGAN +""" +import numpy as np +import torch.nn as nn +from torch.nn import functional as F + + +class NormLayer(nn.Module): + """Normalization Layers. + + Args: + channels: input channels, for batch norm and instance norm. + input_size: input shape without batch size, for layer norm. + """ + + def __init__(self, channels, normalize_shape=None, norm_type='bn'): + super(NormLayer, self).__init__() + norm_type = norm_type.lower() + self.norm_type = norm_type + if norm_type == 'bn': + self.norm = nn.BatchNorm2d(channels, affine=True) + elif norm_type == 'in': + self.norm = nn.InstanceNorm2d(channels, affine=False) + elif norm_type == 'gn': + self.norm = nn.GroupNorm(32, channels, affine=True) + elif norm_type == 'pixel': + self.norm = lambda x: F.normalize(x, p=2, dim=1) + elif norm_type == 'layer': + self.norm = nn.LayerNorm(normalize_shape) + elif norm_type == 'none': + self.norm = lambda x: x * 1.0 + else: + assert 1 == 0, f'Norm type {norm_type} not support.' + + def forward(self, x, ref=None): + if self.norm_type == 'spade': + return self.norm(x, ref) + else: + return self.norm(x) + + +class ReluLayer(nn.Module): + """Relu Layer. + + Args: + relu type: type of relu layer, candidates are + - ReLU + - LeakyReLU: default relu slope 0.2 + - PRelu + - SELU + - none: direct pass + """ + + def __init__(self, channels, relu_type='relu'): + super(ReluLayer, self).__init__() + relu_type = relu_type.lower() + if relu_type == 'relu': + self.func = nn.ReLU(True) + elif relu_type == 'leakyrelu': + self.func = nn.LeakyReLU(0.2, inplace=True) + elif relu_type == 'prelu': + self.func = nn.PReLU(channels) + elif relu_type == 'selu': + self.func = nn.SELU(True) + elif relu_type == 'none': + self.func = lambda x: x * 1.0 + else: + assert 1 == 0, f'Relu type {relu_type} not support.' + + def forward(self, x): + return self.func(x) + + +class ConvLayer(nn.Module): + + def __init__(self, + in_channels, + out_channels, + kernel_size=3, + scale='none', + norm_type='none', + relu_type='none', + use_pad=True, + bias=True): + super(ConvLayer, self).__init__() + self.use_pad = use_pad + self.norm_type = norm_type + if norm_type in ['bn']: + bias = False + + stride = 2 if scale == 'down' else 1 + + self.scale_func = lambda x: x + if scale == 'up': + self.scale_func = lambda x: nn.functional.interpolate(x, scale_factor=2, mode='nearest') + + self.reflection_pad = nn.ReflectionPad2d(int(np.ceil((kernel_size - 1.) / 2))) + self.conv2d = nn.Conv2d(in_channels, out_channels, kernel_size, stride, bias=bias) + + self.relu = ReluLayer(out_channels, relu_type) + self.norm = NormLayer(out_channels, norm_type=norm_type) + + def forward(self, x): + out = self.scale_func(x) + if self.use_pad: + out = self.reflection_pad(out) + out = self.conv2d(out) + out = self.norm(out) + out = self.relu(out) + return out + + +class ResidualBlock(nn.Module): + """ + Residual block recommended in: http://torch.ch/blog/2016/02/04/resnets.html + """ + + def __init__(self, c_in, c_out, relu_type='prelu', norm_type='bn', scale='none'): + super(ResidualBlock, self).__init__() + + if scale == 'none' and c_in == c_out: + self.shortcut_func = lambda x: x + else: + self.shortcut_func = ConvLayer(c_in, c_out, 3, scale) + + scale_config_dict = {'down': ['none', 'down'], 'up': ['up', 'none'], 'none': ['none', 'none']} + scale_conf = scale_config_dict[scale] + + self.conv1 = ConvLayer(c_in, c_out, 3, scale_conf[0], norm_type=norm_type, relu_type=relu_type) + self.conv2 = ConvLayer(c_out, c_out, 3, scale_conf[1], norm_type=norm_type, relu_type='none') + + def forward(self, x): + identity = self.shortcut_func(x) + + res = self.conv1(x) + res = self.conv2(res) + return identity + res + + +class ParseNet(nn.Module): + + def __init__(self, + in_size=128, + out_size=128, + min_feat_size=32, + base_ch=64, + parsing_ch=19, + res_depth=10, + relu_type='LeakyReLU', + norm_type='bn', + ch_range=[32, 256]): + super().__init__() + self.res_depth = res_depth + act_args = {'norm_type': norm_type, 'relu_type': relu_type} + min_ch, max_ch = ch_range + + ch_clip = lambda x: max(min_ch, min(x, max_ch)) # noqa: E731 + min_feat_size = min(in_size, min_feat_size) + + down_steps = int(np.log2(in_size // min_feat_size)) + up_steps = int(np.log2(out_size // min_feat_size)) + + # =============== define encoder-body-decoder ==================== + self.encoder = [] + self.encoder.append(ConvLayer(3, base_ch, 3, 1)) + head_ch = base_ch + for i in range(down_steps): + cin, cout = ch_clip(head_ch), ch_clip(head_ch * 2) + self.encoder.append(ResidualBlock(cin, cout, scale='down', **act_args)) + head_ch = head_ch * 2 + + self.body = [] + for i in range(res_depth): + self.body.append(ResidualBlock(ch_clip(head_ch), ch_clip(head_ch), **act_args)) + + self.decoder = [] + for i in range(up_steps): + cin, cout = ch_clip(head_ch), ch_clip(head_ch // 2) + self.decoder.append(ResidualBlock(cin, cout, scale='up', **act_args)) + head_ch = head_ch // 2 + + self.encoder = nn.Sequential(*self.encoder) + self.body = nn.Sequential(*self.body) + self.decoder = nn.Sequential(*self.decoder) + self.out_img_conv = ConvLayer(ch_clip(head_ch), 3) + self.out_mask_conv = ConvLayer(ch_clip(head_ch), parsing_ch) + + def forward(self, x): + feat = self.encoder(x) + x = feat + self.body(feat) + x = self.decoder(x) + out_img = self.out_img_conv(x) + out_mask = self.out_mask_conv(x) + return out_mask, out_img diff --git a/repositories/codeformer/facelib/parsing/resnet.py b/repositories/codeformer/facelib/parsing/resnet.py new file mode 100644 index 000000000..fec8e82cf --- /dev/null +++ b/repositories/codeformer/facelib/parsing/resnet.py @@ -0,0 +1,69 @@ +import torch.nn as nn +import torch.nn.functional as F + + +def conv3x3(in_planes, out_planes, stride=1): + """3x3 convolution with padding""" + return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False) + + +class BasicBlock(nn.Module): + + def __init__(self, in_chan, out_chan, stride=1): + super(BasicBlock, self).__init__() + self.conv1 = conv3x3(in_chan, out_chan, stride) + self.bn1 = nn.BatchNorm2d(out_chan) + self.conv2 = conv3x3(out_chan, out_chan) + self.bn2 = nn.BatchNorm2d(out_chan) + self.relu = nn.ReLU(inplace=True) + self.downsample = None + if in_chan != out_chan or stride != 1: + self.downsample = nn.Sequential( + nn.Conv2d(in_chan, out_chan, kernel_size=1, stride=stride, bias=False), + nn.BatchNorm2d(out_chan), + ) + + def forward(self, x): + residual = self.conv1(x) + residual = F.relu(self.bn1(residual)) + residual = self.conv2(residual) + residual = self.bn2(residual) + + shortcut = x + if self.downsample is not None: + shortcut = self.downsample(x) + + out = shortcut + residual + out = self.relu(out) + return out + + +def create_layer_basic(in_chan, out_chan, bnum, stride=1): + layers = [BasicBlock(in_chan, out_chan, stride=stride)] + for i in range(bnum - 1): + layers.append(BasicBlock(out_chan, out_chan, stride=1)) + return nn.Sequential(*layers) + + +class ResNet18(nn.Module): + + def __init__(self): + super(ResNet18, self).__init__() + self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False) + self.bn1 = nn.BatchNorm2d(64) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + self.layer1 = create_layer_basic(64, 64, bnum=2, stride=1) + self.layer2 = create_layer_basic(64, 128, bnum=2, stride=2) + self.layer3 = create_layer_basic(128, 256, bnum=2, stride=2) + self.layer4 = create_layer_basic(256, 512, bnum=2, stride=2) + + def forward(self, x): + x = self.conv1(x) + x = F.relu(self.bn1(x)) + x = self.maxpool(x) + + x = self.layer1(x) + feat8 = self.layer2(x) # 1/8 + feat16 = self.layer3(feat8) # 1/16 + feat32 = self.layer4(feat16) # 1/32 + return feat8, feat16, feat32 diff --git a/repositories/codeformer/facelib/utils/__init__.py b/repositories/codeformer/facelib/utils/__init__.py new file mode 100644 index 000000000..f03b1c2ba --- /dev/null +++ b/repositories/codeformer/facelib/utils/__init__.py @@ -0,0 +1,7 @@ +from .face_utils import align_crop_face_landmarks, compute_increased_bbox, get_valid_bboxes, paste_face_back +from .misc import img2tensor, load_file_from_url, download_pretrained_models, scandir + +__all__ = [ + 'align_crop_face_landmarks', 'compute_increased_bbox', 'get_valid_bboxes', 'load_file_from_url', + 'download_pretrained_models', 'paste_face_back', 'img2tensor', 'scandir' +] diff --git a/repositories/codeformer/facelib/utils/face_restoration_helper.py b/repositories/codeformer/facelib/utils/face_restoration_helper.py new file mode 100644 index 000000000..5d3fb8f3b --- /dev/null +++ b/repositories/codeformer/facelib/utils/face_restoration_helper.py @@ -0,0 +1,460 @@ +import cv2 +import numpy as np +import os +import torch +from torchvision.transforms.functional import normalize + +from facelib.detection import init_detection_model +from facelib.parsing import init_parsing_model +from facelib.utils.misc import img2tensor, imwrite, is_gray, bgr2gray + + +def get_largest_face(det_faces, h, w): + + def get_location(val, length): + if val < 0: + return 0 + elif val > length: + return length + else: + return val + + face_areas = [] + for det_face in det_faces: + left = get_location(det_face[0], w) + right = get_location(det_face[2], w) + top = get_location(det_face[1], h) + bottom = get_location(det_face[3], h) + face_area = (right - left) * (bottom - top) + face_areas.append(face_area) + largest_idx = face_areas.index(max(face_areas)) + return det_faces[largest_idx], largest_idx + + +def get_center_face(det_faces, h=0, w=0, center=None): + if center is not None: + center = np.array(center) + else: + center = np.array([w / 2, h / 2]) + center_dist = [] + for det_face in det_faces: + face_center = np.array([(det_face[0] + det_face[2]) / 2, (det_face[1] + det_face[3]) / 2]) + dist = np.linalg.norm(face_center - center) + center_dist.append(dist) + center_idx = center_dist.index(min(center_dist)) + return det_faces[center_idx], center_idx + + +class FaceRestoreHelper(object): + """Helper for the face restoration pipeline (base class).""" + + def __init__(self, + upscale_factor, + face_size=512, + crop_ratio=(1, 1), + det_model='retinaface_resnet50', + save_ext='png', + template_3points=False, + pad_blur=False, + use_parse=False, + device=None): + self.template_3points = template_3points # improve robustness + self.upscale_factor = int(upscale_factor) + # the cropped face ratio based on the square face + self.crop_ratio = crop_ratio # (h, w) + assert (self.crop_ratio[0] >= 1 and self.crop_ratio[1] >= 1), 'crop ration only supports >=1' + self.face_size = (int(face_size * self.crop_ratio[1]), int(face_size * self.crop_ratio[0])) + + if self.template_3points: + self.face_template = np.array([[192, 240], [319, 240], [257, 371]]) + else: + # standard 5 landmarks for FFHQ faces with 512 x 512 + # facexlib + self.face_template = np.array([[192.98138, 239.94708], [318.90277, 240.1936], [256.63416, 314.01935], + [201.26117, 371.41043], [313.08905, 371.15118]]) + + # dlib: left_eye: 36:41 right_eye: 42:47 nose: 30,32,33,34 left mouth corner: 48 right mouth corner: 54 + # self.face_template = np.array([[193.65928, 242.98541], [318.32558, 243.06108], [255.67984, 328.82894], + # [198.22603, 372.82502], [313.91018, 372.75659]]) + + + self.face_template = self.face_template * (face_size / 512.0) + if self.crop_ratio[0] > 1: + self.face_template[:, 1] += face_size * (self.crop_ratio[0] - 1) / 2 + if self.crop_ratio[1] > 1: + self.face_template[:, 0] += face_size * (self.crop_ratio[1] - 1) / 2 + self.save_ext = save_ext + self.pad_blur = pad_blur + if self.pad_blur is True: + self.template_3points = False + + self.all_landmarks_5 = [] + self.det_faces = [] + self.affine_matrices = [] + self.inverse_affine_matrices = [] + self.cropped_faces = [] + self.restored_faces = [] + self.pad_input_imgs = [] + + if device is None: + self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + else: + self.device = device + + # init face detection model + self.face_det = init_detection_model(det_model, half=False, device=self.device) + + # init face parsing model + self.use_parse = use_parse + self.face_parse = init_parsing_model(model_name='parsenet', device=self.device) + + def set_upscale_factor(self, upscale_factor): + self.upscale_factor = upscale_factor + + def read_image(self, img): + """img can be image path or cv2 loaded image.""" + # self.input_img is Numpy array, (h, w, c), BGR, uint8, [0, 255] + if isinstance(img, str): + img = cv2.imread(img) + + if np.max(img) > 256: # 16-bit image + img = img / 65535 * 255 + if len(img.shape) == 2: # gray image + img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) + elif img.shape[2] == 4: # BGRA image with alpha channel + img = img[:, :, 0:3] + + self.input_img = img + self.is_gray = is_gray(img, threshold=5) + if self.is_gray: + print('Grayscale input: True') + + if min(self.input_img.shape[:2])<512: + f = 512.0/min(self.input_img.shape[:2]) + self.input_img = cv2.resize(self.input_img, (0,0), fx=f, fy=f, interpolation=cv2.INTER_LINEAR) + + def get_face_landmarks_5(self, + only_keep_largest=False, + only_center_face=False, + resize=None, + blur_ratio=0.01, + eye_dist_threshold=None): + if resize is None: + scale = 1 + input_img = self.input_img + else: + h, w = self.input_img.shape[0:2] + scale = resize / min(h, w) + scale = max(1, scale) # always scale up + h, w = int(h * scale), int(w * scale) + interp = cv2.INTER_AREA if scale < 1 else cv2.INTER_LINEAR + input_img = cv2.resize(self.input_img, (w, h), interpolation=interp) + + with torch.no_grad(): + bboxes = self.face_det.detect_faces(input_img) + + if bboxes is None or bboxes.shape[0] == 0: + return 0 + else: + bboxes = bboxes / scale + + for bbox in bboxes: + # remove faces with too small eye distance: side faces or too small faces + eye_dist = np.linalg.norm([bbox[6] - bbox[8], bbox[7] - bbox[9]]) + if eye_dist_threshold is not None and (eye_dist < eye_dist_threshold): + continue + + if self.template_3points: + landmark = np.array([[bbox[i], bbox[i + 1]] for i in range(5, 11, 2)]) + else: + landmark = np.array([[bbox[i], bbox[i + 1]] for i in range(5, 15, 2)]) + self.all_landmarks_5.append(landmark) + self.det_faces.append(bbox[0:5]) + + if len(self.det_faces) == 0: + return 0 + if only_keep_largest: + h, w, _ = self.input_img.shape + self.det_faces, largest_idx = get_largest_face(self.det_faces, h, w) + self.all_landmarks_5 = [self.all_landmarks_5[largest_idx]] + elif only_center_face: + h, w, _ = self.input_img.shape + self.det_faces, center_idx = get_center_face(self.det_faces, h, w) + self.all_landmarks_5 = [self.all_landmarks_5[center_idx]] + + # pad blurry images + if self.pad_blur: + self.pad_input_imgs = [] + for landmarks in self.all_landmarks_5: + # get landmarks + eye_left = landmarks[0, :] + eye_right = landmarks[1, :] + eye_avg = (eye_left + eye_right) * 0.5 + mouth_avg = (landmarks[3, :] + landmarks[4, :]) * 0.5 + eye_to_eye = eye_right - eye_left + eye_to_mouth = mouth_avg - eye_avg + + # Get the oriented crop rectangle + # x: half width of the oriented crop rectangle + x = eye_to_eye - np.flipud(eye_to_mouth) * [-1, 1] + # - np.flipud(eye_to_mouth) * [-1, 1]: rotate 90 clockwise + # norm with the hypotenuse: get the direction + x /= np.hypot(*x) # get the hypotenuse of a right triangle + rect_scale = 1.5 + x *= max(np.hypot(*eye_to_eye) * 2.0 * rect_scale, np.hypot(*eye_to_mouth) * 1.8 * rect_scale) + # y: half height of the oriented crop rectangle + y = np.flipud(x) * [-1, 1] + + # c: center + c = eye_avg + eye_to_mouth * 0.1 + # quad: (left_top, left_bottom, right_bottom, right_top) + quad = np.stack([c - x - y, c - x + y, c + x + y, c + x - y]) + # qsize: side length of the square + qsize = np.hypot(*x) * 2 + border = max(int(np.rint(qsize * 0.1)), 3) + + # get pad + # pad: (width_left, height_top, width_right, height_bottom) + pad = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))), int(np.ceil(max(quad[:, 0]))), + int(np.ceil(max(quad[:, 1])))) + pad = [ + max(-pad[0] + border, 1), + max(-pad[1] + border, 1), + max(pad[2] - self.input_img.shape[0] + border, 1), + max(pad[3] - self.input_img.shape[1] + border, 1) + ] + + if max(pad) > 1: + # pad image + pad_img = np.pad(self.input_img, ((pad[1], pad[3]), (pad[0], pad[2]), (0, 0)), 'reflect') + # modify landmark coords + landmarks[:, 0] += pad[0] + landmarks[:, 1] += pad[1] + # blur pad images + h, w, _ = pad_img.shape + y, x, _ = np.ogrid[:h, :w, :1] + mask = np.maximum(1.0 - np.minimum(np.float32(x) / pad[0], + np.float32(w - 1 - x) / pad[2]), + 1.0 - np.minimum(np.float32(y) / pad[1], + np.float32(h - 1 - y) / pad[3])) + blur = int(qsize * blur_ratio) + if blur % 2 == 0: + blur += 1 + blur_img = cv2.boxFilter(pad_img, 0, ksize=(blur, blur)) + # blur_img = cv2.GaussianBlur(pad_img, (blur, blur), 0) + + pad_img = pad_img.astype('float32') + pad_img += (blur_img - pad_img) * np.clip(mask * 3.0 + 1.0, 0.0, 1.0) + pad_img += (np.median(pad_img, axis=(0, 1)) - pad_img) * np.clip(mask, 0.0, 1.0) + pad_img = np.clip(pad_img, 0, 255) # float32, [0, 255] + self.pad_input_imgs.append(pad_img) + else: + self.pad_input_imgs.append(np.copy(self.input_img)) + + return len(self.all_landmarks_5) + + def align_warp_face(self, save_cropped_path=None, border_mode='constant'): + """Align and warp faces with face template. + """ + if self.pad_blur: + assert len(self.pad_input_imgs) == len( + self.all_landmarks_5), f'Mismatched samples: {len(self.pad_input_imgs)} and {len(self.all_landmarks_5)}' + for idx, landmark in enumerate(self.all_landmarks_5): + # use 5 landmarks to get affine matrix + # use cv2.LMEDS method for the equivalence to skimage transform + # ref: https://blog.csdn.net/yichxi/article/details/115827338 + affine_matrix = cv2.estimateAffinePartial2D(landmark, self.face_template, method=cv2.LMEDS)[0] + self.affine_matrices.append(affine_matrix) + # warp and crop faces + if border_mode == 'constant': + border_mode = cv2.BORDER_CONSTANT + elif border_mode == 'reflect101': + border_mode = cv2.BORDER_REFLECT101 + elif border_mode == 'reflect': + border_mode = cv2.BORDER_REFLECT + if self.pad_blur: + input_img = self.pad_input_imgs[idx] + else: + input_img = self.input_img + cropped_face = cv2.warpAffine( + input_img, affine_matrix, self.face_size, borderMode=border_mode, borderValue=(135, 133, 132)) # gray + self.cropped_faces.append(cropped_face) + # save the cropped face + if save_cropped_path is not None: + path = os.path.splitext(save_cropped_path)[0] + save_path = f'{path}_{idx:02d}.{self.save_ext}' + imwrite(cropped_face, save_path) + + def get_inverse_affine(self, save_inverse_affine_path=None): + """Get inverse affine matrix.""" + for idx, affine_matrix in enumerate(self.affine_matrices): + inverse_affine = cv2.invertAffineTransform(affine_matrix) + inverse_affine *= self.upscale_factor + self.inverse_affine_matrices.append(inverse_affine) + # save inverse affine matrices + if save_inverse_affine_path is not None: + path, _ = os.path.splitext(save_inverse_affine_path) + save_path = f'{path}_{idx:02d}.pth' + torch.save(inverse_affine, save_path) + + + def add_restored_face(self, face): + if self.is_gray: + face = bgr2gray(face) # convert img into grayscale + self.restored_faces.append(face) + + + def paste_faces_to_input_image(self, save_path=None, upsample_img=None, draw_box=False, face_upsampler=None): + h, w, _ = self.input_img.shape + h_up, w_up = int(h * self.upscale_factor), int(w * self.upscale_factor) + + if upsample_img is None: + # simply resize the background + # upsample_img = cv2.resize(self.input_img, (w_up, h_up), interpolation=cv2.INTER_LANCZOS4) + upsample_img = cv2.resize(self.input_img, (w_up, h_up), interpolation=cv2.INTER_LINEAR) + else: + upsample_img = cv2.resize(upsample_img, (w_up, h_up), interpolation=cv2.INTER_LANCZOS4) + + assert len(self.restored_faces) == len( + self.inverse_affine_matrices), ('length of restored_faces and affine_matrices are different.') + + inv_mask_borders = [] + for restored_face, inverse_affine in zip(self.restored_faces, self.inverse_affine_matrices): + if face_upsampler is not None: + restored_face = face_upsampler.enhance(restored_face, outscale=self.upscale_factor)[0] + inverse_affine /= self.upscale_factor + inverse_affine[:, 2] *= self.upscale_factor + face_size = (self.face_size[0]*self.upscale_factor, self.face_size[1]*self.upscale_factor) + else: + # Add an offset to inverse affine matrix, for more precise back alignment + if self.upscale_factor > 1: + extra_offset = 0.5 * self.upscale_factor + else: + extra_offset = 0 + inverse_affine[:, 2] += extra_offset + face_size = self.face_size + inv_restored = cv2.warpAffine(restored_face, inverse_affine, (w_up, h_up)) + + # if draw_box or not self.use_parse: # use square parse maps + # mask = np.ones(face_size, dtype=np.float32) + # inv_mask = cv2.warpAffine(mask, inverse_affine, (w_up, h_up)) + # # remove the black borders + # inv_mask_erosion = cv2.erode( + # inv_mask, np.ones((int(2 * self.upscale_factor), int(2 * self.upscale_factor)), np.uint8)) + # pasted_face = inv_mask_erosion[:, :, None] * inv_restored + # total_face_area = np.sum(inv_mask_erosion) # // 3 + # # add border + # if draw_box: + # h, w = face_size + # mask_border = np.ones((h, w, 3), dtype=np.float32) + # border = int(1400/np.sqrt(total_face_area)) + # mask_border[border:h-border, border:w-border,:] = 0 + # inv_mask_border = cv2.warpAffine(mask_border, inverse_affine, (w_up, h_up)) + # inv_mask_borders.append(inv_mask_border) + # if not self.use_parse: + # # compute the fusion edge based on the area of face + # w_edge = int(total_face_area**0.5) // 20 + # erosion_radius = w_edge * 2 + # inv_mask_center = cv2.erode(inv_mask_erosion, np.ones((erosion_radius, erosion_radius), np.uint8)) + # blur_size = w_edge * 2 + # inv_soft_mask = cv2.GaussianBlur(inv_mask_center, (blur_size + 1, blur_size + 1), 0) + # if len(upsample_img.shape) == 2: # upsample_img is gray image + # upsample_img = upsample_img[:, :, None] + # inv_soft_mask = inv_soft_mask[:, :, None] + + # always use square mask + mask = np.ones(face_size, dtype=np.float32) + inv_mask = cv2.warpAffine(mask, inverse_affine, (w_up, h_up)) + # remove the black borders + inv_mask_erosion = cv2.erode( + inv_mask, np.ones((int(2 * self.upscale_factor), int(2 * self.upscale_factor)), np.uint8)) + pasted_face = inv_mask_erosion[:, :, None] * inv_restored + total_face_area = np.sum(inv_mask_erosion) # // 3 + # add border + if draw_box: + h, w = face_size + mask_border = np.ones((h, w, 3), dtype=np.float32) + border = int(1400/np.sqrt(total_face_area)) + mask_border[border:h-border, border:w-border,:] = 0 + inv_mask_border = cv2.warpAffine(mask_border, inverse_affine, (w_up, h_up)) + inv_mask_borders.append(inv_mask_border) + # compute the fusion edge based on the area of face + w_edge = int(total_face_area**0.5) // 20 + erosion_radius = w_edge * 2 + inv_mask_center = cv2.erode(inv_mask_erosion, np.ones((erosion_radius, erosion_radius), np.uint8)) + blur_size = w_edge * 2 + inv_soft_mask = cv2.GaussianBlur(inv_mask_center, (blur_size + 1, blur_size + 1), 0) + if len(upsample_img.shape) == 2: # upsample_img is gray image + upsample_img = upsample_img[:, :, None] + inv_soft_mask = inv_soft_mask[:, :, None] + + # parse mask + if self.use_parse: + # inference + face_input = cv2.resize(restored_face, (512, 512), interpolation=cv2.INTER_LINEAR) + face_input = img2tensor(face_input.astype('float32') / 255., bgr2rgb=True, float32=True) + normalize(face_input, (0.5, 0.5, 0.5), (0.5, 0.5, 0.5), inplace=True) + face_input = torch.unsqueeze(face_input, 0).to(self.device) + with torch.no_grad(): + out = self.face_parse(face_input)[0] + out = out.argmax(dim=1).squeeze().cpu().numpy() + + parse_mask = np.zeros(out.shape) + MASK_COLORMAP = [0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 0, 0, 0] + for idx, color in enumerate(MASK_COLORMAP): + parse_mask[out == idx] = color + # blur the mask + parse_mask = cv2.GaussianBlur(parse_mask, (101, 101), 11) + parse_mask = cv2.GaussianBlur(parse_mask, (101, 101), 11) + # remove the black borders + thres = 10 + parse_mask[:thres, :] = 0 + parse_mask[-thres:, :] = 0 + parse_mask[:, :thres] = 0 + parse_mask[:, -thres:] = 0 + parse_mask = parse_mask / 255. + + parse_mask = cv2.resize(parse_mask, face_size) + parse_mask = cv2.warpAffine(parse_mask, inverse_affine, (w_up, h_up), flags=3) + inv_soft_parse_mask = parse_mask[:, :, None] + # pasted_face = inv_restored + fuse_mask = (inv_soft_parse_mask 256: # 16-bit image + upsample_img = upsample_img.astype(np.uint16) + else: + upsample_img = upsample_img.astype(np.uint8) + + # draw bounding box + if draw_box: + # upsample_input_img = cv2.resize(input_img, (w_up, h_up)) + img_color = np.ones([*upsample_img.shape], dtype=np.float32) + img_color[:,:,0] = 0 + img_color[:,:,1] = 255 + img_color[:,:,2] = 0 + for inv_mask_border in inv_mask_borders: + upsample_img = inv_mask_border * img_color + (1 - inv_mask_border) * upsample_img + # upsample_input_img = inv_mask_border * img_color + (1 - inv_mask_border) * upsample_input_img + + if save_path is not None: + path = os.path.splitext(save_path)[0] + save_path = f'{path}.{self.save_ext}' + imwrite(upsample_img, save_path) + return upsample_img + + def clean_all(self): + self.all_landmarks_5 = [] + self.restored_faces = [] + self.affine_matrices = [] + self.cropped_faces = [] + self.inverse_affine_matrices = [] + self.det_faces = [] + self.pad_input_imgs = [] \ No newline at end of file diff --git a/repositories/codeformer/facelib/utils/face_utils.py b/repositories/codeformer/facelib/utils/face_utils.py new file mode 100644 index 000000000..f1474a2a4 --- /dev/null +++ b/repositories/codeformer/facelib/utils/face_utils.py @@ -0,0 +1,248 @@ +import cv2 +import numpy as np +import torch + + +def compute_increased_bbox(bbox, increase_area, preserve_aspect=True): + left, top, right, bot = bbox + width = right - left + height = bot - top + + if preserve_aspect: + width_increase = max(increase_area, ((1 + 2 * increase_area) * height - width) / (2 * width)) + height_increase = max(increase_area, ((1 + 2 * increase_area) * width - height) / (2 * height)) + else: + width_increase = height_increase = increase_area + left = int(left - width_increase * width) + top = int(top - height_increase * height) + right = int(right + width_increase * width) + bot = int(bot + height_increase * height) + return (left, top, right, bot) + + +def get_valid_bboxes(bboxes, h, w): + left = max(bboxes[0], 0) + top = max(bboxes[1], 0) + right = min(bboxes[2], w) + bottom = min(bboxes[3], h) + return (left, top, right, bottom) + + +def align_crop_face_landmarks(img, + landmarks, + output_size, + transform_size=None, + enable_padding=True, + return_inverse_affine=False, + shrink_ratio=(1, 1)): + """Align and crop face with landmarks. + + The output_size and transform_size are based on width. The height is + adjusted based on shrink_ratio_h/shring_ration_w. + + Modified from: + https://github.com/NVlabs/ffhq-dataset/blob/master/download_ffhq.py + + Args: + img (Numpy array): Input image. + landmarks (Numpy array): 5 or 68 or 98 landmarks. + output_size (int): Output face size. + transform_size (ing): Transform size. Usually the four time of + output_size. + enable_padding (float): Default: True. + shrink_ratio (float | tuple[float] | list[float]): Shring the whole + face for height and width (crop larger area). Default: (1, 1). + + Returns: + (Numpy array): Cropped face. + """ + lm_type = 'retinaface_5' # Options: dlib_5, retinaface_5 + + if isinstance(shrink_ratio, (float, int)): + shrink_ratio = (shrink_ratio, shrink_ratio) + if transform_size is None: + transform_size = output_size * 4 + + # Parse landmarks + lm = np.array(landmarks) + if lm.shape[0] == 5 and lm_type == 'retinaface_5': + eye_left = lm[0] + eye_right = lm[1] + mouth_avg = (lm[3] + lm[4]) * 0.5 + elif lm.shape[0] == 5 and lm_type == 'dlib_5': + lm_eye_left = lm[2:4] + lm_eye_right = lm[0:2] + eye_left = np.mean(lm_eye_left, axis=0) + eye_right = np.mean(lm_eye_right, axis=0) + mouth_avg = lm[4] + elif lm.shape[0] == 68: + lm_eye_left = lm[36:42] + lm_eye_right = lm[42:48] + eye_left = np.mean(lm_eye_left, axis=0) + eye_right = np.mean(lm_eye_right, axis=0) + mouth_avg = (lm[48] + lm[54]) * 0.5 + elif lm.shape[0] == 98: + lm_eye_left = lm[60:68] + lm_eye_right = lm[68:76] + eye_left = np.mean(lm_eye_left, axis=0) + eye_right = np.mean(lm_eye_right, axis=0) + mouth_avg = (lm[76] + lm[82]) * 0.5 + + eye_avg = (eye_left + eye_right) * 0.5 + eye_to_eye = eye_right - eye_left + eye_to_mouth = mouth_avg - eye_avg + + # Get the oriented crop rectangle + # x: half width of the oriented crop rectangle + x = eye_to_eye - np.flipud(eye_to_mouth) * [-1, 1] + # - np.flipud(eye_to_mouth) * [-1, 1]: rotate 90 clockwise + # norm with the hypotenuse: get the direction + x /= np.hypot(*x) # get the hypotenuse of a right triangle + rect_scale = 1 # TODO: you can edit it to get larger rect + x *= max(np.hypot(*eye_to_eye) * 2.0 * rect_scale, np.hypot(*eye_to_mouth) * 1.8 * rect_scale) + # y: half height of the oriented crop rectangle + y = np.flipud(x) * [-1, 1] + + x *= shrink_ratio[1] # width + y *= shrink_ratio[0] # height + + # c: center + c = eye_avg + eye_to_mouth * 0.1 + # quad: (left_top, left_bottom, right_bottom, right_top) + quad = np.stack([c - x - y, c - x + y, c + x + y, c + x - y]) + # qsize: side length of the square + qsize = np.hypot(*x) * 2 + + quad_ori = np.copy(quad) + # Shrink, for large face + # TODO: do we really need shrink + shrink = int(np.floor(qsize / output_size * 0.5)) + if shrink > 1: + h, w = img.shape[0:2] + rsize = (int(np.rint(float(w) / shrink)), int(np.rint(float(h) / shrink))) + img = cv2.resize(img, rsize, interpolation=cv2.INTER_AREA) + quad /= shrink + qsize /= shrink + + # Crop + h, w = img.shape[0:2] + border = max(int(np.rint(qsize * 0.1)), 3) + crop = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))), int(np.ceil(max(quad[:, 0]))), + int(np.ceil(max(quad[:, 1])))) + crop = (max(crop[0] - border, 0), max(crop[1] - border, 0), min(crop[2] + border, w), min(crop[3] + border, h)) + if crop[2] - crop[0] < w or crop[3] - crop[1] < h: + img = img[crop[1]:crop[3], crop[0]:crop[2], :] + quad -= crop[0:2] + + # Pad + # pad: (width_left, height_top, width_right, height_bottom) + h, w = img.shape[0:2] + pad = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))), int(np.ceil(max(quad[:, 0]))), + int(np.ceil(max(quad[:, 1])))) + pad = (max(-pad[0] + border, 0), max(-pad[1] + border, 0), max(pad[2] - w + border, 0), max(pad[3] - h + border, 0)) + if enable_padding and max(pad) > border - 4: + pad = np.maximum(pad, int(np.rint(qsize * 0.3))) + img = np.pad(img, ((pad[1], pad[3]), (pad[0], pad[2]), (0, 0)), 'reflect') + h, w = img.shape[0:2] + y, x, _ = np.ogrid[:h, :w, :1] + mask = np.maximum(1.0 - np.minimum(np.float32(x) / pad[0], + np.float32(w - 1 - x) / pad[2]), + 1.0 - np.minimum(np.float32(y) / pad[1], + np.float32(h - 1 - y) / pad[3])) + blur = int(qsize * 0.02) + if blur % 2 == 0: + blur += 1 + blur_img = cv2.boxFilter(img, 0, ksize=(blur, blur)) + + img = img.astype('float32') + img += (blur_img - img) * np.clip(mask * 3.0 + 1.0, 0.0, 1.0) + img += (np.median(img, axis=(0, 1)) - img) * np.clip(mask, 0.0, 1.0) + img = np.clip(img, 0, 255) # float32, [0, 255] + quad += pad[:2] + + # Transform use cv2 + h_ratio = shrink_ratio[0] / shrink_ratio[1] + dst_h, dst_w = int(transform_size * h_ratio), transform_size + template = np.array([[0, 0], [0, dst_h], [dst_w, dst_h], [dst_w, 0]]) + # use cv2.LMEDS method for the equivalence to skimage transform + # ref: https://blog.csdn.net/yichxi/article/details/115827338 + affine_matrix = cv2.estimateAffinePartial2D(quad, template, method=cv2.LMEDS)[0] + cropped_face = cv2.warpAffine( + img, affine_matrix, (dst_w, dst_h), borderMode=cv2.BORDER_CONSTANT, borderValue=(135, 133, 132)) # gray + + if output_size < transform_size: + cropped_face = cv2.resize( + cropped_face, (output_size, int(output_size * h_ratio)), interpolation=cv2.INTER_LINEAR) + + if return_inverse_affine: + dst_h, dst_w = int(output_size * h_ratio), output_size + template = np.array([[0, 0], [0, dst_h], [dst_w, dst_h], [dst_w, 0]]) + # use cv2.LMEDS method for the equivalence to skimage transform + # ref: https://blog.csdn.net/yichxi/article/details/115827338 + affine_matrix = cv2.estimateAffinePartial2D( + quad_ori, np.array([[0, 0], [0, output_size], [dst_w, dst_h], [dst_w, 0]]), method=cv2.LMEDS)[0] + inverse_affine = cv2.invertAffineTransform(affine_matrix) + else: + inverse_affine = None + return cropped_face, inverse_affine + + +def paste_face_back(img, face, inverse_affine): + h, w = img.shape[0:2] + face_h, face_w = face.shape[0:2] + inv_restored = cv2.warpAffine(face, inverse_affine, (w, h)) + mask = np.ones((face_h, face_w, 3), dtype=np.float32) + inv_mask = cv2.warpAffine(mask, inverse_affine, (w, h)) + # remove the black borders + inv_mask_erosion = cv2.erode(inv_mask, np.ones((2, 2), np.uint8)) + inv_restored_remove_border = inv_mask_erosion * inv_restored + total_face_area = np.sum(inv_mask_erosion) // 3 + # compute the fusion edge based on the area of face + w_edge = int(total_face_area**0.5) // 20 + erosion_radius = w_edge * 2 + inv_mask_center = cv2.erode(inv_mask_erosion, np.ones((erosion_radius, erosion_radius), np.uint8)) + blur_size = w_edge * 2 + inv_soft_mask = cv2.GaussianBlur(inv_mask_center, (blur_size + 1, blur_size + 1), 0) + img = inv_soft_mask * inv_restored_remove_border + (1 - inv_soft_mask) * img + # float32, [0, 255] + return img + + +if __name__ == '__main__': + import os + + from facelib.detection import init_detection_model + from facelib.utils.face_restoration_helper import get_largest_face + + img_path = '/home/wxt/datasets/ffhq/ffhq_wild/00009.png' + img_name = os.splitext(os.path.basename(img_path))[0] + + # initialize model + det_net = init_detection_model('retinaface_resnet50', half=False) + img_ori = cv2.imread(img_path) + h, w = img_ori.shape[0:2] + # if larger than 800, scale it + scale = max(h / 800, w / 800) + if scale > 1: + img = cv2.resize(img_ori, (int(w / scale), int(h / scale)), interpolation=cv2.INTER_LINEAR) + + with torch.no_grad(): + bboxes = det_net.detect_faces(img, 0.97) + if scale > 1: + bboxes *= scale # the score is incorrect + bboxes = get_largest_face(bboxes, h, w)[0] + + landmarks = np.array([[bboxes[i], bboxes[i + 1]] for i in range(5, 15, 2)]) + + cropped_face, inverse_affine = align_crop_face_landmarks( + img_ori, + landmarks, + output_size=512, + transform_size=None, + enable_padding=True, + return_inverse_affine=True, + shrink_ratio=(1, 1)) + + cv2.imwrite(f'tmp/{img_name}_cropeed_face.png', cropped_face) + img = paste_face_back(img_ori, cropped_face, inverse_affine) + cv2.imwrite(f'tmp/{img_name}_back.png', img) diff --git a/repositories/codeformer/facelib/utils/misc.py b/repositories/codeformer/facelib/utils/misc.py new file mode 100644 index 000000000..7f5c95506 --- /dev/null +++ b/repositories/codeformer/facelib/utils/misc.py @@ -0,0 +1,174 @@ +import cv2 +import os +import os.path as osp +import numpy as np +from PIL import Image +import torch +from torch.hub import download_url_to_file, get_dir +from urllib.parse import urlparse +# from basicsr.utils.download_util import download_file_from_google_drive + +ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + +def download_pretrained_models(file_ids, save_path_root): + import gdown + + os.makedirs(save_path_root, exist_ok=True) + + for file_name, file_id in file_ids.items(): + file_url = 'https://drive.google.com/uc?id='+file_id + save_path = osp.abspath(osp.join(save_path_root, file_name)) + if osp.exists(save_path): + user_response = input(f'{file_name} already exist. Do you want to cover it? Y/N\n') + if user_response.lower() == 'y': + print(f'Covering {file_name} to {save_path}') + gdown.download(file_url, save_path, quiet=False) + # download_file_from_google_drive(file_id, save_path) + elif user_response.lower() == 'n': + print(f'Skipping {file_name}') + else: + raise ValueError('Wrong input. Only accepts Y/N.') + else: + print(f'Downloading {file_name} to {save_path}') + gdown.download(file_url, save_path, quiet=False) + # download_file_from_google_drive(file_id, save_path) + + +def imwrite(img, file_path, params=None, auto_mkdir=True): + """Write image to file. + + Args: + img (ndarray): Image array to be written. + file_path (str): Image file path. + params (None or list): Same as opencv's :func:`imwrite` interface. + auto_mkdir (bool): If the parent folder of `file_path` does not exist, + whether to create it automatically. + + Returns: + bool: Successful or not. + """ + if auto_mkdir: + dir_name = os.path.abspath(os.path.dirname(file_path)) + os.makedirs(dir_name, exist_ok=True) + return cv2.imwrite(file_path, img, params) + + +def img2tensor(imgs, bgr2rgb=True, float32=True): + """Numpy array to tensor. + + Args: + imgs (list[ndarray] | ndarray): Input images. + bgr2rgb (bool): Whether to change bgr to rgb. + float32 (bool): Whether to change to float32. + + Returns: + list[tensor] | tensor: Tensor images. If returned results only have + one element, just return tensor. + """ + + def _totensor(img, bgr2rgb, float32): + if img.shape[2] == 3 and bgr2rgb: + if img.dtype == 'float64': + img = img.astype('float32') + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + img = torch.from_numpy(img.transpose(2, 0, 1)) + if float32: + img = img.float() + return img + + if isinstance(imgs, list): + return [_totensor(img, bgr2rgb, float32) for img in imgs] + else: + return _totensor(imgs, bgr2rgb, float32) + + +def load_file_from_url(url, model_dir=None, progress=True, file_name=None): + """Ref:https://github.com/1adrianb/face-alignment/blob/master/face_alignment/utils.py + """ + if model_dir is None: + hub_dir = get_dir() + model_dir = os.path.join(hub_dir, 'checkpoints') + + os.makedirs(os.path.join(ROOT_DIR, model_dir), exist_ok=True) + + parts = urlparse(url) + filename = os.path.basename(parts.path) + if file_name is not None: + filename = file_name + cached_file = os.path.abspath(os.path.join(ROOT_DIR, model_dir, filename)) + if not os.path.exists(cached_file): + print(f'Downloading: "{url}" to {cached_file}\n') + download_url_to_file(url, cached_file, hash_prefix=None, progress=progress) + return cached_file + + +def scandir(dir_path, suffix=None, recursive=False, full_path=False): + """Scan a directory to find the interested files. + Args: + dir_path (str): Path of the directory. + suffix (str | tuple(str), optional): File suffix that we are + interested in. Default: None. + recursive (bool, optional): If set to True, recursively scan the + directory. Default: False. + full_path (bool, optional): If set to True, include the dir_path. + Default: False. + Returns: + A generator for all the interested files with relative paths. + """ + + if (suffix is not None) and not isinstance(suffix, (str, tuple)): + raise TypeError('"suffix" must be a string or tuple of strings') + + root = dir_path + + def _scandir(dir_path, suffix, recursive): + for entry in os.scandir(dir_path): + if not entry.name.startswith('.') and entry.is_file(): + if full_path: + return_path = entry.path + else: + return_path = osp.relpath(entry.path, root) + + if suffix is None: + yield return_path + elif return_path.endswith(suffix): + yield return_path + else: + if recursive: + yield from _scandir(entry.path, suffix=suffix, recursive=recursive) + else: + continue + + return _scandir(dir_path, suffix=suffix, recursive=recursive) + + +def is_gray(img, threshold=10): + img = Image.fromarray(img) + if len(img.getbands()) == 1: + return True + img1 = np.asarray(img.getchannel(channel=0), dtype=np.int16) + img2 = np.asarray(img.getchannel(channel=1), dtype=np.int16) + img3 = np.asarray(img.getchannel(channel=2), dtype=np.int16) + diff1 = (img1 - img2).var() + diff2 = (img2 - img3).var() + diff3 = (img3 - img1).var() + diff_sum = (diff1 + diff2 + diff3) / 3.0 + if diff_sum <= threshold: + return True + else: + return False + +def rgb2gray(img, out_channel=3): + r, g, b = img[:,:,0], img[:,:,1], img[:,:,2] + gray = 0.2989 * r + 0.5870 * g + 0.1140 * b + if out_channel == 3: + gray = gray[:,:,np.newaxis].repeat(3, axis=2) + return gray + +def bgr2gray(img, out_channel=3): + b, g, r = img[:,:,0], img[:,:,1], img[:,:,2] + gray = 0.2989 * r + 0.5870 * g + 0.1140 * b + if out_channel == 3: + gray = gray[:,:,np.newaxis].repeat(3, axis=2) + return gray diff --git a/repositories/codeformer/inference_codeformer.py b/repositories/codeformer/inference_codeformer.py new file mode 100644 index 000000000..cde1094af --- /dev/null +++ b/repositories/codeformer/inference_codeformer.py @@ -0,0 +1,269 @@ +import os +import cv2 +import argparse +import glob +import torch +from torchvision.transforms.functional import normalize +from basicsr.utils import imwrite, img2tensor, tensor2img +from basicsr.utils.download_util import load_file_from_url +from facelib.utils.face_restoration_helper import FaceRestoreHelper +from facelib.utils.misc import is_gray +import torch.nn.functional as F + +from basicsr.utils.registry import ARCH_REGISTRY + +pretrain_model_url = { + 'restoration': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth', +} + +def set_realesrgan(): + from basicsr.archs.rrdbnet_arch import RRDBNet + from basicsr.utils.realesrgan_utils import RealESRGANer + + cuda_is_available = torch.cuda.is_available() + half = True if cuda_is_available else False + model = RRDBNet( + num_in_ch=3, + num_out_ch=3, + num_feat=64, + num_block=23, + num_grow_ch=32, + scale=2, + ) + upsampler = RealESRGANer( + scale=2, + model_path="https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/RealESRGAN_x2plus.pth", + model=model, + tile=args.bg_tile, + tile_pad=40, + pre_pad=0, + half=half, # need to set False in CPU mode + ) + + if not cuda_is_available: # CPU + import warnings + warnings.warn('Running on CPU now! Make sure your PyTorch version matches your CUDA.' + 'The unoptimized RealESRGAN is slow on CPU. ' + 'If you want to disable it, please remove `--bg_upsampler` and `--face_upsample` in command.', + category=RuntimeWarning) + return upsampler + +if __name__ == '__main__': + device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + parser = argparse.ArgumentParser() + + parser.add_argument('-i', '--input_path', type=str, default='./inputs/whole_imgs', + help='Input image, video or folder. Default: inputs/whole_imgs') + parser.add_argument('-o', '--output_path', type=str, default=None, + help='Output folder. Default: results/_') + parser.add_argument('-w', '--fidelity_weight', type=float, default=0.5, + help='Balance the quality and fidelity. Default: 0.5') + parser.add_argument('-s', '--upscale', type=int, default=2, + help='The final upsampling scale of the image. Default: 2') + parser.add_argument('--has_aligned', action='store_true', help='Input are cropped and aligned faces. Default: False') + parser.add_argument('--only_center_face', action='store_true', help='Only restore the center face. Default: False') + parser.add_argument('--draw_box', action='store_true', help='Draw the bounding box for the detected faces. Default: False') + # large det_model: 'YOLOv5l', 'retinaface_resnet50' + # small det_model: 'YOLOv5n', 'retinaface_mobile0.25' + parser.add_argument('--detection_model', type=str, default='retinaface_resnet50', + help='Face detector. Optional: retinaface_resnet50, retinaface_mobile0.25, YOLOv5l, YOLOv5n. \ + Default: retinaface_resnet50') + parser.add_argument('--bg_upsampler', type=str, default='None', help='Background upsampler. Optional: realesrgan') + parser.add_argument('--face_upsample', action='store_true', help='Face upsampler after enhancement. Default: False') + parser.add_argument('--bg_tile', type=int, default=400, help='Tile size for background sampler. Default: 400') + parser.add_argument('--suffix', type=str, default=None, help='Suffix of the restored faces. Default: None') + parser.add_argument('--save_video_fps', type=float, default=None, help='Frame rate for saving video. Default: None') + + args = parser.parse_args() + + # ------------------------ input & output ------------------------ + w = args.fidelity_weight + input_video = False + if args.input_path.endswith(('jpg', 'png')): # input single img path + input_img_list = [args.input_path] + result_root = f'results/test_img_{w}' + elif args.input_path.endswith(('mp4', 'mov', 'avi')): # input video path + from basicsr.utils.video_util import VideoReader, VideoWriter + input_img_list = [] + vidreader = VideoReader(args.input_path) + image = vidreader.get_frame() + while image is not None: + input_img_list.append(image) + image = vidreader.get_frame() + audio = vidreader.get_audio() + fps = vidreader.get_fps() if args.save_video_fps is None else args.save_video_fps + video_name = os.path.basename(args.input_path)[:-4] + result_root = f'results/{video_name}_{w}' + input_video = True + vidreader.close() + else: # input img folder + if args.input_path.endswith('/'): # solve when path ends with / + args.input_path = args.input_path[:-1] + # scan all the jpg and png images + input_img_list = sorted(glob.glob(os.path.join(args.input_path, '*.[jp][pn]g'))) + result_root = f'results/{os.path.basename(args.input_path)}_{w}' + + if not args.output_path is None: # set output path + result_root = args.output_path + + test_img_num = len(input_img_list) + if test_img_num == 0: + raise FileNotFoundError("\nInput file is not found.") + + # ------------------ set up background upsampler ------------------ + if args.bg_upsampler == 'realesrgan': + bg_upsampler = set_realesrgan() + else: + bg_upsampler = None + + # ------------------ set up face upsampler ------------------ + if args.face_upsample: + face_upsampler = None + # if bg_upsampler is not None: + # face_upsampler = bg_upsampler + # else: + # face_upsampler = set_realesrgan() + else: + face_upsampler = None + + # ------------------ set up CodeFormer restorer ------------------- + net = ARCH_REGISTRY.get('CodeFormer')(dim_embd=512, codebook_size=1024, n_head=8, n_layers=9, + connect_list=['32', '64', '128', '256']).to(device) + + # ckpt_path = 'weights/CodeFormer/codeformer.pth' + ckpt_path = load_file_from_url(url=pretrain_model_url['restoration'], + model_dir='weights/CodeFormer', progress=True, file_name=None) + checkpoint = torch.load(ckpt_path)['params_ema'] + net.load_state_dict(checkpoint) + net.eval() + + # ------------------ set up FaceRestoreHelper ------------------- + # large det_model: 'YOLOv5l', 'retinaface_resnet50' + # small det_model: 'YOLOv5n', 'retinaface_mobile0.25' + if not args.has_aligned: + print(f'Face detection model: {args.detection_model}') + if bg_upsampler is not None: + print(f'Background upsampling: True, Face upsampling: {args.face_upsample}') + else: + print(f'Background upsampling: False, Face upsampling: {args.face_upsample}') + + face_helper = FaceRestoreHelper( + args.upscale, + face_size=512, + crop_ratio=(1, 1), + det_model = args.detection_model, + save_ext='png', + use_parse=True, + device=device) + + # -------------------- start to processing --------------------- + for i, img_path in enumerate(input_img_list): + # clean all the intermediate results to process the next image + face_helper.clean_all() + + if isinstance(img_path, str): + img_name = os.path.basename(img_path) + basename, ext = os.path.splitext(img_name) + print(f'[{i+1}/{test_img_num}] Processing: {img_name}') + img = cv2.imread(img_path, cv2.IMREAD_COLOR) + else: # for video processing + basename = str(i).zfill(6) + img_name = f'{video_name}_{basename}' if input_video else basename + print(f'[{i+1}/{test_img_num}] Processing: {img_name}') + img = img_path + + if args.has_aligned: + # the input faces are already cropped and aligned + img = cv2.resize(img, (512, 512), interpolation=cv2.INTER_LINEAR) + face_helper.is_gray = is_gray(img, threshold=5) + if face_helper.is_gray: + print('Grayscale input: True') + face_helper.cropped_faces = [img] + else: + face_helper.read_image(img) + # get face landmarks for each face + num_det_faces = face_helper.get_face_landmarks_5( + only_center_face=args.only_center_face, resize=640, eye_dist_threshold=5) + print(f'\tdetect {num_det_faces} faces') + # align and warp each face + face_helper.align_warp_face() + + # face restoration for each cropped face + for idx, cropped_face in enumerate(face_helper.cropped_faces): + # prepare data + cropped_face_t = img2tensor(cropped_face / 255., bgr2rgb=True, float32=True) + normalize(cropped_face_t, (0.5, 0.5, 0.5), (0.5, 0.5, 0.5), inplace=True) + cropped_face_t = cropped_face_t.unsqueeze(0).to(device) + + try: + with torch.no_grad(): + output = net(cropped_face_t, w=w, adain=True)[0] + restored_face = tensor2img(output, rgb2bgr=True, min_max=(-1, 1)) + del output + torch.cuda.empty_cache() + except Exception as error: + print(f'\tFailed inference for CodeFormer: {error}') + restored_face = tensor2img(cropped_face_t, rgb2bgr=True, min_max=(-1, 1)) + + restored_face = restored_face.astype('uint8') + face_helper.add_restored_face(restored_face) + + # paste_back + if not args.has_aligned: + # upsample the background + if bg_upsampler is not None: + # Now only support RealESRGAN for upsampling background + bg_img = bg_upsampler.enhance(img, outscale=args.upscale)[0] + else: + bg_img = None + face_helper.get_inverse_affine(None) + # paste each restored face to the input image + if args.face_upsample and face_upsampler is not None: + restored_img = face_helper.paste_faces_to_input_image(upsample_img=bg_img, draw_box=args.draw_box, face_upsampler=face_upsampler) + else: + restored_img = face_helper.paste_faces_to_input_image(upsample_img=bg_img, draw_box=args.draw_box) + + # save faces + for idx, (cropped_face, restored_face) in enumerate(zip(face_helper.cropped_faces, face_helper.restored_faces)): + # save cropped face + if not args.has_aligned: + save_crop_path = os.path.join(result_root, 'cropped_faces', f'{basename}_{idx:02d}.png') + imwrite(cropped_face, save_crop_path) + # save restored face + if args.has_aligned: + save_face_name = f'{basename}.png' + else: + save_face_name = f'{basename}_{idx:02d}.png' + if args.suffix is not None: + save_face_name = f'{save_face_name[:-4]}_{args.suffix}.png' + save_restore_path = os.path.join(result_root, 'restored_faces', save_face_name) + imwrite(restored_face, save_restore_path) + + # save restored img + if not args.has_aligned and restored_img is not None: + if args.suffix is not None: + basename = f'{basename}_{args.suffix}' + save_restore_path = os.path.join(result_root, 'final_results', f'{basename}.png') + imwrite(restored_img, save_restore_path) + + # save enhanced video + if input_video: + print('Video Saving...') + # load images + video_frames = [] + img_list = sorted(glob.glob(os.path.join(result_root, 'final_results', '*.[jp][pn]g'))) + for img_path in img_list: + img = cv2.imread(img_path) + video_frames.append(img) + # write images to video + height, width = video_frames[0].shape[:2] + if args.suffix is not None: + video_name = f'{video_name}_{args.suffix}.png' + save_restore_path = os.path.join(result_root, f'{video_name}.mp4') + vidwriter = VideoWriter(save_restore_path, height, width, fps, audio) + + for f in video_frames: + vidwriter.write_frame(f) + vidwriter.close() + + print(f'\nAll results are saved in {result_root}') diff --git a/repositories/codeformer/requirements.txt b/repositories/codeformer/requirements.txt new file mode 100644 index 000000000..7e1950a06 --- /dev/null +++ b/repositories/codeformer/requirements.txt @@ -0,0 +1,17 @@ +addict +future +lmdb +numpy +opencv-python +Pillow +pyyaml +requests +scikit-image +scipy +tb-nightly +torch>=1.7.1 +torchvision +tqdm +yapf +lpips +gdown # supports downloading the large file from Google Drive \ No newline at end of file diff --git a/repositories/codeformer/scripts/crop_align_face.py b/repositories/codeformer/scripts/crop_align_face.py new file mode 100755 index 000000000..31e66266a --- /dev/null +++ b/repositories/codeformer/scripts/crop_align_face.py @@ -0,0 +1,192 @@ +""" +brief: face alignment with FFHQ method (https://github.com/NVlabs/ffhq-dataset) +author: lzhbrian (https://lzhbrian.me) +link: https://gist.github.com/lzhbrian/bde87ab23b499dd02ba4f588258f57d5 +date: 2020.1.5 +note: code is heavily borrowed from + https://github.com/NVlabs/ffhq-dataset + http://dlib.net/face_landmark_detection.py.html +requirements: + conda install Pillow numpy scipy + conda install -c conda-forge dlib + # download face landmark model from: + # http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2 +""" + +import cv2 +import dlib +import glob +import numpy as np +import os +import PIL +import PIL.Image +import scipy +import scipy.ndimage +import sys +import argparse + +# download model from: http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2 +predictor = dlib.shape_predictor('weights/dlib/shape_predictor_68_face_landmarks-fbdc2cb8.dat') + + +def get_landmark(filepath, only_keep_largest=True): + """get landmark with dlib + :return: np.array shape=(68, 2) + """ + detector = dlib.get_frontal_face_detector() + + img = dlib.load_rgb_image(filepath) + dets = detector(img, 1) + + # Shangchen modified + print("Number of faces detected: {}".format(len(dets))) + if only_keep_largest: + print('Detect several faces and only keep the largest.') + face_areas = [] + for k, d in enumerate(dets): + face_area = (d.right() - d.left()) * (d.bottom() - d.top()) + face_areas.append(face_area) + + largest_idx = face_areas.index(max(face_areas)) + d = dets[largest_idx] + shape = predictor(img, d) + print("Part 0: {}, Part 1: {} ...".format( + shape.part(0), shape.part(1))) + else: + for k, d in enumerate(dets): + print("Detection {}: Left: {} Top: {} Right: {} Bottom: {}".format( + k, d.left(), d.top(), d.right(), d.bottom())) + # Get the landmarks/parts for the face in box d. + shape = predictor(img, d) + print("Part 0: {}, Part 1: {} ...".format( + shape.part(0), shape.part(1))) + + t = list(shape.parts()) + a = [] + for tt in t: + a.append([tt.x, tt.y]) + lm = np.array(a) + # lm is a shape=(68,2) np.array + return lm + +def align_face(filepath, out_path): + """ + :param filepath: str + :return: PIL Image + """ + try: + lm = get_landmark(filepath) + except: + print('No landmark ...') + return + + lm_chin = lm[0:17] # left-right + lm_eyebrow_left = lm[17:22] # left-right + lm_eyebrow_right = lm[22:27] # left-right + lm_nose = lm[27:31] # top-down + lm_nostrils = lm[31:36] # top-down + lm_eye_left = lm[36:42] # left-clockwise + lm_eye_right = lm[42:48] # left-clockwise + lm_mouth_outer = lm[48:60] # left-clockwise + lm_mouth_inner = lm[60:68] # left-clockwise + + # Calculate auxiliary vectors. + eye_left = np.mean(lm_eye_left, axis=0) + eye_right = np.mean(lm_eye_right, axis=0) + eye_avg = (eye_left + eye_right) * 0.5 + eye_to_eye = eye_right - eye_left + mouth_left = lm_mouth_outer[0] + mouth_right = lm_mouth_outer[6] + mouth_avg = (mouth_left + mouth_right) * 0.5 + eye_to_mouth = mouth_avg - eye_avg + + # Choose oriented crop rectangle. + x = eye_to_eye - np.flipud(eye_to_mouth) * [-1, 1] + x /= np.hypot(*x) + x *= max(np.hypot(*eye_to_eye) * 2.0, np.hypot(*eye_to_mouth) * 1.8) + y = np.flipud(x) * [-1, 1] + c = eye_avg + eye_to_mouth * 0.1 + quad = np.stack([c - x - y, c - x + y, c + x + y, c + x - y]) + qsize = np.hypot(*x) * 2 + + # read image + img = PIL.Image.open(filepath) + + output_size = 512 + transform_size = 4096 + enable_padding = False + + # Shrink. + shrink = int(np.floor(qsize / output_size * 0.5)) + if shrink > 1: + rsize = (int(np.rint(float(img.size[0]) / shrink)), + int(np.rint(float(img.size[1]) / shrink))) + img = img.resize(rsize, PIL.Image.ANTIALIAS) + quad /= shrink + qsize /= shrink + + # Crop. + border = max(int(np.rint(qsize * 0.1)), 3) + crop = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))), + int(np.ceil(max(quad[:, 0]))), int(np.ceil(max(quad[:, 1])))) + crop = (max(crop[0] - border, 0), max(crop[1] - border, 0), + min(crop[2] + border, + img.size[0]), min(crop[3] + border, img.size[1])) + if crop[2] - crop[0] < img.size[0] or crop[3] - crop[1] < img.size[1]: + img = img.crop(crop) + quad -= crop[0:2] + + # Pad. + pad = (int(np.floor(min(quad[:, 0]))), int(np.floor(min(quad[:, 1]))), + int(np.ceil(max(quad[:, 0]))), int(np.ceil(max(quad[:, 1])))) + pad = (max(-pad[0] + border, + 0), max(-pad[1] + border, + 0), max(pad[2] - img.size[0] + border, + 0), max(pad[3] - img.size[1] + border, 0)) + if enable_padding and max(pad) > border - 4: + pad = np.maximum(pad, int(np.rint(qsize * 0.3))) + img = np.pad( + np.float32(img), ((pad[1], pad[3]), (pad[0], pad[2]), (0, 0)), + 'reflect') + h, w, _ = img.shape + y, x, _ = np.ogrid[:h, :w, :1] + mask = np.maximum( + 1.0 - + np.minimum(np.float32(x) / pad[0], + np.float32(w - 1 - x) / pad[2]), 1.0 - + np.minimum(np.float32(y) / pad[1], + np.float32(h - 1 - y) / pad[3])) + blur = qsize * 0.02 + img += (scipy.ndimage.gaussian_filter(img, [blur, blur, 0]) - + img) * np.clip(mask * 3.0 + 1.0, 0.0, 1.0) + img += (np.median(img, axis=(0, 1)) - img) * np.clip(mask, 0.0, 1.0) + img = PIL.Image.fromarray( + np.uint8(np.clip(np.rint(img), 0, 255)), 'RGB') + quad += pad[:2] + + img = img.transform((transform_size, transform_size), PIL.Image.QUAD, + (quad + 0.5).flatten(), PIL.Image.BILINEAR) + + if output_size < transform_size: + img = img.resize((output_size, output_size), PIL.Image.ANTIALIAS) + + # Save aligned image. + print('saveing: ', out_path) + img.save(out_path) + + return img, np.max(quad[:, 0]) - np.min(quad[:, 0]) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--in_dir', type=str, default='./inputs/whole_imgs') + parser.add_argument('--out_dir', type=str, default='./inputs/cropped_faces') + args = parser.parse_args() + + img_list = sorted(glob.glob(f'{args.in_dir}/*.png')) + img_list = sorted(img_list) + + for in_path in img_list: + out_path = os.path.join(args.out_dir, in_path.split("/")[-1]) + out_path = out_path.replace('.jpg', '.png') + size_ = align_face(in_path, out_path) \ No newline at end of file diff --git a/repositories/codeformer/scripts/download_pretrained_models.py b/repositories/codeformer/scripts/download_pretrained_models.py new file mode 100644 index 000000000..daa6e8ca1 --- /dev/null +++ b/repositories/codeformer/scripts/download_pretrained_models.py @@ -0,0 +1,40 @@ +import argparse +import os +from os import path as osp + +from basicsr.utils.download_util import load_file_from_url + + +def download_pretrained_models(method, file_urls): + save_path_root = f'./weights/{method}' + os.makedirs(save_path_root, exist_ok=True) + + for file_name, file_url in file_urls.items(): + save_path = load_file_from_url(url=file_url, model_dir=save_path_root, progress=True, file_name=file_name) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + + parser.add_argument( + 'method', + type=str, + help=("Options: 'CodeFormer' 'facelib'. Set to 'all' to download all the models.")) + args = parser.parse_args() + + file_urls = { + 'CodeFormer': { + 'codeformer.pth': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth' + }, + 'facelib': { + # 'yolov5l-face.pth': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/yolov5l-face.pth', + 'detection_Resnet50_Final.pth': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/detection_Resnet50_Final.pth', + 'parsing_parsenet.pth': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/parsing_parsenet.pth' + } + } + + if args.method == 'all': + for method in file_urls.keys(): + download_pretrained_models(method, file_urls[method]) + else: + download_pretrained_models(args.method, file_urls[args.method]) \ No newline at end of file diff --git a/repositories/codeformer/scripts/download_pretrained_models_from_gdrive.py b/repositories/codeformer/scripts/download_pretrained_models_from_gdrive.py new file mode 100644 index 000000000..7df5be6fc --- /dev/null +++ b/repositories/codeformer/scripts/download_pretrained_models_from_gdrive.py @@ -0,0 +1,60 @@ +import argparse +import os +from os import path as osp + +# from basicsr.utils.download_util import download_file_from_google_drive +import gdown + + +def download_pretrained_models(method, file_ids): + save_path_root = f'./weights/{method}' + os.makedirs(save_path_root, exist_ok=True) + + for file_name, file_id in file_ids.items(): + file_url = 'https://drive.google.com/uc?id='+file_id + save_path = osp.abspath(osp.join(save_path_root, file_name)) + if osp.exists(save_path): + user_response = input(f'{file_name} already exist. Do you want to cover it? Y/N\n') + if user_response.lower() == 'y': + print(f'Covering {file_name} to {save_path}') + gdown.download(file_url, save_path, quiet=False) + # download_file_from_google_drive(file_id, save_path) + elif user_response.lower() == 'n': + print(f'Skipping {file_name}') + else: + raise ValueError('Wrong input. Only accepts Y/N.') + else: + print(f'Downloading {file_name} to {save_path}') + gdown.download(file_url, save_path, quiet=False) + # download_file_from_google_drive(file_id, save_path) + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + + parser.add_argument( + 'method', + type=str, + help=("Options: 'CodeFormer' 'facelib'. Set to 'all' to download all the models.")) + args = parser.parse_args() + + # file name: file id + # 'dlib': { + # 'mmod_human_face_detector-4cb19393.dat': '1qD-OqY8M6j4PWUP_FtqfwUPFPRMu6ubX', + # 'shape_predictor_5_face_landmarks-c4b1e980.dat': '1vF3WBUApw4662v9Pw6wke3uk1qxnmLdg', + # 'shape_predictor_68_face_landmarks-fbdc2cb8.dat': '1tJyIVdCHaU6IDMDx86BZCxLGZfsWB8yq' + # } + file_ids = { + 'CodeFormer': { + 'codeformer.pth': '1v_E_vZvP-dQPF55Kc5SRCjaKTQXDz-JB' + }, + 'facelib': { + 'yolov5l-face.pth': '131578zMA6B2x8VQHyHfa6GEPtulMCNzV', + 'parsing_parsenet.pth': '16pkohyZZ8ViHGBk3QtVqxLZKzdo466bK' + } + } + + if args.method == 'all': + for method in file_ids.keys(): + download_pretrained_models(method, file_ids[method]) + else: + download_pretrained_models(args.method, file_ids[args.method]) \ No newline at end of file diff --git a/repositories/codeformer/web-demos/hugging_face/app.py b/repositories/codeformer/web-demos/hugging_face/app.py new file mode 100644 index 000000000..7da0fc947 --- /dev/null +++ b/repositories/codeformer/web-demos/hugging_face/app.py @@ -0,0 +1,280 @@ +""" +This file is used for deploying hugging face demo: +https://huggingface.co/spaces/sczhou/CodeFormer +""" + +import sys +sys.path.append('CodeFormer') +import os +import cv2 +import torch +import torch.nn.functional as F +import gradio as gr + +from torchvision.transforms.functional import normalize + +from basicsr.utils import imwrite, img2tensor, tensor2img +from basicsr.utils.download_util import load_file_from_url +from facelib.utils.face_restoration_helper import FaceRestoreHelper +from facelib.utils.misc import is_gray +from basicsr.archs.rrdbnet_arch import RRDBNet +from basicsr.utils.realesrgan_utils import RealESRGANer + +from basicsr.utils.registry import ARCH_REGISTRY + + +os.system("pip freeze") + +pretrain_model_url = { + 'codeformer': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth', + 'detection': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/detection_Resnet50_Final.pth', + 'parsing': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/parsing_parsenet.pth', + 'realesrgan': 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/RealESRGAN_x2plus.pth' +} +# download weights +if not os.path.exists('CodeFormer/weights/CodeFormer/codeformer.pth'): + load_file_from_url(url=pretrain_model_url['codeformer'], model_dir='CodeFormer/weights/CodeFormer', progress=True, file_name=None) +if not os.path.exists('CodeFormer/weights/facelib/detection_Resnet50_Final.pth'): + load_file_from_url(url=pretrain_model_url['detection'], model_dir='CodeFormer/weights/facelib', progress=True, file_name=None) +if not os.path.exists('CodeFormer/weights/facelib/parsing_parsenet.pth'): + load_file_from_url(url=pretrain_model_url['parsing'], model_dir='CodeFormer/weights/facelib', progress=True, file_name=None) +if not os.path.exists('CodeFormer/weights/realesrgan/RealESRGAN_x2plus.pth'): + load_file_from_url(url=pretrain_model_url['realesrgan'], model_dir='CodeFormer/weights/realesrgan', progress=True, file_name=None) + +# download images +torch.hub.download_url_to_file( + 'https://replicate.com/api/models/sczhou/codeformer/files/fa3fe3d1-76b0-4ca8-ac0d-0a925cb0ff54/06.png', + '01.png') +torch.hub.download_url_to_file( + 'https://replicate.com/api/models/sczhou/codeformer/files/a1daba8e-af14-4b00-86a4-69cec9619b53/04.jpg', + '02.jpg') +torch.hub.download_url_to_file( + 'https://replicate.com/api/models/sczhou/codeformer/files/542d64f9-1712-4de7-85f7-3863009a7c3d/03.jpg', + '03.jpg') +torch.hub.download_url_to_file( + 'https://replicate.com/api/models/sczhou/codeformer/files/a11098b0-a18a-4c02-a19a-9a7045d68426/010.jpg', + '04.jpg') +torch.hub.download_url_to_file( + 'https://replicate.com/api/models/sczhou/codeformer/files/7cf19c2c-e0cf-4712-9af8-cf5bdbb8d0ee/012.jpg', + '05.jpg') + +def imread(img_path): + img = cv2.imread(img_path) + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + return img + +# set enhancer with RealESRGAN +def set_realesrgan(): + half = True if torch.cuda.is_available() else False + model = RRDBNet( + num_in_ch=3, + num_out_ch=3, + num_feat=64, + num_block=23, + num_grow_ch=32, + scale=2, + ) + upsampler = RealESRGANer( + scale=2, + model_path="CodeFormer/weights/realesrgan/RealESRGAN_x2plus.pth", + model=model, + tile=400, + tile_pad=40, + pre_pad=0, + half=half, + ) + return upsampler + +upsampler = set_realesrgan() +device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') +codeformer_net = ARCH_REGISTRY.get("CodeFormer")( + dim_embd=512, + codebook_size=1024, + n_head=8, + n_layers=9, + connect_list=["32", "64", "128", "256"], +).to(device) +ckpt_path = "CodeFormer/weights/CodeFormer/codeformer.pth" +checkpoint = torch.load(ckpt_path)["params_ema"] +codeformer_net.load_state_dict(checkpoint) +codeformer_net.eval() + +os.makedirs('output', exist_ok=True) + +def inference(image, background_enhance, face_upsample, upscale, codeformer_fidelity): + """Run a single prediction on the model""" + try: # global try + # take the default setting for the demo + has_aligned = False + only_center_face = False + draw_box = False + detection_model = "retinaface_resnet50" + print('Inp:', image, background_enhance, face_upsample, upscale, codeformer_fidelity) + + img = cv2.imread(str(image), cv2.IMREAD_COLOR) + print('\timage size:', img.shape) + + upscale = int(upscale) # convert type to int + if upscale > 4: # avoid memory exceeded due to too large upscale + upscale = 4 + if upscale > 2 and max(img.shape[:2])>1000: # avoid memory exceeded due to too large img resolution + upscale = 2 + if max(img.shape[:2]) > 1500: # avoid memory exceeded due to too large img resolution + upscale = 1 + background_enhance = False + face_upsample = False + + face_helper = FaceRestoreHelper( + upscale, + face_size=512, + crop_ratio=(1, 1), + det_model=detection_model, + save_ext="png", + use_parse=True, + device=device, + ) + bg_upsampler = upsampler if background_enhance else None + face_upsampler = upsampler if face_upsample else None + + if has_aligned: + # the input faces are already cropped and aligned + img = cv2.resize(img, (512, 512), interpolation=cv2.INTER_LINEAR) + face_helper.is_gray = is_gray(img, threshold=5) + if face_helper.is_gray: + print('\tgrayscale input: True') + face_helper.cropped_faces = [img] + else: + face_helper.read_image(img) + # get face landmarks for each face + num_det_faces = face_helper.get_face_landmarks_5( + only_center_face=only_center_face, resize=640, eye_dist_threshold=5 + ) + print(f'\tdetect {num_det_faces} faces') + # align and warp each face + face_helper.align_warp_face() + + # face restoration for each cropped face + for idx, cropped_face in enumerate(face_helper.cropped_faces): + # prepare data + cropped_face_t = img2tensor( + cropped_face / 255.0, bgr2rgb=True, float32=True + ) + normalize(cropped_face_t, (0.5, 0.5, 0.5), (0.5, 0.5, 0.5), inplace=True) + cropped_face_t = cropped_face_t.unsqueeze(0).to(device) + + try: + with torch.no_grad(): + output = codeformer_net( + cropped_face_t, w=codeformer_fidelity, adain=True + )[0] + restored_face = tensor2img(output, rgb2bgr=True, min_max=(-1, 1)) + del output + torch.cuda.empty_cache() + except RuntimeError as error: + print(f"Failed inference for CodeFormer: {error}") + restored_face = tensor2img( + cropped_face_t, rgb2bgr=True, min_max=(-1, 1) + ) + + restored_face = restored_face.astype("uint8") + face_helper.add_restored_face(restored_face) + + # paste_back + if not has_aligned: + # upsample the background + if bg_upsampler is not None: + # Now only support RealESRGAN for upsampling background + bg_img = bg_upsampler.enhance(img, outscale=upscale)[0] + else: + bg_img = None + face_helper.get_inverse_affine(None) + # paste each restored face to the input image + if face_upsample and face_upsampler is not None: + restored_img = face_helper.paste_faces_to_input_image( + upsample_img=bg_img, + draw_box=draw_box, + face_upsampler=face_upsampler, + ) + else: + restored_img = face_helper.paste_faces_to_input_image( + upsample_img=bg_img, draw_box=draw_box + ) + + # save restored img + save_path = f'output/out.png' + imwrite(restored_img, str(save_path)) + + restored_img = cv2.cvtColor(restored_img, cv2.COLOR_BGR2RGB) + return restored_img, save_path + except Exception as error: + print('Global exception', error) + return None, None + + +title = "CodeFormer: Robust Face Restoration and Enhancement Network" +description = r"""
CodeFormer logo
+Official Gradio demo for Towards Robust Blind Face Restoration with Codebook Lookup Transformer (NeurIPS 2022).
+🔥 CodeFormer is a robust face restoration algorithm for old photos or AI-generated faces.
+🤗 Try CodeFormer for improved stable-diffusion generation!
+""" +article = r""" +If CodeFormer is helpful, please help to ⭐ the Github Repo. Thanks! +[![GitHub Stars](https://img.shields.io/github/stars/sczhou/CodeFormer?style=social)](https://github.com/sczhou/CodeFormer) + +--- + +📝 **Citation** + +If our work is useful for your research, please consider citing: +```bibtex +@inproceedings{zhou2022codeformer, + author = {Zhou, Shangchen and Chan, Kelvin C.K. and Li, Chongyi and Loy, Chen Change}, + title = {Towards Robust Blind Face Restoration with Codebook Lookup TransFormer}, + booktitle = {NeurIPS}, + year = {2022} +} +``` + +📋 **License** + +This project is licensed under S-Lab License 1.0. +Redistribution and use for non-commercial purposes should follow this license. + +📧 **Contact** + +If you have any questions, please feel free to reach me out at shangchenzhou@gmail.com. + +
+ 🤗 Find Me: + Twitter Follow + Github Follow +
+ +
visitors
+""" + +demo = gr.Interface( + inference, [ + gr.inputs.Image(type="filepath", label="Input"), + gr.inputs.Checkbox(default=True, label="Background_Enhance"), + gr.inputs.Checkbox(default=True, label="Face_Upsample"), + gr.inputs.Number(default=2, label="Rescaling_Factor (up to 4)"), + gr.Slider(0, 1, value=0.5, step=0.01, label='Codeformer_Fidelity (0 for better quality, 1 for better identity)') + ], [ + gr.outputs.Image(type="numpy", label="Output"), + gr.outputs.File(label="Download the output") + ], + title=title, + description=description, + article=article, + examples=[ + ['01.png', True, True, 2, 0.7], + ['02.jpg', True, True, 2, 0.7], + ['03.jpg', True, True, 2, 0.7], + ['04.jpg', True, True, 2, 0.1], + ['05.jpg', True, True, 2, 0.1] + ] + ) + +demo.queue(concurrency_count=2) +demo.launch() \ No newline at end of file diff --git a/repositories/codeformer/web-demos/replicate/cog.yaml b/repositories/codeformer/web-demos/replicate/cog.yaml new file mode 100644 index 000000000..3f4589690 --- /dev/null +++ b/repositories/codeformer/web-demos/replicate/cog.yaml @@ -0,0 +1,30 @@ +""" +This file is used for deploying replicate demo: +https://replicate.com/sczhou/codeformer +""" + +build: + gpu: true + cuda: "11.3" + python_version: "3.8" + system_packages: + - "libgl1-mesa-glx" + - "libglib2.0-0" + python_packages: + - "ipython==8.4.0" + - "future==0.18.2" + - "lmdb==1.3.0" + - "scikit-image==0.19.3" + - "torch==1.11.0 --extra-index-url=https://download.pytorch.org/whl/cu113" + - "torchvision==0.12.0 --extra-index-url=https://download.pytorch.org/whl/cu113" + - "scipy==1.9.0" + - "gdown==4.5.1" + - "pyyaml==6.0" + - "tb-nightly==2.11.0a20220906" + - "tqdm==4.64.1" + - "yapf==0.32.0" + - "lpips==0.1.4" + - "Pillow==9.2.0" + - "opencv-python==4.6.0.66" + +predict: "predict.py:Predictor" diff --git a/repositories/codeformer/web-demos/replicate/predict.py b/repositories/codeformer/web-demos/replicate/predict.py new file mode 100644 index 000000000..61935e9e7 --- /dev/null +++ b/repositories/codeformer/web-demos/replicate/predict.py @@ -0,0 +1,189 @@ +""" +This file is used for deploying replicate demo: +https://replicate.com/sczhou/codeformer +running: cog predict -i image=@inputs/whole_imgs/04.jpg -i codeformer_fidelity=0.5 -i upscale=2 +push: cog push r8.im/sczhou/codeformer +""" + +import tempfile +import cv2 +import torch +from torchvision.transforms.functional import normalize +try: + from cog import BasePredictor, Input, Path +except Exception: + print('please install cog package') + +from basicsr.utils import imwrite, img2tensor, tensor2img +from basicsr.archs.rrdbnet_arch import RRDBNet +from basicsr.utils.realesrgan_utils import RealESRGANer +from basicsr.utils.registry import ARCH_REGISTRY +from facelib.utils.face_restoration_helper import FaceRestoreHelper + + +class Predictor(BasePredictor): + def setup(self): + """Load the model into memory to make running multiple predictions efficient""" + self.device = "cuda:0" + self.upsampler = set_realesrgan() + self.net = ARCH_REGISTRY.get("CodeFormer")( + dim_embd=512, + codebook_size=1024, + n_head=8, + n_layers=9, + connect_list=["32", "64", "128", "256"], + ).to(self.device) + ckpt_path = "weights/CodeFormer/codeformer.pth" + checkpoint = torch.load(ckpt_path)[ + "params_ema" + ] # update file permission if cannot load + self.net.load_state_dict(checkpoint) + self.net.eval() + + def predict( + self, + image: Path = Input(description="Input image"), + codeformer_fidelity: float = Input( + default=0.5, + ge=0, + le=1, + description="Balance the quality (lower number) and fidelity (higher number).", + ), + background_enhance: bool = Input( + description="Enhance background image with Real-ESRGAN", default=True + ), + face_upsample: bool = Input( + description="Upsample restored faces for high-resolution AI-created images", + default=True, + ), + upscale: int = Input( + description="The final upsampling scale of the image", + default=2, + ), + ) -> Path: + """Run a single prediction on the model""" + + # take the default setting for the demo + has_aligned = False + only_center_face = False + draw_box = False + detection_model = "retinaface_resnet50" + + self.face_helper = FaceRestoreHelper( + upscale, + face_size=512, + crop_ratio=(1, 1), + det_model=detection_model, + save_ext="png", + use_parse=True, + device=self.device, + ) + + bg_upsampler = self.upsampler if background_enhance else None + face_upsampler = self.upsampler if face_upsample else None + + img = cv2.imread(str(image), cv2.IMREAD_COLOR) + + if has_aligned: + # the input faces are already cropped and aligned + img = cv2.resize(img, (512, 512), interpolation=cv2.INTER_LINEAR) + self.face_helper.cropped_faces = [img] + else: + self.face_helper.read_image(img) + # get face landmarks for each face + num_det_faces = self.face_helper.get_face_landmarks_5( + only_center_face=only_center_face, resize=640, eye_dist_threshold=5 + ) + print(f"\tdetect {num_det_faces} faces") + # align and warp each face + self.face_helper.align_warp_face() + + # face restoration for each cropped face + for idx, cropped_face in enumerate(self.face_helper.cropped_faces): + # prepare data + cropped_face_t = img2tensor( + cropped_face / 255.0, bgr2rgb=True, float32=True + ) + normalize(cropped_face_t, (0.5, 0.5, 0.5), (0.5, 0.5, 0.5), inplace=True) + cropped_face_t = cropped_face_t.unsqueeze(0).to(self.device) + + try: + with torch.no_grad(): + output = self.net( + cropped_face_t, w=codeformer_fidelity, adain=True + )[0] + restored_face = tensor2img(output, rgb2bgr=True, min_max=(-1, 1)) + del output + torch.cuda.empty_cache() + except Exception as error: + print(f"\tFailed inference for CodeFormer: {error}") + restored_face = tensor2img( + cropped_face_t, rgb2bgr=True, min_max=(-1, 1) + ) + + restored_face = restored_face.astype("uint8") + self.face_helper.add_restored_face(restored_face) + + # paste_back + if not has_aligned: + # upsample the background + if bg_upsampler is not None: + # Now only support RealESRGAN for upsampling background + bg_img = bg_upsampler.enhance(img, outscale=upscale)[0] + else: + bg_img = None + self.face_helper.get_inverse_affine(None) + # paste each restored face to the input image + if face_upsample and face_upsampler is not None: + restored_img = self.face_helper.paste_faces_to_input_image( + upsample_img=bg_img, + draw_box=draw_box, + face_upsampler=face_upsampler, + ) + else: + restored_img = self.face_helper.paste_faces_to_input_image( + upsample_img=bg_img, draw_box=draw_box + ) + + # save restored img + out_path = Path(tempfile.mkdtemp()) / 'output.png' + imwrite(restored_img, str(out_path)) + + return out_path + + +def imread(img_path): + img = cv2.imread(img_path) + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + return img + + +def set_realesrgan(): + if not torch.cuda.is_available(): # CPU + import warnings + + warnings.warn( + "The unoptimized RealESRGAN is slow on CPU. We do not use it. " + "If you really want to use it, please modify the corresponding codes.", + category=RuntimeWarning, + ) + upsampler = None + else: + model = RRDBNet( + num_in_ch=3, + num_out_ch=3, + num_feat=64, + num_block=23, + num_grow_ch=32, + scale=2, + ) + upsampler = RealESRGANer( + scale=2, + model_path="./weights/realesrgan/RealESRGAN_x2plus.pth", + model=model, + tile=400, + tile_pad=40, + pre_pad=0, + half=True, + ) + return upsampler diff --git a/repositories/codeformer/weights/CodeFormer/.gitkeep b/repositories/codeformer/weights/CodeFormer/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/codeformer/weights/README.md b/repositories/codeformer/weights/README.md new file mode 100644 index 000000000..67ad334bd --- /dev/null +++ b/repositories/codeformer/weights/README.md @@ -0,0 +1,3 @@ +# Weights + +Put the downloaded pre-trained models to this folder. \ No newline at end of file diff --git a/repositories/codeformer/weights/facelib/.gitkeep b/repositories/codeformer/weights/facelib/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/ldm/README.md b/repositories/ldm/README.md new file mode 100644 index 000000000..2dfddca33 --- /dev/null +++ b/repositories/ldm/README.md @@ -0,0 +1,302 @@ +# Stable Diffusion Version 2 +![t2i](assets/stable-samples/txt2img/768/merged-0006.png) +![t2i](assets/stable-samples/txt2img/768/merged-0002.png) +![t2i](assets/stable-samples/txt2img/768/merged-0005.png) + +This repository contains [Stable Diffusion](https://github.com/CompVis/stable-diffusion) models trained from scratch and will be continuously updated with +new checkpoints. The following list provides an overview of all currently available models. More coming soon. + +## News + + +**March 24, 2023** + +*Stable UnCLIP 2.1* + +- New stable diffusion finetune (_Stable unCLIP 2.1_, [Hugging Face](https://huggingface.co/stabilityai/)) at 768x768 resolution, based on SD2.1-768. This model allows for image variations and mixing operations as described in [*Hierarchical Text-Conditional Image Generation with CLIP Latents*](https://arxiv.org/abs/2204.06125), and, thanks to its modularity, can be combined with other models such as [KARLO](https://github.com/kakaobrain/karlo). Comes in two variants: [*Stable unCLIP-L*](https://huggingface.co/stabilityai/stable-diffusion-2-1-unclip/blob/main/sd21-unclip-l.ckpt) and [*Stable unCLIP-H*](https://huggingface.co/stabilityai/stable-diffusion-2-1-unclip/blob/main/sd21-unclip-h.ckpt), which are conditioned on CLIP ViT-L and ViT-H image embeddings, respectively. Instructions are available [here](doc/UNCLIP.MD). + +- A public demo of SD-unCLIP is already available at [clipdrop.co/stable-diffusion-reimagine](https://clipdrop.co/stable-diffusion-reimagine) + + +**December 7, 2022** + +*Version 2.1* + +- New stable diffusion model (_Stable Diffusion 2.1-v_, [Hugging Face](https://huggingface.co/stabilityai/stable-diffusion-2-1)) at 768x768 resolution and (_Stable Diffusion 2.1-base_, [HuggingFace](https://huggingface.co/stabilityai/stable-diffusion-2-1-base)) at 512x512 resolution, both based on the same number of parameters and architecture as 2.0 and fine-tuned on 2.0, on a less restrictive NSFW filtering of the [LAION-5B](https://laion.ai/blog/laion-5b/) dataset. +Per default, the attention operation of the model is evaluated at full precision when `xformers` is not installed. To enable fp16 (which can cause numerical instabilities with the vanilla attention module on the v2.1 model) , run your script with `ATTN_PRECISION=fp16 python ` + +**November 24, 2022** + +*Version 2.0* + +- New stable diffusion model (_Stable Diffusion 2.0-v_) at 768x768 resolution. Same number of parameters in the U-Net as 1.5, but uses [OpenCLIP-ViT/H](https://github.com/mlfoundations/open_clip) as the text encoder and is trained from scratch. _SD 2.0-v_ is a so-called [v-prediction](https://arxiv.org/abs/2202.00512) model. +- The above model is finetuned from _SD 2.0-base_, which was trained as a standard noise-prediction model on 512x512 images and is also made available. +- Added a [x4 upscaling latent text-guided diffusion model](#image-upscaling-with-stable-diffusion). +- New [depth-guided stable diffusion model](#depth-conditional-stable-diffusion), finetuned from _SD 2.0-base_. The model is conditioned on monocular depth estimates inferred via [MiDaS](https://github.com/isl-org/MiDaS) and can be used for structure-preserving img2img and shape-conditional synthesis. + + ![d2i](assets/stable-samples/depth2img/depth2img01.png) +- A [text-guided inpainting model](#image-inpainting-with-stable-diffusion), finetuned from SD _2.0-base_. + +We follow the [original repository](https://github.com/CompVis/stable-diffusion) and provide basic inference scripts to sample from the models. + +________________ +*The original Stable Diffusion model was created in a collaboration with [CompVis](https://arxiv.org/abs/2202.00512) and [RunwayML](https://runwayml.com/) and builds upon the work:* + +[**High-Resolution Image Synthesis with Latent Diffusion Models**](https://ommer-lab.com/research/latent-diffusion-models/)
+[Robin Rombach](https://github.com/rromb)\*, +[Andreas Blattmann](https://github.com/ablattmann)\*, +[Dominik Lorenz](https://github.com/qp-qp)\, +[Patrick Esser](https://github.com/pesser), +[Björn Ommer](https://hci.iwr.uni-heidelberg.de/Staff/bommer)
+_[CVPR '22 Oral](https://openaccess.thecvf.com/content/CVPR2022/html/Rombach_High-Resolution_Image_Synthesis_With_Latent_Diffusion_Models_CVPR_2022_paper.html) | +[GitHub](https://github.com/CompVis/latent-diffusion) | [arXiv](https://arxiv.org/abs/2112.10752) | [Project page](https://ommer-lab.com/research/latent-diffusion-models/)_ + +and [many others](#shout-outs). + +Stable Diffusion is a latent text-to-image diffusion model. +________________________________ + +## Requirements + +You can update an existing [latent diffusion](https://github.com/CompVis/latent-diffusion) environment by running + +``` +conda install pytorch==1.12.1 torchvision==0.13.1 -c pytorch +pip install transformers==4.19.2 diffusers invisible-watermark +pip install -e . +``` +#### xformers efficient attention +For more efficiency and speed on GPUs, +we highly recommended installing the [xformers](https://github.com/facebookresearch/xformers) +library. + +Tested on A100 with CUDA 11.4. +Installation needs a somewhat recent version of nvcc and gcc/g++, obtain those, e.g., via +```commandline +export CUDA_HOME=/usr/local/cuda-11.4 +conda install -c nvidia/label/cuda-11.4.0 cuda-nvcc +conda install -c conda-forge gcc +conda install -c conda-forge gxx_linux-64==9.5.0 +``` + +Then, run the following (compiling takes up to 30 min). + +```commandline +cd .. +git clone https://github.com/facebookresearch/xformers.git +cd xformers +git submodule update --init --recursive +pip install -r requirements.txt +pip install -e . +cd ../stablediffusion +``` +Upon successful installation, the code will automatically default to [memory efficient attention](https://github.com/facebookresearch/xformers) +for the self- and cross-attention layers in the U-Net and autoencoder. + +## General Disclaimer +Stable Diffusion models are general text-to-image diffusion models and therefore mirror biases and (mis-)conceptions that are present +in their training data. Although efforts were made to reduce the inclusion of explicit pornographic material, **we do not recommend using the provided weights for services or products without additional safety mechanisms and considerations. +The weights are research artifacts and should be treated as such.** +Details on the training procedure and data, as well as the intended use of the model can be found in the corresponding [model card](https://huggingface.co/stabilityai/stable-diffusion-2). +The weights are available via [the StabilityAI organization at Hugging Face](https://huggingface.co/StabilityAI) under the [CreativeML Open RAIL++-M License](LICENSE-MODEL). + + + +## Stable Diffusion v2 + +Stable Diffusion v2 refers to a specific configuration of the model +architecture that uses a downsampling-factor 8 autoencoder with an 865M UNet +and OpenCLIP ViT-H/14 text encoder for the diffusion model. The _SD 2-v_ model produces 768x768 px outputs. + +Evaluations with different classifier-free guidance scales (1.5, 2.0, 3.0, 4.0, +5.0, 6.0, 7.0, 8.0) and 50 DDIM sampling steps show the relative improvements of the checkpoints: + +![sd evaluation results](assets/model-variants.jpg) + + + +### Text-to-Image +![txt2img-stable2](assets/stable-samples/txt2img/merged-0003.png) +![txt2img-stable2](assets/stable-samples/txt2img/merged-0001.png) + +Stable Diffusion 2 is a latent diffusion model conditioned on the penultimate text embeddings of a CLIP ViT-H/14 text encoder. +We provide a [reference script for sampling](#reference-sampling-script). +#### Reference Sampling Script + +This script incorporates an [invisible watermarking](https://github.com/ShieldMnt/invisible-watermark) of the outputs, to help viewers [identify the images as machine-generated](scripts/tests/test_watermark.py). +We provide the configs for the _SD2-v_ (768px) and _SD2-base_ (512px) model. + +First, download the weights for [_SD2.1-v_](https://huggingface.co/stabilityai/stable-diffusion-2-1) and [_SD2.1-base_](https://huggingface.co/stabilityai/stable-diffusion-2-1-base). + +To sample from the _SD2.1-v_ model, run the following: + +``` +python scripts/txt2img.py --prompt "a professional photograph of an astronaut riding a horse" --ckpt --config configs/stable-diffusion/v2-inference-v.yaml --H 768 --W 768 +``` +or try out the Web Demo: [![Hugging Face Spaces](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue)](https://huggingface.co/spaces/stabilityai/stable-diffusion). + +To sample from the base model, use +``` +python scripts/txt2img.py --prompt "a professional photograph of an astronaut riding a horse" --ckpt --config +``` + +By default, this uses the [DDIM sampler](https://arxiv.org/abs/2010.02502), and renders images of size 768x768 (which it was trained on) in 50 steps. +Empirically, the v-models can be sampled with higher guidance scales. + +Note: The inference config for all model versions is designed to be used with EMA-only checkpoints. +For this reason `use_ema=False` is set in the configuration, otherwise the code will try to switch from +non-EMA to EMA weights. + +#### Enable Intel® Extension for PyTorch* optimizations in Text-to-Image script + +If you're planning on running Text-to-Image on Intel® CPU, try to sample an image with TorchScript and Intel® Extension for PyTorch* optimizations. Intel® Extension for PyTorch* extends PyTorch by enabling up-to-date features optimizations for an extra performance boost on Intel® hardware. It can optimize memory layout of the operators to Channel Last memory format, which is generally beneficial for Intel CPUs, take advantage of the most advanced instruction set available on a machine, optimize operators and many more. + +**Prerequisites** + +Before running the script, make sure you have all needed libraries installed. (the optimization was checked on `Ubuntu 20.04`). Install [jemalloc](https://github.com/jemalloc/jemalloc), [numactl](https://linux.die.net/man/8/numactl), Intel® OpenMP and Intel® Extension for PyTorch*. + +```bash +apt-get install numactl libjemalloc-dev +pip install intel-openmp +pip install intel_extension_for_pytorch -f https://software.intel.com/ipex-whl-stable +``` + +To sample from the _SD2.1-v_ model with TorchScript+IPEX optimizations, run the following. Remember to specify desired number of instances you want to run the program on ([more](https://github.com/intel/intel-extension-for-pytorch/blob/master/intel_extension_for_pytorch/cpu/launch.py#L48)). + +``` +MALLOC_CONF=oversize_threshold:1,background_thread:true,metadata_thp:auto,dirty_decay_ms:9000000000,muzzy_decay_ms:9000000000 python -m intel_extension_for_pytorch.cpu.launch --ninstance --enable_jemalloc scripts/txt2img.py --prompt \"a corgi is playing guitar, oil on canvas\" --ckpt --config configs/stable-diffusion/intel/v2-inference-v-fp32.yaml --H 768 --W 768 --precision full --device cpu --torchscript --ipex +``` + +To sample from the base model with IPEX optimizations, use + +``` +MALLOC_CONF=oversize_threshold:1,background_thread:true,metadata_thp:auto,dirty_decay_ms:9000000000,muzzy_decay_ms:9000000000 python -m intel_extension_for_pytorch.cpu.launch --ninstance --enable_jemalloc scripts/txt2img.py --prompt \"a corgi is playing guitar, oil on canvas\" --ckpt --config configs/stable-diffusion/intel/v2-inference-fp32.yaml --n_samples 1 --n_iter 4 --precision full --device cpu --torchscript --ipex +``` + +If you're using a CPU that supports `bfloat16`, consider sample from the model with bfloat16 enabled for a performance boost, like so + +```bash +# SD2.1-v +MALLOC_CONF=oversize_threshold:1,background_thread:true,metadata_thp:auto,dirty_decay_ms:9000000000,muzzy_decay_ms:9000000000 python -m intel_extension_for_pytorch.cpu.launch --ninstance --enable_jemalloc scripts/txt2img.py --prompt \"a corgi is playing guitar, oil on canvas\" --ckpt --config configs/stable-diffusion/intel/v2-inference-v-bf16.yaml --H 768 --W 768 --precision full --device cpu --torchscript --ipex --bf16 +# SD2.1-base +MALLOC_CONF=oversize_threshold:1,background_thread:true,metadata_thp:auto,dirty_decay_ms:9000000000,muzzy_decay_ms:9000000000 python -m intel_extension_for_pytorch.cpu.launch --ninstance --enable_jemalloc scripts/txt2img.py --prompt \"a corgi is playing guitar, oil on canvas\" --ckpt --config configs/stable-diffusion/intel/v2-inference-bf16.yaml --precision full --device cpu --torchscript --ipex --bf16 +``` + +### Image Modification with Stable Diffusion + +![depth2img-stable2](assets/stable-samples/depth2img/merged-0000.png) +#### Depth-Conditional Stable Diffusion + +To augment the well-established [img2img](https://github.com/CompVis/stable-diffusion#image-modification-with-stable-diffusion) functionality of Stable Diffusion, we provide a _shape-preserving_ stable diffusion model. + + +Note that the original method for image modification introduces significant semantic changes w.r.t. the initial image. +If that is not desired, download our [depth-conditional stable diffusion](https://huggingface.co/stabilityai/stable-diffusion-2-depth) model and the `dpt_hybrid` MiDaS [model weights](https://github.com/intel-isl/DPT/releases/download/1_0/dpt_hybrid-midas-501f0c75.pt), place the latter in a folder `midas_models` and sample via +``` +python scripts/gradio/depth2img.py configs/stable-diffusion/v2-midas-inference.yaml +``` + +or + +``` +streamlit run scripts/streamlit/depth2img.py configs/stable-diffusion/v2-midas-inference.yaml +``` + +This method can be used on the samples of the base model itself. +For example, take [this sample](assets/stable-samples/depth2img/old_man.png) generated by an anonymous discord user. +Using the [gradio](https://gradio.app) or [streamlit](https://streamlit.io/) script `depth2img.py`, the MiDaS model first infers a monocular depth estimate given this input, +and the diffusion model is then conditioned on the (relative) depth output. + +

+ depth2image
+ +

+ +This model is particularly useful for a photorealistic style; see the [examples](assets/stable-samples/depth2img). +For a maximum strength of 1.0, the model removes all pixel-based information and only relies on the text prompt and the inferred monocular depth estimate. + +![depth2img-stable3](assets/stable-samples/depth2img/merged-0005.png) + +#### Classic Img2Img + +For running the "classic" img2img, use +``` +python scripts/img2img.py --prompt "A fantasy landscape, trending on artstation" --init-img --strength 0.8 --ckpt +``` +and adapt the checkpoint and config paths accordingly. + +### Image Upscaling with Stable Diffusion +![upscaling-x4](assets/stable-samples/upscaling/merged-dog.png) +After [downloading the weights](https://huggingface.co/stabilityai/stable-diffusion-x4-upscaler), run +``` +python scripts/gradio/superresolution.py configs/stable-diffusion/x4-upscaling.yaml +``` + +or + +``` +streamlit run scripts/streamlit/superresolution.py -- configs/stable-diffusion/x4-upscaling.yaml +``` + +for a Gradio or Streamlit demo of the text-guided x4 superresolution model. +This model can be used both on real inputs and on synthesized examples. For the latter, we recommend setting a higher +`noise_level`, e.g. `noise_level=100`. + +### Image Inpainting with Stable Diffusion + +![inpainting-stable2](assets/stable-inpainting/merged-leopards.png) + +[Download the SD 2.0-inpainting checkpoint](https://huggingface.co/stabilityai/stable-diffusion-2-inpainting) and run + +``` +python scripts/gradio/inpainting.py configs/stable-diffusion/v2-inpainting-inference.yaml +``` + +or + +``` +streamlit run scripts/streamlit/inpainting.py -- configs/stable-diffusion/v2-inpainting-inference.yaml +``` + +for a Gradio or Streamlit demo of the inpainting model. +This scripts adds invisible watermarking to the demo in the [RunwayML](https://github.com/runwayml/stable-diffusion/blob/main/scripts/inpaint_st.py) repository, but both should work interchangeably with the checkpoints/configs. + + + +## Shout-Outs +- Thanks to [Hugging Face](https://huggingface.co/) and in particular [Apolinário](https://github.com/apolinario) for support with our model releases! +- Stable Diffusion would not be possible without [LAION](https://laion.ai/) and their efforts to create open, large-scale datasets. +- The [DeepFloyd team](https://twitter.com/deepfloydai) at Stability AI, for creating the subset of [LAION-5B](https://laion.ai/blog/laion-5b/) dataset used to train the model. +- Stable Diffusion 2.0 uses [OpenCLIP](https://laion.ai/blog/large-openclip/), trained by [Romain Beaumont](https://github.com/rom1504). +- Our codebase for the diffusion models builds heavily on [OpenAI's ADM codebase](https://github.com/openai/guided-diffusion) +and [https://github.com/lucidrains/denoising-diffusion-pytorch](https://github.com/lucidrains/denoising-diffusion-pytorch). +Thanks for open-sourcing! +- [CompVis](https://github.com/CompVis/stable-diffusion) initial stable diffusion release +- [Patrick](https://github.com/pesser)'s [implementation](https://github.com/runwayml/stable-diffusion/blob/main/scripts/inpaint_st.py) of the streamlit demo for inpainting. +- `img2img` is an application of [SDEdit](https://arxiv.org/abs/2108.01073) by [Chenlin Meng](https://cs.stanford.edu/~chenlin/) from the [Stanford AI Lab](https://cs.stanford.edu/~ermon/website/). +- [Kat's implementation]((https://github.com/CompVis/latent-diffusion/pull/51)) of the [PLMS](https://arxiv.org/abs/2202.09778) sampler, and [more](https://github.com/crowsonkb/k-diffusion). +- [DPMSolver](https://arxiv.org/abs/2206.00927) [integration](https://github.com/CompVis/stable-diffusion/pull/440) by [Cheng Lu](https://github.com/LuChengTHU). +- Facebook's [xformers](https://github.com/facebookresearch/xformers) for efficient attention computation. +- [MiDaS](https://github.com/isl-org/MiDaS) for monocular depth estimation. + + +## License + +The code in this repository is released under the MIT License. + +The weights are available via [the StabilityAI organization at Hugging Face](https://huggingface.co/StabilityAI), and released under the [CreativeML Open RAIL++-M License](LICENSE-MODEL) License. + +## BibTeX + +``` +@misc{rombach2021highresolution, + title={High-Resolution Image Synthesis with Latent Diffusion Models}, + author={Robin Rombach and Andreas Blattmann and Dominik Lorenz and Patrick Esser and Björn Ommer}, + year={2021}, + eprint={2112.10752}, + archivePrefix={arXiv}, + primaryClass={cs.CV} +} +``` + + diff --git a/repositories/ldm/data/__init__.py b/repositories/ldm/data/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/ldm/data/util.py b/repositories/ldm/data/util.py new file mode 100644 index 000000000..5b60ceb23 --- /dev/null +++ b/repositories/ldm/data/util.py @@ -0,0 +1,24 @@ +import torch + +from ldm.modules.midas.api import load_midas_transform + + +class AddMiDaS(object): + def __init__(self, model_type): + super().__init__() + self.transform = load_midas_transform(model_type) + + def pt2np(self, x): + x = ((x + 1.0) * .5).detach().cpu().numpy() + return x + + def np2pt(self, x): + x = torch.from_numpy(x) * 2 - 1. + return x + + def __call__(self, sample): + # sample['jpg'] is tensor hwc in [-1, 1] at this point + x = self.pt2np(sample['jpg']) + x = self.transform({"image": x})["image"] + sample['midas_in'] = x + return sample \ No newline at end of file diff --git a/repositories/ldm/models/autoencoder.py b/repositories/ldm/models/autoencoder.py new file mode 100644 index 000000000..d12254999 --- /dev/null +++ b/repositories/ldm/models/autoencoder.py @@ -0,0 +1,219 @@ +import torch +import pytorch_lightning as pl +import torch.nn.functional as F +from contextlib import contextmanager + +from ldm.modules.diffusionmodules.model import Encoder, Decoder +from ldm.modules.distributions.distributions import DiagonalGaussianDistribution + +from ldm.util import instantiate_from_config +from ldm.modules.ema import LitEma + + +class AutoencoderKL(pl.LightningModule): + def __init__(self, + ddconfig, + lossconfig, + embed_dim, + ckpt_path=None, + ignore_keys=[], + image_key="image", + colorize_nlabels=None, + monitor=None, + ema_decay=None, + learn_logvar=False + ): + super().__init__() + self.learn_logvar = learn_logvar + self.image_key = image_key + self.encoder = Encoder(**ddconfig) + self.decoder = Decoder(**ddconfig) + self.loss = instantiate_from_config(lossconfig) + assert ddconfig["double_z"] + self.quant_conv = torch.nn.Conv2d(2*ddconfig["z_channels"], 2*embed_dim, 1) + self.post_quant_conv = torch.nn.Conv2d(embed_dim, ddconfig["z_channels"], 1) + self.embed_dim = embed_dim + if colorize_nlabels is not None: + assert type(colorize_nlabels)==int + self.register_buffer("colorize", torch.randn(3, colorize_nlabels, 1, 1)) + if monitor is not None: + self.monitor = monitor + + self.use_ema = ema_decay is not None + if self.use_ema: + self.ema_decay = ema_decay + assert 0. < ema_decay < 1. + self.model_ema = LitEma(self, decay=ema_decay) + print(f"Keeping EMAs of {len(list(self.model_ema.buffers()))}.") + + if ckpt_path is not None: + self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys) + + def init_from_ckpt(self, path, ignore_keys=list()): + sd = torch.load(path, map_location="cpu")["state_dict"] + keys = list(sd.keys()) + for k in keys: + for ik in ignore_keys: + if k.startswith(ik): + print("Deleting key {} from state_dict.".format(k)) + del sd[k] + self.load_state_dict(sd, strict=False) + print(f"Restored from {path}") + + @contextmanager + def ema_scope(self, context=None): + if self.use_ema: + self.model_ema.store(self.parameters()) + self.model_ema.copy_to(self) + if context is not None: + print(f"{context}: Switched to EMA weights") + try: + yield None + finally: + if self.use_ema: + self.model_ema.restore(self.parameters()) + if context is not None: + print(f"{context}: Restored training weights") + + def on_train_batch_end(self, *args, **kwargs): + if self.use_ema: + self.model_ema(self) + + def encode(self, x): + h = self.encoder(x) + moments = self.quant_conv(h) + posterior = DiagonalGaussianDistribution(moments) + return posterior + + def decode(self, z): + z = self.post_quant_conv(z) + dec = self.decoder(z) + return dec + + def forward(self, input, sample_posterior=True): + posterior = self.encode(input) + if sample_posterior: + z = posterior.sample() + else: + z = posterior.mode() + dec = self.decode(z) + return dec, posterior + + def get_input(self, batch, k): + x = batch[k] + if len(x.shape) == 3: + x = x[..., None] + x = x.permute(0, 3, 1, 2).to(memory_format=torch.contiguous_format).float() + return x + + def training_step(self, batch, batch_idx, optimizer_idx): + inputs = self.get_input(batch, self.image_key) + reconstructions, posterior = self(inputs) + + if optimizer_idx == 0: + # train encoder+decoder+logvar + aeloss, log_dict_ae = self.loss(inputs, reconstructions, posterior, optimizer_idx, self.global_step, + last_layer=self.get_last_layer(), split="train") + self.log("aeloss", aeloss, prog_bar=True, logger=True, on_step=True, on_epoch=True) + self.log_dict(log_dict_ae, prog_bar=False, logger=True, on_step=True, on_epoch=False) + return aeloss + + if optimizer_idx == 1: + # train the discriminator + discloss, log_dict_disc = self.loss(inputs, reconstructions, posterior, optimizer_idx, self.global_step, + last_layer=self.get_last_layer(), split="train") + + self.log("discloss", discloss, prog_bar=True, logger=True, on_step=True, on_epoch=True) + self.log_dict(log_dict_disc, prog_bar=False, logger=True, on_step=True, on_epoch=False) + return discloss + + def validation_step(self, batch, batch_idx): + log_dict = self._validation_step(batch, batch_idx) + with self.ema_scope(): + log_dict_ema = self._validation_step(batch, batch_idx, postfix="_ema") + return log_dict + + def _validation_step(self, batch, batch_idx, postfix=""): + inputs = self.get_input(batch, self.image_key) + reconstructions, posterior = self(inputs) + aeloss, log_dict_ae = self.loss(inputs, reconstructions, posterior, 0, self.global_step, + last_layer=self.get_last_layer(), split="val"+postfix) + + discloss, log_dict_disc = self.loss(inputs, reconstructions, posterior, 1, self.global_step, + last_layer=self.get_last_layer(), split="val"+postfix) + + self.log(f"val{postfix}/rec_loss", log_dict_ae[f"val{postfix}/rec_loss"]) + self.log_dict(log_dict_ae) + self.log_dict(log_dict_disc) + return self.log_dict + + def configure_optimizers(self): + lr = self.learning_rate + ae_params_list = list(self.encoder.parameters()) + list(self.decoder.parameters()) + list( + self.quant_conv.parameters()) + list(self.post_quant_conv.parameters()) + if self.learn_logvar: + print(f"{self.__class__.__name__}: Learning logvar") + ae_params_list.append(self.loss.logvar) + opt_ae = torch.optim.Adam(ae_params_list, + lr=lr, betas=(0.5, 0.9)) + opt_disc = torch.optim.Adam(self.loss.discriminator.parameters(), + lr=lr, betas=(0.5, 0.9)) + return [opt_ae, opt_disc], [] + + def get_last_layer(self): + return self.decoder.conv_out.weight + + @torch.no_grad() + def log_images(self, batch, only_inputs=False, log_ema=False, **kwargs): + log = dict() + x = self.get_input(batch, self.image_key) + x = x.to(self.device) + if not only_inputs: + xrec, posterior = self(x) + if x.shape[1] > 3: + # colorize with random projection + assert xrec.shape[1] > 3 + x = self.to_rgb(x) + xrec = self.to_rgb(xrec) + log["samples"] = self.decode(torch.randn_like(posterior.sample())) + log["reconstructions"] = xrec + if log_ema or self.use_ema: + with self.ema_scope(): + xrec_ema, posterior_ema = self(x) + if x.shape[1] > 3: + # colorize with random projection + assert xrec_ema.shape[1] > 3 + xrec_ema = self.to_rgb(xrec_ema) + log["samples_ema"] = self.decode(torch.randn_like(posterior_ema.sample())) + log["reconstructions_ema"] = xrec_ema + log["inputs"] = x + return log + + def to_rgb(self, x): + assert self.image_key == "segmentation" + if not hasattr(self, "colorize"): + self.register_buffer("colorize", torch.randn(3, x.shape[1], 1, 1).to(x)) + x = F.conv2d(x, weight=self.colorize) + x = 2.*(x-x.min())/(x.max()-x.min()) - 1. + return x + + +class IdentityFirstStage(torch.nn.Module): + def __init__(self, *args, vq_interface=False, **kwargs): + self.vq_interface = vq_interface + super().__init__() + + def encode(self, x, *args, **kwargs): + return x + + def decode(self, x, *args, **kwargs): + return x + + def quantize(self, x, *args, **kwargs): + if self.vq_interface: + return x, None, [None, None, None] + return x + + def forward(self, x, *args, **kwargs): + return x + diff --git a/repositories/ldm/models/diffusion/__init__.py b/repositories/ldm/models/diffusion/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/ldm/models/diffusion/ddim.py b/repositories/ldm/models/diffusion/ddim.py new file mode 100644 index 000000000..c6cfd5712 --- /dev/null +++ b/repositories/ldm/models/diffusion/ddim.py @@ -0,0 +1,337 @@ +"""SAMPLING ONLY.""" + +import torch +import numpy as np +from tqdm import tqdm + +from ldm.modules.diffusionmodules.util import make_ddim_sampling_parameters, make_ddim_timesteps, noise_like, extract_into_tensor + + +class DDIMSampler(object): + def __init__(self, model, schedule="linear", device=torch.device("cuda"), **kwargs): + super().__init__() + self.model = model + self.ddpm_num_timesteps = model.num_timesteps + self.schedule = schedule + self.device = device + + def register_buffer(self, name, attr): + if type(attr) == torch.Tensor: + if attr.device != self.device: + attr = attr.to(self.device) + setattr(self, name, attr) + + def make_schedule(self, ddim_num_steps, ddim_discretize="uniform", ddim_eta=0., verbose=True): + self.ddim_timesteps = make_ddim_timesteps(ddim_discr_method=ddim_discretize, num_ddim_timesteps=ddim_num_steps, + num_ddpm_timesteps=self.ddpm_num_timesteps,verbose=verbose) + alphas_cumprod = self.model.alphas_cumprod + assert alphas_cumprod.shape[0] == self.ddpm_num_timesteps, 'alphas have to be defined for each timestep' + to_torch = lambda x: x.clone().detach().to(torch.float32).to(self.model.device) + + self.register_buffer('betas', to_torch(self.model.betas)) + self.register_buffer('alphas_cumprod', to_torch(alphas_cumprod)) + self.register_buffer('alphas_cumprod_prev', to_torch(self.model.alphas_cumprod_prev)) + + # calculations for diffusion q(x_t | x_{t-1}) and others + self.register_buffer('sqrt_alphas_cumprod', to_torch(np.sqrt(alphas_cumprod.cpu()))) + self.register_buffer('sqrt_one_minus_alphas_cumprod', to_torch(np.sqrt(1. - alphas_cumprod.cpu()))) + self.register_buffer('log_one_minus_alphas_cumprod', to_torch(np.log(1. - alphas_cumprod.cpu()))) + self.register_buffer('sqrt_recip_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod.cpu()))) + self.register_buffer('sqrt_recipm1_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod.cpu() - 1))) + + # ddim sampling parameters + ddim_sigmas, ddim_alphas, ddim_alphas_prev = make_ddim_sampling_parameters(alphacums=alphas_cumprod.cpu(), + ddim_timesteps=self.ddim_timesteps, + eta=ddim_eta,verbose=verbose) + self.register_buffer('ddim_sigmas', ddim_sigmas) + self.register_buffer('ddim_alphas', ddim_alphas) + self.register_buffer('ddim_alphas_prev', ddim_alphas_prev) + self.register_buffer('ddim_sqrt_one_minus_alphas', np.sqrt(1. - ddim_alphas)) + sigmas_for_original_sampling_steps = ddim_eta * torch.sqrt( + (1 - self.alphas_cumprod_prev) / (1 - self.alphas_cumprod) * ( + 1 - self.alphas_cumprod / self.alphas_cumprod_prev)) + self.register_buffer('ddim_sigmas_for_original_num_steps', sigmas_for_original_sampling_steps) + + @torch.no_grad() + def sample(self, + S, + batch_size, + shape, + conditioning=None, + callback=None, + normals_sequence=None, + img_callback=None, + quantize_x0=False, + eta=0., + mask=None, + x0=None, + temperature=1., + noise_dropout=0., + score_corrector=None, + corrector_kwargs=None, + verbose=True, + x_T=None, + log_every_t=100, + unconditional_guidance_scale=1., + unconditional_conditioning=None, # this has to come in the same format as the conditioning, # e.g. as encoded tokens, ... + dynamic_threshold=None, + ucg_schedule=None, + **kwargs + ): + if conditioning is not None: + if isinstance(conditioning, dict): + ctmp = conditioning[list(conditioning.keys())[0]] + while isinstance(ctmp, list): ctmp = ctmp[0] + cbs = ctmp.shape[0] + if cbs != batch_size: + print(f"Warning: Got {cbs} conditionings but batch-size is {batch_size}") + + elif isinstance(conditioning, list): + for ctmp in conditioning: + if ctmp.shape[0] != batch_size: + print(f"Warning: Got {cbs} conditionings but batch-size is {batch_size}") + + else: + if conditioning.shape[0] != batch_size: + print(f"Warning: Got {conditioning.shape[0]} conditionings but batch-size is {batch_size}") + + self.make_schedule(ddim_num_steps=S, ddim_eta=eta, verbose=verbose) + # sampling + C, H, W = shape + size = (batch_size, C, H, W) + print(f'Data shape for DDIM sampling is {size}, eta {eta}') + + samples, intermediates = self.ddim_sampling(conditioning, size, + callback=callback, + img_callback=img_callback, + quantize_denoised=quantize_x0, + mask=mask, x0=x0, + ddim_use_original_steps=False, + noise_dropout=noise_dropout, + temperature=temperature, + score_corrector=score_corrector, + corrector_kwargs=corrector_kwargs, + x_T=x_T, + log_every_t=log_every_t, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=unconditional_conditioning, + dynamic_threshold=dynamic_threshold, + ucg_schedule=ucg_schedule + ) + return samples, intermediates + + @torch.no_grad() + def ddim_sampling(self, cond, shape, + x_T=None, ddim_use_original_steps=False, + callback=None, timesteps=None, quantize_denoised=False, + mask=None, x0=None, img_callback=None, log_every_t=100, + temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None, + unconditional_guidance_scale=1., unconditional_conditioning=None, dynamic_threshold=None, + ucg_schedule=None): + device = self.model.betas.device + b = shape[0] + if x_T is None: + img = torch.randn(shape, device=device) + else: + img = x_T + + if timesteps is None: + timesteps = self.ddpm_num_timesteps if ddim_use_original_steps else self.ddim_timesteps + elif timesteps is not None and not ddim_use_original_steps: + subset_end = int(min(timesteps / self.ddim_timesteps.shape[0], 1) * self.ddim_timesteps.shape[0]) - 1 + timesteps = self.ddim_timesteps[:subset_end] + + intermediates = {'x_inter': [img], 'pred_x0': [img]} + time_range = reversed(range(0,timesteps)) if ddim_use_original_steps else np.flip(timesteps) + total_steps = timesteps if ddim_use_original_steps else timesteps.shape[0] + print(f"Running DDIM Sampling with {total_steps} timesteps") + + iterator = tqdm(time_range, desc='DDIM Sampler', total=total_steps) + + for i, step in enumerate(iterator): + index = total_steps - i - 1 + ts = torch.full((b,), step, device=device, dtype=torch.long) + + if mask is not None: + assert x0 is not None + img_orig = self.model.q_sample(x0, ts) # TODO: deterministic forward pass? + img = img_orig * mask + (1. - mask) * img + + if ucg_schedule is not None: + assert len(ucg_schedule) == len(time_range) + unconditional_guidance_scale = ucg_schedule[i] + + outs = self.p_sample_ddim(img, cond, ts, index=index, use_original_steps=ddim_use_original_steps, + quantize_denoised=quantize_denoised, temperature=temperature, + noise_dropout=noise_dropout, score_corrector=score_corrector, + corrector_kwargs=corrector_kwargs, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=unconditional_conditioning, + dynamic_threshold=dynamic_threshold) + img, pred_x0 = outs + if callback: callback(i) + if img_callback: img_callback(pred_x0, i) + + if index % log_every_t == 0 or index == total_steps - 1: + intermediates['x_inter'].append(img) + intermediates['pred_x0'].append(pred_x0) + + return img, intermediates + + @torch.no_grad() + def p_sample_ddim(self, x, c, t, index, repeat_noise=False, use_original_steps=False, quantize_denoised=False, + temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None, + unconditional_guidance_scale=1., unconditional_conditioning=None, + dynamic_threshold=None): + b, *_, device = *x.shape, x.device + + if unconditional_conditioning is None or unconditional_guidance_scale == 1.: + model_output = self.model.apply_model(x, t, c) + else: + x_in = torch.cat([x] * 2) + t_in = torch.cat([t] * 2) + if isinstance(c, dict): + assert isinstance(unconditional_conditioning, dict) + c_in = dict() + for k in c: + if isinstance(c[k], list): + c_in[k] = [torch.cat([ + unconditional_conditioning[k][i], + c[k][i]]) for i in range(len(c[k]))] + else: + c_in[k] = torch.cat([ + unconditional_conditioning[k], + c[k]]) + elif isinstance(c, list): + c_in = list() + assert isinstance(unconditional_conditioning, list) + for i in range(len(c)): + c_in.append(torch.cat([unconditional_conditioning[i], c[i]])) + else: + c_in = torch.cat([unconditional_conditioning, c]) + model_uncond, model_t = self.model.apply_model(x_in, t_in, c_in).chunk(2) + model_output = model_uncond + unconditional_guidance_scale * (model_t - model_uncond) + + if self.model.parameterization == "v": + e_t = self.model.predict_eps_from_z_and_v(x, t, model_output) + else: + e_t = model_output + + if score_corrector is not None: + assert self.model.parameterization == "eps", 'not implemented' + e_t = score_corrector.modify_score(self.model, e_t, x, t, c, **corrector_kwargs) + + alphas = self.model.alphas_cumprod if use_original_steps else self.ddim_alphas + alphas_prev = self.model.alphas_cumprod_prev if use_original_steps else self.ddim_alphas_prev + sqrt_one_minus_alphas = self.model.sqrt_one_minus_alphas_cumprod if use_original_steps else self.ddim_sqrt_one_minus_alphas + sigmas = self.model.ddim_sigmas_for_original_num_steps if use_original_steps else self.ddim_sigmas + # select parameters corresponding to the currently considered timestep + a_t = torch.full((b, 1, 1, 1), alphas[index], device=device) + a_prev = torch.full((b, 1, 1, 1), alphas_prev[index], device=device) + sigma_t = torch.full((b, 1, 1, 1), sigmas[index], device=device) + sqrt_one_minus_at = torch.full((b, 1, 1, 1), sqrt_one_minus_alphas[index],device=device) + + # current prediction for x_0 + if self.model.parameterization != "v": + pred_x0 = (x - sqrt_one_minus_at * e_t) / a_t.sqrt() + else: + pred_x0 = self.model.predict_start_from_z_and_v(x, t, model_output) + + if quantize_denoised: + pred_x0, _, *_ = self.model.first_stage_model.quantize(pred_x0) + + if dynamic_threshold is not None: + raise NotImplementedError() + + # direction pointing to x_t + dir_xt = (1. - a_prev - sigma_t**2).sqrt() * e_t + noise = sigma_t * noise_like(x.shape, device, repeat_noise) * temperature + if noise_dropout > 0.: + noise = torch.nn.functional.dropout(noise, p=noise_dropout) + x_prev = a_prev.sqrt() * pred_x0 + dir_xt + noise + return x_prev, pred_x0 + + @torch.no_grad() + def encode(self, x0, c, t_enc, use_original_steps=False, return_intermediates=None, + unconditional_guidance_scale=1.0, unconditional_conditioning=None, callback=None): + num_reference_steps = self.ddpm_num_timesteps if use_original_steps else self.ddim_timesteps.shape[0] + + assert t_enc <= num_reference_steps + num_steps = t_enc + + if use_original_steps: + alphas_next = self.alphas_cumprod[:num_steps] + alphas = self.alphas_cumprod_prev[:num_steps] + else: + alphas_next = self.ddim_alphas[:num_steps] + alphas = torch.tensor(self.ddim_alphas_prev[:num_steps]) + + x_next = x0 + intermediates = [] + inter_steps = [] + for i in tqdm(range(num_steps), desc='Encoding Image'): + t = torch.full((x0.shape[0],), i, device=self.model.device, dtype=torch.long) + if unconditional_guidance_scale == 1.: + noise_pred = self.model.apply_model(x_next, t, c) + else: + assert unconditional_conditioning is not None + e_t_uncond, noise_pred = torch.chunk( + self.model.apply_model(torch.cat((x_next, x_next)), torch.cat((t, t)), + torch.cat((unconditional_conditioning, c))), 2) + noise_pred = e_t_uncond + unconditional_guidance_scale * (noise_pred - e_t_uncond) + + xt_weighted = (alphas_next[i] / alphas[i]).sqrt() * x_next + weighted_noise_pred = alphas_next[i].sqrt() * ( + (1 / alphas_next[i] - 1).sqrt() - (1 / alphas[i] - 1).sqrt()) * noise_pred + x_next = xt_weighted + weighted_noise_pred + if return_intermediates and i % ( + num_steps // return_intermediates) == 0 and i < num_steps - 1: + intermediates.append(x_next) + inter_steps.append(i) + elif return_intermediates and i >= num_steps - 2: + intermediates.append(x_next) + inter_steps.append(i) + if callback: callback(i) + + out = {'x_encoded': x_next, 'intermediate_steps': inter_steps} + if return_intermediates: + out.update({'intermediates': intermediates}) + return x_next, out + + @torch.no_grad() + def stochastic_encode(self, x0, t, use_original_steps=False, noise=None): + # fast, but does not allow for exact reconstruction + # t serves as an index to gather the correct alphas + if use_original_steps: + sqrt_alphas_cumprod = self.sqrt_alphas_cumprod + sqrt_one_minus_alphas_cumprod = self.sqrt_one_minus_alphas_cumprod + else: + sqrt_alphas_cumprod = torch.sqrt(self.ddim_alphas) + sqrt_one_minus_alphas_cumprod = self.ddim_sqrt_one_minus_alphas + + if noise is None: + noise = torch.randn_like(x0) + return (extract_into_tensor(sqrt_alphas_cumprod, t, x0.shape) * x0 + + extract_into_tensor(sqrt_one_minus_alphas_cumprod, t, x0.shape) * noise) + + @torch.no_grad() + def decode(self, x_latent, cond, t_start, unconditional_guidance_scale=1.0, unconditional_conditioning=None, + use_original_steps=False, callback=None): + + timesteps = np.arange(self.ddpm_num_timesteps) if use_original_steps else self.ddim_timesteps + timesteps = timesteps[:t_start] + + time_range = np.flip(timesteps) + total_steps = timesteps.shape[0] + print(f"Running DDIM Sampling with {total_steps} timesteps") + + iterator = tqdm(time_range, desc='Decoding image', total=total_steps) + x_dec = x_latent + for i, step in enumerate(iterator): + index = total_steps - i - 1 + ts = torch.full((x_latent.shape[0],), step, device=x_latent.device, dtype=torch.long) + x_dec, _ = self.p_sample_ddim(x_dec, cond, ts, index=index, use_original_steps=use_original_steps, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=unconditional_conditioning) + if callback: callback(i) + return x_dec \ No newline at end of file diff --git a/repositories/ldm/models/diffusion/ddpm.py b/repositories/ldm/models/diffusion/ddpm.py new file mode 100644 index 000000000..3350c032f --- /dev/null +++ b/repositories/ldm/models/diffusion/ddpm.py @@ -0,0 +1,1873 @@ +""" +wild mixture of +https://github.com/lucidrains/denoising-diffusion-pytorch/blob/7706bdfc6f527f58d33f84b7b522e61e6e3164b3/denoising_diffusion_pytorch/denoising_diffusion_pytorch.py +https://github.com/openai/improved-diffusion/blob/e94489283bb876ac1477d5dd7709bbbd2d9902ce/improved_diffusion/gaussian_diffusion.py +https://github.com/CompVis/taming-transformers +-- merci +""" + +import torch +import torch.nn as nn +import numpy as np +import pytorch_lightning as pl +from torch.optim.lr_scheduler import LambdaLR +from einops import rearrange, repeat +from contextlib import contextmanager, nullcontext +from functools import partial +import itertools +from tqdm import tqdm +from torchvision.utils import make_grid +from pytorch_lightning.utilities.distributed import rank_zero_only +from omegaconf import ListConfig + +from ldm.util import log_txt_as_img, exists, default, ismap, isimage, mean_flat, count_params, instantiate_from_config +from ldm.modules.ema import LitEma +from ldm.modules.distributions.distributions import normal_kl, DiagonalGaussianDistribution +from ldm.models.autoencoder import IdentityFirstStage, AutoencoderKL +from ldm.modules.diffusionmodules.util import make_beta_schedule, extract_into_tensor, noise_like +from ldm.models.diffusion.ddim import DDIMSampler + + +__conditioning_keys__ = {'concat': 'c_concat', + 'crossattn': 'c_crossattn', + 'adm': 'y'} + + +def disabled_train(self, mode=True): + """Overwrite model.train with this function to make sure train/eval mode + does not change anymore.""" + return self + + +def uniform_on_device(r1, r2, shape, device): + return (r1 - r2) * torch.rand(*shape, device=device) + r2 + + +class DDPM(pl.LightningModule): + # classic DDPM with Gaussian diffusion, in image space + def __init__(self, + unet_config, + timesteps=1000, + beta_schedule="linear", + loss_type="l2", + ckpt_path=None, + ignore_keys=[], + load_only_unet=False, + monitor="val/loss", + use_ema=True, + first_stage_key="image", + image_size=256, + channels=3, + log_every_t=100, + clip_denoised=True, + linear_start=1e-4, + linear_end=2e-2, + cosine_s=8e-3, + given_betas=None, + original_elbo_weight=0., + v_posterior=0., # weight for choosing posterior variance as sigma = (1-v) * beta_tilde + v * beta + l_simple_weight=1., + conditioning_key=None, + parameterization="eps", # all assuming fixed variance schedules + scheduler_config=None, + use_positional_encodings=False, + learn_logvar=False, + logvar_init=0., + make_it_fit=False, + ucg_training=None, + reset_ema=False, + reset_num_ema_updates=False, + ): + super().__init__() + assert parameterization in ["eps", "x0", "v"], 'currently only supporting "eps" and "x0" and "v"' + self.parameterization = parameterization + print(f"{self.__class__.__name__}: Running in {self.parameterization}-prediction mode") + self.cond_stage_model = None + self.clip_denoised = clip_denoised + self.log_every_t = log_every_t + self.first_stage_key = first_stage_key + self.image_size = image_size # try conv? + self.channels = channels + self.use_positional_encodings = use_positional_encodings + self.model = DiffusionWrapper(unet_config, conditioning_key) + count_params(self.model, verbose=True) + self.use_ema = use_ema + if self.use_ema: + self.model_ema = LitEma(self.model) + print(f"Keeping EMAs of {len(list(self.model_ema.buffers()))}.") + + self.use_scheduler = scheduler_config is not None + if self.use_scheduler: + self.scheduler_config = scheduler_config + + self.v_posterior = v_posterior + self.original_elbo_weight = original_elbo_weight + self.l_simple_weight = l_simple_weight + + if monitor is not None: + self.monitor = monitor + self.make_it_fit = make_it_fit + if reset_ema: assert exists(ckpt_path) + if ckpt_path is not None: + self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys, only_model=load_only_unet) + if reset_ema: + assert self.use_ema + print(f"Resetting ema to pure model weights. This is useful when restoring from an ema-only checkpoint.") + self.model_ema = LitEma(self.model) + if reset_num_ema_updates: + print(" +++++++++++ WARNING: RESETTING NUM_EMA UPDATES TO ZERO +++++++++++ ") + assert self.use_ema + self.model_ema.reset_num_updates() + + self.register_schedule(given_betas=given_betas, beta_schedule=beta_schedule, timesteps=timesteps, + linear_start=linear_start, linear_end=linear_end, cosine_s=cosine_s) + + self.loss_type = loss_type + + self.learn_logvar = learn_logvar + self.logvar = torch.full(fill_value=logvar_init, size=(self.num_timesteps,)) + if self.learn_logvar: + self.logvar = nn.Parameter(self.logvar, requires_grad=True) + + self.ucg_training = ucg_training or dict() + if self.ucg_training: + self.ucg_prng = np.random.RandomState() + + def register_schedule(self, given_betas=None, beta_schedule="linear", timesteps=1000, + linear_start=1e-4, linear_end=2e-2, cosine_s=8e-3): + if exists(given_betas): + betas = given_betas + else: + betas = make_beta_schedule(beta_schedule, timesteps, linear_start=linear_start, linear_end=linear_end, + cosine_s=cosine_s) + alphas = 1. - betas + alphas_cumprod = np.cumprod(alphas, axis=0) + alphas_cumprod_prev = np.append(1., alphas_cumprod[:-1]) + + timesteps, = betas.shape + self.num_timesteps = int(timesteps) + self.linear_start = linear_start + self.linear_end = linear_end + assert alphas_cumprod.shape[0] == self.num_timesteps, 'alphas have to be defined for each timestep' + + to_torch = partial(torch.tensor, dtype=torch.float32) + + self.register_buffer('betas', to_torch(betas)) + self.register_buffer('alphas_cumprod', to_torch(alphas_cumprod)) + self.register_buffer('alphas_cumprod_prev', to_torch(alphas_cumprod_prev)) + + # calculations for diffusion q(x_t | x_{t-1}) and others + self.register_buffer('sqrt_alphas_cumprod', to_torch(np.sqrt(alphas_cumprod))) + self.register_buffer('sqrt_one_minus_alphas_cumprod', to_torch(np.sqrt(1. - alphas_cumprod))) + self.register_buffer('log_one_minus_alphas_cumprod', to_torch(np.log(1. - alphas_cumprod))) + self.register_buffer('sqrt_recip_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod))) + self.register_buffer('sqrt_recipm1_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod - 1))) + + # calculations for posterior q(x_{t-1} | x_t, x_0) + posterior_variance = (1 - self.v_posterior) * betas * (1. - alphas_cumprod_prev) / ( + 1. - alphas_cumprod) + self.v_posterior * betas + # above: equal to 1. / (1. / (1. - alpha_cumprod_tm1) + alpha_t / beta_t) + self.register_buffer('posterior_variance', to_torch(posterior_variance)) + # below: log calculation clipped because the posterior variance is 0 at the beginning of the diffusion chain + self.register_buffer('posterior_log_variance_clipped', to_torch(np.log(np.maximum(posterior_variance, 1e-20)))) + self.register_buffer('posterior_mean_coef1', to_torch( + betas * np.sqrt(alphas_cumprod_prev) / (1. - alphas_cumprod))) + self.register_buffer('posterior_mean_coef2', to_torch( + (1. - alphas_cumprod_prev) * np.sqrt(alphas) / (1. - alphas_cumprod))) + + if self.parameterization == "eps": + lvlb_weights = self.betas ** 2 / ( + 2 * self.posterior_variance * to_torch(alphas) * (1 - self.alphas_cumprod)) + elif self.parameterization == "x0": + lvlb_weights = 0.5 * np.sqrt(torch.Tensor(alphas_cumprod)) / (2. * 1 - torch.Tensor(alphas_cumprod)) + elif self.parameterization == "v": + lvlb_weights = torch.ones_like(self.betas ** 2 / ( + 2 * self.posterior_variance * to_torch(alphas) * (1 - self.alphas_cumprod))) + else: + raise NotImplementedError("mu not supported") + lvlb_weights[0] = lvlb_weights[1] + self.register_buffer('lvlb_weights', lvlb_weights, persistent=False) + assert not torch.isnan(self.lvlb_weights).all() + + @contextmanager + def ema_scope(self, context=None): + if self.use_ema: + self.model_ema.store(self.model.parameters()) + self.model_ema.copy_to(self.model) + if context is not None: + print(f"{context}: Switched to EMA weights") + try: + yield None + finally: + if self.use_ema: + self.model_ema.restore(self.model.parameters()) + if context is not None: + print(f"{context}: Restored training weights") + + @torch.no_grad() + def init_from_ckpt(self, path, ignore_keys=list(), only_model=False): + sd = torch.load(path, map_location="cpu") + if "state_dict" in list(sd.keys()): + sd = sd["state_dict"] + keys = list(sd.keys()) + for k in keys: + for ik in ignore_keys: + if k.startswith(ik): + print("Deleting key {} from state_dict.".format(k)) + del sd[k] + if self.make_it_fit: + n_params = len([name for name, _ in + itertools.chain(self.named_parameters(), + self.named_buffers())]) + for name, param in tqdm( + itertools.chain(self.named_parameters(), + self.named_buffers()), + desc="Fitting old weights to new weights", + total=n_params + ): + if not name in sd: + continue + old_shape = sd[name].shape + new_shape = param.shape + assert len(old_shape) == len(new_shape) + if len(new_shape) > 2: + # we only modify first two axes + assert new_shape[2:] == old_shape[2:] + # assumes first axis corresponds to output dim + if not new_shape == old_shape: + new_param = param.clone() + old_param = sd[name] + if len(new_shape) == 1: + for i in range(new_param.shape[0]): + new_param[i] = old_param[i % old_shape[0]] + elif len(new_shape) >= 2: + for i in range(new_param.shape[0]): + for j in range(new_param.shape[1]): + new_param[i, j] = old_param[i % old_shape[0], j % old_shape[1]] + + n_used_old = torch.ones(old_shape[1]) + for j in range(new_param.shape[1]): + n_used_old[j % old_shape[1]] += 1 + n_used_new = torch.zeros(new_shape[1]) + for j in range(new_param.shape[1]): + n_used_new[j] = n_used_old[j % old_shape[1]] + + n_used_new = n_used_new[None, :] + while len(n_used_new.shape) < len(new_shape): + n_used_new = n_used_new.unsqueeze(-1) + new_param /= n_used_new + + sd[name] = new_param + + missing, unexpected = self.load_state_dict(sd, strict=False) if not only_model else self.model.load_state_dict( + sd, strict=False) + print(f"Restored from {path} with {len(missing)} missing and {len(unexpected)} unexpected keys") + if len(missing) > 0: + print(f"Missing Keys:\n {missing}") + if len(unexpected) > 0: + print(f"\nUnexpected Keys:\n {unexpected}") + + def q_mean_variance(self, x_start, t): + """ + Get the distribution q(x_t | x_0). + :param x_start: the [N x C x ...] tensor of noiseless inputs. + :param t: the number of diffusion steps (minus 1). Here, 0 means one step. + :return: A tuple (mean, variance, log_variance), all of x_start's shape. + """ + mean = (extract_into_tensor(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start) + variance = extract_into_tensor(1.0 - self.alphas_cumprod, t, x_start.shape) + log_variance = extract_into_tensor(self.log_one_minus_alphas_cumprod, t, x_start.shape) + return mean, variance, log_variance + + def predict_start_from_noise(self, x_t, t, noise): + return ( + extract_into_tensor(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t - + extract_into_tensor(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape) * noise + ) + + def predict_start_from_z_and_v(self, x_t, t, v): + # self.register_buffer('sqrt_alphas_cumprod', to_torch(np.sqrt(alphas_cumprod))) + # self.register_buffer('sqrt_one_minus_alphas_cumprod', to_torch(np.sqrt(1. - alphas_cumprod))) + return ( + extract_into_tensor(self.sqrt_alphas_cumprod, t, x_t.shape) * x_t - + extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x_t.shape) * v + ) + + def predict_eps_from_z_and_v(self, x_t, t, v): + return ( + extract_into_tensor(self.sqrt_alphas_cumprod, t, x_t.shape) * v + + extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x_t.shape) * x_t + ) + + def q_posterior(self, x_start, x_t, t): + posterior_mean = ( + extract_into_tensor(self.posterior_mean_coef1, t, x_t.shape) * x_start + + extract_into_tensor(self.posterior_mean_coef2, t, x_t.shape) * x_t + ) + posterior_variance = extract_into_tensor(self.posterior_variance, t, x_t.shape) + posterior_log_variance_clipped = extract_into_tensor(self.posterior_log_variance_clipped, t, x_t.shape) + return posterior_mean, posterior_variance, posterior_log_variance_clipped + + def p_mean_variance(self, x, t, clip_denoised: bool): + model_out = self.model(x, t) + if self.parameterization == "eps": + x_recon = self.predict_start_from_noise(x, t=t, noise=model_out) + elif self.parameterization == "x0": + x_recon = model_out + if clip_denoised: + x_recon.clamp_(-1., 1.) + + model_mean, posterior_variance, posterior_log_variance = self.q_posterior(x_start=x_recon, x_t=x, t=t) + return model_mean, posterior_variance, posterior_log_variance + + @torch.no_grad() + def p_sample(self, x, t, clip_denoised=True, repeat_noise=False): + b, *_, device = *x.shape, x.device + model_mean, _, model_log_variance = self.p_mean_variance(x=x, t=t, clip_denoised=clip_denoised) + noise = noise_like(x.shape, device, repeat_noise) + # no noise when t == 0 + nonzero_mask = (1 - (t == 0).float()).reshape(b, *((1,) * (len(x.shape) - 1))) + return model_mean + nonzero_mask * (0.5 * model_log_variance).exp() * noise + + @torch.no_grad() + def p_sample_loop(self, shape, return_intermediates=False): + device = self.betas.device + b = shape[0] + img = torch.randn(shape, device=device) + intermediates = [img] + for i in tqdm(reversed(range(0, self.num_timesteps)), desc='Sampling t', total=self.num_timesteps): + img = self.p_sample(img, torch.full((b,), i, device=device, dtype=torch.long), + clip_denoised=self.clip_denoised) + if i % self.log_every_t == 0 or i == self.num_timesteps - 1: + intermediates.append(img) + if return_intermediates: + return img, intermediates + return img + + @torch.no_grad() + def sample(self, batch_size=16, return_intermediates=False): + image_size = self.image_size + channels = self.channels + return self.p_sample_loop((batch_size, channels, image_size, image_size), + return_intermediates=return_intermediates) + + def q_sample(self, x_start, t, noise=None): + noise = default(noise, lambda: torch.randn_like(x_start)) + return (extract_into_tensor(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start + + extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x_start.shape) * noise) + + def get_v(self, x, noise, t): + return ( + extract_into_tensor(self.sqrt_alphas_cumprod, t, x.shape) * noise - + extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x.shape) * x + ) + + def get_loss(self, pred, target, mean=True): + if self.loss_type == 'l1': + loss = (target - pred).abs() + if mean: + loss = loss.mean() + elif self.loss_type == 'l2': + if mean: + loss = torch.nn.functional.mse_loss(target, pred) + else: + loss = torch.nn.functional.mse_loss(target, pred, reduction='none') + else: + raise NotImplementedError("unknown loss type '{loss_type}'") + + return loss + + def p_losses(self, x_start, t, noise=None): + noise = default(noise, lambda: torch.randn_like(x_start)) + x_noisy = self.q_sample(x_start=x_start, t=t, noise=noise) + model_out = self.model(x_noisy, t) + + loss_dict = {} + if self.parameterization == "eps": + target = noise + elif self.parameterization == "x0": + target = x_start + elif self.parameterization == "v": + target = self.get_v(x_start, noise, t) + else: + raise NotImplementedError(f"Parameterization {self.parameterization} not yet supported") + + loss = self.get_loss(model_out, target, mean=False).mean(dim=[1, 2, 3]) + + log_prefix = 'train' if self.training else 'val' + + loss_dict.update({f'{log_prefix}/loss_simple': loss.mean()}) + loss_simple = loss.mean() * self.l_simple_weight + + loss_vlb = (self.lvlb_weights[t] * loss).mean() + loss_dict.update({f'{log_prefix}/loss_vlb': loss_vlb}) + + loss = loss_simple + self.original_elbo_weight * loss_vlb + + loss_dict.update({f'{log_prefix}/loss': loss}) + + return loss, loss_dict + + def forward(self, x, *args, **kwargs): + # b, c, h, w, device, img_size, = *x.shape, x.device, self.image_size + # assert h == img_size and w == img_size, f'height and width of image must be {img_size}' + t = torch.randint(0, self.num_timesteps, (x.shape[0],), device=self.device).long() + return self.p_losses(x, t, *args, **kwargs) + + def get_input(self, batch, k): + x = batch[k] + if len(x.shape) == 3: + x = x[..., None] + x = rearrange(x, 'b h w c -> b c h w') + x = x.to(memory_format=torch.contiguous_format).float() + return x + + def shared_step(self, batch): + x = self.get_input(batch, self.first_stage_key) + loss, loss_dict = self(x) + return loss, loss_dict + + def training_step(self, batch, batch_idx): + for k in self.ucg_training: + p = self.ucg_training[k]["p"] + val = self.ucg_training[k]["val"] + if val is None: + val = "" + for i in range(len(batch[k])): + if self.ucg_prng.choice(2, p=[1 - p, p]): + batch[k][i] = val + + loss, loss_dict = self.shared_step(batch) + + self.log_dict(loss_dict, prog_bar=True, + logger=True, on_step=True, on_epoch=True) + + self.log("global_step", self.global_step, + prog_bar=True, logger=True, on_step=True, on_epoch=False) + + if self.use_scheduler: + lr = self.optimizers().param_groups[0]['lr'] + self.log('lr_abs', lr, prog_bar=True, logger=True, on_step=True, on_epoch=False) + + return loss + + @torch.no_grad() + def validation_step(self, batch, batch_idx): + _, loss_dict_no_ema = self.shared_step(batch) + with self.ema_scope(): + _, loss_dict_ema = self.shared_step(batch) + loss_dict_ema = {key + '_ema': loss_dict_ema[key] for key in loss_dict_ema} + self.log_dict(loss_dict_no_ema, prog_bar=False, logger=True, on_step=False, on_epoch=True) + self.log_dict(loss_dict_ema, prog_bar=False, logger=True, on_step=False, on_epoch=True) + + def on_train_batch_end(self, *args, **kwargs): + if self.use_ema: + self.model_ema(self.model) + + def _get_rows_from_list(self, samples): + n_imgs_per_row = len(samples) + denoise_grid = rearrange(samples, 'n b c h w -> b n c h w') + denoise_grid = rearrange(denoise_grid, 'b n c h w -> (b n) c h w') + denoise_grid = make_grid(denoise_grid, nrow=n_imgs_per_row) + return denoise_grid + + @torch.no_grad() + def log_images(self, batch, N=8, n_row=2, sample=True, return_keys=None, **kwargs): + log = dict() + x = self.get_input(batch, self.first_stage_key) + N = min(x.shape[0], N) + n_row = min(x.shape[0], n_row) + x = x.to(self.device)[:N] + log["inputs"] = x + + # get diffusion row + diffusion_row = list() + x_start = x[:n_row] + + for t in range(self.num_timesteps): + if t % self.log_every_t == 0 or t == self.num_timesteps - 1: + t = repeat(torch.tensor([t]), '1 -> b', b=n_row) + t = t.to(self.device).long() + noise = torch.randn_like(x_start) + x_noisy = self.q_sample(x_start=x_start, t=t, noise=noise) + diffusion_row.append(x_noisy) + + log["diffusion_row"] = self._get_rows_from_list(diffusion_row) + + if sample: + # get denoise row + with self.ema_scope("Plotting"): + samples, denoise_row = self.sample(batch_size=N, return_intermediates=True) + + log["samples"] = samples + log["denoise_row"] = self._get_rows_from_list(denoise_row) + + if return_keys: + if np.intersect1d(list(log.keys()), return_keys).shape[0] == 0: + return log + else: + return {key: log[key] for key in return_keys} + return log + + def configure_optimizers(self): + lr = self.learning_rate + params = list(self.model.parameters()) + if self.learn_logvar: + params = params + [self.logvar] + opt = torch.optim.AdamW(params, lr=lr) + return opt + + +class LatentDiffusion(DDPM): + """main class""" + + def __init__(self, + first_stage_config, + cond_stage_config, + num_timesteps_cond=None, + cond_stage_key="image", + cond_stage_trainable=False, + concat_mode=True, + cond_stage_forward=None, + conditioning_key=None, + scale_factor=1.0, + scale_by_std=False, + force_null_conditioning=False, + *args, **kwargs): + self.force_null_conditioning = force_null_conditioning + self.num_timesteps_cond = default(num_timesteps_cond, 1) + self.scale_by_std = scale_by_std + assert self.num_timesteps_cond <= kwargs['timesteps'] + # for backwards compatibility after implementation of DiffusionWrapper + if conditioning_key is None: + conditioning_key = 'concat' if concat_mode else 'crossattn' + if cond_stage_config == '__is_unconditional__' and not self.force_null_conditioning: + conditioning_key = None + ckpt_path = kwargs.pop("ckpt_path", None) + reset_ema = kwargs.pop("reset_ema", False) + reset_num_ema_updates = kwargs.pop("reset_num_ema_updates", False) + ignore_keys = kwargs.pop("ignore_keys", []) + super().__init__(conditioning_key=conditioning_key, *args, **kwargs) + self.concat_mode = concat_mode + self.cond_stage_trainable = cond_stage_trainable + self.cond_stage_key = cond_stage_key + try: + self.num_downs = len(first_stage_config.params.ddconfig.ch_mult) - 1 + except: + self.num_downs = 0 + if not scale_by_std: + self.scale_factor = scale_factor + else: + self.register_buffer('scale_factor', torch.tensor(scale_factor)) + self.instantiate_first_stage(first_stage_config) + self.instantiate_cond_stage(cond_stage_config) + self.cond_stage_forward = cond_stage_forward + self.clip_denoised = False + self.bbox_tokenizer = None + + self.restarted_from_ckpt = False + if ckpt_path is not None: + self.init_from_ckpt(ckpt_path, ignore_keys) + self.restarted_from_ckpt = True + if reset_ema: + assert self.use_ema + print( + f"Resetting ema to pure model weights. This is useful when restoring from an ema-only checkpoint.") + self.model_ema = LitEma(self.model) + if reset_num_ema_updates: + print(" +++++++++++ WARNING: RESETTING NUM_EMA UPDATES TO ZERO +++++++++++ ") + assert self.use_ema + self.model_ema.reset_num_updates() + + def make_cond_schedule(self, ): + self.cond_ids = torch.full(size=(self.num_timesteps,), fill_value=self.num_timesteps - 1, dtype=torch.long) + ids = torch.round(torch.linspace(0, self.num_timesteps - 1, self.num_timesteps_cond)).long() + self.cond_ids[:self.num_timesteps_cond] = ids + + @rank_zero_only + @torch.no_grad() + def on_train_batch_start(self, batch, batch_idx, dataloader_idx): + # only for very first batch + if self.scale_by_std and self.current_epoch == 0 and self.global_step == 0 and batch_idx == 0 and not self.restarted_from_ckpt: + assert self.scale_factor == 1., 'rather not use custom rescaling and std-rescaling simultaneously' + # set rescale weight to 1./std of encodings + print("### USING STD-RESCALING ###") + x = super().get_input(batch, self.first_stage_key) + x = x.to(self.device) + encoder_posterior = self.encode_first_stage(x) + z = self.get_first_stage_encoding(encoder_posterior).detach() + del self.scale_factor + self.register_buffer('scale_factor', 1. / z.flatten().std()) + print(f"setting self.scale_factor to {self.scale_factor}") + print("### USING STD-RESCALING ###") + + def register_schedule(self, + given_betas=None, beta_schedule="linear", timesteps=1000, + linear_start=1e-4, linear_end=2e-2, cosine_s=8e-3): + super().register_schedule(given_betas, beta_schedule, timesteps, linear_start, linear_end, cosine_s) + + self.shorten_cond_schedule = self.num_timesteps_cond > 1 + if self.shorten_cond_schedule: + self.make_cond_schedule() + + def instantiate_first_stage(self, config): + model = instantiate_from_config(config) + self.first_stage_model = model.eval() + self.first_stage_model.train = disabled_train + for param in self.first_stage_model.parameters(): + param.requires_grad = False + + def instantiate_cond_stage(self, config): + if not self.cond_stage_trainable: + if config == "__is_first_stage__": + print("Using first stage also as cond stage.") + self.cond_stage_model = self.first_stage_model + elif config == "__is_unconditional__": + print(f"Training {self.__class__.__name__} as an unconditional model.") + self.cond_stage_model = None + # self.be_unconditional = True + else: + model = instantiate_from_config(config) + self.cond_stage_model = model.eval() + self.cond_stage_model.train = disabled_train + for param in self.cond_stage_model.parameters(): + param.requires_grad = False + else: + assert config != '__is_first_stage__' + assert config != '__is_unconditional__' + model = instantiate_from_config(config) + self.cond_stage_model = model + + def _get_denoise_row_from_list(self, samples, desc='', force_no_decoder_quantization=False): + denoise_row = [] + for zd in tqdm(samples, desc=desc): + denoise_row.append(self.decode_first_stage(zd.to(self.device), + force_not_quantize=force_no_decoder_quantization)) + n_imgs_per_row = len(denoise_row) + denoise_row = torch.stack(denoise_row) # n_log_step, n_row, C, H, W + denoise_grid = rearrange(denoise_row, 'n b c h w -> b n c h w') + denoise_grid = rearrange(denoise_grid, 'b n c h w -> (b n) c h w') + denoise_grid = make_grid(denoise_grid, nrow=n_imgs_per_row) + return denoise_grid + + def get_first_stage_encoding(self, encoder_posterior): + if isinstance(encoder_posterior, DiagonalGaussianDistribution): + z = encoder_posterior.sample() + elif isinstance(encoder_posterior, torch.Tensor): + z = encoder_posterior + else: + raise NotImplementedError(f"encoder_posterior of type '{type(encoder_posterior)}' not yet implemented") + return self.scale_factor * z + + def get_learned_conditioning(self, c): + if self.cond_stage_forward is None: + if hasattr(self.cond_stage_model, 'encode') and callable(self.cond_stage_model.encode): + c = self.cond_stage_model.encode(c) + if isinstance(c, DiagonalGaussianDistribution): + c = c.mode() + else: + c = self.cond_stage_model(c) + else: + assert hasattr(self.cond_stage_model, self.cond_stage_forward) + c = getattr(self.cond_stage_model, self.cond_stage_forward)(c) + return c + + def meshgrid(self, h, w): + y = torch.arange(0, h).view(h, 1, 1).repeat(1, w, 1) + x = torch.arange(0, w).view(1, w, 1).repeat(h, 1, 1) + + arr = torch.cat([y, x], dim=-1) + return arr + + def delta_border(self, h, w): + """ + :param h: height + :param w: width + :return: normalized distance to image border, + wtith min distance = 0 at border and max dist = 0.5 at image center + """ + lower_right_corner = torch.tensor([h - 1, w - 1]).view(1, 1, 2) + arr = self.meshgrid(h, w) / lower_right_corner + dist_left_up = torch.min(arr, dim=-1, keepdims=True)[0] + dist_right_down = torch.min(1 - arr, dim=-1, keepdims=True)[0] + edge_dist = torch.min(torch.cat([dist_left_up, dist_right_down], dim=-1), dim=-1)[0] + return edge_dist + + def get_weighting(self, h, w, Ly, Lx, device): + weighting = self.delta_border(h, w) + weighting = torch.clip(weighting, self.split_input_params["clip_min_weight"], + self.split_input_params["clip_max_weight"], ) + weighting = weighting.view(1, h * w, 1).repeat(1, 1, Ly * Lx).to(device) + + if self.split_input_params["tie_braker"]: + L_weighting = self.delta_border(Ly, Lx) + L_weighting = torch.clip(L_weighting, + self.split_input_params["clip_min_tie_weight"], + self.split_input_params["clip_max_tie_weight"]) + + L_weighting = L_weighting.view(1, 1, Ly * Lx).to(device) + weighting = weighting * L_weighting + return weighting + + def get_fold_unfold(self, x, kernel_size, stride, uf=1, df=1): # todo load once not every time, shorten code + """ + :param x: img of size (bs, c, h, w) + :return: n img crops of size (n, bs, c, kernel_size[0], kernel_size[1]) + """ + bs, nc, h, w = x.shape + + # number of crops in image + Ly = (h - kernel_size[0]) // stride[0] + 1 + Lx = (w - kernel_size[1]) // stride[1] + 1 + + if uf == 1 and df == 1: + fold_params = dict(kernel_size=kernel_size, dilation=1, padding=0, stride=stride) + unfold = torch.nn.Unfold(**fold_params) + + fold = torch.nn.Fold(output_size=x.shape[2:], **fold_params) + + weighting = self.get_weighting(kernel_size[0], kernel_size[1], Ly, Lx, x.device).to(x.dtype) + normalization = fold(weighting).view(1, 1, h, w) # normalizes the overlap + weighting = weighting.view((1, 1, kernel_size[0], kernel_size[1], Ly * Lx)) + + elif uf > 1 and df == 1: + fold_params = dict(kernel_size=kernel_size, dilation=1, padding=0, stride=stride) + unfold = torch.nn.Unfold(**fold_params) + + fold_params2 = dict(kernel_size=(kernel_size[0] * uf, kernel_size[0] * uf), + dilation=1, padding=0, + stride=(stride[0] * uf, stride[1] * uf)) + fold = torch.nn.Fold(output_size=(x.shape[2] * uf, x.shape[3] * uf), **fold_params2) + + weighting = self.get_weighting(kernel_size[0] * uf, kernel_size[1] * uf, Ly, Lx, x.device).to(x.dtype) + normalization = fold(weighting).view(1, 1, h * uf, w * uf) # normalizes the overlap + weighting = weighting.view((1, 1, kernel_size[0] * uf, kernel_size[1] * uf, Ly * Lx)) + + elif df > 1 and uf == 1: + fold_params = dict(kernel_size=kernel_size, dilation=1, padding=0, stride=stride) + unfold = torch.nn.Unfold(**fold_params) + + fold_params2 = dict(kernel_size=(kernel_size[0] // df, kernel_size[0] // df), + dilation=1, padding=0, + stride=(stride[0] // df, stride[1] // df)) + fold = torch.nn.Fold(output_size=(x.shape[2] // df, x.shape[3] // df), **fold_params2) + + weighting = self.get_weighting(kernel_size[0] // df, kernel_size[1] // df, Ly, Lx, x.device).to(x.dtype) + normalization = fold(weighting).view(1, 1, h // df, w // df) # normalizes the overlap + weighting = weighting.view((1, 1, kernel_size[0] // df, kernel_size[1] // df, Ly * Lx)) + + else: + raise NotImplementedError + + return fold, unfold, normalization, weighting + + @torch.no_grad() + def get_input(self, batch, k, return_first_stage_outputs=False, force_c_encode=False, + cond_key=None, return_original_cond=False, bs=None, return_x=False): + x = super().get_input(batch, k) + if bs is not None: + x = x[:bs] + x = x.to(self.device) + encoder_posterior = self.encode_first_stage(x) + z = self.get_first_stage_encoding(encoder_posterior).detach() + + if self.model.conditioning_key is not None and not self.force_null_conditioning: + if cond_key is None: + cond_key = self.cond_stage_key + if cond_key != self.first_stage_key: + if cond_key in ['caption', 'coordinates_bbox', "txt"]: + xc = batch[cond_key] + elif cond_key in ['class_label', 'cls']: + xc = batch + else: + xc = super().get_input(batch, cond_key).to(self.device) + else: + xc = x + if not self.cond_stage_trainable or force_c_encode: + if isinstance(xc, dict) or isinstance(xc, list): + c = self.get_learned_conditioning(xc) + else: + c = self.get_learned_conditioning(xc.to(self.device)) + else: + c = xc + if bs is not None: + c = c[:bs] + + if self.use_positional_encodings: + pos_x, pos_y = self.compute_latent_shifts(batch) + ckey = __conditioning_keys__[self.model.conditioning_key] + c = {ckey: c, 'pos_x': pos_x, 'pos_y': pos_y} + + else: + c = None + xc = None + if self.use_positional_encodings: + pos_x, pos_y = self.compute_latent_shifts(batch) + c = {'pos_x': pos_x, 'pos_y': pos_y} + out = [z, c] + if return_first_stage_outputs: + xrec = self.decode_first_stage(z) + out.extend([x, xrec]) + if return_x: + out.extend([x]) + if return_original_cond: + out.append(xc) + return out + + @torch.no_grad() + def decode_first_stage(self, z, predict_cids=False, force_not_quantize=False): + if predict_cids: + if z.dim() == 4: + z = torch.argmax(z.exp(), dim=1).long() + z = self.first_stage_model.quantize.get_codebook_entry(z, shape=None) + z = rearrange(z, 'b h w c -> b c h w').contiguous() + + z = 1. / self.scale_factor * z + return self.first_stage_model.decode(z) + + @torch.no_grad() + def encode_first_stage(self, x): + return self.first_stage_model.encode(x) + + def shared_step(self, batch, **kwargs): + x, c = self.get_input(batch, self.first_stage_key) + loss = self(x, c) + return loss + + def forward(self, x, c, *args, **kwargs): + t = torch.randint(0, self.num_timesteps, (x.shape[0],), device=self.device).long() + if self.model.conditioning_key is not None: + assert c is not None + if self.cond_stage_trainable: + c = self.get_learned_conditioning(c) + if self.shorten_cond_schedule: # TODO: drop this option + tc = self.cond_ids[t].to(self.device) + c = self.q_sample(x_start=c, t=tc, noise=torch.randn_like(c.float())) + return self.p_losses(x, c, t, *args, **kwargs) + + def apply_model(self, x_noisy, t, cond, return_ids=False): + if isinstance(cond, dict): + # hybrid case, cond is expected to be a dict + pass + else: + if not isinstance(cond, list): + cond = [cond] + key = 'c_concat' if self.model.conditioning_key == 'concat' else 'c_crossattn' + cond = {key: cond} + + x_recon = self.model(x_noisy, t, **cond) + + if isinstance(x_recon, tuple) and not return_ids: + return x_recon[0] + else: + return x_recon + + def _predict_eps_from_xstart(self, x_t, t, pred_xstart): + return (extract_into_tensor(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t - pred_xstart) / \ + extract_into_tensor(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape) + + def _prior_bpd(self, x_start): + """ + Get the prior KL term for the variational lower-bound, measured in + bits-per-dim. + This term can't be optimized, as it only depends on the encoder. + :param x_start: the [N x C x ...] tensor of inputs. + :return: a batch of [N] KL values (in bits), one per batch element. + """ + batch_size = x_start.shape[0] + t = torch.tensor([self.num_timesteps - 1] * batch_size, device=x_start.device) + qt_mean, _, qt_log_variance = self.q_mean_variance(x_start, t) + kl_prior = normal_kl(mean1=qt_mean, logvar1=qt_log_variance, mean2=0.0, logvar2=0.0) + return mean_flat(kl_prior) / np.log(2.0) + + def p_losses(self, x_start, cond, t, noise=None): + noise = default(noise, lambda: torch.randn_like(x_start)) + x_noisy = self.q_sample(x_start=x_start, t=t, noise=noise) + model_output = self.apply_model(x_noisy, t, cond) + + loss_dict = {} + prefix = 'train' if self.training else 'val' + + if self.parameterization == "x0": + target = x_start + elif self.parameterization == "eps": + target = noise + elif self.parameterization == "v": + target = self.get_v(x_start, noise, t) + else: + raise NotImplementedError() + + loss_simple = self.get_loss(model_output, target, mean=False).mean([1, 2, 3]) + loss_dict.update({f'{prefix}/loss_simple': loss_simple.mean()}) + + logvar_t = self.logvar[t].to(self.device) + loss = loss_simple / torch.exp(logvar_t) + logvar_t + # loss = loss_simple / torch.exp(self.logvar) + self.logvar + if self.learn_logvar: + loss_dict.update({f'{prefix}/loss_gamma': loss.mean()}) + loss_dict.update({'logvar': self.logvar.data.mean()}) + + loss = self.l_simple_weight * loss.mean() + + loss_vlb = self.get_loss(model_output, target, mean=False).mean(dim=(1, 2, 3)) + loss_vlb = (self.lvlb_weights[t] * loss_vlb).mean() + loss_dict.update({f'{prefix}/loss_vlb': loss_vlb}) + loss += (self.original_elbo_weight * loss_vlb) + loss_dict.update({f'{prefix}/loss': loss}) + + return loss, loss_dict + + def p_mean_variance(self, x, c, t, clip_denoised: bool, return_codebook_ids=False, quantize_denoised=False, + return_x0=False, score_corrector=None, corrector_kwargs=None): + t_in = t + model_out = self.apply_model(x, t_in, c, return_ids=return_codebook_ids) + + if score_corrector is not None: + assert self.parameterization == "eps" + model_out = score_corrector.modify_score(self, model_out, x, t, c, **corrector_kwargs) + + if return_codebook_ids: + model_out, logits = model_out + + if self.parameterization == "eps": + x_recon = self.predict_start_from_noise(x, t=t, noise=model_out) + elif self.parameterization == "x0": + x_recon = model_out + else: + raise NotImplementedError() + + if clip_denoised: + x_recon.clamp_(-1., 1.) + if quantize_denoised: + x_recon, _, [_, _, indices] = self.first_stage_model.quantize(x_recon) + model_mean, posterior_variance, posterior_log_variance = self.q_posterior(x_start=x_recon, x_t=x, t=t) + if return_codebook_ids: + return model_mean, posterior_variance, posterior_log_variance, logits + elif return_x0: + return model_mean, posterior_variance, posterior_log_variance, x_recon + else: + return model_mean, posterior_variance, posterior_log_variance + + @torch.no_grad() + def p_sample(self, x, c, t, clip_denoised=False, repeat_noise=False, + return_codebook_ids=False, quantize_denoised=False, return_x0=False, + temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None): + b, *_, device = *x.shape, x.device + outputs = self.p_mean_variance(x=x, c=c, t=t, clip_denoised=clip_denoised, + return_codebook_ids=return_codebook_ids, + quantize_denoised=quantize_denoised, + return_x0=return_x0, + score_corrector=score_corrector, corrector_kwargs=corrector_kwargs) + if return_codebook_ids: + raise DeprecationWarning("Support dropped.") + model_mean, _, model_log_variance, logits = outputs + elif return_x0: + model_mean, _, model_log_variance, x0 = outputs + else: + model_mean, _, model_log_variance = outputs + + noise = noise_like(x.shape, device, repeat_noise) * temperature + if noise_dropout > 0.: + noise = torch.nn.functional.dropout(noise, p=noise_dropout) + # no noise when t == 0 + nonzero_mask = (1 - (t == 0).float()).reshape(b, *((1,) * (len(x.shape) - 1))) + + if return_codebook_ids: + return model_mean + nonzero_mask * (0.5 * model_log_variance).exp() * noise, logits.argmax(dim=1) + if return_x0: + return model_mean + nonzero_mask * (0.5 * model_log_variance).exp() * noise, x0 + else: + return model_mean + nonzero_mask * (0.5 * model_log_variance).exp() * noise + + @torch.no_grad() + def progressive_denoising(self, cond, shape, verbose=True, callback=None, quantize_denoised=False, + img_callback=None, mask=None, x0=None, temperature=1., noise_dropout=0., + score_corrector=None, corrector_kwargs=None, batch_size=None, x_T=None, start_T=None, + log_every_t=None): + if not log_every_t: + log_every_t = self.log_every_t + timesteps = self.num_timesteps + if batch_size is not None: + b = batch_size if batch_size is not None else shape[0] + shape = [batch_size] + list(shape) + else: + b = batch_size = shape[0] + if x_T is None: + img = torch.randn(shape, device=self.device) + else: + img = x_T + intermediates = [] + if cond is not None: + if isinstance(cond, dict): + cond = {key: cond[key][:batch_size] if not isinstance(cond[key], list) else + list(map(lambda x: x[:batch_size], cond[key])) for key in cond} + else: + cond = [c[:batch_size] for c in cond] if isinstance(cond, list) else cond[:batch_size] + + if start_T is not None: + timesteps = min(timesteps, start_T) + iterator = tqdm(reversed(range(0, timesteps)), desc='Progressive Generation', + total=timesteps) if verbose else reversed( + range(0, timesteps)) + if type(temperature) == float: + temperature = [temperature] * timesteps + + for i in iterator: + ts = torch.full((b,), i, device=self.device, dtype=torch.long) + if self.shorten_cond_schedule: + assert self.model.conditioning_key != 'hybrid' + tc = self.cond_ids[ts].to(cond.device) + cond = self.q_sample(x_start=cond, t=tc, noise=torch.randn_like(cond)) + + img, x0_partial = self.p_sample(img, cond, ts, + clip_denoised=self.clip_denoised, + quantize_denoised=quantize_denoised, return_x0=True, + temperature=temperature[i], noise_dropout=noise_dropout, + score_corrector=score_corrector, corrector_kwargs=corrector_kwargs) + if mask is not None: + assert x0 is not None + img_orig = self.q_sample(x0, ts) + img = img_orig * mask + (1. - mask) * img + + if i % log_every_t == 0 or i == timesteps - 1: + intermediates.append(x0_partial) + if callback: callback(i) + if img_callback: img_callback(img, i) + return img, intermediates + + @torch.no_grad() + def p_sample_loop(self, cond, shape, return_intermediates=False, + x_T=None, verbose=True, callback=None, timesteps=None, quantize_denoised=False, + mask=None, x0=None, img_callback=None, start_T=None, + log_every_t=None): + + if not log_every_t: + log_every_t = self.log_every_t + device = self.betas.device + b = shape[0] + if x_T is None: + img = torch.randn(shape, device=device) + else: + img = x_T + + intermediates = [img] + if timesteps is None: + timesteps = self.num_timesteps + + if start_T is not None: + timesteps = min(timesteps, start_T) + iterator = tqdm(reversed(range(0, timesteps)), desc='Sampling t', total=timesteps) if verbose else reversed( + range(0, timesteps)) + + if mask is not None: + assert x0 is not None + assert x0.shape[2:3] == mask.shape[2:3] # spatial size has to match + + for i in iterator: + ts = torch.full((b,), i, device=device, dtype=torch.long) + if self.shorten_cond_schedule: + assert self.model.conditioning_key != 'hybrid' + tc = self.cond_ids[ts].to(cond.device) + cond = self.q_sample(x_start=cond, t=tc, noise=torch.randn_like(cond)) + + img = self.p_sample(img, cond, ts, + clip_denoised=self.clip_denoised, + quantize_denoised=quantize_denoised) + if mask is not None: + img_orig = self.q_sample(x0, ts) + img = img_orig * mask + (1. - mask) * img + + if i % log_every_t == 0 or i == timesteps - 1: + intermediates.append(img) + if callback: callback(i) + if img_callback: img_callback(img, i) + + if return_intermediates: + return img, intermediates + return img + + @torch.no_grad() + def sample(self, cond, batch_size=16, return_intermediates=False, x_T=None, + verbose=True, timesteps=None, quantize_denoised=False, + mask=None, x0=None, shape=None, **kwargs): + if shape is None: + shape = (batch_size, self.channels, self.image_size, self.image_size) + if cond is not None: + if isinstance(cond, dict): + cond = {key: cond[key][:batch_size] if not isinstance(cond[key], list) else + list(map(lambda x: x[:batch_size], cond[key])) for key in cond} + else: + cond = [c[:batch_size] for c in cond] if isinstance(cond, list) else cond[:batch_size] + return self.p_sample_loop(cond, + shape, + return_intermediates=return_intermediates, x_T=x_T, + verbose=verbose, timesteps=timesteps, quantize_denoised=quantize_denoised, + mask=mask, x0=x0) + + @torch.no_grad() + def sample_log(self, cond, batch_size, ddim, ddim_steps, **kwargs): + if ddim: + ddim_sampler = DDIMSampler(self) + shape = (self.channels, self.image_size, self.image_size) + samples, intermediates = ddim_sampler.sample(ddim_steps, batch_size, + shape, cond, verbose=False, **kwargs) + + else: + samples, intermediates = self.sample(cond=cond, batch_size=batch_size, + return_intermediates=True, **kwargs) + + return samples, intermediates + + @torch.no_grad() + def get_unconditional_conditioning(self, batch_size, null_label=None): + if null_label is not None: + xc = null_label + if isinstance(xc, ListConfig): + xc = list(xc) + if isinstance(xc, dict) or isinstance(xc, list): + c = self.get_learned_conditioning(xc) + else: + if hasattr(xc, "to"): + xc = xc.to(self.device) + c = self.get_learned_conditioning(xc) + else: + if self.cond_stage_key in ["class_label", "cls"]: + xc = self.cond_stage_model.get_unconditional_conditioning(batch_size, device=self.device) + return self.get_learned_conditioning(xc) + else: + raise NotImplementedError("todo") + if isinstance(c, list): # in case the encoder gives us a list + for i in range(len(c)): + c[i] = repeat(c[i], '1 ... -> b ...', b=batch_size).to(self.device) + else: + c = repeat(c, '1 ... -> b ...', b=batch_size).to(self.device) + return c + + @torch.no_grad() + def log_images(self, batch, N=8, n_row=4, sample=True, ddim_steps=50, ddim_eta=0., return_keys=None, + quantize_denoised=True, inpaint=True, plot_denoise_rows=False, plot_progressive_rows=True, + plot_diffusion_rows=True, unconditional_guidance_scale=1., unconditional_guidance_label=None, + use_ema_scope=True, + **kwargs): + ema_scope = self.ema_scope if use_ema_scope else nullcontext + use_ddim = ddim_steps is not None + + log = dict() + z, c, x, xrec, xc = self.get_input(batch, self.first_stage_key, + return_first_stage_outputs=True, + force_c_encode=True, + return_original_cond=True, + bs=N) + N = min(x.shape[0], N) + n_row = min(x.shape[0], n_row) + log["inputs"] = x + log["reconstruction"] = xrec + if self.model.conditioning_key is not None: + if hasattr(self.cond_stage_model, "decode"): + xc = self.cond_stage_model.decode(c) + log["conditioning"] = xc + elif self.cond_stage_key in ["caption", "txt"]: + xc = log_txt_as_img((x.shape[2], x.shape[3]), batch[self.cond_stage_key], size=x.shape[2] // 25) + log["conditioning"] = xc + elif self.cond_stage_key in ['class_label', "cls"]: + try: + xc = log_txt_as_img((x.shape[2], x.shape[3]), batch["human_label"], size=x.shape[2] // 25) + log['conditioning'] = xc + except KeyError: + # probably no "human_label" in batch + pass + elif isimage(xc): + log["conditioning"] = xc + if ismap(xc): + log["original_conditioning"] = self.to_rgb(xc) + + if plot_diffusion_rows: + # get diffusion row + diffusion_row = list() + z_start = z[:n_row] + for t in range(self.num_timesteps): + if t % self.log_every_t == 0 or t == self.num_timesteps - 1: + t = repeat(torch.tensor([t]), '1 -> b', b=n_row) + t = t.to(self.device).long() + noise = torch.randn_like(z_start) + z_noisy = self.q_sample(x_start=z_start, t=t, noise=noise) + diffusion_row.append(self.decode_first_stage(z_noisy)) + + diffusion_row = torch.stack(diffusion_row) # n_log_step, n_row, C, H, W + diffusion_grid = rearrange(diffusion_row, 'n b c h w -> b n c h w') + diffusion_grid = rearrange(diffusion_grid, 'b n c h w -> (b n) c h w') + diffusion_grid = make_grid(diffusion_grid, nrow=diffusion_row.shape[0]) + log["diffusion_row"] = diffusion_grid + + if sample: + # get denoise row + with ema_scope("Sampling"): + samples, z_denoise_row = self.sample_log(cond=c, batch_size=N, ddim=use_ddim, + ddim_steps=ddim_steps, eta=ddim_eta) + # samples, z_denoise_row = self.sample(cond=c, batch_size=N, return_intermediates=True) + x_samples = self.decode_first_stage(samples) + log["samples"] = x_samples + if plot_denoise_rows: + denoise_grid = self._get_denoise_row_from_list(z_denoise_row) + log["denoise_row"] = denoise_grid + + if quantize_denoised and not isinstance(self.first_stage_model, AutoencoderKL) and not isinstance( + self.first_stage_model, IdentityFirstStage): + # also display when quantizing x0 while sampling + with ema_scope("Plotting Quantized Denoised"): + samples, z_denoise_row = self.sample_log(cond=c, batch_size=N, ddim=use_ddim, + ddim_steps=ddim_steps, eta=ddim_eta, + quantize_denoised=True) + # samples, z_denoise_row = self.sample(cond=c, batch_size=N, return_intermediates=True, + # quantize_denoised=True) + x_samples = self.decode_first_stage(samples.to(self.device)) + log["samples_x0_quantized"] = x_samples + + if unconditional_guidance_scale > 1.0: + uc = self.get_unconditional_conditioning(N, unconditional_guidance_label) + if self.model.conditioning_key == "crossattn-adm": + uc = {"c_crossattn": [uc], "c_adm": c["c_adm"]} + with ema_scope("Sampling with classifier-free guidance"): + samples_cfg, _ = self.sample_log(cond=c, batch_size=N, ddim=use_ddim, + ddim_steps=ddim_steps, eta=ddim_eta, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=uc, + ) + x_samples_cfg = self.decode_first_stage(samples_cfg) + log[f"samples_cfg_scale_{unconditional_guidance_scale:.2f}"] = x_samples_cfg + + if inpaint: + # make a simple center square + b, h, w = z.shape[0], z.shape[2], z.shape[3] + mask = torch.ones(N, h, w).to(self.device) + # zeros will be filled in + mask[:, h // 4:3 * h // 4, w // 4:3 * w // 4] = 0. + mask = mask[:, None, ...] + with ema_scope("Plotting Inpaint"): + samples, _ = self.sample_log(cond=c, batch_size=N, ddim=use_ddim, eta=ddim_eta, + ddim_steps=ddim_steps, x0=z[:N], mask=mask) + x_samples = self.decode_first_stage(samples.to(self.device)) + log["samples_inpainting"] = x_samples + log["mask"] = mask + + # outpaint + mask = 1. - mask + with ema_scope("Plotting Outpaint"): + samples, _ = self.sample_log(cond=c, batch_size=N, ddim=use_ddim, eta=ddim_eta, + ddim_steps=ddim_steps, x0=z[:N], mask=mask) + x_samples = self.decode_first_stage(samples.to(self.device)) + log["samples_outpainting"] = x_samples + + if plot_progressive_rows: + with ema_scope("Plotting Progressives"): + img, progressives = self.progressive_denoising(c, + shape=(self.channels, self.image_size, self.image_size), + batch_size=N) + prog_row = self._get_denoise_row_from_list(progressives, desc="Progressive Generation") + log["progressive_row"] = prog_row + + if return_keys: + if np.intersect1d(list(log.keys()), return_keys).shape[0] == 0: + return log + else: + return {key: log[key] for key in return_keys} + return log + + def configure_optimizers(self): + lr = self.learning_rate + params = list(self.model.parameters()) + if self.cond_stage_trainable: + print(f"{self.__class__.__name__}: Also optimizing conditioner params!") + params = params + list(self.cond_stage_model.parameters()) + if self.learn_logvar: + print('Diffusion model optimizing logvar') + params.append(self.logvar) + opt = torch.optim.AdamW(params, lr=lr) + if self.use_scheduler: + assert 'target' in self.scheduler_config + scheduler = instantiate_from_config(self.scheduler_config) + + print("Setting up LambdaLR scheduler...") + scheduler = [ + { + 'scheduler': LambdaLR(opt, lr_lambda=scheduler.schedule), + 'interval': 'step', + 'frequency': 1 + }] + return [opt], scheduler + return opt + + @torch.no_grad() + def to_rgb(self, x): + x = x.float() + if not hasattr(self, "colorize"): + self.colorize = torch.randn(3, x.shape[1], 1, 1).to(x) + x = nn.functional.conv2d(x, weight=self.colorize) + x = 2. * (x - x.min()) / (x.max() - x.min()) - 1. + return x + + +class DiffusionWrapper(pl.LightningModule): + def __init__(self, diff_model_config, conditioning_key): + super().__init__() + self.sequential_cross_attn = diff_model_config.pop("sequential_crossattn", False) + self.diffusion_model = instantiate_from_config(diff_model_config) + self.conditioning_key = conditioning_key + assert self.conditioning_key in [None, 'concat', 'crossattn', 'hybrid', 'adm', 'hybrid-adm', 'crossattn-adm'] + + def forward(self, x, t, c_concat: list = None, c_crossattn: list = None, c_adm=None): + if self.conditioning_key is None: + out = self.diffusion_model(x, t) + elif self.conditioning_key == 'concat': + xc = torch.cat([x] + c_concat, dim=1) + out = self.diffusion_model(xc, t) + elif self.conditioning_key == 'crossattn': + if not self.sequential_cross_attn: + cc = torch.cat(c_crossattn, 1) + else: + cc = c_crossattn + if hasattr(self, "scripted_diffusion_model"): + # TorchScript changes names of the arguments + # with argument cc defined as context=cc scripted model will produce + # an error: RuntimeError: forward() is missing value for argument 'argument_3'. + out = self.scripted_diffusion_model(x, t, cc) + else: + out = self.diffusion_model(x, t, context=cc) + elif self.conditioning_key == 'hybrid': + xc = torch.cat([x] + c_concat, dim=1) + cc = torch.cat(c_crossattn, 1) + out = self.diffusion_model(xc, t, context=cc) + elif self.conditioning_key == 'hybrid-adm': + assert c_adm is not None + xc = torch.cat([x] + c_concat, dim=1) + cc = torch.cat(c_crossattn, 1) + out = self.diffusion_model(xc, t, context=cc, y=c_adm) + elif self.conditioning_key == 'crossattn-adm': + assert c_adm is not None + cc = torch.cat(c_crossattn, 1) + out = self.diffusion_model(x, t, context=cc, y=c_adm) + elif self.conditioning_key == 'adm': + cc = c_crossattn[0] + out = self.diffusion_model(x, t, y=cc) + else: + raise NotImplementedError() + + return out + + +class LatentUpscaleDiffusion(LatentDiffusion): + def __init__(self, *args, low_scale_config, low_scale_key="LR", noise_level_key=None, **kwargs): + super().__init__(*args, **kwargs) + # assumes that neither the cond_stage nor the low_scale_model contain trainable params + assert not self.cond_stage_trainable + self.instantiate_low_stage(low_scale_config) + self.low_scale_key = low_scale_key + self.noise_level_key = noise_level_key + + def instantiate_low_stage(self, config): + model = instantiate_from_config(config) + self.low_scale_model = model.eval() + self.low_scale_model.train = disabled_train + for param in self.low_scale_model.parameters(): + param.requires_grad = False + + @torch.no_grad() + def get_input(self, batch, k, cond_key=None, bs=None, log_mode=False): + if not log_mode: + z, c = super().get_input(batch, k, force_c_encode=True, bs=bs) + else: + z, c, x, xrec, xc = super().get_input(batch, self.first_stage_key, return_first_stage_outputs=True, + force_c_encode=True, return_original_cond=True, bs=bs) + x_low = batch[self.low_scale_key][:bs] + x_low = rearrange(x_low, 'b h w c -> b c h w') + x_low = x_low.to(memory_format=torch.contiguous_format).float() + zx, noise_level = self.low_scale_model(x_low) + if self.noise_level_key is not None: + # get noise level from batch instead, e.g. when extracting a custom noise level for bsr + raise NotImplementedError('TODO') + + all_conds = {"c_concat": [zx], "c_crossattn": [c], "c_adm": noise_level} + if log_mode: + # TODO: maybe disable if too expensive + x_low_rec = self.low_scale_model.decode(zx) + return z, all_conds, x, xrec, xc, x_low, x_low_rec, noise_level + return z, all_conds + + @torch.no_grad() + def log_images(self, batch, N=8, n_row=4, sample=True, ddim_steps=200, ddim_eta=1., return_keys=None, + plot_denoise_rows=False, plot_progressive_rows=True, plot_diffusion_rows=True, + unconditional_guidance_scale=1., unconditional_guidance_label=None, use_ema_scope=True, + **kwargs): + ema_scope = self.ema_scope if use_ema_scope else nullcontext + use_ddim = ddim_steps is not None + + log = dict() + z, c, x, xrec, xc, x_low, x_low_rec, noise_level = self.get_input(batch, self.first_stage_key, bs=N, + log_mode=True) + N = min(x.shape[0], N) + n_row = min(x.shape[0], n_row) + log["inputs"] = x + log["reconstruction"] = xrec + log["x_lr"] = x_low + log[f"x_lr_rec_@noise_levels{'-'.join(map(lambda x: str(x), list(noise_level.cpu().numpy())))}"] = x_low_rec + if self.model.conditioning_key is not None: + if hasattr(self.cond_stage_model, "decode"): + xc = self.cond_stage_model.decode(c) + log["conditioning"] = xc + elif self.cond_stage_key in ["caption", "txt"]: + xc = log_txt_as_img((x.shape[2], x.shape[3]), batch[self.cond_stage_key], size=x.shape[2] // 25) + log["conditioning"] = xc + elif self.cond_stage_key in ['class_label', 'cls']: + xc = log_txt_as_img((x.shape[2], x.shape[3]), batch["human_label"], size=x.shape[2] // 25) + log['conditioning'] = xc + elif isimage(xc): + log["conditioning"] = xc + if ismap(xc): + log["original_conditioning"] = self.to_rgb(xc) + + if plot_diffusion_rows: + # get diffusion row + diffusion_row = list() + z_start = z[:n_row] + for t in range(self.num_timesteps): + if t % self.log_every_t == 0 or t == self.num_timesteps - 1: + t = repeat(torch.tensor([t]), '1 -> b', b=n_row) + t = t.to(self.device).long() + noise = torch.randn_like(z_start) + z_noisy = self.q_sample(x_start=z_start, t=t, noise=noise) + diffusion_row.append(self.decode_first_stage(z_noisy)) + + diffusion_row = torch.stack(diffusion_row) # n_log_step, n_row, C, H, W + diffusion_grid = rearrange(diffusion_row, 'n b c h w -> b n c h w') + diffusion_grid = rearrange(diffusion_grid, 'b n c h w -> (b n) c h w') + diffusion_grid = make_grid(diffusion_grid, nrow=diffusion_row.shape[0]) + log["diffusion_row"] = diffusion_grid + + if sample: + # get denoise row + with ema_scope("Sampling"): + samples, z_denoise_row = self.sample_log(cond=c, batch_size=N, ddim=use_ddim, + ddim_steps=ddim_steps, eta=ddim_eta) + # samples, z_denoise_row = self.sample(cond=c, batch_size=N, return_intermediates=True) + x_samples = self.decode_first_stage(samples) + log["samples"] = x_samples + if plot_denoise_rows: + denoise_grid = self._get_denoise_row_from_list(z_denoise_row) + log["denoise_row"] = denoise_grid + + if unconditional_guidance_scale > 1.0: + uc_tmp = self.get_unconditional_conditioning(N, unconditional_guidance_label) + # TODO explore better "unconditional" choices for the other keys + # maybe guide away from empty text label and highest noise level and maximally degraded zx? + uc = dict() + for k in c: + if k == "c_crossattn": + assert isinstance(c[k], list) and len(c[k]) == 1 + uc[k] = [uc_tmp] + elif k == "c_adm": # todo: only run with text-based guidance? + assert isinstance(c[k], torch.Tensor) + #uc[k] = torch.ones_like(c[k]) * self.low_scale_model.max_noise_level + uc[k] = c[k] + elif isinstance(c[k], list): + uc[k] = [c[k][i] for i in range(len(c[k]))] + else: + uc[k] = c[k] + + with ema_scope("Sampling with classifier-free guidance"): + samples_cfg, _ = self.sample_log(cond=c, batch_size=N, ddim=use_ddim, + ddim_steps=ddim_steps, eta=ddim_eta, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=uc, + ) + x_samples_cfg = self.decode_first_stage(samples_cfg) + log[f"samples_cfg_scale_{unconditional_guidance_scale:.2f}"] = x_samples_cfg + + if plot_progressive_rows: + with ema_scope("Plotting Progressives"): + img, progressives = self.progressive_denoising(c, + shape=(self.channels, self.image_size, self.image_size), + batch_size=N) + prog_row = self._get_denoise_row_from_list(progressives, desc="Progressive Generation") + log["progressive_row"] = prog_row + + return log + + +class LatentFinetuneDiffusion(LatentDiffusion): + """ + Basis for different finetunas, such as inpainting or depth2image + To disable finetuning mode, set finetune_keys to None + """ + + def __init__(self, + concat_keys: tuple, + finetune_keys=("model.diffusion_model.input_blocks.0.0.weight", + "model_ema.diffusion_modelinput_blocks00weight" + ), + keep_finetune_dims=4, + # if model was trained without concat mode before and we would like to keep these channels + c_concat_log_start=None, # to log reconstruction of c_concat codes + c_concat_log_end=None, + *args, **kwargs + ): + ckpt_path = kwargs.pop("ckpt_path", None) + ignore_keys = kwargs.pop("ignore_keys", list()) + super().__init__(*args, **kwargs) + self.finetune_keys = finetune_keys + self.concat_keys = concat_keys + self.keep_dims = keep_finetune_dims + self.c_concat_log_start = c_concat_log_start + self.c_concat_log_end = c_concat_log_end + if exists(self.finetune_keys): assert exists(ckpt_path), 'can only finetune from a given checkpoint' + if exists(ckpt_path): + self.init_from_ckpt(ckpt_path, ignore_keys) + + def init_from_ckpt(self, path, ignore_keys=list(), only_model=False): + sd = torch.load(path, map_location="cpu") + if "state_dict" in list(sd.keys()): + sd = sd["state_dict"] + keys = list(sd.keys()) + for k in keys: + for ik in ignore_keys: + if k.startswith(ik): + print("Deleting key {} from state_dict.".format(k)) + del sd[k] + + # make it explicit, finetune by including extra input channels + if exists(self.finetune_keys) and k in self.finetune_keys: + new_entry = None + for name, param in self.named_parameters(): + if name in self.finetune_keys: + print( + f"modifying key '{name}' and keeping its original {self.keep_dims} (channels) dimensions only") + new_entry = torch.zeros_like(param) # zero init + assert exists(new_entry), 'did not find matching parameter to modify' + new_entry[:, :self.keep_dims, ...] = sd[k] + sd[k] = new_entry + + missing, unexpected = self.load_state_dict(sd, strict=False) if not only_model else self.model.load_state_dict( + sd, strict=False) + print(f"Restored from {path} with {len(missing)} missing and {len(unexpected)} unexpected keys") + if len(missing) > 0: + print(f"Missing Keys: {missing}") + if len(unexpected) > 0: + print(f"Unexpected Keys: {unexpected}") + + @torch.no_grad() + def log_images(self, batch, N=8, n_row=4, sample=True, ddim_steps=200, ddim_eta=1., return_keys=None, + quantize_denoised=True, inpaint=True, plot_denoise_rows=False, plot_progressive_rows=True, + plot_diffusion_rows=True, unconditional_guidance_scale=1., unconditional_guidance_label=None, + use_ema_scope=True, + **kwargs): + ema_scope = self.ema_scope if use_ema_scope else nullcontext + use_ddim = ddim_steps is not None + + log = dict() + z, c, x, xrec, xc = self.get_input(batch, self.first_stage_key, bs=N, return_first_stage_outputs=True) + c_cat, c = c["c_concat"][0], c["c_crossattn"][0] + N = min(x.shape[0], N) + n_row = min(x.shape[0], n_row) + log["inputs"] = x + log["reconstruction"] = xrec + if self.model.conditioning_key is not None: + if hasattr(self.cond_stage_model, "decode"): + xc = self.cond_stage_model.decode(c) + log["conditioning"] = xc + elif self.cond_stage_key in ["caption", "txt"]: + xc = log_txt_as_img((x.shape[2], x.shape[3]), batch[self.cond_stage_key], size=x.shape[2] // 25) + log["conditioning"] = xc + elif self.cond_stage_key in ['class_label', 'cls']: + xc = log_txt_as_img((x.shape[2], x.shape[3]), batch["human_label"], size=x.shape[2] // 25) + log['conditioning'] = xc + elif isimage(xc): + log["conditioning"] = xc + if ismap(xc): + log["original_conditioning"] = self.to_rgb(xc) + + if not (self.c_concat_log_start is None and self.c_concat_log_end is None): + log["c_concat_decoded"] = self.decode_first_stage(c_cat[:, self.c_concat_log_start:self.c_concat_log_end]) + + if plot_diffusion_rows: + # get diffusion row + diffusion_row = list() + z_start = z[:n_row] + for t in range(self.num_timesteps): + if t % self.log_every_t == 0 or t == self.num_timesteps - 1: + t = repeat(torch.tensor([t]), '1 -> b', b=n_row) + t = t.to(self.device).long() + noise = torch.randn_like(z_start) + z_noisy = self.q_sample(x_start=z_start, t=t, noise=noise) + diffusion_row.append(self.decode_first_stage(z_noisy)) + + diffusion_row = torch.stack(diffusion_row) # n_log_step, n_row, C, H, W + diffusion_grid = rearrange(diffusion_row, 'n b c h w -> b n c h w') + diffusion_grid = rearrange(diffusion_grid, 'b n c h w -> (b n) c h w') + diffusion_grid = make_grid(diffusion_grid, nrow=diffusion_row.shape[0]) + log["diffusion_row"] = diffusion_grid + + if sample: + # get denoise row + with ema_scope("Sampling"): + samples, z_denoise_row = self.sample_log(cond={"c_concat": [c_cat], "c_crossattn": [c]}, + batch_size=N, ddim=use_ddim, + ddim_steps=ddim_steps, eta=ddim_eta) + # samples, z_denoise_row = self.sample(cond=c, batch_size=N, return_intermediates=True) + x_samples = self.decode_first_stage(samples) + log["samples"] = x_samples + if plot_denoise_rows: + denoise_grid = self._get_denoise_row_from_list(z_denoise_row) + log["denoise_row"] = denoise_grid + + if unconditional_guidance_scale > 1.0: + uc_cross = self.get_unconditional_conditioning(N, unconditional_guidance_label) + uc_cat = c_cat + uc_full = {"c_concat": [uc_cat], "c_crossattn": [uc_cross]} + with ema_scope("Sampling with classifier-free guidance"): + samples_cfg, _ = self.sample_log(cond={"c_concat": [c_cat], "c_crossattn": [c]}, + batch_size=N, ddim=use_ddim, + ddim_steps=ddim_steps, eta=ddim_eta, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=uc_full, + ) + x_samples_cfg = self.decode_first_stage(samples_cfg) + log[f"samples_cfg_scale_{unconditional_guidance_scale:.2f}"] = x_samples_cfg + + return log + + +class LatentInpaintDiffusion(LatentFinetuneDiffusion): + """ + can either run as pure inpainting model (only concat mode) or with mixed conditionings, + e.g. mask as concat and text via cross-attn. + To disable finetuning mode, set finetune_keys to None + """ + + def __init__(self, + concat_keys=("mask", "masked_image"), + masked_image_key="masked_image", + *args, **kwargs + ): + super().__init__(concat_keys, *args, **kwargs) + self.masked_image_key = masked_image_key + assert self.masked_image_key in concat_keys + + @torch.no_grad() + def get_input(self, batch, k, cond_key=None, bs=None, return_first_stage_outputs=False): + # note: restricted to non-trainable encoders currently + assert not self.cond_stage_trainable, 'trainable cond stages not yet supported for inpainting' + z, c, x, xrec, xc = super().get_input(batch, self.first_stage_key, return_first_stage_outputs=True, + force_c_encode=True, return_original_cond=True, bs=bs) + + assert exists(self.concat_keys) + c_cat = list() + for ck in self.concat_keys: + cc = rearrange(batch[ck], 'b h w c -> b c h w').to(memory_format=torch.contiguous_format).float() + if bs is not None: + cc = cc[:bs] + cc = cc.to(self.device) + bchw = z.shape + if ck != self.masked_image_key: + cc = torch.nn.functional.interpolate(cc, size=bchw[-2:]) + else: + cc = self.get_first_stage_encoding(self.encode_first_stage(cc)) + c_cat.append(cc) + c_cat = torch.cat(c_cat, dim=1) + all_conds = {"c_concat": [c_cat], "c_crossattn": [c]} + if return_first_stage_outputs: + return z, all_conds, x, xrec, xc + return z, all_conds + + @torch.no_grad() + def log_images(self, *args, **kwargs): + log = super(LatentInpaintDiffusion, self).log_images(*args, **kwargs) + log["masked_image"] = rearrange(args[0]["masked_image"], + 'b h w c -> b c h w').to(memory_format=torch.contiguous_format).float() + return log + + +class LatentDepth2ImageDiffusion(LatentFinetuneDiffusion): + """ + condition on monocular depth estimation + """ + + def __init__(self, depth_stage_config, concat_keys=("midas_in",), *args, **kwargs): + super().__init__(concat_keys=concat_keys, *args, **kwargs) + self.depth_model = instantiate_from_config(depth_stage_config) + self.depth_stage_key = concat_keys[0] + + @torch.no_grad() + def get_input(self, batch, k, cond_key=None, bs=None, return_first_stage_outputs=False): + # note: restricted to non-trainable encoders currently + assert not self.cond_stage_trainable, 'trainable cond stages not yet supported for depth2img' + z, c, x, xrec, xc = super().get_input(batch, self.first_stage_key, return_first_stage_outputs=True, + force_c_encode=True, return_original_cond=True, bs=bs) + + assert exists(self.concat_keys) + assert len(self.concat_keys) == 1 + c_cat = list() + for ck in self.concat_keys: + cc = batch[ck] + if bs is not None: + cc = cc[:bs] + cc = cc.to(self.device) + cc = self.depth_model(cc) + cc = torch.nn.functional.interpolate( + cc, + size=z.shape[2:], + mode="bicubic", + align_corners=False, + ) + + depth_min, depth_max = torch.amin(cc, dim=[1, 2, 3], keepdim=True), torch.amax(cc, dim=[1, 2, 3], + keepdim=True) + cc = 2. * (cc - depth_min) / (depth_max - depth_min + 0.001) - 1. + c_cat.append(cc) + c_cat = torch.cat(c_cat, dim=1) + all_conds = {"c_concat": [c_cat], "c_crossattn": [c]} + if return_first_stage_outputs: + return z, all_conds, x, xrec, xc + return z, all_conds + + @torch.no_grad() + def log_images(self, *args, **kwargs): + log = super().log_images(*args, **kwargs) + depth = self.depth_model(args[0][self.depth_stage_key]) + depth_min, depth_max = torch.amin(depth, dim=[1, 2, 3], keepdim=True), \ + torch.amax(depth, dim=[1, 2, 3], keepdim=True) + log["depth"] = 2. * (depth - depth_min) / (depth_max - depth_min) - 1. + return log + + +class LatentUpscaleFinetuneDiffusion(LatentFinetuneDiffusion): + """ + condition on low-res image (and optionally on some spatial noise augmentation) + """ + def __init__(self, concat_keys=("lr",), reshuffle_patch_size=None, + low_scale_config=None, low_scale_key=None, *args, **kwargs): + super().__init__(concat_keys=concat_keys, *args, **kwargs) + self.reshuffle_patch_size = reshuffle_patch_size + self.low_scale_model = None + if low_scale_config is not None: + print("Initializing a low-scale model") + assert exists(low_scale_key) + self.instantiate_low_stage(low_scale_config) + self.low_scale_key = low_scale_key + + def instantiate_low_stage(self, config): + model = instantiate_from_config(config) + self.low_scale_model = model.eval() + self.low_scale_model.train = disabled_train + for param in self.low_scale_model.parameters(): + param.requires_grad = False + + @torch.no_grad() + def get_input(self, batch, k, cond_key=None, bs=None, return_first_stage_outputs=False): + # note: restricted to non-trainable encoders currently + assert not self.cond_stage_trainable, 'trainable cond stages not yet supported for upscaling-ft' + z, c, x, xrec, xc = super().get_input(batch, self.first_stage_key, return_first_stage_outputs=True, + force_c_encode=True, return_original_cond=True, bs=bs) + + assert exists(self.concat_keys) + assert len(self.concat_keys) == 1 + # optionally make spatial noise_level here + c_cat = list() + noise_level = None + for ck in self.concat_keys: + cc = batch[ck] + cc = rearrange(cc, 'b h w c -> b c h w') + if exists(self.reshuffle_patch_size): + assert isinstance(self.reshuffle_patch_size, int) + cc = rearrange(cc, 'b c (p1 h) (p2 w) -> b (p1 p2 c) h w', + p1=self.reshuffle_patch_size, p2=self.reshuffle_patch_size) + if bs is not None: + cc = cc[:bs] + cc = cc.to(self.device) + if exists(self.low_scale_model) and ck == self.low_scale_key: + cc, noise_level = self.low_scale_model(cc) + c_cat.append(cc) + c_cat = torch.cat(c_cat, dim=1) + if exists(noise_level): + all_conds = {"c_concat": [c_cat], "c_crossattn": [c], "c_adm": noise_level} + else: + all_conds = {"c_concat": [c_cat], "c_crossattn": [c]} + if return_first_stage_outputs: + return z, all_conds, x, xrec, xc + return z, all_conds + + @torch.no_grad() + def log_images(self, *args, **kwargs): + log = super().log_images(*args, **kwargs) + log["lr"] = rearrange(args[0]["lr"], 'b h w c -> b c h w') + return log + + +class ImageEmbeddingConditionedLatentDiffusion(LatentDiffusion): + def __init__(self, embedder_config, embedding_key="jpg", embedding_dropout=0.5, + freeze_embedder=True, noise_aug_config=None, *args, **kwargs): + super().__init__(*args, **kwargs) + self.embed_key = embedding_key + self.embedding_dropout = embedding_dropout + self._init_embedder(embedder_config, freeze_embedder) + self._init_noise_aug(noise_aug_config) + + def _init_embedder(self, config, freeze=True): + embedder = instantiate_from_config(config) + if freeze: + self.embedder = embedder.eval() + self.embedder.train = disabled_train + for param in self.embedder.parameters(): + param.requires_grad = False + + def _init_noise_aug(self, config): + if config is not None: + # use the KARLO schedule for noise augmentation on CLIP image embeddings + noise_augmentor = instantiate_from_config(config) + assert isinstance(noise_augmentor, nn.Module) + noise_augmentor = noise_augmentor.eval() + noise_augmentor.train = disabled_train + self.noise_augmentor = noise_augmentor + else: + self.noise_augmentor = None + + def get_input(self, batch, k, cond_key=None, bs=None, **kwargs): + outputs = LatentDiffusion.get_input(self, batch, k, bs=bs, **kwargs) + z, c = outputs[0], outputs[1] + img = batch[self.embed_key][:bs] + img = rearrange(img, 'b h w c -> b c h w') + c_adm = self.embedder(img) + if self.noise_augmentor is not None: + c_adm, noise_level_emb = self.noise_augmentor(c_adm) + # assume this gives embeddings of noise levels + c_adm = torch.cat((c_adm, noise_level_emb), 1) + if self.training: + c_adm = torch.bernoulli((1. - self.embedding_dropout) * torch.ones(c_adm.shape[0], + device=c_adm.device)[:, None]) * c_adm + all_conds = {"c_crossattn": [c], "c_adm": c_adm} + noutputs = [z, all_conds] + noutputs.extend(outputs[2:]) + return noutputs + + @torch.no_grad() + def log_images(self, batch, N=8, n_row=4, **kwargs): + log = dict() + z, c, x, xrec, xc = self.get_input(batch, self.first_stage_key, bs=N, return_first_stage_outputs=True, + return_original_cond=True) + log["inputs"] = x + log["reconstruction"] = xrec + assert self.model.conditioning_key is not None + assert self.cond_stage_key in ["caption", "txt"] + xc = log_txt_as_img((x.shape[2], x.shape[3]), batch[self.cond_stage_key], size=x.shape[2] // 25) + log["conditioning"] = xc + uc = self.get_unconditional_conditioning(N, kwargs.get('unconditional_guidance_label', '')) + unconditional_guidance_scale = kwargs.get('unconditional_guidance_scale', 5.) + + uc_ = {"c_crossattn": [uc], "c_adm": c["c_adm"]} + ema_scope = self.ema_scope if kwargs.get('use_ema_scope', True) else nullcontext + with ema_scope(f"Sampling"): + samples_cfg, _ = self.sample_log(cond=c, batch_size=N, ddim=True, + ddim_steps=kwargs.get('ddim_steps', 50), eta=kwargs.get('ddim_eta', 0.), + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=uc_, ) + x_samples_cfg = self.decode_first_stage(samples_cfg) + log[f"samplescfg_scale_{unconditional_guidance_scale:.2f}"] = x_samples_cfg + return log diff --git a/repositories/ldm/models/diffusion/dpm_solver/__init__.py b/repositories/ldm/models/diffusion/dpm_solver/__init__.py new file mode 100644 index 000000000..7427f38c0 --- /dev/null +++ b/repositories/ldm/models/diffusion/dpm_solver/__init__.py @@ -0,0 +1 @@ +from .sampler import DPMSolverSampler \ No newline at end of file diff --git a/repositories/ldm/models/diffusion/dpm_solver/dpm_solver.py b/repositories/ldm/models/diffusion/dpm_solver/dpm_solver.py new file mode 100644 index 000000000..da8d41f9c --- /dev/null +++ b/repositories/ldm/models/diffusion/dpm_solver/dpm_solver.py @@ -0,0 +1,1163 @@ +import torch +import torch.nn.functional as F +import math +from tqdm import tqdm + + +class NoiseScheduleVP: + def __init__( + self, + schedule='discrete', + betas=None, + alphas_cumprod=None, + continuous_beta_0=0.1, + continuous_beta_1=20., + ): + """Create a wrapper class for the forward SDE (VP type). + *** + Update: We support discrete-time diffusion models by implementing a picewise linear interpolation for log_alpha_t. + We recommend to use schedule='discrete' for the discrete-time diffusion models, especially for high-resolution images. + *** + The forward SDE ensures that the condition distribution q_{t|0}(x_t | x_0) = N ( alpha_t * x_0, sigma_t^2 * I ). + We further define lambda_t = log(alpha_t) - log(sigma_t), which is the half-logSNR (described in the DPM-Solver paper). + Therefore, we implement the functions for computing alpha_t, sigma_t and lambda_t. For t in [0, T], we have: + log_alpha_t = self.marginal_log_mean_coeff(t) + sigma_t = self.marginal_std(t) + lambda_t = self.marginal_lambda(t) + Moreover, as lambda(t) is an invertible function, we also support its inverse function: + t = self.inverse_lambda(lambda_t) + =============================================================== + We support both discrete-time DPMs (trained on n = 0, 1, ..., N-1) and continuous-time DPMs (trained on t in [t_0, T]). + 1. For discrete-time DPMs: + For discrete-time DPMs trained on n = 0, 1, ..., N-1, we convert the discrete steps to continuous time steps by: + t_i = (i + 1) / N + e.g. for N = 1000, we have t_0 = 1e-3 and T = t_{N-1} = 1. + We solve the corresponding diffusion ODE from time T = 1 to time t_0 = 1e-3. + Args: + betas: A `torch.Tensor`. The beta array for the discrete-time DPM. (See the original DDPM paper for details) + alphas_cumprod: A `torch.Tensor`. The cumprod alphas for the discrete-time DPM. (See the original DDPM paper for details) + Note that we always have alphas_cumprod = cumprod(betas). Therefore, we only need to set one of `betas` and `alphas_cumprod`. + **Important**: Please pay special attention for the args for `alphas_cumprod`: + The `alphas_cumprod` is the \hat{alpha_n} arrays in the notations of DDPM. Specifically, DDPMs assume that + q_{t_n | 0}(x_{t_n} | x_0) = N ( \sqrt{\hat{alpha_n}} * x_0, (1 - \hat{alpha_n}) * I ). + Therefore, the notation \hat{alpha_n} is different from the notation alpha_t in DPM-Solver. In fact, we have + alpha_{t_n} = \sqrt{\hat{alpha_n}}, + and + log(alpha_{t_n}) = 0.5 * log(\hat{alpha_n}). + 2. For continuous-time DPMs: + We support two types of VPSDEs: linear (DDPM) and cosine (improved-DDPM). The hyperparameters for the noise + schedule are the default settings in DDPM and improved-DDPM: + Args: + beta_min: A `float` number. The smallest beta for the linear schedule. + beta_max: A `float` number. The largest beta for the linear schedule. + cosine_s: A `float` number. The hyperparameter in the cosine schedule. + cosine_beta_max: A `float` number. The hyperparameter in the cosine schedule. + T: A `float` number. The ending time of the forward process. + =============================================================== + Args: + schedule: A `str`. The noise schedule of the forward SDE. 'discrete' for discrete-time DPMs, + 'linear' or 'cosine' for continuous-time DPMs. + Returns: + A wrapper object of the forward SDE (VP type). + + =============================================================== + Example: + # For discrete-time DPMs, given betas (the beta array for n = 0, 1, ..., N - 1): + >>> ns = NoiseScheduleVP('discrete', betas=betas) + # For discrete-time DPMs, given alphas_cumprod (the \hat{alpha_n} array for n = 0, 1, ..., N - 1): + >>> ns = NoiseScheduleVP('discrete', alphas_cumprod=alphas_cumprod) + # For continuous-time DPMs (VPSDE), linear schedule: + >>> ns = NoiseScheduleVP('linear', continuous_beta_0=0.1, continuous_beta_1=20.) + """ + + if schedule not in ['discrete', 'linear', 'cosine']: + raise ValueError( + "Unsupported noise schedule {}. The schedule needs to be 'discrete' or 'linear' or 'cosine'".format( + schedule)) + + self.schedule = schedule + if schedule == 'discrete': + if betas is not None: + log_alphas = 0.5 * torch.log(1 - betas).cumsum(dim=0) + else: + assert alphas_cumprod is not None + log_alphas = 0.5 * torch.log(alphas_cumprod) + self.total_N = len(log_alphas) + self.T = 1. + self.t_array = torch.linspace(0., 1., self.total_N + 1)[1:].reshape((1, -1)) + self.log_alpha_array = log_alphas.reshape((1, -1,)) + else: + self.total_N = 1000 + self.beta_0 = continuous_beta_0 + self.beta_1 = continuous_beta_1 + self.cosine_s = 0.008 + self.cosine_beta_max = 999. + self.cosine_t_max = math.atan(self.cosine_beta_max * (1. + self.cosine_s) / math.pi) * 2. * ( + 1. + self.cosine_s) / math.pi - self.cosine_s + self.cosine_log_alpha_0 = math.log(math.cos(self.cosine_s / (1. + self.cosine_s) * math.pi / 2.)) + self.schedule = schedule + if schedule == 'cosine': + # For the cosine schedule, T = 1 will have numerical issues. So we manually set the ending time T. + # Note that T = 0.9946 may be not the optimal setting. However, we find it works well. + self.T = 0.9946 + else: + self.T = 1. + + def marginal_log_mean_coeff(self, t): + """ + Compute log(alpha_t) of a given continuous-time label t in [0, T]. + """ + if self.schedule == 'discrete': + return interpolate_fn(t.reshape((-1, 1)), self.t_array.to(t.device), + self.log_alpha_array.to(t.device)).reshape((-1)) + elif self.schedule == 'linear': + return -0.25 * t ** 2 * (self.beta_1 - self.beta_0) - 0.5 * t * self.beta_0 + elif self.schedule == 'cosine': + log_alpha_fn = lambda s: torch.log(torch.cos((s + self.cosine_s) / (1. + self.cosine_s) * math.pi / 2.)) + log_alpha_t = log_alpha_fn(t) - self.cosine_log_alpha_0 + return log_alpha_t + + def marginal_alpha(self, t): + """ + Compute alpha_t of a given continuous-time label t in [0, T]. + """ + return torch.exp(self.marginal_log_mean_coeff(t)) + + def marginal_std(self, t): + """ + Compute sigma_t of a given continuous-time label t in [0, T]. + """ + return torch.sqrt(1. - torch.exp(2. * self.marginal_log_mean_coeff(t))) + + def marginal_lambda(self, t): + """ + Compute lambda_t = log(alpha_t) - log(sigma_t) of a given continuous-time label t in [0, T]. + """ + log_mean_coeff = self.marginal_log_mean_coeff(t) + log_std = 0.5 * torch.log(1. - torch.exp(2. * log_mean_coeff)) + return log_mean_coeff - log_std + + def inverse_lambda(self, lamb): + """ + Compute the continuous-time label t in [0, T] of a given half-logSNR lambda_t. + """ + if self.schedule == 'linear': + tmp = 2. * (self.beta_1 - self.beta_0) * torch.logaddexp(-2. * lamb, torch.zeros((1,)).to(lamb)) + Delta = self.beta_0 ** 2 + tmp + return tmp / (torch.sqrt(Delta) + self.beta_0) / (self.beta_1 - self.beta_0) + elif self.schedule == 'discrete': + log_alpha = -0.5 * torch.logaddexp(torch.zeros((1,)).to(lamb.device), -2. * lamb) + t = interpolate_fn(log_alpha.reshape((-1, 1)), torch.flip(self.log_alpha_array.to(lamb.device), [1]), + torch.flip(self.t_array.to(lamb.device), [1])) + return t.reshape((-1,)) + else: + log_alpha = -0.5 * torch.logaddexp(-2. * lamb, torch.zeros((1,)).to(lamb)) + t_fn = lambda log_alpha_t: torch.arccos(torch.exp(log_alpha_t + self.cosine_log_alpha_0)) * 2. * ( + 1. + self.cosine_s) / math.pi - self.cosine_s + t = t_fn(log_alpha) + return t + + +def model_wrapper( + model, + noise_schedule, + model_type="noise", + model_kwargs={}, + guidance_type="uncond", + condition=None, + unconditional_condition=None, + guidance_scale=1., + classifier_fn=None, + classifier_kwargs={}, +): + """Create a wrapper function for the noise prediction model. + DPM-Solver needs to solve the continuous-time diffusion ODEs. For DPMs trained on discrete-time labels, we need to + firstly wrap the model function to a noise prediction model that accepts the continuous time as the input. + We support four types of the diffusion model by setting `model_type`: + 1. "noise": noise prediction model. (Trained by predicting noise). + 2. "x_start": data prediction model. (Trained by predicting the data x_0 at time 0). + 3. "v": velocity prediction model. (Trained by predicting the velocity). + The "v" prediction is derivation detailed in Appendix D of [1], and is used in Imagen-Video [2]. + [1] Salimans, Tim, and Jonathan Ho. "Progressive distillation for fast sampling of diffusion models." + arXiv preprint arXiv:2202.00512 (2022). + [2] Ho, Jonathan, et al. "Imagen Video: High Definition Video Generation with Diffusion Models." + arXiv preprint arXiv:2210.02303 (2022). + + 4. "score": marginal score function. (Trained by denoising score matching). + Note that the score function and the noise prediction model follows a simple relationship: + ``` + noise(x_t, t) = -sigma_t * score(x_t, t) + ``` + We support three types of guided sampling by DPMs by setting `guidance_type`: + 1. "uncond": unconditional sampling by DPMs. + The input `model` has the following format: + `` + model(x, t_input, **model_kwargs) -> noise | x_start | v | score + `` + 2. "classifier": classifier guidance sampling [3] by DPMs and another classifier. + The input `model` has the following format: + `` + model(x, t_input, **model_kwargs) -> noise | x_start | v | score + `` + The input `classifier_fn` has the following format: + `` + classifier_fn(x, t_input, cond, **classifier_kwargs) -> logits(x, t_input, cond) + `` + [3] P. Dhariwal and A. Q. Nichol, "Diffusion models beat GANs on image synthesis," + in Advances in Neural Information Processing Systems, vol. 34, 2021, pp. 8780-8794. + 3. "classifier-free": classifier-free guidance sampling by conditional DPMs. + The input `model` has the following format: + `` + model(x, t_input, cond, **model_kwargs) -> noise | x_start | v | score + `` + And if cond == `unconditional_condition`, the model output is the unconditional DPM output. + [4] Ho, Jonathan, and Tim Salimans. "Classifier-free diffusion guidance." + arXiv preprint arXiv:2207.12598 (2022). + + The `t_input` is the time label of the model, which may be discrete-time labels (i.e. 0 to 999) + or continuous-time labels (i.e. epsilon to T). + We wrap the model function to accept only `x` and `t_continuous` as inputs, and outputs the predicted noise: + `` + def model_fn(x, t_continuous) -> noise: + t_input = get_model_input_time(t_continuous) + return noise_pred(model, x, t_input, **model_kwargs) + `` + where `t_continuous` is the continuous time labels (i.e. epsilon to T). And we use `model_fn` for DPM-Solver. + =============================================================== + Args: + model: A diffusion model with the corresponding format described above. + noise_schedule: A noise schedule object, such as NoiseScheduleVP. + model_type: A `str`. The parameterization type of the diffusion model. + "noise" or "x_start" or "v" or "score". + model_kwargs: A `dict`. A dict for the other inputs of the model function. + guidance_type: A `str`. The type of the guidance for sampling. + "uncond" or "classifier" or "classifier-free". + condition: A pytorch tensor. The condition for the guided sampling. + Only used for "classifier" or "classifier-free" guidance type. + unconditional_condition: A pytorch tensor. The condition for the unconditional sampling. + Only used for "classifier-free" guidance type. + guidance_scale: A `float`. The scale for the guided sampling. + classifier_fn: A classifier function. Only used for the classifier guidance. + classifier_kwargs: A `dict`. A dict for the other inputs of the classifier function. + Returns: + A noise prediction model that accepts the noised data and the continuous time as the inputs. + """ + + def get_model_input_time(t_continuous): + """ + Convert the continuous-time `t_continuous` (in [epsilon, T]) to the model input time. + For discrete-time DPMs, we convert `t_continuous` in [1 / N, 1] to `t_input` in [0, 1000 * (N - 1) / N]. + For continuous-time DPMs, we just use `t_continuous`. + """ + if noise_schedule.schedule == 'discrete': + return (t_continuous - 1. / noise_schedule.total_N) * 1000. + else: + return t_continuous + + def noise_pred_fn(x, t_continuous, cond=None): + if t_continuous.reshape((-1,)).shape[0] == 1: + t_continuous = t_continuous.expand((x.shape[0])) + t_input = get_model_input_time(t_continuous) + if cond is None: + output = model(x, t_input, **model_kwargs) + else: + output = model(x, t_input, cond, **model_kwargs) + if model_type == "noise": + return output + elif model_type == "x_start": + alpha_t, sigma_t = noise_schedule.marginal_alpha(t_continuous), noise_schedule.marginal_std(t_continuous) + dims = x.dim() + return (x - expand_dims(alpha_t, dims) * output) / expand_dims(sigma_t, dims) + elif model_type == "v": + alpha_t, sigma_t = noise_schedule.marginal_alpha(t_continuous), noise_schedule.marginal_std(t_continuous) + dims = x.dim() + return expand_dims(alpha_t, dims) * output + expand_dims(sigma_t, dims) * x + elif model_type == "score": + sigma_t = noise_schedule.marginal_std(t_continuous) + dims = x.dim() + return -expand_dims(sigma_t, dims) * output + + def cond_grad_fn(x, t_input): + """ + Compute the gradient of the classifier, i.e. nabla_{x} log p_t(cond | x_t). + """ + with torch.enable_grad(): + x_in = x.detach().requires_grad_(True) + log_prob = classifier_fn(x_in, t_input, condition, **classifier_kwargs) + return torch.autograd.grad(log_prob.sum(), x_in)[0] + + def model_fn(x, t_continuous): + """ + The noise predicition model function that is used for DPM-Solver. + """ + if t_continuous.reshape((-1,)).shape[0] == 1: + t_continuous = t_continuous.expand((x.shape[0])) + if guidance_type == "uncond": + return noise_pred_fn(x, t_continuous) + elif guidance_type == "classifier": + assert classifier_fn is not None + t_input = get_model_input_time(t_continuous) + cond_grad = cond_grad_fn(x, t_input) + sigma_t = noise_schedule.marginal_std(t_continuous) + noise = noise_pred_fn(x, t_continuous) + return noise - guidance_scale * expand_dims(sigma_t, dims=cond_grad.dim()) * cond_grad + elif guidance_type == "classifier-free": + if guidance_scale == 1. or unconditional_condition is None: + return noise_pred_fn(x, t_continuous, cond=condition) + else: + x_in = torch.cat([x] * 2) + t_in = torch.cat([t_continuous] * 2) + if isinstance(condition, dict): + assert isinstance(unconditional_condition, dict) + c_in = dict() + for k in condition: + if isinstance(condition[k], list): + c_in[k] = [torch.cat([unconditional_condition[k][i], condition[k][i]]) for i in range(len(condition[k]))] + else: + c_in[k] = torch.cat([unconditional_condition[k], condition[k]]) + else: + c_in = torch.cat([unconditional_condition, condition]) + noise_uncond, noise = noise_pred_fn(x_in, t_in, cond=c_in).chunk(2) + return noise_uncond + guidance_scale * (noise - noise_uncond) + + assert model_type in ["noise", "x_start", "v"] + assert guidance_type in ["uncond", "classifier", "classifier-free"] + return model_fn + + +class DPM_Solver: + def __init__(self, model_fn, noise_schedule, predict_x0=False, thresholding=False, max_val=1.): + """Construct a DPM-Solver. + We support both the noise prediction model ("predicting epsilon") and the data prediction model ("predicting x0"). + If `predict_x0` is False, we use the solver for the noise prediction model (DPM-Solver). + If `predict_x0` is True, we use the solver for the data prediction model (DPM-Solver++). + In such case, we further support the "dynamic thresholding" in [1] when `thresholding` is True. + The "dynamic thresholding" can greatly improve the sample quality for pixel-space DPMs with large guidance scales. + Args: + model_fn: A noise prediction model function which accepts the continuous-time input (t in [epsilon, T]): + `` + def model_fn(x, t_continuous): + return noise + `` + noise_schedule: A noise schedule object, such as NoiseScheduleVP. + predict_x0: A `bool`. If true, use the data prediction model; else, use the noise prediction model. + thresholding: A `bool`. Valid when `predict_x0` is True. Whether to use the "dynamic thresholding" in [1]. + max_val: A `float`. Valid when both `predict_x0` and `thresholding` are True. The max value for thresholding. + + [1] Chitwan Saharia, William Chan, Saurabh Saxena, Lala Li, Jay Whang, Emily Denton, Seyed Kamyar Seyed Ghasemipour, Burcu Karagol Ayan, S Sara Mahdavi, Rapha Gontijo Lopes, et al. Photorealistic text-to-image diffusion models with deep language understanding. arXiv preprint arXiv:2205.11487, 2022b. + """ + self.model = model_fn + self.noise_schedule = noise_schedule + self.predict_x0 = predict_x0 + self.thresholding = thresholding + self.max_val = max_val + + def noise_prediction_fn(self, x, t): + """ + Return the noise prediction model. + """ + return self.model(x, t) + + def data_prediction_fn(self, x, t): + """ + Return the data prediction model (with thresholding). + """ + noise = self.noise_prediction_fn(x, t) + dims = x.dim() + alpha_t, sigma_t = self.noise_schedule.marginal_alpha(t), self.noise_schedule.marginal_std(t) + x0 = (x - expand_dims(sigma_t, dims) * noise) / expand_dims(alpha_t, dims) + if self.thresholding: + p = 0.995 # A hyperparameter in the paper of "Imagen" [1]. + s = torch.quantile(torch.abs(x0).reshape((x0.shape[0], -1)), p, dim=1) + s = expand_dims(torch.maximum(s, self.max_val * torch.ones_like(s).to(s.device)), dims) + x0 = torch.clamp(x0, -s, s) / s + return x0 + + def model_fn(self, x, t): + """ + Convert the model to the noise prediction model or the data prediction model. + """ + if self.predict_x0: + return self.data_prediction_fn(x, t) + else: + return self.noise_prediction_fn(x, t) + + def get_time_steps(self, skip_type, t_T, t_0, N, device): + """Compute the intermediate time steps for sampling. + Args: + skip_type: A `str`. The type for the spacing of the time steps. We support three types: + - 'logSNR': uniform logSNR for the time steps. + - 'time_uniform': uniform time for the time steps. (**Recommended for high-resolutional data**.) + - 'time_quadratic': quadratic time for the time steps. (Used in DDIM for low-resolutional data.) + t_T: A `float`. The starting time of the sampling (default is T). + t_0: A `float`. The ending time of the sampling (default is epsilon). + N: A `int`. The total number of the spacing of the time steps. + device: A torch device. + Returns: + A pytorch tensor of the time steps, with the shape (N + 1,). + """ + if skip_type == 'logSNR': + lambda_T = self.noise_schedule.marginal_lambda(torch.tensor(t_T).to(device)) + lambda_0 = self.noise_schedule.marginal_lambda(torch.tensor(t_0).to(device)) + logSNR_steps = torch.linspace(lambda_T.cpu().item(), lambda_0.cpu().item(), N + 1).to(device) + return self.noise_schedule.inverse_lambda(logSNR_steps) + elif skip_type == 'time_uniform': + return torch.linspace(t_T, t_0, N + 1).to(device) + elif skip_type == 'time_quadratic': + t_order = 2 + t = torch.linspace(t_T ** (1. / t_order), t_0 ** (1. / t_order), N + 1).pow(t_order).to(device) + return t + else: + raise ValueError( + "Unsupported skip_type {}, need to be 'logSNR' or 'time_uniform' or 'time_quadratic'".format(skip_type)) + + def get_orders_and_timesteps_for_singlestep_solver(self, steps, order, skip_type, t_T, t_0, device): + """ + Get the order of each step for sampling by the singlestep DPM-Solver. + We combine both DPM-Solver-1,2,3 to use all the function evaluations, which is named as "DPM-Solver-fast". + Given a fixed number of function evaluations by `steps`, the sampling procedure by DPM-Solver-fast is: + - If order == 1: + We take `steps` of DPM-Solver-1 (i.e. DDIM). + - If order == 2: + - Denote K = (steps // 2). We take K or (K + 1) intermediate time steps for sampling. + - If steps % 2 == 0, we use K steps of DPM-Solver-2. + - If steps % 2 == 1, we use K steps of DPM-Solver-2 and 1 step of DPM-Solver-1. + - If order == 3: + - Denote K = (steps // 3 + 1). We take K intermediate time steps for sampling. + - If steps % 3 == 0, we use (K - 2) steps of DPM-Solver-3, and 1 step of DPM-Solver-2 and 1 step of DPM-Solver-1. + - If steps % 3 == 1, we use (K - 1) steps of DPM-Solver-3 and 1 step of DPM-Solver-1. + - If steps % 3 == 2, we use (K - 1) steps of DPM-Solver-3 and 1 step of DPM-Solver-2. + ============================================ + Args: + order: A `int`. The max order for the solver (2 or 3). + steps: A `int`. The total number of function evaluations (NFE). + skip_type: A `str`. The type for the spacing of the time steps. We support three types: + - 'logSNR': uniform logSNR for the time steps. + - 'time_uniform': uniform time for the time steps. (**Recommended for high-resolutional data**.) + - 'time_quadratic': quadratic time for the time steps. (Used in DDIM for low-resolutional data.) + t_T: A `float`. The starting time of the sampling (default is T). + t_0: A `float`. The ending time of the sampling (default is epsilon). + device: A torch device. + Returns: + orders: A list of the solver order of each step. + """ + if order == 3: + K = steps // 3 + 1 + if steps % 3 == 0: + orders = [3, ] * (K - 2) + [2, 1] + elif steps % 3 == 1: + orders = [3, ] * (K - 1) + [1] + else: + orders = [3, ] * (K - 1) + [2] + elif order == 2: + if steps % 2 == 0: + K = steps // 2 + orders = [2, ] * K + else: + K = steps // 2 + 1 + orders = [2, ] * (K - 1) + [1] + elif order == 1: + K = 1 + orders = [1, ] * steps + else: + raise ValueError("'order' must be '1' or '2' or '3'.") + if skip_type == 'logSNR': + # To reproduce the results in DPM-Solver paper + timesteps_outer = self.get_time_steps(skip_type, t_T, t_0, K, device) + else: + timesteps_outer = self.get_time_steps(skip_type, t_T, t_0, steps, device)[ + torch.cumsum(torch.tensor([0, ] + orders)).to(device)] + return timesteps_outer, orders + + def denoise_to_zero_fn(self, x, s): + """ + Denoise at the final step, which is equivalent to solve the ODE from lambda_s to infty by first-order discretization. + """ + return self.data_prediction_fn(x, s) + + def dpm_solver_first_update(self, x, s, t, model_s=None, return_intermediate=False): + """ + DPM-Solver-1 (equivalent to DDIM) from time `s` to time `t`. + Args: + x: A pytorch tensor. The initial value at time `s`. + s: A pytorch tensor. The starting time, with the shape (x.shape[0],). + t: A pytorch tensor. The ending time, with the shape (x.shape[0],). + model_s: A pytorch tensor. The model function evaluated at time `s`. + If `model_s` is None, we evaluate the model by `x` and `s`; otherwise we directly use it. + return_intermediate: A `bool`. If true, also return the model value at time `s`. + Returns: + x_t: A pytorch tensor. The approximated solution at time `t`. + """ + ns = self.noise_schedule + dims = x.dim() + lambda_s, lambda_t = ns.marginal_lambda(s), ns.marginal_lambda(t) + h = lambda_t - lambda_s + log_alpha_s, log_alpha_t = ns.marginal_log_mean_coeff(s), ns.marginal_log_mean_coeff(t) + sigma_s, sigma_t = ns.marginal_std(s), ns.marginal_std(t) + alpha_t = torch.exp(log_alpha_t) + + if self.predict_x0: + phi_1 = torch.expm1(-h) + if model_s is None: + model_s = self.model_fn(x, s) + x_t = ( + expand_dims(sigma_t / sigma_s, dims) * x + - expand_dims(alpha_t * phi_1, dims) * model_s + ) + if return_intermediate: + return x_t, {'model_s': model_s} + else: + return x_t + else: + phi_1 = torch.expm1(h) + if model_s is None: + model_s = self.model_fn(x, s) + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_s), dims) * x + - expand_dims(sigma_t * phi_1, dims) * model_s + ) + if return_intermediate: + return x_t, {'model_s': model_s} + else: + return x_t + + def singlestep_dpm_solver_second_update(self, x, s, t, r1=0.5, model_s=None, return_intermediate=False, + solver_type='dpm_solver'): + """ + Singlestep solver DPM-Solver-2 from time `s` to time `t`. + Args: + x: A pytorch tensor. The initial value at time `s`. + s: A pytorch tensor. The starting time, with the shape (x.shape[0],). + t: A pytorch tensor. The ending time, with the shape (x.shape[0],). + r1: A `float`. The hyperparameter of the second-order solver. + model_s: A pytorch tensor. The model function evaluated at time `s`. + If `model_s` is None, we evaluate the model by `x` and `s`; otherwise we directly use it. + return_intermediate: A `bool`. If true, also return the model value at time `s` and `s1` (the intermediate time). + solver_type: either 'dpm_solver' or 'taylor'. The type for the high-order solvers. + The type slightly impacts the performance. We recommend to use 'dpm_solver' type. + Returns: + x_t: A pytorch tensor. The approximated solution at time `t`. + """ + if solver_type not in ['dpm_solver', 'taylor']: + raise ValueError("'solver_type' must be either 'dpm_solver' or 'taylor', got {}".format(solver_type)) + if r1 is None: + r1 = 0.5 + ns = self.noise_schedule + dims = x.dim() + lambda_s, lambda_t = ns.marginal_lambda(s), ns.marginal_lambda(t) + h = lambda_t - lambda_s + lambda_s1 = lambda_s + r1 * h + s1 = ns.inverse_lambda(lambda_s1) + log_alpha_s, log_alpha_s1, log_alpha_t = ns.marginal_log_mean_coeff(s), ns.marginal_log_mean_coeff( + s1), ns.marginal_log_mean_coeff(t) + sigma_s, sigma_s1, sigma_t = ns.marginal_std(s), ns.marginal_std(s1), ns.marginal_std(t) + alpha_s1, alpha_t = torch.exp(log_alpha_s1), torch.exp(log_alpha_t) + + if self.predict_x0: + phi_11 = torch.expm1(-r1 * h) + phi_1 = torch.expm1(-h) + + if model_s is None: + model_s = self.model_fn(x, s) + x_s1 = ( + expand_dims(sigma_s1 / sigma_s, dims) * x + - expand_dims(alpha_s1 * phi_11, dims) * model_s + ) + model_s1 = self.model_fn(x_s1, s1) + if solver_type == 'dpm_solver': + x_t = ( + expand_dims(sigma_t / sigma_s, dims) * x + - expand_dims(alpha_t * phi_1, dims) * model_s + - (0.5 / r1) * expand_dims(alpha_t * phi_1, dims) * (model_s1 - model_s) + ) + elif solver_type == 'taylor': + x_t = ( + expand_dims(sigma_t / sigma_s, dims) * x + - expand_dims(alpha_t * phi_1, dims) * model_s + + (1. / r1) * expand_dims(alpha_t * ((torch.exp(-h) - 1.) / h + 1.), dims) * ( + model_s1 - model_s) + ) + else: + phi_11 = torch.expm1(r1 * h) + phi_1 = torch.expm1(h) + + if model_s is None: + model_s = self.model_fn(x, s) + x_s1 = ( + expand_dims(torch.exp(log_alpha_s1 - log_alpha_s), dims) * x + - expand_dims(sigma_s1 * phi_11, dims) * model_s + ) + model_s1 = self.model_fn(x_s1, s1) + if solver_type == 'dpm_solver': + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_s), dims) * x + - expand_dims(sigma_t * phi_1, dims) * model_s + - (0.5 / r1) * expand_dims(sigma_t * phi_1, dims) * (model_s1 - model_s) + ) + elif solver_type == 'taylor': + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_s), dims) * x + - expand_dims(sigma_t * phi_1, dims) * model_s + - (1. / r1) * expand_dims(sigma_t * ((torch.exp(h) - 1.) / h - 1.), dims) * (model_s1 - model_s) + ) + if return_intermediate: + return x_t, {'model_s': model_s, 'model_s1': model_s1} + else: + return x_t + + def singlestep_dpm_solver_third_update(self, x, s, t, r1=1. / 3., r2=2. / 3., model_s=None, model_s1=None, + return_intermediate=False, solver_type='dpm_solver'): + """ + Singlestep solver DPM-Solver-3 from time `s` to time `t`. + Args: + x: A pytorch tensor. The initial value at time `s`. + s: A pytorch tensor. The starting time, with the shape (x.shape[0],). + t: A pytorch tensor. The ending time, with the shape (x.shape[0],). + r1: A `float`. The hyperparameter of the third-order solver. + r2: A `float`. The hyperparameter of the third-order solver. + model_s: A pytorch tensor. The model function evaluated at time `s`. + If `model_s` is None, we evaluate the model by `x` and `s`; otherwise we directly use it. + model_s1: A pytorch tensor. The model function evaluated at time `s1` (the intermediate time given by `r1`). + If `model_s1` is None, we evaluate the model at `s1`; otherwise we directly use it. + return_intermediate: A `bool`. If true, also return the model value at time `s`, `s1` and `s2` (the intermediate times). + solver_type: either 'dpm_solver' or 'taylor'. The type for the high-order solvers. + The type slightly impacts the performance. We recommend to use 'dpm_solver' type. + Returns: + x_t: A pytorch tensor. The approximated solution at time `t`. + """ + if solver_type not in ['dpm_solver', 'taylor']: + raise ValueError("'solver_type' must be either 'dpm_solver' or 'taylor', got {}".format(solver_type)) + if r1 is None: + r1 = 1. / 3. + if r2 is None: + r2 = 2. / 3. + ns = self.noise_schedule + dims = x.dim() + lambda_s, lambda_t = ns.marginal_lambda(s), ns.marginal_lambda(t) + h = lambda_t - lambda_s + lambda_s1 = lambda_s + r1 * h + lambda_s2 = lambda_s + r2 * h + s1 = ns.inverse_lambda(lambda_s1) + s2 = ns.inverse_lambda(lambda_s2) + log_alpha_s, log_alpha_s1, log_alpha_s2, log_alpha_t = ns.marginal_log_mean_coeff( + s), ns.marginal_log_mean_coeff(s1), ns.marginal_log_mean_coeff(s2), ns.marginal_log_mean_coeff(t) + sigma_s, sigma_s1, sigma_s2, sigma_t = ns.marginal_std(s), ns.marginal_std(s1), ns.marginal_std( + s2), ns.marginal_std(t) + alpha_s1, alpha_s2, alpha_t = torch.exp(log_alpha_s1), torch.exp(log_alpha_s2), torch.exp(log_alpha_t) + + if self.predict_x0: + phi_11 = torch.expm1(-r1 * h) + phi_12 = torch.expm1(-r2 * h) + phi_1 = torch.expm1(-h) + phi_22 = torch.expm1(-r2 * h) / (r2 * h) + 1. + phi_2 = phi_1 / h + 1. + phi_3 = phi_2 / h - 0.5 + + if model_s is None: + model_s = self.model_fn(x, s) + if model_s1 is None: + x_s1 = ( + expand_dims(sigma_s1 / sigma_s, dims) * x + - expand_dims(alpha_s1 * phi_11, dims) * model_s + ) + model_s1 = self.model_fn(x_s1, s1) + x_s2 = ( + expand_dims(sigma_s2 / sigma_s, dims) * x + - expand_dims(alpha_s2 * phi_12, dims) * model_s + + r2 / r1 * expand_dims(alpha_s2 * phi_22, dims) * (model_s1 - model_s) + ) + model_s2 = self.model_fn(x_s2, s2) + if solver_type == 'dpm_solver': + x_t = ( + expand_dims(sigma_t / sigma_s, dims) * x + - expand_dims(alpha_t * phi_1, dims) * model_s + + (1. / r2) * expand_dims(alpha_t * phi_2, dims) * (model_s2 - model_s) + ) + elif solver_type == 'taylor': + D1_0 = (1. / r1) * (model_s1 - model_s) + D1_1 = (1. / r2) * (model_s2 - model_s) + D1 = (r2 * D1_0 - r1 * D1_1) / (r2 - r1) + D2 = 2. * (D1_1 - D1_0) / (r2 - r1) + x_t = ( + expand_dims(sigma_t / sigma_s, dims) * x + - expand_dims(alpha_t * phi_1, dims) * model_s + + expand_dims(alpha_t * phi_2, dims) * D1 + - expand_dims(alpha_t * phi_3, dims) * D2 + ) + else: + phi_11 = torch.expm1(r1 * h) + phi_12 = torch.expm1(r2 * h) + phi_1 = torch.expm1(h) + phi_22 = torch.expm1(r2 * h) / (r2 * h) - 1. + phi_2 = phi_1 / h - 1. + phi_3 = phi_2 / h - 0.5 + + if model_s is None: + model_s = self.model_fn(x, s) + if model_s1 is None: + x_s1 = ( + expand_dims(torch.exp(log_alpha_s1 - log_alpha_s), dims) * x + - expand_dims(sigma_s1 * phi_11, dims) * model_s + ) + model_s1 = self.model_fn(x_s1, s1) + x_s2 = ( + expand_dims(torch.exp(log_alpha_s2 - log_alpha_s), dims) * x + - expand_dims(sigma_s2 * phi_12, dims) * model_s + - r2 / r1 * expand_dims(sigma_s2 * phi_22, dims) * (model_s1 - model_s) + ) + model_s2 = self.model_fn(x_s2, s2) + if solver_type == 'dpm_solver': + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_s), dims) * x + - expand_dims(sigma_t * phi_1, dims) * model_s + - (1. / r2) * expand_dims(sigma_t * phi_2, dims) * (model_s2 - model_s) + ) + elif solver_type == 'taylor': + D1_0 = (1. / r1) * (model_s1 - model_s) + D1_1 = (1. / r2) * (model_s2 - model_s) + D1 = (r2 * D1_0 - r1 * D1_1) / (r2 - r1) + D2 = 2. * (D1_1 - D1_0) / (r2 - r1) + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_s), dims) * x + - expand_dims(sigma_t * phi_1, dims) * model_s + - expand_dims(sigma_t * phi_2, dims) * D1 + - expand_dims(sigma_t * phi_3, dims) * D2 + ) + + if return_intermediate: + return x_t, {'model_s': model_s, 'model_s1': model_s1, 'model_s2': model_s2} + else: + return x_t + + def multistep_dpm_solver_second_update(self, x, model_prev_list, t_prev_list, t, solver_type="dpm_solver"): + """ + Multistep solver DPM-Solver-2 from time `t_prev_list[-1]` to time `t`. + Args: + x: A pytorch tensor. The initial value at time `s`. + model_prev_list: A list of pytorch tensor. The previous computed model values. + t_prev_list: A list of pytorch tensor. The previous times, each time has the shape (x.shape[0],) + t: A pytorch tensor. The ending time, with the shape (x.shape[0],). + solver_type: either 'dpm_solver' or 'taylor'. The type for the high-order solvers. + The type slightly impacts the performance. We recommend to use 'dpm_solver' type. + Returns: + x_t: A pytorch tensor. The approximated solution at time `t`. + """ + if solver_type not in ['dpm_solver', 'taylor']: + raise ValueError("'solver_type' must be either 'dpm_solver' or 'taylor', got {}".format(solver_type)) + ns = self.noise_schedule + dims = x.dim() + model_prev_1, model_prev_0 = model_prev_list + t_prev_1, t_prev_0 = t_prev_list + lambda_prev_1, lambda_prev_0, lambda_t = ns.marginal_lambda(t_prev_1), ns.marginal_lambda( + t_prev_0), ns.marginal_lambda(t) + log_alpha_prev_0, log_alpha_t = ns.marginal_log_mean_coeff(t_prev_0), ns.marginal_log_mean_coeff(t) + sigma_prev_0, sigma_t = ns.marginal_std(t_prev_0), ns.marginal_std(t) + alpha_t = torch.exp(log_alpha_t) + + h_0 = lambda_prev_0 - lambda_prev_1 + h = lambda_t - lambda_prev_0 + r0 = h_0 / h + D1_0 = expand_dims(1. / r0, dims) * (model_prev_0 - model_prev_1) + if self.predict_x0: + if solver_type == 'dpm_solver': + x_t = ( + expand_dims(sigma_t / sigma_prev_0, dims) * x + - expand_dims(alpha_t * (torch.exp(-h) - 1.), dims) * model_prev_0 + - 0.5 * expand_dims(alpha_t * (torch.exp(-h) - 1.), dims) * D1_0 + ) + elif solver_type == 'taylor': + x_t = ( + expand_dims(sigma_t / sigma_prev_0, dims) * x + - expand_dims(alpha_t * (torch.exp(-h) - 1.), dims) * model_prev_0 + + expand_dims(alpha_t * ((torch.exp(-h) - 1.) / h + 1.), dims) * D1_0 + ) + else: + if solver_type == 'dpm_solver': + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_prev_0), dims) * x + - expand_dims(sigma_t * (torch.exp(h) - 1.), dims) * model_prev_0 + - 0.5 * expand_dims(sigma_t * (torch.exp(h) - 1.), dims) * D1_0 + ) + elif solver_type == 'taylor': + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_prev_0), dims) * x + - expand_dims(sigma_t * (torch.exp(h) - 1.), dims) * model_prev_0 + - expand_dims(sigma_t * ((torch.exp(h) - 1.) / h - 1.), dims) * D1_0 + ) + return x_t + + def multistep_dpm_solver_third_update(self, x, model_prev_list, t_prev_list, t, solver_type='dpm_solver'): + """ + Multistep solver DPM-Solver-3 from time `t_prev_list[-1]` to time `t`. + Args: + x: A pytorch tensor. The initial value at time `s`. + model_prev_list: A list of pytorch tensor. The previous computed model values. + t_prev_list: A list of pytorch tensor. The previous times, each time has the shape (x.shape[0],) + t: A pytorch tensor. The ending time, with the shape (x.shape[0],). + solver_type: either 'dpm_solver' or 'taylor'. The type for the high-order solvers. + The type slightly impacts the performance. We recommend to use 'dpm_solver' type. + Returns: + x_t: A pytorch tensor. The approximated solution at time `t`. + """ + ns = self.noise_schedule + dims = x.dim() + model_prev_2, model_prev_1, model_prev_0 = model_prev_list + t_prev_2, t_prev_1, t_prev_0 = t_prev_list + lambda_prev_2, lambda_prev_1, lambda_prev_0, lambda_t = ns.marginal_lambda(t_prev_2), ns.marginal_lambda( + t_prev_1), ns.marginal_lambda(t_prev_0), ns.marginal_lambda(t) + log_alpha_prev_0, log_alpha_t = ns.marginal_log_mean_coeff(t_prev_0), ns.marginal_log_mean_coeff(t) + sigma_prev_0, sigma_t = ns.marginal_std(t_prev_0), ns.marginal_std(t) + alpha_t = torch.exp(log_alpha_t) + + h_1 = lambda_prev_1 - lambda_prev_2 + h_0 = lambda_prev_0 - lambda_prev_1 + h = lambda_t - lambda_prev_0 + r0, r1 = h_0 / h, h_1 / h + D1_0 = expand_dims(1. / r0, dims) * (model_prev_0 - model_prev_1) + D1_1 = expand_dims(1. / r1, dims) * (model_prev_1 - model_prev_2) + D1 = D1_0 + expand_dims(r0 / (r0 + r1), dims) * (D1_0 - D1_1) + D2 = expand_dims(1. / (r0 + r1), dims) * (D1_0 - D1_1) + if self.predict_x0: + x_t = ( + expand_dims(sigma_t / sigma_prev_0, dims) * x + - expand_dims(alpha_t * (torch.exp(-h) - 1.), dims) * model_prev_0 + + expand_dims(alpha_t * ((torch.exp(-h) - 1.) / h + 1.), dims) * D1 + - expand_dims(alpha_t * ((torch.exp(-h) - 1. + h) / h ** 2 - 0.5), dims) * D2 + ) + else: + x_t = ( + expand_dims(torch.exp(log_alpha_t - log_alpha_prev_0), dims) * x + - expand_dims(sigma_t * (torch.exp(h) - 1.), dims) * model_prev_0 + - expand_dims(sigma_t * ((torch.exp(h) - 1.) / h - 1.), dims) * D1 + - expand_dims(sigma_t * ((torch.exp(h) - 1. - h) / h ** 2 - 0.5), dims) * D2 + ) + return x_t + + def singlestep_dpm_solver_update(self, x, s, t, order, return_intermediate=False, solver_type='dpm_solver', r1=None, + r2=None): + """ + Singlestep DPM-Solver with the order `order` from time `s` to time `t`. + Args: + x: A pytorch tensor. The initial value at time `s`. + s: A pytorch tensor. The starting time, with the shape (x.shape[0],). + t: A pytorch tensor. The ending time, with the shape (x.shape[0],). + order: A `int`. The order of DPM-Solver. We only support order == 1 or 2 or 3. + return_intermediate: A `bool`. If true, also return the model value at time `s`, `s1` and `s2` (the intermediate times). + solver_type: either 'dpm_solver' or 'taylor'. The type for the high-order solvers. + The type slightly impacts the performance. We recommend to use 'dpm_solver' type. + r1: A `float`. The hyperparameter of the second-order or third-order solver. + r2: A `float`. The hyperparameter of the third-order solver. + Returns: + x_t: A pytorch tensor. The approximated solution at time `t`. + """ + if order == 1: + return self.dpm_solver_first_update(x, s, t, return_intermediate=return_intermediate) + elif order == 2: + return self.singlestep_dpm_solver_second_update(x, s, t, return_intermediate=return_intermediate, + solver_type=solver_type, r1=r1) + elif order == 3: + return self.singlestep_dpm_solver_third_update(x, s, t, return_intermediate=return_intermediate, + solver_type=solver_type, r1=r1, r2=r2) + else: + raise ValueError("Solver order must be 1 or 2 or 3, got {}".format(order)) + + def multistep_dpm_solver_update(self, x, model_prev_list, t_prev_list, t, order, solver_type='dpm_solver'): + """ + Multistep DPM-Solver with the order `order` from time `t_prev_list[-1]` to time `t`. + Args: + x: A pytorch tensor. The initial value at time `s`. + model_prev_list: A list of pytorch tensor. The previous computed model values. + t_prev_list: A list of pytorch tensor. The previous times, each time has the shape (x.shape[0],) + t: A pytorch tensor. The ending time, with the shape (x.shape[0],). + order: A `int`. The order of DPM-Solver. We only support order == 1 or 2 or 3. + solver_type: either 'dpm_solver' or 'taylor'. The type for the high-order solvers. + The type slightly impacts the performance. We recommend to use 'dpm_solver' type. + Returns: + x_t: A pytorch tensor. The approximated solution at time `t`. + """ + if order == 1: + return self.dpm_solver_first_update(x, t_prev_list[-1], t, model_s=model_prev_list[-1]) + elif order == 2: + return self.multistep_dpm_solver_second_update(x, model_prev_list, t_prev_list, t, solver_type=solver_type) + elif order == 3: + return self.multistep_dpm_solver_third_update(x, model_prev_list, t_prev_list, t, solver_type=solver_type) + else: + raise ValueError("Solver order must be 1 or 2 or 3, got {}".format(order)) + + def dpm_solver_adaptive(self, x, order, t_T, t_0, h_init=0.05, atol=0.0078, rtol=0.05, theta=0.9, t_err=1e-5, + solver_type='dpm_solver'): + """ + The adaptive step size solver based on singlestep DPM-Solver. + Args: + x: A pytorch tensor. The initial value at time `t_T`. + order: A `int`. The (higher) order of the solver. We only support order == 2 or 3. + t_T: A `float`. The starting time of the sampling (default is T). + t_0: A `float`. The ending time of the sampling (default is epsilon). + h_init: A `float`. The initial step size (for logSNR). + atol: A `float`. The absolute tolerance of the solver. For image data, the default setting is 0.0078, followed [1]. + rtol: A `float`. The relative tolerance of the solver. The default setting is 0.05. + theta: A `float`. The safety hyperparameter for adapting the step size. The default setting is 0.9, followed [1]. + t_err: A `float`. The tolerance for the time. We solve the diffusion ODE until the absolute error between the + current time and `t_0` is less than `t_err`. The default setting is 1e-5. + solver_type: either 'dpm_solver' or 'taylor'. The type for the high-order solvers. + The type slightly impacts the performance. We recommend to use 'dpm_solver' type. + Returns: + x_0: A pytorch tensor. The approximated solution at time `t_0`. + [1] A. Jolicoeur-Martineau, K. Li, R. Piché-Taillefer, T. Kachman, and I. Mitliagkas, "Gotta go fast when generating data with score-based models," arXiv preprint arXiv:2105.14080, 2021. + """ + ns = self.noise_schedule + s = t_T * torch.ones((x.shape[0],)).to(x) + lambda_s = ns.marginal_lambda(s) + lambda_0 = ns.marginal_lambda(t_0 * torch.ones_like(s).to(x)) + h = h_init * torch.ones_like(s).to(x) + x_prev = x + nfe = 0 + if order == 2: + r1 = 0.5 + lower_update = lambda x, s, t: self.dpm_solver_first_update(x, s, t, return_intermediate=True) + higher_update = lambda x, s, t, **kwargs: self.singlestep_dpm_solver_second_update(x, s, t, r1=r1, + solver_type=solver_type, + **kwargs) + elif order == 3: + r1, r2 = 1. / 3., 2. / 3. + lower_update = lambda x, s, t: self.singlestep_dpm_solver_second_update(x, s, t, r1=r1, + return_intermediate=True, + solver_type=solver_type) + higher_update = lambda x, s, t, **kwargs: self.singlestep_dpm_solver_third_update(x, s, t, r1=r1, r2=r2, + solver_type=solver_type, + **kwargs) + else: + raise ValueError("For adaptive step size solver, order must be 2 or 3, got {}".format(order)) + while torch.abs((s - t_0)).mean() > t_err: + t = ns.inverse_lambda(lambda_s + h) + x_lower, lower_noise_kwargs = lower_update(x, s, t) + x_higher = higher_update(x, s, t, **lower_noise_kwargs) + delta = torch.max(torch.ones_like(x).to(x) * atol, rtol * torch.max(torch.abs(x_lower), torch.abs(x_prev))) + norm_fn = lambda v: torch.sqrt(torch.square(v.reshape((v.shape[0], -1))).mean(dim=-1, keepdim=True)) + E = norm_fn((x_higher - x_lower) / delta).max() + if torch.all(E <= 1.): + x = x_higher + s = t + x_prev = x_lower + lambda_s = ns.marginal_lambda(s) + h = torch.min(theta * h * torch.float_power(E, -1. / order).float(), lambda_0 - lambda_s) + nfe += order + print('adaptive solver nfe', nfe) + return x + + def sample(self, x, steps=20, t_start=None, t_end=None, order=3, skip_type='time_uniform', + method='singlestep', lower_order_final=True, denoise_to_zero=False, solver_type='dpm_solver', + atol=0.0078, rtol=0.05, + ): + """ + Compute the sample at time `t_end` by DPM-Solver, given the initial `x` at time `t_start`. + ===================================================== + We support the following algorithms for both noise prediction model and data prediction model: + - 'singlestep': + Singlestep DPM-Solver (i.e. "DPM-Solver-fast" in the paper), which combines different orders of singlestep DPM-Solver. + We combine all the singlestep solvers with order <= `order` to use up all the function evaluations (steps). + The total number of function evaluations (NFE) == `steps`. + Given a fixed NFE == `steps`, the sampling procedure is: + - If `order` == 1: + - Denote K = steps. We use K steps of DPM-Solver-1 (i.e. DDIM). + - If `order` == 2: + - Denote K = (steps // 2) + (steps % 2). We take K intermediate time steps for sampling. + - If steps % 2 == 0, we use K steps of singlestep DPM-Solver-2. + - If steps % 2 == 1, we use (K - 1) steps of singlestep DPM-Solver-2 and 1 step of DPM-Solver-1. + - If `order` == 3: + - Denote K = (steps // 3 + 1). We take K intermediate time steps for sampling. + - If steps % 3 == 0, we use (K - 2) steps of singlestep DPM-Solver-3, and 1 step of singlestep DPM-Solver-2 and 1 step of DPM-Solver-1. + - If steps % 3 == 1, we use (K - 1) steps of singlestep DPM-Solver-3 and 1 step of DPM-Solver-1. + - If steps % 3 == 2, we use (K - 1) steps of singlestep DPM-Solver-3 and 1 step of singlestep DPM-Solver-2. + - 'multistep': + Multistep DPM-Solver with the order of `order`. The total number of function evaluations (NFE) == `steps`. + We initialize the first `order` values by lower order multistep solvers. + Given a fixed NFE == `steps`, the sampling procedure is: + Denote K = steps. + - If `order` == 1: + - We use K steps of DPM-Solver-1 (i.e. DDIM). + - If `order` == 2: + - We firstly use 1 step of DPM-Solver-1, then use (K - 1) step of multistep DPM-Solver-2. + - If `order` == 3: + - We firstly use 1 step of DPM-Solver-1, then 1 step of multistep DPM-Solver-2, then (K - 2) step of multistep DPM-Solver-3. + - 'singlestep_fixed': + Fixed order singlestep DPM-Solver (i.e. DPM-Solver-1 or singlestep DPM-Solver-2 or singlestep DPM-Solver-3). + We use singlestep DPM-Solver-`order` for `order`=1 or 2 or 3, with total [`steps` // `order`] * `order` NFE. + - 'adaptive': + Adaptive step size DPM-Solver (i.e. "DPM-Solver-12" and "DPM-Solver-23" in the paper). + We ignore `steps` and use adaptive step size DPM-Solver with a higher order of `order`. + You can adjust the absolute tolerance `atol` and the relative tolerance `rtol` to balance the computatation costs + (NFE) and the sample quality. + - If `order` == 2, we use DPM-Solver-12 which combines DPM-Solver-1 and singlestep DPM-Solver-2. + - If `order` == 3, we use DPM-Solver-23 which combines singlestep DPM-Solver-2 and singlestep DPM-Solver-3. + ===================================================== + Some advices for choosing the algorithm: + - For **unconditional sampling** or **guided sampling with small guidance scale** by DPMs: + Use singlestep DPM-Solver ("DPM-Solver-fast" in the paper) with `order = 3`. + e.g. + >>> dpm_solver = DPM_Solver(model_fn, noise_schedule, predict_x0=False) + >>> x_sample = dpm_solver.sample(x, steps=steps, t_start=t_start, t_end=t_end, order=3, + skip_type='time_uniform', method='singlestep') + - For **guided sampling with large guidance scale** by DPMs: + Use multistep DPM-Solver with `predict_x0 = True` and `order = 2`. + e.g. + >>> dpm_solver = DPM_Solver(model_fn, noise_schedule, predict_x0=True) + >>> x_sample = dpm_solver.sample(x, steps=steps, t_start=t_start, t_end=t_end, order=2, + skip_type='time_uniform', method='multistep') + We support three types of `skip_type`: + - 'logSNR': uniform logSNR for the time steps. **Recommended for low-resolutional images** + - 'time_uniform': uniform time for the time steps. **Recommended for high-resolutional images**. + - 'time_quadratic': quadratic time for the time steps. + ===================================================== + Args: + x: A pytorch tensor. The initial value at time `t_start` + e.g. if `t_start` == T, then `x` is a sample from the standard normal distribution. + steps: A `int`. The total number of function evaluations (NFE). + t_start: A `float`. The starting time of the sampling. + If `T` is None, we use self.noise_schedule.T (default is 1.0). + t_end: A `float`. The ending time of the sampling. + If `t_end` is None, we use 1. / self.noise_schedule.total_N. + e.g. if total_N == 1000, we have `t_end` == 1e-3. + For discrete-time DPMs: + - We recommend `t_end` == 1. / self.noise_schedule.total_N. + For continuous-time DPMs: + - We recommend `t_end` == 1e-3 when `steps` <= 15; and `t_end` == 1e-4 when `steps` > 15. + order: A `int`. The order of DPM-Solver. + skip_type: A `str`. The type for the spacing of the time steps. 'time_uniform' or 'logSNR' or 'time_quadratic'. + method: A `str`. The method for sampling. 'singlestep' or 'multistep' or 'singlestep_fixed' or 'adaptive'. + denoise_to_zero: A `bool`. Whether to denoise to time 0 at the final step. + Default is `False`. If `denoise_to_zero` is `True`, the total NFE is (`steps` + 1). + This trick is firstly proposed by DDPM (https://arxiv.org/abs/2006.11239) and + score_sde (https://arxiv.org/abs/2011.13456). Such trick can improve the FID + for diffusion models sampling by diffusion SDEs for low-resolutional images + (such as CIFAR-10). However, we observed that such trick does not matter for + high-resolutional images. As it needs an additional NFE, we do not recommend + it for high-resolutional images. + lower_order_final: A `bool`. Whether to use lower order solvers at the final steps. + Only valid for `method=multistep` and `steps < 15`. We empirically find that + this trick is a key to stabilizing the sampling by DPM-Solver with very few steps + (especially for steps <= 10). So we recommend to set it to be `True`. + solver_type: A `str`. The taylor expansion type for the solver. `dpm_solver` or `taylor`. We recommend `dpm_solver`. + atol: A `float`. The absolute tolerance of the adaptive step size solver. Valid when `method` == 'adaptive'. + rtol: A `float`. The relative tolerance of the adaptive step size solver. Valid when `method` == 'adaptive'. + Returns: + x_end: A pytorch tensor. The approximated solution at time `t_end`. + """ + t_0 = 1. / self.noise_schedule.total_N if t_end is None else t_end + t_T = self.noise_schedule.T if t_start is None else t_start + device = x.device + if method == 'adaptive': + with torch.no_grad(): + x = self.dpm_solver_adaptive(x, order=order, t_T=t_T, t_0=t_0, atol=atol, rtol=rtol, + solver_type=solver_type) + elif method == 'multistep': + assert steps >= order + timesteps = self.get_time_steps(skip_type=skip_type, t_T=t_T, t_0=t_0, N=steps, device=device) + assert timesteps.shape[0] - 1 == steps + with torch.no_grad(): + vec_t = timesteps[0].expand((x.shape[0])) + model_prev_list = [self.model_fn(x, vec_t)] + t_prev_list = [vec_t] + # Init the first `order` values by lower order multistep DPM-Solver. + for init_order in tqdm(range(1, order), desc="DPM init order"): + vec_t = timesteps[init_order].expand(x.shape[0]) + x = self.multistep_dpm_solver_update(x, model_prev_list, t_prev_list, vec_t, init_order, + solver_type=solver_type) + model_prev_list.append(self.model_fn(x, vec_t)) + t_prev_list.append(vec_t) + # Compute the remaining values by `order`-th order multistep DPM-Solver. + for step in tqdm(range(order, steps + 1), desc="DPM multistep"): + vec_t = timesteps[step].expand(x.shape[0]) + if lower_order_final and steps < 15: + step_order = min(order, steps + 1 - step) + else: + step_order = order + x = self.multistep_dpm_solver_update(x, model_prev_list, t_prev_list, vec_t, step_order, + solver_type=solver_type) + for i in range(order - 1): + t_prev_list[i] = t_prev_list[i + 1] + model_prev_list[i] = model_prev_list[i + 1] + t_prev_list[-1] = vec_t + # We do not need to evaluate the final model value. + if step < steps: + model_prev_list[-1] = self.model_fn(x, vec_t) + elif method in ['singlestep', 'singlestep_fixed']: + if method == 'singlestep': + timesteps_outer, orders = self.get_orders_and_timesteps_for_singlestep_solver(steps=steps, order=order, + skip_type=skip_type, + t_T=t_T, t_0=t_0, + device=device) + elif method == 'singlestep_fixed': + K = steps // order + orders = [order, ] * K + timesteps_outer = self.get_time_steps(skip_type=skip_type, t_T=t_T, t_0=t_0, N=K, device=device) + for i, order in enumerate(orders): + t_T_inner, t_0_inner = timesteps_outer[i], timesteps_outer[i + 1] + timesteps_inner = self.get_time_steps(skip_type=skip_type, t_T=t_T_inner.item(), t_0=t_0_inner.item(), + N=order, device=device) + lambda_inner = self.noise_schedule.marginal_lambda(timesteps_inner) + vec_s, vec_t = t_T_inner.tile(x.shape[0]), t_0_inner.tile(x.shape[0]) + h = lambda_inner[-1] - lambda_inner[0] + r1 = None if order <= 1 else (lambda_inner[1] - lambda_inner[0]) / h + r2 = None if order <= 2 else (lambda_inner[2] - lambda_inner[0]) / h + x = self.singlestep_dpm_solver_update(x, vec_s, vec_t, order, solver_type=solver_type, r1=r1, r2=r2) + if denoise_to_zero: + x = self.denoise_to_zero_fn(x, torch.ones((x.shape[0],)).to(device) * t_0) + return x + + +############################################################# +# other utility functions +############################################################# + +def interpolate_fn(x, xp, yp): + """ + A piecewise linear function y = f(x), using xp and yp as keypoints. + We implement f(x) in a differentiable way (i.e. applicable for autograd). + The function f(x) is well-defined for all x-axis. (For x beyond the bounds of xp, we use the outmost points of xp to define the linear function.) + Args: + x: PyTorch tensor with shape [N, C], where N is the batch size, C is the number of channels (we use C = 1 for DPM-Solver). + xp: PyTorch tensor with shape [C, K], where K is the number of keypoints. + yp: PyTorch tensor with shape [C, K]. + Returns: + The function values f(x), with shape [N, C]. + """ + N, K = x.shape[0], xp.shape[1] + all_x = torch.cat([x.unsqueeze(2), xp.unsqueeze(0).repeat((N, 1, 1))], dim=2) + sorted_all_x, x_indices = torch.sort(all_x, dim=2) + x_idx = torch.argmin(x_indices, dim=2) + cand_start_idx = x_idx - 1 + start_idx = torch.where( + torch.eq(x_idx, 0), + torch.tensor(1, device=x.device), + torch.where( + torch.eq(x_idx, K), torch.tensor(K - 2, device=x.device), cand_start_idx, + ), + ) + end_idx = torch.where(torch.eq(start_idx, cand_start_idx), start_idx + 2, start_idx + 1) + start_x = torch.gather(sorted_all_x, dim=2, index=start_idx.unsqueeze(2)).squeeze(2) + end_x = torch.gather(sorted_all_x, dim=2, index=end_idx.unsqueeze(2)).squeeze(2) + start_idx2 = torch.where( + torch.eq(x_idx, 0), + torch.tensor(0, device=x.device), + torch.where( + torch.eq(x_idx, K), torch.tensor(K - 2, device=x.device), cand_start_idx, + ), + ) + y_positions_expanded = yp.unsqueeze(0).expand(N, -1, -1) + start_y = torch.gather(y_positions_expanded, dim=2, index=start_idx2.unsqueeze(2)).squeeze(2) + end_y = torch.gather(y_positions_expanded, dim=2, index=(start_idx2 + 1).unsqueeze(2)).squeeze(2) + cand = start_y + (x - start_x) * (end_y - start_y) / (end_x - start_x) + return cand + + +def expand_dims(v, dims): + """ + Expand the tensor `v` to the dim `dims`. + Args: + `v`: a PyTorch tensor with shape [N]. + `dim`: a `int`. + Returns: + a PyTorch tensor with shape [N, 1, 1, ..., 1] and the total dimension is `dims`. + """ + return v[(...,) + (None,) * (dims - 1)] \ No newline at end of file diff --git a/repositories/ldm/models/diffusion/dpm_solver/sampler.py b/repositories/ldm/models/diffusion/dpm_solver/sampler.py new file mode 100644 index 000000000..e4d0d0a38 --- /dev/null +++ b/repositories/ldm/models/diffusion/dpm_solver/sampler.py @@ -0,0 +1,96 @@ +"""SAMPLING ONLY.""" +import torch + +from .dpm_solver import NoiseScheduleVP, model_wrapper, DPM_Solver + +MODEL_TYPES = { + "eps": "noise", + "v": "v" +} + + +class DPMSolverSampler(object): + def __init__(self, model, device=torch.device("cuda"), **kwargs): + super().__init__() + self.model = model + self.device = device + to_torch = lambda x: x.clone().detach().to(torch.float32).to(model.device) + self.register_buffer('alphas_cumprod', to_torch(model.alphas_cumprod)) + + def register_buffer(self, name, attr): + if type(attr) == torch.Tensor: + if attr.device != self.device: + attr = attr.to(self.device) + setattr(self, name, attr) + + @torch.no_grad() + def sample(self, + S, + batch_size, + shape, + conditioning=None, + callback=None, + normals_sequence=None, + img_callback=None, + quantize_x0=False, + eta=0., + mask=None, + x0=None, + temperature=1., + noise_dropout=0., + score_corrector=None, + corrector_kwargs=None, + verbose=True, + x_T=None, + log_every_t=100, + unconditional_guidance_scale=1., + unconditional_conditioning=None, + # this has to come in the same format as the conditioning, # e.g. as encoded tokens, ... + **kwargs + ): + if conditioning is not None: + if isinstance(conditioning, dict): + ctmp = conditioning[list(conditioning.keys())[0]] + while isinstance(ctmp, list): ctmp = ctmp[0] + if isinstance(ctmp, torch.Tensor): + cbs = ctmp.shape[0] + if cbs != batch_size: + print(f"Warning: Got {cbs} conditionings but batch-size is {batch_size}") + elif isinstance(conditioning, list): + for ctmp in conditioning: + if ctmp.shape[0] != batch_size: + print(f"Warning: Got {ctmp.shape[0]} conditionings but batch-size is {batch_size}") + else: + if isinstance(conditioning, torch.Tensor): + if conditioning.shape[0] != batch_size: + print(f"Warning: Got {conditioning.shape[0]} conditionings but batch-size is {batch_size}") + + # sampling + C, H, W = shape + size = (batch_size, C, H, W) + + print(f'Data shape for DPM-Solver sampling is {size}, sampling steps {S}') + + device = self.model.betas.device + if x_T is None: + img = torch.randn(size, device=device) + else: + img = x_T + + ns = NoiseScheduleVP('discrete', alphas_cumprod=self.alphas_cumprod) + + model_fn = model_wrapper( + lambda x, t, c: self.model.apply_model(x, t, c), + ns, + model_type=MODEL_TYPES[self.model.parameterization], + guidance_type="classifier-free", + condition=conditioning, + unconditional_condition=unconditional_conditioning, + guidance_scale=unconditional_guidance_scale, + ) + + dpm_solver = DPM_Solver(model_fn, ns, predict_x0=True, thresholding=False) + x = dpm_solver.sample(img, steps=S, skip_type="time_uniform", method="multistep", order=2, + lower_order_final=True) + + return x.to(device), None diff --git a/repositories/ldm/models/diffusion/plms.py b/repositories/ldm/models/diffusion/plms.py new file mode 100644 index 000000000..9d31b3994 --- /dev/null +++ b/repositories/ldm/models/diffusion/plms.py @@ -0,0 +1,245 @@ +"""SAMPLING ONLY.""" + +import torch +import numpy as np +from tqdm import tqdm +from functools import partial + +from ldm.modules.diffusionmodules.util import make_ddim_sampling_parameters, make_ddim_timesteps, noise_like +from ldm.models.diffusion.sampling_util import norm_thresholding + + +class PLMSSampler(object): + def __init__(self, model, schedule="linear", device=torch.device("cuda"), **kwargs): + super().__init__() + self.model = model + self.ddpm_num_timesteps = model.num_timesteps + self.schedule = schedule + self.device = device + + def register_buffer(self, name, attr): + if type(attr) == torch.Tensor: + if attr.device != self.device: + attr = attr.to(self.device) + setattr(self, name, attr) + + def make_schedule(self, ddim_num_steps, ddim_discretize="uniform", ddim_eta=0., verbose=True): + if ddim_eta != 0: + raise ValueError('ddim_eta must be 0 for PLMS') + self.ddim_timesteps = make_ddim_timesteps(ddim_discr_method=ddim_discretize, num_ddim_timesteps=ddim_num_steps, + num_ddpm_timesteps=self.ddpm_num_timesteps,verbose=verbose) + alphas_cumprod = self.model.alphas_cumprod + assert alphas_cumprod.shape[0] == self.ddpm_num_timesteps, 'alphas have to be defined for each timestep' + to_torch = lambda x: x.clone().detach().to(torch.float32).to(self.model.device) + + self.register_buffer('betas', to_torch(self.model.betas)) + self.register_buffer('alphas_cumprod', to_torch(alphas_cumprod)) + self.register_buffer('alphas_cumprod_prev', to_torch(self.model.alphas_cumprod_prev)) + + # calculations for diffusion q(x_t | x_{t-1}) and others + self.register_buffer('sqrt_alphas_cumprod', to_torch(np.sqrt(alphas_cumprod.cpu()))) + self.register_buffer('sqrt_one_minus_alphas_cumprod', to_torch(np.sqrt(1. - alphas_cumprod.cpu()))) + self.register_buffer('log_one_minus_alphas_cumprod', to_torch(np.log(1. - alphas_cumprod.cpu()))) + self.register_buffer('sqrt_recip_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod.cpu()))) + self.register_buffer('sqrt_recipm1_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod.cpu() - 1))) + + # ddim sampling parameters + ddim_sigmas, ddim_alphas, ddim_alphas_prev = make_ddim_sampling_parameters(alphacums=alphas_cumprod.cpu(), + ddim_timesteps=self.ddim_timesteps, + eta=ddim_eta,verbose=verbose) + self.register_buffer('ddim_sigmas', ddim_sigmas) + self.register_buffer('ddim_alphas', ddim_alphas) + self.register_buffer('ddim_alphas_prev', ddim_alphas_prev) + self.register_buffer('ddim_sqrt_one_minus_alphas', np.sqrt(1. - ddim_alphas)) + sigmas_for_original_sampling_steps = ddim_eta * torch.sqrt( + (1 - self.alphas_cumprod_prev) / (1 - self.alphas_cumprod) * ( + 1 - self.alphas_cumprod / self.alphas_cumprod_prev)) + self.register_buffer('ddim_sigmas_for_original_num_steps', sigmas_for_original_sampling_steps) + + @torch.no_grad() + def sample(self, + S, + batch_size, + shape, + conditioning=None, + callback=None, + normals_sequence=None, + img_callback=None, + quantize_x0=False, + eta=0., + mask=None, + x0=None, + temperature=1., + noise_dropout=0., + score_corrector=None, + corrector_kwargs=None, + verbose=True, + x_T=None, + log_every_t=100, + unconditional_guidance_scale=1., + unconditional_conditioning=None, + # this has to come in the same format as the conditioning, # e.g. as encoded tokens, ... + dynamic_threshold=None, + **kwargs + ): + if conditioning is not None: + if isinstance(conditioning, dict): + cbs = conditioning[list(conditioning.keys())[0]].shape[0] + if cbs != batch_size: + print(f"Warning: Got {cbs} conditionings but batch-size is {batch_size}") + else: + if conditioning.shape[0] != batch_size: + print(f"Warning: Got {conditioning.shape[0]} conditionings but batch-size is {batch_size}") + + self.make_schedule(ddim_num_steps=S, ddim_eta=eta, verbose=verbose) + # sampling + C, H, W = shape + size = (batch_size, C, H, W) + print(f'Data shape for PLMS sampling is {size}') + + samples, intermediates = self.plms_sampling(conditioning, size, + callback=callback, + img_callback=img_callback, + quantize_denoised=quantize_x0, + mask=mask, x0=x0, + ddim_use_original_steps=False, + noise_dropout=noise_dropout, + temperature=temperature, + score_corrector=score_corrector, + corrector_kwargs=corrector_kwargs, + x_T=x_T, + log_every_t=log_every_t, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=unconditional_conditioning, + dynamic_threshold=dynamic_threshold, + ) + return samples, intermediates + + @torch.no_grad() + def plms_sampling(self, cond, shape, + x_T=None, ddim_use_original_steps=False, + callback=None, timesteps=None, quantize_denoised=False, + mask=None, x0=None, img_callback=None, log_every_t=100, + temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None, + unconditional_guidance_scale=1., unconditional_conditioning=None, + dynamic_threshold=None): + device = self.model.betas.device + b = shape[0] + if x_T is None: + img = torch.randn(shape, device=device) + else: + img = x_T + + if timesteps is None: + timesteps = self.ddpm_num_timesteps if ddim_use_original_steps else self.ddim_timesteps + elif timesteps is not None and not ddim_use_original_steps: + subset_end = int(min(timesteps / self.ddim_timesteps.shape[0], 1) * self.ddim_timesteps.shape[0]) - 1 + timesteps = self.ddim_timesteps[:subset_end] + + intermediates = {'x_inter': [img], 'pred_x0': [img]} + time_range = list(reversed(range(0,timesteps))) if ddim_use_original_steps else np.flip(timesteps) + total_steps = timesteps if ddim_use_original_steps else timesteps.shape[0] + print(f"Running PLMS Sampling with {total_steps} timesteps") + + iterator = tqdm(time_range, desc='PLMS Sampler', total=total_steps) + old_eps = [] + + for i, step in enumerate(iterator): + index = total_steps - i - 1 + ts = torch.full((b,), step, device=device, dtype=torch.long) + ts_next = torch.full((b,), time_range[min(i + 1, len(time_range) - 1)], device=device, dtype=torch.long) + + if mask is not None: + assert x0 is not None + img_orig = self.model.q_sample(x0, ts) # TODO: deterministic forward pass? + img = img_orig * mask + (1. - mask) * img + + outs = self.p_sample_plms(img, cond, ts, index=index, use_original_steps=ddim_use_original_steps, + quantize_denoised=quantize_denoised, temperature=temperature, + noise_dropout=noise_dropout, score_corrector=score_corrector, + corrector_kwargs=corrector_kwargs, + unconditional_guidance_scale=unconditional_guidance_scale, + unconditional_conditioning=unconditional_conditioning, + old_eps=old_eps, t_next=ts_next, + dynamic_threshold=dynamic_threshold) + img, pred_x0, e_t = outs + old_eps.append(e_t) + if len(old_eps) >= 4: + old_eps.pop(0) + if callback: callback(i) + if img_callback: img_callback(pred_x0, i) + + if index % log_every_t == 0 or index == total_steps - 1: + intermediates['x_inter'].append(img) + intermediates['pred_x0'].append(pred_x0) + + return img, intermediates + + @torch.no_grad() + def p_sample_plms(self, x, c, t, index, repeat_noise=False, use_original_steps=False, quantize_denoised=False, + temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None, + unconditional_guidance_scale=1., unconditional_conditioning=None, old_eps=None, t_next=None, + dynamic_threshold=None): + b, *_, device = *x.shape, x.device + + def get_model_output(x, t): + if unconditional_conditioning is None or unconditional_guidance_scale == 1.: + e_t = self.model.apply_model(x, t, c) + else: + x_in = torch.cat([x] * 2) + t_in = torch.cat([t] * 2) + c_in = torch.cat([unconditional_conditioning, c]) + e_t_uncond, e_t = self.model.apply_model(x_in, t_in, c_in).chunk(2) + e_t = e_t_uncond + unconditional_guidance_scale * (e_t - e_t_uncond) + + if score_corrector is not None: + assert self.model.parameterization == "eps" + e_t = score_corrector.modify_score(self.model, e_t, x, t, c, **corrector_kwargs) + + return e_t + + alphas = self.model.alphas_cumprod if use_original_steps else self.ddim_alphas + alphas_prev = self.model.alphas_cumprod_prev if use_original_steps else self.ddim_alphas_prev + sqrt_one_minus_alphas = self.model.sqrt_one_minus_alphas_cumprod if use_original_steps else self.ddim_sqrt_one_minus_alphas + sigmas = self.model.ddim_sigmas_for_original_num_steps if use_original_steps else self.ddim_sigmas + + def get_x_prev_and_pred_x0(e_t, index): + # select parameters corresponding to the currently considered timestep + a_t = torch.full((b, 1, 1, 1), alphas[index], device=device) + a_prev = torch.full((b, 1, 1, 1), alphas_prev[index], device=device) + sigma_t = torch.full((b, 1, 1, 1), sigmas[index], device=device) + sqrt_one_minus_at = torch.full((b, 1, 1, 1), sqrt_one_minus_alphas[index],device=device) + + # current prediction for x_0 + pred_x0 = (x - sqrt_one_minus_at * e_t) / a_t.sqrt() + if quantize_denoised: + pred_x0, _, *_ = self.model.first_stage_model.quantize(pred_x0) + if dynamic_threshold is not None: + pred_x0 = norm_thresholding(pred_x0, dynamic_threshold) + # direction pointing to x_t + dir_xt = (1. - a_prev - sigma_t**2).sqrt() * e_t + noise = sigma_t * noise_like(x.shape, device, repeat_noise) * temperature + if noise_dropout > 0.: + noise = torch.nn.functional.dropout(noise, p=noise_dropout) + x_prev = a_prev.sqrt() * pred_x0 + dir_xt + noise + return x_prev, pred_x0 + + e_t = get_model_output(x, t) + if len(old_eps) == 0: + # Pseudo Improved Euler (2nd order) + x_prev, pred_x0 = get_x_prev_and_pred_x0(e_t, index) + e_t_next = get_model_output(x_prev, t_next) + e_t_prime = (e_t + e_t_next) / 2 + elif len(old_eps) == 1: + # 2nd order Pseudo Linear Multistep (Adams-Bashforth) + e_t_prime = (3 * e_t - old_eps[-1]) / 2 + elif len(old_eps) == 2: + # 3nd order Pseudo Linear Multistep (Adams-Bashforth) + e_t_prime = (23 * e_t - 16 * old_eps[-1] + 5 * old_eps[-2]) / 12 + elif len(old_eps) >= 3: + # 4nd order Pseudo Linear Multistep (Adams-Bashforth) + e_t_prime = (55 * e_t - 59 * old_eps[-1] + 37 * old_eps[-2] - 9 * old_eps[-3]) / 24 + + x_prev, pred_x0 = get_x_prev_and_pred_x0(e_t_prime, index) + + return x_prev, pred_x0, e_t diff --git a/repositories/ldm/models/diffusion/sampling_util.py b/repositories/ldm/models/diffusion/sampling_util.py new file mode 100644 index 000000000..7eff02be6 --- /dev/null +++ b/repositories/ldm/models/diffusion/sampling_util.py @@ -0,0 +1,22 @@ +import torch +import numpy as np + + +def append_dims(x, target_dims): + """Appends dimensions to the end of a tensor until it has target_dims dimensions. + From https://github.com/crowsonkb/k-diffusion/blob/master/k_diffusion/utils.py""" + dims_to_append = target_dims - x.ndim + if dims_to_append < 0: + raise ValueError(f'input has {x.ndim} dims but target_dims is {target_dims}, which is less') + return x[(...,) + (None,) * dims_to_append] + + +def norm_thresholding(x0, value): + s = append_dims(x0.pow(2).flatten(1).mean(1).sqrt().clamp(min=value), x0.ndim) + return x0 * (value / s) + + +def spatial_norm_thresholding(x0, value): + # b c h w + s = x0.pow(2).mean(1, keepdim=True).sqrt().clamp(min=value) + return x0 * (value / s) \ No newline at end of file diff --git a/repositories/ldm/modules/attention.py b/repositories/ldm/modules/attention.py new file mode 100644 index 000000000..509cd8737 --- /dev/null +++ b/repositories/ldm/modules/attention.py @@ -0,0 +1,341 @@ +from inspect import isfunction +import math +import torch +import torch.nn.functional as F +from torch import nn, einsum +from einops import rearrange, repeat +from typing import Optional, Any + +from ldm.modules.diffusionmodules.util import checkpoint + + +try: + import xformers + import xformers.ops + XFORMERS_IS_AVAILBLE = True +except: + XFORMERS_IS_AVAILBLE = False + +# CrossAttn precision handling +import os +_ATTN_PRECISION = os.environ.get("ATTN_PRECISION", "fp32") + +def exists(val): + return val is not None + + +def uniq(arr): + return{el: True for el in arr}.keys() + + +def default(val, d): + if exists(val): + return val + return d() if isfunction(d) else d + + +def max_neg_value(t): + return -torch.finfo(t.dtype).max + + +def init_(tensor): + dim = tensor.shape[-1] + std = 1 / math.sqrt(dim) + tensor.uniform_(-std, std) + return tensor + + +# feedforward +class GEGLU(nn.Module): + def __init__(self, dim_in, dim_out): + super().__init__() + self.proj = nn.Linear(dim_in, dim_out * 2) + + def forward(self, x): + x, gate = self.proj(x).chunk(2, dim=-1) + return x * F.gelu(gate) + + +class FeedForward(nn.Module): + def __init__(self, dim, dim_out=None, mult=4, glu=False, dropout=0.): + super().__init__() + inner_dim = int(dim * mult) + dim_out = default(dim_out, dim) + project_in = nn.Sequential( + nn.Linear(dim, inner_dim), + nn.GELU() + ) if not glu else GEGLU(dim, inner_dim) + + self.net = nn.Sequential( + project_in, + nn.Dropout(dropout), + nn.Linear(inner_dim, dim_out) + ) + + def forward(self, x): + return self.net(x) + + +def zero_module(module): + """ + Zero out the parameters of a module and return it. + """ + for p in module.parameters(): + p.detach().zero_() + return module + + +def Normalize(in_channels): + return torch.nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True) + + +class SpatialSelfAttention(nn.Module): + def __init__(self, in_channels): + super().__init__() + self.in_channels = in_channels + + self.norm = Normalize(in_channels) + self.q = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.k = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.v = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.proj_out = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + + def forward(self, x): + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + + # compute attention + b,c,h,w = q.shape + q = rearrange(q, 'b c h w -> b (h w) c') + k = rearrange(k, 'b c h w -> b c (h w)') + w_ = torch.einsum('bij,bjk->bik', q, k) + + w_ = w_ * (int(c)**(-0.5)) + w_ = torch.nn.functional.softmax(w_, dim=2) + + # attend to values + v = rearrange(v, 'b c h w -> b c (h w)') + w_ = rearrange(w_, 'b i j -> b j i') + h_ = torch.einsum('bij,bjk->bik', v, w_) + h_ = rearrange(h_, 'b c (h w) -> b c h w', h=h) + h_ = self.proj_out(h_) + + return x+h_ + + +class CrossAttention(nn.Module): + def __init__(self, query_dim, context_dim=None, heads=8, dim_head=64, dropout=0.): + super().__init__() + inner_dim = dim_head * heads + context_dim = default(context_dim, query_dim) + + self.scale = dim_head ** -0.5 + self.heads = heads + + self.to_q = nn.Linear(query_dim, inner_dim, bias=False) + self.to_k = nn.Linear(context_dim, inner_dim, bias=False) + self.to_v = nn.Linear(context_dim, inner_dim, bias=False) + + self.to_out = nn.Sequential( + nn.Linear(inner_dim, query_dim), + nn.Dropout(dropout) + ) + + def forward(self, x, context=None, mask=None): + h = self.heads + + q = self.to_q(x) + context = default(context, x) + k = self.to_k(context) + v = self.to_v(context) + + q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> (b h) n d', h=h), (q, k, v)) + + # force cast to fp32 to avoid overflowing + if _ATTN_PRECISION =="fp32": + with torch.autocast(enabled=False, device_type = 'cuda'): + q, k = q.float(), k.float() + sim = einsum('b i d, b j d -> b i j', q, k) * self.scale + else: + sim = einsum('b i d, b j d -> b i j', q, k) * self.scale + + del q, k + + if exists(mask): + mask = rearrange(mask, 'b ... -> b (...)') + max_neg_value = -torch.finfo(sim.dtype).max + mask = repeat(mask, 'b j -> (b h) () j', h=h) + sim.masked_fill_(~mask, max_neg_value) + + # attention, what we cannot get enough of + sim = sim.softmax(dim=-1) + + out = einsum('b i j, b j d -> b i d', sim, v) + out = rearrange(out, '(b h) n d -> b n (h d)', h=h) + return self.to_out(out) + + +class MemoryEfficientCrossAttention(nn.Module): + # https://github.com/MatthieuTPHR/diffusers/blob/d80b531ff8060ec1ea982b65a1b8df70f73aa67c/src/diffusers/models/attention.py#L223 + def __init__(self, query_dim, context_dim=None, heads=8, dim_head=64, dropout=0.0): + super().__init__() + print(f"Setting up {self.__class__.__name__}. Query dim is {query_dim}, context_dim is {context_dim} and using " + f"{heads} heads.") + inner_dim = dim_head * heads + context_dim = default(context_dim, query_dim) + + self.heads = heads + self.dim_head = dim_head + + self.to_q = nn.Linear(query_dim, inner_dim, bias=False) + self.to_k = nn.Linear(context_dim, inner_dim, bias=False) + self.to_v = nn.Linear(context_dim, inner_dim, bias=False) + + self.to_out = nn.Sequential(nn.Linear(inner_dim, query_dim), nn.Dropout(dropout)) + self.attention_op: Optional[Any] = None + + def forward(self, x, context=None, mask=None): + q = self.to_q(x) + context = default(context, x) + k = self.to_k(context) + v = self.to_v(context) + + b, _, _ = q.shape + q, k, v = map( + lambda t: t.unsqueeze(3) + .reshape(b, t.shape[1], self.heads, self.dim_head) + .permute(0, 2, 1, 3) + .reshape(b * self.heads, t.shape[1], self.dim_head) + .contiguous(), + (q, k, v), + ) + + # actually compute the attention, what we cannot get enough of + out = xformers.ops.memory_efficient_attention(q, k, v, attn_bias=None, op=self.attention_op) + + if exists(mask): + raise NotImplementedError + out = ( + out.unsqueeze(0) + .reshape(b, self.heads, out.shape[1], self.dim_head) + .permute(0, 2, 1, 3) + .reshape(b, out.shape[1], self.heads * self.dim_head) + ) + return self.to_out(out) + + +class BasicTransformerBlock(nn.Module): + ATTENTION_MODES = { + "softmax": CrossAttention, # vanilla attention + "softmax-xformers": MemoryEfficientCrossAttention + } + def __init__(self, dim, n_heads, d_head, dropout=0., context_dim=None, gated_ff=True, checkpoint=True, + disable_self_attn=False): + super().__init__() + attn_mode = "softmax-xformers" if XFORMERS_IS_AVAILBLE else "softmax" + assert attn_mode in self.ATTENTION_MODES + attn_cls = self.ATTENTION_MODES[attn_mode] + self.disable_self_attn = disable_self_attn + self.attn1 = attn_cls(query_dim=dim, heads=n_heads, dim_head=d_head, dropout=dropout, + context_dim=context_dim if self.disable_self_attn else None) # is a self-attention if not self.disable_self_attn + self.ff = FeedForward(dim, dropout=dropout, glu=gated_ff) + self.attn2 = attn_cls(query_dim=dim, context_dim=context_dim, + heads=n_heads, dim_head=d_head, dropout=dropout) # is self-attn if context is none + self.norm1 = nn.LayerNorm(dim) + self.norm2 = nn.LayerNorm(dim) + self.norm3 = nn.LayerNorm(dim) + self.checkpoint = checkpoint + + def forward(self, x, context=None): + return checkpoint(self._forward, (x, context), self.parameters(), self.checkpoint) + + def _forward(self, x, context=None): + x = self.attn1(self.norm1(x), context=context if self.disable_self_attn else None) + x + x = self.attn2(self.norm2(x), context=context) + x + x = self.ff(self.norm3(x)) + x + return x + + +class SpatialTransformer(nn.Module): + """ + Transformer block for image-like data. + First, project the input (aka embedding) + and reshape to b, t, d. + Then apply standard transformer action. + Finally, reshape to image + NEW: use_linear for more efficiency instead of the 1x1 convs + """ + def __init__(self, in_channels, n_heads, d_head, + depth=1, dropout=0., context_dim=None, + disable_self_attn=False, use_linear=False, + use_checkpoint=True): + super().__init__() + if exists(context_dim) and not isinstance(context_dim, list): + context_dim = [context_dim] + self.in_channels = in_channels + inner_dim = n_heads * d_head + self.norm = Normalize(in_channels) + if not use_linear: + self.proj_in = nn.Conv2d(in_channels, + inner_dim, + kernel_size=1, + stride=1, + padding=0) + else: + self.proj_in = nn.Linear(in_channels, inner_dim) + + self.transformer_blocks = nn.ModuleList( + [BasicTransformerBlock(inner_dim, n_heads, d_head, dropout=dropout, context_dim=context_dim[d], + disable_self_attn=disable_self_attn, checkpoint=use_checkpoint) + for d in range(depth)] + ) + if not use_linear: + self.proj_out = zero_module(nn.Conv2d(inner_dim, + in_channels, + kernel_size=1, + stride=1, + padding=0)) + else: + self.proj_out = zero_module(nn.Linear(in_channels, inner_dim)) + self.use_linear = use_linear + + def forward(self, x, context=None): + # note: if no context is given, cross-attention defaults to self-attention + if not isinstance(context, list): + context = [context] + b, c, h, w = x.shape + x_in = x + x = self.norm(x) + if not self.use_linear: + x = self.proj_in(x) + x = rearrange(x, 'b c h w -> b (h w) c').contiguous() + if self.use_linear: + x = self.proj_in(x) + for i, block in enumerate(self.transformer_blocks): + x = block(x, context=context[i]) + if self.use_linear: + x = self.proj_out(x) + x = rearrange(x, 'b (h w) c -> b c h w', h=h, w=w).contiguous() + if not self.use_linear: + x = self.proj_out(x) + return x + x_in + diff --git a/repositories/ldm/modules/diffusionmodules/__init__.py b/repositories/ldm/modules/diffusionmodules/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/ldm/modules/diffusionmodules/model.py b/repositories/ldm/modules/diffusionmodules/model.py new file mode 100644 index 000000000..b089eebbe --- /dev/null +++ b/repositories/ldm/modules/diffusionmodules/model.py @@ -0,0 +1,852 @@ +# pytorch_diffusion + derived encoder decoder +import math +import torch +import torch.nn as nn +import numpy as np +from einops import rearrange +from typing import Optional, Any + +from ldm.modules.attention import MemoryEfficientCrossAttention + +try: + import xformers + import xformers.ops + XFORMERS_IS_AVAILBLE = True +except: + XFORMERS_IS_AVAILBLE = False + print("No module 'xformers'. Proceeding without it.") + + +def get_timestep_embedding(timesteps, embedding_dim): + """ + This matches the implementation in Denoising Diffusion Probabilistic Models: + From Fairseq. + Build sinusoidal embeddings. + This matches the implementation in tensor2tensor, but differs slightly + from the description in Section 3.5 of "Attention Is All You Need". + """ + assert len(timesteps.shape) == 1 + + half_dim = embedding_dim // 2 + emb = math.log(10000) / (half_dim - 1) + emb = torch.exp(torch.arange(half_dim, dtype=torch.float32) * -emb) + emb = emb.to(device=timesteps.device) + emb = timesteps.float()[:, None] * emb[None, :] + emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=1) + if embedding_dim % 2 == 1: # zero pad + emb = torch.nn.functional.pad(emb, (0,1,0,0)) + return emb + + +def nonlinearity(x): + # swish + return x*torch.sigmoid(x) + + +def Normalize(in_channels, num_groups=32): + return torch.nn.GroupNorm(num_groups=num_groups, num_channels=in_channels, eps=1e-6, affine=True) + + +class Upsample(nn.Module): + def __init__(self, in_channels, with_conv): + super().__init__() + self.with_conv = with_conv + if self.with_conv: + self.conv = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + x = torch.nn.functional.interpolate(x, scale_factor=2.0, mode="nearest") + if self.with_conv: + x = self.conv(x) + return x + + +class Downsample(nn.Module): + def __init__(self, in_channels, with_conv): + super().__init__() + self.with_conv = with_conv + if self.with_conv: + # no asymmetric padding in torch conv, must do it ourselves + self.conv = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=3, + stride=2, + padding=0) + + def forward(self, x): + if self.with_conv: + pad = (0,1,0,1) + x = torch.nn.functional.pad(x, pad, mode="constant", value=0) + x = self.conv(x) + else: + x = torch.nn.functional.avg_pool2d(x, kernel_size=2, stride=2) + return x + + +class ResnetBlock(nn.Module): + def __init__(self, *, in_channels, out_channels=None, conv_shortcut=False, + dropout, temb_channels=512): + super().__init__() + self.in_channels = in_channels + out_channels = in_channels if out_channels is None else out_channels + self.out_channels = out_channels + self.use_conv_shortcut = conv_shortcut + + self.norm1 = Normalize(in_channels) + self.conv1 = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + if temb_channels > 0: + self.temb_proj = torch.nn.Linear(temb_channels, + out_channels) + self.norm2 = Normalize(out_channels) + self.dropout = torch.nn.Dropout(dropout) + self.conv2 = torch.nn.Conv2d(out_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + if self.in_channels != self.out_channels: + if self.use_conv_shortcut: + self.conv_shortcut = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + else: + self.nin_shortcut = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=1, + stride=1, + padding=0) + + def forward(self, x, temb): + h = x + h = self.norm1(h) + h = nonlinearity(h) + h = self.conv1(h) + + if temb is not None: + h = h + self.temb_proj(nonlinearity(temb))[:,:,None,None] + + h = self.norm2(h) + h = nonlinearity(h) + h = self.dropout(h) + h = self.conv2(h) + + if self.in_channels != self.out_channels: + if self.use_conv_shortcut: + x = self.conv_shortcut(x) + else: + x = self.nin_shortcut(x) + + return x+h + + +class AttnBlock(nn.Module): + def __init__(self, in_channels): + super().__init__() + self.in_channels = in_channels + + self.norm = Normalize(in_channels) + self.q = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.k = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.v = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.proj_out = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + + def forward(self, x): + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + + # compute attention + b,c,h,w = q.shape + q = q.reshape(b,c,h*w) + q = q.permute(0,2,1) # b,hw,c + k = k.reshape(b,c,h*w) # b,c,hw + w_ = torch.bmm(q,k) # b,hw,hw w[b,i,j]=sum_c q[b,i,c]k[b,c,j] + w_ = w_ * (int(c)**(-0.5)) + w_ = torch.nn.functional.softmax(w_, dim=2) + + # attend to values + v = v.reshape(b,c,h*w) + w_ = w_.permute(0,2,1) # b,hw,hw (first hw of k, second of q) + h_ = torch.bmm(v,w_) # b, c,hw (hw of q) h_[b,c,j] = sum_i v[b,c,i] w_[b,i,j] + h_ = h_.reshape(b,c,h,w) + + h_ = self.proj_out(h_) + + return x+h_ + +class MemoryEfficientAttnBlock(nn.Module): + """ + Uses xformers efficient implementation, + see https://github.com/MatthieuTPHR/diffusers/blob/d80b531ff8060ec1ea982b65a1b8df70f73aa67c/src/diffusers/models/attention.py#L223 + Note: this is a single-head self-attention operation + """ + # + def __init__(self, in_channels): + super().__init__() + self.in_channels = in_channels + + self.norm = Normalize(in_channels) + self.q = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.k = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.v = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.proj_out = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.attention_op: Optional[Any] = None + + def forward(self, x): + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + + # compute attention + B, C, H, W = q.shape + q, k, v = map(lambda x: rearrange(x, 'b c h w -> b (h w) c'), (q, k, v)) + + q, k, v = map( + lambda t: t.unsqueeze(3) + .reshape(B, t.shape[1], 1, C) + .permute(0, 2, 1, 3) + .reshape(B * 1, t.shape[1], C) + .contiguous(), + (q, k, v), + ) + out = xformers.ops.memory_efficient_attention(q, k, v, attn_bias=None, op=self.attention_op) + + out = ( + out.unsqueeze(0) + .reshape(B, 1, out.shape[1], C) + .permute(0, 2, 1, 3) + .reshape(B, out.shape[1], C) + ) + out = rearrange(out, 'b (h w) c -> b c h w', b=B, h=H, w=W, c=C) + out = self.proj_out(out) + return x+out + + +class MemoryEfficientCrossAttentionWrapper(MemoryEfficientCrossAttention): + def forward(self, x, context=None, mask=None): + b, c, h, w = x.shape + x = rearrange(x, 'b c h w -> b (h w) c') + out = super().forward(x, context=context, mask=mask) + out = rearrange(out, 'b (h w) c -> b c h w', h=h, w=w, c=c) + return x + out + + +def make_attn(in_channels, attn_type="vanilla", attn_kwargs=None): + assert attn_type in ["vanilla", "vanilla-xformers", "memory-efficient-cross-attn", "linear", "none"], f'attn_type {attn_type} unknown' + if XFORMERS_IS_AVAILBLE and attn_type == "vanilla": + attn_type = "vanilla-xformers" + print(f"making attention of type '{attn_type}' with {in_channels} in_channels") + if attn_type == "vanilla": + assert attn_kwargs is None + return AttnBlock(in_channels) + elif attn_type == "vanilla-xformers": + print(f"building MemoryEfficientAttnBlock with {in_channels} in_channels...") + return MemoryEfficientAttnBlock(in_channels) + elif type == "memory-efficient-cross-attn": + attn_kwargs["query_dim"] = in_channels + return MemoryEfficientCrossAttentionWrapper(**attn_kwargs) + elif attn_type == "none": + return nn.Identity(in_channels) + else: + raise NotImplementedError() + + +class Model(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels, + resolution, use_timestep=True, use_linear_attn=False, attn_type="vanilla"): + super().__init__() + if use_linear_attn: attn_type = "linear" + self.ch = ch + self.temb_ch = self.ch*4 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.in_channels = in_channels + + self.use_timestep = use_timestep + if self.use_timestep: + # timestep embedding + self.temb = nn.Module() + self.temb.dense = nn.ModuleList([ + torch.nn.Linear(self.ch, + self.temb_ch), + torch.nn.Linear(self.temb_ch, + self.temb_ch), + ]) + + # downsampling + self.conv_in = torch.nn.Conv2d(in_channels, + self.ch, + kernel_size=3, + stride=1, + padding=1) + + curr_res = resolution + in_ch_mult = (1,)+tuple(ch_mult) + self.down = nn.ModuleList() + for i_level in range(self.num_resolutions): + block = nn.ModuleList() + attn = nn.ModuleList() + block_in = ch*in_ch_mult[i_level] + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(make_attn(block_in, attn_type=attn_type)) + down = nn.Module() + down.block = block + down.attn = attn + if i_level != self.num_resolutions-1: + down.downsample = Downsample(block_in, resamp_with_conv) + curr_res = curr_res // 2 + self.down.append(down) + + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + self.mid.attn_1 = make_attn(block_in, attn_type=attn_type) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + + # upsampling + self.up = nn.ModuleList() + for i_level in reversed(range(self.num_resolutions)): + block = nn.ModuleList() + attn = nn.ModuleList() + block_out = ch*ch_mult[i_level] + skip_in = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks+1): + if i_block == self.num_res_blocks: + skip_in = ch*in_ch_mult[i_level] + block.append(ResnetBlock(in_channels=block_in+skip_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(make_attn(block_in, attn_type=attn_type)) + up = nn.Module() + up.block = block + up.attn = attn + if i_level != 0: + up.upsample = Upsample(block_in, resamp_with_conv) + curr_res = curr_res * 2 + self.up.insert(0, up) # prepend to get consistent order + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + out_ch, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x, t=None, context=None): + #assert x.shape[2] == x.shape[3] == self.resolution + if context is not None: + # assume aligned context, cat along channel axis + x = torch.cat((x, context), dim=1) + if self.use_timestep: + # timestep embedding + assert t is not None + temb = get_timestep_embedding(t, self.ch) + temb = self.temb.dense[0](temb) + temb = nonlinearity(temb) + temb = self.temb.dense[1](temb) + else: + temb = None + + # downsampling + hs = [self.conv_in(x)] + for i_level in range(self.num_resolutions): + for i_block in range(self.num_res_blocks): + h = self.down[i_level].block[i_block](hs[-1], temb) + if len(self.down[i_level].attn) > 0: + h = self.down[i_level].attn[i_block](h) + hs.append(h) + if i_level != self.num_resolutions-1: + hs.append(self.down[i_level].downsample(hs[-1])) + + # middle + h = hs[-1] + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # upsampling + for i_level in reversed(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks+1): + h = self.up[i_level].block[i_block]( + torch.cat([h, hs.pop()], dim=1), temb) + if len(self.up[i_level].attn) > 0: + h = self.up[i_level].attn[i_block](h) + if i_level != 0: + h = self.up[i_level].upsample(h) + + # end + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + def get_last_layer(self): + return self.conv_out.weight + + +class Encoder(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels, + resolution, z_channels, double_z=True, use_linear_attn=False, attn_type="vanilla", + **ignore_kwargs): + super().__init__() + if use_linear_attn: attn_type = "linear" + self.ch = ch + self.temb_ch = 0 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.in_channels = in_channels + + # downsampling + self.conv_in = torch.nn.Conv2d(in_channels, + self.ch, + kernel_size=3, + stride=1, + padding=1) + + curr_res = resolution + in_ch_mult = (1,)+tuple(ch_mult) + self.in_ch_mult = in_ch_mult + self.down = nn.ModuleList() + for i_level in range(self.num_resolutions): + block = nn.ModuleList() + attn = nn.ModuleList() + block_in = ch*in_ch_mult[i_level] + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(make_attn(block_in, attn_type=attn_type)) + down = nn.Module() + down.block = block + down.attn = attn + if i_level != self.num_resolutions-1: + down.downsample = Downsample(block_in, resamp_with_conv) + curr_res = curr_res // 2 + self.down.append(down) + + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + self.mid.attn_1 = make_attn(block_in, attn_type=attn_type) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + 2*z_channels if double_z else z_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + # timestep embedding + temb = None + + # downsampling + hs = [self.conv_in(x)] + for i_level in range(self.num_resolutions): + for i_block in range(self.num_res_blocks): + h = self.down[i_level].block[i_block](hs[-1], temb) + if len(self.down[i_level].attn) > 0: + h = self.down[i_level].attn[i_block](h) + hs.append(h) + if i_level != self.num_resolutions-1: + hs.append(self.down[i_level].downsample(hs[-1])) + + # middle + h = hs[-1] + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # end + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + +class Decoder(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels, + resolution, z_channels, give_pre_end=False, tanh_out=False, use_linear_attn=False, + attn_type="vanilla", **ignorekwargs): + super().__init__() + if use_linear_attn: attn_type = "linear" + self.ch = ch + self.temb_ch = 0 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.in_channels = in_channels + self.give_pre_end = give_pre_end + self.tanh_out = tanh_out + + # compute in_ch_mult, block_in and curr_res at lowest res + in_ch_mult = (1,)+tuple(ch_mult) + block_in = ch*ch_mult[self.num_resolutions-1] + curr_res = resolution // 2**(self.num_resolutions-1) + self.z_shape = (1,z_channels,curr_res,curr_res) + print("Working with z of shape {} = {} dimensions.".format( + self.z_shape, np.prod(self.z_shape))) + + # z to block_in + self.conv_in = torch.nn.Conv2d(z_channels, + block_in, + kernel_size=3, + stride=1, + padding=1) + + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + self.mid.attn_1 = make_attn(block_in, attn_type=attn_type) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + + # upsampling + self.up = nn.ModuleList() + for i_level in reversed(range(self.num_resolutions)): + block = nn.ModuleList() + attn = nn.ModuleList() + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks+1): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(make_attn(block_in, attn_type=attn_type)) + up = nn.Module() + up.block = block + up.attn = attn + if i_level != 0: + up.upsample = Upsample(block_in, resamp_with_conv) + curr_res = curr_res * 2 + self.up.insert(0, up) # prepend to get consistent order + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + out_ch, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, z): + #assert z.shape[1:] == self.z_shape[1:] + self.last_z_shape = z.shape + + # timestep embedding + temb = None + + # z to block_in + h = self.conv_in(z) + + # middle + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # upsampling + for i_level in reversed(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks+1): + h = self.up[i_level].block[i_block](h, temb) + if len(self.up[i_level].attn) > 0: + h = self.up[i_level].attn[i_block](h) + if i_level != 0: + h = self.up[i_level].upsample(h) + + # end + if self.give_pre_end: + return h + + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + if self.tanh_out: + h = torch.tanh(h) + return h + + +class SimpleDecoder(nn.Module): + def __init__(self, in_channels, out_channels, *args, **kwargs): + super().__init__() + self.model = nn.ModuleList([nn.Conv2d(in_channels, in_channels, 1), + ResnetBlock(in_channels=in_channels, + out_channels=2 * in_channels, + temb_channels=0, dropout=0.0), + ResnetBlock(in_channels=2 * in_channels, + out_channels=4 * in_channels, + temb_channels=0, dropout=0.0), + ResnetBlock(in_channels=4 * in_channels, + out_channels=2 * in_channels, + temb_channels=0, dropout=0.0), + nn.Conv2d(2*in_channels, in_channels, 1), + Upsample(in_channels, with_conv=True)]) + # end + self.norm_out = Normalize(in_channels) + self.conv_out = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + for i, layer in enumerate(self.model): + if i in [1,2,3]: + x = layer(x, None) + else: + x = layer(x) + + h = self.norm_out(x) + h = nonlinearity(h) + x = self.conv_out(h) + return x + + +class UpsampleDecoder(nn.Module): + def __init__(self, in_channels, out_channels, ch, num_res_blocks, resolution, + ch_mult=(2,2), dropout=0.0): + super().__init__() + # upsampling + self.temb_ch = 0 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + block_in = in_channels + curr_res = resolution // 2 ** (self.num_resolutions - 1) + self.res_blocks = nn.ModuleList() + self.upsample_blocks = nn.ModuleList() + for i_level in range(self.num_resolutions): + res_block = [] + block_out = ch * ch_mult[i_level] + for i_block in range(self.num_res_blocks + 1): + res_block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + self.res_blocks.append(nn.ModuleList(res_block)) + if i_level != self.num_resolutions - 1: + self.upsample_blocks.append(Upsample(block_in, True)) + curr_res = curr_res * 2 + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + out_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + # upsampling + h = x + for k, i_level in enumerate(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks + 1): + h = self.res_blocks[i_level][i_block](h, None) + if i_level != self.num_resolutions - 1: + h = self.upsample_blocks[k](h) + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + +class LatentRescaler(nn.Module): + def __init__(self, factor, in_channels, mid_channels, out_channels, depth=2): + super().__init__() + # residual block, interpolate, residual block + self.factor = factor + self.conv_in = nn.Conv2d(in_channels, + mid_channels, + kernel_size=3, + stride=1, + padding=1) + self.res_block1 = nn.ModuleList([ResnetBlock(in_channels=mid_channels, + out_channels=mid_channels, + temb_channels=0, + dropout=0.0) for _ in range(depth)]) + self.attn = AttnBlock(mid_channels) + self.res_block2 = nn.ModuleList([ResnetBlock(in_channels=mid_channels, + out_channels=mid_channels, + temb_channels=0, + dropout=0.0) for _ in range(depth)]) + + self.conv_out = nn.Conv2d(mid_channels, + out_channels, + kernel_size=1, + ) + + def forward(self, x): + x = self.conv_in(x) + for block in self.res_block1: + x = block(x, None) + x = torch.nn.functional.interpolate(x, size=(int(round(x.shape[2]*self.factor)), int(round(x.shape[3]*self.factor)))) + x = self.attn(x) + for block in self.res_block2: + x = block(x, None) + x = self.conv_out(x) + return x + + +class MergedRescaleEncoder(nn.Module): + def __init__(self, in_channels, ch, resolution, out_ch, num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, + ch_mult=(1,2,4,8), rescale_factor=1.0, rescale_module_depth=1): + super().__init__() + intermediate_chn = ch * ch_mult[-1] + self.encoder = Encoder(in_channels=in_channels, num_res_blocks=num_res_blocks, ch=ch, ch_mult=ch_mult, + z_channels=intermediate_chn, double_z=False, resolution=resolution, + attn_resolutions=attn_resolutions, dropout=dropout, resamp_with_conv=resamp_with_conv, + out_ch=None) + self.rescaler = LatentRescaler(factor=rescale_factor, in_channels=intermediate_chn, + mid_channels=intermediate_chn, out_channels=out_ch, depth=rescale_module_depth) + + def forward(self, x): + x = self.encoder(x) + x = self.rescaler(x) + return x + + +class MergedRescaleDecoder(nn.Module): + def __init__(self, z_channels, out_ch, resolution, num_res_blocks, attn_resolutions, ch, ch_mult=(1,2,4,8), + dropout=0.0, resamp_with_conv=True, rescale_factor=1.0, rescale_module_depth=1): + super().__init__() + tmp_chn = z_channels*ch_mult[-1] + self.decoder = Decoder(out_ch=out_ch, z_channels=tmp_chn, attn_resolutions=attn_resolutions, dropout=dropout, + resamp_with_conv=resamp_with_conv, in_channels=None, num_res_blocks=num_res_blocks, + ch_mult=ch_mult, resolution=resolution, ch=ch) + self.rescaler = LatentRescaler(factor=rescale_factor, in_channels=z_channels, mid_channels=tmp_chn, + out_channels=tmp_chn, depth=rescale_module_depth) + + def forward(self, x): + x = self.rescaler(x) + x = self.decoder(x) + return x + + +class Upsampler(nn.Module): + def __init__(self, in_size, out_size, in_channels, out_channels, ch_mult=2): + super().__init__() + assert out_size >= in_size + num_blocks = int(np.log2(out_size//in_size))+1 + factor_up = 1.+ (out_size % in_size) + print(f"Building {self.__class__.__name__} with in_size: {in_size} --> out_size {out_size} and factor {factor_up}") + self.rescaler = LatentRescaler(factor=factor_up, in_channels=in_channels, mid_channels=2*in_channels, + out_channels=in_channels) + self.decoder = Decoder(out_ch=out_channels, resolution=out_size, z_channels=in_channels, num_res_blocks=2, + attn_resolutions=[], in_channels=None, ch=in_channels, + ch_mult=[ch_mult for _ in range(num_blocks)]) + + def forward(self, x): + x = self.rescaler(x) + x = self.decoder(x) + return x + + +class Resize(nn.Module): + def __init__(self, in_channels=None, learned=False, mode="bilinear"): + super().__init__() + self.with_conv = learned + self.mode = mode + if self.with_conv: + print(f"Note: {self.__class__.__name} uses learned downsampling and will ignore the fixed {mode} mode") + raise NotImplementedError() + assert in_channels is not None + # no asymmetric padding in torch conv, must do it ourselves + self.conv = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=4, + stride=2, + padding=1) + + def forward(self, x, scale_factor=1.0): + if scale_factor==1.0: + return x + else: + x = torch.nn.functional.interpolate(x, mode=self.mode, align_corners=False, scale_factor=scale_factor) + return x diff --git a/repositories/ldm/modules/diffusionmodules/openaimodel.py b/repositories/ldm/modules/diffusionmodules/openaimodel.py new file mode 100644 index 000000000..cc3875c63 --- /dev/null +++ b/repositories/ldm/modules/diffusionmodules/openaimodel.py @@ -0,0 +1,807 @@ +from abc import abstractmethod +import math + +import numpy as np +import torch as th +import torch.nn as nn +import torch.nn.functional as F + +from ldm.modules.diffusionmodules.util import ( + checkpoint, + conv_nd, + linear, + avg_pool_nd, + zero_module, + normalization, + timestep_embedding, +) +from ldm.modules.attention import SpatialTransformer +from ldm.util import exists + + +# dummy replace +def convert_module_to_f16(x): + pass + +def convert_module_to_f32(x): + pass + + +## go +class AttentionPool2d(nn.Module): + """ + Adapted from CLIP: https://github.com/openai/CLIP/blob/main/clip/model.py + """ + + def __init__( + self, + spacial_dim: int, + embed_dim: int, + num_heads_channels: int, + output_dim: int = None, + ): + super().__init__() + self.positional_embedding = nn.Parameter(th.randn(embed_dim, spacial_dim ** 2 + 1) / embed_dim ** 0.5) + self.qkv_proj = conv_nd(1, embed_dim, 3 * embed_dim, 1) + self.c_proj = conv_nd(1, embed_dim, output_dim or embed_dim, 1) + self.num_heads = embed_dim // num_heads_channels + self.attention = QKVAttention(self.num_heads) + + def forward(self, x): + b, c, *_spatial = x.shape + x = x.reshape(b, c, -1) # NC(HW) + x = th.cat([x.mean(dim=-1, keepdim=True), x], dim=-1) # NC(HW+1) + x = x + self.positional_embedding[None, :, :].to(x.dtype) # NC(HW+1) + x = self.qkv_proj(x) + x = self.attention(x) + x = self.c_proj(x) + return x[:, :, 0] + + +class TimestepBlock(nn.Module): + """ + Any module where forward() takes timestep embeddings as a second argument. + """ + + @abstractmethod + def forward(self, x, emb): + """ + Apply the module to `x` given `emb` timestep embeddings. + """ + + +class TimestepEmbedSequential(nn.Sequential, TimestepBlock): + """ + A sequential module that passes timestep embeddings to the children that + support it as an extra input. + """ + + def forward(self, x, emb, context=None): + for layer in self: + if isinstance(layer, TimestepBlock): + x = layer(x, emb) + elif isinstance(layer, SpatialTransformer): + x = layer(x, context) + else: + x = layer(x) + return x + + +class Upsample(nn.Module): + """ + An upsampling layer with an optional convolution. + :param channels: channels in the inputs and outputs. + :param use_conv: a bool determining if a convolution is applied. + :param dims: determines if the signal is 1D, 2D, or 3D. If 3D, then + upsampling occurs in the inner-two dimensions. + """ + + def __init__(self, channels, use_conv, dims=2, out_channels=None, padding=1): + super().__init__() + self.channels = channels + self.out_channels = out_channels or channels + self.use_conv = use_conv + self.dims = dims + if use_conv: + self.conv = conv_nd(dims, self.channels, self.out_channels, 3, padding=padding) + + def forward(self, x): + assert x.shape[1] == self.channels + if self.dims == 3: + x = F.interpolate( + x, (x.shape[2], x.shape[3] * 2, x.shape[4] * 2), mode="nearest" + ) + else: + x = F.interpolate(x, scale_factor=2, mode="nearest") + if self.use_conv: + x = self.conv(x) + return x + +class TransposedUpsample(nn.Module): + 'Learned 2x upsampling without padding' + def __init__(self, channels, out_channels=None, ks=5): + super().__init__() + self.channels = channels + self.out_channels = out_channels or channels + + self.up = nn.ConvTranspose2d(self.channels,self.out_channels,kernel_size=ks,stride=2) + + def forward(self,x): + return self.up(x) + + +class Downsample(nn.Module): + """ + A downsampling layer with an optional convolution. + :param channels: channels in the inputs and outputs. + :param use_conv: a bool determining if a convolution is applied. + :param dims: determines if the signal is 1D, 2D, or 3D. If 3D, then + downsampling occurs in the inner-two dimensions. + """ + + def __init__(self, channels, use_conv, dims=2, out_channels=None,padding=1): + super().__init__() + self.channels = channels + self.out_channels = out_channels or channels + self.use_conv = use_conv + self.dims = dims + stride = 2 if dims != 3 else (1, 2, 2) + if use_conv: + self.op = conv_nd( + dims, self.channels, self.out_channels, 3, stride=stride, padding=padding + ) + else: + assert self.channels == self.out_channels + self.op = avg_pool_nd(dims, kernel_size=stride, stride=stride) + + def forward(self, x): + assert x.shape[1] == self.channels + return self.op(x) + + +class ResBlock(TimestepBlock): + """ + A residual block that can optionally change the number of channels. + :param channels: the number of input channels. + :param emb_channels: the number of timestep embedding channels. + :param dropout: the rate of dropout. + :param out_channels: if specified, the number of out channels. + :param use_conv: if True and out_channels is specified, use a spatial + convolution instead of a smaller 1x1 convolution to change the + channels in the skip connection. + :param dims: determines if the signal is 1D, 2D, or 3D. + :param use_checkpoint: if True, use gradient checkpointing on this module. + :param up: if True, use this block for upsampling. + :param down: if True, use this block for downsampling. + """ + + def __init__( + self, + channels, + emb_channels, + dropout, + out_channels=None, + use_conv=False, + use_scale_shift_norm=False, + dims=2, + use_checkpoint=False, + up=False, + down=False, + ): + super().__init__() + self.channels = channels + self.emb_channels = emb_channels + self.dropout = dropout + self.out_channels = out_channels or channels + self.use_conv = use_conv + self.use_checkpoint = use_checkpoint + self.use_scale_shift_norm = use_scale_shift_norm + + self.in_layers = nn.Sequential( + normalization(channels), + nn.SiLU(), + conv_nd(dims, channels, self.out_channels, 3, padding=1), + ) + + self.updown = up or down + + if up: + self.h_upd = Upsample(channels, False, dims) + self.x_upd = Upsample(channels, False, dims) + elif down: + self.h_upd = Downsample(channels, False, dims) + self.x_upd = Downsample(channels, False, dims) + else: + self.h_upd = self.x_upd = nn.Identity() + + self.emb_layers = nn.Sequential( + nn.SiLU(), + linear( + emb_channels, + 2 * self.out_channels if use_scale_shift_norm else self.out_channels, + ), + ) + self.out_layers = nn.Sequential( + normalization(self.out_channels), + nn.SiLU(), + nn.Dropout(p=dropout), + zero_module( + conv_nd(dims, self.out_channels, self.out_channels, 3, padding=1) + ), + ) + + if self.out_channels == channels: + self.skip_connection = nn.Identity() + elif use_conv: + self.skip_connection = conv_nd( + dims, channels, self.out_channels, 3, padding=1 + ) + else: + self.skip_connection = conv_nd(dims, channels, self.out_channels, 1) + + def forward(self, x, emb): + """ + Apply the block to a Tensor, conditioned on a timestep embedding. + :param x: an [N x C x ...] Tensor of features. + :param emb: an [N x emb_channels] Tensor of timestep embeddings. + :return: an [N x C x ...] Tensor of outputs. + """ + return checkpoint( + self._forward, (x, emb), self.parameters(), self.use_checkpoint + ) + + + def _forward(self, x, emb): + if self.updown: + in_rest, in_conv = self.in_layers[:-1], self.in_layers[-1] + h = in_rest(x) + h = self.h_upd(h) + x = self.x_upd(x) + h = in_conv(h) + else: + h = self.in_layers(x) + emb_out = self.emb_layers(emb).type(h.dtype) + while len(emb_out.shape) < len(h.shape): + emb_out = emb_out[..., None] + if self.use_scale_shift_norm: + out_norm, out_rest = self.out_layers[0], self.out_layers[1:] + scale, shift = th.chunk(emb_out, 2, dim=1) + h = out_norm(h) * (1 + scale) + shift + h = out_rest(h) + else: + h = h + emb_out + h = self.out_layers(h) + return self.skip_connection(x) + h + + +class AttentionBlock(nn.Module): + """ + An attention block that allows spatial positions to attend to each other. + Originally ported from here, but adapted to the N-d case. + https://github.com/hojonathanho/diffusion/blob/1e0dceb3b3495bbe19116a5e1b3596cd0706c543/diffusion_tf/models/unet.py#L66. + """ + + def __init__( + self, + channels, + num_heads=1, + num_head_channels=-1, + use_checkpoint=False, + use_new_attention_order=False, + ): + super().__init__() + self.channels = channels + if num_head_channels == -1: + self.num_heads = num_heads + else: + assert ( + channels % num_head_channels == 0 + ), f"q,k,v channels {channels} is not divisible by num_head_channels {num_head_channels}" + self.num_heads = channels // num_head_channels + self.use_checkpoint = use_checkpoint + self.norm = normalization(channels) + self.qkv = conv_nd(1, channels, channels * 3, 1) + if use_new_attention_order: + # split qkv before split heads + self.attention = QKVAttention(self.num_heads) + else: + # split heads before split qkv + self.attention = QKVAttentionLegacy(self.num_heads) + + self.proj_out = zero_module(conv_nd(1, channels, channels, 1)) + + def forward(self, x): + return checkpoint(self._forward, (x,), self.parameters(), True) # TODO: check checkpoint usage, is True # TODO: fix the .half call!!! + #return pt_checkpoint(self._forward, x) # pytorch + + def _forward(self, x): + b, c, *spatial = x.shape + x = x.reshape(b, c, -1) + qkv = self.qkv(self.norm(x)) + h = self.attention(qkv) + h = self.proj_out(h) + return (x + h).reshape(b, c, *spatial) + + +def count_flops_attn(model, _x, y): + """ + A counter for the `thop` package to count the operations in an + attention operation. + Meant to be used like: + macs, params = thop.profile( + model, + inputs=(inputs, timestamps), + custom_ops={QKVAttention: QKVAttention.count_flops}, + ) + """ + b, c, *spatial = y[0].shape + num_spatial = int(np.prod(spatial)) + # We perform two matmuls with the same number of ops. + # The first computes the weight matrix, the second computes + # the combination of the value vectors. + matmul_ops = 2 * b * (num_spatial ** 2) * c + model.total_ops += th.DoubleTensor([matmul_ops]) + + +class QKVAttentionLegacy(nn.Module): + """ + A module which performs QKV attention. Matches legacy QKVAttention + input/ouput heads shaping + """ + + def __init__(self, n_heads): + super().__init__() + self.n_heads = n_heads + + def forward(self, qkv): + """ + Apply QKV attention. + :param qkv: an [N x (H * 3 * C) x T] tensor of Qs, Ks, and Vs. + :return: an [N x (H * C) x T] tensor after attention. + """ + bs, width, length = qkv.shape + assert width % (3 * self.n_heads) == 0 + ch = width // (3 * self.n_heads) + q, k, v = qkv.reshape(bs * self.n_heads, ch * 3, length).split(ch, dim=1) + scale = 1 / math.sqrt(math.sqrt(ch)) + weight = th.einsum( + "bct,bcs->bts", q * scale, k * scale + ) # More stable with f16 than dividing afterwards + weight = th.softmax(weight.float(), dim=-1).type(weight.dtype) + a = th.einsum("bts,bcs->bct", weight, v) + return a.reshape(bs, -1, length) + + @staticmethod + def count_flops(model, _x, y): + return count_flops_attn(model, _x, y) + + +class QKVAttention(nn.Module): + """ + A module which performs QKV attention and splits in a different order. + """ + + def __init__(self, n_heads): + super().__init__() + self.n_heads = n_heads + + def forward(self, qkv): + """ + Apply QKV attention. + :param qkv: an [N x (3 * H * C) x T] tensor of Qs, Ks, and Vs. + :return: an [N x (H * C) x T] tensor after attention. + """ + bs, width, length = qkv.shape + assert width % (3 * self.n_heads) == 0 + ch = width // (3 * self.n_heads) + q, k, v = qkv.chunk(3, dim=1) + scale = 1 / math.sqrt(math.sqrt(ch)) + weight = th.einsum( + "bct,bcs->bts", + (q * scale).view(bs * self.n_heads, ch, length), + (k * scale).view(bs * self.n_heads, ch, length), + ) # More stable with f16 than dividing afterwards + weight = th.softmax(weight.float(), dim=-1).type(weight.dtype) + a = th.einsum("bts,bcs->bct", weight, v.reshape(bs * self.n_heads, ch, length)) + return a.reshape(bs, -1, length) + + @staticmethod + def count_flops(model, _x, y): + return count_flops_attn(model, _x, y) + + +class Timestep(nn.Module): + def __init__(self, dim): + super().__init__() + self.dim = dim + + def forward(self, t): + return timestep_embedding(t, self.dim) + + +class UNetModel(nn.Module): + """ + The full UNet model with attention and timestep embedding. + :param in_channels: channels in the input Tensor. + :param model_channels: base channel count for the model. + :param out_channels: channels in the output Tensor. + :param num_res_blocks: number of residual blocks per downsample. + :param attention_resolutions: a collection of downsample rates at which + attention will take place. May be a set, list, or tuple. + For example, if this contains 4, then at 4x downsampling, attention + will be used. + :param dropout: the dropout probability. + :param channel_mult: channel multiplier for each level of the UNet. + :param conv_resample: if True, use learned convolutions for upsampling and + downsampling. + :param dims: determines if the signal is 1D, 2D, or 3D. + :param num_classes: if specified (as an int), then this model will be + class-conditional with `num_classes` classes. + :param use_checkpoint: use gradient checkpointing to reduce memory usage. + :param num_heads: the number of attention heads in each attention layer. + :param num_heads_channels: if specified, ignore num_heads and instead use + a fixed channel width per attention head. + :param num_heads_upsample: works with num_heads to set a different number + of heads for upsampling. Deprecated. + :param use_scale_shift_norm: use a FiLM-like conditioning mechanism. + :param resblock_updown: use residual blocks for up/downsampling. + :param use_new_attention_order: use a different attention pattern for potentially + increased efficiency. + """ + + def __init__( + self, + image_size, + in_channels, + model_channels, + out_channels, + num_res_blocks, + attention_resolutions, + dropout=0, + channel_mult=(1, 2, 4, 8), + conv_resample=True, + dims=2, + num_classes=None, + use_checkpoint=False, + use_fp16=False, + use_bf16=False, + num_heads=-1, + num_head_channels=-1, + num_heads_upsample=-1, + use_scale_shift_norm=False, + resblock_updown=False, + use_new_attention_order=False, + use_spatial_transformer=False, # custom transformer support + transformer_depth=1, # custom transformer support + context_dim=None, # custom transformer support + n_embed=None, # custom support for prediction of discrete ids into codebook of first stage vq model + legacy=True, + disable_self_attentions=None, + num_attention_blocks=None, + disable_middle_self_attn=False, + use_linear_in_transformer=False, + adm_in_channels=None, + ): + super().__init__() + if use_spatial_transformer: + assert context_dim is not None, 'Fool!! You forgot to include the dimension of your cross-attention conditioning...' + + if context_dim is not None: + assert use_spatial_transformer, 'Fool!! You forgot to use the spatial transformer for your cross-attention conditioning...' + from omegaconf.listconfig import ListConfig + if type(context_dim) == ListConfig: + context_dim = list(context_dim) + + if num_heads_upsample == -1: + num_heads_upsample = num_heads + + if num_heads == -1: + assert num_head_channels != -1, 'Either num_heads or num_head_channels has to be set' + + if num_head_channels == -1: + assert num_heads != -1, 'Either num_heads or num_head_channels has to be set' + + self.image_size = image_size + self.in_channels = in_channels + self.model_channels = model_channels + self.out_channels = out_channels + if isinstance(num_res_blocks, int): + self.num_res_blocks = len(channel_mult) * [num_res_blocks] + else: + if len(num_res_blocks) != len(channel_mult): + raise ValueError("provide num_res_blocks either as an int (globally constant) or " + "as a list/tuple (per-level) with the same length as channel_mult") + self.num_res_blocks = num_res_blocks + if disable_self_attentions is not None: + # should be a list of booleans, indicating whether to disable self-attention in TransformerBlocks or not + assert len(disable_self_attentions) == len(channel_mult) + if num_attention_blocks is not None: + assert len(num_attention_blocks) == len(self.num_res_blocks) + assert all(map(lambda i: self.num_res_blocks[i] >= num_attention_blocks[i], range(len(num_attention_blocks)))) + print(f"Constructor of UNetModel received num_attention_blocks={num_attention_blocks}. " + f"This option has LESS priority than attention_resolutions {attention_resolutions}, " + f"i.e., in cases where num_attention_blocks[i] > 0 but 2**i not in attention_resolutions, " + f"attention will still not be set.") + + self.attention_resolutions = attention_resolutions + self.dropout = dropout + self.channel_mult = channel_mult + self.conv_resample = conv_resample + self.num_classes = num_classes + self.use_checkpoint = use_checkpoint + self.dtype = th.float16 if use_fp16 else th.float32 + self.dtype = th.bfloat16 if use_bf16 else self.dtype + self.num_heads = num_heads + self.num_head_channels = num_head_channels + self.num_heads_upsample = num_heads_upsample + self.predict_codebook_ids = n_embed is not None + + time_embed_dim = model_channels * 4 + self.time_embed = nn.Sequential( + linear(model_channels, time_embed_dim), + nn.SiLU(), + linear(time_embed_dim, time_embed_dim), + ) + + if self.num_classes is not None: + if isinstance(self.num_classes, int): + self.label_emb = nn.Embedding(num_classes, time_embed_dim) + elif self.num_classes == "continuous": + print("setting up linear c_adm embedding layer") + self.label_emb = nn.Linear(1, time_embed_dim) + elif self.num_classes == "sequential": + assert adm_in_channels is not None + self.label_emb = nn.Sequential( + nn.Sequential( + linear(adm_in_channels, time_embed_dim), + nn.SiLU(), + linear(time_embed_dim, time_embed_dim), + ) + ) + else: + raise ValueError() + + self.input_blocks = nn.ModuleList( + [ + TimestepEmbedSequential( + conv_nd(dims, in_channels, model_channels, 3, padding=1) + ) + ] + ) + self._feature_size = model_channels + input_block_chans = [model_channels] + ch = model_channels + ds = 1 + for level, mult in enumerate(channel_mult): + for nr in range(self.num_res_blocks[level]): + layers = [ + ResBlock( + ch, + time_embed_dim, + dropout, + out_channels=mult * model_channels, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + ) + ] + ch = mult * model_channels + if ds in attention_resolutions: + if num_head_channels == -1: + dim_head = ch // num_heads + else: + num_heads = ch // num_head_channels + dim_head = num_head_channels + if legacy: + #num_heads = 1 + dim_head = ch // num_heads if use_spatial_transformer else num_head_channels + if exists(disable_self_attentions): + disabled_sa = disable_self_attentions[level] + else: + disabled_sa = False + + if not exists(num_attention_blocks) or nr < num_attention_blocks[level]: + layers.append( + AttentionBlock( + ch, + use_checkpoint=use_checkpoint, + num_heads=num_heads, + num_head_channels=dim_head, + use_new_attention_order=use_new_attention_order, + ) if not use_spatial_transformer else SpatialTransformer( + ch, num_heads, dim_head, depth=transformer_depth, context_dim=context_dim, + disable_self_attn=disabled_sa, use_linear=use_linear_in_transformer, + use_checkpoint=use_checkpoint + ) + ) + self.input_blocks.append(TimestepEmbedSequential(*layers)) + self._feature_size += ch + input_block_chans.append(ch) + if level != len(channel_mult) - 1: + out_ch = ch + self.input_blocks.append( + TimestepEmbedSequential( + ResBlock( + ch, + time_embed_dim, + dropout, + out_channels=out_ch, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + down=True, + ) + if resblock_updown + else Downsample( + ch, conv_resample, dims=dims, out_channels=out_ch + ) + ) + ) + ch = out_ch + input_block_chans.append(ch) + ds *= 2 + self._feature_size += ch + + if num_head_channels == -1: + dim_head = ch // num_heads + else: + num_heads = ch // num_head_channels + dim_head = num_head_channels + if legacy: + #num_heads = 1 + dim_head = ch // num_heads if use_spatial_transformer else num_head_channels + self.middle_block = TimestepEmbedSequential( + ResBlock( + ch, + time_embed_dim, + dropout, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + ), + AttentionBlock( + ch, + use_checkpoint=use_checkpoint, + num_heads=num_heads, + num_head_channels=dim_head, + use_new_attention_order=use_new_attention_order, + ) if not use_spatial_transformer else SpatialTransformer( # always uses a self-attn + ch, num_heads, dim_head, depth=transformer_depth, context_dim=context_dim, + disable_self_attn=disable_middle_self_attn, use_linear=use_linear_in_transformer, + use_checkpoint=use_checkpoint + ), + ResBlock( + ch, + time_embed_dim, + dropout, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + ), + ) + self._feature_size += ch + + self.output_blocks = nn.ModuleList([]) + for level, mult in list(enumerate(channel_mult))[::-1]: + for i in range(self.num_res_blocks[level] + 1): + ich = input_block_chans.pop() + layers = [ + ResBlock( + ch + ich, + time_embed_dim, + dropout, + out_channels=model_channels * mult, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + ) + ] + ch = model_channels * mult + if ds in attention_resolutions: + if num_head_channels == -1: + dim_head = ch // num_heads + else: + num_heads = ch // num_head_channels + dim_head = num_head_channels + if legacy: + #num_heads = 1 + dim_head = ch // num_heads if use_spatial_transformer else num_head_channels + if exists(disable_self_attentions): + disabled_sa = disable_self_attentions[level] + else: + disabled_sa = False + + if not exists(num_attention_blocks) or i < num_attention_blocks[level]: + layers.append( + AttentionBlock( + ch, + use_checkpoint=use_checkpoint, + num_heads=num_heads_upsample, + num_head_channels=dim_head, + use_new_attention_order=use_new_attention_order, + ) if not use_spatial_transformer else SpatialTransformer( + ch, num_heads, dim_head, depth=transformer_depth, context_dim=context_dim, + disable_self_attn=disabled_sa, use_linear=use_linear_in_transformer, + use_checkpoint=use_checkpoint + ) + ) + if level and i == self.num_res_blocks[level]: + out_ch = ch + layers.append( + ResBlock( + ch, + time_embed_dim, + dropout, + out_channels=out_ch, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + up=True, + ) + if resblock_updown + else Upsample(ch, conv_resample, dims=dims, out_channels=out_ch) + ) + ds //= 2 + self.output_blocks.append(TimestepEmbedSequential(*layers)) + self._feature_size += ch + + self.out = nn.Sequential( + normalization(ch), + nn.SiLU(), + zero_module(conv_nd(dims, model_channels, out_channels, 3, padding=1)), + ) + if self.predict_codebook_ids: + self.id_predictor = nn.Sequential( + normalization(ch), + conv_nd(dims, model_channels, n_embed, 1), + #nn.LogSoftmax(dim=1) # change to cross_entropy and produce non-normalized logits + ) + + def convert_to_fp16(self): + """ + Convert the torso of the model to float16. + """ + self.input_blocks.apply(convert_module_to_f16) + self.middle_block.apply(convert_module_to_f16) + self.output_blocks.apply(convert_module_to_f16) + + def convert_to_fp32(self): + """ + Convert the torso of the model to float32. + """ + self.input_blocks.apply(convert_module_to_f32) + self.middle_block.apply(convert_module_to_f32) + self.output_blocks.apply(convert_module_to_f32) + + def forward(self, x, timesteps=None, context=None, y=None,**kwargs): + """ + Apply the model to an input batch. + :param x: an [N x C x ...] Tensor of inputs. + :param timesteps: a 1-D batch of timesteps. + :param context: conditioning plugged in via crossattn + :param y: an [N] Tensor of labels, if class-conditional. + :return: an [N x C x ...] Tensor of outputs. + """ + assert (y is not None) == ( + self.num_classes is not None + ), "must specify y if and only if the model is class-conditional" + hs = [] + t_emb = timestep_embedding(timesteps, self.model_channels, repeat_only=False) + emb = self.time_embed(t_emb) + + if self.num_classes is not None: + assert y.shape[0] == x.shape[0] + emb = emb + self.label_emb(y) + + h = x.type(self.dtype) + for module in self.input_blocks: + h = module(h, emb, context) + hs.append(h) + h = self.middle_block(h, emb, context) + for module in self.output_blocks: + h = th.cat([h, hs.pop()], dim=1) + h = module(h, emb, context) + h = h.type(x.dtype) + if self.predict_codebook_ids: + return self.id_predictor(h) + else: + return self.out(h) diff --git a/repositories/ldm/modules/diffusionmodules/upscaling.py b/repositories/ldm/modules/diffusionmodules/upscaling.py new file mode 100644 index 000000000..038166620 --- /dev/null +++ b/repositories/ldm/modules/diffusionmodules/upscaling.py @@ -0,0 +1,81 @@ +import torch +import torch.nn as nn +import numpy as np +from functools import partial + +from ldm.modules.diffusionmodules.util import extract_into_tensor, make_beta_schedule +from ldm.util import default + + +class AbstractLowScaleModel(nn.Module): + # for concatenating a downsampled image to the latent representation + def __init__(self, noise_schedule_config=None): + super(AbstractLowScaleModel, self).__init__() + if noise_schedule_config is not None: + self.register_schedule(**noise_schedule_config) + + def register_schedule(self, beta_schedule="linear", timesteps=1000, + linear_start=1e-4, linear_end=2e-2, cosine_s=8e-3): + betas = make_beta_schedule(beta_schedule, timesteps, linear_start=linear_start, linear_end=linear_end, + cosine_s=cosine_s) + alphas = 1. - betas + alphas_cumprod = np.cumprod(alphas, axis=0) + alphas_cumprod_prev = np.append(1., alphas_cumprod[:-1]) + + timesteps, = betas.shape + self.num_timesteps = int(timesteps) + self.linear_start = linear_start + self.linear_end = linear_end + assert alphas_cumprod.shape[0] == self.num_timesteps, 'alphas have to be defined for each timestep' + + to_torch = partial(torch.tensor, dtype=torch.float32) + + self.register_buffer('betas', to_torch(betas)) + self.register_buffer('alphas_cumprod', to_torch(alphas_cumprod)) + self.register_buffer('alphas_cumprod_prev', to_torch(alphas_cumprod_prev)) + + # calculations for diffusion q(x_t | x_{t-1}) and others + self.register_buffer('sqrt_alphas_cumprod', to_torch(np.sqrt(alphas_cumprod))) + self.register_buffer('sqrt_one_minus_alphas_cumprod', to_torch(np.sqrt(1. - alphas_cumprod))) + self.register_buffer('log_one_minus_alphas_cumprod', to_torch(np.log(1. - alphas_cumprod))) + self.register_buffer('sqrt_recip_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod))) + self.register_buffer('sqrt_recipm1_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod - 1))) + + def q_sample(self, x_start, t, noise=None): + noise = default(noise, lambda: torch.randn_like(x_start)) + return (extract_into_tensor(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start + + extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x_start.shape) * noise) + + def forward(self, x): + return x, None + + def decode(self, x): + return x + + +class SimpleImageConcat(AbstractLowScaleModel): + # no noise level conditioning + def __init__(self): + super(SimpleImageConcat, self).__init__(noise_schedule_config=None) + self.max_noise_level = 0 + + def forward(self, x): + # fix to constant noise level + return x, torch.zeros(x.shape[0], device=x.device).long() + + +class ImageConcatWithNoiseAugmentation(AbstractLowScaleModel): + def __init__(self, noise_schedule_config, max_noise_level=1000, to_cuda=False): + super().__init__(noise_schedule_config=noise_schedule_config) + self.max_noise_level = max_noise_level + + def forward(self, x, noise_level=None): + if noise_level is None: + noise_level = torch.randint(0, self.max_noise_level, (x.shape[0],), device=x.device).long() + else: + assert isinstance(noise_level, torch.Tensor) + z = self.q_sample(x, noise_level) + return z, noise_level + + + diff --git a/repositories/ldm/modules/diffusionmodules/util.py b/repositories/ldm/modules/diffusionmodules/util.py new file mode 100644 index 000000000..daf35da7b --- /dev/null +++ b/repositories/ldm/modules/diffusionmodules/util.py @@ -0,0 +1,278 @@ +# adopted from +# https://github.com/openai/improved-diffusion/blob/main/improved_diffusion/gaussian_diffusion.py +# and +# https://github.com/lucidrains/denoising-diffusion-pytorch/blob/7706bdfc6f527f58d33f84b7b522e61e6e3164b3/denoising_diffusion_pytorch/denoising_diffusion_pytorch.py +# and +# https://github.com/openai/guided-diffusion/blob/0ba878e517b276c45d1195eb29f6f5f72659a05b/guided_diffusion/nn.py +# +# thanks! + + +import os +import math +import torch +import torch.nn as nn +import numpy as np +from einops import repeat + +from ldm.util import instantiate_from_config + + +def make_beta_schedule(schedule, n_timestep, linear_start=1e-4, linear_end=2e-2, cosine_s=8e-3): + if schedule == "linear": + betas = ( + torch.linspace(linear_start ** 0.5, linear_end ** 0.5, n_timestep, dtype=torch.float64) ** 2 + ) + + elif schedule == "cosine": + timesteps = ( + torch.arange(n_timestep + 1, dtype=torch.float64) / n_timestep + cosine_s + ) + alphas = timesteps / (1 + cosine_s) * np.pi / 2 + alphas = torch.cos(alphas).pow(2) + alphas = alphas / alphas[0] + betas = 1 - alphas[1:] / alphas[:-1] + betas = np.clip(betas, a_min=0, a_max=0.999) + + elif schedule == "squaredcos_cap_v2": # used for karlo prior + # return early + return betas_for_alpha_bar( + n_timestep, + lambda t: math.cos((t + 0.008) / 1.008 * math.pi / 2) ** 2, + ) + + elif schedule == "sqrt_linear": + betas = torch.linspace(linear_start, linear_end, n_timestep, dtype=torch.float64) + elif schedule == "sqrt": + betas = torch.linspace(linear_start, linear_end, n_timestep, dtype=torch.float64) ** 0.5 + else: + raise ValueError(f"schedule '{schedule}' unknown.") + return betas.numpy() + + +def make_ddim_timesteps(ddim_discr_method, num_ddim_timesteps, num_ddpm_timesteps, verbose=True): + if ddim_discr_method == 'uniform': + c = num_ddpm_timesteps // num_ddim_timesteps + ddim_timesteps = np.asarray(list(range(0, num_ddpm_timesteps, c))) + elif ddim_discr_method == 'quad': + ddim_timesteps = ((np.linspace(0, np.sqrt(num_ddpm_timesteps * .8), num_ddim_timesteps)) ** 2).astype(int) + else: + raise NotImplementedError(f'There is no ddim discretization method called "{ddim_discr_method}"') + + # assert ddim_timesteps.shape[0] == num_ddim_timesteps + # add one to get the final alpha values right (the ones from first scale to data during sampling) + steps_out = ddim_timesteps + 1 + if verbose: + print(f'Selected timesteps for ddim sampler: {steps_out}') + return steps_out + + +def make_ddim_sampling_parameters(alphacums, ddim_timesteps, eta, verbose=True): + # select alphas for computing the variance schedule + alphas = alphacums[ddim_timesteps] + alphas_prev = np.asarray([alphacums[0]] + alphacums[ddim_timesteps[:-1]].tolist()) + + # according the the formula provided in https://arxiv.org/abs/2010.02502 + sigmas = eta * np.sqrt((1 - alphas_prev) / (1 - alphas) * (1 - alphas / alphas_prev)) + if verbose: + print(f'Selected alphas for ddim sampler: a_t: {alphas}; a_(t-1): {alphas_prev}') + print(f'For the chosen value of eta, which is {eta}, ' + f'this results in the following sigma_t schedule for ddim sampler {sigmas}') + return sigmas, alphas, alphas_prev + + +def betas_for_alpha_bar(num_diffusion_timesteps, alpha_bar, max_beta=0.999): + """ + Create a beta schedule that discretizes the given alpha_t_bar function, + which defines the cumulative product of (1-beta) over time from t = [0,1]. + :param num_diffusion_timesteps: the number of betas to produce. + :param alpha_bar: a lambda that takes an argument t from 0 to 1 and + produces the cumulative product of (1-beta) up to that + part of the diffusion process. + :param max_beta: the maximum beta to use; use values lower than 1 to + prevent singularities. + """ + betas = [] + for i in range(num_diffusion_timesteps): + t1 = i / num_diffusion_timesteps + t2 = (i + 1) / num_diffusion_timesteps + betas.append(min(1 - alpha_bar(t2) / alpha_bar(t1), max_beta)) + return np.array(betas) + + +def extract_into_tensor(a, t, x_shape): + b, *_ = t.shape + out = a.gather(-1, t) + return out.reshape(b, *((1,) * (len(x_shape) - 1))) + + +def checkpoint(func, inputs, params, flag): + """ + Evaluate a function without caching intermediate activations, allowing for + reduced memory at the expense of extra compute in the backward pass. + :param func: the function to evaluate. + :param inputs: the argument sequence to pass to `func`. + :param params: a sequence of parameters `func` depends on but does not + explicitly take as arguments. + :param flag: if False, disable gradient checkpointing. + """ + if flag: + args = tuple(inputs) + tuple(params) + return CheckpointFunction.apply(func, len(inputs), *args) + else: + return func(*inputs) + + +class CheckpointFunction(torch.autograd.Function): + @staticmethod + def forward(ctx, run_function, length, *args): + ctx.run_function = run_function + ctx.input_tensors = list(args[:length]) + ctx.input_params = list(args[length:]) + ctx.gpu_autocast_kwargs = {"enabled": torch.is_autocast_enabled(), + "dtype": torch.get_autocast_gpu_dtype(), + "cache_enabled": torch.is_autocast_cache_enabled()} + with torch.no_grad(): + output_tensors = ctx.run_function(*ctx.input_tensors) + return output_tensors + + @staticmethod + def backward(ctx, *output_grads): + ctx.input_tensors = [x.detach().requires_grad_(True) for x in ctx.input_tensors] + with torch.enable_grad(), \ + torch.cuda.amp.autocast(**ctx.gpu_autocast_kwargs): + # Fixes a bug where the first op in run_function modifies the + # Tensor storage in place, which is not allowed for detach()'d + # Tensors. + shallow_copies = [x.view_as(x) for x in ctx.input_tensors] + output_tensors = ctx.run_function(*shallow_copies) + input_grads = torch.autograd.grad( + output_tensors, + ctx.input_tensors + ctx.input_params, + output_grads, + allow_unused=True, + ) + del ctx.input_tensors + del ctx.input_params + del output_tensors + return (None, None) + input_grads + + +def timestep_embedding(timesteps, dim, max_period=10000, repeat_only=False): + """ + Create sinusoidal timestep embeddings. + :param timesteps: a 1-D Tensor of N indices, one per batch element. + These may be fractional. + :param dim: the dimension of the output. + :param max_period: controls the minimum frequency of the embeddings. + :return: an [N x dim] Tensor of positional embeddings. + """ + if not repeat_only: + half = dim // 2 + freqs = torch.exp( + -math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half + ).to(device=timesteps.device) + args = timesteps[:, None].float() * freqs[None] + embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1) + if dim % 2: + embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1) + else: + embedding = repeat(timesteps, 'b -> b d', d=dim) + return embedding + + +def zero_module(module): + """ + Zero out the parameters of a module and return it. + """ + for p in module.parameters(): + p.detach().zero_() + return module + + +def scale_module(module, scale): + """ + Scale the parameters of a module and return it. + """ + for p in module.parameters(): + p.detach().mul_(scale) + return module + + +def mean_flat(tensor): + """ + Take the mean over all non-batch dimensions. + """ + return tensor.mean(dim=list(range(1, len(tensor.shape)))) + + +def normalization(channels): + """ + Make a standard normalization layer. + :param channels: number of input channels. + :return: an nn.Module for normalization. + """ + return GroupNorm32(32, channels) + + +# PyTorch 1.7 has SiLU, but we support PyTorch 1.5. +class SiLU(nn.Module): + def forward(self, x): + return x * torch.sigmoid(x) + + +class GroupNorm32(nn.GroupNorm): + def forward(self, x): + return super().forward(x.float()).type(x.dtype) + + +def conv_nd(dims, *args, **kwargs): + """ + Create a 1D, 2D, or 3D convolution module. + """ + if dims == 1: + return nn.Conv1d(*args, **kwargs) + elif dims == 2: + return nn.Conv2d(*args, **kwargs) + elif dims == 3: + return nn.Conv3d(*args, **kwargs) + raise ValueError(f"unsupported dimensions: {dims}") + + +def linear(*args, **kwargs): + """ + Create a linear module. + """ + return nn.Linear(*args, **kwargs) + + +def avg_pool_nd(dims, *args, **kwargs): + """ + Create a 1D, 2D, or 3D average pooling module. + """ + if dims == 1: + return nn.AvgPool1d(*args, **kwargs) + elif dims == 2: + return nn.AvgPool2d(*args, **kwargs) + elif dims == 3: + return nn.AvgPool3d(*args, **kwargs) + raise ValueError(f"unsupported dimensions: {dims}") + + +class HybridConditioner(nn.Module): + + def __init__(self, c_concat_config, c_crossattn_config): + super().__init__() + self.concat_conditioner = instantiate_from_config(c_concat_config) + self.crossattn_conditioner = instantiate_from_config(c_crossattn_config) + + def forward(self, c_concat, c_crossattn): + c_concat = self.concat_conditioner(c_concat) + c_crossattn = self.crossattn_conditioner(c_crossattn) + return {'c_concat': [c_concat], 'c_crossattn': [c_crossattn]} + + +def noise_like(shape, device, repeat=False): + repeat_noise = lambda: torch.randn((1, *shape[1:]), device=device).repeat(shape[0], *((1,) * (len(shape) - 1))) + noise = lambda: torch.randn(shape, device=device) + return repeat_noise() if repeat else noise() diff --git a/repositories/ldm/modules/distributions/__init__.py b/repositories/ldm/modules/distributions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/ldm/modules/distributions/distributions.py b/repositories/ldm/modules/distributions/distributions.py new file mode 100644 index 000000000..f2b8ef901 --- /dev/null +++ b/repositories/ldm/modules/distributions/distributions.py @@ -0,0 +1,92 @@ +import torch +import numpy as np + + +class AbstractDistribution: + def sample(self): + raise NotImplementedError() + + def mode(self): + raise NotImplementedError() + + +class DiracDistribution(AbstractDistribution): + def __init__(self, value): + self.value = value + + def sample(self): + return self.value + + def mode(self): + return self.value + + +class DiagonalGaussianDistribution(object): + def __init__(self, parameters, deterministic=False): + self.parameters = parameters + self.mean, self.logvar = torch.chunk(parameters, 2, dim=1) + self.logvar = torch.clamp(self.logvar, -30.0, 20.0) + self.deterministic = deterministic + self.std = torch.exp(0.5 * self.logvar) + self.var = torch.exp(self.logvar) + if self.deterministic: + self.var = self.std = torch.zeros_like(self.mean).to(device=self.parameters.device) + + def sample(self): + x = self.mean + self.std * torch.randn(self.mean.shape).to(device=self.parameters.device) + return x + + def kl(self, other=None): + if self.deterministic: + return torch.Tensor([0.]) + else: + if other is None: + return 0.5 * torch.sum(torch.pow(self.mean, 2) + + self.var - 1.0 - self.logvar, + dim=[1, 2, 3]) + else: + return 0.5 * torch.sum( + torch.pow(self.mean - other.mean, 2) / other.var + + self.var / other.var - 1.0 - self.logvar + other.logvar, + dim=[1, 2, 3]) + + def nll(self, sample, dims=[1,2,3]): + if self.deterministic: + return torch.Tensor([0.]) + logtwopi = np.log(2.0 * np.pi) + return 0.5 * torch.sum( + logtwopi + self.logvar + torch.pow(sample - self.mean, 2) / self.var, + dim=dims) + + def mode(self): + return self.mean + + +def normal_kl(mean1, logvar1, mean2, logvar2): + """ + source: https://github.com/openai/guided-diffusion/blob/27c20a8fab9cb472df5d6bdd6c8d11c8f430b924/guided_diffusion/losses.py#L12 + Compute the KL divergence between two gaussians. + Shapes are automatically broadcasted, so batches can be compared to + scalars, among other use cases. + """ + tensor = None + for obj in (mean1, logvar1, mean2, logvar2): + if isinstance(obj, torch.Tensor): + tensor = obj + break + assert tensor is not None, "at least one argument must be a Tensor" + + # Force variances to be Tensors. Broadcasting helps convert scalars to + # Tensors, but it does not work for torch.exp(). + logvar1, logvar2 = [ + x if isinstance(x, torch.Tensor) else torch.tensor(x).to(tensor) + for x in (logvar1, logvar2) + ] + + return 0.5 * ( + -1.0 + + logvar2 + - logvar1 + + torch.exp(logvar1 - logvar2) + + ((mean1 - mean2) ** 2) * torch.exp(-logvar2) + ) diff --git a/repositories/ldm/modules/ema.py b/repositories/ldm/modules/ema.py new file mode 100644 index 000000000..bded25019 --- /dev/null +++ b/repositories/ldm/modules/ema.py @@ -0,0 +1,80 @@ +import torch +from torch import nn + + +class LitEma(nn.Module): + def __init__(self, model, decay=0.9999, use_num_upates=True): + super().__init__() + if decay < 0.0 or decay > 1.0: + raise ValueError('Decay must be between 0 and 1') + + self.m_name2s_name = {} + self.register_buffer('decay', torch.tensor(decay, dtype=torch.float32)) + self.register_buffer('num_updates', torch.tensor(0, dtype=torch.int) if use_num_upates + else torch.tensor(-1, dtype=torch.int)) + + for name, p in model.named_parameters(): + if p.requires_grad: + # remove as '.'-character is not allowed in buffers + s_name = name.replace('.', '') + self.m_name2s_name.update({name: s_name}) + self.register_buffer(s_name, p.clone().detach().data) + + self.collected_params = [] + + def reset_num_updates(self): + del self.num_updates + self.register_buffer('num_updates', torch.tensor(0, dtype=torch.int)) + + def forward(self, model): + decay = self.decay + + if self.num_updates >= 0: + self.num_updates += 1 + decay = min(self.decay, (1 + self.num_updates) / (10 + self.num_updates)) + + one_minus_decay = 1.0 - decay + + with torch.no_grad(): + m_param = dict(model.named_parameters()) + shadow_params = dict(self.named_buffers()) + + for key in m_param: + if m_param[key].requires_grad: + sname = self.m_name2s_name[key] + shadow_params[sname] = shadow_params[sname].type_as(m_param[key]) + shadow_params[sname].sub_(one_minus_decay * (shadow_params[sname] - m_param[key])) + else: + assert not key in self.m_name2s_name + + def copy_to(self, model): + m_param = dict(model.named_parameters()) + shadow_params = dict(self.named_buffers()) + for key in m_param: + if m_param[key].requires_grad: + m_param[key].data.copy_(shadow_params[self.m_name2s_name[key]].data) + else: + assert not key in self.m_name2s_name + + def store(self, parameters): + """ + Save the current parameters for restoring later. + Args: + parameters: Iterable of `torch.nn.Parameter`; the parameters to be + temporarily stored. + """ + self.collected_params = [param.clone() for param in parameters] + + def restore(self, parameters): + """ + Restore the parameters stored with the `store` method. + Useful to validate the model with EMA parameters without affecting the + original optimization process. Store the parameters before the + `copy_to` method. After validation (or model saving), use this to + restore the former parameters. + Args: + parameters: Iterable of `torch.nn.Parameter`; the parameters to be + updated with the stored parameters. + """ + for c_param, param in zip(self.collected_params, parameters): + param.data.copy_(c_param.data) diff --git a/repositories/ldm/modules/encoders/__init__.py b/repositories/ldm/modules/encoders/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/ldm/modules/encoders/modules.py b/repositories/ldm/modules/encoders/modules.py new file mode 100644 index 000000000..523a7d853 --- /dev/null +++ b/repositories/ldm/modules/encoders/modules.py @@ -0,0 +1,350 @@ +import torch +import torch.nn as nn +import kornia +from torch.utils.checkpoint import checkpoint + +from transformers import T5Tokenizer, T5EncoderModel, CLIPTokenizer, CLIPTextModel + +import open_clip +from ldm.util import default, count_params, autocast + + +class AbstractEncoder(nn.Module): + def __init__(self): + super().__init__() + + def encode(self, *args, **kwargs): + raise NotImplementedError + + +class IdentityEncoder(AbstractEncoder): + + def encode(self, x): + return x + + +class ClassEmbedder(nn.Module): + def __init__(self, embed_dim, n_classes=1000, key='class', ucg_rate=0.1): + super().__init__() + self.key = key + self.embedding = nn.Embedding(n_classes, embed_dim) + self.n_classes = n_classes + self.ucg_rate = ucg_rate + + def forward(self, batch, key=None, disable_dropout=False): + if key is None: + key = self.key + # this is for use in crossattn + c = batch[key][:, None] + if self.ucg_rate > 0. and not disable_dropout: + mask = 1. - torch.bernoulli(torch.ones_like(c) * self.ucg_rate) + c = mask * c + (1 - mask) * torch.ones_like(c) * (self.n_classes - 1) + c = c.long() + c = self.embedding(c) + return c + + def get_unconditional_conditioning(self, bs, device="cuda"): + uc_class = self.n_classes - 1 # 1000 classes --> 0 ... 999, one extra class for ucg (class 1000) + uc = torch.ones((bs,), device=device) * uc_class + uc = {self.key: uc} + return uc + + +def disabled_train(self, mode=True): + """Overwrite model.train with this function to make sure train/eval mode + does not change anymore.""" + return self + + +class FrozenT5Embedder(AbstractEncoder): + """Uses the T5 transformer encoder for text""" + + def __init__(self, version="google/t5-v1_1-large", device="cuda", max_length=77, + freeze=True): # others are google/t5-v1_1-xl and google/t5-v1_1-xxl + super().__init__() + self.tokenizer = T5Tokenizer.from_pretrained(version) + self.transformer = T5EncoderModel.from_pretrained(version) + self.device = device + self.max_length = max_length # TODO: typical value? + if freeze: + self.freeze() + + def freeze(self): + self.transformer = self.transformer.eval() + # self.train = disabled_train + for param in self.parameters(): + param.requires_grad = False + + def forward(self, text): + batch_encoding = self.tokenizer(text, truncation=True, max_length=self.max_length, return_length=True, + return_overflowing_tokens=False, padding="max_length", return_tensors="pt") + tokens = batch_encoding["input_ids"].to(self.device) + outputs = self.transformer(input_ids=tokens) + + z = outputs.last_hidden_state + return z + + def encode(self, text): + return self(text) + + +class FrozenCLIPEmbedder(AbstractEncoder): + """Uses the CLIP transformer encoder for text (from huggingface)""" + LAYERS = [ + "last", + "pooled", + "hidden" + ] + + def __init__(self, version="openai/clip-vit-large-patch14", device="cuda", max_length=77, + freeze=True, layer="last", layer_idx=None): # clip-vit-base-patch32 + super().__init__() + assert layer in self.LAYERS + self.tokenizer = CLIPTokenizer.from_pretrained(version) + self.transformer = CLIPTextModel.from_pretrained(version) + self.device = device + self.max_length = max_length + if freeze: + self.freeze() + self.layer = layer + self.layer_idx = layer_idx + if layer == "hidden": + assert layer_idx is not None + assert 0 <= abs(layer_idx) <= 12 + + def freeze(self): + self.transformer = self.transformer.eval() + # self.train = disabled_train + for param in self.parameters(): + param.requires_grad = False + + def forward(self, text): + batch_encoding = self.tokenizer(text, truncation=True, max_length=self.max_length, return_length=True, + return_overflowing_tokens=False, padding="max_length", return_tensors="pt") + tokens = batch_encoding["input_ids"].to(self.device) + outputs = self.transformer(input_ids=tokens, output_hidden_states=self.layer == "hidden") + if self.layer == "last": + z = outputs.last_hidden_state + elif self.layer == "pooled": + z = outputs.pooler_output[:, None, :] + else: + z = outputs.hidden_states[self.layer_idx] + return z + + def encode(self, text): + return self(text) + + +class ClipImageEmbedder(nn.Module): + def __init__( + self, + model, + jit=False, + device='cuda' if torch.cuda.is_available() else 'cpu', + antialias=True, + ucg_rate=0. + ): + super().__init__() + from clip import load as load_clip + self.model, _ = load_clip(name=model, device=device, jit=jit) + + self.antialias = antialias + + self.register_buffer('mean', torch.Tensor([0.48145466, 0.4578275, 0.40821073]), persistent=False) + self.register_buffer('std', torch.Tensor([0.26862954, 0.26130258, 0.27577711]), persistent=False) + self.ucg_rate = ucg_rate + + def preprocess(self, x): + # normalize to [0,1] + x = kornia.geometry.resize(x, (224, 224), + interpolation='bicubic', align_corners=True, + antialias=self.antialias) + x = (x + 1.) / 2. + # re-normalize according to clip + x = kornia.enhance.normalize(x, self.mean, self.std) + return x + + def forward(self, x, no_dropout=False): + # x is assumed to be in range [-1,1] + out = self.model.encode_image(self.preprocess(x)) + out = out.to(x.dtype) + if self.ucg_rate > 0. and not no_dropout: + out = torch.bernoulli((1. - self.ucg_rate) * torch.ones(out.shape[0], device=out.device))[:, None] * out + return out + + +class FrozenOpenCLIPEmbedder(AbstractEncoder): + """ + Uses the OpenCLIP transformer encoder for text + """ + LAYERS = [ + # "pooled", + "last", + "penultimate" + ] + + def __init__(self, arch="ViT-H-14", version="laion2b_s32b_b79k", device="cuda", max_length=77, + freeze=True, layer="last"): + super().__init__() + assert layer in self.LAYERS + model, _, _ = open_clip.create_model_and_transforms(arch, device=torch.device('cpu'), pretrained=version) + del model.visual + self.model = model + + self.device = device + self.max_length = max_length + if freeze: + self.freeze() + self.layer = layer + if self.layer == "last": + self.layer_idx = 0 + elif self.layer == "penultimate": + self.layer_idx = 1 + else: + raise NotImplementedError() + + def freeze(self): + self.model = self.model.eval() + for param in self.parameters(): + param.requires_grad = False + + def forward(self, text): + tokens = open_clip.tokenize(text) + z = self.encode_with_transformer(tokens.to(self.device)) + return z + + def encode_with_transformer(self, text): + x = self.model.token_embedding(text) # [batch_size, n_ctx, d_model] + x = x + self.model.positional_embedding + x = x.permute(1, 0, 2) # NLD -> LND + x = self.text_transformer_forward(x, attn_mask=self.model.attn_mask) + x = x.permute(1, 0, 2) # LND -> NLD + x = self.model.ln_final(x) + return x + + def text_transformer_forward(self, x: torch.Tensor, attn_mask=None): + for i, r in enumerate(self.model.transformer.resblocks): + if i == len(self.model.transformer.resblocks) - self.layer_idx: + break + if self.model.transformer.grad_checkpointing and not torch.jit.is_scripting(): + x = checkpoint(r, x, attn_mask) + else: + x = r(x, attn_mask=attn_mask) + return x + + def encode(self, text): + return self(text) + + +class FrozenOpenCLIPImageEmbedder(AbstractEncoder): + """ + Uses the OpenCLIP vision transformer encoder for images + """ + + def __init__(self, arch="ViT-H-14", version="laion2b_s32b_b79k", device="cuda", max_length=77, + freeze=True, layer="pooled", antialias=True, ucg_rate=0.): + super().__init__() + model, _, _ = open_clip.create_model_and_transforms(arch, device=torch.device('cpu'), + pretrained=version, ) + del model.transformer + self.model = model + + self.device = device + self.max_length = max_length + if freeze: + self.freeze() + self.layer = layer + if self.layer == "penultimate": + raise NotImplementedError() + self.layer_idx = 1 + + self.antialias = antialias + + self.register_buffer('mean', torch.Tensor([0.48145466, 0.4578275, 0.40821073]), persistent=False) + self.register_buffer('std', torch.Tensor([0.26862954, 0.26130258, 0.27577711]), persistent=False) + self.ucg_rate = ucg_rate + + def preprocess(self, x): + # normalize to [0,1] + x = kornia.geometry.resize(x, (224, 224), + interpolation='bicubic', align_corners=True, + antialias=self.antialias) + x = (x + 1.) / 2. + # renormalize according to clip + x = kornia.enhance.normalize(x, self.mean, self.std) + return x + + def freeze(self): + self.model = self.model.eval() + for param in self.parameters(): + param.requires_grad = False + + @autocast + def forward(self, image, no_dropout=False): + z = self.encode_with_vision_transformer(image) + if self.ucg_rate > 0. and not no_dropout: + z = torch.bernoulli((1. - self.ucg_rate) * torch.ones(z.shape[0], device=z.device))[:, None] * z + return z + + def encode_with_vision_transformer(self, img): + img = self.preprocess(img) + x = self.model.visual(img) + return x + + def encode(self, text): + return self(text) + + +class FrozenCLIPT5Encoder(AbstractEncoder): + def __init__(self, clip_version="openai/clip-vit-large-patch14", t5_version="google/t5-v1_1-xl", device="cuda", + clip_max_length=77, t5_max_length=77): + super().__init__() + self.clip_encoder = FrozenCLIPEmbedder(clip_version, device, max_length=clip_max_length) + self.t5_encoder = FrozenT5Embedder(t5_version, device, max_length=t5_max_length) + print(f"{self.clip_encoder.__class__.__name__} has {count_params(self.clip_encoder) * 1.e-6:.2f} M parameters, " + f"{self.t5_encoder.__class__.__name__} comes with {count_params(self.t5_encoder) * 1.e-6:.2f} M params.") + + def encode(self, text): + return self(text) + + def forward(self, text): + clip_z = self.clip_encoder.encode(text) + t5_z = self.t5_encoder.encode(text) + return [clip_z, t5_z] + + +from ldm.modules.diffusionmodules.upscaling import ImageConcatWithNoiseAugmentation +from ldm.modules.diffusionmodules.openaimodel import Timestep + + +class CLIPEmbeddingNoiseAugmentation(ImageConcatWithNoiseAugmentation): + def __init__(self, *args, clip_stats_path=None, timestep_dim=256, **kwargs): + super().__init__(*args, **kwargs) + if clip_stats_path is None: + clip_mean, clip_std = torch.zeros(timestep_dim), torch.ones(timestep_dim) + else: + clip_mean, clip_std = torch.load(clip_stats_path, map_location="cpu") + self.register_buffer("data_mean", clip_mean[None, :], persistent=False) + self.register_buffer("data_std", clip_std[None, :], persistent=False) + self.time_embed = Timestep(timestep_dim) + + def scale(self, x): + # re-normalize to centered mean and unit variance + x = (x - self.data_mean) * 1. / self.data_std + return x + + def unscale(self, x): + # back to original data stats + x = (x * self.data_std) + self.data_mean + return x + + def forward(self, x, noise_level=None): + if noise_level is None: + noise_level = torch.randint(0, self.max_noise_level, (x.shape[0],), device=x.device).long() + else: + assert isinstance(noise_level, torch.Tensor) + x = self.scale(x) + z = self.q_sample(x, noise_level) + z = self.unscale(z) + noise_level = self.time_embed(noise_level) + return z, noise_level diff --git a/repositories/ldm/modules/image_degradation/__init__.py b/repositories/ldm/modules/image_degradation/__init__.py new file mode 100644 index 000000000..7836cada8 --- /dev/null +++ b/repositories/ldm/modules/image_degradation/__init__.py @@ -0,0 +1,2 @@ +from ldm.modules.image_degradation.bsrgan import degradation_bsrgan_variant as degradation_fn_bsr +from ldm.modules.image_degradation.bsrgan_light import degradation_bsrgan_variant as degradation_fn_bsr_light diff --git a/repositories/ldm/modules/image_degradation/bsrgan.py b/repositories/ldm/modules/image_degradation/bsrgan.py new file mode 100644 index 000000000..32ef56169 --- /dev/null +++ b/repositories/ldm/modules/image_degradation/bsrgan.py @@ -0,0 +1,730 @@ +# -*- coding: utf-8 -*- +""" +# -------------------------------------------- +# Super-Resolution +# -------------------------------------------- +# +# Kai Zhang (cskaizhang@gmail.com) +# https://github.com/cszn +# From 2019/03--2021/08 +# -------------------------------------------- +""" + +import numpy as np +import cv2 +import torch + +from functools import partial +import random +from scipy import ndimage +import scipy +import scipy.stats as ss +from scipy.interpolate import interp2d +from scipy.linalg import orth +import albumentations + +import ldm.modules.image_degradation.utils_image as util + + +def modcrop_np(img, sf): + ''' + Args: + img: numpy image, WxH or WxHxC + sf: scale factor + Return: + cropped image + ''' + w, h = img.shape[:2] + im = np.copy(img) + return im[:w - w % sf, :h - h % sf, ...] + + +""" +# -------------------------------------------- +# anisotropic Gaussian kernels +# -------------------------------------------- +""" + + +def analytic_kernel(k): + """Calculate the X4 kernel from the X2 kernel (for proof see appendix in paper)""" + k_size = k.shape[0] + # Calculate the big kernels size + big_k = np.zeros((3 * k_size - 2, 3 * k_size - 2)) + # Loop over the small kernel to fill the big one + for r in range(k_size): + for c in range(k_size): + big_k[2 * r:2 * r + k_size, 2 * c:2 * c + k_size] += k[r, c] * k + # Crop the edges of the big kernel to ignore very small values and increase run time of SR + crop = k_size // 2 + cropped_big_k = big_k[crop:-crop, crop:-crop] + # Normalize to 1 + return cropped_big_k / cropped_big_k.sum() + + +def anisotropic_Gaussian(ksize=15, theta=np.pi, l1=6, l2=6): + """ generate an anisotropic Gaussian kernel + Args: + ksize : e.g., 15, kernel size + theta : [0, pi], rotation angle range + l1 : [0.1,50], scaling of eigenvalues + l2 : [0.1,l1], scaling of eigenvalues + If l1 = l2, will get an isotropic Gaussian kernel. + Returns: + k : kernel + """ + + v = np.dot(np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]), np.array([1., 0.])) + V = np.array([[v[0], v[1]], [v[1], -v[0]]]) + D = np.array([[l1, 0], [0, l2]]) + Sigma = np.dot(np.dot(V, D), np.linalg.inv(V)) + k = gm_blur_kernel(mean=[0, 0], cov=Sigma, size=ksize) + + return k + + +def gm_blur_kernel(mean, cov, size=15): + center = size / 2.0 + 0.5 + k = np.zeros([size, size]) + for y in range(size): + for x in range(size): + cy = y - center + 1 + cx = x - center + 1 + k[y, x] = ss.multivariate_normal.pdf([cx, cy], mean=mean, cov=cov) + + k = k / np.sum(k) + return k + + +def shift_pixel(x, sf, upper_left=True): + """shift pixel for super-resolution with different scale factors + Args: + x: WxHxC or WxH + sf: scale factor + upper_left: shift direction + """ + h, w = x.shape[:2] + shift = (sf - 1) * 0.5 + xv, yv = np.arange(0, w, 1.0), np.arange(0, h, 1.0) + if upper_left: + x1 = xv + shift + y1 = yv + shift + else: + x1 = xv - shift + y1 = yv - shift + + x1 = np.clip(x1, 0, w - 1) + y1 = np.clip(y1, 0, h - 1) + + if x.ndim == 2: + x = interp2d(xv, yv, x)(x1, y1) + if x.ndim == 3: + for i in range(x.shape[-1]): + x[:, :, i] = interp2d(xv, yv, x[:, :, i])(x1, y1) + + return x + + +def blur(x, k): + ''' + x: image, NxcxHxW + k: kernel, Nx1xhxw + ''' + n, c = x.shape[:2] + p1, p2 = (k.shape[-2] - 1) // 2, (k.shape[-1] - 1) // 2 + x = torch.nn.functional.pad(x, pad=(p1, p2, p1, p2), mode='replicate') + k = k.repeat(1, c, 1, 1) + k = k.view(-1, 1, k.shape[2], k.shape[3]) + x = x.view(1, -1, x.shape[2], x.shape[3]) + x = torch.nn.functional.conv2d(x, k, bias=None, stride=1, padding=0, groups=n * c) + x = x.view(n, c, x.shape[2], x.shape[3]) + + return x + + +def gen_kernel(k_size=np.array([15, 15]), scale_factor=np.array([4, 4]), min_var=0.6, max_var=10., noise_level=0): + """" + # modified version of https://github.com/assafshocher/BlindSR_dataset_generator + # Kai Zhang + # min_var = 0.175 * sf # variance of the gaussian kernel will be sampled between min_var and max_var + # max_var = 2.5 * sf + """ + # Set random eigen-vals (lambdas) and angle (theta) for COV matrix + lambda_1 = min_var + np.random.rand() * (max_var - min_var) + lambda_2 = min_var + np.random.rand() * (max_var - min_var) + theta = np.random.rand() * np.pi # random theta + noise = -noise_level + np.random.rand(*k_size) * noise_level * 2 + + # Set COV matrix using Lambdas and Theta + LAMBDA = np.diag([lambda_1, lambda_2]) + Q = np.array([[np.cos(theta), -np.sin(theta)], + [np.sin(theta), np.cos(theta)]]) + SIGMA = Q @ LAMBDA @ Q.T + INV_SIGMA = np.linalg.inv(SIGMA)[None, None, :, :] + + # Set expectation position (shifting kernel for aligned image) + MU = k_size // 2 - 0.5 * (scale_factor - 1) # - 0.5 * (scale_factor - k_size % 2) + MU = MU[None, None, :, None] + + # Create meshgrid for Gaussian + [X, Y] = np.meshgrid(range(k_size[0]), range(k_size[1])) + Z = np.stack([X, Y], 2)[:, :, :, None] + + # Calcualte Gaussian for every pixel of the kernel + ZZ = Z - MU + ZZ_t = ZZ.transpose(0, 1, 3, 2) + raw_kernel = np.exp(-0.5 * np.squeeze(ZZ_t @ INV_SIGMA @ ZZ)) * (1 + noise) + + # shift the kernel so it will be centered + # raw_kernel_centered = kernel_shift(raw_kernel, scale_factor) + + # Normalize the kernel and return + # kernel = raw_kernel_centered / np.sum(raw_kernel_centered) + kernel = raw_kernel / np.sum(raw_kernel) + return kernel + + +def fspecial_gaussian(hsize, sigma): + hsize = [hsize, hsize] + siz = [(hsize[0] - 1.0) / 2.0, (hsize[1] - 1.0) / 2.0] + std = sigma + [x, y] = np.meshgrid(np.arange(-siz[1], siz[1] + 1), np.arange(-siz[0], siz[0] + 1)) + arg = -(x * x + y * y) / (2 * std * std) + h = np.exp(arg) + h[h < scipy.finfo(float).eps * h.max()] = 0 + sumh = h.sum() + if sumh != 0: + h = h / sumh + return h + + +def fspecial_laplacian(alpha): + alpha = max([0, min([alpha, 1])]) + h1 = alpha / (alpha + 1) + h2 = (1 - alpha) / (alpha + 1) + h = [[h1, h2, h1], [h2, -4 / (alpha + 1), h2], [h1, h2, h1]] + h = np.array(h) + return h + + +def fspecial(filter_type, *args, **kwargs): + ''' + python code from: + https://github.com/ronaldosena/imagens-medicas-2/blob/40171a6c259edec7827a6693a93955de2bd39e76/Aulas/aula_2_-_uniform_filter/matlab_fspecial.py + ''' + if filter_type == 'gaussian': + return fspecial_gaussian(*args, **kwargs) + if filter_type == 'laplacian': + return fspecial_laplacian(*args, **kwargs) + + +""" +# -------------------------------------------- +# degradation models +# -------------------------------------------- +""" + + +def bicubic_degradation(x, sf=3): + ''' + Args: + x: HxWxC image, [0, 1] + sf: down-scale factor + Return: + bicubicly downsampled LR image + ''' + x = util.imresize_np(x, scale=1 / sf) + return x + + +def srmd_degradation(x, k, sf=3): + ''' blur + bicubic downsampling + Args: + x: HxWxC image, [0, 1] + k: hxw, double + sf: down-scale factor + Return: + downsampled LR image + Reference: + @inproceedings{zhang2018learning, + title={Learning a single convolutional super-resolution network for multiple degradations}, + author={Zhang, Kai and Zuo, Wangmeng and Zhang, Lei}, + booktitle={IEEE Conference on Computer Vision and Pattern Recognition}, + pages={3262--3271}, + year={2018} + } + ''' + x = ndimage.filters.convolve(x, np.expand_dims(k, axis=2), mode='wrap') # 'nearest' | 'mirror' + x = bicubic_degradation(x, sf=sf) + return x + + +def dpsr_degradation(x, k, sf=3): + ''' bicubic downsampling + blur + Args: + x: HxWxC image, [0, 1] + k: hxw, double + sf: down-scale factor + Return: + downsampled LR image + Reference: + @inproceedings{zhang2019deep, + title={Deep Plug-and-Play Super-Resolution for Arbitrary Blur Kernels}, + author={Zhang, Kai and Zuo, Wangmeng and Zhang, Lei}, + booktitle={IEEE Conference on Computer Vision and Pattern Recognition}, + pages={1671--1681}, + year={2019} + } + ''' + x = bicubic_degradation(x, sf=sf) + x = ndimage.filters.convolve(x, np.expand_dims(k, axis=2), mode='wrap') + return x + + +def classical_degradation(x, k, sf=3): + ''' blur + downsampling + Args: + x: HxWxC image, [0, 1]/[0, 255] + k: hxw, double + sf: down-scale factor + Return: + downsampled LR image + ''' + x = ndimage.filters.convolve(x, np.expand_dims(k, axis=2), mode='wrap') + # x = filters.correlate(x, np.expand_dims(np.flip(k), axis=2)) + st = 0 + return x[st::sf, st::sf, ...] + + +def add_sharpening(img, weight=0.5, radius=50, threshold=10): + """USM sharpening. borrowed from real-ESRGAN + Input image: I; Blurry image: B. + 1. K = I + weight * (I - B) + 2. Mask = 1 if abs(I - B) > threshold, else: 0 + 3. Blur mask: + 4. Out = Mask * K + (1 - Mask) * I + Args: + img (Numpy array): Input image, HWC, BGR; float32, [0, 1]. + weight (float): Sharp weight. Default: 1. + radius (float): Kernel size of Gaussian blur. Default: 50. + threshold (int): + """ + if radius % 2 == 0: + radius += 1 + blur = cv2.GaussianBlur(img, (radius, radius), 0) + residual = img - blur + mask = np.abs(residual) * 255 > threshold + mask = mask.astype('float32') + soft_mask = cv2.GaussianBlur(mask, (radius, radius), 0) + + K = img + weight * residual + K = np.clip(K, 0, 1) + return soft_mask * K + (1 - soft_mask) * img + + +def add_blur(img, sf=4): + wd2 = 4.0 + sf + wd = 2.0 + 0.2 * sf + if random.random() < 0.5: + l1 = wd2 * random.random() + l2 = wd2 * random.random() + k = anisotropic_Gaussian(ksize=2 * random.randint(2, 11) + 3, theta=random.random() * np.pi, l1=l1, l2=l2) + else: + k = fspecial('gaussian', 2 * random.randint(2, 11) + 3, wd * random.random()) + img = ndimage.filters.convolve(img, np.expand_dims(k, axis=2), mode='mirror') + + return img + + +def add_resize(img, sf=4): + rnum = np.random.rand() + if rnum > 0.8: # up + sf1 = random.uniform(1, 2) + elif rnum < 0.7: # down + sf1 = random.uniform(0.5 / sf, 1) + else: + sf1 = 1.0 + img = cv2.resize(img, (int(sf1 * img.shape[1]), int(sf1 * img.shape[0])), interpolation=random.choice([1, 2, 3])) + img = np.clip(img, 0.0, 1.0) + + return img + + +# def add_Gaussian_noise(img, noise_level1=2, noise_level2=25): +# noise_level = random.randint(noise_level1, noise_level2) +# rnum = np.random.rand() +# if rnum > 0.6: # add color Gaussian noise +# img += np.random.normal(0, noise_level / 255.0, img.shape).astype(np.float32) +# elif rnum < 0.4: # add grayscale Gaussian noise +# img += np.random.normal(0, noise_level / 255.0, (*img.shape[:2], 1)).astype(np.float32) +# else: # add noise +# L = noise_level2 / 255. +# D = np.diag(np.random.rand(3)) +# U = orth(np.random.rand(3, 3)) +# conv = np.dot(np.dot(np.transpose(U), D), U) +# img += np.random.multivariate_normal([0, 0, 0], np.abs(L ** 2 * conv), img.shape[:2]).astype(np.float32) +# img = np.clip(img, 0.0, 1.0) +# return img + +def add_Gaussian_noise(img, noise_level1=2, noise_level2=25): + noise_level = random.randint(noise_level1, noise_level2) + rnum = np.random.rand() + if rnum > 0.6: # add color Gaussian noise + img = img + np.random.normal(0, noise_level / 255.0, img.shape).astype(np.float32) + elif rnum < 0.4: # add grayscale Gaussian noise + img = img + np.random.normal(0, noise_level / 255.0, (*img.shape[:2], 1)).astype(np.float32) + else: # add noise + L = noise_level2 / 255. + D = np.diag(np.random.rand(3)) + U = orth(np.random.rand(3, 3)) + conv = np.dot(np.dot(np.transpose(U), D), U) + img = img + np.random.multivariate_normal([0, 0, 0], np.abs(L ** 2 * conv), img.shape[:2]).astype(np.float32) + img = np.clip(img, 0.0, 1.0) + return img + + +def add_speckle_noise(img, noise_level1=2, noise_level2=25): + noise_level = random.randint(noise_level1, noise_level2) + img = np.clip(img, 0.0, 1.0) + rnum = random.random() + if rnum > 0.6: + img += img * np.random.normal(0, noise_level / 255.0, img.shape).astype(np.float32) + elif rnum < 0.4: + img += img * np.random.normal(0, noise_level / 255.0, (*img.shape[:2], 1)).astype(np.float32) + else: + L = noise_level2 / 255. + D = np.diag(np.random.rand(3)) + U = orth(np.random.rand(3, 3)) + conv = np.dot(np.dot(np.transpose(U), D), U) + img += img * np.random.multivariate_normal([0, 0, 0], np.abs(L ** 2 * conv), img.shape[:2]).astype(np.float32) + img = np.clip(img, 0.0, 1.0) + return img + + +def add_Poisson_noise(img): + img = np.clip((img * 255.0).round(), 0, 255) / 255. + vals = 10 ** (2 * random.random() + 2.0) # [2, 4] + if random.random() < 0.5: + img = np.random.poisson(img * vals).astype(np.float32) / vals + else: + img_gray = np.dot(img[..., :3], [0.299, 0.587, 0.114]) + img_gray = np.clip((img_gray * 255.0).round(), 0, 255) / 255. + noise_gray = np.random.poisson(img_gray * vals).astype(np.float32) / vals - img_gray + img += noise_gray[:, :, np.newaxis] + img = np.clip(img, 0.0, 1.0) + return img + + +def add_JPEG_noise(img): + quality_factor = random.randint(30, 95) + img = cv2.cvtColor(util.single2uint(img), cv2.COLOR_RGB2BGR) + result, encimg = cv2.imencode('.jpg', img, [int(cv2.IMWRITE_JPEG_QUALITY), quality_factor]) + img = cv2.imdecode(encimg, 1) + img = cv2.cvtColor(util.uint2single(img), cv2.COLOR_BGR2RGB) + return img + + +def random_crop(lq, hq, sf=4, lq_patchsize=64): + h, w = lq.shape[:2] + rnd_h = random.randint(0, h - lq_patchsize) + rnd_w = random.randint(0, w - lq_patchsize) + lq = lq[rnd_h:rnd_h + lq_patchsize, rnd_w:rnd_w + lq_patchsize, :] + + rnd_h_H, rnd_w_H = int(rnd_h * sf), int(rnd_w * sf) + hq = hq[rnd_h_H:rnd_h_H + lq_patchsize * sf, rnd_w_H:rnd_w_H + lq_patchsize * sf, :] + return lq, hq + + +def degradation_bsrgan(img, sf=4, lq_patchsize=72, isp_model=None): + """ + This is the degradation model of BSRGAN from the paper + "Designing a Practical Degradation Model for Deep Blind Image Super-Resolution" + ---------- + img: HXWXC, [0, 1], its size should be large than (lq_patchsizexsf)x(lq_patchsizexsf) + sf: scale factor + isp_model: camera ISP model + Returns + ------- + img: low-quality patch, size: lq_patchsizeXlq_patchsizeXC, range: [0, 1] + hq: corresponding high-quality patch, size: (lq_patchsizexsf)X(lq_patchsizexsf)XC, range: [0, 1] + """ + isp_prob, jpeg_prob, scale2_prob = 0.25, 0.9, 0.25 + sf_ori = sf + + h1, w1 = img.shape[:2] + img = img.copy()[:w1 - w1 % sf, :h1 - h1 % sf, ...] # mod crop + h, w = img.shape[:2] + + if h < lq_patchsize * sf or w < lq_patchsize * sf: + raise ValueError(f'img size ({h1}X{w1}) is too small!') + + hq = img.copy() + + if sf == 4 and random.random() < scale2_prob: # downsample1 + if np.random.rand() < 0.5: + img = cv2.resize(img, (int(1 / 2 * img.shape[1]), int(1 / 2 * img.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + img = util.imresize_np(img, 1 / 2, True) + img = np.clip(img, 0.0, 1.0) + sf = 2 + + shuffle_order = random.sample(range(7), 7) + idx1, idx2 = shuffle_order.index(2), shuffle_order.index(3) + if idx1 > idx2: # keep downsample3 last + shuffle_order[idx1], shuffle_order[idx2] = shuffle_order[idx2], shuffle_order[idx1] + + for i in shuffle_order: + + if i == 0: + img = add_blur(img, sf=sf) + + elif i == 1: + img = add_blur(img, sf=sf) + + elif i == 2: + a, b = img.shape[1], img.shape[0] + # downsample2 + if random.random() < 0.75: + sf1 = random.uniform(1, 2 * sf) + img = cv2.resize(img, (int(1 / sf1 * img.shape[1]), int(1 / sf1 * img.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + k = fspecial('gaussian', 25, random.uniform(0.1, 0.6 * sf)) + k_shifted = shift_pixel(k, sf) + k_shifted = k_shifted / k_shifted.sum() # blur with shifted kernel + img = ndimage.filters.convolve(img, np.expand_dims(k_shifted, axis=2), mode='mirror') + img = img[0::sf, 0::sf, ...] # nearest downsampling + img = np.clip(img, 0.0, 1.0) + + elif i == 3: + # downsample3 + img = cv2.resize(img, (int(1 / sf * a), int(1 / sf * b)), interpolation=random.choice([1, 2, 3])) + img = np.clip(img, 0.0, 1.0) + + elif i == 4: + # add Gaussian noise + img = add_Gaussian_noise(img, noise_level1=2, noise_level2=25) + + elif i == 5: + # add JPEG noise + if random.random() < jpeg_prob: + img = add_JPEG_noise(img) + + elif i == 6: + # add processed camera sensor noise + if random.random() < isp_prob and isp_model is not None: + with torch.no_grad(): + img, hq = isp_model.forward(img.copy(), hq) + + # add final JPEG compression noise + img = add_JPEG_noise(img) + + # random crop + img, hq = random_crop(img, hq, sf_ori, lq_patchsize) + + return img, hq + + +# todo no isp_model? +def degradation_bsrgan_variant(image, sf=4, isp_model=None): + """ + This is the degradation model of BSRGAN from the paper + "Designing a Practical Degradation Model for Deep Blind Image Super-Resolution" + ---------- + sf: scale factor + isp_model: camera ISP model + Returns + ------- + img: low-quality patch, size: lq_patchsizeXlq_patchsizeXC, range: [0, 1] + hq: corresponding high-quality patch, size: (lq_patchsizexsf)X(lq_patchsizexsf)XC, range: [0, 1] + """ + image = util.uint2single(image) + isp_prob, jpeg_prob, scale2_prob = 0.25, 0.9, 0.25 + sf_ori = sf + + h1, w1 = image.shape[:2] + image = image.copy()[:w1 - w1 % sf, :h1 - h1 % sf, ...] # mod crop + h, w = image.shape[:2] + + hq = image.copy() + + if sf == 4 and random.random() < scale2_prob: # downsample1 + if np.random.rand() < 0.5: + image = cv2.resize(image, (int(1 / 2 * image.shape[1]), int(1 / 2 * image.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + image = util.imresize_np(image, 1 / 2, True) + image = np.clip(image, 0.0, 1.0) + sf = 2 + + shuffle_order = random.sample(range(7), 7) + idx1, idx2 = shuffle_order.index(2), shuffle_order.index(3) + if idx1 > idx2: # keep downsample3 last + shuffle_order[idx1], shuffle_order[idx2] = shuffle_order[idx2], shuffle_order[idx1] + + for i in shuffle_order: + + if i == 0: + image = add_blur(image, sf=sf) + + elif i == 1: + image = add_blur(image, sf=sf) + + elif i == 2: + a, b = image.shape[1], image.shape[0] + # downsample2 + if random.random() < 0.75: + sf1 = random.uniform(1, 2 * sf) + image = cv2.resize(image, (int(1 / sf1 * image.shape[1]), int(1 / sf1 * image.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + k = fspecial('gaussian', 25, random.uniform(0.1, 0.6 * sf)) + k_shifted = shift_pixel(k, sf) + k_shifted = k_shifted / k_shifted.sum() # blur with shifted kernel + image = ndimage.filters.convolve(image, np.expand_dims(k_shifted, axis=2), mode='mirror') + image = image[0::sf, 0::sf, ...] # nearest downsampling + image = np.clip(image, 0.0, 1.0) + + elif i == 3: + # downsample3 + image = cv2.resize(image, (int(1 / sf * a), int(1 / sf * b)), interpolation=random.choice([1, 2, 3])) + image = np.clip(image, 0.0, 1.0) + + elif i == 4: + # add Gaussian noise + image = add_Gaussian_noise(image, noise_level1=2, noise_level2=25) + + elif i == 5: + # add JPEG noise + if random.random() < jpeg_prob: + image = add_JPEG_noise(image) + + # elif i == 6: + # # add processed camera sensor noise + # if random.random() < isp_prob and isp_model is not None: + # with torch.no_grad(): + # img, hq = isp_model.forward(img.copy(), hq) + + # add final JPEG compression noise + image = add_JPEG_noise(image) + image = util.single2uint(image) + example = {"image":image} + return example + + +# TODO incase there is a pickle error one needs to replace a += x with a = a + x in add_speckle_noise etc... +def degradation_bsrgan_plus(img, sf=4, shuffle_prob=0.5, use_sharp=True, lq_patchsize=64, isp_model=None): + """ + This is an extended degradation model by combining + the degradation models of BSRGAN and Real-ESRGAN + ---------- + img: HXWXC, [0, 1], its size should be large than (lq_patchsizexsf)x(lq_patchsizexsf) + sf: scale factor + use_shuffle: the degradation shuffle + use_sharp: sharpening the img + Returns + ------- + img: low-quality patch, size: lq_patchsizeXlq_patchsizeXC, range: [0, 1] + hq: corresponding high-quality patch, size: (lq_patchsizexsf)X(lq_patchsizexsf)XC, range: [0, 1] + """ + + h1, w1 = img.shape[:2] + img = img.copy()[:w1 - w1 % sf, :h1 - h1 % sf, ...] # mod crop + h, w = img.shape[:2] + + if h < lq_patchsize * sf or w < lq_patchsize * sf: + raise ValueError(f'img size ({h1}X{w1}) is too small!') + + if use_sharp: + img = add_sharpening(img) + hq = img.copy() + + if random.random() < shuffle_prob: + shuffle_order = random.sample(range(13), 13) + else: + shuffle_order = list(range(13)) + # local shuffle for noise, JPEG is always the last one + shuffle_order[2:6] = random.sample(shuffle_order[2:6], len(range(2, 6))) + shuffle_order[9:13] = random.sample(shuffle_order[9:13], len(range(9, 13))) + + poisson_prob, speckle_prob, isp_prob = 0.1, 0.1, 0.1 + + for i in shuffle_order: + if i == 0: + img = add_blur(img, sf=sf) + elif i == 1: + img = add_resize(img, sf=sf) + elif i == 2: + img = add_Gaussian_noise(img, noise_level1=2, noise_level2=25) + elif i == 3: + if random.random() < poisson_prob: + img = add_Poisson_noise(img) + elif i == 4: + if random.random() < speckle_prob: + img = add_speckle_noise(img) + elif i == 5: + if random.random() < isp_prob and isp_model is not None: + with torch.no_grad(): + img, hq = isp_model.forward(img.copy(), hq) + elif i == 6: + img = add_JPEG_noise(img) + elif i == 7: + img = add_blur(img, sf=sf) + elif i == 8: + img = add_resize(img, sf=sf) + elif i == 9: + img = add_Gaussian_noise(img, noise_level1=2, noise_level2=25) + elif i == 10: + if random.random() < poisson_prob: + img = add_Poisson_noise(img) + elif i == 11: + if random.random() < speckle_prob: + img = add_speckle_noise(img) + elif i == 12: + if random.random() < isp_prob and isp_model is not None: + with torch.no_grad(): + img, hq = isp_model.forward(img.copy(), hq) + else: + print('check the shuffle!') + + # resize to desired size + img = cv2.resize(img, (int(1 / sf * hq.shape[1]), int(1 / sf * hq.shape[0])), + interpolation=random.choice([1, 2, 3])) + + # add final JPEG compression noise + img = add_JPEG_noise(img) + + # random crop + img, hq = random_crop(img, hq, sf, lq_patchsize) + + return img, hq + + +if __name__ == '__main__': + print("hey") + img = util.imread_uint('utils/test.png', 3) + print(img) + img = util.uint2single(img) + print(img) + img = img[:448, :448] + h = img.shape[0] // 4 + print("resizing to", h) + sf = 4 + deg_fn = partial(degradation_bsrgan_variant, sf=sf) + for i in range(20): + print(i) + img_lq = deg_fn(img) + print(img_lq) + img_lq_bicubic = albumentations.SmallestMaxSize(max_size=h, interpolation=cv2.INTER_CUBIC)(image=img)["image"] + print(img_lq.shape) + print("bicubic", img_lq_bicubic.shape) + print(img_hq.shape) + lq_nearest = cv2.resize(util.single2uint(img_lq), (int(sf * img_lq.shape[1]), int(sf * img_lq.shape[0])), + interpolation=0) + lq_bicubic_nearest = cv2.resize(util.single2uint(img_lq_bicubic), (int(sf * img_lq.shape[1]), int(sf * img_lq.shape[0])), + interpolation=0) + img_concat = np.concatenate([lq_bicubic_nearest, lq_nearest, util.single2uint(img_hq)], axis=1) + util.imsave(img_concat, str(i) + '.png') + + diff --git a/repositories/ldm/modules/image_degradation/bsrgan_light.py b/repositories/ldm/modules/image_degradation/bsrgan_light.py new file mode 100644 index 000000000..808c7f882 --- /dev/null +++ b/repositories/ldm/modules/image_degradation/bsrgan_light.py @@ -0,0 +1,651 @@ +# -*- coding: utf-8 -*- +import numpy as np +import cv2 +import torch + +from functools import partial +import random +from scipy import ndimage +import scipy +import scipy.stats as ss +from scipy.interpolate import interp2d +from scipy.linalg import orth +import albumentations + +import ldm.modules.image_degradation.utils_image as util + +""" +# -------------------------------------------- +# Super-Resolution +# -------------------------------------------- +# +# Kai Zhang (cskaizhang@gmail.com) +# https://github.com/cszn +# From 2019/03--2021/08 +# -------------------------------------------- +""" + +def modcrop_np(img, sf): + ''' + Args: + img: numpy image, WxH or WxHxC + sf: scale factor + Return: + cropped image + ''' + w, h = img.shape[:2] + im = np.copy(img) + return im[:w - w % sf, :h - h % sf, ...] + + +""" +# -------------------------------------------- +# anisotropic Gaussian kernels +# -------------------------------------------- +""" + + +def analytic_kernel(k): + """Calculate the X4 kernel from the X2 kernel (for proof see appendix in paper)""" + k_size = k.shape[0] + # Calculate the big kernels size + big_k = np.zeros((3 * k_size - 2, 3 * k_size - 2)) + # Loop over the small kernel to fill the big one + for r in range(k_size): + for c in range(k_size): + big_k[2 * r:2 * r + k_size, 2 * c:2 * c + k_size] += k[r, c] * k + # Crop the edges of the big kernel to ignore very small values and increase run time of SR + crop = k_size // 2 + cropped_big_k = big_k[crop:-crop, crop:-crop] + # Normalize to 1 + return cropped_big_k / cropped_big_k.sum() + + +def anisotropic_Gaussian(ksize=15, theta=np.pi, l1=6, l2=6): + """ generate an anisotropic Gaussian kernel + Args: + ksize : e.g., 15, kernel size + theta : [0, pi], rotation angle range + l1 : [0.1,50], scaling of eigenvalues + l2 : [0.1,l1], scaling of eigenvalues + If l1 = l2, will get an isotropic Gaussian kernel. + Returns: + k : kernel + """ + + v = np.dot(np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]), np.array([1., 0.])) + V = np.array([[v[0], v[1]], [v[1], -v[0]]]) + D = np.array([[l1, 0], [0, l2]]) + Sigma = np.dot(np.dot(V, D), np.linalg.inv(V)) + k = gm_blur_kernel(mean=[0, 0], cov=Sigma, size=ksize) + + return k + + +def gm_blur_kernel(mean, cov, size=15): + center = size / 2.0 + 0.5 + k = np.zeros([size, size]) + for y in range(size): + for x in range(size): + cy = y - center + 1 + cx = x - center + 1 + k[y, x] = ss.multivariate_normal.pdf([cx, cy], mean=mean, cov=cov) + + k = k / np.sum(k) + return k + + +def shift_pixel(x, sf, upper_left=True): + """shift pixel for super-resolution with different scale factors + Args: + x: WxHxC or WxH + sf: scale factor + upper_left: shift direction + """ + h, w = x.shape[:2] + shift = (sf - 1) * 0.5 + xv, yv = np.arange(0, w, 1.0), np.arange(0, h, 1.0) + if upper_left: + x1 = xv + shift + y1 = yv + shift + else: + x1 = xv - shift + y1 = yv - shift + + x1 = np.clip(x1, 0, w - 1) + y1 = np.clip(y1, 0, h - 1) + + if x.ndim == 2: + x = interp2d(xv, yv, x)(x1, y1) + if x.ndim == 3: + for i in range(x.shape[-1]): + x[:, :, i] = interp2d(xv, yv, x[:, :, i])(x1, y1) + + return x + + +def blur(x, k): + ''' + x: image, NxcxHxW + k: kernel, Nx1xhxw + ''' + n, c = x.shape[:2] + p1, p2 = (k.shape[-2] - 1) // 2, (k.shape[-1] - 1) // 2 + x = torch.nn.functional.pad(x, pad=(p1, p2, p1, p2), mode='replicate') + k = k.repeat(1, c, 1, 1) + k = k.view(-1, 1, k.shape[2], k.shape[3]) + x = x.view(1, -1, x.shape[2], x.shape[3]) + x = torch.nn.functional.conv2d(x, k, bias=None, stride=1, padding=0, groups=n * c) + x = x.view(n, c, x.shape[2], x.shape[3]) + + return x + + +def gen_kernel(k_size=np.array([15, 15]), scale_factor=np.array([4, 4]), min_var=0.6, max_var=10., noise_level=0): + """" + # modified version of https://github.com/assafshocher/BlindSR_dataset_generator + # Kai Zhang + # min_var = 0.175 * sf # variance of the gaussian kernel will be sampled between min_var and max_var + # max_var = 2.5 * sf + """ + # Set random eigen-vals (lambdas) and angle (theta) for COV matrix + lambda_1 = min_var + np.random.rand() * (max_var - min_var) + lambda_2 = min_var + np.random.rand() * (max_var - min_var) + theta = np.random.rand() * np.pi # random theta + noise = -noise_level + np.random.rand(*k_size) * noise_level * 2 + + # Set COV matrix using Lambdas and Theta + LAMBDA = np.diag([lambda_1, lambda_2]) + Q = np.array([[np.cos(theta), -np.sin(theta)], + [np.sin(theta), np.cos(theta)]]) + SIGMA = Q @ LAMBDA @ Q.T + INV_SIGMA = np.linalg.inv(SIGMA)[None, None, :, :] + + # Set expectation position (shifting kernel for aligned image) + MU = k_size // 2 - 0.5 * (scale_factor - 1) # - 0.5 * (scale_factor - k_size % 2) + MU = MU[None, None, :, None] + + # Create meshgrid for Gaussian + [X, Y] = np.meshgrid(range(k_size[0]), range(k_size[1])) + Z = np.stack([X, Y], 2)[:, :, :, None] + + # Calcualte Gaussian for every pixel of the kernel + ZZ = Z - MU + ZZ_t = ZZ.transpose(0, 1, 3, 2) + raw_kernel = np.exp(-0.5 * np.squeeze(ZZ_t @ INV_SIGMA @ ZZ)) * (1 + noise) + + # shift the kernel so it will be centered + # raw_kernel_centered = kernel_shift(raw_kernel, scale_factor) + + # Normalize the kernel and return + # kernel = raw_kernel_centered / np.sum(raw_kernel_centered) + kernel = raw_kernel / np.sum(raw_kernel) + return kernel + + +def fspecial_gaussian(hsize, sigma): + hsize = [hsize, hsize] + siz = [(hsize[0] - 1.0) / 2.0, (hsize[1] - 1.0) / 2.0] + std = sigma + [x, y] = np.meshgrid(np.arange(-siz[1], siz[1] + 1), np.arange(-siz[0], siz[0] + 1)) + arg = -(x * x + y * y) / (2 * std * std) + h = np.exp(arg) + h[h < scipy.finfo(float).eps * h.max()] = 0 + sumh = h.sum() + if sumh != 0: + h = h / sumh + return h + + +def fspecial_laplacian(alpha): + alpha = max([0, min([alpha, 1])]) + h1 = alpha / (alpha + 1) + h2 = (1 - alpha) / (alpha + 1) + h = [[h1, h2, h1], [h2, -4 / (alpha + 1), h2], [h1, h2, h1]] + h = np.array(h) + return h + + +def fspecial(filter_type, *args, **kwargs): + ''' + python code from: + https://github.com/ronaldosena/imagens-medicas-2/blob/40171a6c259edec7827a6693a93955de2bd39e76/Aulas/aula_2_-_uniform_filter/matlab_fspecial.py + ''' + if filter_type == 'gaussian': + return fspecial_gaussian(*args, **kwargs) + if filter_type == 'laplacian': + return fspecial_laplacian(*args, **kwargs) + + +""" +# -------------------------------------------- +# degradation models +# -------------------------------------------- +""" + + +def bicubic_degradation(x, sf=3): + ''' + Args: + x: HxWxC image, [0, 1] + sf: down-scale factor + Return: + bicubicly downsampled LR image + ''' + x = util.imresize_np(x, scale=1 / sf) + return x + + +def srmd_degradation(x, k, sf=3): + ''' blur + bicubic downsampling + Args: + x: HxWxC image, [0, 1] + k: hxw, double + sf: down-scale factor + Return: + downsampled LR image + Reference: + @inproceedings{zhang2018learning, + title={Learning a single convolutional super-resolution network for multiple degradations}, + author={Zhang, Kai and Zuo, Wangmeng and Zhang, Lei}, + booktitle={IEEE Conference on Computer Vision and Pattern Recognition}, + pages={3262--3271}, + year={2018} + } + ''' + x = ndimage.convolve(x, np.expand_dims(k, axis=2), mode='wrap') # 'nearest' | 'mirror' + x = bicubic_degradation(x, sf=sf) + return x + + +def dpsr_degradation(x, k, sf=3): + ''' bicubic downsampling + blur + Args: + x: HxWxC image, [0, 1] + k: hxw, double + sf: down-scale factor + Return: + downsampled LR image + Reference: + @inproceedings{zhang2019deep, + title={Deep Plug-and-Play Super-Resolution for Arbitrary Blur Kernels}, + author={Zhang, Kai and Zuo, Wangmeng and Zhang, Lei}, + booktitle={IEEE Conference on Computer Vision and Pattern Recognition}, + pages={1671--1681}, + year={2019} + } + ''' + x = bicubic_degradation(x, sf=sf) + x = ndimage.convolve(x, np.expand_dims(k, axis=2), mode='wrap') + return x + + +def classical_degradation(x, k, sf=3): + ''' blur + downsampling + Args: + x: HxWxC image, [0, 1]/[0, 255] + k: hxw, double + sf: down-scale factor + Return: + downsampled LR image + ''' + x = ndimage.convolve(x, np.expand_dims(k, axis=2), mode='wrap') + # x = filters.correlate(x, np.expand_dims(np.flip(k), axis=2)) + st = 0 + return x[st::sf, st::sf, ...] + + +def add_sharpening(img, weight=0.5, radius=50, threshold=10): + """USM sharpening. borrowed from real-ESRGAN + Input image: I; Blurry image: B. + 1. K = I + weight * (I - B) + 2. Mask = 1 if abs(I - B) > threshold, else: 0 + 3. Blur mask: + 4. Out = Mask * K + (1 - Mask) * I + Args: + img (Numpy array): Input image, HWC, BGR; float32, [0, 1]. + weight (float): Sharp weight. Default: 1. + radius (float): Kernel size of Gaussian blur. Default: 50. + threshold (int): + """ + if radius % 2 == 0: + radius += 1 + blur = cv2.GaussianBlur(img, (radius, radius), 0) + residual = img - blur + mask = np.abs(residual) * 255 > threshold + mask = mask.astype('float32') + soft_mask = cv2.GaussianBlur(mask, (radius, radius), 0) + + K = img + weight * residual + K = np.clip(K, 0, 1) + return soft_mask * K + (1 - soft_mask) * img + + +def add_blur(img, sf=4): + wd2 = 4.0 + sf + wd = 2.0 + 0.2 * sf + + wd2 = wd2/4 + wd = wd/4 + + if random.random() < 0.5: + l1 = wd2 * random.random() + l2 = wd2 * random.random() + k = anisotropic_Gaussian(ksize=random.randint(2, 11) + 3, theta=random.random() * np.pi, l1=l1, l2=l2) + else: + k = fspecial('gaussian', random.randint(2, 4) + 3, wd * random.random()) + img = ndimage.convolve(img, np.expand_dims(k, axis=2), mode='mirror') + + return img + + +def add_resize(img, sf=4): + rnum = np.random.rand() + if rnum > 0.8: # up + sf1 = random.uniform(1, 2) + elif rnum < 0.7: # down + sf1 = random.uniform(0.5 / sf, 1) + else: + sf1 = 1.0 + img = cv2.resize(img, (int(sf1 * img.shape[1]), int(sf1 * img.shape[0])), interpolation=random.choice([1, 2, 3])) + img = np.clip(img, 0.0, 1.0) + + return img + + +# def add_Gaussian_noise(img, noise_level1=2, noise_level2=25): +# noise_level = random.randint(noise_level1, noise_level2) +# rnum = np.random.rand() +# if rnum > 0.6: # add color Gaussian noise +# img += np.random.normal(0, noise_level / 255.0, img.shape).astype(np.float32) +# elif rnum < 0.4: # add grayscale Gaussian noise +# img += np.random.normal(0, noise_level / 255.0, (*img.shape[:2], 1)).astype(np.float32) +# else: # add noise +# L = noise_level2 / 255. +# D = np.diag(np.random.rand(3)) +# U = orth(np.random.rand(3, 3)) +# conv = np.dot(np.dot(np.transpose(U), D), U) +# img += np.random.multivariate_normal([0, 0, 0], np.abs(L ** 2 * conv), img.shape[:2]).astype(np.float32) +# img = np.clip(img, 0.0, 1.0) +# return img + +def add_Gaussian_noise(img, noise_level1=2, noise_level2=25): + noise_level = random.randint(noise_level1, noise_level2) + rnum = np.random.rand() + if rnum > 0.6: # add color Gaussian noise + img = img + np.random.normal(0, noise_level / 255.0, img.shape).astype(np.float32) + elif rnum < 0.4: # add grayscale Gaussian noise + img = img + np.random.normal(0, noise_level / 255.0, (*img.shape[:2], 1)).astype(np.float32) + else: # add noise + L = noise_level2 / 255. + D = np.diag(np.random.rand(3)) + U = orth(np.random.rand(3, 3)) + conv = np.dot(np.dot(np.transpose(U), D), U) + img = img + np.random.multivariate_normal([0, 0, 0], np.abs(L ** 2 * conv), img.shape[:2]).astype(np.float32) + img = np.clip(img, 0.0, 1.0) + return img + + +def add_speckle_noise(img, noise_level1=2, noise_level2=25): + noise_level = random.randint(noise_level1, noise_level2) + img = np.clip(img, 0.0, 1.0) + rnum = random.random() + if rnum > 0.6: + img += img * np.random.normal(0, noise_level / 255.0, img.shape).astype(np.float32) + elif rnum < 0.4: + img += img * np.random.normal(0, noise_level / 255.0, (*img.shape[:2], 1)).astype(np.float32) + else: + L = noise_level2 / 255. + D = np.diag(np.random.rand(3)) + U = orth(np.random.rand(3, 3)) + conv = np.dot(np.dot(np.transpose(U), D), U) + img += img * np.random.multivariate_normal([0, 0, 0], np.abs(L ** 2 * conv), img.shape[:2]).astype(np.float32) + img = np.clip(img, 0.0, 1.0) + return img + + +def add_Poisson_noise(img): + img = np.clip((img * 255.0).round(), 0, 255) / 255. + vals = 10 ** (2 * random.random() + 2.0) # [2, 4] + if random.random() < 0.5: + img = np.random.poisson(img * vals).astype(np.float32) / vals + else: + img_gray = np.dot(img[..., :3], [0.299, 0.587, 0.114]) + img_gray = np.clip((img_gray * 255.0).round(), 0, 255) / 255. + noise_gray = np.random.poisson(img_gray * vals).astype(np.float32) / vals - img_gray + img += noise_gray[:, :, np.newaxis] + img = np.clip(img, 0.0, 1.0) + return img + + +def add_JPEG_noise(img): + quality_factor = random.randint(80, 95) + img = cv2.cvtColor(util.single2uint(img), cv2.COLOR_RGB2BGR) + result, encimg = cv2.imencode('.jpg', img, [int(cv2.IMWRITE_JPEG_QUALITY), quality_factor]) + img = cv2.imdecode(encimg, 1) + img = cv2.cvtColor(util.uint2single(img), cv2.COLOR_BGR2RGB) + return img + + +def random_crop(lq, hq, sf=4, lq_patchsize=64): + h, w = lq.shape[:2] + rnd_h = random.randint(0, h - lq_patchsize) + rnd_w = random.randint(0, w - lq_patchsize) + lq = lq[rnd_h:rnd_h + lq_patchsize, rnd_w:rnd_w + lq_patchsize, :] + + rnd_h_H, rnd_w_H = int(rnd_h * sf), int(rnd_w * sf) + hq = hq[rnd_h_H:rnd_h_H + lq_patchsize * sf, rnd_w_H:rnd_w_H + lq_patchsize * sf, :] + return lq, hq + + +def degradation_bsrgan(img, sf=4, lq_patchsize=72, isp_model=None): + """ + This is the degradation model of BSRGAN from the paper + "Designing a Practical Degradation Model for Deep Blind Image Super-Resolution" + ---------- + img: HXWXC, [0, 1], its size should be large than (lq_patchsizexsf)x(lq_patchsizexsf) + sf: scale factor + isp_model: camera ISP model + Returns + ------- + img: low-quality patch, size: lq_patchsizeXlq_patchsizeXC, range: [0, 1] + hq: corresponding high-quality patch, size: (lq_patchsizexsf)X(lq_patchsizexsf)XC, range: [0, 1] + """ + isp_prob, jpeg_prob, scale2_prob = 0.25, 0.9, 0.25 + sf_ori = sf + + h1, w1 = img.shape[:2] + img = img.copy()[:w1 - w1 % sf, :h1 - h1 % sf, ...] # mod crop + h, w = img.shape[:2] + + if h < lq_patchsize * sf or w < lq_patchsize * sf: + raise ValueError(f'img size ({h1}X{w1}) is too small!') + + hq = img.copy() + + if sf == 4 and random.random() < scale2_prob: # downsample1 + if np.random.rand() < 0.5: + img = cv2.resize(img, (int(1 / 2 * img.shape[1]), int(1 / 2 * img.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + img = util.imresize_np(img, 1 / 2, True) + img = np.clip(img, 0.0, 1.0) + sf = 2 + + shuffle_order = random.sample(range(7), 7) + idx1, idx2 = shuffle_order.index(2), shuffle_order.index(3) + if idx1 > idx2: # keep downsample3 last + shuffle_order[idx1], shuffle_order[idx2] = shuffle_order[idx2], shuffle_order[idx1] + + for i in shuffle_order: + + if i == 0: + img = add_blur(img, sf=sf) + + elif i == 1: + img = add_blur(img, sf=sf) + + elif i == 2: + a, b = img.shape[1], img.shape[0] + # downsample2 + if random.random() < 0.75: + sf1 = random.uniform(1, 2 * sf) + img = cv2.resize(img, (int(1 / sf1 * img.shape[1]), int(1 / sf1 * img.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + k = fspecial('gaussian', 25, random.uniform(0.1, 0.6 * sf)) + k_shifted = shift_pixel(k, sf) + k_shifted = k_shifted / k_shifted.sum() # blur with shifted kernel + img = ndimage.convolve(img, np.expand_dims(k_shifted, axis=2), mode='mirror') + img = img[0::sf, 0::sf, ...] # nearest downsampling + img = np.clip(img, 0.0, 1.0) + + elif i == 3: + # downsample3 + img = cv2.resize(img, (int(1 / sf * a), int(1 / sf * b)), interpolation=random.choice([1, 2, 3])) + img = np.clip(img, 0.0, 1.0) + + elif i == 4: + # add Gaussian noise + img = add_Gaussian_noise(img, noise_level1=2, noise_level2=8) + + elif i == 5: + # add JPEG noise + if random.random() < jpeg_prob: + img = add_JPEG_noise(img) + + elif i == 6: + # add processed camera sensor noise + if random.random() < isp_prob and isp_model is not None: + with torch.no_grad(): + img, hq = isp_model.forward(img.copy(), hq) + + # add final JPEG compression noise + img = add_JPEG_noise(img) + + # random crop + img, hq = random_crop(img, hq, sf_ori, lq_patchsize) + + return img, hq + + +# todo no isp_model? +def degradation_bsrgan_variant(image, sf=4, isp_model=None, up=False): + """ + This is the degradation model of BSRGAN from the paper + "Designing a Practical Degradation Model for Deep Blind Image Super-Resolution" + ---------- + sf: scale factor + isp_model: camera ISP model + Returns + ------- + img: low-quality patch, size: lq_patchsizeXlq_patchsizeXC, range: [0, 1] + hq: corresponding high-quality patch, size: (lq_patchsizexsf)X(lq_patchsizexsf)XC, range: [0, 1] + """ + image = util.uint2single(image) + isp_prob, jpeg_prob, scale2_prob = 0.25, 0.9, 0.25 + sf_ori = sf + + h1, w1 = image.shape[:2] + image = image.copy()[:w1 - w1 % sf, :h1 - h1 % sf, ...] # mod crop + h, w = image.shape[:2] + + hq = image.copy() + + if sf == 4 and random.random() < scale2_prob: # downsample1 + if np.random.rand() < 0.5: + image = cv2.resize(image, (int(1 / 2 * image.shape[1]), int(1 / 2 * image.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + image = util.imresize_np(image, 1 / 2, True) + image = np.clip(image, 0.0, 1.0) + sf = 2 + + shuffle_order = random.sample(range(7), 7) + idx1, idx2 = shuffle_order.index(2), shuffle_order.index(3) + if idx1 > idx2: # keep downsample3 last + shuffle_order[idx1], shuffle_order[idx2] = shuffle_order[idx2], shuffle_order[idx1] + + for i in shuffle_order: + + if i == 0: + image = add_blur(image, sf=sf) + + # elif i == 1: + # image = add_blur(image, sf=sf) + + if i == 0: + pass + + elif i == 2: + a, b = image.shape[1], image.shape[0] + # downsample2 + if random.random() < 0.8: + sf1 = random.uniform(1, 2 * sf) + image = cv2.resize(image, (int(1 / sf1 * image.shape[1]), int(1 / sf1 * image.shape[0])), + interpolation=random.choice([1, 2, 3])) + else: + k = fspecial('gaussian', 25, random.uniform(0.1, 0.6 * sf)) + k_shifted = shift_pixel(k, sf) + k_shifted = k_shifted / k_shifted.sum() # blur with shifted kernel + image = ndimage.convolve(image, np.expand_dims(k_shifted, axis=2), mode='mirror') + image = image[0::sf, 0::sf, ...] # nearest downsampling + + image = np.clip(image, 0.0, 1.0) + + elif i == 3: + # downsample3 + image = cv2.resize(image, (int(1 / sf * a), int(1 / sf * b)), interpolation=random.choice([1, 2, 3])) + image = np.clip(image, 0.0, 1.0) + + elif i == 4: + # add Gaussian noise + image = add_Gaussian_noise(image, noise_level1=1, noise_level2=2) + + elif i == 5: + # add JPEG noise + if random.random() < jpeg_prob: + image = add_JPEG_noise(image) + # + # elif i == 6: + # # add processed camera sensor noise + # if random.random() < isp_prob and isp_model is not None: + # with torch.no_grad(): + # img, hq = isp_model.forward(img.copy(), hq) + + # add final JPEG compression noise + image = add_JPEG_noise(image) + image = util.single2uint(image) + if up: + image = cv2.resize(image, (w1, h1), interpolation=cv2.INTER_CUBIC) # todo: random, as above? want to condition on it then + example = {"image": image} + return example + + + + +if __name__ == '__main__': + print("hey") + img = util.imread_uint('utils/test.png', 3) + img = img[:448, :448] + h = img.shape[0] // 4 + print("resizing to", h) + sf = 4 + deg_fn = partial(degradation_bsrgan_variant, sf=sf) + for i in range(20): + print(i) + img_hq = img + img_lq = deg_fn(img)["image"] + img_hq, img_lq = util.uint2single(img_hq), util.uint2single(img_lq) + print(img_lq) + img_lq_bicubic = albumentations.SmallestMaxSize(max_size=h, interpolation=cv2.INTER_CUBIC)(image=img_hq)["image"] + print(img_lq.shape) + print("bicubic", img_lq_bicubic.shape) + print(img_hq.shape) + lq_nearest = cv2.resize(util.single2uint(img_lq), (int(sf * img_lq.shape[1]), int(sf * img_lq.shape[0])), + interpolation=0) + lq_bicubic_nearest = cv2.resize(util.single2uint(img_lq_bicubic), + (int(sf * img_lq.shape[1]), int(sf * img_lq.shape[0])), + interpolation=0) + img_concat = np.concatenate([lq_bicubic_nearest, lq_nearest, util.single2uint(img_hq)], axis=1) + util.imsave(img_concat, str(i) + '.png') diff --git a/repositories/ldm/modules/image_degradation/utils/test.png b/repositories/ldm/modules/image_degradation/utils/test.png new file mode 100644 index 0000000000000000000000000000000000000000..4249b43de0f22707758d13c240268a401642f6e6 GIT binary patch literal 441072 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ϐ}ewmE<1P{om13ZIR;da-v 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

9YZ4 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 z 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

C;kIRhg~gM!vAOC`Sn4{$JCXdp2~=e39X=Y38A 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 W{vqoXvb{`%VS%WMs+AypQUKtQ7_{2@A^ytA-f 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%0A~?y&eN-{wPneIq!@VTU2wGtzL+} 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 bzm8AbuVoE9qaMlyfL|Cr6WvPfd(r;^ifYg6 literal 0 HcmV?d00001 diff --git a/repositories/ldm/modules/image_degradation/utils_image.py b/repositories/ldm/modules/image_degradation/utils_image.py new file mode 100644 index 000000000..0175f155a --- /dev/null +++ b/repositories/ldm/modules/image_degradation/utils_image.py @@ -0,0 +1,916 @@ +import os +import math +import random +import numpy as np +import torch +import cv2 +from torchvision.utils import make_grid +from datetime import datetime +#import matplotlib.pyplot as plt # TODO: check with Dominik, also bsrgan.py vs bsrgan_light.py + + +os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE" + + +''' +# -------------------------------------------- +# Kai Zhang (github: https://github.com/cszn) +# 03/Mar/2019 +# -------------------------------------------- +# https://github.com/twhui/SRGAN-pyTorch +# https://github.com/xinntao/BasicSR +# -------------------------------------------- +''' + + +IMG_EXTENSIONS = ['.jpg', '.JPG', '.jpeg', '.JPEG', '.png', '.PNG', '.ppm', '.PPM', '.bmp', '.BMP', '.tif'] + + +def is_image_file(filename): + return any(filename.endswith(extension) for extension in IMG_EXTENSIONS) + + +def get_timestamp(): + return datetime.now().strftime('%y%m%d-%H%M%S') + + +def imshow(x, title=None, cbar=False, figsize=None): + plt.figure(figsize=figsize) + plt.imshow(np.squeeze(x), interpolation='nearest', cmap='gray') + if title: + plt.title(title) + if cbar: + plt.colorbar() + plt.show() + + +def surf(Z, cmap='rainbow', figsize=None): + plt.figure(figsize=figsize) + ax3 = plt.axes(projection='3d') + + w, h = Z.shape[:2] + xx = np.arange(0,w,1) + yy = np.arange(0,h,1) + X, Y = np.meshgrid(xx, yy) + ax3.plot_surface(X,Y,Z,cmap=cmap) + #ax3.contour(X,Y,Z, zdim='z',offset=-2,cmap=cmap) + plt.show() + + +''' +# -------------------------------------------- +# get image pathes +# -------------------------------------------- +''' + + +def get_image_paths(dataroot): + paths = None # return None if dataroot is None + if dataroot is not None: + paths = sorted(_get_paths_from_images(dataroot)) + return paths + + +def _get_paths_from_images(path): + assert os.path.isdir(path), '{:s} is not a valid directory'.format(path) + images = [] + for dirpath, _, fnames in sorted(os.walk(path)): + for fname in sorted(fnames): + if is_image_file(fname): + img_path = os.path.join(dirpath, fname) + images.append(img_path) + assert images, '{:s} has no valid image file'.format(path) + return images + + +''' +# -------------------------------------------- +# split large images into small images +# -------------------------------------------- +''' + + +def patches_from_image(img, p_size=512, p_overlap=64, p_max=800): + w, h = img.shape[:2] + patches = [] + if w > p_max and h > p_max: + w1 = list(np.arange(0, w-p_size, p_size-p_overlap, dtype=np.int)) + h1 = list(np.arange(0, h-p_size, p_size-p_overlap, dtype=np.int)) + w1.append(w-p_size) + h1.append(h-p_size) +# print(w1) +# print(h1) + for i in w1: + for j in h1: + patches.append(img[i:i+p_size, j:j+p_size,:]) + else: + patches.append(img) + + return patches + + +def imssave(imgs, img_path): + """ + imgs: list, N images of size WxHxC + """ + img_name, ext = os.path.splitext(os.path.basename(img_path)) + + for i, img in enumerate(imgs): + if img.ndim == 3: + img = img[:, :, [2, 1, 0]] + new_path = os.path.join(os.path.dirname(img_path), img_name+str('_s{:04d}'.format(i))+'.png') + cv2.imwrite(new_path, img) + + +def split_imageset(original_dataroot, taget_dataroot, n_channels=3, p_size=800, p_overlap=96, p_max=1000): + """ + split the large images from original_dataroot into small overlapped images with size (p_size)x(p_size), + and save them into taget_dataroot; only the images with larger size than (p_max)x(p_max) + will be splitted. + Args: + original_dataroot: + taget_dataroot: + p_size: size of small images + p_overlap: patch size in training is a good choice + p_max: images with smaller size than (p_max)x(p_max) keep unchanged. + """ + paths = get_image_paths(original_dataroot) + for img_path in paths: + # img_name, ext = os.path.splitext(os.path.basename(img_path)) + img = imread_uint(img_path, n_channels=n_channels) + patches = patches_from_image(img, p_size, p_overlap, p_max) + imssave(patches, os.path.join(taget_dataroot,os.path.basename(img_path))) + #if original_dataroot == taget_dataroot: + #del img_path + +''' +# -------------------------------------------- +# makedir +# -------------------------------------------- +''' + + +def mkdir(path): + if not os.path.exists(path): + os.makedirs(path) + + +def mkdirs(paths): + if isinstance(paths, str): + mkdir(paths) + else: + for path in paths: + mkdir(path) + + +def mkdir_and_rename(path): + if os.path.exists(path): + new_name = path + '_archived_' + get_timestamp() + print('Path already exists. Rename it to [{:s}]'.format(new_name)) + os.rename(path, new_name) + os.makedirs(path) + + +''' +# -------------------------------------------- +# read image from path +# opencv is fast, but read BGR numpy image +# -------------------------------------------- +''' + + +# -------------------------------------------- +# get uint8 image of size HxWxn_channles (RGB) +# -------------------------------------------- +def imread_uint(path, n_channels=3): + # input: path + # output: HxWx3(RGB or GGG), or HxWx1 (G) + if n_channels == 1: + img = cv2.imread(path, 0) # cv2.IMREAD_GRAYSCALE + img = np.expand_dims(img, axis=2) # HxWx1 + elif n_channels == 3: + img = cv2.imread(path, cv2.IMREAD_UNCHANGED) # BGR or G + if img.ndim == 2: + img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) # GGG + else: + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # RGB + return img + + +# -------------------------------------------- +# matlab's imwrite +# -------------------------------------------- +def imsave(img, img_path): + img = np.squeeze(img) + if img.ndim == 3: + img = img[:, :, [2, 1, 0]] + cv2.imwrite(img_path, img) + +def imwrite(img, img_path): + img = np.squeeze(img) + if img.ndim == 3: + img = img[:, :, [2, 1, 0]] + cv2.imwrite(img_path, img) + + + +# -------------------------------------------- +# get single image of size HxWxn_channles (BGR) +# -------------------------------------------- +def read_img(path): + # read image by cv2 + # return: Numpy float32, HWC, BGR, [0,1] + img = cv2.imread(path, cv2.IMREAD_UNCHANGED) # cv2.IMREAD_GRAYSCALE + img = img.astype(np.float32) / 255. + if img.ndim == 2: + img = np.expand_dims(img, axis=2) + # some images have 4 channels + if img.shape[2] > 3: + img = img[:, :, :3] + return img + + +''' +# -------------------------------------------- +# image format conversion +# -------------------------------------------- +# numpy(single) <---> numpy(unit) +# numpy(single) <---> tensor +# numpy(unit) <---> tensor +# -------------------------------------------- +''' + + +# -------------------------------------------- +# numpy(single) [0, 1] <---> numpy(unit) +# -------------------------------------------- + + +def uint2single(img): + + return np.float32(img/255.) + + +def single2uint(img): + + return np.uint8((img.clip(0, 1)*255.).round()) + + +def uint162single(img): + + return np.float32(img/65535.) + + +def single2uint16(img): + + return np.uint16((img.clip(0, 1)*65535.).round()) + + +# -------------------------------------------- +# numpy(unit) (HxWxC or HxW) <---> tensor +# -------------------------------------------- + + +# convert uint to 4-dimensional torch tensor +def uint2tensor4(img): + if img.ndim == 2: + img = np.expand_dims(img, axis=2) + return torch.from_numpy(np.ascontiguousarray(img)).permute(2, 0, 1).float().div(255.).unsqueeze(0) + + +# convert uint to 3-dimensional torch tensor +def uint2tensor3(img): + if img.ndim == 2: + img = np.expand_dims(img, axis=2) + return torch.from_numpy(np.ascontiguousarray(img)).permute(2, 0, 1).float().div(255.) + + +# convert 2/3/4-dimensional torch tensor to uint +def tensor2uint(img): + img = img.data.squeeze().float().clamp_(0, 1).cpu().numpy() + if img.ndim == 3: + img = np.transpose(img, (1, 2, 0)) + return np.uint8((img*255.0).round()) + + +# -------------------------------------------- +# numpy(single) (HxWxC) <---> tensor +# -------------------------------------------- + + +# convert single (HxWxC) to 3-dimensional torch tensor +def single2tensor3(img): + return torch.from_numpy(np.ascontiguousarray(img)).permute(2, 0, 1).float() + + +# convert single (HxWxC) to 4-dimensional torch tensor +def single2tensor4(img): + return torch.from_numpy(np.ascontiguousarray(img)).permute(2, 0, 1).float().unsqueeze(0) + + +# convert torch tensor to single +def tensor2single(img): + img = img.data.squeeze().float().cpu().numpy() + if img.ndim == 3: + img = np.transpose(img, (1, 2, 0)) + + return img + +# convert torch tensor to single +def tensor2single3(img): + img = img.data.squeeze().float().cpu().numpy() + if img.ndim == 3: + img = np.transpose(img, (1, 2, 0)) + elif img.ndim == 2: + img = np.expand_dims(img, axis=2) + return img + + +def single2tensor5(img): + return torch.from_numpy(np.ascontiguousarray(img)).permute(2, 0, 1, 3).float().unsqueeze(0) + + +def single32tensor5(img): + return torch.from_numpy(np.ascontiguousarray(img)).float().unsqueeze(0).unsqueeze(0) + + +def single42tensor4(img): + return torch.from_numpy(np.ascontiguousarray(img)).permute(2, 0, 1, 3).float() + + +# from skimage.io import imread, imsave +def tensor2img(tensor, out_type=np.uint8, min_max=(0, 1)): + ''' + Converts a torch Tensor into an image Numpy array of BGR channel order + Input: 4D(B,(3/1),H,W), 3D(C,H,W), or 2D(H,W), any range, RGB channel order + Output: 3D(H,W,C) or 2D(H,W), [0,255], np.uint8 (default) + ''' + tensor = tensor.squeeze().float().cpu().clamp_(*min_max) # squeeze first, then clamp + tensor = (tensor - min_max[0]) / (min_max[1] - min_max[0]) # to range [0,1] + n_dim = tensor.dim() + if n_dim == 4: + n_img = len(tensor) + img_np = make_grid(tensor, nrow=int(math.sqrt(n_img)), normalize=False).numpy() + img_np = np.transpose(img_np[[2, 1, 0], :, :], (1, 2, 0)) # HWC, BGR + elif n_dim == 3: + img_np = tensor.numpy() + img_np = np.transpose(img_np[[2, 1, 0], :, :], (1, 2, 0)) # HWC, BGR + elif n_dim == 2: + img_np = tensor.numpy() + else: + raise TypeError( + 'Only support 4D, 3D and 2D tensor. But received with dimension: {:d}'.format(n_dim)) + if out_type == np.uint8: + img_np = (img_np * 255.0).round() + # Important. Unlike matlab, numpy.unit8() WILL NOT round by default. + return img_np.astype(out_type) + + +''' +# -------------------------------------------- +# Augmentation, flipe and/or rotate +# -------------------------------------------- +# The following two are enough. +# (1) augmet_img: numpy image of WxHxC or WxH +# (2) augment_img_tensor4: tensor image 1xCxWxH +# -------------------------------------------- +''' + + +def augment_img(img, mode=0): + '''Kai Zhang (github: https://github.com/cszn) + ''' + if mode == 0: + return img + elif mode == 1: + return np.flipud(np.rot90(img)) + elif mode == 2: + return np.flipud(img) + elif mode == 3: + return np.rot90(img, k=3) + elif mode == 4: + return np.flipud(np.rot90(img, k=2)) + elif mode == 5: + return np.rot90(img) + elif mode == 6: + return np.rot90(img, k=2) + elif mode == 7: + return np.flipud(np.rot90(img, k=3)) + + +def augment_img_tensor4(img, mode=0): + '''Kai Zhang (github: https://github.com/cszn) + ''' + if mode == 0: + return img + elif mode == 1: + return img.rot90(1, [2, 3]).flip([2]) + elif mode == 2: + return img.flip([2]) + elif mode == 3: + return img.rot90(3, [2, 3]) + elif mode == 4: + return img.rot90(2, [2, 3]).flip([2]) + elif mode == 5: + return img.rot90(1, [2, 3]) + elif mode == 6: + return img.rot90(2, [2, 3]) + elif mode == 7: + return img.rot90(3, [2, 3]).flip([2]) + + +def augment_img_tensor(img, mode=0): + '''Kai Zhang (github: https://github.com/cszn) + ''' + img_size = img.size() + img_np = img.data.cpu().numpy() + if len(img_size) == 3: + img_np = np.transpose(img_np, (1, 2, 0)) + elif len(img_size) == 4: + img_np = np.transpose(img_np, (2, 3, 1, 0)) + img_np = augment_img(img_np, mode=mode) + img_tensor = torch.from_numpy(np.ascontiguousarray(img_np)) + if len(img_size) == 3: + img_tensor = img_tensor.permute(2, 0, 1) + elif len(img_size) == 4: + img_tensor = img_tensor.permute(3, 2, 0, 1) + + return img_tensor.type_as(img) + + +def augment_img_np3(img, mode=0): + if mode == 0: + return img + elif mode == 1: + return img.transpose(1, 0, 2) + elif mode == 2: + return img[::-1, :, :] + elif mode == 3: + img = img[::-1, :, :] + img = img.transpose(1, 0, 2) + return img + elif mode == 4: + return img[:, ::-1, :] + elif mode == 5: + img = img[:, ::-1, :] + img = img.transpose(1, 0, 2) + return img + elif mode == 6: + img = img[:, ::-1, :] + img = img[::-1, :, :] + return img + elif mode == 7: + img = img[:, ::-1, :] + img = img[::-1, :, :] + img = img.transpose(1, 0, 2) + return img + + +def augment_imgs(img_list, hflip=True, rot=True): + # horizontal flip OR rotate + hflip = hflip and random.random() < 0.5 + vflip = rot and random.random() < 0.5 + rot90 = rot and random.random() < 0.5 + + def _augment(img): + if hflip: + img = img[:, ::-1, :] + if vflip: + img = img[::-1, :, :] + if rot90: + img = img.transpose(1, 0, 2) + return img + + return [_augment(img) for img in img_list] + + +''' +# -------------------------------------------- +# modcrop and shave +# -------------------------------------------- +''' + + +def modcrop(img_in, scale): + # img_in: Numpy, HWC or HW + img = np.copy(img_in) + if img.ndim == 2: + H, W = img.shape + H_r, W_r = H % scale, W % scale + img = img[:H - H_r, :W - W_r] + elif img.ndim == 3: + H, W, C = img.shape + H_r, W_r = H % scale, W % scale + img = img[:H - H_r, :W - W_r, :] + else: + raise ValueError('Wrong img ndim: [{:d}].'.format(img.ndim)) + return img + + +def shave(img_in, border=0): + # img_in: Numpy, HWC or HW + img = np.copy(img_in) + h, w = img.shape[:2] + img = img[border:h-border, border:w-border] + return img + + +''' +# -------------------------------------------- +# image processing process on numpy image +# channel_convert(in_c, tar_type, img_list): +# rgb2ycbcr(img, only_y=True): +# bgr2ycbcr(img, only_y=True): +# ycbcr2rgb(img): +# -------------------------------------------- +''' + + +def rgb2ycbcr(img, only_y=True): + '''same as matlab rgb2ycbcr + only_y: only return Y channel + Input: + uint8, [0, 255] + float, [0, 1] + ''' + in_img_type = img.dtype + img.astype(np.float32) + if in_img_type != np.uint8: + img *= 255. + # convert + if only_y: + rlt = np.dot(img, [65.481, 128.553, 24.966]) / 255.0 + 16.0 + else: + rlt = np.matmul(img, [[65.481, -37.797, 112.0], [128.553, -74.203, -93.786], + [24.966, 112.0, -18.214]]) / 255.0 + [16, 128, 128] + if in_img_type == np.uint8: + rlt = rlt.round() + else: + rlt /= 255. + return rlt.astype(in_img_type) + + +def ycbcr2rgb(img): + '''same as matlab ycbcr2rgb + Input: + uint8, [0, 255] + float, [0, 1] + ''' + in_img_type = img.dtype + img.astype(np.float32) + if in_img_type != np.uint8: + img *= 255. + # convert + rlt = np.matmul(img, [[0.00456621, 0.00456621, 0.00456621], [0, -0.00153632, 0.00791071], + [0.00625893, -0.00318811, 0]]) * 255.0 + [-222.921, 135.576, -276.836] + if in_img_type == np.uint8: + rlt = rlt.round() + else: + rlt /= 255. + return rlt.astype(in_img_type) + + +def bgr2ycbcr(img, only_y=True): + '''bgr version of rgb2ycbcr + only_y: only return Y channel + Input: + uint8, [0, 255] + float, [0, 1] + ''' + in_img_type = img.dtype + img.astype(np.float32) + if in_img_type != np.uint8: + img *= 255. + # convert + if only_y: + rlt = np.dot(img, [24.966, 128.553, 65.481]) / 255.0 + 16.0 + else: + rlt = np.matmul(img, [[24.966, 112.0, -18.214], [128.553, -74.203, -93.786], + [65.481, -37.797, 112.0]]) / 255.0 + [16, 128, 128] + if in_img_type == np.uint8: + rlt = rlt.round() + else: + rlt /= 255. + return rlt.astype(in_img_type) + + +def channel_convert(in_c, tar_type, img_list): + # conversion among BGR, gray and y + if in_c == 3 and tar_type == 'gray': # BGR to gray + gray_list = [cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) for img in img_list] + return [np.expand_dims(img, axis=2) for img in gray_list] + elif in_c == 3 and tar_type == 'y': # BGR to y + y_list = [bgr2ycbcr(img, only_y=True) for img in img_list] + return [np.expand_dims(img, axis=2) for img in y_list] + elif in_c == 1 and tar_type == 'RGB': # gray/y to BGR + return [cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) for img in img_list] + else: + return img_list + + +''' +# -------------------------------------------- +# metric, PSNR and SSIM +# -------------------------------------------- +''' + + +# -------------------------------------------- +# PSNR +# -------------------------------------------- +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() + + +''' +# -------------------------------------------- +# matlab's bicubic imresize (numpy and torch) [0, 1] +# -------------------------------------------- +''' + + +# matlab 'imresize' function, now only support 'bicubic' +def cubic(x): + absx = torch.abs(x) + absx2 = absx**2 + absx3 = absx**3 + return (1.5*absx3 - 2.5*absx2 + 1) * ((absx <= 1).type_as(absx)) + \ + (-0.5*absx3 + 2.5*absx2 - 4*absx + 2) * (((absx > 1)*(absx <= 2)).type_as(absx)) + + +def calculate_weights_indices(in_length, out_length, scale, kernel, kernel_width, antialiasing): + if (scale < 1) and (antialiasing): + # Use a modified kernel to simultaneously interpolate and antialias- larger kernel width + kernel_width = kernel_width / scale + + # Output-space coordinates + x = torch.linspace(1, out_length, out_length) + + # Input-space coordinates. Calculate the inverse mapping such that 0.5 + # in output space maps to 0.5 in input space, and 0.5+scale in output + # space maps to 1.5 in input space. + u = x / scale + 0.5 * (1 - 1 / scale) + + # What is the left-most pixel that can be involved in the computation? + left = torch.floor(u - kernel_width / 2) + + # What is the maximum number of pixels that can be involved in the + # computation? Note: it's OK to use an extra pixel here; if the + # corresponding weights are all zero, it will be eliminated at the end + # of this function. + P = math.ceil(kernel_width) + 2 + + # The indices of the input pixels involved in computing the k-th output + # pixel are in row k of the indices matrix. + indices = left.view(out_length, 1).expand(out_length, P) + torch.linspace(0, P - 1, P).view( + 1, P).expand(out_length, P) + + # The weights used to compute the k-th output pixel are in row k of the + # weights matrix. + distance_to_center = u.view(out_length, 1).expand(out_length, P) - indices + # apply cubic kernel + if (scale < 1) and (antialiasing): + weights = scale * cubic(distance_to_center * scale) + else: + weights = cubic(distance_to_center) + # Normalize the weights matrix so that each row sums to 1. + weights_sum = torch.sum(weights, 1).view(out_length, 1) + weights = weights / weights_sum.expand(out_length, P) + + # If a column in weights is all zero, get rid of it. only consider the first and last column. + weights_zero_tmp = torch.sum((weights == 0), 0) + if not math.isclose(weights_zero_tmp[0], 0, rel_tol=1e-6): + indices = indices.narrow(1, 1, P - 2) + weights = weights.narrow(1, 1, P - 2) + if not math.isclose(weights_zero_tmp[-1], 0, rel_tol=1e-6): + indices = indices.narrow(1, 0, P - 2) + weights = weights.narrow(1, 0, P - 2) + weights = weights.contiguous() + indices = indices.contiguous() + sym_len_s = -indices.min() + 1 + sym_len_e = indices.max() - in_length + indices = indices + sym_len_s - 1 + return weights, indices, int(sym_len_s), int(sym_len_e) + + +# -------------------------------------------- +# imresize for tensor image [0, 1] +# -------------------------------------------- +def imresize(img, scale, antialiasing=True): + # Now the scale should be the same for H and W + # input: img: pytorch tensor, CHW or HW [0,1] + # output: CHW or HW [0,1] w/o round + need_squeeze = True if img.dim() == 2 else False + if need_squeeze: + img.unsqueeze_(0) + in_C, in_H, in_W = img.size() + out_C, out_H, out_W = in_C, math.ceil(in_H * scale), math.ceil(in_W * scale) + kernel_width = 4 + kernel = 'cubic' + + # Return the desired dimension order for performing the resize. The + # strategy is to perform the resize first along the dimension with the + # smallest scale factor. + # Now we do not support this. + + # get weights and indices + weights_H, indices_H, sym_len_Hs, sym_len_He = calculate_weights_indices( + in_H, out_H, scale, kernel, kernel_width, antialiasing) + weights_W, indices_W, sym_len_Ws, sym_len_We = calculate_weights_indices( + in_W, out_W, scale, kernel, kernel_width, antialiasing) + # process H dimension + # symmetric copying + img_aug = torch.FloatTensor(in_C, in_H + sym_len_Hs + sym_len_He, in_W) + img_aug.narrow(1, sym_len_Hs, in_H).copy_(img) + + sym_patch = img[:, :sym_len_Hs, :] + inv_idx = torch.arange(sym_patch.size(1) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(1, inv_idx) + img_aug.narrow(1, 0, sym_len_Hs).copy_(sym_patch_inv) + + sym_patch = img[:, -sym_len_He:, :] + inv_idx = torch.arange(sym_patch.size(1) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(1, inv_idx) + img_aug.narrow(1, sym_len_Hs + in_H, sym_len_He).copy_(sym_patch_inv) + + out_1 = torch.FloatTensor(in_C, out_H, in_W) + kernel_width = weights_H.size(1) + for i in range(out_H): + idx = int(indices_H[i][0]) + for j in range(out_C): + out_1[j, i, :] = img_aug[j, idx:idx + kernel_width, :].transpose(0, 1).mv(weights_H[i]) + + # process W dimension + # symmetric copying + out_1_aug = torch.FloatTensor(in_C, out_H, in_W + sym_len_Ws + sym_len_We) + out_1_aug.narrow(2, sym_len_Ws, in_W).copy_(out_1) + + sym_patch = out_1[:, :, :sym_len_Ws] + inv_idx = torch.arange(sym_patch.size(2) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(2, inv_idx) + out_1_aug.narrow(2, 0, sym_len_Ws).copy_(sym_patch_inv) + + sym_patch = out_1[:, :, -sym_len_We:] + inv_idx = torch.arange(sym_patch.size(2) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(2, inv_idx) + out_1_aug.narrow(2, sym_len_Ws + in_W, sym_len_We).copy_(sym_patch_inv) + + out_2 = torch.FloatTensor(in_C, out_H, out_W) + kernel_width = weights_W.size(1) + for i in range(out_W): + idx = int(indices_W[i][0]) + for j in range(out_C): + out_2[j, :, i] = out_1_aug[j, :, idx:idx + kernel_width].mv(weights_W[i]) + if need_squeeze: + out_2.squeeze_() + return out_2 + + +# -------------------------------------------- +# imresize for numpy image [0, 1] +# -------------------------------------------- +def imresize_np(img, scale, antialiasing=True): + # Now the scale should be the same for H and W + # input: img: Numpy, HWC or HW [0,1] + # output: HWC or HW [0,1] w/o round + img = torch.from_numpy(img) + need_squeeze = True if img.dim() == 2 else False + if need_squeeze: + img.unsqueeze_(2) + + in_H, in_W, in_C = img.size() + out_C, out_H, out_W = in_C, math.ceil(in_H * scale), math.ceil(in_W * scale) + kernel_width = 4 + kernel = 'cubic' + + # Return the desired dimension order for performing the resize. The + # strategy is to perform the resize first along the dimension with the + # smallest scale factor. + # Now we do not support this. + + # get weights and indices + weights_H, indices_H, sym_len_Hs, sym_len_He = calculate_weights_indices( + in_H, out_H, scale, kernel, kernel_width, antialiasing) + weights_W, indices_W, sym_len_Ws, sym_len_We = calculate_weights_indices( + in_W, out_W, scale, kernel, kernel_width, antialiasing) + # process H dimension + # symmetric copying + img_aug = torch.FloatTensor(in_H + sym_len_Hs + sym_len_He, in_W, in_C) + img_aug.narrow(0, sym_len_Hs, in_H).copy_(img) + + sym_patch = img[:sym_len_Hs, :, :] + inv_idx = torch.arange(sym_patch.size(0) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(0, inv_idx) + img_aug.narrow(0, 0, sym_len_Hs).copy_(sym_patch_inv) + + sym_patch = img[-sym_len_He:, :, :] + inv_idx = torch.arange(sym_patch.size(0) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(0, inv_idx) + img_aug.narrow(0, sym_len_Hs + in_H, sym_len_He).copy_(sym_patch_inv) + + out_1 = torch.FloatTensor(out_H, in_W, in_C) + kernel_width = weights_H.size(1) + for i in range(out_H): + idx = int(indices_H[i][0]) + for j in range(out_C): + out_1[i, :, j] = img_aug[idx:idx + kernel_width, :, j].transpose(0, 1).mv(weights_H[i]) + + # process W dimension + # symmetric copying + out_1_aug = torch.FloatTensor(out_H, in_W + sym_len_Ws + sym_len_We, in_C) + out_1_aug.narrow(1, sym_len_Ws, in_W).copy_(out_1) + + sym_patch = out_1[:, :sym_len_Ws, :] + inv_idx = torch.arange(sym_patch.size(1) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(1, inv_idx) + out_1_aug.narrow(1, 0, sym_len_Ws).copy_(sym_patch_inv) + + sym_patch = out_1[:, -sym_len_We:, :] + inv_idx = torch.arange(sym_patch.size(1) - 1, -1, -1).long() + sym_patch_inv = sym_patch.index_select(1, inv_idx) + out_1_aug.narrow(1, sym_len_Ws + in_W, sym_len_We).copy_(sym_patch_inv) + + out_2 = torch.FloatTensor(out_H, out_W, in_C) + kernel_width = weights_W.size(1) + for i in range(out_W): + idx = int(indices_W[i][0]) + for j in range(out_C): + out_2[:, i, j] = out_1_aug[:, idx:idx + kernel_width, j].mv(weights_W[i]) + if need_squeeze: + out_2.squeeze_() + + return out_2.numpy() + + +if __name__ == '__main__': + print('---') +# img = imread_uint('test.bmp', 3) +# img = uint2single(img) +# img_bicubic = imresize_np(img, 1/4) \ No newline at end of file diff --git a/repositories/ldm/modules/karlo/__init__.py b/repositories/ldm/modules/karlo/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/ldm/modules/karlo/diffusers_pipeline.py b/repositories/ldm/modules/karlo/diffusers_pipeline.py new file mode 100644 index 000000000..07f72b35a --- /dev/null +++ b/repositories/ldm/modules/karlo/diffusers_pipeline.py @@ -0,0 +1,512 @@ +# Copyright 2022 Kakao Brain and The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import inspect +from typing import List, Optional, Tuple, Union + +import torch +from torch.nn import functional as F + +from transformers import CLIPTextModelWithProjection, CLIPTokenizer +from transformers.models.clip.modeling_clip import CLIPTextModelOutput + +from ...models import PriorTransformer, UNet2DConditionModel, UNet2DModel +from ...pipelines import DiffusionPipeline, ImagePipelineOutput +from ...schedulers import UnCLIPScheduler +from ...utils import is_accelerate_available, logging, randn_tensor +from .text_proj import UnCLIPTextProjModel + + +logger = logging.get_logger(__name__) # pylint: disable=invalid-name + + +class UnCLIPPipeline(DiffusionPipeline): + """ + Pipeline for text-to-image generation using unCLIP + This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the + library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.) + Args: + text_encoder ([`CLIPTextModelWithProjection`]): + Frozen text-encoder. + tokenizer (`CLIPTokenizer`): + Tokenizer of class + [CLIPTokenizer](https://huggingface.co/docs/transformers/v4.21.0/en/model_doc/clip#transformers.CLIPTokenizer). + prior ([`PriorTransformer`]): + The canonincal unCLIP prior to approximate the image embedding from the text embedding. + text_proj ([`UnCLIPTextProjModel`]): + Utility class to prepare and combine the embeddings before they are passed to the decoder. + decoder ([`UNet2DConditionModel`]): + The decoder to invert the image embedding into an image. + super_res_first ([`UNet2DModel`]): + Super resolution unet. Used in all but the last step of the super resolution diffusion process. + super_res_last ([`UNet2DModel`]): + Super resolution unet. Used in the last step of the super resolution diffusion process. + prior_scheduler ([`UnCLIPScheduler`]): + Scheduler used in the prior denoising process. Just a modified DDPMScheduler. + decoder_scheduler ([`UnCLIPScheduler`]): + Scheduler used in the decoder denoising process. Just a modified DDPMScheduler. + super_res_scheduler ([`UnCLIPScheduler`]): + Scheduler used in the super resolution denoising process. Just a modified DDPMScheduler. + """ + + prior: PriorTransformer + decoder: UNet2DConditionModel + text_proj: UnCLIPTextProjModel + text_encoder: CLIPTextModelWithProjection + tokenizer: CLIPTokenizer + super_res_first: UNet2DModel + super_res_last: UNet2DModel + + prior_scheduler: UnCLIPScheduler + decoder_scheduler: UnCLIPScheduler + super_res_scheduler: UnCLIPScheduler + + def __init__( + self, + prior: PriorTransformer, + decoder: UNet2DConditionModel, + text_encoder: CLIPTextModelWithProjection, + tokenizer: CLIPTokenizer, + text_proj: UnCLIPTextProjModel, + super_res_first: UNet2DModel, + super_res_last: UNet2DModel, + prior_scheduler: UnCLIPScheduler, + decoder_scheduler: UnCLIPScheduler, + super_res_scheduler: UnCLIPScheduler, + ): + super().__init__() + + self.register_modules( + prior=prior, + decoder=decoder, + text_encoder=text_encoder, + tokenizer=tokenizer, + text_proj=text_proj, + super_res_first=super_res_first, + super_res_last=super_res_last, + prior_scheduler=prior_scheduler, + decoder_scheduler=decoder_scheduler, + super_res_scheduler=super_res_scheduler, + ) + + def prepare_latents(self, shape, dtype, device, generator, latents, scheduler): + if latents is None: + latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype) + else: + if latents.shape != shape: + raise ValueError(f"Unexpected latents shape, got {latents.shape}, expected {shape}") + latents = latents.to(device) + + latents = latents * scheduler.init_noise_sigma + return latents + + def _encode_prompt( + self, + prompt, + device, + num_images_per_prompt, + do_classifier_free_guidance, + text_model_output: Optional[Union[CLIPTextModelOutput, Tuple]] = None, + text_attention_mask: Optional[torch.Tensor] = None, + ): + if text_model_output is None: + batch_size = len(prompt) if isinstance(prompt, list) else 1 + # get prompt text embeddings + text_inputs = self.tokenizer( + prompt, + padding="max_length", + max_length=self.tokenizer.model_max_length, + return_tensors="pt", + ) + text_input_ids = text_inputs.input_ids + text_mask = text_inputs.attention_mask.bool().to(device) + + if text_input_ids.shape[-1] > self.tokenizer.model_max_length: + removed_text = self.tokenizer.batch_decode(text_input_ids[:, self.tokenizer.model_max_length :]) + logger.warning( + "The following part of your input was truncated because CLIP can only handle sequences up to" + f" {self.tokenizer.model_max_length} tokens: {removed_text}" + ) + text_input_ids = text_input_ids[:, : self.tokenizer.model_max_length] + + text_encoder_output = self.text_encoder(text_input_ids.to(device)) + + text_embeddings = text_encoder_output.text_embeds + text_encoder_hidden_states = text_encoder_output.last_hidden_state + + else: + batch_size = text_model_output[0].shape[0] + text_embeddings, text_encoder_hidden_states = text_model_output[0], text_model_output[1] + text_mask = text_attention_mask + + text_embeddings = text_embeddings.repeat_interleave(num_images_per_prompt, dim=0) + text_encoder_hidden_states = text_encoder_hidden_states.repeat_interleave(num_images_per_prompt, dim=0) + text_mask = text_mask.repeat_interleave(num_images_per_prompt, dim=0) + + if do_classifier_free_guidance: + uncond_tokens = [""] * batch_size + + uncond_input = self.tokenizer( + uncond_tokens, + padding="max_length", + max_length=self.tokenizer.model_max_length, + truncation=True, + return_tensors="pt", + ) + uncond_text_mask = uncond_input.attention_mask.bool().to(device) + uncond_embeddings_text_encoder_output = self.text_encoder(uncond_input.input_ids.to(device)) + + uncond_embeddings = uncond_embeddings_text_encoder_output.text_embeds + uncond_text_encoder_hidden_states = uncond_embeddings_text_encoder_output.last_hidden_state + + # duplicate unconditional embeddings for each generation per prompt, using mps friendly method + + seq_len = uncond_embeddings.shape[1] + uncond_embeddings = uncond_embeddings.repeat(1, num_images_per_prompt) + uncond_embeddings = uncond_embeddings.view(batch_size * num_images_per_prompt, seq_len) + + seq_len = uncond_text_encoder_hidden_states.shape[1] + uncond_text_encoder_hidden_states = uncond_text_encoder_hidden_states.repeat(1, num_images_per_prompt, 1) + uncond_text_encoder_hidden_states = uncond_text_encoder_hidden_states.view( + batch_size * num_images_per_prompt, seq_len, -1 + ) + uncond_text_mask = uncond_text_mask.repeat_interleave(num_images_per_prompt, dim=0) + + # done duplicates + + # For classifier free guidance, we need to do two forward passes. + # Here we concatenate the unconditional and text embeddings into a single batch + # to avoid doing two forward passes + text_embeddings = torch.cat([uncond_embeddings, text_embeddings]) + text_encoder_hidden_states = torch.cat([uncond_text_encoder_hidden_states, text_encoder_hidden_states]) + + text_mask = torch.cat([uncond_text_mask, text_mask]) + + return text_embeddings, text_encoder_hidden_states, text_mask + + def enable_sequential_cpu_offload(self, gpu_id=0): + r""" + Offloads all models to CPU using accelerate, significantly reducing memory usage. When called, the pipeline's + models have their state dicts saved to CPU and then are moved to a `torch.device('meta') and loaded to GPU only + when their specific submodule has its `forward` method called. + """ + if is_accelerate_available(): + from accelerate import cpu_offload + else: + raise ImportError("Please install accelerate via `pip install accelerate`") + + device = torch.device(f"cuda:{gpu_id}") + + # TODO: self.prior.post_process_latents is not covered by the offload hooks, so it fails if added to the list + models = [ + self.decoder, + self.text_proj, + self.text_encoder, + self.super_res_first, + self.super_res_last, + ] + for cpu_offloaded_model in models: + if cpu_offloaded_model is not None: + cpu_offload(cpu_offloaded_model, device) + + @property + def _execution_device(self): + r""" + Returns the device on which the pipeline's models will be executed. After calling + `pipeline.enable_sequential_cpu_offload()` the execution device can only be inferred from Accelerate's module + hooks. + """ + if self.device != torch.device("meta") or not hasattr(self.decoder, "_hf_hook"): + return self.device + for module in self.decoder.modules(): + if ( + hasattr(module, "_hf_hook") + and hasattr(module._hf_hook, "execution_device") + and module._hf_hook.execution_device is not None + ): + return torch.device(module._hf_hook.execution_device) + return self.device + + @torch.no_grad() + def __call__( + self, + prompt: Optional[Union[str, List[str]]] = None, + num_images_per_prompt: int = 1, + prior_num_inference_steps: int = 25, + decoder_num_inference_steps: int = 25, + super_res_num_inference_steps: int = 7, + generator: Optional[torch.Generator] = None, + prior_latents: Optional[torch.FloatTensor] = None, + decoder_latents: Optional[torch.FloatTensor] = None, + super_res_latents: Optional[torch.FloatTensor] = None, + text_model_output: Optional[Union[CLIPTextModelOutput, Tuple]] = None, + text_attention_mask: Optional[torch.Tensor] = None, + prior_guidance_scale: float = 4.0, + decoder_guidance_scale: float = 8.0, + output_type: Optional[str] = "pil", + return_dict: bool = True, + ): + """ + Function invoked when calling the pipeline for generation. + Args: + prompt (`str` or `List[str]`): + The prompt or prompts to guide the image generation. This can only be left undefined if + `text_model_output` and `text_attention_mask` is passed. + num_images_per_prompt (`int`, *optional*, defaults to 1): + The number of images to generate per prompt. + prior_num_inference_steps (`int`, *optional*, defaults to 25): + The number of denoising steps for the prior. More denoising steps usually lead to a higher quality + image at the expense of slower inference. + decoder_num_inference_steps (`int`, *optional*, defaults to 25): + The number of denoising steps for the decoder. More denoising steps usually lead to a higher quality + image at the expense of slower inference. + super_res_num_inference_steps (`int`, *optional*, defaults to 7): + The number of denoising steps for super resolution. More denoising steps usually lead to a higher + quality image at the expense of slower inference. + generator (`torch.Generator`, *optional*): + One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html) + to make generation deterministic. + prior_latents (`torch.FloatTensor` of shape (batch size, embeddings dimension), *optional*): + Pre-generated noisy latents to be used as inputs for the prior. + decoder_latents (`torch.FloatTensor` of shape (batch size, channels, height, width), *optional*): + Pre-generated noisy latents to be used as inputs for the decoder. + super_res_latents (`torch.FloatTensor` of shape (batch size, channels, super res height, super res width), *optional*): + Pre-generated noisy latents to be used as inputs for the decoder. + prior_guidance_scale (`float`, *optional*, defaults to 4.0): + Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598). + `guidance_scale` is defined as `w` of equation 2. of [Imagen + Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale > + 1`. Higher guidance scale encourages to generate images that are closely linked to the text `prompt`, + usually at the expense of lower image quality. + decoder_guidance_scale (`float`, *optional*, defaults to 4.0): + Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598). + `guidance_scale` is defined as `w` of equation 2. of [Imagen + Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale > + 1`. Higher guidance scale encourages to generate images that are closely linked to the text `prompt`, + usually at the expense of lower image quality. + text_model_output (`CLIPTextModelOutput`, *optional*): + Pre-defined CLIPTextModel outputs that can be derived from the text encoder. Pre-defined text outputs + can be passed for tasks like text embedding interpolations. Make sure to also pass + `text_attention_mask` in this case. `prompt` can the be left to `None`. + text_attention_mask (`torch.Tensor`, *optional*): + Pre-defined CLIP text attention mask that can be derived from the tokenizer. Pre-defined text attention + masks are necessary when passing `text_model_output`. + output_type (`str`, *optional*, defaults to `"pil"`): + The output format of the generated image. Choose between + [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`. + return_dict (`bool`, *optional*, defaults to `True`): + Whether or not to return a [`~pipelines.ImagePipelineOutput`] instead of a plain tuple. + """ + if prompt is not None: + if isinstance(prompt, str): + batch_size = 1 + elif isinstance(prompt, list): + batch_size = len(prompt) + else: + raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}") + else: + batch_size = text_model_output[0].shape[0] + + device = self._execution_device + + batch_size = batch_size * num_images_per_prompt + + do_classifier_free_guidance = prior_guidance_scale > 1.0 or decoder_guidance_scale > 1.0 + + text_embeddings, text_encoder_hidden_states, text_mask = self._encode_prompt( + prompt, device, num_images_per_prompt, do_classifier_free_guidance, text_model_output, text_attention_mask + ) + + # prior + + self.prior_scheduler.set_timesteps(prior_num_inference_steps, device=device) + prior_timesteps_tensor = self.prior_scheduler.timesteps + + embedding_dim = self.prior.config.embedding_dim + + prior_latents = self.prepare_latents( + (batch_size, embedding_dim), + text_embeddings.dtype, + device, + generator, + prior_latents, + self.prior_scheduler, + ) + + for i, t in enumerate(self.progress_bar(prior_timesteps_tensor)): + # expand the latents if we are doing classifier free guidance + latent_model_input = torch.cat([prior_latents] * 2) if do_classifier_free_guidance else prior_latents + + predicted_image_embedding = self.prior( + latent_model_input, + timestep=t, + proj_embedding=text_embeddings, + encoder_hidden_states=text_encoder_hidden_states, + attention_mask=text_mask, + ).predicted_image_embedding + + if do_classifier_free_guidance: + predicted_image_embedding_uncond, predicted_image_embedding_text = predicted_image_embedding.chunk(2) + predicted_image_embedding = predicted_image_embedding_uncond + prior_guidance_scale * ( + predicted_image_embedding_text - predicted_image_embedding_uncond + ) + + if i + 1 == prior_timesteps_tensor.shape[0]: + prev_timestep = None + else: + prev_timestep = prior_timesteps_tensor[i + 1] + + prior_latents = self.prior_scheduler.step( + predicted_image_embedding, + timestep=t, + sample=prior_latents, + generator=generator, + prev_timestep=prev_timestep, + ).prev_sample + + prior_latents = self.prior.post_process_latents(prior_latents) + + image_embeddings = prior_latents + + # done prior + + # decoder + + text_encoder_hidden_states, additive_clip_time_embeddings = self.text_proj( + image_embeddings=image_embeddings, + text_embeddings=text_embeddings, + text_encoder_hidden_states=text_encoder_hidden_states, + do_classifier_free_guidance=do_classifier_free_guidance, + ) + + decoder_text_mask = F.pad(text_mask, (self.text_proj.clip_extra_context_tokens, 0), value=1) + + self.decoder_scheduler.set_timesteps(decoder_num_inference_steps, device=device) + decoder_timesteps_tensor = self.decoder_scheduler.timesteps + + num_channels_latents = self.decoder.in_channels + height = self.decoder.sample_size + width = self.decoder.sample_size + + decoder_latents = self.prepare_latents( + (batch_size, num_channels_latents, height, width), + text_encoder_hidden_states.dtype, + device, + generator, + decoder_latents, + self.decoder_scheduler, + ) + + for i, t in enumerate(self.progress_bar(decoder_timesteps_tensor)): + # expand the latents if we are doing classifier free guidance + latent_model_input = torch.cat([decoder_latents] * 2) if do_classifier_free_guidance else decoder_latents + + noise_pred = self.decoder( + sample=latent_model_input, + timestep=t, + encoder_hidden_states=text_encoder_hidden_states, + class_labels=additive_clip_time_embeddings, + attention_mask=decoder_text_mask, + ).sample + + if do_classifier_free_guidance: + noise_pred_uncond, noise_pred_text = noise_pred.chunk(2) + noise_pred_uncond, _ = noise_pred_uncond.split(latent_model_input.shape[1], dim=1) + noise_pred_text, predicted_variance = noise_pred_text.split(latent_model_input.shape[1], dim=1) + noise_pred = noise_pred_uncond + decoder_guidance_scale * (noise_pred_text - noise_pred_uncond) + noise_pred = torch.cat([noise_pred, predicted_variance], dim=1) + + if i + 1 == decoder_timesteps_tensor.shape[0]: + prev_timestep = None + else: + prev_timestep = decoder_timesteps_tensor[i + 1] + + # compute the previous noisy sample x_t -> x_t-1 + decoder_latents = self.decoder_scheduler.step( + noise_pred, t, decoder_latents, prev_timestep=prev_timestep, generator=generator + ).prev_sample + + decoder_latents = decoder_latents.clamp(-1, 1) + + image_small = decoder_latents + + # done decoder + + # super res + + self.super_res_scheduler.set_timesteps(super_res_num_inference_steps, device=device) + super_res_timesteps_tensor = self.super_res_scheduler.timesteps + + channels = self.super_res_first.in_channels // 2 + height = self.super_res_first.sample_size + width = self.super_res_first.sample_size + + super_res_latents = self.prepare_latents( + (batch_size, channels, height, width), + image_small.dtype, + device, + generator, + super_res_latents, + self.super_res_scheduler, + ) + + interpolate_antialias = {} + if "antialias" in inspect.signature(F.interpolate).parameters: + interpolate_antialias["antialias"] = True + + image_upscaled = F.interpolate( + image_small, size=[height, width], mode="bicubic", align_corners=False, **interpolate_antialias + ) + + for i, t in enumerate(self.progress_bar(super_res_timesteps_tensor)): + # no classifier free guidance + + if i == super_res_timesteps_tensor.shape[0] - 1: + unet = self.super_res_last + else: + unet = self.super_res_first + + latent_model_input = torch.cat([super_res_latents, image_upscaled], dim=1) + + noise_pred = unet( + sample=latent_model_input, + timestep=t, + ).sample + + if i + 1 == super_res_timesteps_tensor.shape[0]: + prev_timestep = None + else: + prev_timestep = super_res_timesteps_tensor[i + 1] + + # compute the previous noisy sample x_t -> x_t-1 + super_res_latents = self.super_res_scheduler.step( + noise_pred, t, super_res_latents, prev_timestep=prev_timestep, generator=generator + ).prev_sample + + image = super_res_latents + # done super res + + # post processing + + image = image * 0.5 + 0.5 + image = image.clamp(0, 1) + image = image.cpu().permute(0, 2, 3, 1).float().numpy() + + if output_type == "pil": + image = self.numpy_to_pil(image) + + if not return_dict: + return (image,) + + return ImagePipelineOutput(images=image) \ No newline at end of file diff --git a/repositories/ldm/modules/karlo/kakao/__init__.py b/repositories/ldm/modules/karlo/kakao/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/ldm/modules/karlo/kakao/models/__init__.py b/repositories/ldm/modules/karlo/kakao/models/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/ldm/modules/karlo/kakao/models/clip.py b/repositories/ldm/modules/karlo/kakao/models/clip.py new file mode 100644 index 000000000..961d81502 --- /dev/null +++ b/repositories/ldm/modules/karlo/kakao/models/clip.py @@ -0,0 +1,182 @@ +# ------------------------------------------------------------------------------------ +# Karlo-v1.0.alpha +# Copyright (c) 2022 KakaoBrain. All Rights Reserved. +# ------------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------------ +# Adapted from OpenAI's CLIP (https://github.com/openai/CLIP/) +# ------------------------------------------------------------------------------------ + + +import torch +import torch.nn as nn +import torch.nn.functional as F +import clip + +from clip.model import CLIP, convert_weights +from clip.simple_tokenizer import SimpleTokenizer, default_bpe + + +"""===== Monkey-Patching original CLIP for JIT compile =====""" + + +class LayerNorm(nn.LayerNorm): + """Subclass torch's LayerNorm to handle fp16.""" + + def forward(self, x: torch.Tensor): + orig_type = x.dtype + ret = F.layer_norm( + x.type(torch.float32), + self.normalized_shape, + self.weight, + self.bias, + self.eps, + ) + return ret.type(orig_type) + + +clip.model.LayerNorm = LayerNorm +delattr(clip.model.CLIP, "forward") + +"""===== End of Monkey-Patching =====""" + + +class CustomizedCLIP(CLIP): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + @torch.jit.export + def encode_image(self, image): + return self.visual(image) + + @torch.jit.export + def encode_text(self, text): + # re-define this function to return unpooled text features + + x = self.token_embedding(text).type(self.dtype) # [batch_size, n_ctx, d_model] + + x = x + self.positional_embedding.type(self.dtype) + x = x.permute(1, 0, 2) # NLD -> LND + x = self.transformer(x) + x = x.permute(1, 0, 2) # LND -> NLD + x = self.ln_final(x).type(self.dtype) + + x_seq = x + # x.shape = [batch_size, n_ctx, transformer.width] + # take features from the eot embedding (eot_token is the highest number in each sequence) + x_out = x[torch.arange(x.shape[0]), text.argmax(dim=-1)] @ self.text_projection + + return x_out, x_seq + + @torch.jit.ignore + def forward(self, image, text): + super().forward(image, text) + + @classmethod + def load_from_checkpoint(cls, ckpt_path: str): + state_dict = torch.load(ckpt_path, map_location="cpu").state_dict() + + vit = "visual.proj" in state_dict + if vit: + vision_width = state_dict["visual.conv1.weight"].shape[0] + vision_layers = len( + [ + k + for k in state_dict.keys() + if k.startswith("visual.") and k.endswith(".attn.in_proj_weight") + ] + ) + vision_patch_size = state_dict["visual.conv1.weight"].shape[-1] + grid_size = round( + (state_dict["visual.positional_embedding"].shape[0] - 1) ** 0.5 + ) + image_resolution = vision_patch_size * grid_size + else: + counts: list = [ + len( + set( + k.split(".")[2] + for k in state_dict + if k.startswith(f"visual.layer{b}") + ) + ) + for b in [1, 2, 3, 4] + ] + vision_layers = tuple(counts) + vision_width = state_dict["visual.layer1.0.conv1.weight"].shape[0] + output_width = round( + (state_dict["visual.attnpool.positional_embedding"].shape[0] - 1) ** 0.5 + ) + vision_patch_size = None + assert ( + output_width**2 + 1 + == state_dict["visual.attnpool.positional_embedding"].shape[0] + ) + image_resolution = output_width * 32 + + embed_dim = state_dict["text_projection"].shape[1] + context_length = state_dict["positional_embedding"].shape[0] + vocab_size = state_dict["token_embedding.weight"].shape[0] + transformer_width = state_dict["ln_final.weight"].shape[0] + transformer_heads = transformer_width // 64 + transformer_layers = len( + set( + k.split(".")[2] + for k in state_dict + if k.startswith("transformer.resblocks") + ) + ) + + model = cls( + embed_dim, + image_resolution, + vision_layers, + vision_width, + vision_patch_size, + context_length, + vocab_size, + transformer_width, + transformer_heads, + transformer_layers, + ) + + for key in ["input_resolution", "context_length", "vocab_size"]: + if key in state_dict: + del state_dict[key] + + convert_weights(model) + model.load_state_dict(state_dict) + model.eval() + model.float() + return model + + +class CustomizedTokenizer(SimpleTokenizer): + def __init__(self): + super().__init__(bpe_path=default_bpe()) + + self.sot_token = self.encoder["<|startoftext|>"] + self.eot_token = self.encoder["<|endoftext|>"] + + def padded_tokens_and_mask(self, texts, text_ctx): + assert isinstance(texts, list) and all( + isinstance(elem, str) for elem in texts + ), "texts should be a list of strings" + + all_tokens = [ + [self.sot_token] + self.encode(text) + [self.eot_token] for text in texts + ] + + mask = [ + [True] * min(text_ctx, len(tokens)) + + [False] * max(text_ctx - len(tokens), 0) + for tokens in all_tokens + ] + mask = torch.tensor(mask, dtype=torch.bool) + result = torch.zeros(len(all_tokens), text_ctx, dtype=torch.int) + for i, tokens in enumerate(all_tokens): + if len(tokens) > text_ctx: + tokens = tokens[:text_ctx] + tokens[-1] = self.eot_token + result[i, : len(tokens)] = torch.tensor(tokens) + + return result, mask diff --git a/repositories/ldm/modules/karlo/kakao/models/decoder_model.py b/repositories/ldm/modules/karlo/kakao/models/decoder_model.py new file mode 100644 index 000000000..84e96c9b2 --- /dev/null +++ b/repositories/ldm/modules/karlo/kakao/models/decoder_model.py @@ -0,0 +1,193 @@ +# ------------------------------------------------------------------------------------ +# Karlo-v1.0.alpha +# Copyright (c) 2022 KakaoBrain. All Rights Reserved. +# ------------------------------------------------------------------------------------ + +import copy +import torch + +from ldm.modules.karlo.kakao.modules import create_gaussian_diffusion +from ldm.modules.karlo.kakao.modules.unet import PLMImUNet + + +class Text2ImProgressiveModel(torch.nn.Module): + """ + A decoder that generates 64x64px images based on the text prompt. + + :param config: yaml config to define the decoder. + :param tokenizer: tokenizer used in clip. + """ + + def __init__( + self, + config, + tokenizer, + ): + super().__init__() + + self._conf = config + self._model_conf = config.model.hparams + self._diffusion_kwargs = dict( + steps=config.diffusion.steps, + learn_sigma=config.diffusion.learn_sigma, + sigma_small=config.diffusion.sigma_small, + noise_schedule=config.diffusion.noise_schedule, + use_kl=config.diffusion.use_kl, + predict_xstart=config.diffusion.predict_xstart, + rescale_learned_sigmas=config.diffusion.rescale_learned_sigmas, + timestep_respacing=config.diffusion.timestep_respacing, + ) + self._tokenizer = tokenizer + + self.model = self.create_plm_dec_model() + + cf_token, cf_mask = self.set_cf_text_tensor() + self.register_buffer("cf_token", cf_token, persistent=False) + self.register_buffer("cf_mask", cf_mask, persistent=False) + + @classmethod + def load_from_checkpoint(cls, config, tokenizer, ckpt_path, strict: bool = True): + ckpt = torch.load(ckpt_path, map_location="cpu")["state_dict"] + + model = cls(config, tokenizer) + model.load_state_dict(ckpt, strict=strict) + return model + + def create_plm_dec_model(self): + image_size = self._model_conf.image_size + if self._model_conf.channel_mult == "": + if image_size == 256: + channel_mult = (1, 1, 2, 2, 4, 4) + elif image_size == 128: + channel_mult = (1, 1, 2, 3, 4) + elif image_size == 64: + channel_mult = (1, 2, 3, 4) + else: + raise ValueError(f"unsupported image size: {image_size}") + else: + channel_mult = tuple( + int(ch_mult) for ch_mult in self._model_conf.channel_mult.split(",") + ) + assert 2 ** (len(channel_mult) + 2) == image_size + + attention_ds = [] + for res in self._model_conf.attention_resolutions.split(","): + attention_ds.append(image_size // int(res)) + + return PLMImUNet( + text_ctx=self._model_conf.text_ctx, + xf_width=self._model_conf.xf_width, + in_channels=3, + model_channels=self._model_conf.num_channels, + out_channels=6 if self._model_conf.learn_sigma else 3, + num_res_blocks=self._model_conf.num_res_blocks, + attention_resolutions=tuple(attention_ds), + dropout=self._model_conf.dropout, + channel_mult=channel_mult, + num_heads=self._model_conf.num_heads, + num_head_channels=self._model_conf.num_head_channels, + num_heads_upsample=self._model_conf.num_heads_upsample, + use_scale_shift_norm=self._model_conf.use_scale_shift_norm, + resblock_updown=self._model_conf.resblock_updown, + clip_dim=self._model_conf.clip_dim, + clip_emb_mult=self._model_conf.clip_emb_mult, + clip_emb_type=self._model_conf.clip_emb_type, + clip_emb_drop=self._model_conf.clip_emb_drop, + ) + + def set_cf_text_tensor(self): + return self._tokenizer.padded_tokens_and_mask([""], self.model.text_ctx) + + def get_sample_fn(self, timestep_respacing): + use_ddim = timestep_respacing.startswith(("ddim", "fast")) + + diffusion_kwargs = copy.deepcopy(self._diffusion_kwargs) + diffusion_kwargs.update(timestep_respacing=timestep_respacing) + diffusion = create_gaussian_diffusion(**diffusion_kwargs) + sample_fn = ( + diffusion.ddim_sample_loop_progressive + if use_ddim + else diffusion.p_sample_loop_progressive + ) + + return sample_fn + + def forward( + self, + txt_feat, + txt_feat_seq, + tok, + mask, + img_feat=None, + cf_guidance_scales=None, + timestep_respacing=None, + ): + # cfg should be enabled in inference + assert cf_guidance_scales is not None and all(cf_guidance_scales > 0.0) + assert img_feat is not None + + bsz = txt_feat.shape[0] + img_sz = self._model_conf.image_size + + def guided_model_fn(x_t, ts, **kwargs): + half = x_t[: len(x_t) // 2] + combined = torch.cat([half, half], dim=0) + model_out = self.model(combined, ts, **kwargs) + eps, rest = model_out[:, :3], model_out[:, 3:] + cond_eps, uncond_eps = torch.split(eps, len(eps) // 2, dim=0) + half_eps = uncond_eps + cf_guidance_scales.view(-1, 1, 1, 1) * ( + cond_eps - uncond_eps + ) + eps = torch.cat([half_eps, half_eps], dim=0) + return torch.cat([eps, rest], dim=1) + + cf_feat = self.model.cf_param.unsqueeze(0) + cf_feat = cf_feat.expand(bsz // 2, -1) + feat = torch.cat([img_feat, cf_feat.to(txt_feat.device)], dim=0) + + cond = { + "y": feat, + "txt_feat": txt_feat, + "txt_feat_seq": txt_feat_seq, + "mask": mask, + } + sample_fn = self.get_sample_fn(timestep_respacing) + sample_outputs = sample_fn( + guided_model_fn, + (bsz, 3, img_sz, img_sz), + noise=None, + device=txt_feat.device, + clip_denoised=True, + model_kwargs=cond, + ) + + for out in sample_outputs: + sample = out["sample"] + yield sample if cf_guidance_scales is None else sample[ + : sample.shape[0] // 2 + ] + + +class Text2ImModel(Text2ImProgressiveModel): + def forward( + self, + txt_feat, + txt_feat_seq, + tok, + mask, + img_feat=None, + cf_guidance_scales=None, + timestep_respacing=None, + ): + last_out = None + for out in super().forward( + txt_feat, + txt_feat_seq, + tok, + mask, + img_feat, + cf_guidance_scales, + timestep_respacing, + ): + last_out = out + return last_out diff --git a/repositories/ldm/modules/karlo/kakao/models/prior_model.py b/repositories/ldm/modules/karlo/kakao/models/prior_model.py new file mode 100644 index 000000000..03ef230d2 --- /dev/null +++ b/repositories/ldm/modules/karlo/kakao/models/prior_model.py @@ -0,0 +1,138 @@ +# ------------------------------------------------------------------------------------ +# Karlo-v1.0.alpha +# Copyright (c) 2022 KakaoBrain. All Rights Reserved. +# ------------------------------------------------------------------------------------ + +import copy +import torch + +from ldm.modules.karlo.kakao.modules import create_gaussian_diffusion +from ldm.modules.karlo.kakao.modules.xf import PriorTransformer + + +class PriorDiffusionModel(torch.nn.Module): + """ + A prior that generates clip image feature based on the text prompt. + + :param config: yaml config to define the decoder. + :param tokenizer: tokenizer used in clip. + :param clip_mean: mean to normalize the clip image feature (zero-mean, unit variance). + :param clip_std: std to noramlize the clip image feature (zero-mean, unit variance). + """ + + def __init__(self, config, tokenizer, clip_mean, clip_std): + super().__init__() + + self._conf = config + self._model_conf = config.model.hparams + self._diffusion_kwargs = dict( + steps=config.diffusion.steps, + learn_sigma=config.diffusion.learn_sigma, + sigma_small=config.diffusion.sigma_small, + noise_schedule=config.diffusion.noise_schedule, + use_kl=config.diffusion.use_kl, + predict_xstart=config.diffusion.predict_xstart, + rescale_learned_sigmas=config.diffusion.rescale_learned_sigmas, + timestep_respacing=config.diffusion.timestep_respacing, + ) + self._tokenizer = tokenizer + + self.register_buffer("clip_mean", clip_mean[None, :], persistent=False) + self.register_buffer("clip_std", clip_std[None, :], persistent=False) + + causal_mask = self.get_causal_mask() + self.register_buffer("causal_mask", causal_mask, persistent=False) + + self.model = PriorTransformer( + text_ctx=self._model_conf.text_ctx, + xf_width=self._model_conf.xf_width, + xf_layers=self._model_conf.xf_layers, + xf_heads=self._model_conf.xf_heads, + xf_final_ln=self._model_conf.xf_final_ln, + clip_dim=self._model_conf.clip_dim, + ) + + cf_token, cf_mask = self.set_cf_text_tensor() + self.register_buffer("cf_token", cf_token, persistent=False) + self.register_buffer("cf_mask", cf_mask, persistent=False) + + @classmethod + def load_from_checkpoint( + cls, config, tokenizer, clip_mean, clip_std, ckpt_path, strict: bool = True + ): + ckpt = torch.load(ckpt_path, map_location="cpu")["state_dict"] + + model = cls(config, tokenizer, clip_mean, clip_std) + model.load_state_dict(ckpt, strict=strict) + return model + + def set_cf_text_tensor(self): + return self._tokenizer.padded_tokens_and_mask([""], self.model.text_ctx) + + def get_sample_fn(self, timestep_respacing): + use_ddim = timestep_respacing.startswith(("ddim", "fast")) + + diffusion_kwargs = copy.deepcopy(self._diffusion_kwargs) + diffusion_kwargs.update(timestep_respacing=timestep_respacing) + diffusion = create_gaussian_diffusion(**diffusion_kwargs) + sample_fn = diffusion.ddim_sample_loop if use_ddim else diffusion.p_sample_loop + + return sample_fn + + def get_causal_mask(self): + seq_len = self._model_conf.text_ctx + 4 + mask = torch.empty(seq_len, seq_len) + mask.fill_(float("-inf")) + mask.triu_(1) + mask = mask[None, ...] + return mask + + def forward( + self, + txt_feat, + txt_feat_seq, + mask, + cf_guidance_scales=None, + timestep_respacing=None, + denoised_fn=True, + ): + # cfg should be enabled in inference + assert cf_guidance_scales is not None and all(cf_guidance_scales > 0.0) + + bsz_ = txt_feat.shape[0] + bsz = bsz_ // 2 + + def guided_model_fn(x_t, ts, **kwargs): + half = x_t[: len(x_t) // 2] + combined = torch.cat([half, half], dim=0) + model_out = self.model(combined, ts, **kwargs) + eps, rest = ( + model_out[:, : int(x_t.shape[1])], + model_out[:, int(x_t.shape[1]) :], + ) + cond_eps, uncond_eps = torch.split(eps, len(eps) // 2, dim=0) + half_eps = uncond_eps + cf_guidance_scales.view(-1, 1) * ( + cond_eps - uncond_eps + ) + eps = torch.cat([half_eps, half_eps], dim=0) + return torch.cat([eps, rest], dim=1) + + cond = { + "text_emb": txt_feat, + "text_enc": txt_feat_seq, + "mask": mask, + "causal_mask": self.causal_mask, + } + sample_fn = self.get_sample_fn(timestep_respacing) + sample = sample_fn( + guided_model_fn, + (bsz_, self.model.clip_dim), + noise=None, + device=txt_feat.device, + clip_denoised=False, + denoised_fn=lambda x: torch.clamp(x, -10, 10), + model_kwargs=cond, + ) + sample = (sample * self.clip_std) + self.clip_mean + + return sample[:bsz] diff --git a/repositories/ldm/modules/karlo/kakao/models/sr_256_1k.py b/repositories/ldm/modules/karlo/kakao/models/sr_256_1k.py new file mode 100644 index 000000000..1e874f6f1 --- /dev/null +++ b/repositories/ldm/modules/karlo/kakao/models/sr_256_1k.py @@ -0,0 +1,10 @@ +# ------------------------------------------------------------------------------------ +# Karlo-v1.0.alpha +# Copyright (c) 2022 KakaoBrain. All Rights Reserved. +# ------------------------------------------------------------------------------------ + +from ldm.modules.karlo.kakao.models.sr_64_256 import SupRes64to256Progressive + + +class SupRes256to1kProgressive(SupRes64to256Progressive): + pass # no difference currently diff --git a/repositories/ldm/modules/karlo/kakao/models/sr_64_256.py b/repositories/ldm/modules/karlo/kakao/models/sr_64_256.py new file mode 100644 index 000000000..32687afe3 --- /dev/null +++ b/repositories/ldm/modules/karlo/kakao/models/sr_64_256.py @@ -0,0 +1,88 @@ +# ------------------------------------------------------------------------------------ +# Karlo-v1.0.alpha +# Copyright (c) 2022 KakaoBrain. All Rights Reserved. +# ------------------------------------------------------------------------------------ + +import copy +import torch + +from ldm.modules.karlo.kakao.modules.unet import SuperResUNetModel +from ldm.modules.karlo.kakao.modules import create_gaussian_diffusion + + +class ImprovedSupRes64to256ProgressiveModel(torch.nn.Module): + """ + ImprovedSR model fine-tunes the pretrained DDPM-based SR model by using adversarial and perceptual losses. + In specific, the low-resolution sample is iteratively recovered by 6 steps with the frozen pretrained SR model. + In the following additional one step, a seperate fine-tuned model recovers high-frequency details. + This approach greatly improves the fidelity of images of 256x256px, even with small number of reverse steps. + """ + + def __init__(self, config): + super().__init__() + + self._config = config + self._diffusion_kwargs = dict( + steps=config.diffusion.steps, + learn_sigma=config.diffusion.learn_sigma, + sigma_small=config.diffusion.sigma_small, + noise_schedule=config.diffusion.noise_schedule, + use_kl=config.diffusion.use_kl, + predict_xstart=config.diffusion.predict_xstart, + rescale_learned_sigmas=config.diffusion.rescale_learned_sigmas, + ) + + self.model_first_steps = SuperResUNetModel( + in_channels=3, # auto-changed to 6 inside the model + model_channels=config.model.hparams.channels, + out_channels=3, + num_res_blocks=config.model.hparams.depth, + attention_resolutions=(), # no attention + dropout=config.model.hparams.dropout, + channel_mult=config.model.hparams.channels_multiple, + resblock_updown=True, + use_middle_attention=False, + ) + self.model_last_step = SuperResUNetModel( + in_channels=3, # auto-changed to 6 inside the model + model_channels=config.model.hparams.channels, + out_channels=3, + num_res_blocks=config.model.hparams.depth, + attention_resolutions=(), # no attention + dropout=config.model.hparams.dropout, + channel_mult=config.model.hparams.channels_multiple, + resblock_updown=True, + use_middle_attention=False, + ) + + @classmethod + def load_from_checkpoint(cls, config, ckpt_path, strict: bool = True): + ckpt = torch.load(ckpt_path, map_location="cpu")["state_dict"] + + model = cls(config) + model.load_state_dict(ckpt, strict=strict) + return model + + def get_sample_fn(self, timestep_respacing): + diffusion_kwargs = copy.deepcopy(self._diffusion_kwargs) + diffusion_kwargs.update(timestep_respacing=timestep_respacing) + diffusion = create_gaussian_diffusion(**diffusion_kwargs) + return diffusion.p_sample_loop_progressive_for_improved_sr + + def forward(self, low_res, timestep_respacing="7", **kwargs): + assert ( + timestep_respacing == "7" + ), "different respacing method may work, but no guaranteed" + + sample_fn = self.get_sample_fn(timestep_respacing) + sample_outputs = sample_fn( + self.model_first_steps, + self.model_last_step, + shape=low_res.shape, + clip_denoised=True, + model_kwargs=dict(low_res=low_res), + **kwargs, + ) + for x in sample_outputs: + sample = x["sample"] + yield sample diff --git a/repositories/ldm/modules/karlo/kakao/modules/__init__.py b/repositories/ldm/modules/karlo/kakao/modules/__init__.py new file mode 100644 index 000000000..11d4358a6 --- /dev/null +++ b/repositories/ldm/modules/karlo/kakao/modules/__init__.py @@ -0,0 +1,49 @@ +# ------------------------------------------------------------------------------------ +# Adapted from Guided-Diffusion repo (https://github.com/openai/guided-diffusion) +# ------------------------------------------------------------------------------------ + + +from .diffusion import gaussian_diffusion as gd +from .diffusion.respace import ( + SpacedDiffusion, + space_timesteps, +) + + +def create_gaussian_diffusion( + steps, + learn_sigma, + sigma_small, + noise_schedule, + use_kl, + predict_xstart, + rescale_learned_sigmas, + timestep_respacing, +): + betas = gd.get_named_beta_schedule(noise_schedule, steps) + if use_kl: + loss_type = gd.LossType.RESCALED_KL + elif rescale_learned_sigmas: + loss_type = gd.LossType.RESCALED_MSE + else: + loss_type = gd.LossType.MSE + if not timestep_respacing: + timestep_respacing = [steps] + + return SpacedDiffusion( + use_timesteps=space_timesteps(steps, timestep_respacing), + betas=betas, + model_mean_type=( + gd.ModelMeanType.EPSILON if not predict_xstart else gd.ModelMeanType.START_X + ), + model_var_type=( + ( + gd.ModelVarType.FIXED_LARGE + if not sigma_small + else gd.ModelVarType.FIXED_SMALL + ) + if not learn_sigma + else gd.ModelVarType.LEARNED_RANGE + ), + loss_type=loss_type, + ) diff --git a/repositories/ldm/modules/karlo/kakao/modules/diffusion/gaussian_diffusion.py b/repositories/ldm/modules/karlo/kakao/modules/diffusion/gaussian_diffusion.py new file mode 100644 index 000000000..6a111aa09 --- /dev/null +++ b/repositories/ldm/modules/karlo/kakao/modules/diffusion/gaussian_diffusion.py @@ -0,0 +1,828 @@ +# ------------------------------------------------------------------------------------ +# Adapted from Guided-Diffusion repo (https://github.com/openai/guided-diffusion) +# ------------------------------------------------------------------------------------ + +import enum +import math + +import numpy as np +import torch as th + + +def _warmup_beta(beta_start, beta_end, num_diffusion_timesteps, warmup_frac): + betas = beta_end * np.ones(num_diffusion_timesteps, dtype=np.float64) + warmup_time = int(num_diffusion_timesteps * warmup_frac) + betas[:warmup_time] = np.linspace( + beta_start, beta_end, warmup_time, dtype=np.float64 + ) + return betas + + +def get_beta_schedule(beta_schedule, *, beta_start, beta_end, num_diffusion_timesteps): + """ + This is the deprecated API for creating beta schedules. + See get_named_beta_schedule() for the new library of schedules. + """ + if beta_schedule == "quad": + betas = ( + np.linspace( + beta_start**0.5, + beta_end**0.5, + num_diffusion_timesteps, + dtype=np.float64, + ) + ** 2 + ) + elif beta_schedule == "linear": + betas = np.linspace( + beta_start, beta_end, num_diffusion_timesteps, dtype=np.float64 + ) + elif beta_schedule == "warmup10": + betas = _warmup_beta(beta_start, beta_end, num_diffusion_timesteps, 0.1) + elif beta_schedule == "warmup50": + betas = _warmup_beta(beta_start, beta_end, num_diffusion_timesteps, 0.5) + elif beta_schedule == "const": + betas = beta_end * np.ones(num_diffusion_timesteps, dtype=np.float64) + elif beta_schedule == "jsd": # 1/T, 1/(T-1), 1/(T-2), ..., 1 + betas = 1.0 / np.linspace( + num_diffusion_timesteps, 1, num_diffusion_timesteps, dtype=np.float64 + ) + else: + raise NotImplementedError(beta_schedule) + assert betas.shape == (num_diffusion_timesteps,) + return betas + + +def get_named_beta_schedule(schedule_name, num_diffusion_timesteps): + """ + Get a pre-defined beta schedule for the given name. + The beta schedule library consists of beta schedules which remain similar + in the limit of num_diffusion_timesteps. + Beta schedules may be added, but should not be removed or changed once + they are committed to maintain backwards compatibility. + """ + if schedule_name == "linear": + # Linear schedule from Ho et al, extended to work for any number of + # diffusion steps. + scale = 1000 / num_diffusion_timesteps + return get_beta_schedule( + "linear", + beta_start=scale * 0.0001, + beta_end=scale * 0.02, + num_diffusion_timesteps=num_diffusion_timesteps, + ) + elif schedule_name == "squaredcos_cap_v2": + return betas_for_alpha_bar( + num_diffusion_timesteps, + lambda t: math.cos((t + 0.008) / 1.008 * math.pi / 2) ** 2, + ) + else: + raise NotImplementedError(f"unknown beta schedule: {schedule_name}") + + +def betas_for_alpha_bar(num_diffusion_timesteps, alpha_bar, max_beta=0.999): + """ + Create a beta schedule that discretizes the given alpha_t_bar function, + which defines the cumulative product of (1-beta) over time from t = [0,1]. + :param num_diffusion_timesteps: the number of betas to produce. + :param alpha_bar: a lambda that takes an argument t from 0 to 1 and + produces the cumulative product of (1-beta) up to that + part of the diffusion process. + :param max_beta: the maximum beta to use; use values lower than 1 to + prevent singularities. + """ + betas = [] + for i in range(num_diffusion_timesteps): + t1 = i / num_diffusion_timesteps + t2 = (i + 1) / num_diffusion_timesteps + betas.append(min(1 - alpha_bar(t2) / alpha_bar(t1), max_beta)) + return np.array(betas) + + +class ModelMeanType(enum.Enum): + """ + Which type of output the model predicts. + """ + + PREVIOUS_X = enum.auto() # the model predicts x_{t-1} + START_X = enum.auto() # the model predicts x_0 + EPSILON = enum.auto() # the model predicts epsilon + + +class ModelVarType(enum.Enum): + """ + What is used as the model's output variance. + The LEARNED_RANGE option has been added to allow the model to predict + values between FIXED_SMALL and FIXED_LARGE, making its job easier. + """ + + LEARNED = enum.auto() + FIXED_SMALL = enum.auto() + FIXED_LARGE = enum.auto() + LEARNED_RANGE = enum.auto() + + +class LossType(enum.Enum): + MSE = enum.auto() # use raw MSE loss (and KL when learning variances) + RESCALED_MSE = ( + enum.auto() + ) # use raw MSE loss (with RESCALED_KL when learning variances) + KL = enum.auto() # use the variational lower-bound + RESCALED_KL = enum.auto() # like KL, but rescale to estimate the full VLB + + def is_vb(self): + return self == LossType.KL or self == LossType.RESCALED_KL + + +class GaussianDiffusion(th.nn.Module): + """ + Utilities for training and sampling diffusion models. + Original ported from this codebase: + https://github.com/hojonathanho/diffusion/blob/1e0dceb3b3495bbe19116a5e1b3596cd0706c543/diffusion_tf/diffusion_utils_2.py#L42 + :param betas: a 1-D numpy array of betas for each diffusion timestep, + starting at T and going to 1. + """ + + def __init__( + self, + *, + betas, + model_mean_type, + model_var_type, + loss_type, + ): + super(GaussianDiffusion, self).__init__() + self.model_mean_type = model_mean_type + self.model_var_type = model_var_type + self.loss_type = loss_type + + # Use float64 for accuracy. + betas = np.array(betas, dtype=np.float64) + assert len(betas.shape) == 1, "betas must be 1-D" + assert (betas > 0).all() and (betas <= 1).all() + + self.num_timesteps = int(betas.shape[0]) + + alphas = 1.0 - betas + alphas_cumprod = np.cumprod(alphas, axis=0) + alphas_cumprod_prev = np.append(1.0, alphas_cumprod[:-1]) + alphas_cumprod_next = np.append(alphas_cumprod[1:], 0.0) + assert alphas_cumprod_prev.shape == (self.num_timesteps,) + + # calculations for diffusion q(x_t | x_{t-1}) and others + sqrt_alphas_cumprod = np.sqrt(alphas_cumprod) + sqrt_one_minus_alphas_cumprod = np.sqrt(1.0 - alphas_cumprod) + log_one_minus_alphas_cumprod = np.log(1.0 - alphas_cumprod) + sqrt_recip_alphas_cumprod = np.sqrt(1.0 / alphas_cumprod) + sqrt_recipm1_alphas_cumprod = np.sqrt(1.0 / alphas_cumprod - 1) + + # calculations for posterior q(x_{t-1} | x_t, x_0) + posterior_variance = ( + betas * (1.0 - alphas_cumprod_prev) / (1.0 - alphas_cumprod) + ) + # below: log calculation clipped because the posterior variance is 0 at the beginning of the diffusion chain + posterior_log_variance_clipped = np.log( + np.append(posterior_variance[1], posterior_variance[1:]) + ) + posterior_mean_coef1 = ( + betas * np.sqrt(alphas_cumprod_prev) / (1.0 - alphas_cumprod) + ) + posterior_mean_coef2 = ( + (1.0 - alphas_cumprod_prev) * np.sqrt(alphas) / (1.0 - alphas_cumprod) + ) + + self.register_buffer("betas", th.from_numpy(betas), persistent=False) + self.register_buffer( + "alphas_cumprod", th.from_numpy(alphas_cumprod), persistent=False + ) + self.register_buffer( + "alphas_cumprod_prev", th.from_numpy(alphas_cumprod_prev), persistent=False + ) + self.register_buffer( + "alphas_cumprod_next", th.from_numpy(alphas_cumprod_next), persistent=False + ) + + self.register_buffer( + "sqrt_alphas_cumprod", th.from_numpy(sqrt_alphas_cumprod), persistent=False + ) + self.register_buffer( + "sqrt_one_minus_alphas_cumprod", + th.from_numpy(sqrt_one_minus_alphas_cumprod), + persistent=False, + ) + self.register_buffer( + "log_one_minus_alphas_cumprod", + th.from_numpy(log_one_minus_alphas_cumprod), + persistent=False, + ) + self.register_buffer( + "sqrt_recip_alphas_cumprod", + th.from_numpy(sqrt_recip_alphas_cumprod), + persistent=False, + ) + self.register_buffer( + "sqrt_recipm1_alphas_cumprod", + th.from_numpy(sqrt_recipm1_alphas_cumprod), + persistent=False, + ) + + self.register_buffer( + "posterior_variance", th.from_numpy(posterior_variance), persistent=False + ) + self.register_buffer( + "posterior_log_variance_clipped", + th.from_numpy(posterior_log_variance_clipped), + persistent=False, + ) + self.register_buffer( + "posterior_mean_coef1", + th.from_numpy(posterior_mean_coef1), + persistent=False, + ) + self.register_buffer( + "posterior_mean_coef2", + th.from_numpy(posterior_mean_coef2), + persistent=False, + ) + + def q_mean_variance(self, x_start, t): + """ + Get the distribution q(x_t | x_0). + :param x_start: the [N x C x ...] tensor of noiseless inputs. + :param t: the number of diffusion steps (minus 1). Here, 0 means one step. + :return: A tuple (mean, variance, log_variance), all of x_start's shape. + """ + mean = ( + _extract_into_tensor(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start + ) + variance = _extract_into_tensor(1.0 - self.alphas_cumprod, t, x_start.shape) + log_variance = _extract_into_tensor( + self.log_one_minus_alphas_cumprod, t, x_start.shape + ) + return mean, variance, log_variance + + def q_sample(self, x_start, t, noise=None): + """ + Diffuse the data for a given number of diffusion steps. + In other words, sample from q(x_t | x_0). + :param x_start: the initial data batch. + :param t: the number of diffusion steps (minus 1). Here, 0 means one step. + :param noise: if specified, the split-out normal noise. + :return: A noisy version of x_start. + """ + if noise is None: + noise = th.randn_like(x_start) + assert noise.shape == x_start.shape + return ( + _extract_into_tensor(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start + + _extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x_start.shape) + * noise + ) + + def q_posterior_mean_variance(self, x_start, x_t, t): + """ + Compute the mean and variance of the diffusion posterior: + q(x_{t-1} | x_t, x_0) + """ + assert x_start.shape == x_t.shape + posterior_mean = ( + _extract_into_tensor(self.posterior_mean_coef1, t, x_t.shape) * x_start + + _extract_into_tensor(self.posterior_mean_coef2, t, x_t.shape) * x_t + ) + posterior_variance = _extract_into_tensor(self.posterior_variance, t, x_t.shape) + posterior_log_variance_clipped = _extract_into_tensor( + self.posterior_log_variance_clipped, t, x_t.shape + ) + assert ( + posterior_mean.shape[0] + == posterior_variance.shape[0] + == posterior_log_variance_clipped.shape[0] + == x_start.shape[0] + ) + return posterior_mean, posterior_variance, posterior_log_variance_clipped + + def p_mean_variance( + self, + model, + x, + t, + clip_denoised=True, + denoised_fn=None, + model_kwargs=None, + **ignore_kwargs, + ): + """ + Apply the model to get p(x_{t-1} | x_t), as well as a prediction of + the initial x, x_0. + :param model: the model, which takes a signal and a batch of timesteps + as input. + :param x: the [N x C x ...] tensor at time t. + :param t: a 1-D Tensor of timesteps. + :param clip_denoised: if True, clip the denoised signal into [-1, 1]. + :param denoised_fn: if not None, a function which applies to the + x_start prediction before it is used to sample. Applies before + clip_denoised. + :param model_kwargs: if not None, a dict of extra keyword arguments to + pass to the model. This can be used for conditioning. + :return: a dict with the following keys: + - 'mean': the model mean output. + - 'variance': the model variance output. + - 'log_variance': the log of 'variance'. + - 'pred_xstart': the prediction for x_0. + """ + if model_kwargs is None: + model_kwargs = {} + + B, C = x.shape[:2] + assert t.shape == (B,) + model_output = model(x, t, **model_kwargs) + if isinstance(model_output, tuple): + model_output, extra = model_output + else: + extra = None + + if self.model_var_type in [ModelVarType.LEARNED, ModelVarType.LEARNED_RANGE]: + assert model_output.shape == (B, C * 2, *x.shape[2:]) + model_output, model_var_values = th.split(model_output, C, dim=1) + if self.model_var_type == ModelVarType.LEARNED: + model_log_variance = model_var_values + model_variance = th.exp(model_log_variance) + else: + min_log = _extract_into_tensor( + self.posterior_log_variance_clipped, t, x.shape + ) + max_log = _extract_into_tensor(th.log(self.betas), t, x.shape) + # The model_var_values is [-1, 1] for [min_var, max_var]. + frac = (model_var_values + 1) / 2 + model_log_variance = frac * max_log + (1 - frac) * min_log + model_variance = th.exp(model_log_variance) + else: + model_variance, model_log_variance = { + # for fixedlarge, we set the initial (log-)variance like so + # to get a better decoder log likelihood. + ModelVarType.FIXED_LARGE: ( + th.cat([self.posterior_variance[1][None], self.betas[1:]]), + th.log(th.cat([self.posterior_variance[1][None], self.betas[1:]])), + ), + ModelVarType.FIXED_SMALL: ( + self.posterior_variance, + self.posterior_log_variance_clipped, + ), + }[self.model_var_type] + model_variance = _extract_into_tensor(model_variance, t, x.shape) + model_log_variance = _extract_into_tensor(model_log_variance, t, x.shape) + + def process_xstart(x): + if denoised_fn is not None: + x = denoised_fn(x) + if clip_denoised: + return x.clamp(-1, 1) + return x + + if self.model_mean_type == ModelMeanType.PREVIOUS_X: + pred_xstart = process_xstart( + self._predict_xstart_from_xprev(x_t=x, t=t, xprev=model_output) + ) + model_mean = model_output + elif self.model_mean_type in [ModelMeanType.START_X, ModelMeanType.EPSILON]: + if self.model_mean_type == ModelMeanType.START_X: + pred_xstart = process_xstart(model_output) + else: + pred_xstart = process_xstart( + self._predict_xstart_from_eps(x_t=x, t=t, eps=model_output) + ) + model_mean, _, _ = self.q_posterior_mean_variance( + x_start=pred_xstart, x_t=x, t=t + ) + else: + raise NotImplementedError(self.model_mean_type) + + assert ( + model_mean.shape == model_log_variance.shape == pred_xstart.shape == x.shape + ) + return { + "mean": model_mean, + "variance": model_variance, + "log_variance": model_log_variance, + "pred_xstart": pred_xstart, + } + + def _predict_xstart_from_eps(self, x_t, t, eps): + assert x_t.shape == eps.shape + return ( + _extract_into_tensor(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t + - _extract_into_tensor(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape) * eps + ) + + def _predict_eps_from_xstart(self, x_t, t, pred_xstart): + return ( + _extract_into_tensor(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t + - pred_xstart + ) / _extract_into_tensor(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape) + + def condition_mean(self, cond_fn, p_mean_var, x, t, model_kwargs=None): + """ + Compute the mean for the previous step, given a function cond_fn that + computes the gradient of a conditional log probability with respect to + x. In particular, cond_fn computes grad(log(p(y|x))), and we want to + condition on y. + This uses the conditioning strategy from Sohl-Dickstein et al. (2015). + """ + gradient = cond_fn(x, t, **model_kwargs) + new_mean = ( + p_mean_var["mean"].float() + p_mean_var["variance"] * gradient.float() + ) + return new_mean + + def condition_score(self, cond_fn, p_mean_var, x, t, model_kwargs=None): + """ + Compute what the p_mean_variance output would have been, should the + model's score function be conditioned by cond_fn. + See condition_mean() for details on cond_fn. + Unlike condition_mean(), this instead uses the conditioning strategy + from Song et al (2020). + """ + alpha_bar = _extract_into_tensor(self.alphas_cumprod, t, x.shape) + + eps = self._predict_eps_from_xstart(x, t, p_mean_var["pred_xstart"]) + eps = eps - (1 - alpha_bar).sqrt() * cond_fn(x, t, **model_kwargs) + + out = p_mean_var.copy() + out["pred_xstart"] = self._predict_xstart_from_eps(x, t, eps) + out["mean"], _, _ = self.q_posterior_mean_variance( + x_start=out["pred_xstart"], x_t=x, t=t + ) + return out + + def p_sample( + self, + model, + x, + t, + clip_denoised=True, + denoised_fn=None, + cond_fn=None, + model_kwargs=None, + ): + """ + Sample x_{t-1} from the model at the given timestep. + :param model: the model to sample from. + :param x: the current tensor at x_{t-1}. + :param t: the value of t, starting at 0 for the first diffusion step. + :param clip_denoised: if True, clip the x_start prediction to [-1, 1]. + :param denoised_fn: if not None, a function which applies to the + x_start prediction before it is used to sample. + :param cond_fn: if not None, this is a gradient function that acts + similarly to the model. + :param model_kwargs: if not None, a dict of extra keyword arguments to + pass to the model. This can be used for conditioning. + :return: a dict containing the following keys: + - 'sample': a random sample from the model. + - 'pred_xstart': a prediction of x_0. + """ + out = self.p_mean_variance( + model, + x, + t, + clip_denoised=clip_denoised, + denoised_fn=denoised_fn, + model_kwargs=model_kwargs, + ) + noise = th.randn_like(x) + nonzero_mask = ( + (t != 0).float().view(-1, *([1] * (len(x.shape) - 1))) + ) # no noise when t == 0 + if cond_fn is not None: + out["mean"] = self.condition_mean( + cond_fn, out, x, t, model_kwargs=model_kwargs + ) + sample = out["mean"] + nonzero_mask * th.exp(0.5 * out["log_variance"]) * noise + return {"sample": sample, "pred_xstart": out["pred_xstart"]} + + def p_sample_loop( + self, + model, + shape, + noise=None, + clip_denoised=True, + denoised_fn=None, + cond_fn=None, + model_kwargs=None, + device=None, + progress=False, + ): + """ + Generate samples from the model. + :param model: the model module. + :param shape: the shape of the samples, (N, C, H, W). + :param noise: if specified, the noise from the encoder to sample. + Should be of the same shape as `shape`. + :param clip_denoised: if True, clip x_start predictions to [-1, 1]. + :param denoised_fn: if not None, a function which applies to the + x_start prediction before it is used to sample. + :param cond_fn: if not None, this is a gradient function that acts + similarly to the model. + :param model_kwargs: if not None, a dict of extra keyword arguments to + pass to the model. This can be used for conditioning. + :param device: if specified, the device to create the samples on. + If not specified, use a model parameter's device. + :param progress: if True, show a tqdm progress bar. + :return: a non-differentiable batch of samples. + """ + final = None + for sample in self.p_sample_loop_progressive( + model, + shape, + noise=noise, + clip_denoised=clip_denoised, + denoised_fn=denoised_fn, + cond_fn=cond_fn, + model_kwargs=model_kwargs, + device=device, + progress=progress, + ): + final = sample + return final["sample"] + + def p_sample_loop_progressive( + self, + model, + shape, + noise=None, + clip_denoised=True, + denoised_fn=None, + cond_fn=None, + model_kwargs=None, + device=None, + progress=False, + ): + """ + Generate samples from the model and yield intermediate samples from + each timestep of diffusion. + Arguments are the same as p_sample_loop(). + Returns a generator over dicts, where each dict is the return value of + p_sample(). + """ + if device is None: + device = next(model.parameters()).device + assert isinstance(shape, (tuple, list)) + if noise is not None: + img = noise + else: + img = th.randn(*shape, device=device) + indices = list(range(self.num_timesteps))[::-1] + + if progress: + # Lazy import so that we don't depend on tqdm. + from tqdm.auto import tqdm + + indices = tqdm(indices) + + for idx, i in enumerate(indices): + t = th.tensor([i] * shape[0], device=device) + with th.no_grad(): + out = self.p_sample( + model, + img, + t, + clip_denoised=clip_denoised, + denoised_fn=denoised_fn, + cond_fn=cond_fn, + model_kwargs=model_kwargs, + ) + yield out + img = out["sample"] + + def p_sample_loop_progressive_for_improved_sr( + self, + model, + model_aux, + shape, + noise=None, + clip_denoised=True, + denoised_fn=None, + cond_fn=None, + model_kwargs=None, + device=None, + progress=False, + ): + """ + Modified version of p_sample_loop_progressive for sampling from the improved sr model + """ + + if device is None: + device = next(model.parameters()).device + assert isinstance(shape, (tuple, list)) + if noise is not None: + img = noise + else: + img = th.randn(*shape, device=device) + indices = list(range(self.num_timesteps))[::-1] + + if progress: + # Lazy import so that we don't depend on tqdm. + from tqdm.auto import tqdm + + indices = tqdm(indices) + + for idx, i in enumerate(indices): + t = th.tensor([i] * shape[0], device=device) + with th.no_grad(): + out = self.p_sample( + model_aux if len(indices) - 1 == idx else model, + img, + t, + clip_denoised=clip_denoised, + denoised_fn=denoised_fn, + cond_fn=cond_fn, + model_kwargs=model_kwargs, + ) + yield out + img = out["sample"] + + def ddim_sample( + self, + model, + x, + t, + clip_denoised=True, + denoised_fn=None, + cond_fn=None, + model_kwargs=None, + eta=0.0, + ): + """ + Sample x_{t-1} from the model using DDIM. + Same usage as p_sample(). + """ + out = self.p_mean_variance( + model, + x, + t, + clip_denoised=clip_denoised, + denoised_fn=denoised_fn, + model_kwargs=model_kwargs, + ) + if cond_fn is not None: + out = self.condition_score(cond_fn, out, x, t, model_kwargs=model_kwargs) + + # Usually our model outputs epsilon, but we re-derive it + # in case we used x_start or x_prev prediction. + eps = self._predict_eps_from_xstart(x, t, out["pred_xstart"]) + + alpha_bar = _extract_into_tensor(self.alphas_cumprod, t, x.shape) + alpha_bar_prev = _extract_into_tensor(self.alphas_cumprod_prev, t, x.shape) + sigma = ( + eta + * th.sqrt((1 - alpha_bar_prev) / (1 - alpha_bar)) + * th.sqrt(1 - alpha_bar / alpha_bar_prev) + ) + # Equation 12. + noise = th.randn_like(x) + mean_pred = ( + out["pred_xstart"] * th.sqrt(alpha_bar_prev) + + th.sqrt(1 - alpha_bar_prev - sigma**2) * eps + ) + nonzero_mask = ( + (t != 0).float().view(-1, *([1] * (len(x.shape) - 1))) + ) # no noise when t == 0 + sample = mean_pred + nonzero_mask * sigma * noise + return {"sample": sample, "pred_xstart": out["pred_xstart"]} + + def ddim_reverse_sample( + self, + model, + x, + t, + clip_denoised=True, + denoised_fn=None, + cond_fn=None, + model_kwargs=None, + eta=0.0, + ): + """ + Sample x_{t+1} from the model using DDIM reverse ODE. + """ + assert eta == 0.0, "Reverse ODE only for deterministic path" + out = self.p_mean_variance( + model, + x, + t, + clip_denoised=clip_denoised, + denoised_fn=denoised_fn, + model_kwargs=model_kwargs, + ) + if cond_fn is not None: + out = self.condition_score(cond_fn, out, x, t, model_kwargs=model_kwargs) + # Usually our model outputs epsilon, but we re-derive it + # in case we used x_start or x_prev prediction. + eps = ( + _extract_into_tensor(self.sqrt_recip_alphas_cumprod, t, x.shape) * x + - out["pred_xstart"] + ) / _extract_into_tensor(self.sqrt_recipm1_alphas_cumprod, t, x.shape) + alpha_bar_next = _extract_into_tensor(self.alphas_cumprod_next, t, x.shape) + + # Equation 12. reversed + mean_pred = ( + out["pred_xstart"] * th.sqrt(alpha_bar_next) + + th.sqrt(1 - alpha_bar_next) * eps + ) + + return {"sample": mean_pred, "pred_xstart": out["pred_xstart"]} + + def ddim_sample_loop( + self, + model, + shape, + noise=None, + clip_denoised=True, + denoised_fn=None, + cond_fn=None, + model_kwargs=None, + device=None, + progress=False, + eta=0.0, + ): + """ + Generate samples from the model using DDIM. + Same usage as p_sample_loop(). + """ + final = None + for sample in self.ddim_sample_loop_progressive( + model, + shape, + noise=noise, + clip_denoised=clip_denoised, + denoised_fn=denoised_fn, + cond_fn=cond_fn, + model_kwargs=model_kwargs, + device=device, + progress=progress, + eta=eta, + ): + final = sample + return final["sample"] + + def ddim_sample_loop_progressive( + self, + model, + shape, + noise=None, + clip_denoised=True, + denoised_fn=None, + cond_fn=None, + model_kwargs=None, + device=None, + progress=False, + eta=0.0, + ): + """ + Use DDIM to sample from the model and yield intermediate samples from + each timestep of DDIM. + Same usage as p_sample_loop_progressive(). + """ + if device is None: + device = next(model.parameters()).device + assert isinstance(shape, (tuple, list)) + if noise is not None: + img = noise + else: + img = th.randn(*shape, device=device) + indices = list(range(self.num_timesteps))[::-1] + + if progress: + # Lazy import so that we don't depend on tqdm. + from tqdm.auto import tqdm + + indices = tqdm(indices) + + for i in indices: + t = th.tensor([i] * shape[0], device=device) + with th.no_grad(): + out = self.ddim_sample( + model, + img, + t, + clip_denoised=clip_denoised, + denoised_fn=denoised_fn, + cond_fn=cond_fn, + model_kwargs=model_kwargs, + eta=eta, + ) + yield out + img = out["sample"] + + +def _extract_into_tensor(arr, timesteps, broadcast_shape): + """ + Extract values from a 1-D numpy array for a batch of indices. + :param arr: the 1-D numpy array. + :param timesteps: a tensor of indices into the array to extract. + :param broadcast_shape: a larger shape of K dimensions with the batch + dimension equal to the length of timesteps. + :return: a tensor of shape [batch_size, 1, ...] where the shape has K dims. + """ + res = arr.to(device=timesteps.device)[timesteps].float() + while len(res.shape) < len(broadcast_shape): + res = res[..., None] + return res + th.zeros(broadcast_shape, device=timesteps.device) diff --git a/repositories/ldm/modules/karlo/kakao/modules/diffusion/respace.py b/repositories/ldm/modules/karlo/kakao/modules/diffusion/respace.py new file mode 100644 index 000000000..70c808f8b --- /dev/null +++ b/repositories/ldm/modules/karlo/kakao/modules/diffusion/respace.py @@ -0,0 +1,112 @@ +# ------------------------------------------------------------------------------------ +# Adapted from Guided-Diffusion repo (https://github.com/openai/guided-diffusion) +# ------------------------------------------------------------------------------------ + + +import torch as th + +from .gaussian_diffusion import GaussianDiffusion + + +def space_timesteps(num_timesteps, section_counts): + """ + Create a list of timesteps to use from an original diffusion process, + given the number of timesteps we want to take from equally-sized portions + of the original process. + + For example, if there's 300 timesteps and the section counts are [10,15,20] + then the first 100 timesteps are strided to be 10 timesteps, the second 100 + are strided to be 15 timesteps, and the final 100 are strided to be 20. + + :param num_timesteps: the number of diffusion steps in the original + process to divide up. + :param section_counts: either a list of numbers, or a string containing + comma-separated numbers, indicating the step count + per section. As a special case, use "ddimN" where N + is a number of steps to use the striding from the + DDIM paper. + :return: a set of diffusion steps from the original process to use. + """ + if isinstance(section_counts, str): + if section_counts.startswith("ddim"): + desired_count = int(section_counts[len("ddim") :]) + for i in range(1, num_timesteps): + if len(range(0, num_timesteps, i)) == desired_count: + return set(range(0, num_timesteps, i)) + raise ValueError( + f"cannot create exactly {num_timesteps} steps with an integer stride" + ) + elif section_counts == "fast27": + steps = space_timesteps(num_timesteps, "10,10,3,2,2") + # Help reduce DDIM artifacts from noisiest timesteps. + steps.remove(num_timesteps - 1) + steps.add(num_timesteps - 3) + return steps + section_counts = [int(x) for x in section_counts.split(",")] + size_per = num_timesteps // len(section_counts) + extra = num_timesteps % len(section_counts) + start_idx = 0 + all_steps = [] + for i, section_count in enumerate(section_counts): + size = size_per + (1 if i < extra else 0) + if size < section_count: + raise ValueError( + f"cannot divide section of {size} steps into {section_count}" + ) + if section_count <= 1: + frac_stride = 1 + else: + frac_stride = (size - 1) / (section_count - 1) + cur_idx = 0.0 + taken_steps = [] + for _ in range(section_count): + taken_steps.append(start_idx + round(cur_idx)) + cur_idx += frac_stride + all_steps += taken_steps + start_idx += size + return set(all_steps) + + +class SpacedDiffusion(GaussianDiffusion): + """ + A diffusion process which can skip steps in a base diffusion process. + + :param use_timesteps: a collection (sequence or set) of timesteps from the + original diffusion process to retain. + :param kwargs: the kwargs to create the base diffusion process. + """ + + def __init__(self, use_timesteps, **kwargs): + self.use_timesteps = set(use_timesteps) + self.original_num_steps = len(kwargs["betas"]) + + base_diffusion = GaussianDiffusion(**kwargs) # pylint: disable=missing-kwoa + last_alpha_cumprod = 1.0 + new_betas = [] + timestep_map = [] + for i, alpha_cumprod in enumerate(base_diffusion.alphas_cumprod): + if i in self.use_timesteps: + new_betas.append(1 - alpha_cumprod / last_alpha_cumprod) + last_alpha_cumprod = alpha_cumprod + timestep_map.append(i) + kwargs["betas"] = th.tensor(new_betas).numpy() + super().__init__(**kwargs) + self.register_buffer("timestep_map", th.tensor(timestep_map), persistent=False) + + def p_mean_variance(self, model, *args, **kwargs): + return super().p_mean_variance(self._wrap_model(model), *args, **kwargs) + + def condition_mean(self, cond_fn, *args, **kwargs): + return super().condition_mean(self._wrap_model(cond_fn), *args, **kwargs) + + def condition_score(self, cond_fn, *args, **kwargs): + return super().condition_score(self._wrap_model(cond_fn), *args, **kwargs) + + def _wrap_model(self, model): + def wrapped(x, ts, **kwargs): + ts_cpu = ts.detach().to("cpu") + return model( + x, self.timestep_map[ts_cpu].to(device=ts.device, dtype=ts.dtype), **kwargs + ) + + return wrapped diff --git a/repositories/ldm/modules/karlo/kakao/modules/nn.py b/repositories/ldm/modules/karlo/kakao/modules/nn.py new file mode 100644 index 000000000..2eef3f5a0 --- /dev/null +++ b/repositories/ldm/modules/karlo/kakao/modules/nn.py @@ -0,0 +1,114 @@ +# ------------------------------------------------------------------------------------ +# Adapted from Guided-Diffusion repo (https://github.com/openai/guided-diffusion) +# ------------------------------------------------------------------------------------ + +import math + +import torch as th +import torch.nn as nn +import torch.nn.functional as F + + +class GroupNorm32(nn.GroupNorm): + def __init__(self, num_groups, num_channels, swish, eps=1e-5): + super().__init__(num_groups=num_groups, num_channels=num_channels, eps=eps) + self.swish = swish + + def forward(self, x): + y = super().forward(x.float()).to(x.dtype) + if self.swish == 1.0: + y = F.silu(y) + elif self.swish: + y = y * F.sigmoid(y * float(self.swish)) + return y + + +def conv_nd(dims, *args, **kwargs): + """ + Create a 1D, 2D, or 3D convolution module. + """ + if dims == 1: + return nn.Conv1d(*args, **kwargs) + elif dims == 2: + return nn.Conv2d(*args, **kwargs) + elif dims == 3: + return nn.Conv3d(*args, **kwargs) + raise ValueError(f"unsupported dimensions: {dims}") + + +def linear(*args, **kwargs): + """ + Create a linear module. + """ + return nn.Linear(*args, **kwargs) + + +def avg_pool_nd(dims, *args, **kwargs): + """ + Create a 1D, 2D, or 3D average pooling module. + """ + if dims == 1: + return nn.AvgPool1d(*args, **kwargs) + elif dims == 2: + return nn.AvgPool2d(*args, **kwargs) + elif dims == 3: + return nn.AvgPool3d(*args, **kwargs) + raise ValueError(f"unsupported dimensions: {dims}") + + +def zero_module(module): + """ + Zero out the parameters of a module and return it. + """ + for p in module.parameters(): + p.detach().zero_() + return module + + +def scale_module(module, scale): + """ + Scale the parameters of a module and return it. + """ + for p in module.parameters(): + p.detach().mul_(scale) + return module + + +def normalization(channels, swish=0.0): + """ + Make a standard normalization layer, with an optional swish activation. + + :param channels: number of input channels. + :return: an nn.Module for normalization. + """ + return GroupNorm32(num_channels=channels, num_groups=32, swish=swish) + + +def timestep_embedding(timesteps, dim, max_period=10000): + """ + Create sinusoidal timestep embeddings. + + :param timesteps: a 1-D Tensor of N indices, one per batch element. + These may be fractional. + :param dim: the dimension of the output. + :param max_period: controls the minimum frequency of the embeddings. + :return: an [N x dim] Tensor of positional embeddings. + """ + half = dim // 2 + freqs = th.exp( + -math.log(max_period) + * th.arange(start=0, end=half, dtype=th.float32, device=timesteps.device) + / half + ) + args = timesteps[:, None].float() * freqs[None] + embedding = th.cat([th.cos(args), th.sin(args)], dim=-1) + if dim % 2: + embedding = th.cat([embedding, th.zeros_like(embedding[:, :1])], dim=-1) + return embedding + + +def mean_flat(tensor): + """ + Take the mean over all non-batch dimensions. + """ + return tensor.mean(dim=list(range(1, len(tensor.shape)))) diff --git a/repositories/ldm/modules/karlo/kakao/modules/resample.py b/repositories/ldm/modules/karlo/kakao/modules/resample.py new file mode 100644 index 000000000..485421aa4 --- /dev/null +++ b/repositories/ldm/modules/karlo/kakao/modules/resample.py @@ -0,0 +1,68 @@ +# ------------------------------------------------------------------------------------ +# Modified from Guided-Diffusion (https://github.com/openai/guided-diffusion) +# ------------------------------------------------------------------------------------ + +from abc import abstractmethod + +import torch as th + + +def create_named_schedule_sampler(name, diffusion): + """ + Create a ScheduleSampler from a library of pre-defined samplers. + + :param name: the name of the sampler. + :param diffusion: the diffusion object to sample for. + """ + if name == "uniform": + return UniformSampler(diffusion) + else: + raise NotImplementedError(f"unknown schedule sampler: {name}") + + +class ScheduleSampler(th.nn.Module): + """ + A distribution over timesteps in the diffusion process, intended to reduce + variance of the objective. + + By default, samplers perform unbiased importance sampling, in which the + objective's mean is unchanged. + However, subclasses may override sample() to change how the resampled + terms are reweighted, allowing for actual changes in the objective. + """ + + @abstractmethod + def weights(self): + """ + Get a numpy array of weights, one per diffusion step. + + The weights needn't be normalized, but must be positive. + """ + + def sample(self, batch_size, device): + """ + Importance-sample timesteps for a batch. + + :param batch_size: the number of timesteps. + :param device: the torch device to save to. + :return: a tuple (timesteps, weights): + - timesteps: a tensor of timestep indices. + - weights: a tensor of weights to scale the resulting losses. + """ + w = self.weights() + p = w / th.sum(w) + indices = p.multinomial(batch_size, replacement=True) + weights = 1 / (len(p) * p[indices]) + return indices, weights + + +class UniformSampler(ScheduleSampler): + def __init__(self, diffusion): + super(UniformSampler, self).__init__() + self.diffusion = diffusion + self.register_buffer( + "_weights", th.ones([diffusion.num_timesteps]), persistent=False + ) + + def weights(self): + return self._weights diff --git a/repositories/ldm/modules/karlo/kakao/modules/unet.py b/repositories/ldm/modules/karlo/kakao/modules/unet.py new file mode 100644 index 000000000..c99d0b791 --- /dev/null +++ b/repositories/ldm/modules/karlo/kakao/modules/unet.py @@ -0,0 +1,792 @@ +# ------------------------------------------------------------------------------------ +# Modified from Guided-Diffusion (https://github.com/openai/guided-diffusion) +# ------------------------------------------------------------------------------------ + +import math +from abc import abstractmethod + +import torch as th +import torch.nn as nn +import torch.nn.functional as F + +from .nn import ( + avg_pool_nd, + conv_nd, + linear, + normalization, + timestep_embedding, + zero_module, +) +from .xf import LayerNorm + + +class TimestepBlock(nn.Module): + """ + Any module where forward() takes timestep embeddings as a second argument. + """ + + @abstractmethod + def forward(self, x, emb): + """ + Apply the module to `x` given `emb` timestep embeddings. + """ + + +class TimestepEmbedSequential(nn.Sequential, TimestepBlock): + """ + A sequential module that passes timestep embeddings to the children that + support it as an extra input. + """ + + def forward(self, x, emb, encoder_out=None, mask=None): + for layer in self: + if isinstance(layer, TimestepBlock): + x = layer(x, emb) + elif isinstance(layer, AttentionBlock): + x = layer(x, encoder_out, mask=mask) + else: + x = layer(x) + return x + + +class Upsample(nn.Module): + """ + An upsampling layer with an optional convolution. + + :param channels: channels in the inputs and outputs. + :param use_conv: a bool determining if a convolution is applied. + :param dims: determines if the signal is 1D, 2D, or 3D. If 3D, then + upsampling occurs in the inner-two dimensions. + """ + + def __init__(self, channels, use_conv, dims=2, out_channels=None): + super().__init__() + self.channels = channels + self.out_channels = out_channels or channels + self.use_conv = use_conv + self.dims = dims + if use_conv: + self.conv = conv_nd(dims, self.channels, self.out_channels, 3, padding=1) + + def forward(self, x): + assert x.shape[1] == self.channels + if self.dims == 3: + x = F.interpolate( + x, (x.shape[2], x.shape[3] * 2, x.shape[4] * 2), mode="nearest" + ) + else: + x = F.interpolate(x, scale_factor=2, mode="nearest") + if self.use_conv: + x = self.conv(x) + return x + + +class Downsample(nn.Module): + """ + A downsampling layer with an optional convolution. + + :param channels: channels in the inputs and outputs. + :param use_conv: a bool determining if a convolution is applied. + :param dims: determines if the signal is 1D, 2D, or 3D. If 3D, then + downsampling occurs in the inner-two dimensions. + """ + + def __init__(self, channels, use_conv, dims=2, out_channels=None): + super().__init__() + self.channels = channels + self.out_channels = out_channels or channels + self.use_conv = use_conv + self.dims = dims + stride = 2 if dims != 3 else (1, 2, 2) + if use_conv: + self.op = conv_nd( + dims, self.channels, self.out_channels, 3, stride=stride, padding=1 + ) + else: + assert self.channels == self.out_channels + self.op = avg_pool_nd(dims, kernel_size=stride, stride=stride) + + def forward(self, x): + assert x.shape[1] == self.channels + return self.op(x) + + +class ResBlock(TimestepBlock): + """ + A residual block that can optionally change the number of channels. + + :param channels: the number of input channels. + :param emb_channels: the number of timestep embedding channels. + :param dropout: the rate of dropout. + :param out_channels: if specified, the number of out channels. + :param use_conv: if True and out_channels is specified, use a spatial + convolution instead of a smaller 1x1 convolution to change the + channels in the skip connection. + :param dims: determines if the signal is 1D, 2D, or 3D. + :param use_checkpoint: if True, use gradient checkpointing on this module. + :param up: if True, use this block for upsampling. + :param down: if True, use this block for downsampling. + """ + + def __init__( + self, + channels, + emb_channels, + dropout, + out_channels=None, + use_conv=False, + use_scale_shift_norm=False, + dims=2, + use_checkpoint=False, + up=False, + down=False, + ): + super().__init__() + self.channels = channels + self.emb_channels = emb_channels + self.dropout = dropout + self.out_channels = out_channels or channels + self.use_conv = use_conv + self.use_checkpoint = use_checkpoint + self.use_scale_shift_norm = use_scale_shift_norm + + self.in_layers = nn.Sequential( + normalization(channels, swish=1.0), + nn.Identity(), + conv_nd(dims, channels, self.out_channels, 3, padding=1), + ) + + self.updown = up or down + + if up: + self.h_upd = Upsample(channels, False, dims) + self.x_upd = Upsample(channels, False, dims) + elif down: + self.h_upd = Downsample(channels, False, dims) + self.x_upd = Downsample(channels, False, dims) + else: + self.h_upd = self.x_upd = nn.Identity() + + self.emb_layers = nn.Sequential( + nn.SiLU(), + linear( + emb_channels, + 2 * self.out_channels if use_scale_shift_norm else self.out_channels, + ), + ) + self.out_layers = nn.Sequential( + normalization( + self.out_channels, swish=0.0 if use_scale_shift_norm else 1.0 + ), + nn.SiLU() if use_scale_shift_norm else nn.Identity(), + nn.Dropout(p=dropout), + zero_module( + conv_nd(dims, self.out_channels, self.out_channels, 3, padding=1) + ), + ) + + if self.out_channels == channels: + self.skip_connection = nn.Identity() + elif use_conv: + self.skip_connection = conv_nd( + dims, channels, self.out_channels, 3, padding=1 + ) + else: + self.skip_connection = conv_nd(dims, channels, self.out_channels, 1) + + def forward(self, x, emb): + """ + Apply the block to a Tensor, conditioned on a timestep embedding. + + :param x: an [N x C x ...] Tensor of features. + :param emb: an [N x emb_channels] Tensor of timestep embeddings. + :return: an [N x C x ...] Tensor of outputs. + """ + if self.updown: + in_rest, in_conv = self.in_layers[:-1], self.in_layers[-1] + h = in_rest(x) + h = self.h_upd(h) + x = self.x_upd(x) + h = in_conv(h) + else: + h = self.in_layers(x) + emb_out = self.emb_layers(emb) + while len(emb_out.shape) < len(h.shape): + emb_out = emb_out[..., None] + if self.use_scale_shift_norm: + out_norm, out_rest = self.out_layers[0], self.out_layers[1:] + scale, shift = th.chunk(emb_out, 2, dim=1) + h = out_norm(h) * (1 + scale) + shift + h = out_rest(h) + else: + h = h + emb_out + h = self.out_layers(h) + return self.skip_connection(x) + h + + +class ResBlockNoTimeEmbedding(nn.Module): + """ + A residual block without time embedding + + :param channels: the number of input channels. + :param emb_channels: the number of timestep embedding channels. + :param dropout: the rate of dropout. + :param out_channels: if specified, the number of out channels. + :param use_conv: if True and out_channels is specified, use a spatial + convolution instead of a smaller 1x1 convolution to change the + channels in the skip connection. + :param dims: determines if the signal is 1D, 2D, or 3D. + :param use_checkpoint: if True, use gradient checkpointing on this module. + :param up: if True, use this block for upsampling. + :param down: if True, use this block for downsampling. + """ + + def __init__( + self, + channels, + emb_channels, + dropout, + out_channels=None, + use_conv=False, + dims=2, + use_checkpoint=False, + up=False, + down=False, + **kwargs, + ): + super().__init__() + self.channels = channels + self.emb_channels = emb_channels + self.dropout = dropout + self.out_channels = out_channels or channels + self.use_conv = use_conv + self.use_checkpoint = use_checkpoint + + self.in_layers = nn.Sequential( + normalization(channels, swish=1.0), + nn.Identity(), + conv_nd(dims, channels, self.out_channels, 3, padding=1), + ) + + self.updown = up or down + + if up: + self.h_upd = Upsample(channels, False, dims) + self.x_upd = Upsample(channels, False, dims) + elif down: + self.h_upd = Downsample(channels, False, dims) + self.x_upd = Downsample(channels, False, dims) + else: + self.h_upd = self.x_upd = nn.Identity() + + self.out_layers = nn.Sequential( + normalization(self.out_channels, swish=1.0), + nn.Dropout(p=dropout), + zero_module( + conv_nd(dims, self.out_channels, self.out_channels, 3, padding=1) + ), + ) + + if self.out_channels == channels: + self.skip_connection = nn.Identity() + elif use_conv: + self.skip_connection = conv_nd( + dims, channels, self.out_channels, 3, padding=1 + ) + else: + self.skip_connection = conv_nd(dims, channels, self.out_channels, 1) + + def forward(self, x, emb=None): + """ + Apply the block to a Tensor, NOT conditioned on a timestep embedding. + + :param x: an [N x C x ...] Tensor of features. + :param emb: an [N x emb_channels] Tensor of timestep embeddings. + :return: an [N x C x ...] Tensor of outputs. + """ + assert emb is None + + if self.updown: + in_rest, in_conv = self.in_layers[:-1], self.in_layers[-1] + h = in_rest(x) + h = self.h_upd(h) + x = self.x_upd(x) + h = in_conv(h) + else: + h = self.in_layers(x) + h = self.out_layers(h) + return self.skip_connection(x) + h + + +class AttentionBlock(nn.Module): + """ + An attention block that allows spatial positions to attend to each other. + + Originally ported from here, but adapted to the N-d case. + https://github.com/hojonathanho/diffusion/blob/1e0dceb3b3495bbe19116a5e1b3596cd0706c543/diffusion_tf/models/unet.py#L66. + """ + + def __init__( + self, + channels, + num_heads=1, + num_head_channels=-1, + use_checkpoint=False, + encoder_channels=None, + ): + super().__init__() + self.channels = channels + if num_head_channels == -1: + self.num_heads = num_heads + else: + assert ( + channels % num_head_channels == 0 + ), f"q,k,v channels {channels} is not divisible by num_head_channels {num_head_channels}" + self.num_heads = channels // num_head_channels + self.use_checkpoint = use_checkpoint + self.norm = normalization(channels, swish=0.0) + self.qkv = conv_nd(1, channels, channels * 3, 1) + self.attention = QKVAttention(self.num_heads) + + if encoder_channels is not None: + self.encoder_kv = conv_nd(1, encoder_channels, channels * 2, 1) + self.proj_out = zero_module(conv_nd(1, channels, channels, 1)) + + def forward(self, x, encoder_out=None, mask=None): + b, c, *spatial = x.shape + qkv = self.qkv(self.norm(x).view(b, c, -1)) + if encoder_out is not None: + encoder_out = self.encoder_kv(encoder_out) + h = self.attention(qkv, encoder_out, mask=mask) + else: + h = self.attention(qkv) + h = self.proj_out(h) + return x + h.reshape(b, c, *spatial) + + +class QKVAttention(nn.Module): + """ + A module which performs QKV attention. Matches legacy QKVAttention + input/ouput heads shaping + """ + + def __init__(self, n_heads): + super().__init__() + self.n_heads = n_heads + + def forward(self, qkv, encoder_kv=None, mask=None): + """ + Apply QKV attention. + + :param qkv: an [N x (H * 3 * C) x T] tensor of Qs, Ks, and Vs. + :return: an [N x (H * C) x T] tensor after attention. + """ + bs, width, length = qkv.shape + assert width % (3 * self.n_heads) == 0 + ch = width // (3 * self.n_heads) + q, k, v = qkv.reshape(bs * self.n_heads, ch * 3, length).split(ch, dim=1) + if encoder_kv is not None: + assert encoder_kv.shape[1] == self.n_heads * ch * 2 + ek, ev = encoder_kv.reshape(bs * self.n_heads, ch * 2, -1).split(ch, dim=1) + k = th.cat([ek, k], dim=-1) + v = th.cat([ev, v], dim=-1) + scale = 1 / math.sqrt(math.sqrt(ch)) + weight = th.einsum("bct,bcs->bts", q * scale, k * scale) + if mask is not None: + mask = F.pad(mask, (0, length), value=0.0) + mask = ( + mask.unsqueeze(1) + .expand(-1, self.n_heads, -1) + .reshape(bs * self.n_heads, 1, -1) + ) + weight = weight + mask + weight = th.softmax(weight, dim=-1) + a = th.einsum("bts,bcs->bct", weight, v) + return a.reshape(bs, -1, length) + + +class UNetModel(nn.Module): + """ + The full UNet model with attention and timestep embedding. + + :param in_channels: channels in the input Tensor. + :param model_channels: base channel count for the model. + :param out_channels: channels in the output Tensor. + :param num_res_blocks: number of residual blocks per downsample. + :param attention_resolutions: a collection of downsample rates at which + attention will take place. May be a set, list, or tuple. + For example, if this contains 4, then at 4x downsampling, attention + will be used. + :param dropout: the dropout probability. + :param channel_mult: channel multiplier for each level of the UNet. + :param conv_resample: if True, use learned convolutions for upsampling and + downsampling. + :param dims: determines if the signal is 1D, 2D, or 3D. + :param clip_dim: dimension of clip feature. + :param num_classes: if specified (as an int), then this model will be + class-conditional with `num_classes` classes. + :param use_checkpoint: use gradient checkpointing to reduce memory usage. + :param num_heads: the number of attention heads in each attention layer. + :param num_heads_channels: if specified, ignore num_heads and instead use + a fixed channel width per attention head. + :param num_heads_upsample: works with num_heads to set a different number + of heads for upsampling. Deprecated. + :param use_scale_shift_norm: use a FiLM-like conditioning mechanism. + :param resblock_updown: use residual blocks for up/downsampling. + :param encoder_channels: use to make the dimension of query and kv same in AttentionBlock. + :param use_time_embedding: use time embedding for condition. + """ + + def __init__( + self, + in_channels, + model_channels, + out_channels, + num_res_blocks, + attention_resolutions, + dropout=0, + channel_mult=(1, 2, 4, 8), + conv_resample=True, + dims=2, + clip_dim=None, + use_checkpoint=False, + num_heads=1, + num_head_channels=-1, + num_heads_upsample=-1, + use_scale_shift_norm=False, + use_middle_attention=True, + resblock_updown=False, + encoder_channels=None, + use_time_embedding=True, + ): + super().__init__() + + if num_heads_upsample == -1: + num_heads_upsample = num_heads + + self.in_channels = in_channels + self.model_channels = model_channels + self.out_channels = out_channels + self.num_res_blocks = num_res_blocks + self.attention_resolutions = attention_resolutions + self.dropout = dropout + self.channel_mult = channel_mult + self.conv_resample = conv_resample + self.clip_dim = clip_dim + self.use_checkpoint = use_checkpoint + self.num_heads = num_heads + self.num_head_channels = num_head_channels + self.num_heads_upsample = num_heads_upsample + self.use_middle_attention = use_middle_attention + self.use_time_embedding = use_time_embedding + + if self.use_time_embedding: + time_embed_dim = model_channels * 4 + self.time_embed = nn.Sequential( + linear(model_channels, time_embed_dim), + nn.SiLU(), + linear(time_embed_dim, time_embed_dim), + ) + + if self.clip_dim is not None: + self.clip_emb = nn.Linear(clip_dim, time_embed_dim) + else: + time_embed_dim = None + + CustomResidualBlock = ( + ResBlock if self.use_time_embedding else ResBlockNoTimeEmbedding + ) + ch = input_ch = int(channel_mult[0] * model_channels) + self.input_blocks = nn.ModuleList( + [TimestepEmbedSequential(conv_nd(dims, in_channels, ch, 3, padding=1))] + ) + self._feature_size = ch + input_block_chans = [ch] + ds = 1 + for level, mult in enumerate(channel_mult): + for _ in range(num_res_blocks): + layers = [ + CustomResidualBlock( + ch, + time_embed_dim, + dropout, + out_channels=int(mult * model_channels), + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + ) + ] + ch = int(mult * model_channels) + if ds in attention_resolutions: + layers.append( + AttentionBlock( + ch, + use_checkpoint=use_checkpoint, + num_heads=num_heads, + num_head_channels=num_head_channels, + encoder_channels=encoder_channels, + ) + ) + self.input_blocks.append(TimestepEmbedSequential(*layers)) + self._feature_size += ch + input_block_chans.append(ch) + if level != len(channel_mult) - 1: + out_ch = ch + self.input_blocks.append( + TimestepEmbedSequential( + CustomResidualBlock( + ch, + time_embed_dim, + dropout, + out_channels=out_ch, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + down=True, + ) + if resblock_updown + else Downsample( + ch, conv_resample, dims=dims, out_channels=out_ch + ) + ) + ) + ch = out_ch + input_block_chans.append(ch) + ds *= 2 + self._feature_size += ch + + self.middle_block = TimestepEmbedSequential( + CustomResidualBlock( + ch, + time_embed_dim, + dropout, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + ), + *( + AttentionBlock( + ch, + use_checkpoint=use_checkpoint, + num_heads=num_heads, + num_head_channels=num_head_channels, + encoder_channels=encoder_channels, + ), + ) + if self.use_middle_attention + else tuple(), # add AttentionBlock or not + CustomResidualBlock( + ch, + time_embed_dim, + dropout, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + ), + ) + self._feature_size += ch + + self.output_blocks = nn.ModuleList([]) + for level, mult in list(enumerate(channel_mult))[::-1]: + for i in range(num_res_blocks + 1): + ich = input_block_chans.pop() + layers = [ + CustomResidualBlock( + ch + ich, + time_embed_dim, + dropout, + out_channels=int(model_channels * mult), + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + ) + ] + ch = int(model_channels * mult) + if ds in attention_resolutions: + layers.append( + AttentionBlock( + ch, + use_checkpoint=use_checkpoint, + num_heads=num_heads_upsample, + num_head_channels=num_head_channels, + encoder_channels=encoder_channels, + ) + ) + if level and i == num_res_blocks: + out_ch = ch + layers.append( + CustomResidualBlock( + ch, + time_embed_dim, + dropout, + out_channels=out_ch, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + up=True, + ) + if resblock_updown + else Upsample(ch, conv_resample, dims=dims, out_channels=out_ch) + ) + ds //= 2 + self.output_blocks.append(TimestepEmbedSequential(*layers)) + self._feature_size += ch + + self.out = nn.Sequential( + normalization(ch, swish=1.0), + nn.Identity(), + zero_module(conv_nd(dims, input_ch, out_channels, 3, padding=1)), + ) + + def forward(self, x, timesteps, y=None): + """ + Apply the model to an input batch. + + :param x: an [N x C x ...] Tensor of inputs. + :param timesteps: a 1-D batch of timesteps. + :param y: an [N] Tensor of labels, if class-conditional. + :return: an [N x C x ...] Tensor of outputs. + """ + assert (y is not None) == ( + self.clip_dim is not None + ), "must specify y if and only if the model is clip-rep-conditional" + + hs = [] + if self.use_time_embedding: + emb = self.time_embed(timestep_embedding(timesteps, self.model_channels)) + if self.clip_dim is not None: + emb = emb + self.clip_emb(y) + else: + emb = None + + h = x + for module in self.input_blocks: + h = module(h, emb) + hs.append(h) + h = self.middle_block(h, emb) + for module in self.output_blocks: + h = th.cat([h, hs.pop()], dim=1) + h = module(h, emb) + + return self.out(h) + + +class SuperResUNetModel(UNetModel): + """ + A UNetModel that performs super-resolution. + + Expects an extra kwarg `low_res` to condition on a low-resolution image. + Assumes that the shape of low-resolution and the input should be the same. + """ + + def __init__(self, *args, **kwargs): + if "in_channels" in kwargs: + kwargs = dict(kwargs) + kwargs["in_channels"] = kwargs["in_channels"] * 2 + else: + # Curse you, Python. Or really, just curse positional arguments :|. + args = list(args) + args[1] = args[1] * 2 + super().__init__(*args, **kwargs) + + def forward(self, x, timesteps, low_res=None, **kwargs): + _, _, new_height, new_width = x.shape + assert new_height == low_res.shape[2] and new_width == low_res.shape[3] + + x = th.cat([x, low_res], dim=1) + return super().forward(x, timesteps, **kwargs) + + +class PLMImUNet(UNetModel): + """ + A UNetModel that conditions on text with a pretrained text encoder in CLIP. + + :param text_ctx: number of text tokens to expect. + :param xf_width: width of the transformer. + :param clip_emb_mult: #extra tokens by projecting clip text feature. + :param clip_emb_type: type of condition (here, we fix clip image feature). + :param clip_emb_drop: dropout rato of clip image feature for cfg. + """ + + def __init__( + self, + text_ctx, + xf_width, + *args, + clip_emb_mult=None, + clip_emb_type="image", + clip_emb_drop=0.0, + **kwargs, + ): + self.text_ctx = text_ctx + self.xf_width = xf_width + self.clip_emb_mult = clip_emb_mult + self.clip_emb_type = clip_emb_type + self.clip_emb_drop = clip_emb_drop + + if not xf_width: + super().__init__(*args, **kwargs, encoder_channels=None) + else: + super().__init__(*args, **kwargs, encoder_channels=xf_width) + + # Project text encoded feat seq from pre-trained text encoder in CLIP + self.text_seq_proj = nn.Sequential( + nn.Linear(self.clip_dim, xf_width), + LayerNorm(xf_width), + ) + # Project CLIP text feat + self.text_feat_proj = nn.Linear(self.clip_dim, self.model_channels * 4) + + assert clip_emb_mult is not None + assert clip_emb_type == "image" + assert self.clip_dim is not None, "CLIP representation dim should be specified" + + self.clip_tok_proj = nn.Linear( + self.clip_dim, self.xf_width * self.clip_emb_mult + ) + if self.clip_emb_drop > 0: + self.cf_param = nn.Parameter(th.empty(self.clip_dim, dtype=th.float32)) + + def proc_clip_emb_drop(self, feat): + if self.clip_emb_drop > 0: + bsz, feat_dim = feat.shape + assert ( + feat_dim == self.clip_dim + ), f"CLIP input dim: {feat_dim}, model CLIP dim: {self.clip_dim}" + drop_idx = th.rand((bsz,), device=feat.device) < self.clip_emb_drop + feat = th.where( + drop_idx[..., None], self.cf_param[None].type_as(feat), feat + ) + return feat + + def forward( + self, x, timesteps, txt_feat=None, txt_feat_seq=None, mask=None, y=None + ): + bsz = x.shape[0] + hs = [] + emb = self.time_embed(timestep_embedding(timesteps, self.model_channels)) + emb = emb + self.clip_emb(y) + + xf_out = self.text_seq_proj(txt_feat_seq) + xf_out = xf_out.permute(0, 2, 1) + emb = emb + self.text_feat_proj(txt_feat) + xf_out = th.cat( + [ + self.clip_tok_proj(y).reshape(bsz, -1, self.clip_emb_mult), + xf_out, + ], + dim=2, + ) + mask = F.pad(mask, (self.clip_emb_mult, 0), value=True) + mask = th.where(mask, 0.0, float("-inf")) + + h = x + for module in self.input_blocks: + h = module(h, emb, xf_out, mask=mask) + hs.append(h) + h = self.middle_block(h, emb, xf_out, mask=mask) + for module in self.output_blocks: + h = th.cat([h, hs.pop()], dim=1) + h = module(h, emb, xf_out, mask=mask) + h = self.out(h) + + return h diff --git a/repositories/ldm/modules/karlo/kakao/modules/xf.py b/repositories/ldm/modules/karlo/kakao/modules/xf.py new file mode 100644 index 000000000..66d7d4a2f --- /dev/null +++ b/repositories/ldm/modules/karlo/kakao/modules/xf.py @@ -0,0 +1,231 @@ +# ------------------------------------------------------------------------------------ +# Adapted from the repos below: +# (a) Guided-Diffusion (https://github.com/openai/guided-diffusion) +# (b) CLIP ViT (https://github.com/openai/CLIP/) +# ------------------------------------------------------------------------------------ + +import math + +import torch as th +import torch.nn as nn +import torch.nn.functional as F + +from .nn import timestep_embedding + + +def convert_module_to_f16(param): + """ + Convert primitive modules to float16. + """ + if isinstance(param, (nn.Linear, nn.Conv2d, nn.ConvTranspose2d)): + param.weight.data = param.weight.data.half() + if param.bias is not None: + param.bias.data = param.bias.data.half() + + +class LayerNorm(nn.LayerNorm): + """ + Implementation that supports fp16 inputs but fp32 gains/biases. + """ + + def forward(self, x: th.Tensor): + return super().forward(x.float()).to(x.dtype) + + +class MultiheadAttention(nn.Module): + def __init__(self, n_ctx, width, heads): + super().__init__() + self.n_ctx = n_ctx + self.width = width + self.heads = heads + self.c_qkv = nn.Linear(width, width * 3) + self.c_proj = nn.Linear(width, width) + self.attention = QKVMultiheadAttention(heads, n_ctx) + + def forward(self, x, mask=None): + x = self.c_qkv(x) + x = self.attention(x, mask=mask) + x = self.c_proj(x) + return x + + +class MLP(nn.Module): + def __init__(self, width): + super().__init__() + self.width = width + self.c_fc = nn.Linear(width, width * 4) + self.c_proj = nn.Linear(width * 4, width) + self.gelu = nn.GELU() + + def forward(self, x): + return self.c_proj(self.gelu(self.c_fc(x))) + + +class QKVMultiheadAttention(nn.Module): + def __init__(self, n_heads: int, n_ctx: int): + super().__init__() + self.n_heads = n_heads + self.n_ctx = n_ctx + + def forward(self, qkv, mask=None): + bs, n_ctx, width = qkv.shape + attn_ch = width // self.n_heads // 3 + scale = 1 / math.sqrt(math.sqrt(attn_ch)) + qkv = qkv.view(bs, n_ctx, self.n_heads, -1) + q, k, v = th.split(qkv, attn_ch, dim=-1) + weight = th.einsum("bthc,bshc->bhts", q * scale, k * scale) + wdtype = weight.dtype + if mask is not None: + weight = weight + mask[:, None, ...] + weight = th.softmax(weight, dim=-1).type(wdtype) + return th.einsum("bhts,bshc->bthc", weight, v).reshape(bs, n_ctx, -1) + + +class ResidualAttentionBlock(nn.Module): + def __init__( + self, + n_ctx: int, + width: int, + heads: int, + ): + super().__init__() + + self.attn = MultiheadAttention( + n_ctx, + width, + heads, + ) + self.ln_1 = LayerNorm(width) + self.mlp = MLP(width) + self.ln_2 = LayerNorm(width) + + def forward(self, x, mask=None): + x = x + self.attn(self.ln_1(x), mask=mask) + x = x + self.mlp(self.ln_2(x)) + return x + + +class Transformer(nn.Module): + def __init__( + self, + n_ctx: int, + width: int, + layers: int, + heads: int, + ): + super().__init__() + self.n_ctx = n_ctx + self.width = width + self.layers = layers + self.resblocks = nn.ModuleList( + [ + ResidualAttentionBlock( + n_ctx, + width, + heads, + ) + for _ in range(layers) + ] + ) + + def forward(self, x, mask=None): + for block in self.resblocks: + x = block(x, mask=mask) + return x + + +class PriorTransformer(nn.Module): + """ + A Causal Transformer that conditions on CLIP text embedding, text. + + :param text_ctx: number of text tokens to expect. + :param xf_width: width of the transformer. + :param xf_layers: depth of the transformer. + :param xf_heads: heads in the transformer. + :param xf_final_ln: use a LayerNorm after the output layer. + :param clip_dim: dimension of clip feature. + """ + + def __init__( + self, + text_ctx, + xf_width, + xf_layers, + xf_heads, + xf_final_ln, + clip_dim, + ): + super().__init__() + + self.text_ctx = text_ctx + self.xf_width = xf_width + self.xf_layers = xf_layers + self.xf_heads = xf_heads + self.clip_dim = clip_dim + self.ext_len = 4 + + self.time_embed = nn.Sequential( + nn.Linear(xf_width, xf_width), + nn.SiLU(), + nn.Linear(xf_width, xf_width), + ) + self.text_enc_proj = nn.Linear(clip_dim, xf_width) + self.text_emb_proj = nn.Linear(clip_dim, xf_width) + self.clip_img_proj = nn.Linear(clip_dim, xf_width) + self.out_proj = nn.Linear(xf_width, clip_dim) + self.transformer = Transformer( + text_ctx + self.ext_len, + xf_width, + xf_layers, + xf_heads, + ) + if xf_final_ln: + self.final_ln = LayerNorm(xf_width) + else: + self.final_ln = None + + self.positional_embedding = nn.Parameter( + th.empty(1, text_ctx + self.ext_len, xf_width) + ) + self.prd_emb = nn.Parameter(th.randn((1, 1, xf_width))) + + nn.init.normal_(self.prd_emb, std=0.01) + nn.init.normal_(self.positional_embedding, std=0.01) + + def forward( + self, + x, + timesteps, + text_emb=None, + text_enc=None, + mask=None, + causal_mask=None, + ): + bsz = x.shape[0] + mask = F.pad(mask, (0, self.ext_len), value=True) + + t_emb = self.time_embed(timestep_embedding(timesteps, self.xf_width)) + text_enc = self.text_enc_proj(text_enc) + text_emb = self.text_emb_proj(text_emb) + x = self.clip_img_proj(x) + + input_seq = [ + text_enc, + text_emb[:, None, :], + t_emb[:, None, :], + x[:, None, :], + self.prd_emb.to(x.dtype).expand(bsz, -1, -1), + ] + input = th.cat(input_seq, dim=1) + input = input + self.positional_embedding.to(input.dtype) + + mask = th.where(mask, 0.0, float("-inf")) + mask = (mask[:, None, :] + causal_mask).to(input.dtype) + + out = self.transformer(input, mask=mask) + if self.final_ln is not None: + out = self.final_ln(out) + + out = self.out_proj(out[:, -1]) + + return out diff --git a/repositories/ldm/modules/karlo/kakao/sampler.py b/repositories/ldm/modules/karlo/kakao/sampler.py new file mode 100644 index 000000000..b56bf2f20 --- /dev/null +++ b/repositories/ldm/modules/karlo/kakao/sampler.py @@ -0,0 +1,272 @@ +# ------------------------------------------------------------------------------------ +# Karlo-v1.0.alpha +# Copyright (c) 2022 KakaoBrain. All Rights Reserved. + +# source: https://github.com/kakaobrain/karlo/blob/3c68a50a16d76b48a15c181d1c5a5e0879a90f85/karlo/sampler/t2i.py#L15 +# ------------------------------------------------------------------------------------ + +from typing import Iterator + +import torch +import torchvision.transforms.functional as TVF +from torchvision.transforms import InterpolationMode + +from .template import BaseSampler, CKPT_PATH + + +class T2ISampler(BaseSampler): + """ + A sampler for text-to-image generation. + :param root_dir: directory for model checkpoints. + :param sampling_type: ["default", "fast"] + """ + + def __init__( + self, + root_dir: str, + sampling_type: str = "default", + ): + super().__init__(root_dir, sampling_type) + + @classmethod + def from_pretrained( + cls, + root_dir: str, + clip_model_path: str, + clip_stat_path: str, + sampling_type: str = "default", + ): + + model = cls( + root_dir=root_dir, + sampling_type=sampling_type, + ) + model.load_clip(clip_model_path) + model.load_prior( + f"{CKPT_PATH['prior']}", + clip_stat_path=clip_stat_path, + prior_config="configs/karlo/prior_1B_vit_l.yaml" + ) + model.load_decoder(f"{CKPT_PATH['decoder']}", decoder_config="configs/karlo/decoder_900M_vit_l.yaml") + model.load_sr_64_256(CKPT_PATH["sr_256"], sr_config="configs/karlo/improved_sr_64_256_1.4B.yaml") + return model + + def preprocess( + self, + prompt: str, + bsz: int, + ): + """Setup prompts & cfg scales""" + prompts_batch = [prompt for _ in range(bsz)] + + prior_cf_scales_batch = [self._prior_cf_scale] * len(prompts_batch) + prior_cf_scales_batch = torch.tensor(prior_cf_scales_batch, device="cuda") + + decoder_cf_scales_batch = [self._decoder_cf_scale] * len(prompts_batch) + decoder_cf_scales_batch = torch.tensor(decoder_cf_scales_batch, device="cuda") + + """ Get CLIP text feature """ + clip_model = self._clip + tokenizer = self._tokenizer + max_txt_length = self._prior.model.text_ctx + + tok, mask = tokenizer.padded_tokens_and_mask(prompts_batch, max_txt_length) + cf_token, cf_mask = tokenizer.padded_tokens_and_mask([""], max_txt_length) + if not (cf_token.shape == tok.shape): + cf_token = cf_token.expand(tok.shape[0], -1) + cf_mask = cf_mask.expand(tok.shape[0], -1) + + tok = torch.cat([tok, cf_token], dim=0) + mask = torch.cat([mask, cf_mask], dim=0) + + tok, mask = tok.to(device="cuda"), mask.to(device="cuda") + txt_feat, txt_feat_seq = clip_model.encode_text(tok) + + return ( + prompts_batch, + prior_cf_scales_batch, + decoder_cf_scales_batch, + txt_feat, + txt_feat_seq, + tok, + mask, + ) + + def __call__( + self, + prompt: str, + bsz: int, + progressive_mode=None, + ) -> Iterator[torch.Tensor]: + assert progressive_mode in ("loop", "stage", "final") + with torch.no_grad(), torch.cuda.amp.autocast(): + ( + prompts_batch, + prior_cf_scales_batch, + decoder_cf_scales_batch, + txt_feat, + txt_feat_seq, + tok, + mask, + ) = self.preprocess( + prompt, + bsz, + ) + + """ Transform CLIP text feature into image feature """ + img_feat = self._prior( + txt_feat, + txt_feat_seq, + mask, + prior_cf_scales_batch, + timestep_respacing=self._prior_sm, + ) + + """ Generate 64x64px images """ + images_64_outputs = self._decoder( + txt_feat, + txt_feat_seq, + tok, + mask, + img_feat, + cf_guidance_scales=decoder_cf_scales_batch, + timestep_respacing=self._decoder_sm, + ) + + images_64 = None + for k, out in enumerate(images_64_outputs): + images_64 = out + if progressive_mode == "loop": + yield torch.clamp(out * 0.5 + 0.5, 0.0, 1.0) + if progressive_mode == "stage": + yield torch.clamp(out * 0.5 + 0.5, 0.0, 1.0) + + images_64 = torch.clamp(images_64, -1, 1) + + """ Upsample 64x64 to 256x256 """ + images_256 = TVF.resize( + images_64, + [256, 256], + interpolation=InterpolationMode.BICUBIC, + antialias=True, + ) + images_256_outputs = self._sr_64_256( + images_256, timestep_respacing=self._sr_sm + ) + + for k, out in enumerate(images_256_outputs): + images_256 = out + if progressive_mode == "loop": + yield torch.clamp(out * 0.5 + 0.5, 0.0, 1.0) + if progressive_mode == "stage": + yield torch.clamp(out * 0.5 + 0.5, 0.0, 1.0) + + yield torch.clamp(images_256 * 0.5 + 0.5, 0.0, 1.0) + + +class PriorSampler(BaseSampler): + """ + A sampler for text-to-image generation, but only the prior. + :param root_dir: directory for model checkpoints. + :param sampling_type: ["default", "fast"] + """ + + def __init__( + self, + root_dir: str, + sampling_type: str = "default", + ): + super().__init__(root_dir, sampling_type) + + @classmethod + def from_pretrained( + cls, + root_dir: str, + clip_model_path: str, + clip_stat_path: str, + sampling_type: str = "default", + ): + model = cls( + root_dir=root_dir, + sampling_type=sampling_type, + ) + model.load_clip(clip_model_path) + model.load_prior( + f"{CKPT_PATH['prior']}", + clip_stat_path=clip_stat_path, + prior_config="configs/karlo/prior_1B_vit_l.yaml" + ) + return model + + def preprocess( + self, + prompt: str, + bsz: int, + ): + """Setup prompts & cfg scales""" + prompts_batch = [prompt for _ in range(bsz)] + + prior_cf_scales_batch = [self._prior_cf_scale] * len(prompts_batch) + prior_cf_scales_batch = torch.tensor(prior_cf_scales_batch, device="cuda") + + decoder_cf_scales_batch = [self._decoder_cf_scale] * len(prompts_batch) + decoder_cf_scales_batch = torch.tensor(decoder_cf_scales_batch, device="cuda") + + """ Get CLIP text feature """ + clip_model = self._clip + tokenizer = self._tokenizer + max_txt_length = self._prior.model.text_ctx + + tok, mask = tokenizer.padded_tokens_and_mask(prompts_batch, max_txt_length) + cf_token, cf_mask = tokenizer.padded_tokens_and_mask([""], max_txt_length) + if not (cf_token.shape == tok.shape): + cf_token = cf_token.expand(tok.shape[0], -1) + cf_mask = cf_mask.expand(tok.shape[0], -1) + + tok = torch.cat([tok, cf_token], dim=0) + mask = torch.cat([mask, cf_mask], dim=0) + + tok, mask = tok.to(device="cuda"), mask.to(device="cuda") + txt_feat, txt_feat_seq = clip_model.encode_text(tok) + + return ( + prompts_batch, + prior_cf_scales_batch, + decoder_cf_scales_batch, + txt_feat, + txt_feat_seq, + tok, + mask, + ) + + def __call__( + self, + prompt: str, + bsz: int, + progressive_mode=None, + ) -> Iterator[torch.Tensor]: + assert progressive_mode in ("loop", "stage", "final") + with torch.no_grad(), torch.cuda.amp.autocast(): + ( + prompts_batch, + prior_cf_scales_batch, + decoder_cf_scales_batch, + txt_feat, + txt_feat_seq, + tok, + mask, + ) = self.preprocess( + prompt, + bsz, + ) + + """ Transform CLIP text feature into image feature """ + img_feat = self._prior( + txt_feat, + txt_feat_seq, + mask, + prior_cf_scales_batch, + timestep_respacing=self._prior_sm, + ) + + yield img_feat diff --git a/repositories/ldm/modules/karlo/kakao/template.py b/repositories/ldm/modules/karlo/kakao/template.py new file mode 100644 index 000000000..949e80e67 --- /dev/null +++ b/repositories/ldm/modules/karlo/kakao/template.py @@ -0,0 +1,141 @@ +# ------------------------------------------------------------------------------------ +# Karlo-v1.0.alpha +# Copyright (c) 2022 KakaoBrain. All Rights Reserved. +# ------------------------------------------------------------------------------------ + +import os +import logging +import torch + +from omegaconf import OmegaConf + +from ldm.modules.karlo.kakao.models.clip import CustomizedCLIP, CustomizedTokenizer +from ldm.modules.karlo.kakao.models.prior_model import PriorDiffusionModel +from ldm.modules.karlo.kakao.models.decoder_model import Text2ImProgressiveModel +from ldm.modules.karlo.kakao.models.sr_64_256 import ImprovedSupRes64to256ProgressiveModel + + +SAMPLING_CONF = { + "default": { + "prior_sm": "25", + "prior_n_samples": 1, + "prior_cf_scale": 4.0, + "decoder_sm": "50", + "decoder_cf_scale": 8.0, + "sr_sm": "7", + }, + "fast": { + "prior_sm": "25", + "prior_n_samples": 1, + "prior_cf_scale": 4.0, + "decoder_sm": "25", + "decoder_cf_scale": 8.0, + "sr_sm": "7", + }, +} + +CKPT_PATH = { + "prior": "prior-ckpt-step=01000000-of-01000000.ckpt", + "decoder": "decoder-ckpt-step=01000000-of-01000000.ckpt", + "sr_256": "improved-sr-ckpt-step=1.2M.ckpt", +} + + +class BaseSampler: + _PRIOR_CLASS = PriorDiffusionModel + _DECODER_CLASS = Text2ImProgressiveModel + _SR256_CLASS = ImprovedSupRes64to256ProgressiveModel + + def __init__( + self, + root_dir: str, + sampling_type: str = "fast", + ): + self._root_dir = root_dir + + sampling_type = SAMPLING_CONF[sampling_type] + self._prior_sm = sampling_type["prior_sm"] + self._prior_n_samples = sampling_type["prior_n_samples"] + self._prior_cf_scale = sampling_type["prior_cf_scale"] + + assert self._prior_n_samples == 1 + + self._decoder_sm = sampling_type["decoder_sm"] + self._decoder_cf_scale = sampling_type["decoder_cf_scale"] + + self._sr_sm = sampling_type["sr_sm"] + + def __repr__(self): + line = "" + line += f"Prior, sampling method: {self._prior_sm}, cf_scale: {self._prior_cf_scale}\n" + line += f"Decoder, sampling method: {self._decoder_sm}, cf_scale: {self._decoder_cf_scale}\n" + line += f"SR(64->256), sampling method: {self._sr_sm}" + + return line + + def load_clip(self, clip_path: str): + clip = CustomizedCLIP.load_from_checkpoint( + os.path.join(self._root_dir, clip_path) + ) + clip = torch.jit.script(clip) + clip.cuda() + clip.eval() + + self._clip = clip + self._tokenizer = CustomizedTokenizer() + + def load_prior( + self, + ckpt_path: str, + clip_stat_path: str, + prior_config: str = "configs/prior_1B_vit_l.yaml" + ): + logging.info(f"Loading prior: {ckpt_path}") + + config = OmegaConf.load(prior_config) + clip_mean, clip_std = torch.load( + os.path.join(self._root_dir, clip_stat_path), map_location="cpu" + ) + + prior = self._PRIOR_CLASS.load_from_checkpoint( + config, + self._tokenizer, + clip_mean, + clip_std, + os.path.join(self._root_dir, ckpt_path), + strict=True, + ) + prior.cuda() + prior.eval() + logging.info("done.") + + self._prior = prior + + def load_decoder(self, ckpt_path: str, decoder_config: str = "configs/decoder_900M_vit_l.yaml"): + logging.info(f"Loading decoder: {ckpt_path}") + + config = OmegaConf.load(decoder_config) + decoder = self._DECODER_CLASS.load_from_checkpoint( + config, + self._tokenizer, + os.path.join(self._root_dir, ckpt_path), + strict=True, + ) + decoder.cuda() + decoder.eval() + logging.info("done.") + + self._decoder = decoder + + def load_sr_64_256(self, ckpt_path: str, sr_config: str = "configs/improved_sr_64_256_1.4B.yaml"): + logging.info(f"Loading SR(64->256): {ckpt_path}") + + config = OmegaConf.load(sr_config) + sr = self._SR256_CLASS.load_from_checkpoint( + config, os.path.join(self._root_dir, ckpt_path), strict=True + ) + sr.cuda() + sr.eval() + logging.info("done.") + + self._sr_64_256 = sr \ No newline at end of file diff --git a/repositories/ldm/modules/midas/__init__.py b/repositories/ldm/modules/midas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/ldm/modules/midas/api.py b/repositories/ldm/modules/midas/api.py new file mode 100644 index 000000000..b58ebbffd --- /dev/null +++ b/repositories/ldm/modules/midas/api.py @@ -0,0 +1,170 @@ +# based on https://github.com/isl-org/MiDaS + +import cv2 +import torch +import torch.nn as nn +from torchvision.transforms import Compose + +from ldm.modules.midas.midas.dpt_depth import DPTDepthModel +from ldm.modules.midas.midas.midas_net import MidasNet +from ldm.modules.midas.midas.midas_net_custom import MidasNet_small +from ldm.modules.midas.midas.transforms import Resize, NormalizeImage, PrepareForNet + + +ISL_PATHS = { + "dpt_large": "midas_models/dpt_large-midas-2f21e586.pt", + "dpt_hybrid": "midas_models/dpt_hybrid-midas-501f0c75.pt", + "midas_v21": "", + "midas_v21_small": "", +} + + +def disabled_train(self, mode=True): + """Overwrite model.train with this function to make sure train/eval mode + does not change anymore.""" + return self + + +def load_midas_transform(model_type): + # https://github.com/isl-org/MiDaS/blob/master/run.py + # load transform only + if model_type == "dpt_large": # DPT-Large + net_w, net_h = 384, 384 + resize_mode = "minimal" + normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) + + elif model_type == "dpt_hybrid": # DPT-Hybrid + net_w, net_h = 384, 384 + resize_mode = "minimal" + normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) + + elif model_type == "midas_v21": + net_w, net_h = 384, 384 + resize_mode = "upper_bound" + normalization = NormalizeImage(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) + + elif model_type == "midas_v21_small": + net_w, net_h = 256, 256 + resize_mode = "upper_bound" + normalization = NormalizeImage(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) + + else: + assert False, f"model_type '{model_type}' not implemented, use: --model_type large" + + transform = Compose( + [ + Resize( + net_w, + net_h, + resize_target=None, + keep_aspect_ratio=True, + ensure_multiple_of=32, + resize_method=resize_mode, + image_interpolation_method=cv2.INTER_CUBIC, + ), + normalization, + PrepareForNet(), + ] + ) + + return transform + + +def load_model(model_type): + # https://github.com/isl-org/MiDaS/blob/master/run.py + # load network + model_path = ISL_PATHS[model_type] + if model_type == "dpt_large": # DPT-Large + model = DPTDepthModel( + path=model_path, + backbone="vitl16_384", + non_negative=True, + ) + net_w, net_h = 384, 384 + resize_mode = "minimal" + normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) + + elif model_type == "dpt_hybrid": # DPT-Hybrid + model = DPTDepthModel( + path=model_path, + backbone="vitb_rn50_384", + non_negative=True, + ) + net_w, net_h = 384, 384 + resize_mode = "minimal" + normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) + + elif model_type == "midas_v21": + model = MidasNet(model_path, non_negative=True) + net_w, net_h = 384, 384 + resize_mode = "upper_bound" + normalization = NormalizeImage( + mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] + ) + + elif model_type == "midas_v21_small": + model = MidasNet_small(model_path, features=64, backbone="efficientnet_lite3", exportable=True, + non_negative=True, blocks={'expand': True}) + net_w, net_h = 256, 256 + resize_mode = "upper_bound" + normalization = NormalizeImage( + mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] + ) + + else: + print(f"model_type '{model_type}' not implemented, use: --model_type large") + assert False + + transform = Compose( + [ + Resize( + net_w, + net_h, + resize_target=None, + keep_aspect_ratio=True, + ensure_multiple_of=32, + resize_method=resize_mode, + image_interpolation_method=cv2.INTER_CUBIC, + ), + normalization, + PrepareForNet(), + ] + ) + + return model.eval(), transform + + +class MiDaSInference(nn.Module): + MODEL_TYPES_TORCH_HUB = [ + "DPT_Large", + "DPT_Hybrid", + "MiDaS_small" + ] + MODEL_TYPES_ISL = [ + "dpt_large", + "dpt_hybrid", + "midas_v21", + "midas_v21_small", + ] + + def __init__(self, model_type): + super().__init__() + assert (model_type in self.MODEL_TYPES_ISL) + model, _ = load_model(model_type) + self.model = model + self.model.train = disabled_train + + def forward(self, x): + # x in 0..1 as produced by calling self.transform on a 0..1 float64 numpy array + # NOTE: we expect that the correct transform has been called during dataloading. + with torch.no_grad(): + prediction = self.model(x) + prediction = torch.nn.functional.interpolate( + prediction.unsqueeze(1), + size=x.shape[2:], + mode="bicubic", + align_corners=False, + ) + assert prediction.shape == (x.shape[0], 1, x.shape[2], x.shape[3]) + return prediction + diff --git a/repositories/ldm/modules/midas/midas/__init__.py b/repositories/ldm/modules/midas/midas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/repositories/ldm/modules/midas/midas/base_model.py b/repositories/ldm/modules/midas/midas/base_model.py new file mode 100644 index 000000000..5cf430239 --- /dev/null +++ b/repositories/ldm/modules/midas/midas/base_model.py @@ -0,0 +1,16 @@ +import torch + + +class BaseModel(torch.nn.Module): + def load(self, path): + """Load model from file. + + Args: + path (str): file path + """ + parameters = torch.load(path, map_location=torch.device('cpu')) + + if "optimizer" in parameters: + parameters = parameters["model"] + + self.load_state_dict(parameters) diff --git a/repositories/ldm/modules/midas/midas/blocks.py b/repositories/ldm/modules/midas/midas/blocks.py new file mode 100644 index 000000000..2145d18fa --- /dev/null +++ b/repositories/ldm/modules/midas/midas/blocks.py @@ -0,0 +1,342 @@ +import torch +import torch.nn as nn + +from .vit import ( + _make_pretrained_vitb_rn50_384, + _make_pretrained_vitl16_384, + _make_pretrained_vitb16_384, + forward_vit, +) + +def _make_encoder(backbone, features, use_pretrained, groups=1, expand=False, exportable=True, hooks=None, use_vit_only=False, use_readout="ignore",): + if backbone == "vitl16_384": + pretrained = _make_pretrained_vitl16_384( + use_pretrained, hooks=hooks, use_readout=use_readout + ) + scratch = _make_scratch( + [256, 512, 1024, 1024], features, groups=groups, expand=expand + ) # ViT-L/16 - 85.0% Top1 (backbone) + elif backbone == "vitb_rn50_384": + pretrained = _make_pretrained_vitb_rn50_384( + use_pretrained, + hooks=hooks, + use_vit_only=use_vit_only, + use_readout=use_readout, + ) + scratch = _make_scratch( + [256, 512, 768, 768], features, groups=groups, expand=expand + ) # ViT-H/16 - 85.0% Top1 (backbone) + elif backbone == "vitb16_384": + pretrained = _make_pretrained_vitb16_384( + use_pretrained, hooks=hooks, use_readout=use_readout + ) + scratch = _make_scratch( + [96, 192, 384, 768], features, groups=groups, expand=expand + ) # ViT-B/16 - 84.6% Top1 (backbone) + elif backbone == "resnext101_wsl": + pretrained = _make_pretrained_resnext101_wsl(use_pretrained) + scratch = _make_scratch([256, 512, 1024, 2048], features, groups=groups, expand=expand) # efficientnet_lite3 + elif backbone == "efficientnet_lite3": + pretrained = _make_pretrained_efficientnet_lite3(use_pretrained, exportable=exportable) + scratch = _make_scratch([32, 48, 136, 384], features, groups=groups, expand=expand) # efficientnet_lite3 + else: + print(f"Backbone '{backbone}' not implemented") + assert False + + return pretrained, scratch + + +def _make_scratch(in_shape, out_shape, groups=1, expand=False): + scratch = nn.Module() + + out_shape1 = out_shape + out_shape2 = out_shape + out_shape3 = out_shape + out_shape4 = out_shape + if expand==True: + out_shape1 = out_shape + out_shape2 = out_shape*2 + out_shape3 = out_shape*4 + out_shape4 = out_shape*8 + + scratch.layer1_rn = nn.Conv2d( + in_shape[0], out_shape1, kernel_size=3, stride=1, padding=1, bias=False, groups=groups + ) + scratch.layer2_rn = nn.Conv2d( + in_shape[1], out_shape2, kernel_size=3, stride=1, padding=1, bias=False, groups=groups + ) + scratch.layer3_rn = nn.Conv2d( + in_shape[2], out_shape3, kernel_size=3, stride=1, padding=1, bias=False, groups=groups + ) + scratch.layer4_rn = nn.Conv2d( + in_shape[3], out_shape4, kernel_size=3, stride=1, padding=1, bias=False, groups=groups + ) + + return scratch + + +def _make_pretrained_efficientnet_lite3(use_pretrained, exportable=False): + efficientnet = torch.hub.load( + "rwightman/gen-efficientnet-pytorch", + "tf_efficientnet_lite3", + pretrained=use_pretrained, + exportable=exportable + ) + return _make_efficientnet_backbone(efficientnet) + + +def _make_efficientnet_backbone(effnet): + pretrained = nn.Module() + + pretrained.layer1 = nn.Sequential( + effnet.conv_stem, effnet.bn1, effnet.act1, *effnet.blocks[0:2] + ) + pretrained.layer2 = nn.Sequential(*effnet.blocks[2:3]) + pretrained.layer3 = nn.Sequential(*effnet.blocks[3:5]) + pretrained.layer4 = nn.Sequential(*effnet.blocks[5:9]) + + return pretrained + + +def _make_resnet_backbone(resnet): + pretrained = nn.Module() + pretrained.layer1 = nn.Sequential( + resnet.conv1, resnet.bn1, resnet.relu, resnet.maxpool, resnet.layer1 + ) + + pretrained.layer2 = resnet.layer2 + pretrained.layer3 = resnet.layer3 + pretrained.layer4 = resnet.layer4 + + return pretrained + + +def _make_pretrained_resnext101_wsl(use_pretrained): + resnet = torch.hub.load("facebookresearch/WSL-Images", "resnext101_32x8d_wsl") + return _make_resnet_backbone(resnet) + + + +class Interpolate(nn.Module): + """Interpolation module. + """ + + def __init__(self, scale_factor, mode, align_corners=False): + """Init. + + Args: + scale_factor (float): scaling + mode (str): interpolation mode + """ + super(Interpolate, self).__init__() + + self.interp = nn.functional.interpolate + self.scale_factor = scale_factor + self.mode = mode + self.align_corners = align_corners + + def forward(self, x): + """Forward pass. + + Args: + x (tensor): input + + Returns: + tensor: interpolated data + """ + + x = self.interp( + x, scale_factor=self.scale_factor, mode=self.mode, align_corners=self.align_corners + ) + + return x + + +class ResidualConvUnit(nn.Module): + """Residual convolution module. + """ + + def __init__(self, features): + """Init. + + Args: + features (int): number of features + """ + super().__init__() + + self.conv1 = nn.Conv2d( + features, features, kernel_size=3, stride=1, padding=1, bias=True + ) + + self.conv2 = nn.Conv2d( + features, features, kernel_size=3, stride=1, padding=1, bias=True + ) + + self.relu = nn.ReLU(inplace=True) + + def forward(self, x): + """Forward pass. + + Args: + x (tensor): input + + Returns: + tensor: output + """ + out = self.relu(x) + out = self.conv1(out) + out = self.relu(out) + out = self.conv2(out) + + return out + x + + +class FeatureFusionBlock(nn.Module): + """Feature fusion block. + """ + + def __init__(self, features): + """Init. + + Args: + features (int): number of features + """ + super(FeatureFusionBlock, self).__init__() + + self.resConfUnit1 = ResidualConvUnit(features) + self.resConfUnit2 = ResidualConvUnit(features) + + def forward(self, *xs): + """Forward pass. + + Returns: + tensor: output + """ + output = xs[0] + + if len(xs) == 2: + output += self.resConfUnit1(xs[1]) + + output = self.resConfUnit2(output) + + output = nn.functional.interpolate( + output, scale_factor=2, mode="bilinear", align_corners=True + ) + + return output + + + + +class ResidualConvUnit_custom(nn.Module): + """Residual convolution module. + """ + + def __init__(self, features, activation, bn): + """Init. + + Args: + features (int): number of features + """ + super().__init__() + + self.bn = bn + + self.groups=1 + + self.conv1 = nn.Conv2d( + features, features, kernel_size=3, stride=1, padding=1, bias=True, groups=self.groups + ) + + self.conv2 = nn.Conv2d( + features, features, kernel_size=3, stride=1, padding=1, bias=True, groups=self.groups + ) + + if self.bn==True: + self.bn1 = nn.BatchNorm2d(features) + self.bn2 = nn.BatchNorm2d(features) + + self.activation = activation + + self.skip_add = nn.quantized.FloatFunctional() + + def forward(self, x): + """Forward pass. + + Args: + x (tensor): input + + Returns: + tensor: output + """ + + out = self.activation(x) + out = self.conv1(out) + if self.bn==True: + out = self.bn1(out) + + out = self.activation(out) + out = self.conv2(out) + if self.bn==True: + out = self.bn2(out) + + if self.groups > 1: + out = self.conv_merge(out) + + return self.skip_add.add(out, x) + + # return out + x + + +class FeatureFusionBlock_custom(nn.Module): + """Feature fusion block. + """ + + def __init__(self, features, activation, deconv=False, bn=False, expand=False, align_corners=True): + """Init. + + Args: + features (int): number of features + """ + super(FeatureFusionBlock_custom, self).__init__() + + self.deconv = deconv + self.align_corners = align_corners + + self.groups=1 + + self.expand = expand + out_features = features + if self.expand==True: + out_features = features//2 + + self.out_conv = nn.Conv2d(features, out_features, kernel_size=1, stride=1, padding=0, bias=True, groups=1) + + self.resConfUnit1 = ResidualConvUnit_custom(features, activation, bn) + self.resConfUnit2 = ResidualConvUnit_custom(features, activation, bn) + + self.skip_add = nn.quantized.FloatFunctional() + + def forward(self, *xs): + """Forward pass. + + Returns: + tensor: output + """ + output = xs[0] + + if len(xs) == 2: + res = self.resConfUnit1(xs[1]) + output = self.skip_add.add(output, res) + # output += res + + output = self.resConfUnit2(output) + + output = nn.functional.interpolate( + output, scale_factor=2, mode="bilinear", align_corners=self.align_corners + ) + + output = self.out_conv(output) + + return output + diff --git a/repositories/ldm/modules/midas/midas/dpt_depth.py b/repositories/ldm/modules/midas/midas/dpt_depth.py new file mode 100644 index 000000000..4e9aab5d2 --- /dev/null +++ b/repositories/ldm/modules/midas/midas/dpt_depth.py @@ -0,0 +1,109 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + +from .base_model import BaseModel +from .blocks import ( + FeatureFusionBlock, + FeatureFusionBlock_custom, + Interpolate, + _make_encoder, + forward_vit, +) + + +def _make_fusion_block(features, use_bn): + return FeatureFusionBlock_custom( + features, + nn.ReLU(False), + deconv=False, + bn=use_bn, + expand=False, + align_corners=True, + ) + + +class DPT(BaseModel): + def __init__( + self, + head, + features=256, + backbone="vitb_rn50_384", + readout="project", + channels_last=False, + use_bn=False, + ): + + super(DPT, self).__init__() + + self.channels_last = channels_last + + hooks = { + "vitb_rn50_384": [0, 1, 8, 11], + "vitb16_384": [2, 5, 8, 11], + "vitl16_384": [5, 11, 17, 23], + } + + # Instantiate backbone and reassemble blocks + self.pretrained, self.scratch = _make_encoder( + backbone, + features, + False, # Set to true of you want to train from scratch, uses ImageNet weights + groups=1, + expand=False, + exportable=False, + hooks=hooks[backbone], + use_readout=readout, + ) + + self.scratch.refinenet1 = _make_fusion_block(features, use_bn) + self.scratch.refinenet2 = _make_fusion_block(features, use_bn) + self.scratch.refinenet3 = _make_fusion_block(features, use_bn) + self.scratch.refinenet4 = _make_fusion_block(features, use_bn) + + self.scratch.output_conv = head + + + def forward(self, x): + if self.channels_last == True: + x.contiguous(memory_format=torch.channels_last) + + layer_1, layer_2, layer_3, layer_4 = forward_vit(self.pretrained, x) + + layer_1_rn = self.scratch.layer1_rn(layer_1) + layer_2_rn = self.scratch.layer2_rn(layer_2) + layer_3_rn = self.scratch.layer3_rn(layer_3) + layer_4_rn = self.scratch.layer4_rn(layer_4) + + path_4 = self.scratch.refinenet4(layer_4_rn) + path_3 = self.scratch.refinenet3(path_4, layer_3_rn) + path_2 = self.scratch.refinenet2(path_3, layer_2_rn) + path_1 = self.scratch.refinenet1(path_2, layer_1_rn) + + out = self.scratch.output_conv(path_1) + + return out + + +class DPTDepthModel(DPT): + def __init__(self, path=None, non_negative=True, **kwargs): + features = kwargs["features"] if "features" in kwargs else 256 + + head = nn.Sequential( + nn.Conv2d(features, features // 2, kernel_size=3, stride=1, padding=1), + Interpolate(scale_factor=2, mode="bilinear", align_corners=True), + nn.Conv2d(features // 2, 32, kernel_size=3, stride=1, padding=1), + nn.ReLU(True), + nn.Conv2d(32, 1, kernel_size=1, stride=1, padding=0), + nn.ReLU(True) if non_negative else nn.Identity(), + nn.Identity(), + ) + + super().__init__(head, **kwargs) + + if path is not None: + self.load(path) + + def forward(self, x): + return super().forward(x).squeeze(dim=1) + diff --git a/repositories/ldm/modules/midas/midas/midas_net.py b/repositories/ldm/modules/midas/midas/midas_net.py new file mode 100644 index 000000000..8a9549778 --- /dev/null +++ b/repositories/ldm/modules/midas/midas/midas_net.py @@ -0,0 +1,76 @@ +"""MidashNet: Network for monocular depth estimation trained by mixing several datasets. +This file contains code that is adapted from +https://github.com/thomasjpfan/pytorch_refinenet/blob/master/pytorch_refinenet/refinenet/refinenet_4cascade.py +""" +import torch +import torch.nn as nn + +from .base_model import BaseModel +from .blocks import FeatureFusionBlock, Interpolate, _make_encoder + + +class MidasNet(BaseModel): + """Network for monocular depth estimation. + """ + + def __init__(self, path=None, features=256, non_negative=True): + """Init. + + Args: + path (str, optional): Path to saved model. Defaults to None. + features (int, optional): Number of features. Defaults to 256. + backbone (str, optional): Backbone network for encoder. Defaults to resnet50 + """ + print("Loading weights: ", path) + + super(MidasNet, self).__init__() + + use_pretrained = False if path is None else True + + self.pretrained, self.scratch = _make_encoder(backbone="resnext101_wsl", features=features, use_pretrained=use_pretrained) + + self.scratch.refinenet4 = FeatureFusionBlock(features) + self.scratch.refinenet3 = FeatureFusionBlock(features) + self.scratch.refinenet2 = FeatureFusionBlock(features) + self.scratch.refinenet1 = FeatureFusionBlock(features) + + self.scratch.output_conv = nn.Sequential( + nn.Conv2d(features, 128, kernel_size=3, stride=1, padding=1), + Interpolate(scale_factor=2, mode="bilinear"), + nn.Conv2d(128, 32, kernel_size=3, stride=1, padding=1), + nn.ReLU(True), + nn.Conv2d(32, 1, kernel_size=1, stride=1, padding=0), + nn.ReLU(True) if non_negative else nn.Identity(), + ) + + if path: + self.load(path) + + def forward(self, x): + """Forward pass. + + Args: + x (tensor): input data (image) + + Returns: + tensor: depth + """ + + layer_1 = self.pretrained.layer1(x) + layer_2 = self.pretrained.layer2(layer_1) + layer_3 = self.pretrained.layer3(layer_2) + layer_4 = self.pretrained.layer4(layer_3) + + layer_1_rn = self.scratch.layer1_rn(layer_1) + layer_2_rn = self.scratch.layer2_rn(layer_2) + layer_3_rn = self.scratch.layer3_rn(layer_3) + layer_4_rn = self.scratch.layer4_rn(layer_4) + + path_4 = self.scratch.refinenet4(layer_4_rn) + path_3 = self.scratch.refinenet3(path_4, layer_3_rn) + path_2 = self.scratch.refinenet2(path_3, layer_2_rn) + path_1 = self.scratch.refinenet1(path_2, layer_1_rn) + + out = self.scratch.output_conv(path_1) + + return torch.squeeze(out, dim=1) diff --git a/repositories/ldm/modules/midas/midas/midas_net_custom.py b/repositories/ldm/modules/midas/midas/midas_net_custom.py new file mode 100644 index 000000000..50e4acb5e --- /dev/null +++ b/repositories/ldm/modules/midas/midas/midas_net_custom.py @@ -0,0 +1,128 @@ +"""MidashNet: Network for monocular depth estimation trained by mixing several datasets. +This file contains code that is adapted from +https://github.com/thomasjpfan/pytorch_refinenet/blob/master/pytorch_refinenet/refinenet/refinenet_4cascade.py +""" +import torch +import torch.nn as nn + +from .base_model import BaseModel +from .blocks import FeatureFusionBlock, FeatureFusionBlock_custom, Interpolate, _make_encoder + + +class MidasNet_small(BaseModel): + """Network for monocular depth estimation. + """ + + def __init__(self, path=None, features=64, backbone="efficientnet_lite3", non_negative=True, exportable=True, channels_last=False, align_corners=True, + blocks={'expand': True}): + """Init. + + Args: + path (str, optional): Path to saved model. Defaults to None. + features (int, optional): Number of features. Defaults to 256. + backbone (str, optional): Backbone network for encoder. Defaults to resnet50 + """ + print("Loading weights: ", path) + + super(MidasNet_small, self).__init__() + + use_pretrained = False if path else True + + self.channels_last = channels_last + self.blocks = blocks + self.backbone = backbone + + self.groups = 1 + + features1=features + features2=features + features3=features + features4=features + self.expand = False + if "expand" in self.blocks and self.blocks['expand'] == True: + self.expand = True + features1=features + features2=features*2 + features3=features*4 + features4=features*8 + + self.pretrained, self.scratch = _make_encoder(self.backbone, features, use_pretrained, groups=self.groups, expand=self.expand, exportable=exportable) + + self.scratch.activation = nn.ReLU(False) + + self.scratch.refinenet4 = FeatureFusionBlock_custom(features4, self.scratch.activation, deconv=False, bn=False, expand=self.expand, align_corners=align_corners) + self.scratch.refinenet3 = FeatureFusionBlock_custom(features3, self.scratch.activation, deconv=False, bn=False, expand=self.expand, align_corners=align_corners) + self.scratch.refinenet2 = FeatureFusionBlock_custom(features2, self.scratch.activation, deconv=False, bn=False, expand=self.expand, align_corners=align_corners) + self.scratch.refinenet1 = FeatureFusionBlock_custom(features1, self.scratch.activation, deconv=False, bn=False, align_corners=align_corners) + + + self.scratch.output_conv = nn.Sequential( + nn.Conv2d(features, features//2, kernel_size=3, stride=1, padding=1, groups=self.groups), + Interpolate(scale_factor=2, mode="bilinear"), + nn.Conv2d(features//2, 32, kernel_size=3, stride=1, padding=1), + self.scratch.activation, + nn.Conv2d(32, 1, kernel_size=1, stride=1, padding=0), + nn.ReLU(True) if non_negative else nn.Identity(), + nn.Identity(), + ) + + if path: + self.load(path) + + + def forward(self, x): + """Forward pass. + + Args: + x (tensor): input data (image) + + Returns: + tensor: depth + """ + if self.channels_last==True: + print("self.channels_last = ", self.channels_last) + x.contiguous(memory_format=torch.channels_last) + + + layer_1 = self.pretrained.layer1(x) + layer_2 = self.pretrained.layer2(layer_1) + layer_3 = self.pretrained.layer3(layer_2) + layer_4 = self.pretrained.layer4(layer_3) + + layer_1_rn = self.scratch.layer1_rn(layer_1) + layer_2_rn = self.scratch.layer2_rn(layer_2) + layer_3_rn = self.scratch.layer3_rn(layer_3) + layer_4_rn = self.scratch.layer4_rn(layer_4) + + + path_4 = self.scratch.refinenet4(layer_4_rn) + path_3 = self.scratch.refinenet3(path_4, layer_3_rn) + path_2 = self.scratch.refinenet2(path_3, layer_2_rn) + path_1 = self.scratch.refinenet1(path_2, layer_1_rn) + + out = self.scratch.output_conv(path_1) + + return torch.squeeze(out, dim=1) + + + +def fuse_model(m): + prev_previous_type = nn.Identity() + prev_previous_name = '' + previous_type = nn.Identity() + previous_name = '' + for name, module in m.named_modules(): + if prev_previous_type == nn.Conv2d and previous_type == nn.BatchNorm2d and type(module) == nn.ReLU: + # print("FUSED ", prev_previous_name, previous_name, name) + torch.quantization.fuse_modules(m, [prev_previous_name, previous_name, name], inplace=True) + elif prev_previous_type == nn.Conv2d and previous_type == nn.BatchNorm2d: + # print("FUSED ", prev_previous_name, previous_name) + torch.quantization.fuse_modules(m, [prev_previous_name, previous_name], inplace=True) + # elif previous_type == nn.Conv2d and type(module) == nn.ReLU: + # print("FUSED ", previous_name, name) + # torch.quantization.fuse_modules(m, [previous_name, name], inplace=True) + + prev_previous_type = previous_type + prev_previous_name = previous_name + previous_type = type(module) + previous_name = name \ No newline at end of file diff --git a/repositories/ldm/modules/midas/midas/transforms.py b/repositories/ldm/modules/midas/midas/transforms.py new file mode 100644 index 000000000..350cbc116 --- /dev/null +++ b/repositories/ldm/modules/midas/midas/transforms.py @@ -0,0 +1,234 @@ +import numpy as np +import cv2 +import math + + +def apply_min_size(sample, size, image_interpolation_method=cv2.INTER_AREA): + """Rezise the sample to ensure the given size. Keeps aspect ratio. + + Args: + sample (dict): sample + size (tuple): image size + + Returns: + tuple: new size + """ + shape = list(sample["disparity"].shape) + + if shape[0] >= size[0] and shape[1] >= size[1]: + return sample + + scale = [0, 0] + scale[0] = size[0] / shape[0] + scale[1] = size[1] / shape[1] + + scale = max(scale) + + shape[0] = math.ceil(scale * shape[0]) + shape[1] = math.ceil(scale * shape[1]) + + # resize + sample["image"] = cv2.resize( + sample["image"], tuple(shape[::-1]), interpolation=image_interpolation_method + ) + + sample["disparity"] = cv2.resize( + sample["disparity"], tuple(shape[::-1]), interpolation=cv2.INTER_NEAREST + ) + sample["mask"] = cv2.resize( + sample["mask"].astype(np.float32), + tuple(shape[::-1]), + interpolation=cv2.INTER_NEAREST, + ) + sample["mask"] = sample["mask"].astype(bool) + + return tuple(shape) + + +class Resize(object): + """Resize sample to given size (width, height). + """ + + def __init__( + self, + width, + height, + resize_target=True, + keep_aspect_ratio=False, + ensure_multiple_of=1, + resize_method="lower_bound", + image_interpolation_method=cv2.INTER_AREA, + ): + """Init. + + Args: + width (int): desired output width + height (int): desired output height + resize_target (bool, optional): + True: Resize the full sample (image, mask, target). + False: Resize image only. + Defaults to True. + keep_aspect_ratio (bool, optional): + True: Keep the aspect ratio of the input sample. + Output sample might not have the given width and height, and + resize behaviour depends on the parameter 'resize_method'. + Defaults to False. + ensure_multiple_of (int, optional): + Output width and height is constrained to be multiple of this parameter. + Defaults to 1. + resize_method (str, optional): + "lower_bound": Output will be at least as large as the given size. + "upper_bound": Output will be at max as large as the given size. (Output size might be smaller than given size.) + "minimal": Scale as least as possible. (Output size might be smaller than given size.) + Defaults to "lower_bound". + """ + self.__width = width + self.__height = height + + self.__resize_target = resize_target + self.__keep_aspect_ratio = keep_aspect_ratio + self.__multiple_of = ensure_multiple_of + self.__resize_method = resize_method + self.__image_interpolation_method = image_interpolation_method + + def constrain_to_multiple_of(self, x, min_val=0, max_val=None): + y = (np.round(x / self.__multiple_of) * self.__multiple_of).astype(int) + + if max_val is not None and y > max_val: + y = (np.floor(x / self.__multiple_of) * self.__multiple_of).astype(int) + + if y < min_val: + y = (np.ceil(x / self.__multiple_of) * self.__multiple_of).astype(int) + + return y + + def get_size(self, width, height): + # determine new height and width + scale_height = self.__height / height + scale_width = self.__width / width + + if self.__keep_aspect_ratio: + if self.__resize_method == "lower_bound": + # scale such that output size is lower bound + if scale_width > scale_height: + # fit width + scale_height = scale_width + else: + # fit height + scale_width = scale_height + elif self.__resize_method == "upper_bound": + # scale such that output size is upper bound + if scale_width < scale_height: + # fit width + scale_height = scale_width + else: + # fit height + scale_width = scale_height + elif self.__resize_method == "minimal": + # scale as least as possbile + if abs(1 - scale_width) < abs(1 - scale_height): + # fit width + scale_height = scale_width + else: + # fit height + scale_width = scale_height + else: + raise ValueError( + f"resize_method {self.__resize_method} not implemented" + ) + + if self.__resize_method == "lower_bound": + new_height = self.constrain_to_multiple_of( + scale_height * height, min_val=self.__height + ) + new_width = self.constrain_to_multiple_of( + scale_width * width, min_val=self.__width + ) + elif self.__resize_method == "upper_bound": + new_height = self.constrain_to_multiple_of( + scale_height * height, max_val=self.__height + ) + new_width = self.constrain_to_multiple_of( + scale_width * width, max_val=self.__width + ) + elif self.__resize_method == "minimal": + new_height = self.constrain_to_multiple_of(scale_height * height) + new_width = self.constrain_to_multiple_of(scale_width * width) + else: + raise ValueError(f"resize_method {self.__resize_method} not implemented") + + return (new_width, new_height) + + def __call__(self, sample): + width, height = self.get_size( + sample["image"].shape[1], sample["image"].shape[0] + ) + + # resize sample + sample["image"] = cv2.resize( + sample["image"], + (width, height), + interpolation=self.__image_interpolation_method, + ) + + if self.__resize_target: + if "disparity" in sample: + sample["disparity"] = cv2.resize( + sample["disparity"], + (width, height), + interpolation=cv2.INTER_NEAREST, + ) + + if "depth" in sample: + sample["depth"] = cv2.resize( + sample["depth"], (width, height), interpolation=cv2.INTER_NEAREST + ) + + sample["mask"] = cv2.resize( + sample["mask"].astype(np.float32), + (width, height), + interpolation=cv2.INTER_NEAREST, + ) + sample["mask"] = sample["mask"].astype(bool) + + return sample + + +class NormalizeImage(object): + """Normlize image by given mean and std. + """ + + def __init__(self, mean, std): + self.__mean = mean + self.__std = std + + def __call__(self, sample): + sample["image"] = (sample["image"] - self.__mean) / self.__std + + return sample + + +class PrepareForNet(object): + """Prepare sample for usage as network input. + """ + + def __init__(self): + pass + + def __call__(self, sample): + image = np.transpose(sample["image"], (2, 0, 1)) + sample["image"] = np.ascontiguousarray(image).astype(np.float32) + + if "mask" in sample: + sample["mask"] = sample["mask"].astype(np.float32) + sample["mask"] = np.ascontiguousarray(sample["mask"]) + + if "disparity" in sample: + disparity = sample["disparity"].astype(np.float32) + sample["disparity"] = np.ascontiguousarray(disparity) + + if "depth" in sample: + depth = sample["depth"].astype(np.float32) + sample["depth"] = np.ascontiguousarray(depth) + + return sample diff --git a/repositories/ldm/modules/midas/midas/vit.py b/repositories/ldm/modules/midas/midas/vit.py new file mode 100644 index 000000000..ea46b1be8 --- /dev/null +++ b/repositories/ldm/modules/midas/midas/vit.py @@ -0,0 +1,491 @@ +import torch +import torch.nn as nn +import timm +import types +import math +import torch.nn.functional as F + + +class Slice(nn.Module): + def __init__(self, start_index=1): + super(Slice, self).__init__() + self.start_index = start_index + + def forward(self, x): + return x[:, self.start_index :] + + +class AddReadout(nn.Module): + def __init__(self, start_index=1): + super(AddReadout, self).__init__() + self.start_index = start_index + + def forward(self, x): + if self.start_index == 2: + readout = (x[:, 0] + x[:, 1]) / 2 + else: + readout = x[:, 0] + return x[:, self.start_index :] + readout.unsqueeze(1) + + +class ProjectReadout(nn.Module): + def __init__(self, in_features, start_index=1): + super(ProjectReadout, self).__init__() + self.start_index = start_index + + self.project = nn.Sequential(nn.Linear(2 * in_features, in_features), nn.GELU()) + + def forward(self, x): + readout = x[:, 0].unsqueeze(1).expand_as(x[:, self.start_index :]) + features = torch.cat((x[:, self.start_index :], readout), -1) + + return self.project(features) + + +class Transpose(nn.Module): + def __init__(self, dim0, dim1): + super(Transpose, self).__init__() + self.dim0 = dim0 + self.dim1 = dim1 + + def forward(self, x): + x = x.transpose(self.dim0, self.dim1) + return x + + +def forward_vit(pretrained, x): + b, c, h, w = x.shape + + glob = pretrained.model.forward_flex(x) + + layer_1 = pretrained.activations["1"] + layer_2 = pretrained.activations["2"] + layer_3 = pretrained.activations["3"] + layer_4 = pretrained.activations["4"] + + layer_1 = pretrained.act_postprocess1[0:2](layer_1) + layer_2 = pretrained.act_postprocess2[0:2](layer_2) + layer_3 = pretrained.act_postprocess3[0:2](layer_3) + layer_4 = pretrained.act_postprocess4[0:2](layer_4) + + unflatten = nn.Sequential( + nn.Unflatten( + 2, + torch.Size( + [ + h // pretrained.model.patch_size[1], + w // pretrained.model.patch_size[0], + ] + ), + ) + ) + + if layer_1.ndim == 3: + layer_1 = unflatten(layer_1) + if layer_2.ndim == 3: + layer_2 = unflatten(layer_2) + if layer_3.ndim == 3: + layer_3 = unflatten(layer_3) + if layer_4.ndim == 3: + layer_4 = unflatten(layer_4) + + layer_1 = pretrained.act_postprocess1[3 : len(pretrained.act_postprocess1)](layer_1) + layer_2 = pretrained.act_postprocess2[3 : len(pretrained.act_postprocess2)](layer_2) + layer_3 = pretrained.act_postprocess3[3 : len(pretrained.act_postprocess3)](layer_3) + layer_4 = pretrained.act_postprocess4[3 : len(pretrained.act_postprocess4)](layer_4) + + return layer_1, layer_2, layer_3, layer_4 + + +def _resize_pos_embed(self, posemb, gs_h, gs_w): + posemb_tok, posemb_grid = ( + posemb[:, : self.start_index], + posemb[0, self.start_index :], + ) + + gs_old = int(math.sqrt(len(posemb_grid))) + + posemb_grid = posemb_grid.reshape(1, gs_old, gs_old, -1).permute(0, 3, 1, 2) + posemb_grid = F.interpolate(posemb_grid, size=(gs_h, gs_w), mode="bilinear") + posemb_grid = posemb_grid.permute(0, 2, 3, 1).reshape(1, gs_h * gs_w, -1) + + posemb = torch.cat([posemb_tok, posemb_grid], dim=1) + + return posemb + + +def forward_flex(self, x): + b, c, h, w = x.shape + + pos_embed = self._resize_pos_embed( + self.pos_embed, h // self.patch_size[1], w // self.patch_size[0] + ) + + B = x.shape[0] + + if hasattr(self.patch_embed, "backbone"): + x = self.patch_embed.backbone(x) + if isinstance(x, (list, tuple)): + x = x[-1] # last feature if backbone outputs list/tuple of features + + x = self.patch_embed.proj(x).flatten(2).transpose(1, 2) + + if getattr(self, "dist_token", None) is not None: + cls_tokens = self.cls_token.expand( + B, -1, -1 + ) # stole cls_tokens impl from Phil Wang, thanks + dist_token = self.dist_token.expand(B, -1, -1) + x = torch.cat((cls_tokens, dist_token, x), dim=1) + else: + cls_tokens = self.cls_token.expand( + B, -1, -1 + ) # stole cls_tokens impl from Phil Wang, thanks + x = torch.cat((cls_tokens, x), dim=1) + + x = x + pos_embed + x = self.pos_drop(x) + + for blk in self.blocks: + x = blk(x) + + x = self.norm(x) + + return x + + +activations = {} + + +def get_activation(name): + def hook(model, input, output): + activations[name] = output + + return hook + + +def get_readout_oper(vit_features, features, use_readout, start_index=1): + if use_readout == "ignore": + readout_oper = [Slice(start_index)] * len(features) + elif use_readout == "add": + readout_oper = [AddReadout(start_index)] * len(features) + elif use_readout == "project": + readout_oper = [ + ProjectReadout(vit_features, start_index) for out_feat in features + ] + else: + assert ( + False + ), "wrong operation for readout token, use_readout can be 'ignore', 'add', or 'project'" + + return readout_oper + + +def _make_vit_b16_backbone( + model, + features=[96, 192, 384, 768], + size=[384, 384], + hooks=[2, 5, 8, 11], + vit_features=768, + use_readout="ignore", + start_index=1, +): + pretrained = nn.Module() + + pretrained.model = model + pretrained.model.blocks[hooks[0]].register_forward_hook(get_activation("1")) + pretrained.model.blocks[hooks[1]].register_forward_hook(get_activation("2")) + pretrained.model.blocks[hooks[2]].register_forward_hook(get_activation("3")) + pretrained.model.blocks[hooks[3]].register_forward_hook(get_activation("4")) + + pretrained.activations = activations + + readout_oper = get_readout_oper(vit_features, features, use_readout, start_index) + + # 32, 48, 136, 384 + pretrained.act_postprocess1 = nn.Sequential( + readout_oper[0], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[0], + kernel_size=1, + stride=1, + padding=0, + ), + nn.ConvTranspose2d( + in_channels=features[0], + out_channels=features[0], + kernel_size=4, + stride=4, + padding=0, + bias=True, + dilation=1, + groups=1, + ), + ) + + pretrained.act_postprocess2 = nn.Sequential( + readout_oper[1], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[1], + kernel_size=1, + stride=1, + padding=0, + ), + nn.ConvTranspose2d( + in_channels=features[1], + out_channels=features[1], + kernel_size=2, + stride=2, + padding=0, + bias=True, + dilation=1, + groups=1, + ), + ) + + pretrained.act_postprocess3 = nn.Sequential( + readout_oper[2], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[2], + kernel_size=1, + stride=1, + padding=0, + ), + ) + + pretrained.act_postprocess4 = nn.Sequential( + readout_oper[3], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[3], + kernel_size=1, + stride=1, + padding=0, + ), + nn.Conv2d( + in_channels=features[3], + out_channels=features[3], + kernel_size=3, + stride=2, + padding=1, + ), + ) + + pretrained.model.start_index = start_index + pretrained.model.patch_size = [16, 16] + + # We inject this function into the VisionTransformer instances so that + # we can use it with interpolated position embeddings without modifying the library source. + pretrained.model.forward_flex = types.MethodType(forward_flex, pretrained.model) + pretrained.model._resize_pos_embed = types.MethodType( + _resize_pos_embed, pretrained.model + ) + + return pretrained + + +def _make_pretrained_vitl16_384(pretrained, use_readout="ignore", hooks=None): + model = timm.create_model("vit_large_patch16_384", pretrained=pretrained) + + hooks = [5, 11, 17, 23] if hooks == None else hooks + return _make_vit_b16_backbone( + model, + features=[256, 512, 1024, 1024], + hooks=hooks, + vit_features=1024, + use_readout=use_readout, + ) + + +def _make_pretrained_vitb16_384(pretrained, use_readout="ignore", hooks=None): + model = timm.create_model("vit_base_patch16_384", pretrained=pretrained) + + hooks = [2, 5, 8, 11] if hooks == None else hooks + return _make_vit_b16_backbone( + model, features=[96, 192, 384, 768], hooks=hooks, use_readout=use_readout + ) + + +def _make_pretrained_deitb16_384(pretrained, use_readout="ignore", hooks=None): + model = timm.create_model("vit_deit_base_patch16_384", pretrained=pretrained) + + hooks = [2, 5, 8, 11] if hooks == None else hooks + return _make_vit_b16_backbone( + model, features=[96, 192, 384, 768], hooks=hooks, use_readout=use_readout + ) + + +def _make_pretrained_deitb16_distil_384(pretrained, use_readout="ignore", hooks=None): + model = timm.create_model( + "vit_deit_base_distilled_patch16_384", pretrained=pretrained + ) + + hooks = [2, 5, 8, 11] if hooks == None else hooks + return _make_vit_b16_backbone( + model, + features=[96, 192, 384, 768], + hooks=hooks, + use_readout=use_readout, + start_index=2, + ) + + +def _make_vit_b_rn50_backbone( + model, + features=[256, 512, 768, 768], + size=[384, 384], + hooks=[0, 1, 8, 11], + vit_features=768, + use_vit_only=False, + use_readout="ignore", + start_index=1, +): + pretrained = nn.Module() + + pretrained.model = model + + if use_vit_only == True: + pretrained.model.blocks[hooks[0]].register_forward_hook(get_activation("1")) + pretrained.model.blocks[hooks[1]].register_forward_hook(get_activation("2")) + else: + pretrained.model.patch_embed.backbone.stages[0].register_forward_hook( + get_activation("1") + ) + pretrained.model.patch_embed.backbone.stages[1].register_forward_hook( + get_activation("2") + ) + + pretrained.model.blocks[hooks[2]].register_forward_hook(get_activation("3")) + pretrained.model.blocks[hooks[3]].register_forward_hook(get_activation("4")) + + pretrained.activations = activations + + readout_oper = get_readout_oper(vit_features, features, use_readout, start_index) + + if use_vit_only == True: + pretrained.act_postprocess1 = nn.Sequential( + readout_oper[0], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[0], + kernel_size=1, + stride=1, + padding=0, + ), + nn.ConvTranspose2d( + in_channels=features[0], + out_channels=features[0], + kernel_size=4, + stride=4, + padding=0, + bias=True, + dilation=1, + groups=1, + ), + ) + + pretrained.act_postprocess2 = nn.Sequential( + readout_oper[1], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[1], + kernel_size=1, + stride=1, + padding=0, + ), + nn.ConvTranspose2d( + in_channels=features[1], + out_channels=features[1], + kernel_size=2, + stride=2, + padding=0, + bias=True, + dilation=1, + groups=1, + ), + ) + else: + pretrained.act_postprocess1 = nn.Sequential( + nn.Identity(), nn.Identity(), nn.Identity() + ) + pretrained.act_postprocess2 = nn.Sequential( + nn.Identity(), nn.Identity(), nn.Identity() + ) + + pretrained.act_postprocess3 = nn.Sequential( + readout_oper[2], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[2], + kernel_size=1, + stride=1, + padding=0, + ), + ) + + pretrained.act_postprocess4 = nn.Sequential( + readout_oper[3], + Transpose(1, 2), + nn.Unflatten(2, torch.Size([size[0] // 16, size[1] // 16])), + nn.Conv2d( + in_channels=vit_features, + out_channels=features[3], + kernel_size=1, + stride=1, + padding=0, + ), + nn.Conv2d( + in_channels=features[3], + out_channels=features[3], + kernel_size=3, + stride=2, + padding=1, + ), + ) + + pretrained.model.start_index = start_index + pretrained.model.patch_size = [16, 16] + + # We inject this function into the VisionTransformer instances so that + # we can use it with interpolated position embeddings without modifying the library source. + pretrained.model.forward_flex = types.MethodType(forward_flex, pretrained.model) + + # We inject this function into the VisionTransformer instances so that + # we can use it with interpolated position embeddings without modifying the library source. + pretrained.model._resize_pos_embed = types.MethodType( + _resize_pos_embed, pretrained.model + ) + + return pretrained + + +def _make_pretrained_vitb_rn50_384( + pretrained, use_readout="ignore", hooks=None, use_vit_only=False +): + model = timm.create_model("vit_base_resnet50_384", pretrained=pretrained) + + hooks = [0, 1, 8, 11] if hooks == None else hooks + return _make_vit_b_rn50_backbone( + model, + features=[256, 512, 768, 768], + size=[384, 384], + hooks=hooks, + use_vit_only=use_vit_only, + use_readout=use_readout, + ) diff --git a/repositories/ldm/modules/midas/utils.py b/repositories/ldm/modules/midas/utils.py new file mode 100644 index 000000000..9a9d3b5b6 --- /dev/null +++ b/repositories/ldm/modules/midas/utils.py @@ -0,0 +1,189 @@ +"""Utils for monoDepth.""" +import sys +import re +import numpy as np +import cv2 +import torch + + +def read_pfm(path): + """Read pfm file. + + Args: + path (str): path to file + + Returns: + tuple: (data, scale) + """ + with open(path, "rb") as file: + + color = None + width = None + height = None + scale = None + endian = None + + header = file.readline().rstrip() + if header.decode("ascii") == "PF": + color = True + elif header.decode("ascii") == "Pf": + color = False + else: + raise Exception("Not a PFM file: " + path) + + dim_match = re.match(r"^(\d+)\s(\d+)\s$", file.readline().decode("ascii")) + if dim_match: + width, height = list(map(int, dim_match.groups())) + else: + raise Exception("Malformed PFM header.") + + scale = float(file.readline().decode("ascii").rstrip()) + if scale < 0: + # little-endian + endian = "<" + scale = -scale + else: + # big-endian + endian = ">" + + data = np.fromfile(file, endian + "f") + shape = (height, width, 3) if color else (height, width) + + data = np.reshape(data, shape) + data = np.flipud(data) + + return data, scale + + +def write_pfm(path, image, scale=1): + """Write pfm file. + + Args: + path (str): pathto file + image (array): data + scale (int, optional): Scale. Defaults to 1. + """ + + with open(path, "wb") as file: + color = None + + if image.dtype.name != "float32": + raise Exception("Image dtype must be float32.") + + image = np.flipud(image) + + if len(image.shape) == 3 and image.shape[2] == 3: # color image + color = True + elif ( + len(image.shape) == 2 or len(image.shape) == 3 and image.shape[2] == 1 + ): # greyscale + color = False + else: + raise Exception("Image must have H x W x 3, H x W x 1 or H x W dimensions.") + + file.write("PF\n" if color else "Pf\n".encode()) + file.write("%d %d\n".encode() % (image.shape[1], image.shape[0])) + + endian = image.dtype.byteorder + + if endian == "<" or endian == "=" and sys.byteorder == "little": + scale = -scale + + file.write("%f\n".encode() % scale) + + image.tofile(file) + + +def read_image(path): + """Read image and output RGB image (0-1). + + Args: + path (str): path to file + + Returns: + array: RGB image (0-1) + """ + img = cv2.imread(path) + + if img.ndim == 2: + img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) + + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) / 255.0 + + return img + + +def resize_image(img): + """Resize image and make it fit for network. + + Args: + img (array): image + + Returns: + tensor: data ready for network + """ + height_orig = img.shape[0] + width_orig = img.shape[1] + + if width_orig > height_orig: + scale = width_orig / 384 + else: + scale = height_orig / 384 + + height = (np.ceil(height_orig / scale / 32) * 32).astype(int) + width = (np.ceil(width_orig / scale / 32) * 32).astype(int) + + img_resized = cv2.resize(img, (width, height), interpolation=cv2.INTER_AREA) + + img_resized = ( + torch.from_numpy(np.transpose(img_resized, (2, 0, 1))).contiguous().float() + ) + img_resized = img_resized.unsqueeze(0) + + return img_resized + + +def resize_depth(depth, width, height): + """Resize depth map and bring to CPU (numpy). + + Args: + depth (tensor): depth + width (int): image width + height (int): image height + + Returns: + array: processed depth + """ + depth = torch.squeeze(depth[0, :, :, :]).to("cpu") + + depth_resized = cv2.resize( + depth.numpy(), (width, height), interpolation=cv2.INTER_CUBIC + ) + + return depth_resized + +def write_depth(path, depth, bits=1): + """Write depth map to pfm and png file. + + Args: + path (str): filepath without extension + depth (array): depth + """ + write_pfm(path + ".pfm", depth.astype(np.float32)) + + depth_min = depth.min() + depth_max = depth.max() + + max_val = (2**(8*bits))-1 + + if depth_max - depth_min > np.finfo("float").eps: + out = max_val * (depth - depth_min) / (depth_max - depth_min) + else: + out = np.zeros(depth.shape, dtype=depth.type) + + if bits == 1: + cv2.imwrite(path + ".png", out.astype("uint8")) + elif bits == 2: + cv2.imwrite(path + ".png", out.astype("uint16")) + + return diff --git a/repositories/ldm/util.py b/repositories/ldm/util.py new file mode 100644 index 000000000..9ede259d5 --- /dev/null +++ b/repositories/ldm/util.py @@ -0,0 +1,207 @@ +import importlib + +import torch +from torch import optim +import numpy as np + +from inspect import isfunction +from PIL import Image, ImageDraw, ImageFont + + +def autocast(f): + def do_autocast(*args, **kwargs): + with torch.cuda.amp.autocast(enabled=True, + dtype=torch.get_autocast_gpu_dtype(), + cache_enabled=torch.is_autocast_cache_enabled()): + return f(*args, **kwargs) + + return do_autocast + + +def log_txt_as_img(wh, xc, size=10): + # wh a tuple of (width, height) + # xc a list of captions to plot + b = len(xc) + txts = list() + for bi in range(b): + txt = Image.new("RGB", wh, color="white") + draw = ImageDraw.Draw(txt) + font = ImageFont.truetype('data/DejaVuSans.ttf', size=size) + nc = int(40 * (wh[0] / 256)) + lines = "\n".join(xc[bi][start:start + nc] for start in range(0, len(xc[bi]), nc)) + + try: + draw.text((0, 0), lines, fill="black", font=font) + except UnicodeEncodeError: + print("Cant encode string for logging. Skipping.") + + txt = np.array(txt).transpose(2, 0, 1) / 127.5 - 1.0 + txts.append(txt) + txts = np.stack(txts) + txts = torch.tensor(txts) + return txts + + +def ismap(x): + if not isinstance(x, torch.Tensor): + return False + return (len(x.shape) == 4) and (x.shape[1] > 3) + + +def isimage(x): + if not isinstance(x,torch.Tensor): + return False + return (len(x.shape) == 4) and (x.shape[1] == 3 or x.shape[1] == 1) + + +def exists(x): + return x is not None + + +def default(val, d): + if exists(val): + return val + return d() if isfunction(d) else d + + +def mean_flat(tensor): + """ + https://github.com/openai/guided-diffusion/blob/27c20a8fab9cb472df5d6bdd6c8d11c8f430b924/guided_diffusion/nn.py#L86 + Take the mean over all non-batch dimensions. + """ + return tensor.mean(dim=list(range(1, len(tensor.shape)))) + + +def count_params(model, verbose=False): + total_params = sum(p.numel() for p in model.parameters()) + if verbose: + print(f"{model.__class__.__name__} has {total_params*1.e-6:.2f} M params.") + return total_params + + +def instantiate_from_config(config): + if not "target" in config: + if config == '__is_first_stage__': + return None + elif config == "__is_unconditional__": + return None + raise KeyError("Expected key `target` to instantiate.") + return get_obj_from_str(config["target"])(**config.get("params", dict())) + + +def get_obj_from_str(string, reload=False): + module, cls = string.rsplit(".", 1) + if reload: + module_imp = importlib.import_module(module) + importlib.reload(module_imp) + return getattr(importlib.import_module(module, package=None), cls) + + +class AdamWwithEMAandWings(optim.Optimizer): + # credit to https://gist.github.com/crowsonkb/65f7265353f403714fce3b2595e0b298 + def __init__(self, params, lr=1.e-3, betas=(0.9, 0.999), eps=1.e-8, # TODO: check hyperparameters before using + weight_decay=1.e-2, amsgrad=False, ema_decay=0.9999, # ema decay to match previous code + ema_power=1., param_names=()): + """AdamW that saves EMA versions of the parameters.""" + if not 0.0 <= lr: + raise ValueError("Invalid learning rate: {}".format(lr)) + if not 0.0 <= eps: + raise ValueError("Invalid epsilon value: {}".format(eps)) + if not 0.0 <= betas[0] < 1.0: + raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0])) + if not 0.0 <= betas[1] < 1.0: + raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1])) + if not 0.0 <= weight_decay: + raise ValueError("Invalid weight_decay value: {}".format(weight_decay)) + if not 0.0 <= ema_decay <= 1.0: + raise ValueError("Invalid ema_decay value: {}".format(ema_decay)) + defaults = dict(lr=lr, betas=betas, eps=eps, + weight_decay=weight_decay, amsgrad=amsgrad, ema_decay=ema_decay, + ema_power=ema_power, param_names=param_names) + super().__init__(params, defaults) + + def __setstate__(self, state): + super().__setstate__(state) + for group in self.param_groups: + group.setdefault('amsgrad', False) + + @torch.no_grad() + def step(self, closure=None): + """Performs a single optimization step. + Args: + closure (callable, optional): A closure that reevaluates the model + and returns the loss. + """ + loss = None + if closure is not None: + with torch.enable_grad(): + loss = closure() + + for group in self.param_groups: + params_with_grad = [] + grads = [] + exp_avgs = [] + exp_avg_sqs = [] + ema_params_with_grad = [] + state_sums = [] + max_exp_avg_sqs = [] + state_steps = [] + amsgrad = group['amsgrad'] + beta1, beta2 = group['betas'] + ema_decay = group['ema_decay'] + ema_power = group['ema_power'] + + for p in group['params']: + if p.grad is None: + continue + params_with_grad.append(p) + if p.grad.is_sparse: + raise RuntimeError('AdamW does not support sparse gradients') + grads.append(p.grad) + + state = self.state[p] + + # State initialization + if len(state) == 0: + state['step'] = 0 + # Exponential moving average of gradient values + state['exp_avg'] = torch.zeros_like(p, memory_format=torch.preserve_format) + # Exponential moving average of squared gradient values + state['exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format) + if amsgrad: + # Maintains max of all exp. moving avg. of sq. grad. values + state['max_exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format) + # Exponential moving average of parameter values + state['param_exp_avg'] = p.detach().float().clone() + + exp_avgs.append(state['exp_avg']) + exp_avg_sqs.append(state['exp_avg_sq']) + ema_params_with_grad.append(state['param_exp_avg']) + + if amsgrad: + max_exp_avg_sqs.append(state['max_exp_avg_sq']) + + # update the steps for each param group update + state['step'] += 1 + # record the step after step update + state_steps.append(state['step']) + + optim._functional.adamw(params_with_grad, + grads, + exp_avgs, + exp_avg_sqs, + max_exp_avg_sqs, + state_steps, + amsgrad=amsgrad, + beta1=beta1, + beta2=beta2, + lr=group['lr'], + weight_decay=group['weight_decay'], + eps=group['eps'], + maximize=False) + + cur_ema_decay = min(ema_decay, 1 - state['step'] ** -ema_power) + for param, ema_param in zip(params_with_grad, ema_params_with_grad): + ema_param.mul_(cur_ema_decay).add_(param.float(), alpha=1 - cur_ema_decay) + + return loss \ No newline at end of file diff --git a/repositories/taming/README.md b/repositories/taming/README.md new file mode 100644 index 000000000..d295fbf75 --- /dev/null +++ b/repositories/taming/README.md @@ -0,0 +1,410 @@ +# Taming Transformers for High-Resolution Image Synthesis +##### CVPR 2021 (Oral) +![teaser](assets/mountain.jpeg) + +[**Taming Transformers for High-Resolution Image Synthesis**](https://compvis.github.io/taming-transformers/)
+[Patrick Esser](https://github.com/pesser)\*, +[Robin Rombach](https://github.com/rromb)\*, +[Björn Ommer](https://hci.iwr.uni-heidelberg.de/Staff/bommer)
+\* equal contribution + +**tl;dr** We combine the efficiancy of convolutional approaches with the expressivity of transformers by introducing a convolutional VQGAN, which learns a codebook of context-rich visual parts, whose composition is modeled with an autoregressive transformer. + +![teaser](assets/teaser.png) +[arXiv](https://arxiv.org/abs/2012.09841) | [BibTeX](#bibtex) | [Project Page](https://compvis.github.io/taming-transformers/) + + +### News +#### 2022 +- More pretrained VQGANs (e.g. a f8-model with only 256 codebook entries) are available in our new work on [Latent Diffusion Models](https://github.com/CompVis/latent-diffusion). +- Added scene synthesis models as proposed in the paper [High-Resolution Complex Scene Synthesis with Transformers](https://arxiv.org/abs/2105.06458), see [this section](#scene-image-synthesis). +#### 2021 +- Thanks to [rom1504](https://github.com/rom1504) it is now easy to [train a VQGAN on your own datasets](#training-on-custom-data). +- Included a bugfix for the quantizer. For backward compatibility it is + disabled by default (which corresponds to always training with `beta=1.0`). + Use `legacy=False` in the quantizer config to enable it. + Thanks [richcmwang](https://github.com/richcmwang) and [wcshin-git](https://github.com/wcshin-git)! +- Our paper received an update: See https://arxiv.org/abs/2012.09841v3 and the corresponding changelog. +- Added a pretrained, [1.4B transformer model](https://k00.fr/s511rwcv) trained for class-conditional ImageNet synthesis, which obtains state-of-the-art FID scores among autoregressive approaches and outperforms BigGAN. +- Added pretrained, unconditional models on [FFHQ](https://k00.fr/yndvfu95) and [CelebA-HQ](https://k00.fr/2xkmielf). +- Added accelerated sampling via caching of keys/values in the self-attention operation, used in `scripts/sample_fast.py`. +- Added a checkpoint of a [VQGAN](https://heibox.uni-heidelberg.de/d/2e5662443a6b4307b470/) trained with f8 compression and Gumbel-Quantization. + See also our updated [reconstruction notebook](https://colab.research.google.com/github/CompVis/taming-transformers/blob/master/scripts/reconstruction_usage.ipynb). +- We added a [colab notebook](https://colab.research.google.com/github/CompVis/taming-transformers/blob/master/scripts/reconstruction_usage.ipynb) which compares two VQGANs and OpenAI's [DALL-E](https://github.com/openai/DALL-E). See also [this section](#more-resources). +- We now include an overview of pretrained models in [Tab.1](#overview-of-pretrained-models). We added models for [COCO](#coco) and [ADE20k](#ade20k). +- The streamlit demo now supports image completions. +- We now include a couple of examples from the D-RIN dataset so you can run the + [D-RIN demo](#d-rin) without preparing the dataset first. +- You can now jump right into sampling with our [Colab quickstart notebook](https://colab.research.google.com/github/CompVis/taming-transformers/blob/master/scripts/taming-transformers.ipynb). + +## Requirements +A suitable [conda](https://conda.io/) environment named `taming` can be created +and activated with: + +``` +conda env create -f environment.yaml +conda activate taming +``` +## Overview of pretrained models +The following table provides an overview of all models that are currently available. +FID scores were evaluated using [torch-fidelity](https://github.com/toshas/torch-fidelity). +For reference, we also include a link to the recently released autoencoder of the [DALL-E](https://github.com/openai/DALL-E) model. +See the corresponding [colab +notebook](https://colab.research.google.com/github/CompVis/taming-transformers/blob/master/scripts/reconstruction_usage.ipynb) +for a comparison and discussion of reconstruction capabilities. + +| Dataset | FID vs train | FID vs val | Link | Samples (256x256) | Comments +| ------------- | ------------- | ------------- |------------- | ------------- |------------- | +| FFHQ (f=16) | 9.6 | -- | [ffhq_transformer](https://k00.fr/yndvfu95) | [ffhq_samples](https://k00.fr/j626x093) | +| CelebA-HQ (f=16) | 10.2 | -- | [celebahq_transformer](https://k00.fr/2xkmielf) | [celebahq_samples](https://k00.fr/j626x093) | +| ADE20K (f=16) | -- | 35.5 | [ade20k_transformer](https://k00.fr/ot46cksa) | [ade20k_samples.zip](https://heibox.uni-heidelberg.de/f/70bb78cbaf844501b8fb/) [2k] | evaluated on val split (2k images) +| COCO-Stuff (f=16) | -- | 20.4 | [coco_transformer](https://k00.fr/2zz6i2ce) | [coco_samples.zip](https://heibox.uni-heidelberg.de/f/a395a9be612f4a7a8054/) [5k] | evaluated on val split (5k images) +| ImageNet (cIN) (f=16) | 15.98/15.78/6.59/5.88/5.20 | -- | [cin_transformer](https://k00.fr/s511rwcv) | [cin_samples](https://k00.fr/j626x093) | different decoding hyperparameters | +| | | | || | +| FacesHQ (f=16) | -- | -- | [faceshq_transformer](https://k00.fr/qqfl2do8) +| S-FLCKR (f=16) | -- | -- | [sflckr](https://heibox.uni-heidelberg.de/d/73487ab6e5314cb5adba/) +| D-RIN (f=16) | -- | -- | [drin_transformer](https://k00.fr/39jcugc5) +| | | | | || | +| VQGAN ImageNet (f=16), 1024 | 10.54 | 7.94 | [vqgan_imagenet_f16_1024](https://heibox.uni-heidelberg.de/d/8088892a516d4e3baf92/) | [reconstructions](https://k00.fr/j626x093) | Reconstruction-FIDs. +| VQGAN ImageNet (f=16), 16384 | 7.41 | 4.98 |[vqgan_imagenet_f16_16384](https://heibox.uni-heidelberg.de/d/a7530b09fed84f80a887/) | [reconstructions](https://k00.fr/j626x093) | Reconstruction-FIDs. +| VQGAN OpenImages (f=8), 256 | -- | 1.49 |https://ommer-lab.com/files/latent-diffusion/vq-f8-n256.zip | --- | Reconstruction-FIDs. Available via [latent diffusion](https://github.com/CompVis/latent-diffusion). +| VQGAN OpenImages (f=8), 16384 | -- | 1.14 |https://ommer-lab.com/files/latent-diffusion/vq-f8.zip | --- | Reconstruction-FIDs. Available via [latent diffusion](https://github.com/CompVis/latent-diffusion) +| VQGAN OpenImages (f=8), 8192, GumbelQuantization | 3.24 | 1.49 |[vqgan_gumbel_f8](https://heibox.uni-heidelberg.de/d/2e5662443a6b4307b470/) | --- | Reconstruction-FIDs. +| | | | | || | +| DALL-E dVAE (f=8), 8192, GumbelQuantization | 33.88 | 32.01 | https://github.com/openai/DALL-E | [reconstructions](https://k00.fr/j626x093) | Reconstruction-FIDs. + + +## Running pretrained models + +The commands below will start a streamlit demo which supports sampling at +different resolutions and image completions. To run a non-interactive version +of the sampling process, replace `streamlit run scripts/sample_conditional.py --` +by `python scripts/make_samples.py --outdir ` and +keep the remaining command line arguments. + +To sample from unconditional or class-conditional models, +run `python scripts/sample_fast.py -r `. +We describe below how to use this script to sample from the ImageNet, FFHQ, and CelebA-HQ models, +respectively. + +### S-FLCKR +![teaser](assets/sunset_and_ocean.jpg) + +You can also [run this model in a Colab +notebook](https://colab.research.google.com/github/CompVis/taming-transformers/blob/master/scripts/taming-transformers.ipynb), +which includes all necessary steps to start sampling. + +Download the +[2020-11-09T13-31-51_sflckr](https://heibox.uni-heidelberg.de/d/73487ab6e5314cb5adba/) +folder and place it into `logs`. Then, run +``` +streamlit run scripts/sample_conditional.py -- -r logs/2020-11-09T13-31-51_sflckr/ +``` + +### ImageNet +![teaser](assets/imagenet.png) + +Download the [2021-04-03T19-39-50_cin_transformer](https://k00.fr/s511rwcv) +folder and place it into logs. Sampling from the class-conditional ImageNet +model does not require any data preparation. To produce 50 samples for each of +the 1000 classes of ImageNet, with k=600 for top-k sampling, p=0.92 for nucleus +sampling and temperature t=1.0, run + +``` +python scripts/sample_fast.py -r logs/2021-04-03T19-39-50_cin_transformer/ -n 50 -k 600 -t 1.0 -p 0.92 --batch_size 25 +``` + +To restrict the model to certain classes, provide them via the `--classes` argument, separated by +commas. For example, to sample 50 *ostriches*, *border collies* and *whiskey jugs*, run + +``` +python scripts/sample_fast.py -r logs/2021-04-03T19-39-50_cin_transformer/ -n 50 -k 600 -t 1.0 -p 0.92 --batch_size 25 --classes 9,232,901 +``` +We recommended to experiment with the autoregressive decoding parameters (top-k, top-p and temperature) for best results. + +### FFHQ/CelebA-HQ + +Download the [2021-04-23T18-19-01_ffhq_transformer](https://k00.fr/yndvfu95) and +[2021-04-23T18-11-19_celebahq_transformer](https://k00.fr/2xkmielf) +folders and place them into logs. +Again, sampling from these unconditional models does not require any data preparation. +To produce 50000 samples, with k=250 for top-k sampling, +p=1.0 for nucleus sampling and temperature t=1.0, run + +``` +python scripts/sample_fast.py -r logs/2021-04-23T18-19-01_ffhq_transformer/ +``` +for FFHQ and + +``` +python scripts/sample_fast.py -r logs/2021-04-23T18-11-19_celebahq_transformer/ +``` +to sample from the CelebA-HQ model. +For both models it can be advantageous to vary the top-k/top-p parameters for sampling. + +### FacesHQ +![teaser](assets/faceshq.jpg) + +Download [2020-11-13T21-41-45_faceshq_transformer](https://k00.fr/qqfl2do8) and +place it into `logs`. Follow the data preparation steps for +[CelebA-HQ](#celeba-hq) and [FFHQ](#ffhq). Run +``` +streamlit run scripts/sample_conditional.py -- -r logs/2020-11-13T21-41-45_faceshq_transformer/ +``` + +### D-RIN +![teaser](assets/drin.jpg) + +Download [2020-11-20T12-54-32_drin_transformer](https://k00.fr/39jcugc5) and +place it into `logs`. To run the demo on a couple of example depth maps +included in the repository, run + +``` +streamlit run scripts/sample_conditional.py -- -r logs/2020-11-20T12-54-32_drin_transformer/ --ignore_base_data data="{target: main.DataModuleFromConfig, params: {batch_size: 1, validation: {target: taming.data.imagenet.DRINExamples}}}" +``` + +To run the demo on the complete validation set, first follow the data preparation steps for +[ImageNet](#imagenet) and then run +``` +streamlit run scripts/sample_conditional.py -- -r logs/2020-11-20T12-54-32_drin_transformer/ +``` + +### COCO +Download [2021-01-20T16-04-20_coco_transformer](https://k00.fr/2zz6i2ce) and +place it into `logs`. To run the demo on a couple of example segmentation maps +included in the repository, run + +``` +streamlit run scripts/sample_conditional.py -- -r logs/2021-01-20T16-04-20_coco_transformer/ --ignore_base_data data="{target: main.DataModuleFromConfig, params: {batch_size: 1, validation: {target: taming.data.coco.Examples}}}" +``` + +### ADE20k +Download [2020-11-20T21-45-44_ade20k_transformer](https://k00.fr/ot46cksa) and +place it into `logs`. To run the demo on a couple of example segmentation maps +included in the repository, run + +``` +streamlit run scripts/sample_conditional.py -- -r logs/2020-11-20T21-45-44_ade20k_transformer/ --ignore_base_data data="{target: main.DataModuleFromConfig, params: {batch_size: 1, validation: {target: taming.data.ade20k.Examples}}}" +``` + +## Scene Image Synthesis +![teaser](assets/scene_images_samples.svg) +Scene image generation based on bounding box conditionals as done in our CVPR2021 AI4CC workshop paper [High-Resolution Complex Scene Synthesis with Transformers](https://arxiv.org/abs/2105.06458) (see talk on [workshop page](https://visual.cs.brown.edu/workshops/aicc2021/#awards)). Supporting the datasets COCO and Open Images. + +### Training +Download first-stage models [COCO-8k-VQGAN](https://heibox.uni-heidelberg.de/f/78dea9589974474c97c1/) for COCO or [COCO/Open-Images-8k-VQGAN](https://heibox.uni-heidelberg.de/f/461d9a9f4fcf48ab84f4/) for Open Images. +Change `ckpt_path` in `data/coco_scene_images_transformer.yaml` and `data/open_images_scene_images_transformer.yaml` to point to the downloaded first-stage models. +Download the full COCO/OI datasets and adapt `data_path` in the same files, unless working with the 100 files provided for training and validation suits your needs already. + +Code can be run with +`python main.py --base configs/coco_scene_images_transformer.yaml -t True --gpus 0,` +or +`python main.py --base configs/open_images_scene_images_transformer.yaml -t True --gpus 0,` + +### Sampling +Train a model as described above or download a pre-trained model: + - [Open Images 1 billion parameter model](https://drive.google.com/file/d/1FEK-Z7hyWJBvFWQF50pzSK9y1W_CJEig/view?usp=sharing) available that trained 100 epochs. On 256x256 pixels, FID 41.48±0.21, SceneFID 14.60±0.15, Inception Score 18.47±0.27. The model was trained with 2d crops of images and is thus well-prepared for the task of generating high-resolution images, e.g. 512x512. + - [Open Images distilled version of the above model with 125 million parameters](https://drive.google.com/file/d/1xf89g0mc78J3d8Bx5YhbK4tNRNlOoYaO) allows for sampling on smaller GPUs (4 GB is enough for sampling 256x256 px images). Model was trained for 60 epochs with 10% soft loss, 90% hard loss. On 256x256 pixels, FID 43.07±0.40, SceneFID 15.93±0.19, Inception Score 17.23±0.11. + - [COCO 30 epochs](https://heibox.uni-heidelberg.de/f/0d0b2594e9074c7e9a33/) + - [COCO 60 epochs](https://drive.google.com/file/d/1bInd49g2YulTJBjU32Awyt5qnzxxG5U9/) (find model statistics for both COCO versions in `assets/coco_scene_images_training.svg`) + +When downloading a pre-trained model, remember to change `ckpt_path` in `configs/*project.yaml` to point to your downloaded first-stage model (see ->Training). + +Scene image generation can be run with +`python scripts/make_scene_samples.py --outdir=/some/outdir -r /path/to/pretrained/model --resolution=512,512` + + +## Training on custom data + +Training on your own dataset can be beneficial to get better tokens and hence better images for your domain. +Those are the steps to follow to make this work: +1. install the repo with `conda env create -f environment.yaml`, `conda activate taming` and `pip install -e .` +1. put your .jpg files in a folder `your_folder` +2. create 2 text files a `xx_train.txt` and `xx_test.txt` that point to the files in your training and test set respectively (for example `find $(pwd)/your_folder -name "*.jpg" > train.txt`) +3. adapt `configs/custom_vqgan.yaml` to point to these 2 files +4. run `python main.py --base configs/custom_vqgan.yaml -t True --gpus 0,1` to + train on two GPUs. Use `--gpus 0,` (with a trailing comma) to train on a single GPU. + +## Data Preparation + +### ImageNet +The code will try to download (through [Academic +Torrents](http://academictorrents.com/)) and prepare ImageNet the first time it +is used. However, since ImageNet is quite large, this requires a lot of disk +space and time. If you already have ImageNet on your disk, you can speed things +up by putting the data into +`${XDG_CACHE}/autoencoders/data/ILSVRC2012_{split}/data/` (which defaults to +`~/.cache/autoencoders/data/ILSVRC2012_{split}/data/`), where `{split}` is one +of `train`/`validation`. It should have the following structure: + +``` +${XDG_CACHE}/autoencoders/data/ILSVRC2012_{split}/data/ +├── n01440764 +│ ├── n01440764_10026.JPEG +│ ├── n01440764_10027.JPEG +│ ├── ... +├── n01443537 +│ ├── n01443537_10007.JPEG +│ ├── n01443537_10014.JPEG +│ ├── ... +├── ... +``` + +If you haven't extracted the data, you can also place +`ILSVRC2012_img_train.tar`/`ILSVRC2012_img_val.tar` (or symlinks to them) into +`${XDG_CACHE}/autoencoders/data/ILSVRC2012_train/` / +`${XDG_CACHE}/autoencoders/data/ILSVRC2012_validation/`, which will then be +extracted into above structure without downloading it again. Note that this +will only happen if neither a folder +`${XDG_CACHE}/autoencoders/data/ILSVRC2012_{split}/data/` nor a file +`${XDG_CACHE}/autoencoders/data/ILSVRC2012_{split}/.ready` exist. Remove them +if you want to force running the dataset preparation again. + +You will then need to prepare the depth data using +[MiDaS](https://github.com/intel-isl/MiDaS). Create a symlink +`data/imagenet_depth` pointing to a folder with two subfolders `train` and +`val`, each mirroring the structure of the corresponding ImageNet folder +described above and containing a `png` file for each of ImageNet's `JPEG` +files. The `png` encodes `float32` depth values obtained from MiDaS as RGBA +images. We provide the script `scripts/extract_depth.py` to generate this data. +**Please note** that this script uses [MiDaS via PyTorch +Hub](https://pytorch.org/hub/intelisl_midas_v2/). When we prepared the data, +the hub provided the [MiDaS +v2.0](https://github.com/intel-isl/MiDaS/releases/tag/v2) version, but now it +provides a v2.1 version. We haven't tested our models with depth maps obtained +via v2.1 and if you want to make sure that things work as expected, you must +adjust the script to make sure it explicitly uses +[v2.0](https://github.com/intel-isl/MiDaS/releases/tag/v2)! + +### CelebA-HQ +Create a symlink `data/celebahq` pointing to a folder containing the `.npy` +files of CelebA-HQ (instructions to obtain them can be found in the [PGGAN +repository](https://github.com/tkarras/progressive_growing_of_gans)). + +### FFHQ +Create a symlink `data/ffhq` pointing to the `images1024x1024` folder obtained +from the [FFHQ repository](https://github.com/NVlabs/ffhq-dataset). + +### S-FLCKR +Unfortunately, we are not allowed to distribute the images we collected for the +S-FLCKR dataset and can therefore only give a description how it was produced. +There are many resources on [collecting images from the +web](https://github.com/adrianmrit/flickrdatasets) to get started. +We collected sufficiently large images from [flickr](https://www.flickr.com) +(see `data/flickr_tags.txt` for a full list of tags used to find images) +and various [subreddits](https://www.reddit.com/r/sfwpornnetwork/wiki/network) +(see `data/subreddits.txt` for all subreddits that were used). +Overall, we collected 107625 images, and split them randomly into 96861 +training images and 10764 validation images. We then obtained segmentation +masks for each image using [DeepLab v2](https://arxiv.org/abs/1606.00915) +trained on [COCO-Stuff](https://arxiv.org/abs/1612.03716). We used a [PyTorch +reimplementation](https://github.com/kazuto1011/deeplab-pytorch) and include an +example script for this process in `scripts/extract_segmentation.py`. + +### COCO +Create a symlink `data/coco` containing the images from the 2017 split in +`train2017` and `val2017`, and their annotations in `annotations`. Files can be +obtained from the [COCO webpage](https://cocodataset.org/). In addition, we use +the [Stuff+thing PNG-style annotations on COCO 2017 +trainval](http://calvin.inf.ed.ac.uk/wp-content/uploads/data/cocostuffdataset/stuffthingmaps_trainval2017.zip) +annotations from [COCO-Stuff](https://github.com/nightrome/cocostuff), which +should be placed under `data/cocostuffthings`. + +### ADE20k +Create a symlink `data/ade20k_root` containing the contents of +[ADEChallengeData2016.zip](http://data.csail.mit.edu/places/ADEchallenge/ADEChallengeData2016.zip) +from the [MIT Scene Parsing Benchmark](http://sceneparsing.csail.mit.edu/). + +## Training models + +### FacesHQ + +Train a VQGAN with +``` +python main.py --base configs/faceshq_vqgan.yaml -t True --gpus 0, +``` + +Then, adjust the checkpoint path of the config key +`model.params.first_stage_config.params.ckpt_path` in +`configs/faceshq_transformer.yaml` (or download +[2020-11-09T13-33-36_faceshq_vqgan](https://k00.fr/uxy5usa9) and place into `logs`, which +corresponds to the preconfigured checkpoint path), then run +``` +python main.py --base configs/faceshq_transformer.yaml -t True --gpus 0, +``` + +### D-RIN + +Train a VQGAN on ImageNet with +``` +python main.py --base configs/imagenet_vqgan.yaml -t True --gpus 0, +``` + +or download a pretrained one from [2020-09-23T17-56-33_imagenet_vqgan](https://k00.fr/u0j2dtac) +and place under `logs`. If you trained your own, adjust the path in the config +key `model.params.first_stage_config.params.ckpt_path` of +`configs/drin_transformer.yaml`. + +Train a VQGAN on Depth Maps of ImageNet with +``` +python main.py --base configs/imagenetdepth_vqgan.yaml -t True --gpus 0, +``` + +or download a pretrained one from [2020-11-03T15-34-24_imagenetdepth_vqgan](https://k00.fr/55rlxs6i) +and place under `logs`. If you trained your own, adjust the path in the config +key `model.params.cond_stage_config.params.ckpt_path` of +`configs/drin_transformer.yaml`. + +To train the transformer, run +``` +python main.py --base configs/drin_transformer.yaml -t True --gpus 0, +``` + +## More Resources +### Comparing Different First Stage Models +The reconstruction and compression capabilities of different fist stage models can be analyzed in this [colab notebook](https://colab.research.google.com/github/CompVis/taming-transformers/blob/master/scripts/reconstruction_usage.ipynb). +In particular, the notebook compares two VQGANs with a downsampling factor of f=16 for each and codebook dimensionality of 1024 and 16384, +a VQGAN with f=8 and 8192 codebook entries and the discrete autoencoder of OpenAI's [DALL-E](https://github.com/openai/DALL-E) (which has f=8 and 8192 +codebook entries). +![firststages1](assets/first_stage_squirrels.png) +![firststages2](assets/first_stage_mushrooms.png) + +### Other +- A [video summary](https://www.youtube.com/watch?v=o7dqGcLDf0A&feature=emb_imp_woyt) by [Two Minute Papers](https://www.youtube.com/channel/UCbfYPyITQ-7l4upoX8nvctg). +- A [video summary](https://www.youtube.com/watch?v=-wDSDtIAyWQ) by [Gradient Dude](https://www.youtube.com/c/GradientDude/about). +- A [weights and biases report summarizing the paper](https://wandb.ai/ayush-thakur/taming-transformer/reports/-Overview-Taming-Transformers-for-High-Resolution-Image-Synthesis---Vmlldzo0NjEyMTY) +by [ayulockin](https://github.com/ayulockin). +- A [video summary](https://www.youtube.com/watch?v=JfUTd8fjtX8&feature=emb_imp_woyt) by [What's AI](https://www.youtube.com/channel/UCUzGQrN-lyyc0BWTYoJM_Sg). +- Take a look at [ak9250's notebook](https://github.com/ak9250/taming-transformers/blob/master/tamingtransformerscolab.ipynb) if you want to run the streamlit demos on Colab. + +### Text-to-Image Optimization via CLIP +VQGAN has been successfully used as an image generator guided by the [CLIP](https://github.com/openai/CLIP) model, both for pure image generation +from scratch and image-to-image translation. We recommend the following notebooks/videos/resources: + + - [Advadnouns](https://twitter.com/advadnoun/status/1389316507134357506) Patreon and corresponding LatentVision notebooks: https://www.patreon.com/patronizeme + - The [notebook]( https://colab.research.google.com/drive/1L8oL-vLJXVcRzCFbPwOoMkPKJ8-aYdPN) of [Rivers Have Wings](https://twitter.com/RiversHaveWings). + - A [video](https://www.youtube.com/watch?v=90QDe6DQXF4&t=12s) explanation by [Dot CSV](https://www.youtube.com/channel/UCy5znSnfMsDwaLlROnZ7Qbg) (in Spanish, but English subtitles are available) + +![txt2img](assets/birddrawnbyachild.png) + +Text prompt: *'A bird drawn by a child'* + +## Shout-outs +Thanks to everyone who makes their code and models available. In particular, + +- The architecture of our VQGAN is inspired by [Denoising Diffusion Probabilistic Models](https://github.com/hojonathanho/diffusion) +- The very hackable transformer implementation [minGPT](https://github.com/karpathy/minGPT) +- The good ol' [PatchGAN](https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix) and [Learned Perceptual Similarity (LPIPS)](https://github.com/richzhang/PerceptualSimilarity) + +## BibTeX + +``` +@misc{esser2020taming, + title={Taming Transformers for High-Resolution Image Synthesis}, + author={Patrick Esser and Robin Rombach and Björn Ommer}, + year={2020}, + eprint={2012.09841}, + archivePrefix={arXiv}, + primaryClass={cs.CV} +} +``` diff --git a/repositories/taming/data/ade20k.py b/repositories/taming/data/ade20k.py new file mode 100644 index 000000000..366dae972 --- /dev/null +++ b/repositories/taming/data/ade20k.py @@ -0,0 +1,124 @@ +import os +import numpy as np +import cv2 +import albumentations +from PIL import Image +from torch.utils.data import Dataset + +from taming.data.sflckr import SegmentationBase # for examples included in repo + + +class Examples(SegmentationBase): + def __init__(self, size=256, random_crop=False, interpolation="bicubic"): + super().__init__(data_csv="data/ade20k_examples.txt", + data_root="data/ade20k_images", + segmentation_root="data/ade20k_segmentations", + size=size, random_crop=random_crop, + interpolation=interpolation, + n_labels=151, shift_segmentation=False) + + +# With semantic map and scene label +class ADE20kBase(Dataset): + def __init__(self, config=None, size=None, random_crop=False, interpolation="bicubic", crop_size=None): + self.split = self.get_split() + self.n_labels = 151 # unknown + 150 + self.data_csv = {"train": "data/ade20k_train.txt", + "validation": "data/ade20k_test.txt"}[self.split] + self.data_root = "data/ade20k_root" + with open(os.path.join(self.data_root, "sceneCategories.txt"), "r") as f: + self.scene_categories = f.read().splitlines() + self.scene_categories = dict(line.split() for line in self.scene_categories) + with open(self.data_csv, "r") as f: + self.image_paths = f.read().splitlines() + self._length = len(self.image_paths) + self.labels = { + "relative_file_path_": [l for l in self.image_paths], + "file_path_": [os.path.join(self.data_root, "images", l) + for l in self.image_paths], + "relative_segmentation_path_": [l.replace(".jpg", ".png") + for l in self.image_paths], + "segmentation_path_": [os.path.join(self.data_root, "annotations", + l.replace(".jpg", ".png")) + for l in self.image_paths], + "scene_category": [self.scene_categories[l.split("/")[1].replace(".jpg", "")] + for l in self.image_paths], + } + + size = None if size is not None and size<=0 else size + self.size = size + if crop_size is None: + self.crop_size = size if size is not None else None + else: + self.crop_size = crop_size + if self.size is not None: + self.interpolation = interpolation + self.interpolation = { + "nearest": cv2.INTER_NEAREST, + "bilinear": cv2.INTER_LINEAR, + "bicubic": cv2.INTER_CUBIC, + "area": cv2.INTER_AREA, + "lanczos": cv2.INTER_LANCZOS4}[self.interpolation] + self.image_rescaler = albumentations.SmallestMaxSize(max_size=self.size, + interpolation=self.interpolation) + self.segmentation_rescaler = albumentations.SmallestMaxSize(max_size=self.size, + interpolation=cv2.INTER_NEAREST) + + if crop_size is not None: + self.center_crop = not random_crop + if self.center_crop: + self.cropper = albumentations.CenterCrop(height=self.crop_size, width=self.crop_size) + else: + self.cropper = albumentations.RandomCrop(height=self.crop_size, width=self.crop_size) + self.preprocessor = self.cropper + + def __len__(self): + return self._length + + def __getitem__(self, i): + example = dict((k, self.labels[k][i]) for k in self.labels) + image = Image.open(example["file_path_"]) + if not image.mode == "RGB": + image = image.convert("RGB") + image = np.array(image).astype(np.uint8) + if self.size is not None: + image = self.image_rescaler(image=image)["image"] + segmentation = Image.open(example["segmentation_path_"]) + segmentation = np.array(segmentation).astype(np.uint8) + if self.size is not None: + segmentation = self.segmentation_rescaler(image=segmentation)["image"] + if self.size is not None: + processed = self.preprocessor(image=image, mask=segmentation) + else: + processed = {"image": image, "mask": segmentation} + example["image"] = (processed["image"]/127.5 - 1.0).astype(np.float32) + segmentation = processed["mask"] + onehot = np.eye(self.n_labels)[segmentation] + example["segmentation"] = onehot + return example + + +class ADE20kTrain(ADE20kBase): + # default to random_crop=True + def __init__(self, config=None, size=None, random_crop=True, interpolation="bicubic", crop_size=None): + super().__init__(config=config, size=size, random_crop=random_crop, + interpolation=interpolation, crop_size=crop_size) + + def get_split(self): + return "train" + + +class ADE20kValidation(ADE20kBase): + def get_split(self): + return "validation" + + +if __name__ == "__main__": + dset = ADE20kValidation() + ex = dset[0] + for k in ["image", "scene_category", "segmentation"]: + print(type(ex[k])) + try: + print(ex[k].shape) + except: + print(ex[k]) diff --git a/repositories/taming/data/annotated_objects_coco.py b/repositories/taming/data/annotated_objects_coco.py new file mode 100644 index 000000000..af000ecd9 --- /dev/null +++ b/repositories/taming/data/annotated_objects_coco.py @@ -0,0 +1,139 @@ +import json +from itertools import chain +from pathlib import Path +from typing import Iterable, Dict, List, Callable, Any +from collections import defaultdict + +from tqdm import tqdm + +from taming.data.annotated_objects_dataset import AnnotatedObjectsDataset +from taming.data.helper_types import Annotation, ImageDescription, Category + +COCO_PATH_STRUCTURE = { + 'train': { + 'top_level': '', + 'instances_annotations': 'annotations/instances_train2017.json', + 'stuff_annotations': 'annotations/stuff_train2017.json', + 'files': 'train2017' + }, + 'validation': { + 'top_level': '', + 'instances_annotations': 'annotations/instances_val2017.json', + 'stuff_annotations': 'annotations/stuff_val2017.json', + 'files': 'val2017' + } +} + + +def load_image_descriptions(description_json: List[Dict]) -> Dict[str, ImageDescription]: + return { + str(img['id']): ImageDescription( + id=img['id'], + license=img.get('license'), + file_name=img['file_name'], + coco_url=img['coco_url'], + original_size=(img['width'], img['height']), + date_captured=img.get('date_captured'), + flickr_url=img.get('flickr_url') + ) + for img in description_json + } + + +def load_categories(category_json: Iterable) -> Dict[str, Category]: + return {str(cat['id']): Category(id=str(cat['id']), super_category=cat['supercategory'], name=cat['name']) + for cat in category_json if cat['name'] != 'other'} + + +def load_annotations(annotations_json: List[Dict], image_descriptions: Dict[str, ImageDescription], + category_no_for_id: Callable[[str], int], split: str) -> Dict[str, List[Annotation]]: + annotations = defaultdict(list) + total = sum(len(a) for a in annotations_json) + for ann in tqdm(chain(*annotations_json), f'Loading {split} annotations', total=total): + image_id = str(ann['image_id']) + if image_id not in image_descriptions: + raise ValueError(f'image_id [{image_id}] has no image description.') + category_id = ann['category_id'] + try: + category_no = category_no_for_id(str(category_id)) + except KeyError: + continue + + width, height = image_descriptions[image_id].original_size + bbox = (ann['bbox'][0] / width, ann['bbox'][1] / height, ann['bbox'][2] / width, ann['bbox'][3] / height) + + annotations[image_id].append( + Annotation( + id=ann['id'], + area=bbox[2]*bbox[3], # use bbox area + is_group_of=ann['iscrowd'], + image_id=ann['image_id'], + bbox=bbox, + category_id=str(category_id), + category_no=category_no + ) + ) + return dict(annotations) + + +class AnnotatedObjectsCoco(AnnotatedObjectsDataset): + def __init__(self, use_things: bool = True, use_stuff: bool = True, **kwargs): + """ + @param data_path: is the path to the following folder structure: + coco/ + ├── annotations + │ ├── instances_train2017.json + │ ├── instances_val2017.json + │ ├── stuff_train2017.json + │ └── stuff_val2017.json + ├── train2017 + │ ├── 000000000009.jpg + │ ├── 000000000025.jpg + │ └── ... + ├── val2017 + │ ├── 000000000139.jpg + │ ├── 000000000285.jpg + │ └── ... + @param: split: one of 'train' or 'validation' + @param: desired image size (give square images) + """ + super().__init__(**kwargs) + self.use_things = use_things + self.use_stuff = use_stuff + + with open(self.paths['instances_annotations']) as f: + inst_data_json = json.load(f) + with open(self.paths['stuff_annotations']) as f: + stuff_data_json = json.load(f) + + category_jsons = [] + annotation_jsons = [] + if self.use_things: + category_jsons.append(inst_data_json['categories']) + annotation_jsons.append(inst_data_json['annotations']) + if self.use_stuff: + category_jsons.append(stuff_data_json['categories']) + annotation_jsons.append(stuff_data_json['annotations']) + + self.categories = load_categories(chain(*category_jsons)) + self.filter_categories() + self.setup_category_id_and_number() + + self.image_descriptions = load_image_descriptions(inst_data_json['images']) + annotations = load_annotations(annotation_jsons, self.image_descriptions, self.get_category_number, self.split) + self.annotations = self.filter_object_number(annotations, self.min_object_area, + self.min_objects_per_image, self.max_objects_per_image) + self.image_ids = list(self.annotations.keys()) + self.clean_up_annotations_and_image_descriptions() + + def get_path_structure(self) -> Dict[str, str]: + if self.split not in COCO_PATH_STRUCTURE: + raise ValueError(f'Split [{self.split} does not exist for COCO data.]') + return COCO_PATH_STRUCTURE[self.split] + + def get_image_path(self, image_id: str) -> Path: + return self.paths['files'].joinpath(self.image_descriptions[str(image_id)].file_name) + + def get_image_description(self, image_id: str) -> Dict[str, Any]: + # noinspection PyProtectedMember + return self.image_descriptions[image_id]._asdict() diff --git a/repositories/taming/data/annotated_objects_dataset.py b/repositories/taming/data/annotated_objects_dataset.py new file mode 100644 index 000000000..53cc346a1 --- /dev/null +++ b/repositories/taming/data/annotated_objects_dataset.py @@ -0,0 +1,218 @@ +from pathlib import Path +from typing import Optional, List, Callable, Dict, Any, Union +import warnings + +import PIL.Image as pil_image +from torch import Tensor +from torch.utils.data import Dataset +from torchvision import transforms + +from taming.data.conditional_builder.objects_bbox import ObjectsBoundingBoxConditionalBuilder +from taming.data.conditional_builder.objects_center_points import ObjectsCenterPointsConditionalBuilder +from taming.data.conditional_builder.utils import load_object_from_string +from taming.data.helper_types import BoundingBox, CropMethodType, Image, Annotation, SplitType +from taming.data.image_transforms import CenterCropReturnCoordinates, RandomCrop1dReturnCoordinates, \ + Random2dCropReturnCoordinates, RandomHorizontalFlipReturn, convert_pil_to_tensor + + +class AnnotatedObjectsDataset(Dataset): + def __init__(self, data_path: Union[str, Path], split: SplitType, keys: List[str], target_image_size: int, + min_object_area: float, min_objects_per_image: int, max_objects_per_image: int, + crop_method: CropMethodType, random_flip: bool, no_tokens: int, use_group_parameter: bool, + encode_crop: bool, category_allow_list_target: str = "", category_mapping_target: str = "", + no_object_classes: Optional[int] = None): + self.data_path = data_path + self.split = split + self.keys = keys + self.target_image_size = target_image_size + self.min_object_area = min_object_area + self.min_objects_per_image = min_objects_per_image + self.max_objects_per_image = max_objects_per_image + self.crop_method = crop_method + self.random_flip = random_flip + self.no_tokens = no_tokens + self.use_group_parameter = use_group_parameter + self.encode_crop = encode_crop + + self.annotations = None + self.image_descriptions = None + self.categories = None + self.category_ids = None + self.category_number = None + self.image_ids = None + self.transform_functions: List[Callable] = self.setup_transform(target_image_size, crop_method, random_flip) + self.paths = self.build_paths(self.data_path) + self._conditional_builders = None + self.category_allow_list = None + if category_allow_list_target: + allow_list = load_object_from_string(category_allow_list_target) + self.category_allow_list = {name for name, _ in allow_list} + self.category_mapping = {} + if category_mapping_target: + self.category_mapping = load_object_from_string(category_mapping_target) + self.no_object_classes = no_object_classes + + def build_paths(self, top_level: Union[str, Path]) -> Dict[str, Path]: + top_level = Path(top_level) + sub_paths = {name: top_level.joinpath(sub_path) for name, sub_path in self.get_path_structure().items()} + for path in sub_paths.values(): + if not path.exists(): + raise FileNotFoundError(f'{type(self).__name__} data structure error: [{path}] does not exist.') + return sub_paths + + @staticmethod + def load_image_from_disk(path: Path) -> Image: + return pil_image.open(path).convert('RGB') + + @staticmethod + def setup_transform(target_image_size: int, crop_method: CropMethodType, random_flip: bool): + transform_functions = [] + if crop_method == 'none': + transform_functions.append(transforms.Resize((target_image_size, target_image_size))) + elif crop_method == 'center': + transform_functions.extend([ + transforms.Resize(target_image_size), + CenterCropReturnCoordinates(target_image_size) + ]) + elif crop_method == 'random-1d': + transform_functions.extend([ + transforms.Resize(target_image_size), + RandomCrop1dReturnCoordinates(target_image_size) + ]) + elif crop_method == 'random-2d': + transform_functions.extend([ + Random2dCropReturnCoordinates(target_image_size), + transforms.Resize(target_image_size) + ]) + elif crop_method is None: + return None + else: + raise ValueError(f'Received invalid crop method [{crop_method}].') + if random_flip: + transform_functions.append(RandomHorizontalFlipReturn()) + transform_functions.append(transforms.Lambda(lambda x: x / 127.5 - 1.)) + return transform_functions + + def image_transform(self, x: Tensor) -> (Optional[BoundingBox], Optional[bool], Tensor): + crop_bbox = None + flipped = None + for t in self.transform_functions: + if isinstance(t, (RandomCrop1dReturnCoordinates, CenterCropReturnCoordinates, Random2dCropReturnCoordinates)): + crop_bbox, x = t(x) + elif isinstance(t, RandomHorizontalFlipReturn): + flipped, x = t(x) + else: + x = t(x) + return crop_bbox, flipped, x + + @property + def no_classes(self) -> int: + return self.no_object_classes if self.no_object_classes else len(self.categories) + + @property + def conditional_builders(self) -> ObjectsCenterPointsConditionalBuilder: + # cannot set this up in init because no_classes is only known after loading data in init of superclass + if self._conditional_builders is None: + self._conditional_builders = { + 'objects_center_points': ObjectsCenterPointsConditionalBuilder( + self.no_classes, + self.max_objects_per_image, + self.no_tokens, + self.encode_crop, + self.use_group_parameter, + getattr(self, 'use_additional_parameters', False) + ), + 'objects_bbox': ObjectsBoundingBoxConditionalBuilder( + self.no_classes, + self.max_objects_per_image, + self.no_tokens, + self.encode_crop, + self.use_group_parameter, + getattr(self, 'use_additional_parameters', False) + ) + } + return self._conditional_builders + + def filter_categories(self) -> None: + if self.category_allow_list: + self.categories = {id_: cat for id_, cat in self.categories.items() if cat.name in self.category_allow_list} + if self.category_mapping: + self.categories = {id_: cat for id_, cat in self.categories.items() if cat.id not in self.category_mapping} + + def setup_category_id_and_number(self) -> None: + self.category_ids = list(self.categories.keys()) + self.category_ids.sort() + if '/m/01s55n' in self.category_ids: + self.category_ids.remove('/m/01s55n') + self.category_ids.append('/m/01s55n') + self.category_number = {category_id: i for i, category_id in enumerate(self.category_ids)} + if self.category_allow_list is not None and self.category_mapping is None \ + and len(self.category_ids) != len(self.category_allow_list): + warnings.warn('Unexpected number of categories: Mismatch with category_allow_list. ' + 'Make sure all names in category_allow_list exist.') + + def clean_up_annotations_and_image_descriptions(self) -> None: + image_id_set = set(self.image_ids) + self.annotations = {k: v for k, v in self.annotations.items() if k in image_id_set} + self.image_descriptions = {k: v for k, v in self.image_descriptions.items() if k in image_id_set} + + @staticmethod + def filter_object_number(all_annotations: Dict[str, List[Annotation]], min_object_area: float, + min_objects_per_image: int, max_objects_per_image: int) -> Dict[str, List[Annotation]]: + filtered = {} + for image_id, annotations in all_annotations.items(): + annotations_with_min_area = [a for a in annotations if a.area > min_object_area] + if min_objects_per_image <= len(annotations_with_min_area) <= max_objects_per_image: + filtered[image_id] = annotations_with_min_area + return filtered + + def __len__(self): + return len(self.image_ids) + + def __getitem__(self, n: int) -> Dict[str, Any]: + image_id = self.get_image_id(n) + sample = self.get_image_description(image_id) + sample['annotations'] = self.get_annotation(image_id) + + if 'image' in self.keys: + sample['image_path'] = str(self.get_image_path(image_id)) + sample['image'] = self.load_image_from_disk(sample['image_path']) + sample['image'] = convert_pil_to_tensor(sample['image']) + sample['crop_bbox'], sample['flipped'], sample['image'] = self.image_transform(sample['image']) + sample['image'] = sample['image'].permute(1, 2, 0) + + for conditional, builder in self.conditional_builders.items(): + if conditional in self.keys: + sample[conditional] = builder.build(sample['annotations'], sample['crop_bbox'], sample['flipped']) + + if self.keys: + # only return specified keys + sample = {key: sample[key] for key in self.keys} + return sample + + def get_image_id(self, no: int) -> str: + return self.image_ids[no] + + def get_annotation(self, image_id: str) -> str: + return self.annotations[image_id] + + def get_textual_label_for_category_id(self, category_id: str) -> str: + return self.categories[category_id].name + + def get_textual_label_for_category_no(self, category_no: int) -> str: + return self.categories[self.get_category_id(category_no)].name + + def get_category_number(self, category_id: str) -> int: + return self.category_number[category_id] + + def get_category_id(self, category_no: int) -> str: + return self.category_ids[category_no] + + def get_image_description(self, image_id: str) -> Dict[str, Any]: + raise NotImplementedError() + + def get_path_structure(self): + raise NotImplementedError + + def get_image_path(self, image_id: str) -> Path: + raise NotImplementedError diff --git a/repositories/taming/data/annotated_objects_open_images.py b/repositories/taming/data/annotated_objects_open_images.py new file mode 100644 index 000000000..aede6803d --- /dev/null +++ b/repositories/taming/data/annotated_objects_open_images.py @@ -0,0 +1,137 @@ +from collections import defaultdict +from csv import DictReader, reader as TupleReader +from pathlib import Path +from typing import Dict, List, Any +import warnings + +from taming.data.annotated_objects_dataset import AnnotatedObjectsDataset +from taming.data.helper_types import Annotation, Category +from tqdm import tqdm + +OPEN_IMAGES_STRUCTURE = { + 'train': { + 'top_level': '', + 'class_descriptions': 'class-descriptions-boxable.csv', + 'annotations': 'oidv6-train-annotations-bbox.csv', + 'file_list': 'train-images-boxable.csv', + 'files': 'train' + }, + 'validation': { + 'top_level': '', + 'class_descriptions': 'class-descriptions-boxable.csv', + 'annotations': 'validation-annotations-bbox.csv', + 'file_list': 'validation-images.csv', + 'files': 'validation' + }, + 'test': { + 'top_level': '', + 'class_descriptions': 'class-descriptions-boxable.csv', + 'annotations': 'test-annotations-bbox.csv', + 'file_list': 'test-images.csv', + 'files': 'test' + } +} + + +def load_annotations(descriptor_path: Path, min_object_area: float, category_mapping: Dict[str, str], + category_no_for_id: Dict[str, int]) -> Dict[str, List[Annotation]]: + annotations: Dict[str, List[Annotation]] = defaultdict(list) + with open(descriptor_path) as file: + reader = DictReader(file) + for i, row in tqdm(enumerate(reader), total=14620000, desc='Loading OpenImages annotations'): + width = float(row['XMax']) - float(row['XMin']) + height = float(row['YMax']) - float(row['YMin']) + area = width * height + category_id = row['LabelName'] + if category_id in category_mapping: + category_id = category_mapping[category_id] + if area >= min_object_area and category_id in category_no_for_id: + annotations[row['ImageID']].append( + Annotation( + id=i, + image_id=row['ImageID'], + source=row['Source'], + category_id=category_id, + category_no=category_no_for_id[category_id], + confidence=float(row['Confidence']), + bbox=(float(row['XMin']), float(row['YMin']), width, height), + area=area, + is_occluded=bool(int(row['IsOccluded'])), + is_truncated=bool(int(row['IsTruncated'])), + is_group_of=bool(int(row['IsGroupOf'])), + is_depiction=bool(int(row['IsDepiction'])), + is_inside=bool(int(row['IsInside'])) + ) + ) + if 'train' in str(descriptor_path) and i < 14000000: + warnings.warn(f'Running with subset of Open Images. Train dataset has length [{len(annotations)}].') + return dict(annotations) + + +def load_image_ids(csv_path: Path) -> List[str]: + with open(csv_path) as file: + reader = DictReader(file) + return [row['image_name'] for row in reader] + + +def load_categories(csv_path: Path) -> Dict[str, Category]: + with open(csv_path) as file: + reader = TupleReader(file) + return {row[0]: Category(id=row[0], name=row[1], super_category=None) for row in reader} + + +class AnnotatedObjectsOpenImages(AnnotatedObjectsDataset): + def __init__(self, use_additional_parameters: bool, **kwargs): + """ + @param data_path: is the path to the following folder structure: + open_images/ + │ oidv6-train-annotations-bbox.csv + ├── class-descriptions-boxable.csv + ├── oidv6-train-annotations-bbox.csv + ├── test + │ ├── 000026e7ee790996.jpg + │ ├── 000062a39995e348.jpg + │ └── ... + ├── test-annotations-bbox.csv + ├── test-images.csv + ├── train + │ ├── 000002b66c9c498e.jpg + │ ├── 000002b97e5471a0.jpg + │ └── ... + ├── train-images-boxable.csv + ├── validation + │ ├── 0001eeaf4aed83f9.jpg + │ ├── 0004886b7d043cfd.jpg + │ └── ... + ├── validation-annotations-bbox.csv + └── validation-images.csv + @param: split: one of 'train', 'validation' or 'test' + @param: desired image size (returns square images) + """ + + super().__init__(**kwargs) + self.use_additional_parameters = use_additional_parameters + + self.categories = load_categories(self.paths['class_descriptions']) + self.filter_categories() + self.setup_category_id_and_number() + + self.image_descriptions = {} + annotations = load_annotations(self.paths['annotations'], self.min_object_area, self.category_mapping, + self.category_number) + self.annotations = self.filter_object_number(annotations, self.min_object_area, self.min_objects_per_image, + self.max_objects_per_image) + self.image_ids = list(self.annotations.keys()) + self.clean_up_annotations_and_image_descriptions() + + def get_path_structure(self) -> Dict[str, str]: + if self.split not in OPEN_IMAGES_STRUCTURE: + raise ValueError(f'Split [{self.split} does not exist for Open Images data.]') + return OPEN_IMAGES_STRUCTURE[self.split] + + def get_image_path(self, image_id: str) -> Path: + return self.paths['files'].joinpath(f'{image_id:0>16}.jpg') + + def get_image_description(self, image_id: str) -> Dict[str, Any]: + image_path = self.get_image_path(image_id) + return {'file_path': str(image_path), 'file_name': image_path.name} diff --git a/repositories/taming/data/base.py b/repositories/taming/data/base.py new file mode 100644 index 000000000..e21667df4 --- /dev/null +++ b/repositories/taming/data/base.py @@ -0,0 +1,70 @@ +import bisect +import numpy as np +import albumentations +from PIL import Image +from torch.utils.data import Dataset, ConcatDataset + + +class ConcatDatasetWithIndex(ConcatDataset): + """Modified from original pytorch code to return dataset idx""" + def __getitem__(self, idx): + if idx < 0: + if -idx > len(self): + raise ValueError("absolute value of index should not exceed dataset length") + idx = len(self) + idx + dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx) + if dataset_idx == 0: + sample_idx = idx + else: + sample_idx = idx - self.cumulative_sizes[dataset_idx - 1] + return self.datasets[dataset_idx][sample_idx], dataset_idx + + +class ImagePaths(Dataset): + def __init__(self, paths, size=None, random_crop=False, labels=None): + self.size = size + self.random_crop = random_crop + + self.labels = dict() if labels is None else labels + self.labels["file_path_"] = paths + self._length = len(paths) + + if self.size is not None and self.size > 0: + self.rescaler = albumentations.SmallestMaxSize(max_size = self.size) + if not self.random_crop: + self.cropper = albumentations.CenterCrop(height=self.size,width=self.size) + else: + self.cropper = albumentations.RandomCrop(height=self.size,width=self.size) + self.preprocessor = albumentations.Compose([self.rescaler, self.cropper]) + else: + self.preprocessor = lambda **kwargs: kwargs + + def __len__(self): + return self._length + + def preprocess_image(self, image_path): + image = Image.open(image_path) + if not image.mode == "RGB": + image = image.convert("RGB") + image = np.array(image).astype(np.uint8) + image = self.preprocessor(image=image)["image"] + image = (image/127.5 - 1.0).astype(np.float32) + return image + + def __getitem__(self, i): + example = dict() + example["image"] = self.preprocess_image(self.labels["file_path_"][i]) + for k in self.labels: + example[k] = self.labels[k][i] + return example + + +class NumpyPaths(ImagePaths): + def preprocess_image(self, image_path): + image = np.load(image_path).squeeze(0) # 3 x 1024 x 1024 + image = np.transpose(image, (1,2,0)) + image = Image.fromarray(image, mode="RGB") + image = np.array(image).astype(np.uint8) + image = self.preprocessor(image=image)["image"] + image = (image/127.5 - 1.0).astype(np.float32) + return image diff --git a/repositories/taming/data/coco.py b/repositories/taming/data/coco.py new file mode 100644 index 000000000..2b2f78384 --- /dev/null +++ b/repositories/taming/data/coco.py @@ -0,0 +1,176 @@ +import os +import json +import albumentations +import numpy as np +from PIL import Image +from tqdm import tqdm +from torch.utils.data import Dataset + +from taming.data.sflckr import SegmentationBase # for examples included in repo + + +class Examples(SegmentationBase): + def __init__(self, size=256, random_crop=False, interpolation="bicubic"): + super().__init__(data_csv="data/coco_examples.txt", + data_root="data/coco_images", + segmentation_root="data/coco_segmentations", + size=size, random_crop=random_crop, + interpolation=interpolation, + n_labels=183, shift_segmentation=True) + + +class CocoBase(Dataset): + """needed for (image, caption, segmentation) pairs""" + def __init__(self, size=None, dataroot="", datajson="", onehot_segmentation=False, use_stuffthing=False, + crop_size=None, force_no_crop=False, given_files=None): + self.split = self.get_split() + self.size = size + if crop_size is None: + self.crop_size = size + else: + self.crop_size = crop_size + + self.onehot = onehot_segmentation # return segmentation as rgb or one hot + self.stuffthing = use_stuffthing # include thing in segmentation + if self.onehot and not self.stuffthing: + raise NotImplemented("One hot mode is only supported for the " + "stuffthings version because labels are stored " + "a bit different.") + + data_json = datajson + with open(data_json) as json_file: + self.json_data = json.load(json_file) + self.img_id_to_captions = dict() + self.img_id_to_filepath = dict() + self.img_id_to_segmentation_filepath = dict() + + assert data_json.split("/")[-1] in ["captions_train2017.json", + "captions_val2017.json"] + if self.stuffthing: + self.segmentation_prefix = ( + "data/cocostuffthings/val2017" if + data_json.endswith("captions_val2017.json") else + "data/cocostuffthings/train2017") + else: + self.segmentation_prefix = ( + "data/coco/annotations/stuff_val2017_pixelmaps" if + data_json.endswith("captions_val2017.json") else + "data/coco/annotations/stuff_train2017_pixelmaps") + + imagedirs = self.json_data["images"] + self.labels = {"image_ids": list()} + for imgdir in tqdm(imagedirs, desc="ImgToPath"): + self.img_id_to_filepath[imgdir["id"]] = os.path.join(dataroot, imgdir["file_name"]) + self.img_id_to_captions[imgdir["id"]] = list() + pngfilename = imgdir["file_name"].replace("jpg", "png") + self.img_id_to_segmentation_filepath[imgdir["id"]] = os.path.join( + self.segmentation_prefix, pngfilename) + if given_files is not None: + if pngfilename in given_files: + self.labels["image_ids"].append(imgdir["id"]) + else: + self.labels["image_ids"].append(imgdir["id"]) + + capdirs = self.json_data["annotations"] + for capdir in tqdm(capdirs, desc="ImgToCaptions"): + # there are in average 5 captions per image + self.img_id_to_captions[capdir["image_id"]].append(np.array([capdir["caption"]])) + + self.rescaler = albumentations.SmallestMaxSize(max_size=self.size) + if self.split=="validation": + self.cropper = albumentations.CenterCrop(height=self.crop_size, width=self.crop_size) + else: + self.cropper = albumentations.RandomCrop(height=self.crop_size, width=self.crop_size) + self.preprocessor = albumentations.Compose( + [self.rescaler, self.cropper], + additional_targets={"segmentation": "image"}) + if force_no_crop: + self.rescaler = albumentations.Resize(height=self.size, width=self.size) + self.preprocessor = albumentations.Compose( + [self.rescaler], + additional_targets={"segmentation": "image"}) + + def __len__(self): + return len(self.labels["image_ids"]) + + def preprocess_image(self, image_path, segmentation_path): + image = Image.open(image_path) + if not image.mode == "RGB": + image = image.convert("RGB") + image = np.array(image).astype(np.uint8) + + segmentation = Image.open(segmentation_path) + if not self.onehot and not segmentation.mode == "RGB": + segmentation = segmentation.convert("RGB") + segmentation = np.array(segmentation).astype(np.uint8) + if self.onehot: + assert self.stuffthing + # stored in caffe format: unlabeled==255. stuff and thing from + # 0-181. to be compatible with the labels in + # https://github.com/nightrome/cocostuff/blob/master/labels.txt + # we shift stuffthing one to the right and put unlabeled in zero + # as long as segmentation is uint8 shifting to right handles the + # latter too + assert segmentation.dtype == np.uint8 + segmentation = segmentation + 1 + + processed = self.preprocessor(image=image, segmentation=segmentation) + image, segmentation = processed["image"], processed["segmentation"] + image = (image / 127.5 - 1.0).astype(np.float32) + + if self.onehot: + assert segmentation.dtype == np.uint8 + # make it one hot + n_labels = 183 + flatseg = np.ravel(segmentation) + onehot = np.zeros((flatseg.size, n_labels), dtype=np.bool) + onehot[np.arange(flatseg.size), flatseg] = True + onehot = onehot.reshape(segmentation.shape + (n_labels,)).astype(int) + segmentation = onehot + else: + segmentation = (segmentation / 127.5 - 1.0).astype(np.float32) + return image, segmentation + + def __getitem__(self, i): + img_path = self.img_id_to_filepath[self.labels["image_ids"][i]] + seg_path = self.img_id_to_segmentation_filepath[self.labels["image_ids"][i]] + image, segmentation = self.preprocess_image(img_path, seg_path) + captions = self.img_id_to_captions[self.labels["image_ids"][i]] + # randomly draw one of all available captions per image + caption = captions[np.random.randint(0, len(captions))] + example = {"image": image, + "caption": [str(caption[0])], + "segmentation": segmentation, + "img_path": img_path, + "seg_path": seg_path, + "filename_": img_path.split(os.sep)[-1] + } + return example + + +class CocoImagesAndCaptionsTrain(CocoBase): + """returns a pair of (image, caption)""" + def __init__(self, size, onehot_segmentation=False, use_stuffthing=False, crop_size=None, force_no_crop=False): + super().__init__(size=size, + dataroot="data/coco/train2017", + datajson="data/coco/annotations/captions_train2017.json", + onehot_segmentation=onehot_segmentation, + use_stuffthing=use_stuffthing, crop_size=crop_size, force_no_crop=force_no_crop) + + def get_split(self): + return "train" + + +class CocoImagesAndCaptionsValidation(CocoBase): + """returns a pair of (image, caption)""" + def __init__(self, size, onehot_segmentation=False, use_stuffthing=False, crop_size=None, force_no_crop=False, + given_files=None): + super().__init__(size=size, + dataroot="data/coco/val2017", + datajson="data/coco/annotations/captions_val2017.json", + onehot_segmentation=onehot_segmentation, + use_stuffthing=use_stuffthing, crop_size=crop_size, force_no_crop=force_no_crop, + given_files=given_files) + + def get_split(self): + return "validation" diff --git a/repositories/taming/data/conditional_builder/objects_bbox.py b/repositories/taming/data/conditional_builder/objects_bbox.py new file mode 100644 index 000000000..15881e76b --- /dev/null +++ b/repositories/taming/data/conditional_builder/objects_bbox.py @@ -0,0 +1,60 @@ +from itertools import cycle +from typing import List, Tuple, Callable, Optional + +from PIL import Image as pil_image, ImageDraw as pil_img_draw, ImageFont +from more_itertools.recipes import grouper +from taming.data.image_transforms import convert_pil_to_tensor +from torch import LongTensor, Tensor + +from taming.data.helper_types import BoundingBox, Annotation +from taming.data.conditional_builder.objects_center_points import ObjectsCenterPointsConditionalBuilder +from taming.data.conditional_builder.utils import COLOR_PALETTE, WHITE, GRAY_75, BLACK, additional_parameters_string, \ + pad_list, get_plot_font_size, absolute_bbox + + +class ObjectsBoundingBoxConditionalBuilder(ObjectsCenterPointsConditionalBuilder): + @property + def object_descriptor_length(self) -> int: + return 3 + + def _make_object_descriptors(self, annotations: List[Annotation]) -> List[Tuple[int, ...]]: + object_triples = [ + (self.object_representation(ann), *self.token_pair_from_bbox(ann.bbox)) + for ann in annotations + ] + empty_triple = (self.none, self.none, self.none) + object_triples = pad_list(object_triples, empty_triple, self.no_max_objects) + return object_triples + + def inverse_build(self, conditional: LongTensor) -> Tuple[List[Tuple[int, BoundingBox]], Optional[BoundingBox]]: + conditional_list = conditional.tolist() + crop_coordinates = None + if self.encode_crop: + crop_coordinates = self.bbox_from_token_pair(conditional_list[-2], conditional_list[-1]) + conditional_list = conditional_list[:-2] + object_triples = grouper(conditional_list, 3) + assert conditional.shape[0] == self.embedding_dim + return [ + (object_triple[0], self.bbox_from_token_pair(object_triple[1], object_triple[2])) + for object_triple in object_triples if object_triple[0] != self.none + ], crop_coordinates + + def plot(self, conditional: LongTensor, label_for_category_no: Callable[[int], str], figure_size: Tuple[int, int], + line_width: int = 3, font_size: Optional[int] = None) -> Tensor: + plot = pil_image.new('RGB', figure_size, WHITE) + draw = pil_img_draw.Draw(plot) + font = ImageFont.truetype( + "/usr/share/fonts/truetype/lato/Lato-Regular.ttf", + size=get_plot_font_size(font_size, figure_size) + ) + width, height = plot.size + description, crop_coordinates = self.inverse_build(conditional) + for (representation, bbox), color in zip(description, cycle(COLOR_PALETTE)): + annotation = self.representation_to_annotation(representation) + class_label = label_for_category_no(annotation.category_no) + ' ' + additional_parameters_string(annotation) + bbox = absolute_bbox(bbox, width, height) + draw.rectangle(bbox, outline=color, width=line_width) + draw.text((bbox[0] + line_width, bbox[1] + line_width), class_label, anchor='la', fill=BLACK, font=font) + if crop_coordinates is not None: + draw.rectangle(absolute_bbox(crop_coordinates, width, height), outline=GRAY_75, width=line_width) + return convert_pil_to_tensor(plot) / 127.5 - 1. diff --git a/repositories/taming/data/conditional_builder/objects_center_points.py b/repositories/taming/data/conditional_builder/objects_center_points.py new file mode 100644 index 000000000..9a480329c --- /dev/null +++ b/repositories/taming/data/conditional_builder/objects_center_points.py @@ -0,0 +1,168 @@ +import math +import random +import warnings +from itertools import cycle +from typing import List, Optional, Tuple, Callable + +from PIL import Image as pil_image, ImageDraw as pil_img_draw, ImageFont +from more_itertools.recipes import grouper +from taming.data.conditional_builder.utils import COLOR_PALETTE, WHITE, GRAY_75, BLACK, FULL_CROP, filter_annotations, \ + additional_parameters_string, horizontally_flip_bbox, pad_list, get_circle_size, get_plot_font_size, \ + absolute_bbox, rescale_annotations +from taming.data.helper_types import BoundingBox, Annotation +from taming.data.image_transforms import convert_pil_to_tensor +from torch import LongTensor, Tensor + + +class ObjectsCenterPointsConditionalBuilder: + def __init__(self, no_object_classes: int, no_max_objects: int, no_tokens: int, encode_crop: bool, + use_group_parameter: bool, use_additional_parameters: bool): + self.no_object_classes = no_object_classes + self.no_max_objects = no_max_objects + self.no_tokens = no_tokens + self.encode_crop = encode_crop + self.no_sections = int(math.sqrt(self.no_tokens)) + self.use_group_parameter = use_group_parameter + self.use_additional_parameters = use_additional_parameters + + @property + def none(self) -> int: + return self.no_tokens - 1 + + @property + def object_descriptor_length(self) -> int: + return 2 + + @property + def embedding_dim(self) -> int: + extra_length = 2 if self.encode_crop else 0 + return self.no_max_objects * self.object_descriptor_length + extra_length + + def tokenize_coordinates(self, x: float, y: float) -> int: + """ + Express 2d coordinates with one number. + Example: assume self.no_tokens = 16, then no_sections = 4: + 0 0 0 0 + 0 0 # 0 + 0 0 0 0 + 0 0 0 x + Then the # position corresponds to token 6, the x position to token 15. + @param x: float in [0, 1] + @param y: float in [0, 1] + @return: discrete tokenized coordinate + """ + x_discrete = int(round(x * (self.no_sections - 1))) + y_discrete = int(round(y * (self.no_sections - 1))) + return y_discrete * self.no_sections + x_discrete + + def coordinates_from_token(self, token: int) -> (float, float): + x = token % self.no_sections + y = token // self.no_sections + return x / (self.no_sections - 1), y / (self.no_sections - 1) + + def bbox_from_token_pair(self, token1: int, token2: int) -> BoundingBox: + x0, y0 = self.coordinates_from_token(token1) + x1, y1 = self.coordinates_from_token(token2) + return x0, y0, x1 - x0, y1 - y0 + + def token_pair_from_bbox(self, bbox: BoundingBox) -> Tuple[int, int]: + return self.tokenize_coordinates(bbox[0], bbox[1]), \ + self.tokenize_coordinates(bbox[0] + bbox[2], bbox[1] + bbox[3]) + + def inverse_build(self, conditional: LongTensor) \ + -> Tuple[List[Tuple[int, Tuple[float, float]]], Optional[BoundingBox]]: + conditional_list = conditional.tolist() + crop_coordinates = None + if self.encode_crop: + crop_coordinates = self.bbox_from_token_pair(conditional_list[-2], conditional_list[-1]) + conditional_list = conditional_list[:-2] + table_of_content = grouper(conditional_list, self.object_descriptor_length) + assert conditional.shape[0] == self.embedding_dim + return [ + (object_tuple[0], self.coordinates_from_token(object_tuple[1])) + for object_tuple in table_of_content if object_tuple[0] != self.none + ], crop_coordinates + + def plot(self, conditional: LongTensor, label_for_category_no: Callable[[int], str], figure_size: Tuple[int, int], + line_width: int = 3, font_size: Optional[int] = None) -> Tensor: + plot = pil_image.new('RGB', figure_size, WHITE) + draw = pil_img_draw.Draw(plot) + circle_size = get_circle_size(figure_size) + font = ImageFont.truetype('/usr/share/fonts/truetype/lato/Lato-Regular.ttf', + size=get_plot_font_size(font_size, figure_size)) + width, height = plot.size + description, crop_coordinates = self.inverse_build(conditional) + for (representation, (x, y)), color in zip(description, cycle(COLOR_PALETTE)): + x_abs, y_abs = x * width, y * height + ann = self.representation_to_annotation(representation) + label = label_for_category_no(ann.category_no) + ' ' + additional_parameters_string(ann) + ellipse_bbox = [x_abs - circle_size, y_abs - circle_size, x_abs + circle_size, y_abs + circle_size] + draw.ellipse(ellipse_bbox, fill=color, width=0) + draw.text((x_abs, y_abs), label, anchor='md', fill=BLACK, font=font) + if crop_coordinates is not None: + draw.rectangle(absolute_bbox(crop_coordinates, width, height), outline=GRAY_75, width=line_width) + return convert_pil_to_tensor(plot) / 127.5 - 1. + + def object_representation(self, annotation: Annotation) -> int: + modifier = 0 + if self.use_group_parameter: + modifier |= 1 * (annotation.is_group_of is True) + if self.use_additional_parameters: + modifier |= 2 * (annotation.is_occluded is True) + modifier |= 4 * (annotation.is_depiction is True) + modifier |= 8 * (annotation.is_inside is True) + return annotation.category_no + self.no_object_classes * modifier + + def representation_to_annotation(self, representation: int) -> Annotation: + category_no = representation % self.no_object_classes + modifier = representation // self.no_object_classes + # noinspection PyTypeChecker + return Annotation( + area=None, image_id=None, bbox=None, category_id=None, id=None, source=None, confidence=None, + category_no=category_no, + is_group_of=bool((modifier & 1) * self.use_group_parameter), + is_occluded=bool((modifier & 2) * self.use_additional_parameters), + is_depiction=bool((modifier & 4) * self.use_additional_parameters), + is_inside=bool((modifier & 8) * self.use_additional_parameters) + ) + + def _crop_encoder(self, crop_coordinates: BoundingBox) -> List[int]: + return list(self.token_pair_from_bbox(crop_coordinates)) + + def _make_object_descriptors(self, annotations: List[Annotation]) -> List[Tuple[int, ...]]: + object_tuples = [ + (self.object_representation(a), + self.tokenize_coordinates(a.bbox[0] + a.bbox[2] / 2, a.bbox[1] + a.bbox[3] / 2)) + for a in annotations + ] + empty_tuple = (self.none, self.none) + object_tuples = pad_list(object_tuples, empty_tuple, self.no_max_objects) + return object_tuples + + def build(self, annotations: List, crop_coordinates: Optional[BoundingBox] = None, horizontal_flip: bool = False) \ + -> LongTensor: + if len(annotations) == 0: + warnings.warn('Did not receive any annotations.') + if len(annotations) > self.no_max_objects: + warnings.warn('Received more annotations than allowed.') + annotations = annotations[:self.no_max_objects] + + if not crop_coordinates: + crop_coordinates = FULL_CROP + + random.shuffle(annotations) + annotations = filter_annotations(annotations, crop_coordinates) + if self.encode_crop: + annotations = rescale_annotations(annotations, FULL_CROP, horizontal_flip) + if horizontal_flip: + crop_coordinates = horizontally_flip_bbox(crop_coordinates) + extra = self._crop_encoder(crop_coordinates) + else: + annotations = rescale_annotations(annotations, crop_coordinates, horizontal_flip) + extra = [] + + object_tuples = self._make_object_descriptors(annotations) + flattened = [token for tuple_ in object_tuples for token in tuple_] + extra + assert len(flattened) == self.embedding_dim + assert all(0 <= value < self.no_tokens for value in flattened) + return LongTensor(flattened) diff --git a/repositories/taming/data/conditional_builder/utils.py b/repositories/taming/data/conditional_builder/utils.py new file mode 100644 index 000000000..d0ee175f2 --- /dev/null +++ b/repositories/taming/data/conditional_builder/utils.py @@ -0,0 +1,105 @@ +import importlib +from typing import List, Any, Tuple, Optional + +from taming.data.helper_types import BoundingBox, Annotation + +# source: seaborn, color palette tab10 +COLOR_PALETTE = [(30, 118, 179), (255, 126, 13), (43, 159, 43), (213, 38, 39), (147, 102, 188), + (139, 85, 74), (226, 118, 193), (126, 126, 126), (187, 188, 33), (22, 189, 206)] +BLACK = (0, 0, 0) +GRAY_75 = (63, 63, 63) +GRAY_50 = (127, 127, 127) +GRAY_25 = (191, 191, 191) +WHITE = (255, 255, 255) +FULL_CROP = (0., 0., 1., 1.) + + +def intersection_area(rectangle1: BoundingBox, rectangle2: BoundingBox) -> float: + """ + Give intersection area of two rectangles. + @param rectangle1: (x0, y0, w, h) of first rectangle + @param rectangle2: (x0, y0, w, h) of second rectangle + """ + rectangle1 = rectangle1[0], rectangle1[1], rectangle1[0] + rectangle1[2], rectangle1[1] + rectangle1[3] + rectangle2 = rectangle2[0], rectangle2[1], rectangle2[0] + rectangle2[2], rectangle2[1] + rectangle2[3] + x_overlap = max(0., min(rectangle1[2], rectangle2[2]) - max(rectangle1[0], rectangle2[0])) + y_overlap = max(0., min(rectangle1[3], rectangle2[3]) - max(rectangle1[1], rectangle2[1])) + return x_overlap * y_overlap + + +def horizontally_flip_bbox(bbox: BoundingBox) -> BoundingBox: + return 1 - (bbox[0] + bbox[2]), bbox[1], bbox[2], bbox[3] + + +def absolute_bbox(relative_bbox: BoundingBox, width: int, height: int) -> Tuple[int, int, int, int]: + bbox = relative_bbox + bbox = bbox[0] * width, bbox[1] * height, (bbox[0] + bbox[2]) * width, (bbox[1] + bbox[3]) * height + return int(bbox[0]), int(bbox[1]), int(bbox[2]), int(bbox[3]) + + +def pad_list(list_: List, pad_element: Any, pad_to_length: int) -> List: + return list_ + [pad_element for _ in range(pad_to_length - len(list_))] + + +def rescale_annotations(annotations: List[Annotation], crop_coordinates: BoundingBox, flip: bool) -> \ + List[Annotation]: + def clamp(x: float): + return max(min(x, 1.), 0.) + + def rescale_bbox(bbox: BoundingBox) -> BoundingBox: + x0 = clamp((bbox[0] - crop_coordinates[0]) / crop_coordinates[2]) + y0 = clamp((bbox[1] - crop_coordinates[1]) / crop_coordinates[3]) + w = min(bbox[2] / crop_coordinates[2], 1 - x0) + h = min(bbox[3] / crop_coordinates[3], 1 - y0) + if flip: + x0 = 1 - (x0 + w) + return x0, y0, w, h + + return [a._replace(bbox=rescale_bbox(a.bbox)) for a in annotations] + + +def filter_annotations(annotations: List[Annotation], crop_coordinates: BoundingBox) -> List: + return [a for a in annotations if intersection_area(a.bbox, crop_coordinates) > 0.0] + + +def additional_parameters_string(annotation: Annotation, short: bool = True) -> str: + sl = slice(1) if short else slice(None) + string = '' + if not (annotation.is_group_of or annotation.is_occluded or annotation.is_depiction or annotation.is_inside): + return string + if annotation.is_group_of: + string += 'group'[sl] + ',' + if annotation.is_occluded: + string += 'occluded'[sl] + ',' + if annotation.is_depiction: + string += 'depiction'[sl] + ',' + if annotation.is_inside: + string += 'inside'[sl] + return '(' + string.strip(",") + ')' + + +def get_plot_font_size(font_size: Optional[int], figure_size: Tuple[int, int]) -> int: + if font_size is None: + font_size = 10 + if max(figure_size) >= 256: + font_size = 12 + if max(figure_size) >= 512: + font_size = 15 + return font_size + + +def get_circle_size(figure_size: Tuple[int, int]) -> int: + circle_size = 2 + if max(figure_size) >= 256: + circle_size = 3 + if max(figure_size) >= 512: + circle_size = 4 + return circle_size + + +def load_object_from_string(object_string: str) -> Any: + """ + Source: https://stackoverflow.com/a/10773699 + """ + module_name, class_name = object_string.rsplit(".", 1) + return getattr(importlib.import_module(module_name), class_name) diff --git a/repositories/taming/data/custom.py b/repositories/taming/data/custom.py new file mode 100644 index 000000000..33f302a4b --- /dev/null +++ b/repositories/taming/data/custom.py @@ -0,0 +1,38 @@ +import os +import numpy as np +import albumentations +from torch.utils.data import Dataset + +from taming.data.base import ImagePaths, NumpyPaths, ConcatDatasetWithIndex + + +class CustomBase(Dataset): + def __init__(self, *args, **kwargs): + super().__init__() + self.data = None + + def __len__(self): + return len(self.data) + + def __getitem__(self, i): + example = self.data[i] + return example + + + +class CustomTrain(CustomBase): + def __init__(self, size, training_images_list_file): + super().__init__() + with open(training_images_list_file, "r") as f: + paths = f.read().splitlines() + self.data = ImagePaths(paths=paths, size=size, random_crop=False) + + +class CustomTest(CustomBase): + def __init__(self, size, test_images_list_file): + super().__init__() + with open(test_images_list_file, "r") as f: + paths = f.read().splitlines() + self.data = ImagePaths(paths=paths, size=size, random_crop=False) + + diff --git a/repositories/taming/data/faceshq.py b/repositories/taming/data/faceshq.py new file mode 100644 index 000000000..6912d04b6 --- /dev/null +++ b/repositories/taming/data/faceshq.py @@ -0,0 +1,134 @@ +import os +import numpy as np +import albumentations +from torch.utils.data import Dataset + +from taming.data.base import ImagePaths, NumpyPaths, ConcatDatasetWithIndex + + +class FacesBase(Dataset): + def __init__(self, *args, **kwargs): + super().__init__() + self.data = None + self.keys = None + + def __len__(self): + return len(self.data) + + def __getitem__(self, i): + example = self.data[i] + ex = {} + if self.keys is not None: + for k in self.keys: + ex[k] = example[k] + else: + ex = example + return ex + + +class CelebAHQTrain(FacesBase): + def __init__(self, size, keys=None): + super().__init__() + root = "data/celebahq" + with open("data/celebahqtrain.txt", "r") as f: + relpaths = f.read().splitlines() + paths = [os.path.join(root, relpath) for relpath in relpaths] + self.data = NumpyPaths(paths=paths, size=size, random_crop=False) + self.keys = keys + + +class CelebAHQValidation(FacesBase): + def __init__(self, size, keys=None): + super().__init__() + root = "data/celebahq" + with open("data/celebahqvalidation.txt", "r") as f: + relpaths = f.read().splitlines() + paths = [os.path.join(root, relpath) for relpath in relpaths] + self.data = NumpyPaths(paths=paths, size=size, random_crop=False) + self.keys = keys + + +class FFHQTrain(FacesBase): + def __init__(self, size, keys=None): + super().__init__() + root = "data/ffhq" + with open("data/ffhqtrain.txt", "r") as f: + relpaths = f.read().splitlines() + paths = [os.path.join(root, relpath) for relpath in relpaths] + self.data = ImagePaths(paths=paths, size=size, random_crop=False) + self.keys = keys + + +class FFHQValidation(FacesBase): + def __init__(self, size, keys=None): + super().__init__() + root = "data/ffhq" + with open("data/ffhqvalidation.txt", "r") as f: + relpaths = f.read().splitlines() + paths = [os.path.join(root, relpath) for relpath in relpaths] + self.data = ImagePaths(paths=paths, size=size, random_crop=False) + self.keys = keys + + +class FacesHQTrain(Dataset): + # CelebAHQ [0] + FFHQ [1] + def __init__(self, size, keys=None, crop_size=None, coord=False): + d1 = CelebAHQTrain(size=size, keys=keys) + d2 = FFHQTrain(size=size, keys=keys) + self.data = ConcatDatasetWithIndex([d1, d2]) + self.coord = coord + if crop_size is not None: + self.cropper = albumentations.RandomCrop(height=crop_size,width=crop_size) + if self.coord: + self.cropper = albumentations.Compose([self.cropper], + additional_targets={"coord": "image"}) + + def __len__(self): + return len(self.data) + + def __getitem__(self, i): + ex, y = self.data[i] + if hasattr(self, "cropper"): + if not self.coord: + out = self.cropper(image=ex["image"]) + ex["image"] = out["image"] + else: + h,w,_ = ex["image"].shape + coord = np.arange(h*w).reshape(h,w,1)/(h*w) + out = self.cropper(image=ex["image"], coord=coord) + ex["image"] = out["image"] + ex["coord"] = out["coord"] + ex["class"] = y + return ex + + +class FacesHQValidation(Dataset): + # CelebAHQ [0] + FFHQ [1] + def __init__(self, size, keys=None, crop_size=None, coord=False): + d1 = CelebAHQValidation(size=size, keys=keys) + d2 = FFHQValidation(size=size, keys=keys) + self.data = ConcatDatasetWithIndex([d1, d2]) + self.coord = coord + if crop_size is not None: + self.cropper = albumentations.CenterCrop(height=crop_size,width=crop_size) + if self.coord: + self.cropper = albumentations.Compose([self.cropper], + additional_targets={"coord": "image"}) + + def __len__(self): + return len(self.data) + + def __getitem__(self, i): + ex, y = self.data[i] + if hasattr(self, "cropper"): + if not self.coord: + out = self.cropper(image=ex["image"]) + ex["image"] = out["image"] + else: + h,w,_ = ex["image"].shape + coord = np.arange(h*w).reshape(h,w,1)/(h*w) + out = self.cropper(image=ex["image"], coord=coord) + ex["image"] = out["image"] + ex["coord"] = out["coord"] + ex["class"] = y + return ex diff --git a/repositories/taming/data/helper_types.py b/repositories/taming/data/helper_types.py new file mode 100644 index 000000000..fb51e301d --- /dev/null +++ b/repositories/taming/data/helper_types.py @@ -0,0 +1,49 @@ +from typing import Dict, Tuple, Optional, NamedTuple, Union +from PIL.Image import Image as pil_image +from torch import Tensor + +try: + from typing import Literal +except ImportError: + from typing_extensions import Literal + +Image = Union[Tensor, pil_image] +BoundingBox = Tuple[float, float, float, float] # x0, y0, w, h +CropMethodType = Literal['none', 'random', 'center', 'random-2d'] +SplitType = Literal['train', 'validation', 'test'] + + +class ImageDescription(NamedTuple): + id: int + file_name: str + original_size: Tuple[int, int] # w, h + url: Optional[str] = None + license: Optional[int] = None + coco_url: Optional[str] = None + date_captured: Optional[str] = None + flickr_url: Optional[str] = None + flickr_id: Optional[str] = None + coco_id: Optional[str] = None + + +class Category(NamedTuple): + id: str + super_category: Optional[str] + name: str + + +class Annotation(NamedTuple): + area: float + image_id: str + bbox: BoundingBox + category_no: int + category_id: str + id: Optional[int] = None + source: Optional[str] = None + confidence: Optional[float] = None + is_group_of: Optional[bool] = None + is_truncated: Optional[bool] = None + is_occluded: Optional[bool] = None + is_depiction: Optional[bool] = None + is_inside: Optional[bool] = None + segmentation: Optional[Dict] = None diff --git a/repositories/taming/data/image_transforms.py b/repositories/taming/data/image_transforms.py new file mode 100644 index 000000000..657ac3321 --- /dev/null +++ b/repositories/taming/data/image_transforms.py @@ -0,0 +1,132 @@ +import random +import warnings +from typing import Union + +import torch +from torch import Tensor +from torchvision.transforms import RandomCrop, functional as F, CenterCrop, RandomHorizontalFlip, PILToTensor +from torchvision.transforms.functional import _get_image_size as get_image_size + +from taming.data.helper_types import BoundingBox, Image + +pil_to_tensor = PILToTensor() + + +def convert_pil_to_tensor(image: Image) -> Tensor: + with warnings.catch_warnings(): + # to filter PyTorch UserWarning as described here: https://github.com/pytorch/vision/issues/2194 + warnings.simplefilter("ignore") + return pil_to_tensor(image) + + +class RandomCrop1dReturnCoordinates(RandomCrop): + def forward(self, img: Image) -> (BoundingBox, Image): + """ + Additionally to cropping, returns the relative coordinates of the crop bounding box. + Args: + img (PIL Image or Tensor): Image to be cropped. + + Returns: + Bounding box: x0, y0, w, h + PIL Image or Tensor: Cropped image. + + Based on: + torchvision.transforms.RandomCrop, torchvision 1.7.0 + """ + if self.padding is not None: + img = F.pad(img, self.padding, self.fill, self.padding_mode) + + width, height = get_image_size(img) + # pad the width if needed + if self.pad_if_needed and width < self.size[1]: + padding = [self.size[1] - width, 0] + img = F.pad(img, padding, self.fill, self.padding_mode) + # pad the height if needed + if self.pad_if_needed and height < self.size[0]: + padding = [0, self.size[0] - height] + img = F.pad(img, padding, self.fill, self.padding_mode) + + i, j, h, w = self.get_params(img, self.size) + bbox = (j / width, i / height, w / width, h / height) # x0, y0, w, h + return bbox, F.crop(img, i, j, h, w) + + +class Random2dCropReturnCoordinates(torch.nn.Module): + """ + Additionally to cropping, returns the relative coordinates of the crop bounding box. + Args: + img (PIL Image or Tensor): Image to be cropped. + + Returns: + Bounding box: x0, y0, w, h + PIL Image or Tensor: Cropped image. + + Based on: + torchvision.transforms.RandomCrop, torchvision 1.7.0 + """ + + def __init__(self, min_size: int): + super().__init__() + self.min_size = min_size + + def forward(self, img: Image) -> (BoundingBox, Image): + width, height = get_image_size(img) + max_size = min(width, height) + if max_size <= self.min_size: + size = max_size + else: + size = random.randint(self.min_size, max_size) + top = random.randint(0, height - size) + left = random.randint(0, width - size) + bbox = left / width, top / height, size / width, size / height + return bbox, F.crop(img, top, left, size, size) + + +class CenterCropReturnCoordinates(CenterCrop): + @staticmethod + def get_bbox_of_center_crop(width: int, height: int) -> BoundingBox: + if width > height: + w = height / width + h = 1.0 + x0 = 0.5 - w / 2 + y0 = 0. + else: + w = 1.0 + h = width / height + x0 = 0. + y0 = 0.5 - h / 2 + return x0, y0, w, h + + def forward(self, img: Union[Image, Tensor]) -> (BoundingBox, Union[Image, Tensor]): + """ + Additionally to cropping, returns the relative coordinates of the crop bounding box. + Args: + img (PIL Image or Tensor): Image to be cropped. + + Returns: + Bounding box: x0, y0, w, h + PIL Image or Tensor: Cropped image. + Based on: + torchvision.transforms.RandomHorizontalFlip (version 1.7.0) + """ + width, height = get_image_size(img) + return self.get_bbox_of_center_crop(width, height), F.center_crop(img, self.size) + + +class RandomHorizontalFlipReturn(RandomHorizontalFlip): + def forward(self, img: Image) -> (bool, Image): + """ + Additionally to flipping, returns a boolean whether it was flipped or not. + Args: + img (PIL Image or Tensor): Image to be flipped. + + Returns: + flipped: whether the image was flipped or not + PIL Image or Tensor: Randomly flipped image. + + Based on: + torchvision.transforms.RandomHorizontalFlip (version 1.7.0) + """ + if torch.rand(1) < self.p: + return True, F.hflip(img) + return False, img diff --git a/repositories/taming/data/imagenet.py b/repositories/taming/data/imagenet.py new file mode 100644 index 000000000..9a02ec44b --- /dev/null +++ b/repositories/taming/data/imagenet.py @@ -0,0 +1,558 @@ +import os, tarfile, glob, shutil +import yaml +import numpy as np +from tqdm import tqdm +from PIL import Image +import albumentations +from omegaconf import OmegaConf +from torch.utils.data import Dataset + +from taming.data.base import ImagePaths +from taming.util import download, retrieve +import taming.data.utils as bdu + + +def give_synsets_from_indices(indices, path_to_yaml="data/imagenet_idx_to_synset.yaml"): + synsets = [] + with open(path_to_yaml) as f: + di2s = yaml.load(f) + for idx in indices: + synsets.append(str(di2s[idx])) + print("Using {} different synsets for construction of Restriced Imagenet.".format(len(synsets))) + return synsets + + +def str_to_indices(string): + """Expects a string in the format '32-123, 256, 280-321'""" + assert not string.endswith(","), "provided string '{}' ends with a comma, pls remove it".format(string) + subs = string.split(",") + indices = [] + for sub in subs: + subsubs = sub.split("-") + assert len(subsubs) > 0 + if len(subsubs) == 1: + indices.append(int(subsubs[0])) + else: + rang = [j for j in range(int(subsubs[0]), int(subsubs[1]))] + indices.extend(rang) + return sorted(indices) + + +class ImageNetBase(Dataset): + def __init__(self, config=None): + self.config = config or OmegaConf.create() + if not type(self.config)==dict: + self.config = OmegaConf.to_container(self.config) + self._prepare() + self._prepare_synset_to_human() + self._prepare_idx_to_synset() + self._load() + + def __len__(self): + return len(self.data) + + def __getitem__(self, i): + return self.data[i] + + def _prepare(self): + raise NotImplementedError() + + def _filter_relpaths(self, relpaths): + ignore = set([ + "n06596364_9591.JPEG", + ]) + relpaths = [rpath for rpath in relpaths if not rpath.split("/")[-1] in ignore] + if "sub_indices" in self.config: + indices = str_to_indices(self.config["sub_indices"]) + synsets = give_synsets_from_indices(indices, path_to_yaml=self.idx2syn) # returns a list of strings + files = [] + for rpath in relpaths: + syn = rpath.split("/")[0] + if syn in synsets: + files.append(rpath) + return files + else: + return relpaths + + def _prepare_synset_to_human(self): + SIZE = 2655750 + URL = "https://heibox.uni-heidelberg.de/f/9f28e956cd304264bb82/?dl=1" + self.human_dict = os.path.join(self.root, "synset_human.txt") + if (not os.path.exists(self.human_dict) or + not os.path.getsize(self.human_dict)==SIZE): + download(URL, self.human_dict) + + def _prepare_idx_to_synset(self): + URL = "https://heibox.uni-heidelberg.de/f/d835d5b6ceda4d3aa910/?dl=1" + self.idx2syn = os.path.join(self.root, "index_synset.yaml") + if (not os.path.exists(self.idx2syn)): + download(URL, self.idx2syn) + + def _load(self): + with open(self.txt_filelist, "r") as f: + self.relpaths = f.read().splitlines() + l1 = len(self.relpaths) + self.relpaths = self._filter_relpaths(self.relpaths) + print("Removed {} files from filelist during filtering.".format(l1 - len(self.relpaths))) + + self.synsets = [p.split("/")[0] for p in self.relpaths] + self.abspaths = [os.path.join(self.datadir, p) for p in self.relpaths] + + unique_synsets = np.unique(self.synsets) + class_dict = dict((synset, i) for i, synset in enumerate(unique_synsets)) + self.class_labels = [class_dict[s] for s in self.synsets] + + with open(self.human_dict, "r") as f: + human_dict = f.read().splitlines() + human_dict = dict(line.split(maxsplit=1) for line in human_dict) + + self.human_labels = [human_dict[s] for s in self.synsets] + + labels = { + "relpath": np.array(self.relpaths), + "synsets": np.array(self.synsets), + "class_label": np.array(self.class_labels), + "human_label": np.array(self.human_labels), + } + self.data = ImagePaths(self.abspaths, + labels=labels, + size=retrieve(self.config, "size", default=0), + random_crop=self.random_crop) + + +class ImageNetTrain(ImageNetBase): + NAME = "ILSVRC2012_train" + URL = "http://www.image-net.org/challenges/LSVRC/2012/" + AT_HASH = "a306397ccf9c2ead27155983c254227c0fd938e2" + FILES = [ + "ILSVRC2012_img_train.tar", + ] + SIZES = [ + 147897477120, + ] + + def _prepare(self): + self.random_crop = retrieve(self.config, "ImageNetTrain/random_crop", + default=True) + cachedir = os.environ.get("XDG_CACHE_HOME", os.path.expanduser("~/.cache")) + self.root = os.path.join(cachedir, "autoencoders/data", self.NAME) + self.datadir = os.path.join(self.root, "data") + self.txt_filelist = os.path.join(self.root, "filelist.txt") + self.expected_length = 1281167 + if not bdu.is_prepared(self.root): + # prep + print("Preparing dataset {} in {}".format(self.NAME, self.root)) + + datadir = self.datadir + if not os.path.exists(datadir): + path = os.path.join(self.root, self.FILES[0]) + if not os.path.exists(path) or not os.path.getsize(path)==self.SIZES[0]: + import academictorrents as at + atpath = at.get(self.AT_HASH, datastore=self.root) + assert atpath == path + + print("Extracting {} to {}".format(path, datadir)) + os.makedirs(datadir, exist_ok=True) + with tarfile.open(path, "r:") as tar: + tar.extractall(path=datadir) + + print("Extracting sub-tars.") + subpaths = sorted(glob.glob(os.path.join(datadir, "*.tar"))) + for subpath in tqdm(subpaths): + subdir = subpath[:-len(".tar")] + os.makedirs(subdir, exist_ok=True) + with tarfile.open(subpath, "r:") as tar: + tar.extractall(path=subdir) + + + filelist = glob.glob(os.path.join(datadir, "**", "*.JPEG")) + filelist = [os.path.relpath(p, start=datadir) for p in filelist] + filelist = sorted(filelist) + filelist = "\n".join(filelist)+"\n" + with open(self.txt_filelist, "w") as f: + f.write(filelist) + + bdu.mark_prepared(self.root) + + +class ImageNetValidation(ImageNetBase): + NAME = "ILSVRC2012_validation" + URL = "http://www.image-net.org/challenges/LSVRC/2012/" + AT_HASH = "5d6d0df7ed81efd49ca99ea4737e0ae5e3a5f2e5" + VS_URL = "https://heibox.uni-heidelberg.de/f/3e0f6e9c624e45f2bd73/?dl=1" + FILES = [ + "ILSVRC2012_img_val.tar", + "validation_synset.txt", + ] + SIZES = [ + 6744924160, + 1950000, + ] + + def _prepare(self): + self.random_crop = retrieve(self.config, "ImageNetValidation/random_crop", + default=False) + cachedir = os.environ.get("XDG_CACHE_HOME", os.path.expanduser("~/.cache")) + self.root = os.path.join(cachedir, "autoencoders/data", self.NAME) + self.datadir = os.path.join(self.root, "data") + self.txt_filelist = os.path.join(self.root, "filelist.txt") + self.expected_length = 50000 + if not bdu.is_prepared(self.root): + # prep + print("Preparing dataset {} in {}".format(self.NAME, self.root)) + + datadir = self.datadir + if not os.path.exists(datadir): + path = os.path.join(self.root, self.FILES[0]) + if not os.path.exists(path) or not os.path.getsize(path)==self.SIZES[0]: + import academictorrents as at + atpath = at.get(self.AT_HASH, datastore=self.root) + assert atpath == path + + print("Extracting {} to {}".format(path, datadir)) + os.makedirs(datadir, exist_ok=True) + with tarfile.open(path, "r:") as tar: + tar.extractall(path=datadir) + + vspath = os.path.join(self.root, self.FILES[1]) + if not os.path.exists(vspath) or not os.path.getsize(vspath)==self.SIZES[1]: + download(self.VS_URL, vspath) + + with open(vspath, "r") as f: + synset_dict = f.read().splitlines() + synset_dict = dict(line.split() for line in synset_dict) + + print("Reorganizing into synset folders") + synsets = np.unique(list(synset_dict.values())) + for s in synsets: + os.makedirs(os.path.join(datadir, s), exist_ok=True) + for k, v in synset_dict.items(): + src = os.path.join(datadir, k) + dst = os.path.join(datadir, v) + shutil.move(src, dst) + + filelist = glob.glob(os.path.join(datadir, "**", "*.JPEG")) + filelist = [os.path.relpath(p, start=datadir) for p in filelist] + filelist = sorted(filelist) + filelist = "\n".join(filelist)+"\n" + with open(self.txt_filelist, "w") as f: + f.write(filelist) + + bdu.mark_prepared(self.root) + + +def get_preprocessor(size=None, random_crop=False, additional_targets=None, + crop_size=None): + if size is not None and size > 0: + transforms = list() + rescaler = albumentations.SmallestMaxSize(max_size = size) + transforms.append(rescaler) + if not random_crop: + cropper = albumentations.CenterCrop(height=size,width=size) + transforms.append(cropper) + else: + cropper = albumentations.RandomCrop(height=size,width=size) + transforms.append(cropper) + flipper = albumentations.HorizontalFlip() + transforms.append(flipper) + preprocessor = albumentations.Compose(transforms, + additional_targets=additional_targets) + elif crop_size is not None and crop_size > 0: + if not random_crop: + cropper = albumentations.CenterCrop(height=crop_size,width=crop_size) + else: + cropper = albumentations.RandomCrop(height=crop_size,width=crop_size) + transforms = [cropper] + preprocessor = albumentations.Compose(transforms, + additional_targets=additional_targets) + else: + preprocessor = lambda **kwargs: kwargs + return preprocessor + + +def rgba_to_depth(x): + assert x.dtype == np.uint8 + assert len(x.shape) == 3 and x.shape[2] == 4 + y = x.copy() + y.dtype = np.float32 + y = y.reshape(x.shape[:2]) + return np.ascontiguousarray(y) + + +class BaseWithDepth(Dataset): + DEFAULT_DEPTH_ROOT="data/imagenet_depth" + + def __init__(self, config=None, size=None, random_crop=False, + crop_size=None, root=None): + self.config = config + self.base_dset = self.get_base_dset() + self.preprocessor = get_preprocessor( + size=size, + crop_size=crop_size, + random_crop=random_crop, + additional_targets={"depth": "image"}) + self.crop_size = crop_size + if self.crop_size is not None: + self.rescaler = albumentations.Compose( + [albumentations.SmallestMaxSize(max_size = self.crop_size)], + additional_targets={"depth": "image"}) + if root is not None: + self.DEFAULT_DEPTH_ROOT = root + + def __len__(self): + return len(self.base_dset) + + def preprocess_depth(self, path): + rgba = np.array(Image.open(path)) + depth = rgba_to_depth(rgba) + depth = (depth - depth.min())/max(1e-8, depth.max()-depth.min()) + depth = 2.0*depth-1.0 + return depth + + def __getitem__(self, i): + e = self.base_dset[i] + e["depth"] = self.preprocess_depth(self.get_depth_path(e)) + # up if necessary + h,w,c = e["image"].shape + if self.crop_size and min(h,w) < self.crop_size: + # have to upscale to be able to crop - this just uses bilinear + out = self.rescaler(image=e["image"], depth=e["depth"]) + e["image"] = out["image"] + e["depth"] = out["depth"] + transformed = self.preprocessor(image=e["image"], depth=e["depth"]) + e["image"] = transformed["image"] + e["depth"] = transformed["depth"] + return e + + +class ImageNetTrainWithDepth(BaseWithDepth): + # default to random_crop=True + def __init__(self, random_crop=True, sub_indices=None, **kwargs): + self.sub_indices = sub_indices + super().__init__(random_crop=random_crop, **kwargs) + + def get_base_dset(self): + if self.sub_indices is None: + return ImageNetTrain() + else: + return ImageNetTrain({"sub_indices": self.sub_indices}) + + def get_depth_path(self, e): + fid = os.path.splitext(e["relpath"])[0]+".png" + fid = os.path.join(self.DEFAULT_DEPTH_ROOT, "train", fid) + return fid + + +class ImageNetValidationWithDepth(BaseWithDepth): + def __init__(self, sub_indices=None, **kwargs): + self.sub_indices = sub_indices + super().__init__(**kwargs) + + def get_base_dset(self): + if self.sub_indices is None: + return ImageNetValidation() + else: + return ImageNetValidation({"sub_indices": self.sub_indices}) + + def get_depth_path(self, e): + fid = os.path.splitext(e["relpath"])[0]+".png" + fid = os.path.join(self.DEFAULT_DEPTH_ROOT, "val", fid) + return fid + + +class RINTrainWithDepth(ImageNetTrainWithDepth): + def __init__(self, config=None, size=None, random_crop=True, crop_size=None): + sub_indices = "30-32, 33-37, 151-268, 281-285, 80-100, 365-382, 389-397, 118-121, 300-319" + super().__init__(config=config, size=size, random_crop=random_crop, + sub_indices=sub_indices, crop_size=crop_size) + + +class RINValidationWithDepth(ImageNetValidationWithDepth): + def __init__(self, config=None, size=None, random_crop=False, crop_size=None): + sub_indices = "30-32, 33-37, 151-268, 281-285, 80-100, 365-382, 389-397, 118-121, 300-319" + super().__init__(config=config, size=size, random_crop=random_crop, + sub_indices=sub_indices, crop_size=crop_size) + + +class DRINExamples(Dataset): + def __init__(self): + self.preprocessor = get_preprocessor(size=256, additional_targets={"depth": "image"}) + with open("data/drin_examples.txt", "r") as f: + relpaths = f.read().splitlines() + self.image_paths = [os.path.join("data/drin_images", + relpath) for relpath in relpaths] + self.depth_paths = [os.path.join("data/drin_depth", + relpath.replace(".JPEG", ".png")) for relpath in relpaths] + + def __len__(self): + return len(self.image_paths) + + def preprocess_image(self, image_path): + image = Image.open(image_path) + if not image.mode == "RGB": + image = image.convert("RGB") + image = np.array(image).astype(np.uint8) + image = self.preprocessor(image=image)["image"] + image = (image/127.5 - 1.0).astype(np.float32) + return image + + def preprocess_depth(self, path): + rgba = np.array(Image.open(path)) + depth = rgba_to_depth(rgba) + depth = (depth - depth.min())/max(1e-8, depth.max()-depth.min()) + depth = 2.0*depth-1.0 + return depth + + def __getitem__(self, i): + e = dict() + e["image"] = self.preprocess_image(self.image_paths[i]) + e["depth"] = self.preprocess_depth(self.depth_paths[i]) + transformed = self.preprocessor(image=e["image"], depth=e["depth"]) + e["image"] = transformed["image"] + e["depth"] = transformed["depth"] + return e + + +def imscale(x, factor, keepshapes=False, keepmode="bicubic"): + if factor is None or factor==1: + return x + + dtype = x.dtype + assert dtype in [np.float32, np.float64] + assert x.min() >= -1 + assert x.max() <= 1 + + keepmode = {"nearest": Image.NEAREST, "bilinear": Image.BILINEAR, + "bicubic": Image.BICUBIC}[keepmode] + + lr = (x+1.0)*127.5 + lr = lr.clip(0,255).astype(np.uint8) + lr = Image.fromarray(lr) + + h, w, _ = x.shape + nh = h//factor + nw = w//factor + assert nh > 0 and nw > 0, (nh, nw) + + lr = lr.resize((nw,nh), Image.BICUBIC) + if keepshapes: + lr = lr.resize((w,h), keepmode) + lr = np.array(lr)/127.5-1.0 + lr = lr.astype(dtype) + + return lr + + +class ImageNetScale(Dataset): + def __init__(self, size=None, crop_size=None, random_crop=False, + up_factor=None, hr_factor=None, keep_mode="bicubic"): + self.base = self.get_base() + + self.size = size + self.crop_size = crop_size if crop_size is not None else self.size + self.random_crop = random_crop + self.up_factor = up_factor + self.hr_factor = hr_factor + self.keep_mode = keep_mode + + transforms = list() + + if self.size is not None and self.size > 0: + rescaler = albumentations.SmallestMaxSize(max_size = self.size) + self.rescaler = rescaler + transforms.append(rescaler) + + if self.crop_size is not None and self.crop_size > 0: + if len(transforms) == 0: + self.rescaler = albumentations.SmallestMaxSize(max_size = self.crop_size) + + if not self.random_crop: + cropper = albumentations.CenterCrop(height=self.crop_size,width=self.crop_size) + else: + cropper = albumentations.RandomCrop(height=self.crop_size,width=self.crop_size) + transforms.append(cropper) + + if len(transforms) > 0: + if self.up_factor is not None: + additional_targets = {"lr": "image"} + else: + additional_targets = None + self.preprocessor = albumentations.Compose(transforms, + additional_targets=additional_targets) + else: + self.preprocessor = lambda **kwargs: kwargs + + def __len__(self): + return len(self.base) + + def __getitem__(self, i): + example = self.base[i] + image = example["image"] + # adjust resolution + image = imscale(image, self.hr_factor, keepshapes=False) + h,w,c = image.shape + if self.crop_size and min(h,w) < self.crop_size: + # have to upscale to be able to crop - this just uses bilinear + image = self.rescaler(image=image)["image"] + if self.up_factor is None: + image = self.preprocessor(image=image)["image"] + example["image"] = image + else: + lr = imscale(image, self.up_factor, keepshapes=True, + keepmode=self.keep_mode) + + out = self.preprocessor(image=image, lr=lr) + example["image"] = out["image"] + example["lr"] = out["lr"] + + return example + +class ImageNetScaleTrain(ImageNetScale): + def __init__(self, random_crop=True, **kwargs): + super().__init__(random_crop=random_crop, **kwargs) + + def get_base(self): + return ImageNetTrain() + +class ImageNetScaleValidation(ImageNetScale): + def get_base(self): + return ImageNetValidation() + + +from skimage.feature import canny +from skimage.color import rgb2gray + + +class ImageNetEdges(ImageNetScale): + def __init__(self, up_factor=1, **kwargs): + super().__init__(up_factor=1, **kwargs) + + def __getitem__(self, i): + example = self.base[i] + image = example["image"] + h,w,c = image.shape + if self.crop_size and min(h,w) < self.crop_size: + # have to upscale to be able to crop - this just uses bilinear + image = self.rescaler(image=image)["image"] + + lr = canny(rgb2gray(image), sigma=2) + lr = lr.astype(np.float32) + lr = lr[:,:,None][:,:,[0,0,0]] + + out = self.preprocessor(image=image, lr=lr) + example["image"] = out["image"] + example["lr"] = out["lr"] + + return example + + +class ImageNetEdgesTrain(ImageNetEdges): + def __init__(self, random_crop=True, **kwargs): + super().__init__(random_crop=random_crop, **kwargs) + + def get_base(self): + return ImageNetTrain() + +class ImageNetEdgesValidation(ImageNetEdges): + def get_base(self): + return ImageNetValidation() diff --git a/repositories/taming/data/open_images_helper.py b/repositories/taming/data/open_images_helper.py new file mode 100644 index 000000000..8feb7c6e7 --- /dev/null +++ b/repositories/taming/data/open_images_helper.py @@ -0,0 +1,379 @@ +open_images_unify_categories_for_coco = { + '/m/03bt1vf': '/m/01g317', + '/m/04yx4': '/m/01g317', + '/m/05r655': '/m/01g317', + '/m/01bl7v': '/m/01g317', + '/m/0cnyhnx': '/m/01xq0k1', + '/m/01226z': '/m/018xm', + '/m/05ctyq': '/m/018xm', + '/m/058qzx': '/m/04ctx', + '/m/06pcq': '/m/0l515', + '/m/03m3pdh': '/m/02crq1', + '/m/046dlr': '/m/01x3z', + '/m/0h8mzrc': '/m/01x3z', +} + + +top_300_classes_plus_coco_compatibility = [ + ('Man', 1060962), + ('Clothing', 986610), + ('Tree', 748162), + ('Woman', 611896), + ('Person', 610294), + ('Human face', 442948), + ('Girl', 175399), + ('Building', 162147), + ('Car', 159135), + ('Plant', 155704), + ('Human body', 137073), + ('Flower', 133128), + ('Window', 127485), + ('Human arm', 118380), + ('House', 114365), + ('Wheel', 111684), + ('Suit', 99054), + ('Human hair', 98089), + ('Human head', 92763), + ('Chair', 88624), + ('Boy', 79849), + ('Table', 73699), + ('Jeans', 57200), + ('Tire', 55725), + ('Skyscraper', 53321), + ('Food', 52400), + ('Footwear', 50335), + ('Dress', 50236), + ('Human leg', 47124), + ('Toy', 46636), + ('Tower', 45605), + ('Boat', 43486), + ('Land vehicle', 40541), + ('Bicycle wheel', 34646), + ('Palm tree', 33729), + ('Fashion accessory', 32914), + ('Glasses', 31940), + ('Bicycle', 31409), + ('Furniture', 30656), + ('Sculpture', 29643), + ('Bottle', 27558), + ('Dog', 26980), + ('Snack', 26796), + ('Human hand', 26664), + ('Bird', 25791), + ('Book', 25415), + ('Guitar', 24386), + ('Jacket', 23998), + ('Poster', 22192), + ('Dessert', 21284), + ('Baked goods', 20657), + ('Drink', 19754), + ('Flag', 18588), + ('Houseplant', 18205), + ('Tableware', 17613), + ('Airplane', 17218), + ('Door', 17195), + ('Sports uniform', 17068), + ('Shelf', 16865), + ('Drum', 16612), + ('Vehicle', 16542), + ('Microphone', 15269), + ('Street light', 14957), + ('Cat', 14879), + ('Fruit', 13684), + ('Fast food', 13536), + ('Animal', 12932), + ('Vegetable', 12534), + ('Train', 12358), + ('Horse', 11948), + ('Flowerpot', 11728), + ('Motorcycle', 11621), + ('Fish', 11517), + ('Desk', 11405), + ('Helmet', 10996), + ('Truck', 10915), + ('Bus', 10695), + ('Hat', 10532), + ('Auto part', 10488), + ('Musical instrument', 10303), + ('Sunglasses', 10207), + ('Picture frame', 10096), + ('Sports equipment', 10015), + ('Shorts', 9999), + ('Wine glass', 9632), + ('Duck', 9242), + ('Wine', 9032), + ('Rose', 8781), + ('Tie', 8693), + ('Butterfly', 8436), + ('Beer', 7978), + ('Cabinetry', 7956), + ('Laptop', 7907), + ('Insect', 7497), + ('Goggles', 7363), + ('Shirt', 7098), + ('Dairy Product', 7021), + ('Marine invertebrates', 7014), + ('Cattle', 7006), + ('Trousers', 6903), + ('Van', 6843), + ('Billboard', 6777), + ('Balloon', 6367), + ('Human nose', 6103), + ('Tent', 6073), + ('Camera', 6014), + ('Doll', 6002), + ('Coat', 5951), + ('Mobile phone', 5758), + ('Swimwear', 5729), + ('Strawberry', 5691), + ('Stairs', 5643), + ('Goose', 5599), + ('Umbrella', 5536), + ('Cake', 5508), + ('Sun hat', 5475), + ('Bench', 5310), + ('Bookcase', 5163), + ('Bee', 5140), + ('Computer monitor', 5078), + ('Hiking equipment', 4983), + ('Office building', 4981), + ('Coffee cup', 4748), + ('Curtain', 4685), + ('Plate', 4651), + ('Box', 4621), + ('Tomato', 4595), + ('Coffee table', 4529), + ('Office supplies', 4473), + ('Maple', 4416), + ('Muffin', 4365), + ('Cocktail', 4234), + ('Castle', 4197), + ('Couch', 4134), + ('Pumpkin', 3983), + ('Computer keyboard', 3960), + ('Human mouth', 3926), + ('Christmas tree', 3893), + ('Mushroom', 3883), + ('Swimming pool', 3809), + ('Pastry', 3799), + ('Lavender (Plant)', 3769), + ('Football helmet', 3732), + ('Bread', 3648), + ('Traffic sign', 3628), + ('Common sunflower', 3597), + ('Television', 3550), + ('Bed', 3525), + ('Cookie', 3485), + ('Fountain', 3484), + ('Paddle', 3447), + ('Bicycle helmet', 3429), + ('Porch', 3420), + ('Deer', 3387), + ('Fedora', 3339), + ('Canoe', 3338), + ('Carnivore', 3266), + ('Bowl', 3202), + ('Human eye', 3166), + ('Ball', 3118), + ('Pillow', 3077), + ('Salad', 3061), + ('Beetle', 3060), + ('Orange', 3050), + ('Drawer', 2958), + ('Platter', 2937), + ('Elephant', 2921), + ('Seafood', 2921), + ('Monkey', 2915), + ('Countertop', 2879), + ('Watercraft', 2831), + ('Helicopter', 2805), + ('Kitchen appliance', 2797), + ('Personal flotation device', 2781), + ('Swan', 2739), + ('Lamp', 2711), + ('Boot', 2695), + ('Bronze sculpture', 2693), + ('Chicken', 2677), + ('Taxi', 2643), + ('Juice', 2615), + ('Cowboy hat', 2604), + ('Apple', 2600), + ('Tin can', 2590), + ('Necklace', 2564), + ('Ice cream', 2560), + ('Human beard', 2539), + ('Coin', 2536), + ('Candle', 2515), + ('Cart', 2512), + ('High heels', 2441), + ('Weapon', 2433), + ('Handbag', 2406), + ('Penguin', 2396), + ('Rifle', 2352), + ('Violin', 2336), + ('Skull', 2304), + ('Lantern', 2285), + ('Scarf', 2269), + ('Saucer', 2225), + ('Sheep', 2215), + ('Vase', 2189), + ('Lily', 2180), + ('Mug', 2154), + ('Parrot', 2140), + ('Human ear', 2137), + ('Sandal', 2115), + ('Lizard', 2100), + ('Kitchen & dining room table', 2063), + ('Spider', 1977), + ('Coffee', 1974), + ('Goat', 1926), + ('Squirrel', 1922), + ('Cello', 1913), + ('Sushi', 1881), + ('Tortoise', 1876), + ('Pizza', 1870), + ('Studio couch', 1864), + ('Barrel', 1862), + ('Cosmetics', 1841), + ('Moths and butterflies', 1841), + ('Convenience store', 1817), + ('Watch', 1792), + ('Home appliance', 1786), + ('Harbor seal', 1780), + ('Luggage and bags', 1756), + ('Vehicle registration plate', 1754), + ('Shrimp', 1751), + ('Jellyfish', 1730), + ('French fries', 1723), + ('Egg (Food)', 1698), + ('Football', 1697), + ('Musical keyboard', 1683), + ('Falcon', 1674), + ('Candy', 1660), + ('Medical equipment', 1654), + ('Eagle', 1651), + ('Dinosaur', 1634), + ('Surfboard', 1630), + ('Tank', 1628), + ('Grape', 1624), + ('Lion', 1624), + ('Owl', 1622), + ('Ski', 1613), + ('Waste container', 1606), + ('Frog', 1591), + ('Sparrow', 1585), + ('Rabbit', 1581), + ('Pen', 1546), + ('Sea lion', 1537), + ('Spoon', 1521), + ('Sink', 1512), + ('Teddy bear', 1507), + ('Bull', 1495), + ('Sofa bed', 1490), + ('Dragonfly', 1479), + ('Brassiere', 1478), + ('Chest of drawers', 1472), + ('Aircraft', 1466), + ('Human foot', 1463), + ('Pig', 1455), + ('Fork', 1454), + ('Antelope', 1438), + ('Tripod', 1427), + ('Tool', 1424), + ('Cheese', 1422), + ('Lemon', 1397), + ('Hamburger', 1393), + ('Dolphin', 1390), + ('Mirror', 1390), + ('Marine mammal', 1387), + ('Giraffe', 1385), + ('Snake', 1368), + ('Gondola', 1364), + ('Wheelchair', 1360), + ('Piano', 1358), + ('Cupboard', 1348), + ('Banana', 1345), + ('Trumpet', 1335), + ('Lighthouse', 1333), + ('Invertebrate', 1317), + ('Carrot', 1268), + ('Sock', 1260), + ('Tiger', 1241), + ('Camel', 1224), + ('Parachute', 1224), + ('Bathroom accessory', 1223), + ('Earrings', 1221), + ('Headphones', 1218), + ('Skirt', 1198), + ('Skateboard', 1190), + ('Sandwich', 1148), + ('Saxophone', 1141), + ('Goldfish', 1136), + ('Stool', 1104), + ('Traffic light', 1097), + ('Shellfish', 1081), + ('Backpack', 1079), + ('Sea turtle', 1078), + ('Cucumber', 1075), + ('Tea', 1051), + ('Toilet', 1047), + ('Roller skates', 1040), + ('Mule', 1039), + ('Bust', 1031), + ('Broccoli', 1030), + ('Crab', 1020), + ('Oyster', 1019), + ('Cannon', 1012), + ('Zebra', 1012), + ('French horn', 1008), + ('Grapefruit', 998), + ('Whiteboard', 997), + ('Zucchini', 997), + ('Crocodile', 992), + + ('Clock', 960), + ('Wall clock', 958), + + ('Doughnut', 869), + ('Snail', 868), + + ('Baseball glove', 859), + + ('Panda', 830), + ('Tennis racket', 830), + + ('Pear', 652), + + ('Bagel', 617), + ('Oven', 616), + ('Ladybug', 615), + ('Shark', 615), + ('Polar bear', 614), + ('Ostrich', 609), + + ('Hot dog', 473), + ('Microwave oven', 467), + ('Fire hydrant', 20), + ('Stop sign', 20), + ('Parking meter', 20), + ('Bear', 20), + ('Flying disc', 20), + ('Snowboard', 20), + ('Tennis ball', 20), + ('Kite', 20), + ('Baseball bat', 20), + ('Kitchen knife', 20), + ('Knife', 20), + ('Submarine sandwich', 20), + ('Computer mouse', 20), + ('Remote control', 20), + ('Toaster', 20), + ('Sink', 20), + ('Refrigerator', 20), + ('Alarm clock', 20), + ('Wall clock', 20), + ('Scissors', 20), + ('Hair dryer', 20), + ('Toothbrush', 20), + ('Suitcase', 20) +] diff --git a/repositories/taming/data/sflckr.py b/repositories/taming/data/sflckr.py new file mode 100644 index 000000000..91101be59 --- /dev/null +++ b/repositories/taming/data/sflckr.py @@ -0,0 +1,91 @@ +import os +import numpy as np +import cv2 +import albumentations +from PIL import Image +from torch.utils.data import Dataset + + +class SegmentationBase(Dataset): + def __init__(self, + data_csv, data_root, segmentation_root, + size=None, random_crop=False, interpolation="bicubic", + n_labels=182, shift_segmentation=False, + ): + self.n_labels = n_labels + self.shift_segmentation = shift_segmentation + self.data_csv = data_csv + self.data_root = data_root + self.segmentation_root = segmentation_root + with open(self.data_csv, "r") as f: + self.image_paths = f.read().splitlines() + self._length = len(self.image_paths) + self.labels = { + "relative_file_path_": [l for l in self.image_paths], + "file_path_": [os.path.join(self.data_root, l) + for l in self.image_paths], + "segmentation_path_": [os.path.join(self.segmentation_root, l.replace(".jpg", ".png")) + for l in self.image_paths] + } + + size = None if size is not None and size<=0 else size + self.size = size + if self.size is not None: + self.interpolation = interpolation + self.interpolation = { + "nearest": cv2.INTER_NEAREST, + "bilinear": cv2.INTER_LINEAR, + "bicubic": cv2.INTER_CUBIC, + "area": cv2.INTER_AREA, + "lanczos": cv2.INTER_LANCZOS4}[self.interpolation] + self.image_rescaler = albumentations.SmallestMaxSize(max_size=self.size, + interpolation=self.interpolation) + self.segmentation_rescaler = albumentations.SmallestMaxSize(max_size=self.size, + interpolation=cv2.INTER_NEAREST) + self.center_crop = not random_crop + if self.center_crop: + self.cropper = albumentations.CenterCrop(height=self.size, width=self.size) + else: + self.cropper = albumentations.RandomCrop(height=self.size, width=self.size) + self.preprocessor = self.cropper + + def __len__(self): + return self._length + + def __getitem__(self, i): + example = dict((k, self.labels[k][i]) for k in self.labels) + image = Image.open(example["file_path_"]) + if not image.mode == "RGB": + image = image.convert("RGB") + image = np.array(image).astype(np.uint8) + if self.size is not None: + image = self.image_rescaler(image=image)["image"] + segmentation = Image.open(example["segmentation_path_"]) + assert segmentation.mode == "L", segmentation.mode + segmentation = np.array(segmentation).astype(np.uint8) + if self.shift_segmentation: + # used to support segmentations containing unlabeled==255 label + segmentation = segmentation+1 + if self.size is not None: + segmentation = self.segmentation_rescaler(image=segmentation)["image"] + if self.size is not None: + processed = self.preprocessor(image=image, + mask=segmentation + ) + else: + processed = {"image": image, + "mask": segmentation + } + example["image"] = (processed["image"]/127.5 - 1.0).astype(np.float32) + segmentation = processed["mask"] + onehot = np.eye(self.n_labels)[segmentation] + example["segmentation"] = onehot + return example + + +class Examples(SegmentationBase): + def __init__(self, size=None, random_crop=False, interpolation="bicubic"): + super().__init__(data_csv="data/sflckr_examples.txt", + data_root="data/sflckr_images", + segmentation_root="data/sflckr_segmentations", + size=size, random_crop=random_crop, interpolation=interpolation) diff --git a/repositories/taming/data/utils.py b/repositories/taming/data/utils.py new file mode 100644 index 000000000..2b3c3d53c --- /dev/null +++ b/repositories/taming/data/utils.py @@ -0,0 +1,169 @@ +import collections +import os +import tarfile +import urllib +import zipfile +from pathlib import Path + +import numpy as np +import torch +from taming.data.helper_types import Annotation +from torch._six import string_classes +from torch.utils.data._utils.collate import np_str_obj_array_pattern, default_collate_err_msg_format +from tqdm import tqdm + + +def unpack(path): + if path.endswith("tar.gz"): + with tarfile.open(path, "r:gz") as tar: + tar.extractall(path=os.path.split(path)[0]) + elif path.endswith("tar"): + with tarfile.open(path, "r:") as tar: + tar.extractall(path=os.path.split(path)[0]) + elif path.endswith("zip"): + with zipfile.ZipFile(path, "r") as f: + f.extractall(path=os.path.split(path)[0]) + else: + raise NotImplementedError( + "Unknown file extension: {}".format(os.path.splitext(path)[1]) + ) + + +def reporthook(bar): + """tqdm progress bar for downloads.""" + + def hook(b=1, bsize=1, tsize=None): + if tsize is not None: + bar.total = tsize + bar.update(b * bsize - bar.n) + + return hook + + +def get_root(name): + base = "data/" + root = os.path.join(base, name) + os.makedirs(root, exist_ok=True) + return root + + +def is_prepared(root): + return Path(root).joinpath(".ready").exists() + + +def mark_prepared(root): + Path(root).joinpath(".ready").touch() + + +def prompt_download(file_, source, target_dir, content_dir=None): + targetpath = os.path.join(target_dir, file_) + while not os.path.exists(targetpath): + if content_dir is not None and os.path.exists( + os.path.join(target_dir, content_dir) + ): + break + print( + "Please download '{}' from '{}' to '{}'.".format(file_, source, targetpath) + ) + if content_dir is not None: + print( + "Or place its content into '{}'.".format( + os.path.join(target_dir, content_dir) + ) + ) + input("Press Enter when done...") + return targetpath + + +def download_url(file_, url, target_dir): + targetpath = os.path.join(target_dir, file_) + os.makedirs(target_dir, exist_ok=True) + with tqdm( + unit="B", unit_scale=True, unit_divisor=1024, miniters=1, desc=file_ + ) as bar: + urllib.request.urlretrieve(url, targetpath, reporthook=reporthook(bar)) + return targetpath + + +def download_urls(urls, target_dir): + paths = dict() + for fname, url in urls.items(): + outpath = download_url(fname, url, target_dir) + paths[fname] = outpath + return paths + + +def quadratic_crop(x, bbox, alpha=1.0): + """bbox is xmin, ymin, xmax, ymax""" + im_h, im_w = x.shape[:2] + bbox = np.array(bbox, dtype=np.float32) + bbox = np.clip(bbox, 0, max(im_h, im_w)) + center = 0.5 * (bbox[0] + bbox[2]), 0.5 * (bbox[1] + bbox[3]) + w = bbox[2] - bbox[0] + h = bbox[3] - bbox[1] + l = int(alpha * max(w, h)) + l = max(l, 2) + + required_padding = -1 * min( + center[0] - l, center[1] - l, im_w - (center[0] + l), im_h - (center[1] + l) + ) + required_padding = int(np.ceil(required_padding)) + if required_padding > 0: + padding = [ + [required_padding, required_padding], + [required_padding, required_padding], + ] + padding += [[0, 0]] * (len(x.shape) - 2) + x = np.pad(x, padding, "reflect") + center = center[0] + required_padding, center[1] + required_padding + xmin = int(center[0] - l / 2) + ymin = int(center[1] - l / 2) + return np.array(x[ymin : ymin + l, xmin : xmin + l, ...]) + + +def custom_collate(batch): + r"""source: pytorch 1.9.0, only one modification to original code """ + + elem = batch[0] + elem_type = type(elem) + if isinstance(elem, torch.Tensor): + out = None + if torch.utils.data.get_worker_info() is not None: + # If we're in a background process, concatenate directly into a + # shared memory tensor to avoid an extra copy + numel = sum([x.numel() for x in batch]) + storage = elem.storage()._new_shared(numel) + out = elem.new(storage) + return torch.stack(batch, 0, out=out) + elif elem_type.__module__ == 'numpy' and elem_type.__name__ != 'str_' \ + and elem_type.__name__ != 'string_': + if elem_type.__name__ == 'ndarray' or elem_type.__name__ == 'memmap': + # array of string classes and object + if np_str_obj_array_pattern.search(elem.dtype.str) is not None: + raise TypeError(default_collate_err_msg_format.format(elem.dtype)) + + return custom_collate([torch.as_tensor(b) for b in batch]) + elif elem.shape == (): # scalars + return torch.as_tensor(batch) + elif isinstance(elem, float): + return torch.tensor(batch, dtype=torch.float64) + elif isinstance(elem, int): + return torch.tensor(batch) + elif isinstance(elem, string_classes): + return batch + elif isinstance(elem, collections.abc.Mapping): + return {key: custom_collate([d[key] for d in batch]) for key in elem} + elif isinstance(elem, tuple) and hasattr(elem, '_fields'): # namedtuple + return elem_type(*(custom_collate(samples) for samples in zip(*batch))) + if isinstance(elem, collections.abc.Sequence) and isinstance(elem[0], Annotation): # added + return batch # added + elif isinstance(elem, collections.abc.Sequence): + # check to make sure that the elements in batch have consistent size + it = iter(batch) + elem_size = len(next(it)) + if not all(len(elem) == elem_size for elem in it): + raise RuntimeError('each element in list of batch should be of equal size') + transposed = zip(*batch) + return [custom_collate(samples) for samples in transposed] + + raise TypeError(default_collate_err_msg_format.format(elem_type)) diff --git a/repositories/taming/lr_scheduler.py b/repositories/taming/lr_scheduler.py new file mode 100644 index 000000000..e598ed120 --- /dev/null +++ b/repositories/taming/lr_scheduler.py @@ -0,0 +1,34 @@ +import numpy as np + + +class LambdaWarmUpCosineScheduler: + """ + note: use with a base_lr of 1.0 + """ + def __init__(self, warm_up_steps, lr_min, lr_max, lr_start, max_decay_steps, verbosity_interval=0): + self.lr_warm_up_steps = warm_up_steps + self.lr_start = lr_start + self.lr_min = lr_min + self.lr_max = lr_max + self.lr_max_decay_steps = max_decay_steps + self.last_lr = 0. + self.verbosity_interval = verbosity_interval + + def schedule(self, n): + if self.verbosity_interval > 0: + if n % self.verbosity_interval == 0: print(f"current step: {n}, recent lr-multiplier: {self.last_lr}") + if n < self.lr_warm_up_steps: + lr = (self.lr_max - self.lr_start) / self.lr_warm_up_steps * n + self.lr_start + self.last_lr = lr + return lr + else: + t = (n - self.lr_warm_up_steps) / (self.lr_max_decay_steps - self.lr_warm_up_steps) + t = min(t, 1.0) + lr = self.lr_min + 0.5 * (self.lr_max - self.lr_min) * ( + 1 + np.cos(t * np.pi)) + self.last_lr = lr + return lr + + def __call__(self, n): + return self.schedule(n) + diff --git a/repositories/taming/models/cond_transformer.py b/repositories/taming/models/cond_transformer.py new file mode 100644 index 000000000..e4c63730f --- /dev/null +++ b/repositories/taming/models/cond_transformer.py @@ -0,0 +1,352 @@ +import os, math +import torch +import torch.nn.functional as F +import pytorch_lightning as pl + +from main import instantiate_from_config +from taming.modules.util import SOSProvider + + +def disabled_train(self, mode=True): + """Overwrite model.train with this function to make sure train/eval mode + does not change anymore.""" + return self + + +class Net2NetTransformer(pl.LightningModule): + def __init__(self, + transformer_config, + first_stage_config, + cond_stage_config, + permuter_config=None, + ckpt_path=None, + ignore_keys=[], + first_stage_key="image", + cond_stage_key="depth", + downsample_cond_size=-1, + pkeep=1.0, + sos_token=0, + unconditional=False, + ): + super().__init__() + self.be_unconditional = unconditional + self.sos_token = sos_token + self.first_stage_key = first_stage_key + self.cond_stage_key = cond_stage_key + self.init_first_stage_from_ckpt(first_stage_config) + self.init_cond_stage_from_ckpt(cond_stage_config) + if permuter_config is None: + permuter_config = {"target": "taming.modules.transformer.permuter.Identity"} + self.permuter = instantiate_from_config(config=permuter_config) + self.transformer = instantiate_from_config(config=transformer_config) + + if ckpt_path is not None: + self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys) + self.downsample_cond_size = downsample_cond_size + self.pkeep = pkeep + + def init_from_ckpt(self, path, ignore_keys=list()): + sd = torch.load(path, map_location="cpu")["state_dict"] + for k in sd.keys(): + for ik in ignore_keys: + if k.startswith(ik): + self.print("Deleting key {} from state_dict.".format(k)) + del sd[k] + self.load_state_dict(sd, strict=False) + print(f"Restored from {path}") + + def init_first_stage_from_ckpt(self, config): + model = instantiate_from_config(config) + model = model.eval() + model.train = disabled_train + self.first_stage_model = model + + def init_cond_stage_from_ckpt(self, config): + if config == "__is_first_stage__": + print("Using first stage also as cond stage.") + self.cond_stage_model = self.first_stage_model + elif config == "__is_unconditional__" or self.be_unconditional: + print(f"Using no cond stage. Assuming the training is intended to be unconditional. " + f"Prepending {self.sos_token} as a sos token.") + self.be_unconditional = True + self.cond_stage_key = self.first_stage_key + self.cond_stage_model = SOSProvider(self.sos_token) + else: + model = instantiate_from_config(config) + model = model.eval() + model.train = disabled_train + self.cond_stage_model = model + + def forward(self, x, c): + # one step to produce the logits + _, z_indices = self.encode_to_z(x) + _, c_indices = self.encode_to_c(c) + + if self.training and self.pkeep < 1.0: + mask = torch.bernoulli(self.pkeep*torch.ones(z_indices.shape, + device=z_indices.device)) + mask = mask.round().to(dtype=torch.int64) + r_indices = torch.randint_like(z_indices, self.transformer.config.vocab_size) + a_indices = mask*z_indices+(1-mask)*r_indices + else: + a_indices = z_indices + + cz_indices = torch.cat((c_indices, a_indices), dim=1) + + # target includes all sequence elements (no need to handle first one + # differently because we are conditioning) + target = z_indices + # make the prediction + logits, _ = self.transformer(cz_indices[:, :-1]) + # cut off conditioning outputs - output i corresponds to p(z_i | z_{ -1: + c = F.interpolate(c, size=(self.downsample_cond_size, self.downsample_cond_size)) + quant_c, _, [_,_,indices] = self.cond_stage_model.encode(c) + if len(indices.shape) > 2: + indices = indices.view(c.shape[0], -1) + return quant_c, indices + + @torch.no_grad() + def decode_to_img(self, index, zshape): + index = self.permuter(index, reverse=True) + bhwc = (zshape[0],zshape[2],zshape[3],zshape[1]) + quant_z = self.first_stage_model.quantize.get_codebook_entry( + index.reshape(-1), shape=bhwc) + x = self.first_stage_model.decode(quant_z) + return x + + @torch.no_grad() + def log_images(self, batch, temperature=None, top_k=None, callback=None, lr_interface=False, **kwargs): + log = dict() + + N = 4 + if lr_interface: + x, c = self.get_xc(batch, N, diffuse=False, upsample_factor=8) + else: + x, c = self.get_xc(batch, N) + x = x.to(device=self.device) + c = c.to(device=self.device) + + quant_z, z_indices = self.encode_to_z(x) + quant_c, c_indices = self.encode_to_c(c) + + # create a "half"" sample + z_start_indices = z_indices[:,:z_indices.shape[1]//2] + index_sample = self.sample(z_start_indices, c_indices, + steps=z_indices.shape[1]-z_start_indices.shape[1], + temperature=temperature if temperature is not None else 1.0, + sample=True, + top_k=top_k if top_k is not None else 100, + callback=callback if callback is not None else lambda k: None) + x_sample = self.decode_to_img(index_sample, quant_z.shape) + + # sample + z_start_indices = z_indices[:, :0] + index_sample = self.sample(z_start_indices, c_indices, + steps=z_indices.shape[1], + temperature=temperature if temperature is not None else 1.0, + sample=True, + top_k=top_k if top_k is not None else 100, + callback=callback if callback is not None else lambda k: None) + x_sample_nopix = self.decode_to_img(index_sample, quant_z.shape) + + # det sample + z_start_indices = z_indices[:, :0] + index_sample = self.sample(z_start_indices, c_indices, + steps=z_indices.shape[1], + sample=False, + callback=callback if callback is not None else lambda k: None) + x_sample_det = self.decode_to_img(index_sample, quant_z.shape) + + # reconstruction + x_rec = self.decode_to_img(z_indices, quant_z.shape) + + log["inputs"] = x + log["reconstructions"] = x_rec + + if self.cond_stage_key in ["objects_bbox", "objects_center_points"]: + figure_size = (x_rec.shape[2], x_rec.shape[3]) + dataset = kwargs["pl_module"].trainer.datamodule.datasets["validation"] + label_for_category_no = dataset.get_textual_label_for_category_no + plotter = dataset.conditional_builders[self.cond_stage_key].plot + log["conditioning"] = torch.zeros_like(log["reconstructions"]) + for i in range(quant_c.shape[0]): + log["conditioning"][i] = plotter(quant_c[i], label_for_category_no, figure_size) + log["conditioning_rec"] = log["conditioning"] + elif self.cond_stage_key != "image": + cond_rec = self.cond_stage_model.decode(quant_c) + if self.cond_stage_key == "segmentation": + # get image from segmentation mask + num_classes = cond_rec.shape[1] + + c = torch.argmax(c, dim=1, keepdim=True) + c = F.one_hot(c, num_classes=num_classes) + c = c.squeeze(1).permute(0, 3, 1, 2).float() + c = self.cond_stage_model.to_rgb(c) + + cond_rec = torch.argmax(cond_rec, dim=1, keepdim=True) + cond_rec = F.one_hot(cond_rec, num_classes=num_classes) + cond_rec = cond_rec.squeeze(1).permute(0, 3, 1, 2).float() + cond_rec = self.cond_stage_model.to_rgb(cond_rec) + log["conditioning_rec"] = cond_rec + log["conditioning"] = c + + log["samples_half"] = x_sample + log["samples_nopix"] = x_sample_nopix + log["samples_det"] = x_sample_det + return log + + def get_input(self, key, batch): + x = batch[key] + if len(x.shape) == 3: + x = x[..., None] + if len(x.shape) == 4: + x = x.permute(0, 3, 1, 2).to(memory_format=torch.contiguous_format) + if x.dtype == torch.double: + x = x.float() + return x + + def get_xc(self, batch, N=None): + x = self.get_input(self.first_stage_key, batch) + c = self.get_input(self.cond_stage_key, batch) + if N is not None: + x = x[:N] + c = c[:N] + return x, c + + def shared_step(self, batch, batch_idx): + x, c = self.get_xc(batch) + logits, target = self(x, c) + loss = F.cross_entropy(logits.reshape(-1, logits.size(-1)), target.reshape(-1)) + return loss + + def training_step(self, batch, batch_idx): + loss = self.shared_step(batch, batch_idx) + self.log("train/loss", loss, prog_bar=True, logger=True, on_step=True, on_epoch=True) + return loss + + def validation_step(self, batch, batch_idx): + loss = self.shared_step(batch, batch_idx) + self.log("val/loss", loss, prog_bar=True, logger=True, on_step=True, on_epoch=True) + return loss + + def configure_optimizers(self): + """ + Following minGPT: + This long function is unfortunately doing something very simple and is being very defensive: + We are separating out all parameters of the model into two buckets: those that will experience + weight decay for regularization and those that won't (biases, and layernorm/embedding weights). + We are then returning the PyTorch optimizer object. + """ + # separate out all parameters to those that will and won't experience regularizing weight decay + decay = set() + no_decay = set() + whitelist_weight_modules = (torch.nn.Linear, ) + blacklist_weight_modules = (torch.nn.LayerNorm, torch.nn.Embedding) + for mn, m in self.transformer.named_modules(): + for pn, p in m.named_parameters(): + fpn = '%s.%s' % (mn, pn) if mn else pn # full param name + + if pn.endswith('bias'): + # all biases will not be decayed + no_decay.add(fpn) + elif pn.endswith('weight') and isinstance(m, whitelist_weight_modules): + # weights of whitelist modules will be weight decayed + decay.add(fpn) + elif pn.endswith('weight') and isinstance(m, blacklist_weight_modules): + # weights of blacklist modules will NOT be weight decayed + no_decay.add(fpn) + + # special case the position embedding parameter in the root GPT module as not decayed + no_decay.add('pos_emb') + + # validate that we considered every parameter + param_dict = {pn: p for pn, p in self.transformer.named_parameters()} + inter_params = decay & no_decay + union_params = decay | no_decay + assert len(inter_params) == 0, "parameters %s made it into both decay/no_decay sets!" % (str(inter_params), ) + assert len(param_dict.keys() - union_params) == 0, "parameters %s were not separated into either decay/no_decay set!" \ + % (str(param_dict.keys() - union_params), ) + + # create the pytorch optimizer object + optim_groups = [ + {"params": [param_dict[pn] for pn in sorted(list(decay))], "weight_decay": 0.01}, + {"params": [param_dict[pn] for pn in sorted(list(no_decay))], "weight_decay": 0.0}, + ] + optimizer = torch.optim.AdamW(optim_groups, lr=self.learning_rate, betas=(0.9, 0.95)) + return optimizer diff --git a/repositories/taming/models/dummy_cond_stage.py b/repositories/taming/models/dummy_cond_stage.py new file mode 100644 index 000000000..6e1993807 --- /dev/null +++ b/repositories/taming/models/dummy_cond_stage.py @@ -0,0 +1,22 @@ +from torch import Tensor + + +class DummyCondStage: + def __init__(self, conditional_key): + self.conditional_key = conditional_key + self.train = None + + def eval(self): + return self + + @staticmethod + def encode(c: Tensor): + return c, None, (None, None, c) + + @staticmethod + def decode(c: Tensor): + return c + + @staticmethod + def to_rgb(c: Tensor): + return c diff --git a/repositories/taming/models/vqgan.py b/repositories/taming/models/vqgan.py new file mode 100644 index 000000000..a6950baa5 --- /dev/null +++ b/repositories/taming/models/vqgan.py @@ -0,0 +1,404 @@ +import torch +import torch.nn.functional as F +import pytorch_lightning as pl + +from main import instantiate_from_config + +from taming.modules.diffusionmodules.model import Encoder, Decoder +from taming.modules.vqvae.quantize import VectorQuantizer2 as VectorQuantizer +from taming.modules.vqvae.quantize import GumbelQuantize +from taming.modules.vqvae.quantize import EMAVectorQuantizer + +class VQModel(pl.LightningModule): + def __init__(self, + ddconfig, + lossconfig, + n_embed, + embed_dim, + ckpt_path=None, + ignore_keys=[], + image_key="image", + colorize_nlabels=None, + monitor=None, + remap=None, + sane_index_shape=False, # tell vector quantizer to return indices as bhw + ): + super().__init__() + self.image_key = image_key + self.encoder = Encoder(**ddconfig) + self.decoder = Decoder(**ddconfig) + self.loss = instantiate_from_config(lossconfig) + self.quantize = VectorQuantizer(n_embed, embed_dim, beta=0.25, + remap=remap, sane_index_shape=sane_index_shape) + self.quant_conv = torch.nn.Conv2d(ddconfig["z_channels"], embed_dim, 1) + self.post_quant_conv = torch.nn.Conv2d(embed_dim, ddconfig["z_channels"], 1) + if ckpt_path is not None: + self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys) + self.image_key = image_key + if colorize_nlabels is not None: + assert type(colorize_nlabels)==int + self.register_buffer("colorize", torch.randn(3, colorize_nlabels, 1, 1)) + if monitor is not None: + self.monitor = monitor + + def init_from_ckpt(self, path, ignore_keys=list()): + sd = torch.load(path, map_location="cpu")["state_dict"] + keys = list(sd.keys()) + for k in keys: + for ik in ignore_keys: + if k.startswith(ik): + print("Deleting key {} from state_dict.".format(k)) + del sd[k] + self.load_state_dict(sd, strict=False) + print(f"Restored from {path}") + + def encode(self, x): + h = self.encoder(x) + h = self.quant_conv(h) + quant, emb_loss, info = self.quantize(h) + return quant, emb_loss, info + + def decode(self, quant): + quant = self.post_quant_conv(quant) + dec = self.decoder(quant) + return dec + + def decode_code(self, code_b): + quant_b = self.quantize.embed_code(code_b) + dec = self.decode(quant_b) + return dec + + def forward(self, input): + quant, diff, _ = self.encode(input) + dec = self.decode(quant) + return dec, diff + + def get_input(self, batch, k): + x = batch[k] + if len(x.shape) == 3: + x = x[..., None] + x = x.permute(0, 3, 1, 2).to(memory_format=torch.contiguous_format) + return x.float() + + def training_step(self, batch, batch_idx, optimizer_idx): + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x) + + if optimizer_idx == 0: + # autoencode + aeloss, log_dict_ae = self.loss(qloss, x, xrec, optimizer_idx, self.global_step, + last_layer=self.get_last_layer(), split="train") + + self.log("train/aeloss", aeloss, prog_bar=True, logger=True, on_step=True, on_epoch=True) + self.log_dict(log_dict_ae, prog_bar=False, logger=True, on_step=True, on_epoch=True) + return aeloss + + if optimizer_idx == 1: + # discriminator + discloss, log_dict_disc = self.loss(qloss, x, xrec, optimizer_idx, self.global_step, + last_layer=self.get_last_layer(), split="train") + self.log("train/discloss", discloss, prog_bar=True, logger=True, on_step=True, on_epoch=True) + self.log_dict(log_dict_disc, prog_bar=False, logger=True, on_step=True, on_epoch=True) + return discloss + + def validation_step(self, batch, batch_idx): + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x) + aeloss, log_dict_ae = self.loss(qloss, x, xrec, 0, self.global_step, + last_layer=self.get_last_layer(), split="val") + + discloss, log_dict_disc = self.loss(qloss, x, xrec, 1, self.global_step, + last_layer=self.get_last_layer(), split="val") + rec_loss = log_dict_ae["val/rec_loss"] + self.log("val/rec_loss", rec_loss, + prog_bar=True, logger=True, on_step=True, on_epoch=True, sync_dist=True) + self.log("val/aeloss", aeloss, + prog_bar=True, logger=True, on_step=True, on_epoch=True, sync_dist=True) + self.log_dict(log_dict_ae) + self.log_dict(log_dict_disc) + return self.log_dict + + def configure_optimizers(self): + lr = self.learning_rate + opt_ae = torch.optim.Adam(list(self.encoder.parameters())+ + list(self.decoder.parameters())+ + list(self.quantize.parameters())+ + list(self.quant_conv.parameters())+ + list(self.post_quant_conv.parameters()), + lr=lr, betas=(0.5, 0.9)) + opt_disc = torch.optim.Adam(self.loss.discriminator.parameters(), + lr=lr, betas=(0.5, 0.9)) + return [opt_ae, opt_disc], [] + + def get_last_layer(self): + return self.decoder.conv_out.weight + + def log_images(self, batch, **kwargs): + log = dict() + x = self.get_input(batch, self.image_key) + x = x.to(self.device) + xrec, _ = self(x) + if x.shape[1] > 3: + # colorize with random projection + assert xrec.shape[1] > 3 + x = self.to_rgb(x) + xrec = self.to_rgb(xrec) + log["inputs"] = x + log["reconstructions"] = xrec + return log + + def to_rgb(self, x): + assert self.image_key == "segmentation" + if not hasattr(self, "colorize"): + self.register_buffer("colorize", torch.randn(3, x.shape[1], 1, 1).to(x)) + x = F.conv2d(x, weight=self.colorize) + x = 2.*(x-x.min())/(x.max()-x.min()) - 1. + return x + + +class VQSegmentationModel(VQModel): + def __init__(self, n_labels, *args, **kwargs): + super().__init__(*args, **kwargs) + self.register_buffer("colorize", torch.randn(3, n_labels, 1, 1)) + + def configure_optimizers(self): + lr = self.learning_rate + opt_ae = torch.optim.Adam(list(self.encoder.parameters())+ + list(self.decoder.parameters())+ + list(self.quantize.parameters())+ + list(self.quant_conv.parameters())+ + list(self.post_quant_conv.parameters()), + lr=lr, betas=(0.5, 0.9)) + return opt_ae + + def training_step(self, batch, batch_idx): + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x) + aeloss, log_dict_ae = self.loss(qloss, x, xrec, split="train") + self.log_dict(log_dict_ae, prog_bar=False, logger=True, on_step=True, on_epoch=True) + return aeloss + + def validation_step(self, batch, batch_idx): + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x) + aeloss, log_dict_ae = self.loss(qloss, x, xrec, split="val") + self.log_dict(log_dict_ae, prog_bar=False, logger=True, on_step=True, on_epoch=True) + total_loss = log_dict_ae["val/total_loss"] + self.log("val/total_loss", total_loss, + prog_bar=True, logger=True, on_step=True, on_epoch=True, sync_dist=True) + return aeloss + + @torch.no_grad() + def log_images(self, batch, **kwargs): + log = dict() + x = self.get_input(batch, self.image_key) + x = x.to(self.device) + xrec, _ = self(x) + if x.shape[1] > 3: + # colorize with random projection + assert xrec.shape[1] > 3 + # convert logits to indices + xrec = torch.argmax(xrec, dim=1, keepdim=True) + xrec = F.one_hot(xrec, num_classes=x.shape[1]) + xrec = xrec.squeeze(1).permute(0, 3, 1, 2).float() + x = self.to_rgb(x) + xrec = self.to_rgb(xrec) + log["inputs"] = x + log["reconstructions"] = xrec + return log + + +class VQNoDiscModel(VQModel): + def __init__(self, + ddconfig, + lossconfig, + n_embed, + embed_dim, + ckpt_path=None, + ignore_keys=[], + image_key="image", + colorize_nlabels=None + ): + super().__init__(ddconfig=ddconfig, lossconfig=lossconfig, n_embed=n_embed, embed_dim=embed_dim, + ckpt_path=ckpt_path, ignore_keys=ignore_keys, image_key=image_key, + colorize_nlabels=colorize_nlabels) + + def training_step(self, batch, batch_idx): + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x) + # autoencode + aeloss, log_dict_ae = self.loss(qloss, x, xrec, self.global_step, split="train") + output = pl.TrainResult(minimize=aeloss) + output.log("train/aeloss", aeloss, + prog_bar=True, logger=True, on_step=True, on_epoch=True) + output.log_dict(log_dict_ae, prog_bar=False, logger=True, on_step=True, on_epoch=True) + return output + + def validation_step(self, batch, batch_idx): + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x) + aeloss, log_dict_ae = self.loss(qloss, x, xrec, self.global_step, split="val") + rec_loss = log_dict_ae["val/rec_loss"] + output = pl.EvalResult(checkpoint_on=rec_loss) + output.log("val/rec_loss", rec_loss, + prog_bar=True, logger=True, on_step=True, on_epoch=True) + output.log("val/aeloss", aeloss, + prog_bar=True, logger=True, on_step=True, on_epoch=True) + output.log_dict(log_dict_ae) + + return output + + def configure_optimizers(self): + optimizer = torch.optim.Adam(list(self.encoder.parameters())+ + list(self.decoder.parameters())+ + list(self.quantize.parameters())+ + list(self.quant_conv.parameters())+ + list(self.post_quant_conv.parameters()), + lr=self.learning_rate, betas=(0.5, 0.9)) + return optimizer + + +class GumbelVQ(VQModel): + def __init__(self, + ddconfig, + lossconfig, + n_embed, + embed_dim, + temperature_scheduler_config, + ckpt_path=None, + ignore_keys=[], + image_key="image", + colorize_nlabels=None, + monitor=None, + kl_weight=1e-8, + remap=None, + ): + + z_channels = ddconfig["z_channels"] + super().__init__(ddconfig, + lossconfig, + n_embed, + embed_dim, + ckpt_path=None, + ignore_keys=ignore_keys, + image_key=image_key, + colorize_nlabels=colorize_nlabels, + monitor=monitor, + ) + + self.loss.n_classes = n_embed + self.vocab_size = n_embed + + self.quantize = GumbelQuantize(z_channels, embed_dim, + n_embed=n_embed, + kl_weight=kl_weight, temp_init=1.0, + remap=remap) + + self.temperature_scheduler = instantiate_from_config(temperature_scheduler_config) # annealing of temp + + if ckpt_path is not None: + self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys) + + def temperature_scheduling(self): + self.quantize.temperature = self.temperature_scheduler(self.global_step) + + def encode_to_prequant(self, x): + h = self.encoder(x) + h = self.quant_conv(h) + return h + + def decode_code(self, code_b): + raise NotImplementedError + + def training_step(self, batch, batch_idx, optimizer_idx): + self.temperature_scheduling() + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x) + + if optimizer_idx == 0: + # autoencode + aeloss, log_dict_ae = self.loss(qloss, x, xrec, optimizer_idx, self.global_step, + last_layer=self.get_last_layer(), split="train") + + self.log_dict(log_dict_ae, prog_bar=False, logger=True, on_step=True, on_epoch=True) + self.log("temperature", self.quantize.temperature, prog_bar=False, logger=True, on_step=True, on_epoch=True) + return aeloss + + if optimizer_idx == 1: + # discriminator + discloss, log_dict_disc = self.loss(qloss, x, xrec, optimizer_idx, self.global_step, + last_layer=self.get_last_layer(), split="train") + self.log_dict(log_dict_disc, prog_bar=False, logger=True, on_step=True, on_epoch=True) + return discloss + + def validation_step(self, batch, batch_idx): + x = self.get_input(batch, self.image_key) + xrec, qloss = self(x, return_pred_indices=True) + aeloss, log_dict_ae = self.loss(qloss, x, xrec, 0, self.global_step, + last_layer=self.get_last_layer(), split="val") + + discloss, log_dict_disc = self.loss(qloss, x, xrec, 1, self.global_step, + last_layer=self.get_last_layer(), split="val") + rec_loss = log_dict_ae["val/rec_loss"] + self.log("val/rec_loss", rec_loss, + prog_bar=True, logger=True, on_step=False, on_epoch=True, sync_dist=True) + self.log("val/aeloss", aeloss, + prog_bar=True, logger=True, on_step=False, on_epoch=True, sync_dist=True) + self.log_dict(log_dict_ae) + self.log_dict(log_dict_disc) + return self.log_dict + + def log_images(self, batch, **kwargs): + log = dict() + x = self.get_input(batch, self.image_key) + x = x.to(self.device) + # encode + h = self.encoder(x) + h = self.quant_conv(h) + quant, _, _ = self.quantize(h) + # decode + x_rec = self.decode(quant) + log["inputs"] = x + log["reconstructions"] = x_rec + return log + + +class EMAVQ(VQModel): + def __init__(self, + ddconfig, + lossconfig, + n_embed, + embed_dim, + ckpt_path=None, + ignore_keys=[], + image_key="image", + colorize_nlabels=None, + monitor=None, + remap=None, + sane_index_shape=False, # tell vector quantizer to return indices as bhw + ): + super().__init__(ddconfig, + lossconfig, + n_embed, + embed_dim, + ckpt_path=None, + ignore_keys=ignore_keys, + image_key=image_key, + colorize_nlabels=colorize_nlabels, + monitor=monitor, + ) + self.quantize = EMAVectorQuantizer(n_embed=n_embed, + embedding_dim=embed_dim, + beta=0.25, + remap=remap) + def configure_optimizers(self): + lr = self.learning_rate + #Remove self.quantize from parameter list since it is updated via EMA + opt_ae = torch.optim.Adam(list(self.encoder.parameters())+ + list(self.decoder.parameters())+ + list(self.quant_conv.parameters())+ + list(self.post_quant_conv.parameters()), + lr=lr, betas=(0.5, 0.9)) + opt_disc = torch.optim.Adam(self.loss.discriminator.parameters(), + lr=lr, betas=(0.5, 0.9)) + return [opt_ae, opt_disc], [] \ No newline at end of file diff --git a/repositories/taming/modules/diffusionmodules/model.py b/repositories/taming/modules/diffusionmodules/model.py new file mode 100644 index 000000000..d3a5db6aa --- /dev/null +++ b/repositories/taming/modules/diffusionmodules/model.py @@ -0,0 +1,776 @@ +# pytorch_diffusion + derived encoder decoder +import math +import torch +import torch.nn as nn +import numpy as np + + +def get_timestep_embedding(timesteps, embedding_dim): + """ + This matches the implementation in Denoising Diffusion Probabilistic Models: + From Fairseq. + Build sinusoidal embeddings. + This matches the implementation in tensor2tensor, but differs slightly + from the description in Section 3.5 of "Attention Is All You Need". + """ + assert len(timesteps.shape) == 1 + + half_dim = embedding_dim // 2 + emb = math.log(10000) / (half_dim - 1) + emb = torch.exp(torch.arange(half_dim, dtype=torch.float32) * -emb) + emb = emb.to(device=timesteps.device) + emb = timesteps.float()[:, None] * emb[None, :] + emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=1) + if embedding_dim % 2 == 1: # zero pad + emb = torch.nn.functional.pad(emb, (0,1,0,0)) + return emb + + +def nonlinearity(x): + # swish + return x*torch.sigmoid(x) + + +def Normalize(in_channels): + return torch.nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True) + + +class Upsample(nn.Module): + def __init__(self, in_channels, with_conv): + super().__init__() + self.with_conv = with_conv + if self.with_conv: + self.conv = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + x = torch.nn.functional.interpolate(x, scale_factor=2.0, mode="nearest") + if self.with_conv: + x = self.conv(x) + return x + + +class Downsample(nn.Module): + def __init__(self, in_channels, with_conv): + super().__init__() + self.with_conv = with_conv + if self.with_conv: + # no asymmetric padding in torch conv, must do it ourselves + self.conv = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=3, + stride=2, + padding=0) + + def forward(self, x): + if self.with_conv: + pad = (0,1,0,1) + x = torch.nn.functional.pad(x, pad, mode="constant", value=0) + x = self.conv(x) + else: + x = torch.nn.functional.avg_pool2d(x, kernel_size=2, stride=2) + return x + + +class ResnetBlock(nn.Module): + def __init__(self, *, in_channels, out_channels=None, conv_shortcut=False, + dropout, temb_channels=512): + super().__init__() + self.in_channels = in_channels + out_channels = in_channels if out_channels is None else out_channels + self.out_channels = out_channels + self.use_conv_shortcut = conv_shortcut + + self.norm1 = Normalize(in_channels) + self.conv1 = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + if temb_channels > 0: + self.temb_proj = torch.nn.Linear(temb_channels, + out_channels) + self.norm2 = Normalize(out_channels) + self.dropout = torch.nn.Dropout(dropout) + self.conv2 = torch.nn.Conv2d(out_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + if self.in_channels != self.out_channels: + if self.use_conv_shortcut: + self.conv_shortcut = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + else: + self.nin_shortcut = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=1, + stride=1, + padding=0) + + def forward(self, x, temb): + h = x + h = self.norm1(h) + h = nonlinearity(h) + h = self.conv1(h) + + if temb is not None: + h = h + self.temb_proj(nonlinearity(temb))[:,:,None,None] + + h = self.norm2(h) + h = nonlinearity(h) + h = self.dropout(h) + h = self.conv2(h) + + if self.in_channels != self.out_channels: + if self.use_conv_shortcut: + x = self.conv_shortcut(x) + else: + x = self.nin_shortcut(x) + + return x+h + + +class AttnBlock(nn.Module): + def __init__(self, in_channels): + super().__init__() + self.in_channels = in_channels + + self.norm = Normalize(in_channels) + self.q = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.k = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.v = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.proj_out = torch.nn.Conv2d(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + + + def forward(self, x): + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + + # compute attention + b,c,h,w = q.shape + q = q.reshape(b,c,h*w) + q = q.permute(0,2,1) # b,hw,c + k = k.reshape(b,c,h*w) # b,c,hw + w_ = torch.bmm(q,k) # b,hw,hw w[b,i,j]=sum_c q[b,i,c]k[b,c,j] + w_ = w_ * (int(c)**(-0.5)) + w_ = torch.nn.functional.softmax(w_, dim=2) + + # attend to values + v = v.reshape(b,c,h*w) + w_ = w_.permute(0,2,1) # b,hw,hw (first hw of k, second of q) + h_ = torch.bmm(v,w_) # b, c,hw (hw of q) h_[b,c,j] = sum_i v[b,c,i] w_[b,i,j] + h_ = h_.reshape(b,c,h,w) + + h_ = self.proj_out(h_) + + return x+h_ + + +class Model(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels, + resolution, use_timestep=True): + super().__init__() + self.ch = ch + self.temb_ch = self.ch*4 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.in_channels = in_channels + + self.use_timestep = use_timestep + if self.use_timestep: + # timestep embedding + self.temb = nn.Module() + self.temb.dense = nn.ModuleList([ + torch.nn.Linear(self.ch, + self.temb_ch), + torch.nn.Linear(self.temb_ch, + self.temb_ch), + ]) + + # downsampling + self.conv_in = torch.nn.Conv2d(in_channels, + self.ch, + kernel_size=3, + stride=1, + padding=1) + + curr_res = resolution + in_ch_mult = (1,)+tuple(ch_mult) + self.down = nn.ModuleList() + for i_level in range(self.num_resolutions): + block = nn.ModuleList() + attn = nn.ModuleList() + block_in = ch*in_ch_mult[i_level] + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(AttnBlock(block_in)) + down = nn.Module() + down.block = block + down.attn = attn + if i_level != self.num_resolutions-1: + down.downsample = Downsample(block_in, resamp_with_conv) + curr_res = curr_res // 2 + self.down.append(down) + + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + self.mid.attn_1 = AttnBlock(block_in) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + + # upsampling + self.up = nn.ModuleList() + for i_level in reversed(range(self.num_resolutions)): + block = nn.ModuleList() + attn = nn.ModuleList() + block_out = ch*ch_mult[i_level] + skip_in = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks+1): + if i_block == self.num_res_blocks: + skip_in = ch*in_ch_mult[i_level] + block.append(ResnetBlock(in_channels=block_in+skip_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(AttnBlock(block_in)) + up = nn.Module() + up.block = block + up.attn = attn + if i_level != 0: + up.upsample = Upsample(block_in, resamp_with_conv) + curr_res = curr_res * 2 + self.up.insert(0, up) # prepend to get consistent order + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + out_ch, + kernel_size=3, + stride=1, + padding=1) + + + def forward(self, x, t=None): + #assert x.shape[2] == x.shape[3] == self.resolution + + if self.use_timestep: + # timestep embedding + assert t is not None + temb = get_timestep_embedding(t, self.ch) + temb = self.temb.dense[0](temb) + temb = nonlinearity(temb) + temb = self.temb.dense[1](temb) + else: + temb = None + + # downsampling + hs = [self.conv_in(x)] + for i_level in range(self.num_resolutions): + for i_block in range(self.num_res_blocks): + h = self.down[i_level].block[i_block](hs[-1], temb) + if len(self.down[i_level].attn) > 0: + h = self.down[i_level].attn[i_block](h) + hs.append(h) + if i_level != self.num_resolutions-1: + hs.append(self.down[i_level].downsample(hs[-1])) + + # middle + h = hs[-1] + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # upsampling + for i_level in reversed(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks+1): + h = self.up[i_level].block[i_block]( + torch.cat([h, hs.pop()], dim=1), temb) + if len(self.up[i_level].attn) > 0: + h = self.up[i_level].attn[i_block](h) + if i_level != 0: + h = self.up[i_level].upsample(h) + + # end + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + +class Encoder(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels, + resolution, z_channels, double_z=True, **ignore_kwargs): + super().__init__() + self.ch = ch + self.temb_ch = 0 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.in_channels = in_channels + + # downsampling + self.conv_in = torch.nn.Conv2d(in_channels, + self.ch, + kernel_size=3, + stride=1, + padding=1) + + curr_res = resolution + in_ch_mult = (1,)+tuple(ch_mult) + self.down = nn.ModuleList() + for i_level in range(self.num_resolutions): + block = nn.ModuleList() + attn = nn.ModuleList() + block_in = ch*in_ch_mult[i_level] + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(AttnBlock(block_in)) + down = nn.Module() + down.block = block + down.attn = attn + if i_level != self.num_resolutions-1: + down.downsample = Downsample(block_in, resamp_with_conv) + curr_res = curr_res // 2 + self.down.append(down) + + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + self.mid.attn_1 = AttnBlock(block_in) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + 2*z_channels if double_z else z_channels, + kernel_size=3, + stride=1, + padding=1) + + + def forward(self, x): + #assert x.shape[2] == x.shape[3] == self.resolution, "{}, {}, {}".format(x.shape[2], x.shape[3], self.resolution) + + # timestep embedding + temb = None + + # downsampling + hs = [self.conv_in(x)] + for i_level in range(self.num_resolutions): + for i_block in range(self.num_res_blocks): + h = self.down[i_level].block[i_block](hs[-1], temb) + if len(self.down[i_level].attn) > 0: + h = self.down[i_level].attn[i_block](h) + hs.append(h) + if i_level != self.num_resolutions-1: + hs.append(self.down[i_level].downsample(hs[-1])) + + # middle + h = hs[-1] + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # end + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + +class Decoder(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels, + resolution, z_channels, give_pre_end=False, **ignorekwargs): + super().__init__() + self.ch = ch + self.temb_ch = 0 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.in_channels = in_channels + self.give_pre_end = give_pre_end + + # compute in_ch_mult, block_in and curr_res at lowest res + in_ch_mult = (1,)+tuple(ch_mult) + block_in = ch*ch_mult[self.num_resolutions-1] + curr_res = resolution // 2**(self.num_resolutions-1) + self.z_shape = (1,z_channels,curr_res,curr_res) + print("Working with z of shape {} = {} dimensions.".format( + self.z_shape, np.prod(self.z_shape))) + + # z to block_in + self.conv_in = torch.nn.Conv2d(z_channels, + block_in, + kernel_size=3, + stride=1, + padding=1) + + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + self.mid.attn_1 = AttnBlock(block_in) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + + # upsampling + self.up = nn.ModuleList() + for i_level in reversed(range(self.num_resolutions)): + block = nn.ModuleList() + attn = nn.ModuleList() + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks+1): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(AttnBlock(block_in)) + up = nn.Module() + up.block = block + up.attn = attn + if i_level != 0: + up.upsample = Upsample(block_in, resamp_with_conv) + curr_res = curr_res * 2 + self.up.insert(0, up) # prepend to get consistent order + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + out_ch, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, z): + #assert z.shape[1:] == self.z_shape[1:] + self.last_z_shape = z.shape + + # timestep embedding + temb = None + + # z to block_in + h = self.conv_in(z) + + # middle + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # upsampling + for i_level in reversed(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks+1): + h = self.up[i_level].block[i_block](h, temb) + if len(self.up[i_level].attn) > 0: + h = self.up[i_level].attn[i_block](h) + if i_level != 0: + h = self.up[i_level].upsample(h) + + # end + if self.give_pre_end: + return h + + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + +class VUNet(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, + in_channels, c_channels, + resolution, z_channels, use_timestep=False, **ignore_kwargs): + super().__init__() + self.ch = ch + self.temb_ch = self.ch*4 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + + self.use_timestep = use_timestep + if self.use_timestep: + # timestep embedding + self.temb = nn.Module() + self.temb.dense = nn.ModuleList([ + torch.nn.Linear(self.ch, + self.temb_ch), + torch.nn.Linear(self.temb_ch, + self.temb_ch), + ]) + + # downsampling + self.conv_in = torch.nn.Conv2d(c_channels, + self.ch, + kernel_size=3, + stride=1, + padding=1) + + curr_res = resolution + in_ch_mult = (1,)+tuple(ch_mult) + self.down = nn.ModuleList() + for i_level in range(self.num_resolutions): + block = nn.ModuleList() + attn = nn.ModuleList() + block_in = ch*in_ch_mult[i_level] + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(AttnBlock(block_in)) + down = nn.Module() + down.block = block + down.attn = attn + if i_level != self.num_resolutions-1: + down.downsample = Downsample(block_in, resamp_with_conv) + curr_res = curr_res // 2 + self.down.append(down) + + self.z_in = torch.nn.Conv2d(z_channels, + block_in, + kernel_size=1, + stride=1, + padding=0) + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=2*block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + self.mid.attn_1 = AttnBlock(block_in) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + + # upsampling + self.up = nn.ModuleList() + for i_level in reversed(range(self.num_resolutions)): + block = nn.ModuleList() + attn = nn.ModuleList() + block_out = ch*ch_mult[i_level] + skip_in = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks+1): + if i_block == self.num_res_blocks: + skip_in = ch*in_ch_mult[i_level] + block.append(ResnetBlock(in_channels=block_in+skip_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(AttnBlock(block_in)) + up = nn.Module() + up.block = block + up.attn = attn + if i_level != 0: + up.upsample = Upsample(block_in, resamp_with_conv) + curr_res = curr_res * 2 + self.up.insert(0, up) # prepend to get consistent order + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + out_ch, + kernel_size=3, + stride=1, + padding=1) + + + def forward(self, x, z): + #assert x.shape[2] == x.shape[3] == self.resolution + + if self.use_timestep: + # timestep embedding + assert t is not None + temb = get_timestep_embedding(t, self.ch) + temb = self.temb.dense[0](temb) + temb = nonlinearity(temb) + temb = self.temb.dense[1](temb) + else: + temb = None + + # downsampling + hs = [self.conv_in(x)] + for i_level in range(self.num_resolutions): + for i_block in range(self.num_res_blocks): + h = self.down[i_level].block[i_block](hs[-1], temb) + if len(self.down[i_level].attn) > 0: + h = self.down[i_level].attn[i_block](h) + hs.append(h) + if i_level != self.num_resolutions-1: + hs.append(self.down[i_level].downsample(hs[-1])) + + # middle + h = hs[-1] + z = self.z_in(z) + h = torch.cat((h,z),dim=1) + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # upsampling + for i_level in reversed(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks+1): + h = self.up[i_level].block[i_block]( + torch.cat([h, hs.pop()], dim=1), temb) + if len(self.up[i_level].attn) > 0: + h = self.up[i_level].attn[i_block](h) + if i_level != 0: + h = self.up[i_level].upsample(h) + + # end + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + +class SimpleDecoder(nn.Module): + def __init__(self, in_channels, out_channels, *args, **kwargs): + super().__init__() + self.model = nn.ModuleList([nn.Conv2d(in_channels, in_channels, 1), + ResnetBlock(in_channels=in_channels, + out_channels=2 * in_channels, + temb_channels=0, dropout=0.0), + ResnetBlock(in_channels=2 * in_channels, + out_channels=4 * in_channels, + temb_channels=0, dropout=0.0), + ResnetBlock(in_channels=4 * in_channels, + out_channels=2 * in_channels, + temb_channels=0, dropout=0.0), + nn.Conv2d(2*in_channels, in_channels, 1), + Upsample(in_channels, with_conv=True)]) + # end + self.norm_out = Normalize(in_channels) + self.conv_out = torch.nn.Conv2d(in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + for i, layer in enumerate(self.model): + if i in [1,2,3]: + x = layer(x, None) + else: + x = layer(x) + + h = self.norm_out(x) + h = nonlinearity(h) + x = self.conv_out(h) + return x + + +class UpsampleDecoder(nn.Module): + def __init__(self, in_channels, out_channels, ch, num_res_blocks, resolution, + ch_mult=(2,2), dropout=0.0): + super().__init__() + # upsampling + self.temb_ch = 0 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + block_in = in_channels + curr_res = resolution // 2 ** (self.num_resolutions - 1) + self.res_blocks = nn.ModuleList() + self.upsample_blocks = nn.ModuleList() + for i_level in range(self.num_resolutions): + res_block = [] + block_out = ch * ch_mult[i_level] + for i_block in range(self.num_res_blocks + 1): + res_block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + self.res_blocks.append(nn.ModuleList(res_block)) + if i_level != self.num_resolutions - 1: + self.upsample_blocks.append(Upsample(block_in, True)) + curr_res = curr_res * 2 + + # end + self.norm_out = Normalize(block_in) + self.conv_out = torch.nn.Conv2d(block_in, + out_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + # upsampling + h = x + for k, i_level in enumerate(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks + 1): + h = self.res_blocks[i_level][i_block](h, None) + if i_level != self.num_resolutions - 1: + h = self.upsample_blocks[k](h) + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + diff --git a/repositories/taming/modules/discriminator/model.py b/repositories/taming/modules/discriminator/model.py new file mode 100644 index 000000000..2aaa3110d --- /dev/null +++ b/repositories/taming/modules/discriminator/model.py @@ -0,0 +1,67 @@ +import functools +import torch.nn as nn + + +from taming.modules.util import ActNorm + + +def weights_init(m): + classname = m.__class__.__name__ + if classname.find('Conv') != -1: + nn.init.normal_(m.weight.data, 0.0, 0.02) + elif classname.find('BatchNorm') != -1: + nn.init.normal_(m.weight.data, 1.0, 0.02) + nn.init.constant_(m.bias.data, 0) + + +class NLayerDiscriminator(nn.Module): + """Defines a PatchGAN discriminator as in Pix2Pix + --> see https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix/blob/master/models/networks.py + """ + def __init__(self, input_nc=3, ndf=64, n_layers=3, use_actnorm=False): + """Construct a PatchGAN discriminator + Parameters: + input_nc (int) -- the number of channels in input images + ndf (int) -- the number of filters in the last conv layer + n_layers (int) -- the number of conv layers in the discriminator + norm_layer -- normalization layer + """ + super(NLayerDiscriminator, self).__init__() + if not use_actnorm: + norm_layer = nn.BatchNorm2d + else: + norm_layer = ActNorm + if type(norm_layer) == functools.partial: # no need to use bias as BatchNorm2d has affine parameters + use_bias = norm_layer.func != nn.BatchNorm2d + else: + use_bias = norm_layer != nn.BatchNorm2d + + kw = 4 + padw = 1 + sequence = [nn.Conv2d(input_nc, ndf, kernel_size=kw, stride=2, padding=padw), nn.LeakyReLU(0.2, True)] + nf_mult = 1 + nf_mult_prev = 1 + for n in range(1, n_layers): # gradually increase the number of filters + nf_mult_prev = nf_mult + nf_mult = min(2 ** n, 8) + sequence += [ + nn.Conv2d(ndf * nf_mult_prev, ndf * nf_mult, kernel_size=kw, stride=2, padding=padw, bias=use_bias), + norm_layer(ndf * nf_mult), + nn.LeakyReLU(0.2, True) + ] + + nf_mult_prev = nf_mult + nf_mult = min(2 ** n_layers, 8) + sequence += [ + nn.Conv2d(ndf * nf_mult_prev, ndf * nf_mult, kernel_size=kw, stride=1, padding=padw, bias=use_bias), + norm_layer(ndf * nf_mult), + nn.LeakyReLU(0.2, True) + ] + + sequence += [ + nn.Conv2d(ndf * nf_mult, 1, kernel_size=kw, stride=1, padding=padw)] # output 1 channel prediction map + self.main = nn.Sequential(*sequence) + + def forward(self, input): + """Standard forward.""" + return self.main(input) diff --git a/repositories/taming/modules/losses/__init__.py b/repositories/taming/modules/losses/__init__.py new file mode 100644 index 000000000..d09caf9eb --- /dev/null +++ b/repositories/taming/modules/losses/__init__.py @@ -0,0 +1,2 @@ +from taming.modules.losses.vqperceptual import DummyLoss + diff --git a/repositories/taming/modules/losses/lpips.py b/repositories/taming/modules/losses/lpips.py new file mode 100644 index 000000000..a72804476 --- /dev/null +++ b/repositories/taming/modules/losses/lpips.py @@ -0,0 +1,123 @@ +"""Stripped version of https://github.com/richzhang/PerceptualSimilarity/tree/master/models""" + +import torch +import torch.nn as nn +from torchvision import models +from collections import namedtuple + +from taming.util import get_ckpt_path + + +class LPIPS(nn.Module): + # Learned perceptual metric + def __init__(self, use_dropout=True): + super().__init__() + self.scaling_layer = ScalingLayer() + self.chns = [64, 128, 256, 512, 512] # vg16 features + self.net = vgg16(pretrained=True, requires_grad=False) + self.lin0 = NetLinLayer(self.chns[0], use_dropout=use_dropout) + self.lin1 = NetLinLayer(self.chns[1], use_dropout=use_dropout) + self.lin2 = NetLinLayer(self.chns[2], use_dropout=use_dropout) + self.lin3 = NetLinLayer(self.chns[3], use_dropout=use_dropout) + self.lin4 = NetLinLayer(self.chns[4], use_dropout=use_dropout) + self.load_from_pretrained() + for param in self.parameters(): + param.requires_grad = False + + def load_from_pretrained(self, name="vgg_lpips"): + ckpt = get_ckpt_path(name, "taming/modules/autoencoder/lpips") + self.load_state_dict(torch.load(ckpt, map_location=torch.device("cpu")), strict=False) + print("loaded pretrained LPIPS loss from {}".format(ckpt)) + + @classmethod + def from_pretrained(cls, name="vgg_lpips"): + if name != "vgg_lpips": + raise NotImplementedError + model = cls() + ckpt = get_ckpt_path(name) + model.load_state_dict(torch.load(ckpt, map_location=torch.device("cpu")), strict=False) + return model + + def forward(self, input, target): + in0_input, in1_input = (self.scaling_layer(input), self.scaling_layer(target)) + outs0, outs1 = self.net(in0_input), self.net(in1_input) + feats0, feats1, diffs = {}, {}, {} + lins = [self.lin0, self.lin1, self.lin2, self.lin3, self.lin4] + for kk in range(len(self.chns)): + feats0[kk], feats1[kk] = normalize_tensor(outs0[kk]), normalize_tensor(outs1[kk]) + diffs[kk] = (feats0[kk] - feats1[kk]) ** 2 + + res = [spatial_average(lins[kk].model(diffs[kk]), keepdim=True) for kk in range(len(self.chns))] + val = res[0] + for l in range(1, len(self.chns)): + val += res[l] + return val + + +class ScalingLayer(nn.Module): + def __init__(self): + super(ScalingLayer, self).__init__() + self.register_buffer('shift', torch.Tensor([-.030, -.088, -.188])[None, :, None, None]) + self.register_buffer('scale', torch.Tensor([.458, .448, .450])[None, :, None, None]) + + def forward(self, inp): + return (inp - self.shift) / self.scale + + +class NetLinLayer(nn.Module): + """ A single linear layer which does a 1x1 conv """ + def __init__(self, chn_in, chn_out=1, use_dropout=False): + super(NetLinLayer, self).__init__() + layers = [nn.Dropout(), ] if (use_dropout) else [] + layers += [nn.Conv2d(chn_in, chn_out, 1, stride=1, padding=0, bias=False), ] + self.model = nn.Sequential(*layers) + + +class vgg16(torch.nn.Module): + def __init__(self, requires_grad=False, pretrained=True): + super(vgg16, self).__init__() + vgg_pretrained_features = models.vgg16(pretrained=pretrained).features + self.slice1 = torch.nn.Sequential() + self.slice2 = torch.nn.Sequential() + self.slice3 = torch.nn.Sequential() + self.slice4 = torch.nn.Sequential() + self.slice5 = torch.nn.Sequential() + self.N_slices = 5 + for x in range(4): + self.slice1.add_module(str(x), vgg_pretrained_features[x]) + for x in range(4, 9): + self.slice2.add_module(str(x), vgg_pretrained_features[x]) + for x in range(9, 16): + self.slice3.add_module(str(x), vgg_pretrained_features[x]) + for x in range(16, 23): + self.slice4.add_module(str(x), vgg_pretrained_features[x]) + for x in range(23, 30): + self.slice5.add_module(str(x), vgg_pretrained_features[x]) + if not requires_grad: + for param in self.parameters(): + param.requires_grad = False + + def forward(self, X): + h = self.slice1(X) + h_relu1_2 = h + h = self.slice2(h) + h_relu2_2 = h + h = self.slice3(h) + h_relu3_3 = h + h = self.slice4(h) + h_relu4_3 = h + h = self.slice5(h) + h_relu5_3 = h + vgg_outputs = namedtuple("VggOutputs", ['relu1_2', 'relu2_2', 'relu3_3', 'relu4_3', 'relu5_3']) + out = vgg_outputs(h_relu1_2, h_relu2_2, h_relu3_3, h_relu4_3, h_relu5_3) + return out + + +def normalize_tensor(x,eps=1e-10): + norm_factor = torch.sqrt(torch.sum(x**2,dim=1,keepdim=True)) + return x/(norm_factor+eps) + + +def spatial_average(x, keepdim=True): + return x.mean([2,3],keepdim=keepdim) + diff --git a/repositories/taming/modules/losses/segmentation.py b/repositories/taming/modules/losses/segmentation.py new file mode 100644 index 000000000..4ba77deb5 --- /dev/null +++ b/repositories/taming/modules/losses/segmentation.py @@ -0,0 +1,22 @@ +import torch.nn as nn +import torch.nn.functional as F + + +class BCELoss(nn.Module): + def forward(self, prediction, target): + loss = F.binary_cross_entropy_with_logits(prediction,target) + return loss, {} + + +class BCELossWithQuant(nn.Module): + def __init__(self, codebook_weight=1.): + super().__init__() + self.codebook_weight = codebook_weight + + def forward(self, qloss, target, prediction, split): + bce_loss = F.binary_cross_entropy_with_logits(prediction,target) + loss = bce_loss + self.codebook_weight*qloss + return loss, {"{}/total_loss".format(split): loss.clone().detach().mean(), + "{}/bce_loss".format(split): bce_loss.detach().mean(), + "{}/quant_loss".format(split): qloss.detach().mean() + } diff --git a/repositories/taming/modules/losses/vqperceptual.py b/repositories/taming/modules/losses/vqperceptual.py new file mode 100644 index 000000000..c2febd445 --- /dev/null +++ b/repositories/taming/modules/losses/vqperceptual.py @@ -0,0 +1,136 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + +from taming.modules.losses.lpips import LPIPS +from taming.modules.discriminator.model import NLayerDiscriminator, weights_init + + +class DummyLoss(nn.Module): + def __init__(self): + super().__init__() + + +def adopt_weight(weight, global_step, threshold=0, value=0.): + if global_step < threshold: + weight = value + return weight + + +def hinge_d_loss(logits_real, logits_fake): + loss_real = torch.mean(F.relu(1. - logits_real)) + loss_fake = torch.mean(F.relu(1. + logits_fake)) + d_loss = 0.5 * (loss_real + loss_fake) + return d_loss + + +def vanilla_d_loss(logits_real, logits_fake): + d_loss = 0.5 * ( + torch.mean(torch.nn.functional.softplus(-logits_real)) + + torch.mean(torch.nn.functional.softplus(logits_fake))) + return d_loss + + +class VQLPIPSWithDiscriminator(nn.Module): + def __init__(self, disc_start, codebook_weight=1.0, pixelloss_weight=1.0, + disc_num_layers=3, disc_in_channels=3, disc_factor=1.0, disc_weight=1.0, + perceptual_weight=1.0, use_actnorm=False, disc_conditional=False, + disc_ndf=64, disc_loss="hinge"): + super().__init__() + assert disc_loss in ["hinge", "vanilla"] + self.codebook_weight = codebook_weight + self.pixel_weight = pixelloss_weight + self.perceptual_loss = LPIPS().eval() + self.perceptual_weight = perceptual_weight + + self.discriminator = NLayerDiscriminator(input_nc=disc_in_channels, + n_layers=disc_num_layers, + use_actnorm=use_actnorm, + ndf=disc_ndf + ).apply(weights_init) + self.discriminator_iter_start = disc_start + if disc_loss == "hinge": + self.disc_loss = hinge_d_loss + elif disc_loss == "vanilla": + self.disc_loss = vanilla_d_loss + else: + raise ValueError(f"Unknown GAN loss '{disc_loss}'.") + print(f"VQLPIPSWithDiscriminator running with {disc_loss} loss.") + self.disc_factor = disc_factor + self.discriminator_weight = disc_weight + self.disc_conditional = disc_conditional + + def calculate_adaptive_weight(self, nll_loss, g_loss, last_layer=None): + if last_layer is not None: + nll_grads = torch.autograd.grad(nll_loss, last_layer, retain_graph=True)[0] + g_grads = torch.autograd.grad(g_loss, last_layer, retain_graph=True)[0] + else: + nll_grads = torch.autograd.grad(nll_loss, self.last_layer[0], retain_graph=True)[0] + g_grads = torch.autograd.grad(g_loss, self.last_layer[0], retain_graph=True)[0] + + d_weight = torch.norm(nll_grads) / (torch.norm(g_grads) + 1e-4) + d_weight = torch.clamp(d_weight, 0.0, 1e4).detach() + d_weight = d_weight * self.discriminator_weight + return d_weight + + def forward(self, codebook_loss, inputs, reconstructions, optimizer_idx, + global_step, last_layer=None, cond=None, split="train"): + rec_loss = torch.abs(inputs.contiguous() - reconstructions.contiguous()) + if self.perceptual_weight > 0: + p_loss = self.perceptual_loss(inputs.contiguous(), reconstructions.contiguous()) + rec_loss = rec_loss + self.perceptual_weight * p_loss + else: + p_loss = torch.tensor([0.0]) + + nll_loss = rec_loss + #nll_loss = torch.sum(nll_loss) / nll_loss.shape[0] + nll_loss = torch.mean(nll_loss) + + # now the GAN part + if optimizer_idx == 0: + # generator update + if cond is None: + assert not self.disc_conditional + logits_fake = self.discriminator(reconstructions.contiguous()) + else: + assert self.disc_conditional + logits_fake = self.discriminator(torch.cat((reconstructions.contiguous(), cond), dim=1)) + g_loss = -torch.mean(logits_fake) + + try: + d_weight = self.calculate_adaptive_weight(nll_loss, g_loss, last_layer=last_layer) + except RuntimeError: + assert not self.training + d_weight = torch.tensor(0.0) + + disc_factor = adopt_weight(self.disc_factor, global_step, threshold=self.discriminator_iter_start) + loss = nll_loss + d_weight * disc_factor * g_loss + self.codebook_weight * codebook_loss.mean() + + log = {"{}/total_loss".format(split): loss.clone().detach().mean(), + "{}/quant_loss".format(split): codebook_loss.detach().mean(), + "{}/nll_loss".format(split): nll_loss.detach().mean(), + "{}/rec_loss".format(split): rec_loss.detach().mean(), + "{}/p_loss".format(split): p_loss.detach().mean(), + "{}/d_weight".format(split): d_weight.detach(), + "{}/disc_factor".format(split): torch.tensor(disc_factor), + "{}/g_loss".format(split): g_loss.detach().mean(), + } + return loss, log + + if optimizer_idx == 1: + # second pass for discriminator update + if cond is None: + logits_real = self.discriminator(inputs.contiguous().detach()) + logits_fake = self.discriminator(reconstructions.contiguous().detach()) + else: + logits_real = self.discriminator(torch.cat((inputs.contiguous().detach(), cond), dim=1)) + logits_fake = self.discriminator(torch.cat((reconstructions.contiguous().detach(), cond), dim=1)) + + disc_factor = adopt_weight(self.disc_factor, global_step, threshold=self.discriminator_iter_start) + d_loss = disc_factor * self.disc_loss(logits_real, logits_fake) + + log = {"{}/disc_loss".format(split): d_loss.clone().detach().mean(), + "{}/logits_real".format(split): logits_real.detach().mean(), + "{}/logits_fake".format(split): logits_fake.detach().mean() + } + return d_loss, log diff --git a/repositories/taming/modules/misc/coord.py b/repositories/taming/modules/misc/coord.py new file mode 100644 index 000000000..ee69b0c89 --- /dev/null +++ b/repositories/taming/modules/misc/coord.py @@ -0,0 +1,31 @@ +import torch + +class CoordStage(object): + def __init__(self, n_embed, down_factor): + self.n_embed = n_embed + self.down_factor = down_factor + + def eval(self): + return self + + def encode(self, c): + """fake vqmodel interface""" + assert 0.0 <= c.min() and c.max() <= 1.0 + b,ch,h,w = c.shape + assert ch == 1 + + c = torch.nn.functional.interpolate(c, scale_factor=1/self.down_factor, + mode="area") + c = c.clamp(0.0, 1.0) + c = self.n_embed*c + c_quant = c.round() + c_ind = c_quant.to(dtype=torch.long) + + info = None, None, c_ind + return c_quant, None, info + + def decode(self, c): + c = c/self.n_embed + c = torch.nn.functional.interpolate(c, scale_factor=self.down_factor, + mode="nearest") + return c diff --git a/repositories/taming/modules/transformer/mingpt.py b/repositories/taming/modules/transformer/mingpt.py new file mode 100644 index 000000000..d14b7b681 --- /dev/null +++ b/repositories/taming/modules/transformer/mingpt.py @@ -0,0 +1,415 @@ +""" +taken from: https://github.com/karpathy/minGPT/ +GPT model: +- the initial stem consists of a combination of token encoding and a positional encoding +- the meat of it is a uniform sequence of Transformer blocks + - each Transformer is a sequential combination of a 1-hidden-layer MLP block and a self-attention block + - all blocks feed into a central residual pathway similar to resnets +- the final decoder is a linear projection into a vanilla Softmax classifier +""" + +import math +import logging + +import torch +import torch.nn as nn +from torch.nn import functional as F +from transformers import top_k_top_p_filtering + +logger = logging.getLogger(__name__) + + +class GPTConfig: + """ base GPT config, params common to all GPT versions """ + embd_pdrop = 0.1 + resid_pdrop = 0.1 + attn_pdrop = 0.1 + + def __init__(self, vocab_size, block_size, **kwargs): + self.vocab_size = vocab_size + self.block_size = block_size + for k,v in kwargs.items(): + setattr(self, k, v) + + +class GPT1Config(GPTConfig): + """ GPT-1 like network roughly 125M params """ + n_layer = 12 + n_head = 12 + n_embd = 768 + + +class CausalSelfAttention(nn.Module): + """ + A vanilla multi-head masked self-attention layer with a projection at the end. + It is possible to use torch.nn.MultiheadAttention here but I am including an + explicit implementation here to show that there is nothing too scary here. + """ + + def __init__(self, config): + super().__init__() + assert config.n_embd % config.n_head == 0 + # key, query, value projections for all heads + self.key = nn.Linear(config.n_embd, config.n_embd) + self.query = nn.Linear(config.n_embd, config.n_embd) + self.value = nn.Linear(config.n_embd, config.n_embd) + # regularization + self.attn_drop = nn.Dropout(config.attn_pdrop) + self.resid_drop = nn.Dropout(config.resid_pdrop) + # output projection + self.proj = nn.Linear(config.n_embd, config.n_embd) + # causal mask to ensure that attention is only applied to the left in the input sequence + mask = torch.tril(torch.ones(config.block_size, + config.block_size)) + if hasattr(config, "n_unmasked"): + mask[:config.n_unmasked, :config.n_unmasked] = 1 + self.register_buffer("mask", mask.view(1, 1, config.block_size, config.block_size)) + self.n_head = config.n_head + + def forward(self, x, layer_past=None): + B, T, C = x.size() + + # calculate query, key, values for all heads in batch and move head forward to be the batch dim + k = self.key(x).view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs) + q = self.query(x).view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs) + v = self.value(x).view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs) + + present = torch.stack((k, v)) + if layer_past is not None: + past_key, past_value = layer_past + k = torch.cat((past_key, k), dim=-2) + v = torch.cat((past_value, v), dim=-2) + + # causal self-attention; Self-attend: (B, nh, T, hs) x (B, nh, hs, T) -> (B, nh, T, T) + att = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(k.size(-1))) + if layer_past is None: + att = att.masked_fill(self.mask[:,:,:T,:T] == 0, float('-inf')) + + att = F.softmax(att, dim=-1) + att = self.attn_drop(att) + y = att @ v # (B, nh, T, T) x (B, nh, T, hs) -> (B, nh, T, hs) + y = y.transpose(1, 2).contiguous().view(B, T, C) # re-assemble all head outputs side by side + + # output projection + y = self.resid_drop(self.proj(y)) + return y, present # TODO: check that this does not break anything + + +class Block(nn.Module): + """ an unassuming Transformer block """ + def __init__(self, config): + super().__init__() + self.ln1 = nn.LayerNorm(config.n_embd) + self.ln2 = nn.LayerNorm(config.n_embd) + self.attn = CausalSelfAttention(config) + self.mlp = nn.Sequential( + nn.Linear(config.n_embd, 4 * config.n_embd), + nn.GELU(), # nice + nn.Linear(4 * config.n_embd, config.n_embd), + nn.Dropout(config.resid_pdrop), + ) + + def forward(self, x, layer_past=None, return_present=False): + # TODO: check that training still works + if return_present: assert not self.training + # layer past: tuple of length two with B, nh, T, hs + attn, present = self.attn(self.ln1(x), layer_past=layer_past) + + x = x + attn + x = x + self.mlp(self.ln2(x)) + if layer_past is not None or return_present: + return x, present + return x + + +class GPT(nn.Module): + """ the full GPT language model, with a context size of block_size """ + def __init__(self, vocab_size, block_size, n_layer=12, n_head=8, n_embd=256, + embd_pdrop=0., resid_pdrop=0., attn_pdrop=0., n_unmasked=0): + super().__init__() + config = GPTConfig(vocab_size=vocab_size, block_size=block_size, + embd_pdrop=embd_pdrop, resid_pdrop=resid_pdrop, attn_pdrop=attn_pdrop, + n_layer=n_layer, n_head=n_head, n_embd=n_embd, + n_unmasked=n_unmasked) + # input embedding stem + self.tok_emb = nn.Embedding(config.vocab_size, config.n_embd) + self.pos_emb = nn.Parameter(torch.zeros(1, config.block_size, config.n_embd)) + self.drop = nn.Dropout(config.embd_pdrop) + # transformer + self.blocks = nn.Sequential(*[Block(config) for _ in range(config.n_layer)]) + # decoder head + self.ln_f = nn.LayerNorm(config.n_embd) + self.head = nn.Linear(config.n_embd, config.vocab_size, bias=False) + self.block_size = config.block_size + self.apply(self._init_weights) + self.config = config + logger.info("number of parameters: %e", sum(p.numel() for p in self.parameters())) + + def get_block_size(self): + return self.block_size + + def _init_weights(self, module): + if isinstance(module, (nn.Linear, nn.Embedding)): + module.weight.data.normal_(mean=0.0, std=0.02) + if isinstance(module, nn.Linear) and module.bias is not None: + module.bias.data.zero_() + elif isinstance(module, nn.LayerNorm): + module.bias.data.zero_() + module.weight.data.fill_(1.0) + + def forward(self, idx, embeddings=None, targets=None): + # forward the GPT model + token_embeddings = self.tok_emb(idx) # each index maps to a (learnable) vector + + if embeddings is not None: # prepend explicit embeddings + token_embeddings = torch.cat((embeddings, token_embeddings), dim=1) + + t = token_embeddings.shape[1] + assert t <= self.block_size, "Cannot forward, model block size is exhausted." + position_embeddings = self.pos_emb[:, :t, :] # each position maps to a (learnable) vector + x = self.drop(token_embeddings + position_embeddings) + x = self.blocks(x) + x = self.ln_f(x) + logits = self.head(x) + + # if we are given some desired targets also calculate the loss + loss = None + if targets is not None: + loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1)) + + return logits, loss + + def forward_with_past(self, idx, embeddings=None, targets=None, past=None, past_length=None): + # inference only + assert not self.training + token_embeddings = self.tok_emb(idx) # each index maps to a (learnable) vector + if embeddings is not None: # prepend explicit embeddings + token_embeddings = torch.cat((embeddings, token_embeddings), dim=1) + + if past is not None: + assert past_length is not None + past = torch.cat(past, dim=-2) # n_layer, 2, b, nh, len_past, dim_head + past_shape = list(past.shape) + expected_shape = [self.config.n_layer, 2, idx.shape[0], self.config.n_head, past_length, self.config.n_embd//self.config.n_head] + assert past_shape == expected_shape, f"{past_shape} =/= {expected_shape}" + position_embeddings = self.pos_emb[:, past_length, :] # each position maps to a (learnable) vector + else: + position_embeddings = self.pos_emb[:, :token_embeddings.shape[1], :] + + x = self.drop(token_embeddings + position_embeddings) + presents = [] # accumulate over layers + for i, block in enumerate(self.blocks): + x, present = block(x, layer_past=past[i, ...] if past is not None else None, return_present=True) + presents.append(present) + + x = self.ln_f(x) + logits = self.head(x) + # if we are given some desired targets also calculate the loss + loss = None + if targets is not None: + loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1)) + + return logits, loss, torch.stack(presents) # _, _, n_layer, 2, b, nh, 1, dim_head + + +class DummyGPT(nn.Module): + # for debugging + def __init__(self, add_value=1): + super().__init__() + self.add_value = add_value + + def forward(self, idx): + return idx + self.add_value, None + + +class CodeGPT(nn.Module): + """Takes in semi-embeddings""" + def __init__(self, vocab_size, block_size, in_channels, n_layer=12, n_head=8, n_embd=256, + embd_pdrop=0., resid_pdrop=0., attn_pdrop=0., n_unmasked=0): + super().__init__() + config = GPTConfig(vocab_size=vocab_size, block_size=block_size, + embd_pdrop=embd_pdrop, resid_pdrop=resid_pdrop, attn_pdrop=attn_pdrop, + n_layer=n_layer, n_head=n_head, n_embd=n_embd, + n_unmasked=n_unmasked) + # input embedding stem + self.tok_emb = nn.Linear(in_channels, config.n_embd) + self.pos_emb = nn.Parameter(torch.zeros(1, config.block_size, config.n_embd)) + self.drop = nn.Dropout(config.embd_pdrop) + # transformer + self.blocks = nn.Sequential(*[Block(config) for _ in range(config.n_layer)]) + # decoder head + self.ln_f = nn.LayerNorm(config.n_embd) + self.head = nn.Linear(config.n_embd, config.vocab_size, bias=False) + self.block_size = config.block_size + self.apply(self._init_weights) + self.config = config + logger.info("number of parameters: %e", sum(p.numel() for p in self.parameters())) + + def get_block_size(self): + return self.block_size + + def _init_weights(self, module): + if isinstance(module, (nn.Linear, nn.Embedding)): + module.weight.data.normal_(mean=0.0, std=0.02) + if isinstance(module, nn.Linear) and module.bias is not None: + module.bias.data.zero_() + elif isinstance(module, nn.LayerNorm): + module.bias.data.zero_() + module.weight.data.fill_(1.0) + + def forward(self, idx, embeddings=None, targets=None): + # forward the GPT model + token_embeddings = self.tok_emb(idx) # each index maps to a (learnable) vector + + if embeddings is not None: # prepend explicit embeddings + token_embeddings = torch.cat((embeddings, token_embeddings), dim=1) + + t = token_embeddings.shape[1] + assert t <= self.block_size, "Cannot forward, model block size is exhausted." + position_embeddings = self.pos_emb[:, :t, :] # each position maps to a (learnable) vector + x = self.drop(token_embeddings + position_embeddings) + x = self.blocks(x) + x = self.taming_cinln_f(x) + logits = self.head(x) + + # if we are given some desired targets also calculate the loss + loss = None + if targets is not None: + loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1)) + + return logits, loss + + + +#### sampling utils + +def top_k_logits(logits, k): + v, ix = torch.topk(logits, k) + out = logits.clone() + out[out < v[:, [-1]]] = -float('Inf') + return out + +@torch.no_grad() +def sample(model, x, steps, temperature=1.0, sample=False, top_k=None): + """ + take a conditioning sequence of indices in x (of shape (b,t)) and predict the next token in + the sequence, feeding the predictions back into the model each time. Clearly the sampling + has quadratic complexity unlike an RNN that is only linear, and has a finite context window + of block_size, unlike an RNN that has an infinite context window. + """ + block_size = model.get_block_size() + model.eval() + for k in range(steps): + x_cond = x if x.size(1) <= block_size else x[:, -block_size:] # crop context if needed + logits, _ = model(x_cond) + # pluck the logits at the final step and scale by temperature + logits = logits[:, -1, :] / temperature + # optionally crop probabilities to only the top k options + if top_k is not None: + logits = top_k_logits(logits, top_k) + # apply softmax to convert to probabilities + probs = F.softmax(logits, dim=-1) + # sample from the distribution or take the most likely + if sample: + ix = torch.multinomial(probs, num_samples=1) + else: + _, ix = torch.topk(probs, k=1, dim=-1) + # append to the sequence and continue + x = torch.cat((x, ix), dim=1) + + return x + + +@torch.no_grad() +def sample_with_past(x, model, steps, temperature=1., sample_logits=True, + top_k=None, top_p=None, callback=None): + # x is conditioning + sample = x + cond_len = x.shape[1] + past = None + for n in range(steps): + if callback is not None: + callback(n) + logits, _, present = model.forward_with_past(x, past=past, past_length=(n+cond_len-1)) + if past is None: + past = [present] + else: + past.append(present) + logits = logits[:, -1, :] / temperature + if top_k is not None: + logits = top_k_top_p_filtering(logits, top_k=top_k, top_p=top_p) + + probs = F.softmax(logits, dim=-1) + if not sample_logits: + _, x = torch.topk(probs, k=1, dim=-1) + else: + x = torch.multinomial(probs, num_samples=1) + # append to the sequence and continue + sample = torch.cat((sample, x), dim=1) + del past + sample = sample[:, cond_len:] # cut conditioning off + return sample + + +#### clustering utils + +class KMeans(nn.Module): + def __init__(self, ncluster=512, nc=3, niter=10): + super().__init__() + self.ncluster = ncluster + self.nc = nc + self.niter = niter + self.shape = (3,32,32) + self.register_buffer("C", torch.zeros(self.ncluster,nc)) + self.register_buffer('initialized', torch.tensor(0, dtype=torch.uint8)) + + def is_initialized(self): + return self.initialized.item() == 1 + + @torch.no_grad() + def initialize(self, x): + N, D = x.shape + assert D == self.nc, D + c = x[torch.randperm(N)[:self.ncluster]] # init clusters at random + for i in range(self.niter): + # assign all pixels to the closest codebook element + a = ((x[:, None, :] - c[None, :, :])**2).sum(-1).argmin(1) + # move each codebook element to be the mean of the pixels that assigned to it + c = torch.stack([x[a==k].mean(0) for k in range(self.ncluster)]) + # re-assign any poorly positioned codebook elements + nanix = torch.any(torch.isnan(c), dim=1) + ndead = nanix.sum().item() + print('done step %d/%d, re-initialized %d dead clusters' % (i+1, self.niter, ndead)) + c[nanix] = x[torch.randperm(N)[:ndead]] # re-init dead clusters + + self.C.copy_(c) + self.initialized.fill_(1) + + + def forward(self, x, reverse=False, shape=None): + if not reverse: + # flatten + bs,c,h,w = x.shape + assert c == self.nc + x = x.reshape(bs,c,h*w,1) + C = self.C.permute(1,0) + C = C.reshape(1,c,1,self.ncluster) + a = ((x-C)**2).sum(1).argmin(-1) # bs, h*w indices + return a + else: + # flatten + bs, HW = x.shape + """ + c = self.C.reshape( 1, self.nc, 1, self.ncluster) + c = c[bs*[0],:,:,:] + c = c[:,:,HW*[0],:] + x = x.reshape(bs, 1, HW, 1) + x = x[:,3*[0],:,:] + x = torch.gather(c, dim=3, index=x) + """ + x = self.C[x] + x = x.permute(0,2,1) + shape = shape if shape is not None else self.shape + x = x.reshape(bs, *shape) + + return x diff --git a/repositories/taming/modules/transformer/permuter.py b/repositories/taming/modules/transformer/permuter.py new file mode 100644 index 000000000..0d43bb135 --- /dev/null +++ b/repositories/taming/modules/transformer/permuter.py @@ -0,0 +1,248 @@ +import torch +import torch.nn as nn +import numpy as np + + +class AbstractPermuter(nn.Module): + def __init__(self, *args, **kwargs): + super().__init__() + def forward(self, x, reverse=False): + raise NotImplementedError + + +class Identity(AbstractPermuter): + def __init__(self): + super().__init__() + + def forward(self, x, reverse=False): + return x + + +class Subsample(AbstractPermuter): + def __init__(self, H, W): + super().__init__() + C = 1 + indices = np.arange(H*W).reshape(C,H,W) + while min(H, W) > 1: + indices = indices.reshape(C,H//2,2,W//2,2) + indices = indices.transpose(0,2,4,1,3) + indices = indices.reshape(C*4,H//2, W//2) + H = H//2 + W = W//2 + C = C*4 + assert H == W == 1 + idx = torch.tensor(indices.ravel()) + self.register_buffer('forward_shuffle_idx', + nn.Parameter(idx, requires_grad=False)) + self.register_buffer('backward_shuffle_idx', + nn.Parameter(torch.argsort(idx), requires_grad=False)) + + def forward(self, x, reverse=False): + if not reverse: + return x[:, self.forward_shuffle_idx] + else: + return x[:, self.backward_shuffle_idx] + + +def mortonify(i, j): + """(i,j) index to linear morton code""" + i = np.uint64(i) + j = np.uint64(j) + + z = np.uint(0) + + for pos in range(32): + z = (z | + ((j & (np.uint64(1) << np.uint64(pos))) << np.uint64(pos)) | + ((i & (np.uint64(1) << np.uint64(pos))) << np.uint64(pos+1)) + ) + return z + + +class ZCurve(AbstractPermuter): + def __init__(self, H, W): + super().__init__() + reverseidx = [np.int64(mortonify(i,j)) for i in range(H) for j in range(W)] + idx = np.argsort(reverseidx) + idx = torch.tensor(idx) + reverseidx = torch.tensor(reverseidx) + self.register_buffer('forward_shuffle_idx', + idx) + self.register_buffer('backward_shuffle_idx', + reverseidx) + + def forward(self, x, reverse=False): + if not reverse: + return x[:, self.forward_shuffle_idx] + else: + return x[:, self.backward_shuffle_idx] + + +class SpiralOut(AbstractPermuter): + def __init__(self, H, W): + super().__init__() + assert H == W + size = W + indices = np.arange(size*size).reshape(size,size) + + i0 = size//2 + j0 = size//2-1 + + i = i0 + j = j0 + + idx = [indices[i0, j0]] + step_mult = 0 + for c in range(1, size//2+1): + step_mult += 1 + # steps left + for k in range(step_mult): + i = i - 1 + j = j + idx.append(indices[i, j]) + + # step down + for k in range(step_mult): + i = i + j = j + 1 + idx.append(indices[i, j]) + + step_mult += 1 + if c < size//2: + # step right + for k in range(step_mult): + i = i + 1 + j = j + idx.append(indices[i, j]) + + # step up + for k in range(step_mult): + i = i + j = j - 1 + idx.append(indices[i, j]) + else: + # end reached + for k in range(step_mult-1): + i = i + 1 + idx.append(indices[i, j]) + + assert len(idx) == size*size + idx = torch.tensor(idx) + self.register_buffer('forward_shuffle_idx', idx) + self.register_buffer('backward_shuffle_idx', torch.argsort(idx)) + + def forward(self, x, reverse=False): + if not reverse: + return x[:, self.forward_shuffle_idx] + else: + return x[:, self.backward_shuffle_idx] + + +class SpiralIn(AbstractPermuter): + def __init__(self, H, W): + super().__init__() + assert H == W + size = W + indices = np.arange(size*size).reshape(size,size) + + i0 = size//2 + j0 = size//2-1 + + i = i0 + j = j0 + + idx = [indices[i0, j0]] + step_mult = 0 + for c in range(1, size//2+1): + step_mult += 1 + # steps left + for k in range(step_mult): + i = i - 1 + j = j + idx.append(indices[i, j]) + + # step down + for k in range(step_mult): + i = i + j = j + 1 + idx.append(indices[i, j]) + + step_mult += 1 + if c < size//2: + # step right + for k in range(step_mult): + i = i + 1 + j = j + idx.append(indices[i, j]) + + # step up + for k in range(step_mult): + i = i + j = j - 1 + idx.append(indices[i, j]) + else: + # end reached + for k in range(step_mult-1): + i = i + 1 + idx.append(indices[i, j]) + + assert len(idx) == size*size + idx = idx[::-1] + idx = torch.tensor(idx) + self.register_buffer('forward_shuffle_idx', idx) + self.register_buffer('backward_shuffle_idx', torch.argsort(idx)) + + def forward(self, x, reverse=False): + if not reverse: + return x[:, self.forward_shuffle_idx] + else: + return x[:, self.backward_shuffle_idx] + + +class Random(nn.Module): + def __init__(self, H, W): + super().__init__() + indices = np.random.RandomState(1).permutation(H*W) + idx = torch.tensor(indices.ravel()) + self.register_buffer('forward_shuffle_idx', idx) + self.register_buffer('backward_shuffle_idx', torch.argsort(idx)) + + def forward(self, x, reverse=False): + if not reverse: + return x[:, self.forward_shuffle_idx] + else: + return x[:, self.backward_shuffle_idx] + + +class AlternateParsing(AbstractPermuter): + def __init__(self, H, W): + super().__init__() + indices = np.arange(W*H).reshape(H,W) + for i in range(1, H, 2): + indices[i, :] = indices[i, ::-1] + idx = indices.flatten() + assert len(idx) == H*W + idx = torch.tensor(idx) + self.register_buffer('forward_shuffle_idx', idx) + self.register_buffer('backward_shuffle_idx', torch.argsort(idx)) + + def forward(self, x, reverse=False): + if not reverse: + return x[:, self.forward_shuffle_idx] + else: + return x[:, self.backward_shuffle_idx] + + +if __name__ == "__main__": + p0 = AlternateParsing(16, 16) + print(p0.forward_shuffle_idx) + print(p0.backward_shuffle_idx) + + x = torch.randint(0, 768, size=(11, 256)) + y = p0(x) + xre = p0(y, reverse=True) + assert torch.equal(x, xre) + + p1 = SpiralOut(2, 2) + print(p1.forward_shuffle_idx) + print(p1.backward_shuffle_idx) diff --git a/repositories/taming/modules/util.py b/repositories/taming/modules/util.py new file mode 100644 index 000000000..9ee16385d --- /dev/null +++ b/repositories/taming/modules/util.py @@ -0,0 +1,130 @@ +import torch +import torch.nn as nn + + +def count_params(model): + total_params = sum(p.numel() for p in model.parameters()) + return total_params + + +class ActNorm(nn.Module): + def __init__(self, num_features, logdet=False, affine=True, + allow_reverse_init=False): + assert affine + super().__init__() + self.logdet = logdet + self.loc = nn.Parameter(torch.zeros(1, num_features, 1, 1)) + self.scale = nn.Parameter(torch.ones(1, num_features, 1, 1)) + self.allow_reverse_init = allow_reverse_init + + self.register_buffer('initialized', torch.tensor(0, dtype=torch.uint8)) + + def initialize(self, input): + with torch.no_grad(): + flatten = input.permute(1, 0, 2, 3).contiguous().view(input.shape[1], -1) + mean = ( + flatten.mean(1) + .unsqueeze(1) + .unsqueeze(2) + .unsqueeze(3) + .permute(1, 0, 2, 3) + ) + std = ( + flatten.std(1) + .unsqueeze(1) + .unsqueeze(2) + .unsqueeze(3) + .permute(1, 0, 2, 3) + ) + + self.loc.data.copy_(-mean) + self.scale.data.copy_(1 / (std + 1e-6)) + + def forward(self, input, reverse=False): + if reverse: + return self.reverse(input) + if len(input.shape) == 2: + input = input[:,:,None,None] + squeeze = True + else: + squeeze = False + + _, _, height, width = input.shape + + if self.training and self.initialized.item() == 0: + self.initialize(input) + self.initialized.fill_(1) + + h = self.scale * (input + self.loc) + + if squeeze: + h = h.squeeze(-1).squeeze(-1) + + if self.logdet: + log_abs = torch.log(torch.abs(self.scale)) + logdet = height*width*torch.sum(log_abs) + logdet = logdet * torch.ones(input.shape[0]).to(input) + return h, logdet + + return h + + def reverse(self, output): + if self.training and self.initialized.item() == 0: + if not self.allow_reverse_init: + raise RuntimeError( + "Initializing ActNorm in reverse direction is " + "disabled by default. Use allow_reverse_init=True to enable." + ) + else: + self.initialize(output) + self.initialized.fill_(1) + + if len(output.shape) == 2: + output = output[:,:,None,None] + squeeze = True + else: + squeeze = False + + h = output / self.scale - self.loc + + if squeeze: + h = h.squeeze(-1).squeeze(-1) + return h + + +class AbstractEncoder(nn.Module): + def __init__(self): + super().__init__() + + def encode(self, *args, **kwargs): + raise NotImplementedError + + +class Labelator(AbstractEncoder): + """Net2Net Interface for Class-Conditional Model""" + def __init__(self, n_classes, quantize_interface=True): + super().__init__() + self.n_classes = n_classes + self.quantize_interface = quantize_interface + + def encode(self, c): + c = c[:,None] + if self.quantize_interface: + return c, None, [None, None, c.long()] + return c + + +class SOSProvider(AbstractEncoder): + # for unconditional training + def __init__(self, sos_token, quantize_interface=True): + super().__init__() + self.sos_token = sos_token + self.quantize_interface = quantize_interface + + def encode(self, x): + # get batch size from data and replicate sos_token + c = torch.ones(x.shape[0], 1)*self.sos_token + c = c.long().to(x.device) + if self.quantize_interface: + return c, None, [None, None, c] + return c diff --git a/repositories/taming/modules/vqvae/quantize.py b/repositories/taming/modules/vqvae/quantize.py new file mode 100644 index 000000000..d75544e41 --- /dev/null +++ b/repositories/taming/modules/vqvae/quantize.py @@ -0,0 +1,445 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +import numpy as np +from torch import einsum +from einops import rearrange + + +class VectorQuantizer(nn.Module): + """ + see https://github.com/MishaLaskin/vqvae/blob/d761a999e2267766400dc646d82d3ac3657771d4/models/quantizer.py + ____________________________________________ + Discretization bottleneck part of the VQ-VAE. + Inputs: + - n_e : number of embeddings + - e_dim : dimension of embedding + - beta : commitment cost used in loss term, beta * ||z_e(x)-sg[e]||^2 + _____________________________________________ + """ + + # NOTE: this class contains a bug regarding beta; see VectorQuantizer2 for + # a fix and use legacy=False to apply that fix. VectorQuantizer2 can be + # used wherever VectorQuantizer has been used before and is additionally + # more efficient. + def __init__(self, n_e, e_dim, beta): + super(VectorQuantizer, self).__init__() + self.n_e = n_e + self.e_dim = e_dim + self.beta = beta + + self.embedding = nn.Embedding(self.n_e, self.e_dim) + self.embedding.weight.data.uniform_(-1.0 / self.n_e, 1.0 / self.n_e) + + def forward(self, z): + """ + Inputs the output of the encoder network z and maps it to a discrete + one-hot vector that is the index of the closest embedding vector e_j + z (continuous) -> z_q (discrete) + z.shape = (batch, channel, height, width) + quantization pipeline: + 1. get encoder input (B,C,H,W) + 2. flatten input to (B*H*W,C) + """ + # reshape z -> (batch, height, width, channel) and flatten + z = z.permute(0, 2, 3, 1).contiguous() + z_flattened = z.view(-1, self.e_dim) + # distances from z to embeddings e_j (z - e)^2 = z^2 + e^2 - 2 e * z + + d = torch.sum(z_flattened ** 2, dim=1, keepdim=True) + \ + torch.sum(self.embedding.weight**2, dim=1) - 2 * \ + torch.matmul(z_flattened, self.embedding.weight.t()) + + ## could possible replace this here + # #\start... + # find closest encodings + min_encoding_indices = torch.argmin(d, dim=1).unsqueeze(1) + + min_encodings = torch.zeros( + min_encoding_indices.shape[0], self.n_e).to(z) + min_encodings.scatter_(1, min_encoding_indices, 1) + + # dtype min encodings: torch.float32 + # min_encodings shape: torch.Size([2048, 512]) + # min_encoding_indices.shape: torch.Size([2048, 1]) + + # get quantized latent vectors + z_q = torch.matmul(min_encodings, self.embedding.weight).view(z.shape) + #.........\end + + # with: + # .........\start + #min_encoding_indices = torch.argmin(d, dim=1) + #z_q = self.embedding(min_encoding_indices) + # ......\end......... (TODO) + + # compute loss for embedding + loss = torch.mean((z_q.detach()-z)**2) + self.beta * \ + torch.mean((z_q - z.detach()) ** 2) + + # preserve gradients + z_q = z + (z_q - z).detach() + + # perplexity + e_mean = torch.mean(min_encodings, dim=0) + perplexity = torch.exp(-torch.sum(e_mean * torch.log(e_mean + 1e-10))) + + # reshape back to match original input shape + z_q = z_q.permute(0, 3, 1, 2).contiguous() + + return z_q, loss, (perplexity, min_encodings, min_encoding_indices) + + def get_codebook_entry(self, indices, shape): + # shape specifying (batch, height, width, channel) + # TODO: check for more easy handling with nn.Embedding + min_encodings = torch.zeros(indices.shape[0], self.n_e).to(indices) + min_encodings.scatter_(1, indices[:,None], 1) + + # get quantized latent vectors + z_q = torch.matmul(min_encodings.float(), self.embedding.weight) + + if shape is not None: + z_q = z_q.view(shape) + + # reshape back to match original input shape + z_q = z_q.permute(0, 3, 1, 2).contiguous() + + return z_q + + +class GumbelQuantize(nn.Module): + """ + credit to @karpathy: https://github.com/karpathy/deep-vector-quantization/blob/main/model.py (thanks!) + Gumbel Softmax trick quantizer + Categorical Reparameterization with Gumbel-Softmax, Jang et al. 2016 + https://arxiv.org/abs/1611.01144 + """ + def __init__(self, num_hiddens, embedding_dim, n_embed, straight_through=True, + kl_weight=5e-4, temp_init=1.0, use_vqinterface=True, + remap=None, unknown_index="random"): + super().__init__() + + self.embedding_dim = embedding_dim + self.n_embed = n_embed + + self.straight_through = straight_through + self.temperature = temp_init + self.kl_weight = kl_weight + + self.proj = nn.Conv2d(num_hiddens, n_embed, 1) + self.embed = nn.Embedding(n_embed, embedding_dim) + + self.use_vqinterface = use_vqinterface + + self.remap = remap + if self.remap is not None: + self.register_buffer("used", torch.tensor(np.load(self.remap))) + self.re_embed = self.used.shape[0] + self.unknown_index = unknown_index # "random" or "extra" or integer + if self.unknown_index == "extra": + self.unknown_index = self.re_embed + self.re_embed = self.re_embed+1 + print(f"Remapping {self.n_embed} indices to {self.re_embed} indices. " + f"Using {self.unknown_index} for unknown indices.") + else: + self.re_embed = n_embed + + def remap_to_used(self, inds): + ishape = inds.shape + assert len(ishape)>1 + inds = inds.reshape(ishape[0],-1) + used = self.used.to(inds) + match = (inds[:,:,None]==used[None,None,...]).long() + new = match.argmax(-1) + unknown = match.sum(2)<1 + if self.unknown_index == "random": + new[unknown]=torch.randint(0,self.re_embed,size=new[unknown].shape).to(device=new.device) + else: + new[unknown] = self.unknown_index + return new.reshape(ishape) + + def unmap_to_all(self, inds): + ishape = inds.shape + assert len(ishape)>1 + inds = inds.reshape(ishape[0],-1) + used = self.used.to(inds) + if self.re_embed > self.used.shape[0]: # extra token + inds[inds>=self.used.shape[0]] = 0 # simply set to zero + back=torch.gather(used[None,:][inds.shape[0]*[0],:], 1, inds) + return back.reshape(ishape) + + def forward(self, z, temp=None, return_logits=False): + # force hard = True when we are in eval mode, as we must quantize. actually, always true seems to work + hard = self.straight_through if self.training else True + temp = self.temperature if temp is None else temp + + logits = self.proj(z) + if self.remap is not None: + # continue only with used logits + full_zeros = torch.zeros_like(logits) + logits = logits[:,self.used,...] + + soft_one_hot = F.gumbel_softmax(logits, tau=temp, dim=1, hard=hard) + if self.remap is not None: + # go back to all entries but unused set to zero + full_zeros[:,self.used,...] = soft_one_hot + soft_one_hot = full_zeros + z_q = einsum('b n h w, n d -> b d h w', soft_one_hot, self.embed.weight) + + # + kl divergence to the prior loss + qy = F.softmax(logits, dim=1) + diff = self.kl_weight * torch.sum(qy * torch.log(qy * self.n_embed + 1e-10), dim=1).mean() + + ind = soft_one_hot.argmax(dim=1) + if self.remap is not None: + ind = self.remap_to_used(ind) + if self.use_vqinterface: + if return_logits: + return z_q, diff, (None, None, ind), logits + return z_q, diff, (None, None, ind) + return z_q, diff, ind + + def get_codebook_entry(self, indices, shape): + b, h, w, c = shape + assert b*h*w == indices.shape[0] + indices = rearrange(indices, '(b h w) -> b h w', b=b, h=h, w=w) + if self.remap is not None: + indices = self.unmap_to_all(indices) + one_hot = F.one_hot(indices, num_classes=self.n_embed).permute(0, 3, 1, 2).float() + z_q = einsum('b n h w, n d -> b d h w', one_hot, self.embed.weight) + return z_q + + +class VectorQuantizer2(nn.Module): + """ + Improved version over VectorQuantizer, can be used as a drop-in replacement. Mostly + avoids costly matrix multiplications and allows for post-hoc remapping of indices. + """ + # NOTE: due to a bug the beta term was applied to the wrong term. for + # backwards compatibility we use the buggy version by default, but you can + # specify legacy=False to fix it. + def __init__(self, n_e, e_dim, beta, remap=None, unknown_index="random", + sane_index_shape=False, legacy=True): + super().__init__() + self.n_e = n_e + self.e_dim = e_dim + self.beta = beta + self.legacy = legacy + + self.embedding = nn.Embedding(self.n_e, self.e_dim) + self.embedding.weight.data.uniform_(-1.0 / self.n_e, 1.0 / self.n_e) + + self.remap = remap + if self.remap is not None: + self.register_buffer("used", torch.tensor(np.load(self.remap))) + self.re_embed = self.used.shape[0] + self.unknown_index = unknown_index # "random" or "extra" or integer + if self.unknown_index == "extra": + self.unknown_index = self.re_embed + self.re_embed = self.re_embed+1 + print(f"Remapping {self.n_e} indices to {self.re_embed} indices. " + f"Using {self.unknown_index} for unknown indices.") + else: + self.re_embed = n_e + + self.sane_index_shape = sane_index_shape + + def remap_to_used(self, inds): + ishape = inds.shape + assert len(ishape)>1 + inds = inds.reshape(ishape[0],-1) + used = self.used.to(inds) + match = (inds[:,:,None]==used[None,None,...]).long() + new = match.argmax(-1) + unknown = match.sum(2)<1 + if self.unknown_index == "random": + new[unknown]=torch.randint(0,self.re_embed,size=new[unknown].shape).to(device=new.device) + else: + new[unknown] = self.unknown_index + return new.reshape(ishape) + + def unmap_to_all(self, inds): + ishape = inds.shape + assert len(ishape)>1 + inds = inds.reshape(ishape[0],-1) + used = self.used.to(inds) + if self.re_embed > self.used.shape[0]: # extra token + inds[inds>=self.used.shape[0]] = 0 # simply set to zero + back=torch.gather(used[None,:][inds.shape[0]*[0],:], 1, inds) + return back.reshape(ishape) + + def forward(self, z, temp=None, rescale_logits=False, return_logits=False): + assert temp is None or temp==1.0, "Only for interface compatible with Gumbel" + assert rescale_logits==False, "Only for interface compatible with Gumbel" + assert return_logits==False, "Only for interface compatible with Gumbel" + # reshape z -> (batch, height, width, channel) and flatten + z = rearrange(z, 'b c h w -> b h w c').contiguous() + z_flattened = z.view(-1, self.e_dim) + # distances from z to embeddings e_j (z - e)^2 = z^2 + e^2 - 2 e * z + + d = torch.sum(z_flattened ** 2, dim=1, keepdim=True) + \ + torch.sum(self.embedding.weight**2, dim=1) - 2 * \ + torch.einsum('bd,dn->bn', z_flattened, rearrange(self.embedding.weight, 'n d -> d n')) + + min_encoding_indices = torch.argmin(d, dim=1) + z_q = self.embedding(min_encoding_indices).view(z.shape) + perplexity = None + min_encodings = None + + # compute loss for embedding + if not self.legacy: + loss = self.beta * torch.mean((z_q.detach()-z)**2) + \ + torch.mean((z_q - z.detach()) ** 2) + else: + loss = torch.mean((z_q.detach()-z)**2) + self.beta * \ + torch.mean((z_q - z.detach()) ** 2) + + # preserve gradients + z_q = z + (z_q - z).detach() + + # reshape back to match original input shape + z_q = rearrange(z_q, 'b h w c -> b c h w').contiguous() + + if self.remap is not None: + min_encoding_indices = min_encoding_indices.reshape(z.shape[0],-1) # add batch axis + min_encoding_indices = self.remap_to_used(min_encoding_indices) + min_encoding_indices = min_encoding_indices.reshape(-1,1) # flatten + + if self.sane_index_shape: + min_encoding_indices = min_encoding_indices.reshape( + z_q.shape[0], z_q.shape[2], z_q.shape[3]) + + return z_q, loss, (perplexity, min_encodings, min_encoding_indices) + + def get_codebook_entry(self, indices, shape): + # shape specifying (batch, height, width, channel) + if self.remap is not None: + indices = indices.reshape(shape[0],-1) # add batch axis + indices = self.unmap_to_all(indices) + indices = indices.reshape(-1) # flatten again + + # get quantized latent vectors + z_q = self.embedding(indices) + + if shape is not None: + z_q = z_q.view(shape) + # reshape back to match original input shape + z_q = z_q.permute(0, 3, 1, 2).contiguous() + + return z_q + +class EmbeddingEMA(nn.Module): + def __init__(self, num_tokens, codebook_dim, decay=0.99, eps=1e-5): + super().__init__() + self.decay = decay + self.eps = eps + weight = torch.randn(num_tokens, codebook_dim) + self.weight = nn.Parameter(weight, requires_grad = False) + self.cluster_size = nn.Parameter(torch.zeros(num_tokens), requires_grad = False) + self.embed_avg = nn.Parameter(weight.clone(), requires_grad = False) + self.update = True + + def forward(self, embed_id): + return F.embedding(embed_id, self.weight) + + def cluster_size_ema_update(self, new_cluster_size): + self.cluster_size.data.mul_(self.decay).add_(new_cluster_size, alpha=1 - self.decay) + + def embed_avg_ema_update(self, new_embed_avg): + self.embed_avg.data.mul_(self.decay).add_(new_embed_avg, alpha=1 - self.decay) + + def weight_update(self, num_tokens): + n = self.cluster_size.sum() + smoothed_cluster_size = ( + (self.cluster_size + self.eps) / (n + num_tokens * self.eps) * n + ) + #normalize embedding average with smoothed cluster size + embed_normalized = self.embed_avg / smoothed_cluster_size.unsqueeze(1) + self.weight.data.copy_(embed_normalized) + + +class EMAVectorQuantizer(nn.Module): + def __init__(self, n_embed, embedding_dim, beta, decay=0.99, eps=1e-5, + remap=None, unknown_index="random"): + super().__init__() + self.codebook_dim = codebook_dim + self.num_tokens = num_tokens + self.beta = beta + self.embedding = EmbeddingEMA(self.num_tokens, self.codebook_dim, decay, eps) + + self.remap = remap + if self.remap is not None: + self.register_buffer("used", torch.tensor(np.load(self.remap))) + self.re_embed = self.used.shape[0] + self.unknown_index = unknown_index # "random" or "extra" or integer + if self.unknown_index == "extra": + self.unknown_index = self.re_embed + self.re_embed = self.re_embed+1 + print(f"Remapping {self.n_embed} indices to {self.re_embed} indices. " + f"Using {self.unknown_index} for unknown indices.") + else: + self.re_embed = n_embed + + def remap_to_used(self, inds): + ishape = inds.shape + assert len(ishape)>1 + inds = inds.reshape(ishape[0],-1) + used = self.used.to(inds) + match = (inds[:,:,None]==used[None,None,...]).long() + new = match.argmax(-1) + unknown = match.sum(2)<1 + if self.unknown_index == "random": + new[unknown]=torch.randint(0,self.re_embed,size=new[unknown].shape).to(device=new.device) + else: + new[unknown] = self.unknown_index + return new.reshape(ishape) + + def unmap_to_all(self, inds): + ishape = inds.shape + assert len(ishape)>1 + inds = inds.reshape(ishape[0],-1) + used = self.used.to(inds) + if self.re_embed > self.used.shape[0]: # extra token + inds[inds>=self.used.shape[0]] = 0 # simply set to zero + back=torch.gather(used[None,:][inds.shape[0]*[0],:], 1, inds) + return back.reshape(ishape) + + def forward(self, z): + # reshape z -> (batch, height, width, channel) and flatten + #z, 'b c h w -> b h w c' + z = rearrange(z, 'b c h w -> b h w c') + z_flattened = z.reshape(-1, self.codebook_dim) + + # distances from z to embeddings e_j (z - e)^2 = z^2 + e^2 - 2 e * z + d = z_flattened.pow(2).sum(dim=1, keepdim=True) + \ + self.embedding.weight.pow(2).sum(dim=1) - 2 * \ + torch.einsum('bd,nd->bn', z_flattened, self.embedding.weight) # 'n d -> d n' + + + encoding_indices = torch.argmin(d, dim=1) + + z_q = self.embedding(encoding_indices).view(z.shape) + encodings = F.one_hot(encoding_indices, self.num_tokens).type(z.dtype) + avg_probs = torch.mean(encodings, dim=0) + perplexity = torch.exp(-torch.sum(avg_probs * torch.log(avg_probs + 1e-10))) + + if self.training and self.embedding.update: + #EMA cluster size + encodings_sum = encodings.sum(0) + self.embedding.cluster_size_ema_update(encodings_sum) + #EMA embedding average + embed_sum = encodings.transpose(0,1) @ z_flattened + self.embedding.embed_avg_ema_update(embed_sum) + #normalize embed_avg and update weight + self.embedding.weight_update(self.num_tokens) + + # compute loss for embedding + loss = self.beta * F.mse_loss(z_q.detach(), z) + + # preserve gradients + z_q = z + (z_q - z).detach() + + # reshape back to match original input shape + #z_q, 'b h w c -> b c h w' + z_q = rearrange(z_q, 'b h w c -> b c h w') + return z_q, loss, (perplexity, encodings, encoding_indices) diff --git a/repositories/taming/util.py b/repositories/taming/util.py new file mode 100644 index 000000000..06053e5de --- /dev/null +++ b/repositories/taming/util.py @@ -0,0 +1,157 @@ +import os, hashlib +import requests +from tqdm import tqdm + +URL_MAP = { + "vgg_lpips": "https://heibox.uni-heidelberg.de/f/607503859c864bc1b30b/?dl=1" +} + +CKPT_MAP = { + "vgg_lpips": "vgg.pth" +} + +MD5_MAP = { + "vgg_lpips": "d507d7349b931f0638a25a48a722f98a" +} + + +def download(url, local_path, chunk_size=1024): + os.makedirs(os.path.split(local_path)[0], exist_ok=True) + with requests.get(url, stream=True) as r: + total_size = int(r.headers.get("content-length", 0)) + with tqdm(total=total_size, unit="B", unit_scale=True) as pbar: + with open(local_path, "wb") as f: + for data in r.iter_content(chunk_size=chunk_size): + if data: + f.write(data) + pbar.update(chunk_size) + + +def md5_hash(path): + with open(path, "rb") as f: + content = f.read() + return hashlib.md5(content).hexdigest() + + +def get_ckpt_path(name, root, check=False): + assert name in URL_MAP + path = os.path.join(root, CKPT_MAP[name]) + if not os.path.exists(path) or (check and not md5_hash(path) == MD5_MAP[name]): + print("Downloading {} model from {} to {}".format(name, URL_MAP[name], path)) + download(URL_MAP[name], path) + md5 = md5_hash(path) + assert md5 == MD5_MAP[name], md5 + return path + + +class KeyNotFoundError(Exception): + def __init__(self, cause, keys=None, visited=None): + self.cause = cause + self.keys = keys + self.visited = visited + messages = list() + if keys is not None: + messages.append("Key not found: {}".format(keys)) + if visited is not None: + messages.append("Visited: {}".format(visited)) + messages.append("Cause:\n{}".format(cause)) + message = "\n".join(messages) + super().__init__(message) + + +def retrieve( + list_or_dict, key, splitval="/", default=None, expand=True, pass_success=False +): + """Given a nested list or dict return the desired value at key expanding + callable nodes if necessary and :attr:`expand` is ``True``. The expansion + is done in-place. + + Parameters + ---------- + list_or_dict : list or dict + Possibly nested list or dictionary. + key : str + key/to/value, path like string describing all keys necessary to + consider to get to the desired value. List indices can also be + passed here. + splitval : str + String that defines the delimiter between keys of the + different depth levels in `key`. + default : obj + Value returned if :attr:`key` is not found. + expand : bool + Whether to expand callable nodes on the path or not. + + Returns + ------- + The desired value or if :attr:`default` is not ``None`` and the + :attr:`key` is not found returns ``default``. + + Raises + ------ + Exception if ``key`` not in ``list_or_dict`` and :attr:`default` is + ``None``. + """ + + keys = key.split(splitval) + + success = True + try: + visited = [] + parent = None + last_key = None + for key in keys: + if callable(list_or_dict): + if not expand: + raise KeyNotFoundError( + ValueError( + "Trying to get past callable node with expand=False." + ), + keys=keys, + visited=visited, + ) + list_or_dict = list_or_dict() + parent[last_key] = list_or_dict + + last_key = key + parent = list_or_dict + + try: + if isinstance(list_or_dict, dict): + list_or_dict = list_or_dict[key] + else: + list_or_dict = list_or_dict[int(key)] + except (KeyError, IndexError, ValueError) as e: + raise KeyNotFoundError(e, keys=keys, visited=visited) + + visited += [key] + # final expansion of retrieved value + if expand and callable(list_or_dict): + list_or_dict = list_or_dict() + parent[last_key] = list_or_dict + except KeyNotFoundError as e: + if default is None: + raise e + else: + list_or_dict = default + success = False + + if not pass_success: + return list_or_dict + else: + return list_or_dict, success + + +if __name__ == "__main__": + config = {"keya": "a", + "keyb": "b", + "keyc": + {"cc1": 1, + "cc2": 2, + } + } + from omegaconf import OmegaConf + config = OmegaConf.create(config) + print(config) + retrieve(config, "keya") + diff --git a/requirements.txt b/requirements.txt index ffb5dacc3..f7b583b7e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -51,7 +51,7 @@ opencv-python-headless==4.7.0.72 diffusers==0.21.4 einops==0.4.1 gradio==3.43.2 -huggingface_hub==0.17.1 +huggingface_hub==0.18.0 numexpr==2.8.4 numpy==1.24.4 numba==0.57.1 diff --git a/scripts/loopback.py b/scripts/loopback.py index 413d3a6ee..cc4b5a6ee 100644 --- a/scripts/loopback.py +++ b/scripts/loopback.py @@ -90,7 +90,7 @@ def calculate_denoising_strength(loop): elif append_interrogation == "DeepBooru": p.prompt += deepbooru.model.tag(p.init_images[0]) - state.job = f"Iteration {i + 1}/{loops}, batch {n + 1}/{batch_count}" + state.job = f"loopback iteration {i+1}/{loops} batch {n+1}/{batch_count}" processed = processing.process_images(p) diff --git a/scripts/outpainting_mk_2.py b/scripts/outpainting_mk_2.py index 32c928327..d9ec0c02b 100644 --- a/scripts/outpainting_mk_2.py +++ b/scripts/outpainting_mk_2.py @@ -23,7 +23,6 @@ def _fft2(data): out_fft = np.zeros((data.shape[0], data.shape[1]), dtype=np.complex128) out_fft[:, :] = np.fft.fft2(np.fft.fftshift(data), norm="ortho") out_fft[:, :] = np.fft.ifftshift(out_fft[:, :]) - return out_fft def _ifft2(data): @@ -37,13 +36,11 @@ def _ifft2(data): out_ifft = np.zeros((data.shape[0], data.shape[1]), dtype=np.complex128) out_ifft[:, :] = np.fft.ifft2(np.fft.fftshift(data), norm="ortho") out_ifft[:, :] = np.fft.ifftshift(out_ifft[:, :]) - return out_ifft def _get_gaussian_window(width, height, std=3.14, mode=0): window_scale_x = float(width / min(width, height)) window_scale_y = float(height / min(width, height)) - window = np.zeros((width, height)) x = (np.arange(width) / width * 2. - 1.) * window_scale_x for y in range(height): @@ -52,7 +49,6 @@ def _get_gaussian_window(width, height, std=3.14, mode=0): window[:, y] = np.exp(-(x ** 2 + fy ** 2) * std) else: window[:, y] = (1 / ((x ** 2 + 1.) * (fy ** 2 + 1.))) ** (std / 3.14) # hey wait a minute that's not gaussian - return window def _get_masked_window_rgb(np_mask_grey, hardness=1.): @@ -64,57 +60,45 @@ def _get_masked_window_rgb(np_mask_grey, hardness=1.): for c in range(3): np_mask_rgb[:, :, c] = hardened[:] return np_mask_rgb - width = _np_src_image.shape[0] height = _np_src_image.shape[1] num_channels = _np_src_image.shape[2] - _np_src_image[:] * (1. - np_mask_rgb) # pylint: disable=pointless-statement np_mask_grey = np.sum(np_mask_rgb, axis=2) / 3. img_mask = np_mask_grey > 1e-6 ref_mask = np_mask_grey < 1e-3 - windowed_image = _np_src_image * (1. - _get_masked_window_rgb(np_mask_grey)) windowed_image /= np.max(windowed_image) windowed_image += np.average(_np_src_image) * np_mask_rgb # / (1.-np.average(np_mask_rgb)) # rather than leave the masked area black, we get better results from fft by filling the average unmasked color - src_fft = _fft2(windowed_image) # get feature statistics from masked src img src_dist = np.absolute(src_fft) src_phase = src_fft / src_dist - # create a generator with a static seed to make outpainting deterministic / only follow global seed rng = np.random.default_rng(0) - noise_window = _get_gaussian_window(width, height, mode=1) # start with simple gaussian noise noise_rgb = rng.random((width, height, num_channels)) noise_grey = np.sum(noise_rgb, axis=2) / 3. noise_rgb *= color_variation # the colorfulness of the starting noise is blended to greyscale with a parameter for c in range(num_channels): noise_rgb[:, :, c] += (1. - color_variation) * noise_grey - noise_fft = _fft2(noise_rgb) for c in range(num_channels): noise_fft[:, :, c] *= noise_window noise_rgb = np.real(_ifft2(noise_fft)) shaped_noise_fft = _fft2(noise_rgb) shaped_noise_fft[:, :, :] = np.absolute(shaped_noise_fft[:, :, :]) ** 2 * (src_dist ** noise_q) * src_phase # perform the actual shaping - brightness_variation = 0. # color_variation contrast_adjusted_np_src = _np_src_image[:] * (brightness_variation + 1.) - brightness_variation * 2. - # scikit-image is used for histogram matching, very convenient! shaped_noise = np.real(_ifft2(shaped_noise_fft)) shaped_noise -= np.min(shaped_noise) shaped_noise /= np.max(shaped_noise) shaped_noise[img_mask, :] = skimage.exposure.match_histograms(shaped_noise[img_mask, :] ** 1., contrast_adjusted_np_src[ref_mask, :], channel_axis=1) shaped_noise = _np_src_image[:] * (1. - np_mask_rgb) + shaped_noise * np_mask_rgb - matched_noise = shaped_noise[:] - return np.clip(matched_noise, 0., 1.) - class Script(scripts.Script): def title(self): return "Outpainting" @@ -125,56 +109,43 @@ def show(self, is_img2img): def ui(self, is_img2img): if not is_img2img: return None - info = gr.HTML("

Recommended settings: Sampling Steps: 80-100, Sampler: Euler a, Denoising strength: 0.8

") - pixels = gr.Slider(label="Pixels to expand", minimum=8, maximum=256, step=8, value=128, elem_id=self.elem_id("pixels")) mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=8, elem_id=self.elem_id("mask_blur")) direction = gr.CheckboxGroup(label="Outpainting direction", choices=['left', 'right', 'up', 'down'], value=['left', 'right', 'up', 'down'], elem_id=self.elem_id("direction")) noise_q = gr.Slider(label="Fall-off exponent (lower=higher detail)", minimum=0.0, maximum=4.0, step=0.01, value=1.0, elem_id=self.elem_id("noise_q")) color_variation = gr.Slider(label="Color variation", minimum=0.0, maximum=1.0, step=0.01, value=0.05, elem_id=self.elem_id("color_variation")) - return [info, pixels, mask_blur, direction, noise_q, color_variation] def run(self, p, _, pixels, mask_blur, direction, noise_q, color_variation): # pylint: disable=arguments-differ initial_seed_and_info = [None, None] - process_width = p.width process_height = p.height - p.mask_blur = mask_blur*4 p.inpaint_full_res = False p.inpainting_fill = 1 p.do_not_save_samples = True p.do_not_save_grid = True - left = pixels if "left" in direction else 0 right = pixels if "right" in direction else 0 up = pixels if "up" in direction else 0 down = pixels if "down" in direction else 0 - init_img = p.init_images[0] target_w = math.ceil((init_img.width + left + right) / 64) * 64 target_h = math.ceil((init_img.height + up + down) / 64) * 64 - if left > 0: left = left * (target_w - init_img.width) // (left + right) - if right > 0: right = target_w - init_img.width - left - if up > 0: up = up * (target_h - init_img.height) // (up + down) - if down > 0: down = target_h - init_img.height - up - def expand(init, count, expand_pixels, is_left=False, is_right=False, is_top=False, is_bottom=False): is_horiz = is_left or is_right is_vert = is_top or is_bottom pixels_horiz = expand_pixels if is_horiz else 0 pixels_vert = expand_pixels if is_vert else 0 - images_to_process = [] output_images = [] for n in range(count): @@ -182,7 +153,6 @@ def expand(init, count, expand_pixels, is_left=False, is_right=False, is_top=Fal res_h = init[n].height + pixels_vert process_res_w = math.ceil(res_w / 64) * 64 process_res_h = math.ceil(res_h / 64) * 64 - img = Image.new("RGB", (process_res_w, process_res_h)) img.paste(init[n], (pixels_horiz if is_left else 0, pixels_vert if is_top else 0)) mask = Image.new("RGB", (process_res_w, process_res_h), "white") @@ -193,17 +163,14 @@ def expand(init, count, expand_pixels, is_left=False, is_right=False, is_top=Fal mask.width - expand_pixels - mask_blur if is_right else res_w, mask.height - expand_pixels - mask_blur if is_bottom else res_h, ), fill="black") - np_image = (np.asarray(img) / 255.0).astype(np.float64) np_mask = (np.asarray(mask) / 255.0).astype(np.float64) noised = get_matched_noise(np_image, np_mask, noise_q, color_variation) - output_images.append(Image.fromarray(np.clip(noised * 255., 0., 255.).astype(np.uint8), mode="RGB")) - + output_images.append(Image.fromarray(np.clip(255.0 * noised, 0.0, 255.0).astype(np.uint8), mode="RGB")) target_width = min(process_width, init[n].width + pixels_horiz) if is_horiz else img.width target_height = min(process_height, init[n].height + pixels_vert) if is_vert else img.height p.width = target_width if is_horiz else img.width p.height = target_height if is_vert else img.height - crop_region = ( 0 if is_left else output_images[n].width - target_width, 0 if is_top else output_images[n].height - target_height, @@ -212,12 +179,9 @@ def expand(init, count, expand_pixels, is_left=False, is_right=False, is_top=Fal ) mask = mask.crop(crop_region) p.image_mask = mask - image_to_process = output_images[n].crop(crop_region) images_to_process.append(image_to_process) - p.init_images = images_to_process - latent_mask = Image.new("RGB", (p.width, p.height), "white") draw = ImageDraw.Draw(latent_mask) draw.rectangle(( @@ -227,29 +191,22 @@ def expand(init, count, expand_pixels, is_left=False, is_right=False, is_top=Fal mask.height - expand_pixels - mask_blur * 2 if is_bottom else res_h, ), fill="black") p.latent_mask = latent_mask - proc = process_images(p) - if initial_seed_and_info[0] is None: initial_seed_and_info[0] = proc.seed initial_seed_and_info[1] = proc.info - for n in range(count): output_images[n].paste(proc.images[n], (0 if is_left else output_images[n].width - proc.images[n].width, 0 if is_top else output_images[n].height - proc.images[n].height)) output_images[n] = output_images[n].crop((0, 0, res_w, res_h)) - return output_images - batch_count = p.n_iter batch_size = p.batch_size p.n_iter = 1 state.job_count = batch_count * ((1 if left > 0 else 0) + (1 if right > 0 else 0) + (1 if up > 0 else 0) + (1 if down > 0 else 0)) all_processed_images = [] - for i in range(batch_count): imgs = [init_img] * batch_size - state.job = f"Batch {i + 1} out of {batch_count}" - + state.job = f"outpainting batch {i+1}/{batch_count}" if left > 0: imgs = expand(imgs, batch_size, left, is_left=True) if right > 0: @@ -258,22 +215,15 @@ def expand(init, count, expand_pixels, is_left=False, is_right=False, is_top=Fal imgs = expand(imgs, batch_size, up, is_top=True) if down > 0: imgs = expand(imgs, batch_size, down, is_bottom=True) - all_processed_images += imgs - all_images = all_processed_images - combined_grid_image = images.image_grid(all_processed_images) if opts.return_grid and len(all_processed_images) > 1: all_images = [combined_grid_image] + all_processed_images - res = Processed(p, all_images, initial_seed_and_info[0], initial_seed_and_info[1]) - if opts.samples_save: for img in all_processed_images: images.save_image(img, p.outpath_samples, "", res.seed, p.prompt, opts.samples_format, info=res.info, p=p) - if opts.grid_save and len(all_processed_images) > 1: images.save_image(combined_grid_image, p.outpath_grids, "grid", res.seed, p.prompt, opts.samples_format, info=res.info, grid=True, p=p) - return res diff --git a/scripts/poor_mans_outpainting.py b/scripts/poor_mans_outpainting.py index f52b2b615..3f6cbf335 100644 --- a/scripts/poor_mans_outpainting.py +++ b/scripts/poor_mans_outpainting.py @@ -22,40 +22,31 @@ def ui(self, is_img2img): mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4, elem_id=self.elem_id("mask_blur")) inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='fill', type="index", elem_id=self.elem_id("inpainting_fill")) direction = gr.CheckboxGroup(label="Outpainting direction", choices=['left', 'right', 'up', 'down'], value=['left', 'right', 'up', 'down'], elem_id=self.elem_id("direction")) - return [pixels, mask_blur, inpainting_fill, direction] def run(self, p, pixels, mask_blur, inpainting_fill, direction): initial_seed = None initial_info = None - p.mask_blur = mask_blur * 2 p.inpainting_fill = inpainting_fill p.inpaint_full_res = False - left = pixels if "left" in direction else 0 right = pixels if "right" in direction else 0 up = pixels if "up" in direction else 0 down = pixels if "down" in direction else 0 - init_img = p.init_images[0] target_w = math.ceil((init_img.width + left + right) / 64) * 64 target_h = math.ceil((init_img.height + up + down) / 64) * 64 - if left > 0: left = left * (target_w - init_img.width) // (left + right) if right > 0: right = target_w - init_img.width - left - if up > 0: up = up * (target_h - init_img.height) // (up + down) - if down > 0: down = target_h - init_img.height - up - img = Image.new("RGB", (target_w, target_h)) img.paste(init_img, (left, up)) - mask = Image.new("L", (img.width, img.height), "white") draw = ImageDraw.Draw(mask) draw.rectangle(( @@ -64,7 +55,6 @@ def run(self, p, pixels, mask_blur, inpainting_fill, direction): mask.width - right - (mask_blur * 2 if right > 0 else 0), mask.height - down - (mask_blur * 2 if down > 0 else 0) ), fill="black") - latent_mask = Image.new("L", (img.width, img.height), "white") latent_draw = ImageDraw.Draw(latent_mask) latent_draw.rectangle(( @@ -73,71 +63,50 @@ def run(self, p, pixels, mask_blur, inpainting_fill, direction): mask.width - right - (mask_blur//2 if right > 0 else 0), mask.height - down - (mask_blur//2 if down > 0 else 0) ), fill="black") - devices.torch_gc() - grid = images.split_grid(img, tile_w=p.width, tile_h=p.height, overlap=pixels) grid_mask = images.split_grid(mask, tile_w=p.width, tile_h=p.height, overlap=pixels) grid_latent_mask = images.split_grid(latent_mask, tile_w=p.width, tile_h=p.height, overlap=pixels) - p.n_iter = 1 p.batch_size = 1 p.do_not_save_grid = True p.do_not_save_samples = True - work = [] work_mask = [] work_latent_mask = [] work_results = [] - for (y, h, row), (_, _, row_mask), (_, _, row_latent_mask) in zip(grid.tiles, grid_mask.tiles, grid_latent_mask.tiles): for tiledata, tiledata_mask, tiledata_latent_mask in zip(row, row_mask, row_latent_mask): x, w = tiledata[0:2] - if x >= left and x+w <= img.width - right and y >= up and y+h <= img.height - down: continue - work.append(tiledata[2]) work_mask.append(tiledata_mask[2]) work_latent_mask.append(tiledata_latent_mask[2]) - batch_count = len(work) log.info(f"Poor-man-outpainting: images={len(work)} tiles={len(grid.tiles[0][2])}x{len(grid.tiles)}.") - state.job_count = batch_count - for i in range(batch_count): p.init_images = [work[i]] p.image_mask = work_mask[i] p.latent_mask = work_latent_mask[i] - - state.job = f"Batch {i + 1} out of {batch_count}" + state.job = f"outpainting batch {i+1}/{batch_count}" processed = process_images(p) - if initial_seed is None: initial_seed = processed.seed initial_info = processed.info - p.seed = processed.seed + 1 work_results += processed.images - - image_index = 0 for y, h, row in grid.tiles: for tiledata in row: x, w = tiledata[0:2] - if x >= left and x+w <= img.width - right and y >= up and y+h <= img.height - down: continue - tiledata[2] = work_results[image_index] if image_index < len(work_results) else Image.new("RGB", (p.width, p.height)) image_index += 1 - combined_image = images.combine_grid(grid) - if opts.samples_save: images.save_image(combined_image, p.outpath_samples, "", initial_seed, p.prompt, opts.samples_format, info=initial_info, p=p) - processed = Processed(p, [combined_image], initial_seed, initial_info) - return processed diff --git a/scripts/postprocessing_codeformer.py b/scripts/postprocessing_codeformer.py index 54647f27f..ef8200276 100644 --- a/scripts/postprocessing_codeformer.py +++ b/scripts/postprocessing_codeformer.py @@ -14,22 +14,15 @@ def ui(self): with FormRow(): codeformer_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label="Strength", value=0.0, elem_id="extras_codeformer_visibility") codeformer_weight = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label="Weight", value=0.2, elem_id="extras_codeformer_weight") - - return { - "codeformer_visibility": codeformer_visibility, - "codeformer_weight": codeformer_weight, - } + return { "codeformer_visibility": codeformer_visibility, "codeformer_weight": codeformer_weight } def process(self, pp: scripts_postprocessing.PostprocessedImage, codeformer_visibility, codeformer_weight): # pylint: disable=arguments-differ if codeformer_visibility == 0: return - restored_img = codeformer_model.codeformer.restore(np.array(pp.image, dtype=np.uint8), w=codeformer_weight) res = Image.fromarray(restored_img) - if codeformer_visibility < 1.0: res = Image.blend(pp.image, res, codeformer_visibility) - pp.image = res pp.info["CodeFormer visibility"] = round(codeformer_visibility, 3) pp.info["CodeFormer weight"] = round(codeformer_weight, 3) diff --git a/scripts/postprocessing_gfpgan.py b/scripts/postprocessing_gfpgan.py index fcd61fc6e..f78b186d3 100644 --- a/scripts/postprocessing_gfpgan.py +++ b/scripts/postprocessing_gfpgan.py @@ -13,20 +13,14 @@ class ScriptPostprocessingGfpGan(scripts_postprocessing.ScriptPostprocessing): def ui(self): with FormRow(): gfpgan_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Strength", value=0, elem_id="extras_gfpgan_visibility") + return { "gfpgan_visibility": gfpgan_visibility } - return { - "gfpgan_visibility": gfpgan_visibility, - } - - def process(self, pp: scripts_postprocessing.PostprocessedImage, gfpgan_visibility): + def process(self, pp: scripts_postprocessing.PostprocessedImage, gfpgan_visibility): # pylint: disable=arguments-differ if gfpgan_visibility == 0: return - restored_img = gfpgan_model.gfpgan_fix_faces(np.array(pp.image, dtype=np.uint8)) res = Image.fromarray(restored_img) - if gfpgan_visibility < 1.0: res = Image.blend(pp.image, res, gfpgan_visibility) - pp.image = res pp.info["GFPGAN visibility"] = round(gfpgan_visibility, 3) diff --git a/scripts/prompt_matrix.py b/scripts/prompt_matrix.py index a5f388feb..00d6edc7a 100644 --- a/scripts/prompt_matrix.py +++ b/scripts/prompt_matrix.py @@ -45,17 +45,17 @@ def ui(self, is_img2img): gr.HTML('
') with gr.Row(): with gr.Column(): - put_at_start = gr.Checkbox(label='Put variable parts at start of prompt', value=False, elem_id=self.elem_id("put_at_start")) - different_seeds = gr.Checkbox(label='Use different seed for each picture', value=False, elem_id=self.elem_id("different_seeds")) + put_at_start = gr.Checkbox(label='Set at prompt start', value=False, elem_id=self.elem_id("put_at_start")) + different_seeds = gr.Checkbox(label='Random seeds', value=False, elem_id=self.elem_id("different_seeds")) with gr.Column(): - prompt_type = gr.Radio(["positive", "negative"], label="Select prompt", elem_id=self.elem_id("prompt_type"), value="positive") - variations_delimiter = gr.Radio(["comma", "space"], label="Select joining char", elem_id=self.elem_id("variations_delimiter"), value="comma") + prompt_type = gr.Radio(["positive", "negative"], label="Prompt type", elem_id=self.elem_id("prompt_type"), value="positive") + variations_delimiter = gr.Radio(["comma", "space"], label="Joining char", elem_id=self.elem_id("variations_delimiter"), value="comma") with gr.Column(): margin_size = gr.Slider(label="Grid margins", minimum=0, maximum=500, value=0, step=2, elem_id=self.elem_id("margin_size")) return [put_at_start, different_seeds, prompt_type, variations_delimiter, margin_size] - def run(self, p, put_at_start, different_seeds, prompt_type, variations_delimiter, margin_size): + def run(self, p, put_at_start, different_seeds, prompt_type, variations_delimiter, margin_size): # pylint: disable=arguments-differ modules.processing.fix_seed(p) # Raise error if promp type is not positive or negative if prompt_type not in ["positive", "negative"]: diff --git a/scripts/sd_upscale.py b/scripts/sd_upscale.py index 8500ead07..0d35e262b 100644 --- a/scripts/sd_upscale.py +++ b/scripts/sd_upscale.py @@ -72,8 +72,7 @@ def run(self, p, _, overlap, upscaler_index, scale_factor): for i in range(batch_count): p.batch_size = batch_size p.init_images = work[i * batch_size:(i + 1) * batch_size] - - state.job = f"Batch {i + 1 + n * batch_count} out of {state.job_count}" + state.job = f"upscale batch {i+1+n*batch_count}/{state.job_count}" processed = processing.process_images(p) if initial_info is None: diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py index 3dbe72baf..99730b686 100644 --- a/scripts/xyz_grid.py +++ b/scripts/xyz_grid.py @@ -268,7 +268,7 @@ def process_cell(x, y, z, ix, iy, iz): def index(ix, iy, iz): return ix + iy * len(xs) + iz * len(xs) * len(ys) - shared.state.job = f"{index(ix, iy, iz) + 1} out of {list_size}" + shared.state.job = 'grid' processed: Processed = cell(x, y, z, ix, iy, iz) if processed_result is None: processed_result = copy(processed) diff --git a/webui.py b/webui.py index daab8d82b..b2477c1bc 100644 --- a/webui.py +++ b/webui.py @@ -157,7 +157,7 @@ def sigint_handler(_sig, _frame): def load_model(): if opts.sd_checkpoint_autoload: - shared.state.begin('load model') + shared.state.begin('load') thread_model = Thread(target=lambda: shared.sd_model) thread_model.start() thread_refiner = Thread(target=lambda: shared.sd_refiner)