From 67e7ee3c7397bcfd03e123398e5497f31be1bf92 Mon Sep 17 00:00:00 2001 From: Zhizhou Zhong Date: Sat, 12 Apr 2025 23:22:22 +0800 Subject: [PATCH] feat: windows infer & gradio (#312) * fix: windows infer * docs: update readme * docs: update readme * feat: v1.5 gradio for windows&linux * fix: dependencies * feat: windows infer & gradio --------- Co-authored-by: NeRF-Factory --- .gitignore | 7 +- README.md | 169 ++++++++--- app.py | 476 +++++++++++++++++++----------- assets/figs/gradio.png | Bin 0 -> 13911 bytes assets/figs/gradio_2.png | Bin 0 -> 75170 bytes download_weights.bat | 45 +++ download_weights.sh | 37 +++ musetalk/utils/audio_processor.py | 5 +- musetalk/utils/utils.py | 16 +- requirements.txt | 14 +- scripts/inference.py | 18 +- scripts/preprocess.py | 18 +- scripts/realtime_inference.py | 20 +- test_ffmpeg.py | 33 +++ 14 files changed, 613 insertions(+), 245 deletions(-) create mode 100644 assets/figs/gradio.png create mode 100644 assets/figs/gradio_2.png create mode 100644 download_weights.bat create mode 100644 download_weights.sh create mode 100644 test_ffmpeg.py diff --git a/.gitignore b/.gitignore index aa31084..c83750f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,11 +5,14 @@ *.pyc .ipynb_checkpoints results/ -./models +models/ **/__pycache__/ *.py[cod] *$py.class dataset/ ffmpeg* +ffmprobe* +ffplay* debug -exp_out \ No newline at end of file +exp_out +.gradio \ No newline at end of file diff --git a/README.md b/README.md index 6bf3549..49e76c4 100644 --- a/README.md +++ b/README.md @@ -146,50 +146,87 @@ We also hope you note that we have not verified, maintained, or updated third-pa ## Installation To prepare the Python environment and install additional packages such as opencv, diffusers, mmcv, etc., please follow the steps below: -### Build environment -We recommend a python version >=3.10 and cuda version =11.7. Then build environment as follows: +### Build environment +We recommend Python 3.10 and CUDA 11.7. Set up your environment as follows: + +```shell +conda create -n MuseTalk python==3.10 +conda activate MuseTalk +``` + +### Install PyTorch 2.0.1 +Choose one of the following installation methods: + +```shell +# Option 1: Using pip +pip install torch==2.0.1 torchvision==0.15.2 torchaudio==2.0.2 --index-url https://download.pytorch.org/whl/cu118 + +# Option 2: Using conda +conda install pytorch==2.0.1 torchvision==0.15.2 torchaudio==2.0.2 pytorch-cuda=11.8 -c pytorch -c nvidia +``` + +### Install Dependencies +Install the remaining required packages: ```shell pip install -r requirements.txt ``` -### mmlab packages +### Install MMLab Packages +Install the MMLab ecosystem packages: + ```bash -pip install --no-cache-dir -U openmim -mim install mmengine -mim install "mmcv>=2.0.1" -mim install "mmdet>=3.1.0" -mim install "mmpose>=1.1.0" +pip install --no-cache-dir -U openmim +mim install mmengine +mim install "mmcv==2.0.1" +mim install "mmdet==3.1.0" +mim install "mmpose==1.1.0" ``` -### Download ffmpeg-static -Download the ffmpeg-static and -``` +### Setup FFmpeg +1. [Download](https://github.com/BtbN/FFmpeg-Builds/releases) the ffmpeg-static package + +2. Configure FFmpeg based on your operating system: + +For Linux: +```bash export FFMPEG_PATH=/path/to/ffmpeg -``` -for example: -``` +# Example: export FFMPEG_PATH=/musetalk/ffmpeg-4.4-amd64-static ``` -### Download weights -You can download weights manually as follows: -1. Download our trained [weights](https://huggingface.co/TMElyralab/MuseTalk). +For Windows: +Add the `ffmpeg-xxx\bin` directory to your system's PATH environment variable. Verify the installation by running `ffmpeg -version` in the command prompt - it should display the ffmpeg version information. + +### Download weights +You can download weights in two ways: + +#### Option 1: Using Download Scripts +We provide two scripts for automatic downloading: + +For Linux: ```bash -# !pip install -U "huggingface_hub[cli]" -export HF_ENDPOINT=https://hf-mirror.com -huggingface-cli download TMElyralab/MuseTalk --local-dir models/ +sh ./download_weights.sh ``` +For Windows: +```batch +# Run the script +download_weights.bat +``` + +#### Option 2: Manual Download +You can also download the weights manually from the following links: + +1. Download our trained [weights](https://huggingface.co/TMElyralab/MuseTalk/tree/main) 2. Download the weights of other components: - - [sd-vae-ft-mse](https://huggingface.co/stabilityai/sd-vae-ft-mse) + - [sd-vae-ft-mse](https://huggingface.co/stabilityai/sd-vae-ft-mse/tree/main) - [whisper](https://huggingface.co/openai/whisper-tiny/tree/main) - [dwpose](https://huggingface.co/yzd-v/DWPose/tree/main) - - [face-parse-bisent](https://github.com/zllrunning/face-parsing.PyTorch) - - [resnet18](https://download.pytorch.org/models/resnet18-5c106cde.pth) - [syncnet](https://huggingface.co/ByteDance/LatentSync/tree/main) - + - [face-parse-bisent](https://drive.google.com/file/d/154JgKpzCPW82qINcVieuPH3fZ2e0P812/view?pli=1) + - [resnet18](https://download.pytorch.org/models/resnet18-5c106cde.pth) Finally, these weights should be organized in `models` as follows: ``` @@ -207,7 +244,7 @@ Finally, these weights should be organized in `models` as follows: ├── face-parse-bisent │ ├── 79999_iter.pth │ └── resnet18-5c106cde.pth -├── sd-vae-ft-mse +├── sd-vae │ ├── config.json │ └── diffusion_pytorch_model.bin └── whisper @@ -221,21 +258,60 @@ Finally, these weights should be organized in `models` as follows: ### Inference We provide inference scripts for both versions of MuseTalk: -#### MuseTalk 1.5 (Recommended) +#### Prerequisites +Before running inference, please ensure ffmpeg is installed and accessible: ```bash -# Run MuseTalk 1.5 inference -sh inference.sh v1.5 normal +# Check ffmpeg installation +ffmpeg -version ``` +If ffmpeg is not found, please install it first: +- Windows: Download from [ffmpeg-static](https://github.com/BtbN/FFmpeg-Builds/releases) and add to PATH +- Linux: `sudo apt-get install ffmpeg` -#### MuseTalk 1.0 +#### Normal Inference +##### Linux Environment ```bash -# Run MuseTalk 1.0 inference +# MuseTalk 1.5 (Recommended) +sh inference.sh v1.5 normal + +# MuseTalk 1.0 sh inference.sh v1.0 normal ``` -The inference script supports both MuseTalk 1.5 and 1.0 models: -- For MuseTalk 1.5: Use the command above with the V1.5 model path -- For MuseTalk 1.0: Use the same script but point to the V1.0 model path +##### Windows Environment + +Please ensure that you set the `ffmpeg_path` to match the actual location of your FFmpeg installation. + +```bash +# MuseTalk 1.5 (Recommended) +python -m scripts.inference --inference_config configs\inference\test.yaml --result_dir results\test --unet_model_path models\musetalkV15\unet.pth --unet_config models\musetalkV15\musetalk.json --version v15 --ffmpeg_path ffmpeg-master-latest-win64-gpl-shared\bin + +# For MuseTalk 1.0, change: +# - models\musetalkV15 -> models\musetalk +# - unet.pth -> pytorch_model.bin +# - --version v15 -> --version v1 +``` + +#### Real-time Inference +##### Linux Environment +```bash +# MuseTalk 1.5 (Recommended) +sh inference.sh v1.5 realtime + +# MuseTalk 1.0 +sh inference.sh v1.0 realtime +``` + +##### Windows Environment +```bash +# MuseTalk 1.5 (Recommended) +python -m scripts.realtime_inference --inference_config configs\inference\realtime.yaml --result_dir results\realtime --unet_model_path models\musetalkV15\unet.pth --unet_config models\musetalkV15\musetalk.json --version v15 --fps 25 --ffmpeg_path ffmpeg-master-latest-win64-gpl-shared\bin + +# For MuseTalk 1.0, change: +# - models\musetalkV15 -> models\musetalk +# - unet.pth -> pytorch_model.bin +# - --version v15 -> --version v1 +``` The configuration file `configs/inference/test.yaml` contains the inference settings, including: - `video_path`: Path to the input video, image file, or directory of images @@ -243,21 +319,6 @@ The configuration file `configs/inference/test.yaml` contains the inference sett Note: For optimal results, we recommend using input videos with 25fps, which is the same fps used during model training. If your video has a lower frame rate, you can use frame interpolation or convert it to 25fps using ffmpeg. -#### Real-time Inference -For real-time inference, use the following command: -```bash -# Run real-time inference -sh inference.sh v1.5 realtime # For MuseTalk 1.5 -# or -sh inference.sh v1.0 realtime # For MuseTalk 1.0 -``` - -The real-time inference configuration is in `configs/inference/realtime.yaml`, which includes: -- `preparation`: Set to `True` for new avatar preparation -- `video_path`: Path to the input video -- `bbox_shift`: Adjustable parameter for mouth region control -- `audio_clips`: List of audio clips for generation - Important notes for real-time inference: 1. Set `preparation` to `True` when processing a new avatar 2. After preparation, the avatar will generate videos using audio clips from `audio_clips` @@ -269,6 +330,18 @@ For faster generation without saving images, you can use: python -m scripts.realtime_inference --inference_config configs/inference/realtime.yaml --skip_save_images ``` +## Gradio Demo +We provide an intuitive web interface through Gradio for users to easily adjust input parameters. To optimize inference time, users can generate only the **first frame** to fine-tune the best lip-sync parameters, which helps reduce facial artifacts in the final output. +![para](assets/figs/gradio_2.png) +For minimum hardware requirements, we tested the system on a Windows environment using an NVIDIA GeForce RTX 3050 Ti Laptop GPU with 4GB VRAM. In fp16 mode, generating an 8-second video takes approximately 5 minutes. ![speed](assets/figs/gradio.png) + +Both Linux and Windows users can launch the demo using the following command. Please ensure that the `ffmpeg_path` parameter matches your actual FFmpeg installation path: + +```bash +# You can remove --use_float16 for better quality, but it will increase VRAM usage and inference time +python app.py --use_float16 --ffmpeg_path ffmpeg-master-latest-win64-gpl-shared\bin +``` + ## Training ### Data Preparation diff --git a/app.py b/app.py index feec239..448e641 100644 --- a/app.py +++ b/app.py @@ -4,7 +4,6 @@ import pdb import re import gradio as gr -import spaces import numpy as np import sys import subprocess @@ -28,11 +27,101 @@ import gdown import imageio import ffmpeg from moviepy.editor import * - +from transformers import WhisperModel ProjectDir = os.path.abspath(os.path.dirname(__file__)) CheckpointsDir = os.path.join(ProjectDir, "models") +@torch.no_grad() +def debug_inpainting(video_path, bbox_shift, extra_margin=10, parsing_mode="jaw", + left_cheek_width=90, right_cheek_width=90): + """Debug inpainting parameters, only process the first frame""" + # Set default parameters + args_dict = { + "result_dir": './results/debug', + "fps": 25, + "batch_size": 1, + "output_vid_name": '', + "use_saved_coord": False, + "audio_padding_length_left": 2, + "audio_padding_length_right": 2, + "version": "v15", + "extra_margin": extra_margin, + "parsing_mode": parsing_mode, + "left_cheek_width": left_cheek_width, + "right_cheek_width": right_cheek_width + } + args = Namespace(**args_dict) + + # Create debug directory + os.makedirs(args.result_dir, exist_ok=True) + + # Read first frame + if get_file_type(video_path) == "video": + reader = imageio.get_reader(video_path) + first_frame = reader.get_data(0) + reader.close() + else: + first_frame = cv2.imread(video_path) + first_frame = cv2.cvtColor(first_frame, cv2.COLOR_BGR2RGB) + + # Save first frame + debug_frame_path = os.path.join(args.result_dir, "debug_frame.png") + cv2.imwrite(debug_frame_path, cv2.cvtColor(first_frame, cv2.COLOR_RGB2BGR)) + + # Get face coordinates + coord_list, frame_list = get_landmark_and_bbox([debug_frame_path], bbox_shift) + bbox = coord_list[0] + frame = frame_list[0] + + if bbox == coord_placeholder: + return None, "No face detected, please adjust bbox_shift parameter" + + # Initialize face parser + fp = FaceParsing( + left_cheek_width=args.left_cheek_width, + right_cheek_width=args.right_cheek_width + ) + + # Process first frame + x1, y1, x2, y2 = bbox + y2 = y2 + args.extra_margin + y2 = min(y2, frame.shape[0]) + crop_frame = frame[y1:y2, x1:x2] + crop_frame = cv2.resize(crop_frame,(256,256),interpolation = cv2.INTER_LANCZOS4) + + # Generate random audio features + random_audio = torch.randn(1, 50, 384, device=device, dtype=weight_dtype) + audio_feature = pe(random_audio) + + # Get latents + latents = vae.get_latents_for_unet(crop_frame) + latents = latents.to(dtype=weight_dtype) + + # Generate prediction results + pred_latents = unet.model(latents, timesteps, encoder_hidden_states=audio_feature).sample + recon = vae.decode_latents(pred_latents) + + # Inpaint back to original image + res_frame = recon[0] + res_frame = cv2.resize(res_frame.astype(np.uint8),(x2-x1,y2-y1)) + combine_frame = get_image(frame, res_frame, [x1, y1, x2, y2], mode=args.parsing_mode, fp=fp) + + # Save results (no need to convert color space again since get_image already returns RGB format) + debug_result_path = os.path.join(args.result_dir, "debug_result.png") + cv2.imwrite(debug_result_path, combine_frame) + + # Create information text + info_text = f"Parameter information:\n" + \ + f"bbox_shift: {bbox_shift}\n" + \ + f"extra_margin: {extra_margin}\n" + \ + f"parsing_mode: {parsing_mode}\n" + \ + f"left_cheek_width: {left_cheek_width}\n" + \ + f"right_cheek_width: {right_cheek_width}\n" + \ + f"Detected face coordinates: [{x1}, {y1}, {x2}, {y2}]" + + return cv2.cvtColor(combine_frame, cv2.COLOR_RGB2BGR), info_text + def print_directory_contents(path): for child in os.listdir(path): child_path = os.path.join(path, child) @@ -40,119 +129,107 @@ def print_directory_contents(path): print(child_path) def download_model(): - if not os.path.exists(CheckpointsDir): - os.makedirs(CheckpointsDir) - print("Checkpoint Not Downloaded, start downloading...") - tic = time.time() - snapshot_download( - repo_id="TMElyralab/MuseTalk", - local_dir=CheckpointsDir, - max_workers=8, - local_dir_use_symlinks=True, - force_download=True, resume_download=False - ) - # weight - os.makedirs(f"{CheckpointsDir}/sd-vae-ft-mse/") - snapshot_download( - repo_id="stabilityai/sd-vae-ft-mse", - local_dir=CheckpointsDir+'/sd-vae-ft-mse', - max_workers=8, - local_dir_use_symlinks=True, - force_download=True, resume_download=False - ) - #dwpose - os.makedirs(f"{CheckpointsDir}/dwpose/") - snapshot_download( - repo_id="yzd-v/DWPose", - local_dir=CheckpointsDir+'/dwpose', - max_workers=8, - local_dir_use_symlinks=True, - force_download=True, resume_download=False - ) - #vae - url = "https://openaipublic.azureedge.net/main/whisper/models/65147644a518d12f04e32d6f3b26facc3f8dd46e5390956a9424a650c0ce22b9/tiny.pt" - response = requests.get(url) - # 确保请求成功 - if response.status_code == 200: - # 指定文件保存的位置 - file_path = f"{CheckpointsDir}/whisper/tiny.pt" - os.makedirs(f"{CheckpointsDir}/whisper/") - # 将文件内容写入指定位置 - with open(file_path, "wb") as f: - f.write(response.content) + # 检查必需的模型文件是否存在 + required_models = { + "MuseTalk": f"{CheckpointsDir}/musetalkV15/unet.pth", + "MuseTalk": f"{CheckpointsDir}/musetalkV15/musetalk.json", + "SD VAE": f"{CheckpointsDir}/sd-vae/config.json", + "Whisper": f"{CheckpointsDir}/whisper/config.json", + "DWPose": f"{CheckpointsDir}/dwpose/dw-ll_ucoco_384.pth", + "SyncNet": f"{CheckpointsDir}/syncnet/latentsync_syncnet.pt", + "Face Parse": f"{CheckpointsDir}/face-parse-bisent/79999_iter.pth", + "ResNet": f"{CheckpointsDir}/face-parse-bisent/resnet18-5c106cde.pth" + } + + missing_models = [] + for model_name, model_path in required_models.items(): + if not os.path.exists(model_path): + missing_models.append(model_name) + + if missing_models: + # 全用英文 + print("The following required model files are missing:") + for model in missing_models: + print(f"- {model}") + print("\nPlease run the download script to download the missing models:") + if sys.platform == "win32": + print("Windows: Run download_weights.bat") else: - print(f"请求失败,状态码:{response.status_code}") - #gdown face parse - url = "https://drive.google.com/uc?id=154JgKpzCPW82qINcVieuPH3fZ2e0P812" - os.makedirs(f"{CheckpointsDir}/face-parse-bisent/") - file_path = f"{CheckpointsDir}/face-parse-bisent/79999_iter.pth" - gdown.download(url, file_path, quiet=False) - #resnet - url = "https://download.pytorch.org/models/resnet18-5c106cde.pth" - response = requests.get(url) - # 确保请求成功 - if response.status_code == 200: - # 指定文件保存的位置 - file_path = f"{CheckpointsDir}/face-parse-bisent/resnet18-5c106cde.pth" - # 将文件内容写入指定位置 - with open(file_path, "wb") as f: - f.write(response.content) - else: - print(f"请求失败,状态码:{response.status_code}") - - - toc = time.time() - - print(f"download cost {toc-tic} seconds") - print_directory_contents(CheckpointsDir) - + print("Linux/Mac: Run ./download_weights.sh") + sys.exit(1) else: - print("Already download the model.") - + print("All required model files exist.") download_model() # for huggingface deployment. - -from musetalk.utils.utils import get_file_type,get_video_fps,datagen -from musetalk.utils.preprocessing import get_landmark_and_bbox,read_imgs,coord_placeholder,get_bbox_range from musetalk.utils.blending import get_image -from musetalk.utils.utils import load_all_model +from musetalk.utils.face_parsing import FaceParsing +from musetalk.utils.audio_processor import AudioProcessor +from musetalk.utils.utils import get_file_type, get_video_fps, datagen, load_all_model +from musetalk.utils.preprocessing import get_landmark_and_bbox, read_imgs, coord_placeholder, get_bbox_range +def fast_check_ffmpeg(): + try: + subprocess.run(["ffmpeg", "-version"], capture_output=True, check=True) + return True + except: + return False - - -@spaces.GPU(duration=600) @torch.no_grad() -def inference(audio_path,video_path,bbox_shift,progress=gr.Progress(track_tqdm=True)): - args_dict={"result_dir":'./results/output', "fps":25, "batch_size":8, "output_vid_name":'', "use_saved_coord":False}#same with inferenece script +def inference(audio_path, video_path, bbox_shift, extra_margin=10, parsing_mode="jaw", + left_cheek_width=90, right_cheek_width=90, progress=gr.Progress(track_tqdm=True)): + # Set default parameters, aligned with inference.py + args_dict = { + "result_dir": './results/output', + "fps": 25, + "batch_size": 8, + "output_vid_name": '', + "use_saved_coord": False, + "audio_padding_length_left": 2, + "audio_padding_length_right": 2, + "version": "v15", # Fixed use v15 version + "extra_margin": extra_margin, + "parsing_mode": parsing_mode, + "left_cheek_width": left_cheek_width, + "right_cheek_width": right_cheek_width + } args = Namespace(**args_dict) - input_basename = os.path.basename(video_path).split('.')[0] - audio_basename = os.path.basename(audio_path).split('.')[0] - output_basename = f"{input_basename}_{audio_basename}" - result_img_save_path = os.path.join(args.result_dir, output_basename) # related to video & audio inputs - crop_coord_save_path = os.path.join(result_img_save_path, input_basename+".pkl") # only related to video input - os.makedirs(result_img_save_path,exist_ok =True) + # Check ffmpeg + if not fast_check_ffmpeg(): + print("Warning: Unable to find ffmpeg, please ensure ffmpeg is properly installed") - if args.output_vid_name=="": - output_vid_name = os.path.join(args.result_dir, output_basename+".mp4") + input_basename = os.path.basename(video_path).split('.')[0] + audio_basename = os.path.basename(audio_path).split('.')[0] + output_basename = f"{input_basename}_{audio_basename}" + + # Create temporary directory + temp_dir = os.path.join(args.result_dir, f"{args.version}") + os.makedirs(temp_dir, exist_ok=True) + + # Set result save path + result_img_save_path = os.path.join(temp_dir, output_basename) + crop_coord_save_path = os.path.join(args.result_dir, "../", input_basename+".pkl") + os.makedirs(result_img_save_path, exist_ok=True) + + if args.output_vid_name == "": + output_vid_name = os.path.join(temp_dir, output_basename+".mp4") else: - output_vid_name = os.path.join(args.result_dir, args.output_vid_name) + output_vid_name = os.path.join(temp_dir, args.output_vid_name) + ############################################## extract frames from source video ############################################## - if get_file_type(video_path)=="video": - save_dir_full = os.path.join(args.result_dir, input_basename) - os.makedirs(save_dir_full,exist_ok = True) - # cmd = f"ffmpeg -v fatal -i {video_path} -start_number 0 {save_dir_full}/%08d.png" - # os.system(cmd) - # 读取视频 + if get_file_type(video_path) == "video": + save_dir_full = os.path.join(temp_dir, input_basename) + os.makedirs(save_dir_full, exist_ok=True) + # Read video reader = imageio.get_reader(video_path) - # 保存图片 + # Save images for i, im in enumerate(reader): imageio.imwrite(f"{save_dir_full}/{i:08d}.png", im) input_img_list = sorted(glob.glob(os.path.join(save_dir_full, '*.[jpJP][pnPN]*[gG]'))) @@ -161,10 +238,21 @@ def inference(audio_path,video_path,bbox_shift,progress=gr.Progress(track_tqdm=T input_img_list = glob.glob(os.path.join(video_path, '*.[jpJP][pnPN]*[gG]')) input_img_list = sorted(input_img_list, key=lambda x: int(os.path.splitext(os.path.basename(x))[0])) fps = args.fps - #print(input_img_list) + ############################################## extract audio feature ############################################## - whisper_feature = audio_processor.audio2feat(audio_path) - whisper_chunks = audio_processor.feature2chunks(feature_array=whisper_feature,fps=fps) + # Extract audio features + whisper_input_features, librosa_length = audio_processor.get_audio_feature(audio_path) + whisper_chunks = audio_processor.get_whisper_chunk( + whisper_input_features, + device, + weight_dtype, + whisper, + librosa_length, + fps=fps, + audio_padding_length_left=args.audio_padding_length_left, + audio_padding_length_right=args.audio_padding_length_right, + ) + ############################################## preprocess input image ############################################## if os.path.exists(crop_coord_save_path) and args.use_saved_coord: print("using extracted coordinates") @@ -176,13 +264,22 @@ def inference(audio_path,video_path,bbox_shift,progress=gr.Progress(track_tqdm=T coord_list, frame_list = get_landmark_and_bbox(input_img_list, bbox_shift) with open(crop_coord_save_path, 'wb') as f: pickle.dump(coord_list, f) - bbox_shift_text=get_bbox_range(input_img_list, bbox_shift) + bbox_shift_text = get_bbox_range(input_img_list, bbox_shift) + + # Initialize face parser + fp = FaceParsing( + left_cheek_width=args.left_cheek_width, + right_cheek_width=args.right_cheek_width + ) + i = 0 input_latent_list = [] for bbox, frame in zip(coord_list, frame_list): if bbox == coord_placeholder: continue x1, y1, x2, y2 = bbox + y2 = y2 + args.extra_margin + y2 = min(y2, frame.shape[0]) crop_frame = frame[y1:y2, x1:x2] crop_frame = cv2.resize(crop_frame,(256,256),interpolation = cv2.INTER_LANCZOS4) latents = vae.get_latents_for_unet(crop_frame) @@ -192,17 +289,23 @@ def inference(audio_path,video_path,bbox_shift,progress=gr.Progress(track_tqdm=T frame_list_cycle = frame_list + frame_list[::-1] coord_list_cycle = coord_list + coord_list[::-1] input_latent_list_cycle = input_latent_list + input_latent_list[::-1] + ############################################## inference batch by batch ############################################## print("start inference") video_num = len(whisper_chunks) batch_size = args.batch_size - gen = datagen(whisper_chunks,input_latent_list_cycle,batch_size) + gen = datagen( + whisper_chunks=whisper_chunks, + vae_encode_latents=input_latent_list_cycle, + batch_size=batch_size, + delay_frame=0, + device=device, + ) res_frame_list = [] for i, (whisper_batch,latent_batch) in enumerate(tqdm(gen,total=int(np.ceil(float(video_num)/batch_size)))): - - tensor_list = [torch.FloatTensor(arr) for arr in whisper_batch] - audio_feature_batch = torch.stack(tensor_list).to(unet.device) # torch, B, 5*N,384 - audio_feature_batch = pe(audio_feature_batch) + audio_feature_batch = pe(whisper_batch) + # Ensure latent_batch is consistent with model weight type + latent_batch = latent_batch.to(dtype=weight_dtype) pred_latents = unet.model(latent_batch, timesteps, encoder_hidden_states=audio_feature_batch).sample recon = vae.decode_latents(pred_latents) @@ -215,25 +318,24 @@ def inference(audio_path,video_path,bbox_shift,progress=gr.Progress(track_tqdm=T bbox = coord_list_cycle[i%(len(coord_list_cycle))] ori_frame = copy.deepcopy(frame_list_cycle[i%(len(frame_list_cycle))]) x1, y1, x2, y2 = bbox + y2 = y2 + args.extra_margin + y2 = min(y2, frame.shape[0]) try: res_frame = cv2.resize(res_frame.astype(np.uint8),(x2-x1,y2-y1)) except: - # print(bbox) continue - combine_frame = get_image(ori_frame,res_frame,bbox) + # Use v15 version blending + combine_frame = get_image(ori_frame, res_frame, [x1, y1, x2, y2], mode=args.parsing_mode, fp=fp) + cv2.imwrite(f"{result_img_save_path}/{str(i).zfill(8)}.png",combine_frame) - # cmd_img2video = f"ffmpeg -y -v fatal -r {fps} -f image2 -i {result_img_save_path}/%08d.png -vcodec libx264 -vf format=rgb24,scale=out_color_matrix=bt709,format=yuv420p temp.mp4" - # print(cmd_img2video) - # os.system(cmd_img2video) - # 帧率 + # Frame rate fps = 25 - # 图片路径 - # 输出视频路径 + # Output video path output_video = 'temp.mp4' - # 读取图片 + # Read images def is_valid_image(file): pattern = re.compile(r'\d{8}\.png') return pattern.match(file) @@ -247,13 +349,9 @@ def inference(audio_path,video_path,bbox_shift,progress=gr.Progress(track_tqdm=T images.append(imageio.imread(filename)) - # 保存视频 + # Save video imageio.mimwrite(output_video, images, 'FFMPEG', fps=fps, codec='libx264', pixelformat='yuv420p') - # cmd_combine_audio = f"ffmpeg -y -v fatal -i {audio_path} -i temp.mp4 {output_vid_name}" - # print(cmd_combine_audio) - # os.system(cmd_combine_audio) - input_video = './temp.mp4' # Check if the input_video and audio_path exist if not os.path.exists(input_video): @@ -261,40 +359,15 @@ def inference(audio_path,video_path,bbox_shift,progress=gr.Progress(track_tqdm=T if not os.path.exists(audio_path): raise FileNotFoundError(f"Audio file not found: {audio_path}") - # 读取视频 + # Read video reader = imageio.get_reader(input_video) - fps = reader.get_meta_data()['fps'] # 获取原视频的帧率 - reader.close() # 否则在win11上会报错:PermissionError: [WinError 32] 另一个程序正在使用此文件,进程无法访问。: 'temp.mp4' - # 将帧存储在列表中 + fps = reader.get_meta_data()['fps'] # Get original video frame rate + reader.close() # Otherwise, error on win11: PermissionError: [WinError 32] Another program is using this file, process cannot access. : 'temp.mp4' + # Store frames in list frames = images - - # 保存视频并添加音频 - # imageio.mimwrite(output_vid_name, frames, 'FFMPEG', fps=fps, codec='libx264', audio_codec='aac', input_params=['-i', audio_path]) - - # input_video = ffmpeg.input(input_video) - - # input_audio = ffmpeg.input(audio_path) print(len(frames)) - # imageio.mimwrite( - # output_video, - # frames, - # 'FFMPEG', - # fps=25, - # codec='libx264', - # audio_codec='aac', - # input_params=['-i', audio_path], - # output_params=['-y'], # Add the '-y' flag to overwrite the output file if it exists - # ) - # writer = imageio.get_writer(output_vid_name, fps = 25, codec='libx264', quality=10, pixelformat='yuvj444p') - # for im in frames: - # writer.append_data(im) - # writer.close() - - - - # Load the video video_clip = VideoFileClip(input_video) @@ -315,11 +388,45 @@ def inference(audio_path,video_path,bbox_shift,progress=gr.Progress(track_tqdm=T # load model weights -audio_processor,vae,unet,pe = load_all_model() device = torch.device("cuda" if torch.cuda.is_available() else "cpu") +vae, unet, pe = load_all_model( + unet_model_path="./models/musetalkV15/unet.pth", + vae_type="sd-vae", + unet_config="./models/musetalkV15/musetalk.json", + device=device +) + +# Parse command line arguments +parser = argparse.ArgumentParser() +parser.add_argument("--ffmpeg_path", type=str, default=r"ffmpeg-master-latest-win64-gpl-shared\bin", help="Path to ffmpeg executable") +parser.add_argument("--ip", type=str, default="127.0.0.1", help="IP address to bind to") +parser.add_argument("--port", type=int, default=7860, help="Port to bind to") +parser.add_argument("--share", action="store_true", help="Create a public link") +parser.add_argument("--use_float16", action="store_true", help="Use float16 for faster inference") +args = parser.parse_args() + +# Set data type +if args.use_float16: + # Convert models to half precision for better performance + pe = pe.half() + vae.vae = vae.vae.half() + unet.model = unet.model.half() + weight_dtype = torch.float16 +else: + weight_dtype = torch.float32 + +# Move models to specified device +pe = pe.to(device) +vae.vae = vae.vae.to(device) +unet.model = unet.model.to(device) + timesteps = torch.tensor([0], device=device) - +# Initialize audio processor and Whisper model +audio_processor = AudioProcessor(feature_extractor_path="./models/whisper") +whisper = WhisperModel.from_pretrained("./models/whisper") +whisper = whisper.to(device=device, dtype=weight_dtype).eval() +whisper.requires_grad_(False) def check_video(video): @@ -340,9 +447,6 @@ def check_video(video): output_video = os.path.join('./results/input', output_file_name) - # # Run the ffmpeg command to change the frame rate to 25fps - # command = f"ffmpeg -i {video} -r 25 -vcodec libx264 -vtag hvc1 -pix_fmt yuv420p crf 18 {output_video} -y" - # read video reader = imageio.get_reader(video) fps = reader.get_meta_data()['fps'] # get fps from original video @@ -374,34 +478,45 @@ css = """#input_img {max-width: 1024px !important} #output_vid {max-width: 1024p with gr.Blocks(css=css) as demo: gr.Markdown( - "

MuseTalk: Real-Time High Quality Lip Synchronization with Latent Space Inpainting

\ + """

MuseTalk: Real-Time High-Fidelity Video Dubbing via Spatio-Temporal Sampling

\

\
\ - Yue Zhang \*,\ - Minhao Liu\*,\ + Yue Zhang *,\ + Zhizhou Zhong *,\ + Minhao Liu*,\ Zhaokang Chen,\ Bin Wu,\ + Yubin Zeng,\ + Chao Zhang,\ Yingjie He,\ - Chao Zhan,\ - Wenjiang Zhou\ + Junxin Huang,\ + Wenjiang Zhou
\ (*Equal Contribution, Corresponding Author, benbinwu@tencent.com)\ Lyra Lab, Tencent Music Entertainment\

\ [Github Repo]\ [Huggingface]\ - [Technical report(Coming Soon)] \ - [Project Page(Coming Soon)]
" + [Technical report] """ ) with gr.Row(): with gr.Column(): - audio = gr.Audio(label="Driven Audio",type="filepath") + audio = gr.Audio(label="Drving Audio",type="filepath") video = gr.Video(label="Reference Video",sources=['upload']) bbox_shift = gr.Number(label="BBox_shift value, px", value=0) - bbox_shift_scale = gr.Textbox(label="BBox_shift recommend value lower bound,The corresponding bbox range is generated after the initial result is generated. \n If the result is not good, it can be adjusted according to this reference value", value="",interactive=False) + extra_margin = gr.Slider(label="Extra Margin", minimum=0, maximum=40, value=10, step=1) + parsing_mode = gr.Radio(label="Parsing Mode", choices=["jaw", "raw"], value="jaw") + left_cheek_width = gr.Slider(label="Left Cheek Width", minimum=20, maximum=160, value=90, step=5) + right_cheek_width = gr.Slider(label="Right Cheek Width", minimum=20, maximum=160, value=90, step=5) + bbox_shift_scale = gr.Textbox(label="'left_cheek_width' and 'right_cheek_width' parameters determine the range of left and right cheeks editing when parsing model is 'jaw'. The 'extra_margin' parameter determines the movement range of the jaw. Users can freely adjust these three parameters to obtain better inpainting results.") - btn = gr.Button("Generate") - out1 = gr.Video() + with gr.Row(): + debug_btn = gr.Button("1. Test Inpainting ") + btn = gr.Button("2. Generate") + with gr.Column(): + debug_image = gr.Image(label="Test Inpainting Result (First Frame)") + debug_info = gr.Textbox(label="Parameter Information", lines=5) + out1 = gr.Video() video.change( fn=check_video, inputs=[video], outputs=[video] @@ -412,15 +527,44 @@ with gr.Blocks(css=css) as demo: audio, video, bbox_shift, + extra_margin, + parsing_mode, + left_cheek_width, + right_cheek_width ], outputs=[out1,bbox_shift_scale] ) + debug_btn.click( + fn=debug_inpainting, + inputs=[ + video, + bbox_shift, + extra_margin, + parsing_mode, + left_cheek_width, + right_cheek_width + ], + outputs=[debug_image, debug_info] + ) -# Set the IP and port -ip_address = "0.0.0.0" # Replace with your desired IP address -port_number = 7860 # Replace with your desired port number +# Check ffmpeg and add to PATH +if not fast_check_ffmpeg(): + print(f"Adding ffmpeg to PATH: {args.ffmpeg_path}") + # According to operating system, choose path separator + path_separator = ';' if sys.platform == 'win32' else ':' + os.environ["PATH"] = f"{args.ffmpeg_path}{path_separator}{os.environ['PATH']}" + if not fast_check_ffmpeg(): + print("Warning: Unable to find ffmpeg, please ensure ffmpeg is properly installed") +# Solve asynchronous IO issues on Windows +if sys.platform == 'win32': + import asyncio + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) +# Start Gradio application demo.queue().launch( - share=False , debug=True, server_name=ip_address, server_port=port_number + share=args.share, + debug=True, + server_name=args.ip, + server_port=args.port ) diff --git a/assets/figs/gradio.png b/assets/figs/gradio.png new file mode 100644 index 0000000000000000000000000000000000000000..07bb1953db171ca9b67e8357a3b7fb6525af3737 GIT binary patch literal 13911 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 literal 0 HcmV?d00001 diff --git a/assets/figs/gradio_2.png b/assets/figs/gradio_2.png new file mode 100644 index 0000000000000000000000000000000000000000..cd0c092c9a7912a168301d8297dccb9677ce08a6 GIT binary patch literal 75170 zcmdqJc|6qn`#;<{ozh8iiX>~L2#K<0?NF3GV(diL!6f^Pr4kk4bh2d6u^Tg!Wh}#3 z5<>PdV;hQu8AFyC>+pNe4Cj2m=kvQC_xEu>?)#75-J>+TU+>p)ZLjNkUf1QNv7rvf zQNE)G4jkaPbNi;rfdhxB2M+uqapW-YlR%M`9^k*9d`)yT4^2;-~pq)L#Ejs>;&MlEj*8FFp}o zd`^6w6~Aiw4T_JpI006BX%G^eIkS5L*evkp#@s43G?UJK4tJ3G{o|9E;FZpKv1}pc zw+9k3jX7DLbU)ozV|}`G9ZdR(`SC{kuK;-%f8Kr5Z}^qzpAQwa%yU7cyE^$ zQ1J%c>*gKhCdkBPL|$c~2(_Qhu>Cv2TFa81wW|bB2il|_jcc;w`~58#R+#*!{*%48 zJMVNKlm3WK42$Vj<#TuNHmiM`q$IC!%pP-#7e^bX>eC5gjWbf_E z2^SYn?vs^Ed;e3rl=HZmYSsQbslWZ#2hN)YtX1AdmVL9;++&a1hrbzB11ugr!?XVm z*Zz=8j0b-+tlT)MPOp%SHI)Hx(cY(vRSChMrLp~Y+%)%xFdih&DY8Vsfv4loz`v#7 zR}t!^mrY8va!M(shElp1f|mn3T@7)PX>Bdy3;)22Tv@l`tk6`8k zk5AnxBuFF&wqdwG%E6K7((FL{bhm4(q}z~)8SY|oU%_Nv1i|rp(8HQkNFgYAd~;qI zF)Y+ph9CPfUf{7ML_9M?`q1~ae#&Q-KGmm~E}sy&$DqRUgls!kgNExTNQn3@chW~q zuh2)Keu(y`1xSO^nMbtrr>Z!z)r87)&1fb$mV>BK)Jd?!|<1o=C-LwfA?Kej)J2ZZ%V;Fvo!{N z#6)a?uD?Gd+6!X;qDdS)HW*rO?doW>nBVTMCz<8k?EBp5owse>w~ldPkcM7QOj~(U z^a`8f=s@I(dPQZx=bL6oQ`(3v=jo9c)LpqCnM(8cx!BNymZP?Z#gJrn9d<&<4^4rf zr6LQ^TWx<#kF2f6b($lEs<`MiX5=y=OmCY)7qAaBA-gwkhgXLGw7m7BUneovZ$dJw z#&xoO?dIClD0y?zA|2{%O1)05R7^FeS

39VW0e+Wc%bK)CZbm9>sdkH$XghxQWZ z{#V)Kh=|fWzK^P>V0_PXLm{52M^-Qx@b9^roZ%}Ch>r56z=ERKv|BnkJg6}M|)#U}HxDo$F|x0$oZkhsjr)TbQd z1BtdqINIp~`L2l48uN&vyI}3f#sRdGLcL|6l&|9PkFBeGV2RMP?BvY=gYo2chtl8b z)Pv71U^3W`gcpozgltdGwnu!{$%;nws0s56cOfaM21`);fG?`kYxivzX+>+O-`8BgQ_azG6(=HKYZ^BuLyN{zRvKP$ z48DWUAZ6fE*>-v1<~>!SvCyUAV$G1mbUh+wH3WhNlV~N@-Z!MnmmtjEM~Y<-p^_?%P;% zcU}@vu-l)y+M2l%h~mV{;Na#yqz{=xKPH!ozc%4L;7i6dR9L0MuR@%V@|SpUqP$6UlnIvOw+3=LEMpei?{(WBQOLl)?L}{PmV; z;!nM~Q>9YuhpUK*Rj)plx|Y8V8FaEzBHs5gzzzZyYPDObKQQo}Wx1**R%z#vms%YL z*yw@sxld(FrL!twGIrsmiVC>cFP?yFz}BGF)0Vl$*_N$MeMF%v!id9fujA4}!r@t1 zn39vwW&GxSbtpGeA8tQ`BCeShCZ|}mA+M~z6)!2mB*r>O{BoD;_l}C}@ncVhUkVOm zV8lM^*yWkHbVNnicv?>z2020o_4Ez!ql_#cN?D3#+oiucEu`BcY1ot!e*-KRI(<8R zWRPENV(=bxW8?=3d^LNI4f0 zvIiiB!X$ZiPnT09`6HF0`PJzPxXy^e!%v3S^VWl*UGi$^J|}nX`*3a2sl3-7Ist8X z>KoJ7-a}?Kdfqk9pl_PyKXRvU;L_q3UiqSv^zUs0)RMGCcNB(LR)A{NZT($Hm|A}Jxdt}&!;l?LJZ?$LC z5}u2-^j_qT#Ph`djBYtjT_#tm+o`00@UF(nZttzH^ZT zVG0@WzS~vNA-HMVtTpKT@K7NZ?Q)E|jHc)7+giQed>B|7y1bTK+c)zIQx#9SiECje zYg9CF{8;YinO%5e@NX9kgh{b2jZ4JR8orQwq`8Q@0uK}8E3Jt{ezwi>*3Uy5_N`GQ zSfqUo-fUS_jY;n!^hCu^dBBX+XjERN*8(+ zd^tEKStP65PxrB6Y1p?AO%PKsX92A+DZ__^e0(w1vjgu9uiI%ws5td(ZB3#J6bJF`@QOeU75Up|!ge=C zxZiR-&EXmaqZ0Eq)7Ly>>BE;ASC!R_TJ#!}1)h(iaHxQfQJ2RoN93tTMR*A%@m(r3 zv+>dHq2=Yv8Y?$tEMfGhrY}M2f1yk`T?t<(#8NLO$|gy8 z$7A@+7EKu29HR|YQj1>xpiq;+gunV`D5Hsy4TNhiQmd2x1fUSFO z*f9JKpZgAC>N)Hq(`{g@>#2G5MiC0RKrOuzDWk*p3YsQ!vLJwJ7$p|$ujaGnR`c?% zDR?T9%hI~<$I_5F(Ov@si92l6nmOL?;PV&0B(|%z8yD#sBTUMhlUSpN@9+A4>U>q5CqnT`%gY?s$&T39?wXCa5*iBDjI<|Wn|LLM z3*n_rO@LumHMI32!1!anZf73rUXIHJ(}vrXmMq__$tj$S0QG!)X@qL|f~c989&uH- z38l|vWg*no=a)QdYid6XuAW(5Q?dfHLagtu;w#8*z9K@C$pN__r{nVRf4uxtdFheA zRDElXSV5}y#|tbgDx;SI1Rzp4NkqH5w5zRVMx0DQn7yarzR=YaRBYpS`( zoD3BfjMG}|$yWEOQ0ME^-K`&bk5R!0P1st)Av}K%1*S|1FV;eI@@J z0H9mcU6k-s-sMZqnlTqM0*m~{ZevHQ+9pTP(h`bG&|{qOAJ^A|Dp?WhW5d_i-X$E$ z)4$}bB`)Go#fXn)>}c6`7)$X)tUve?m=&_V8_4s#>jJ$nQCFC4 z0_+o+o3LKQd{589E4#lvw~I|iPc!z3MQM2JuCUIa zdk0XoPW63#T?IHOOBZE5W2)2neKXr>1(0$CeKDJ*eeEzeS? zeH_gK&sbp3nzFR;A0RGCK70J<{gG}}03G&`JeE4`bG)&PvB17TXKCTzSm67T|CEhA zu~OFle{O#mW^4tp<5$(yGrd?Rd*7d1a>-enVv_W`3t&3Qi^4Cyetqr9f@L2kiaOsC zm-RReIOz^r{}7XF#{ znppQ{W5?^VY}T`+YdG@TIYr+&mppC-4wU{%e*U2odlbJcey|vk9f(ckK&$lX0V5?z zFN~!@)=rRjsU7+M7Z*`s26%Q*Z^l+aP;vjV8@c~KzU=m`?2Z4E0bUmM-+fEEwkYwp zXdf^*vhSrZ9$ed3j?bEw%UAX#Uhe+>6~uI4{?GSp8lM5_i&;RTJCjl74Ktu({lraY zQdUvcl_bCMJd?jK>KA)HOYs1mSmP@OcD`s~7~BnbE`S4bGkllo5pF=QBql9yzTI=I z-v06QwcO7ho+b33ew{A2SxE=vz8DzNWM_$t-@9eatK}x|ysLU7HKQta2v)RPIbdQZ zymvi6F0N3pysG3dZMhI^m>ON40iKtdriugX(g}Wby!DIJ?n&n5=5Up|#%ZAiiL?gB zdBtL%c*3a+K!f3$A9h7H9se~ZIxA2PoMw2mqim|JwY>5grcOnnWG;>YYF6fmSc)*k zfs%y3;N)z*kUb6@%6eyO6*Q{UD&^r>g7HoB;=ca5RX4MT5`J4L8VVEvLmsR@^el=ClkD4qcw8{V)`(P6 zlmres3*9}!*lO?nXV4?m{Hg>(ftC(QO<1QaAJ6ylpi%NA?oM^c8b<6d_;U497kN0Y zmZM?Rnqw6zWZQ6T&&Nu7YRs8Ez6vmk8}NM6`p`l2(&b1fysBP6TJ>JAr!r#JVIEdi z7T&c|iqIF~U|O0>iH1k~EZ!Q<9;9p^(+ds-eA4^b<4k~gb+shke>7gvB7T8^F;fTW z(gIkawy;XIO4WJ=OquxQS|KC_jt5ZinR_7>IU|+95G!`?3x*U@Y$kHV7h|t#s*>;c zW4kG6u3hXel~EAlxk5|jT3UQ%t=>*BEWY(6MJ-qx^scSLu<5072gr1%szKrbgmV~Tfu)Fvf_A#Z)F!NtJGJwohFHKer)`B1S>iSlY^PJyq&Ss^iCog4f-m#K zKIcZSyMO5_kU))cNeeXC@s|?L;`mkT)G#aZYZ8l9JQW5}Z{fA}aa@52NT6J0erP-; z&R1t;X6%}qZT{~6NN3k!YM1(ZFcNO8Nus^I%*#J*Jhm* z%&3(dPe58qoh_*&kbETue_)x?D#Mj6q=}tNoZc+?G^Z%`p%#ElYgCW$X`GSvQ#_iO z`rLr4%oyUoHZp66Rj8~Bm1-@st?@;vo*-wc@Xj1Ak18%zs`53Z)No=2{LeT!ZOkAH zYlF|(k&Cv+Og99E7gEWku1>VnsR4aqDA~btZo{W8Aa{X&Tqq7P2!8#cRUA@%zCkFl z$Lf0iR!W3V-a_;7d4ng8({J0t&V;tSqnJ|GZ2FdFyNu1pie3P3^8bYdDe^kwd7*5eY`GWLQTE` z-hp3+&!b{Z(u)tw43I zyfTItiOq+kYZV594&WOB+GX)JP_fa5 z0}L~?q0$fF#NBGRn5KiJrJG~AYW32G%f1&`ag&J3Tqq5!I;L#JU#(8OQngB!R@We1 zneV?O;kCj}6)mVJQP<$FsRE27Iz#pulnDoM4t6(Sz+NS=<&7YBLX*EpazR-e$hoMYFy!KM#48;x=_z!*Jw967qGzb9)~Zl~;##=?d) z?5|UStjHSVQm7bq&8{#my;G+m_T%gEk<9?&sF68mirg6mdYv@57>GIpa~0JZRPYo` z5}F%*9;X*Ug@PBeO{|ae=m;qMN_D|_vmc=zp4jxE)|79ma_vC_diTW-#%#P*$Ek4j zguu+kd>1k6GT;=M3P~aTbN!sO2OQt5`q^j!gPH@j12_G|aa*+bDL;QFxv(XN>9DaC z`uZ()yo#r*hF!&Y6>gTlatMC#ygZn}>H0i$a9#;zdj+Xy-+G}J>HX$o?UGM3h` z2QBs$iBK7rV4MLLd0y%Q4t_Y%{TnfLTuE;Ff`KMBq@(-rxsjp4h*Ij;5m>5GVe)fN z)8-Zc_GU^*(g%~nJS$hd3Ikn@O71if?Xe>c6+W5P1+xGO8b~CVNbJpETWzs^qelZz6B@9)M!*xYo|HJ6b#*lOl zN`_;mMi6c6O--ei`ZLrJo>4mO+E$J|1t_Xdfa~hL38z3m$k++kxP|~*fLDC1xDveX zdlVIY_bK~PszmEyHm8U0tL^Ojfzq7UNiPI=G`c3%<^I;*T6tx}^ zTxWrTfvLAQkel*^d3#`i{FKjMsGZQgvVkN`ok-~5oFJ%0O=B!ep4;}7>+bp3W1aco z4>Tn=8?80c+t6LwYx1_jKF1kw25sCg4A=DuV z*C1BJ(7D$27**o%c&IuCF7nd~>1Ad?ppKQ}70x^!K9W5CG7ettFKP$hwc+%N1~MTn z7D8oRBDk22@|8LL0{DV!jL7MIdH=mo2iV7Ra=jh4vUc$SM|r7ABy=L$K-hJZy%#{e zTw`(8H{CDTOf0?W3k2#{j*4u!czIqptf6wIO&!YWfzI-CbUjAOv`u zy5sZbx9#d4t=Sa$0wFt)@zhka0z24y>OUb4uoWPMEoOT0+$8_D?^TD(S%75Hheumi z!bglz0P?b+pFLiz^9RPGwGDmsi3s^n z2f6XhuY{>gvx#{7vCpabD{1~d_S5R>>)POa1(r6Q7=ZMvD;~#blMut)sJuf5$+5OE z2dxSCrwy*hjZ{*Z2-ss9xx^R(DC3vnj-ACe>8P^&v9`3SH1`X`6KA|kP|l%WzVBH0 z+8;GkYMKiG)vG^_RZU4pW>Y;Gaap?Awb*t&?2!S*BJ602H+Bv&ZAg6%{&X1vM`LMfi z6#Y4*#a_ModO((YS3=q?0HNdH;Ao5GN83rVV5!HaJgC+RbqP(SyKAqGEgyOw1<3MzCJN6 zf=k;kCbW1|->FlmxL*|a{@WG%`c(QyUG&&W`qI);CCzDXckjP35(C{CD#JXIeUEy4 zqn_&&)2nUw?t#WX`T6+b(i9s$S6aMGf@Ka&gkwkIM{n&dn(zS)w*NRZWKr3oTb{lb zLstQK5z`*u5Fe{oGq-)C|Gw&)6afgKp+yto<$}4&d2BS_sU_lMyXo z^MWP3)p-Z5L_TVHk5oP>%|08QZ4(y4mzS(PAA5`wBpGffv><C^|Gz%qwFMg0EYfP{_zDCRiAotetPlNd*;qwhADao3( z`~=87^87Hn&(5}MB8E3BG(h}{U7hOPxrX+Cs&92qk(Z|>`zADg)6!}lt}=*mtJAoW zp)OcplohknSK<|&*BM621DhQd{Eb9>RsMY9r5bEJPOSkd7Dgv`IBjlZaP>{Jrl~;Z za@OY;ch{7t5gNrfVc6041h66jti;jxS~>egZyy9X1{l z-|#6_?6LuEKp&&+{C-{wJF&YEL5kg`vK363Q?+PQanahM$)l3z1=biVMOv%tW_aFX((;1TZoc1 z=Gs7~xoV6;fdBr>mxg{?!D2Fx$Pj-+fGVv(z!h?IVF5@3p5IC_#Hc&J|Ez`8^O;9f zIrX68TU|{TzH}*3?R34uxeEA?58J$z)0B?=d^<-@6$%h+sv}fCF@GGm>St>4=33a& z>AeNhCi+zNc<4#BK{%-mY_dGUrMQ9@)+m0b`%QC9eqm9BwFX60H=UtWO zFbPe|Kv~O$wNI{=&ABMUL6b490_(I(yF0x`qB?H5(tayhOd`{wN4s_A=tvXLnp*iR zd2V{ypSmfz+wA}S^9G4KT^tA0D~S*Nmqp7FG(@Q)PnNL3fOkEY&Xu^9VNR3V0OzEB zr^&wYGw5)zd=8x|2sgzBFK#br<4qoJtdjDJK02vG*23T36rkC9n*9;Be!vJ}s=UqkT zQWKsSL*kZS^9!jlSm#t6Vt|E1q$sOt;y7{x-u?)X&;2hnTS4JF3Wy=n>UFBu{qv0> z-p`=Eei$Va>Co)7U6#0m#sYvkwx00PN6(-dJozV7;9k0+`x(n`bl2MfJ_-Ioj% z9KYJB#XSDdANO!5ht-X~KnX=!#`=#(_+HH{I(^qpFy$_P-d7f}NK9UGCGxFz!DH%? zcJ-Nd#YP%xDrbO{x#>S#=*mIbN`8(Uc8SfOuqpmW;~Xy;3U_#~quV1-Ijwqz{2=RY zSqt9+yM`jpVEsxeEA2Kl1O2m^ET=_}TtOBEPB!e^wFy~jxTl5v3U@8rb*z~9^yAY< z93V+5Fq&q%Mpk@3*X%zl7Y#G`KS`r#j`zE8XkjmQgyp|M8l1VjVl3^rZKdGpyU4b* z;gN8~EYLkhWbsw62 zH0=yV{vK~$V7@o0E5<+DF7&4u+@SbPmP4Oo49CD`>7HUaYv}d zt3#O$maS`8b&8n(Q~bYVJ8?vx2Lyn+{-4$_Cf;z0NhOQe|VHQg7Q4c(YdzA7ij zEg~YaGzhXcTVthXSsu!{=xn=U?vhY>W#tp|c@*=MYnrGly7$q&qnW%n z7oN}S^*Z;HHPjo5-MoQPqU7IYdaJzvp%2WS4StbS%sZdgD8)%D&Y^ivWSR~o#Th&y z{^;$!`7YV0=DO)S#bj2>Iu_W%yXJ4dTOGyczZ%*i!CyXC5s zEEnI3>C#=|9OOF$6Mxp(jFmeoeaNrH0c9cH2#AO?}C{` zq<>NIIs5Eo+IID(j5OzJ^bFHqvSRe02qJkaPc>t?JEp(d3H>ktZTL{{XWn4CY$FVM zN#3~X*BMc*IwpBIWWX%Uvs^~{6vI*K6#@ukagKDb`d;kM^)69$j{QuHDXRLML|2=R2 z|6kMHl~+*WZFU+y>?_C1%!#R0sfC64$$**Sa==Xx=)`yx-|{84sns7o=02RLsL(rvYnf-u~sp+nw=(H_H8>I1#XKxJx8oeDnB zp~TW35evZQlQ(~5dju5phG(TsQsI_P?)rya;GnhECucOS5XKfWIR&dQf3e>ZnxN(Y06X8{W0@&RU3HGzK5?PmlptWTCoFpe~I|a zrnEclVNo{p&cHg@UNdqP<+2-2>#q+<)A#e5}Rp|lOs z)Z`)0h|xI-&M zpiq3+G9($8pOJGPX|avpW{e`?qc1saDCkjz(2E?F1P2*2W*vpSts6$TS1 zw&@YUqAB?^0u4fKq@LlGdv*6Z#EqCC;N!9r0CiQhumC<+{JWX_Y_OXbj~^n#6Smw) z954`S*MhVA%-5jNQy9GVnijEin)sS@t~t>O14s37sTrvYk7mNu6~6y8ky_wr6i$7g zI!`s%sN6ytqj(Cbt@T{BPFnKmKt^g+i2bJrYqU4RONXiqTiN6Bv_LhywYB|2vT0ui z+f%MA|H|y~{2;o=w7lsh+HAK{G9^Riy|hBUFZSWJ!(}I690x0{b(}_|=_Y76C*MXiD=AZQxm+Zbb^cBGB-Ww`HTCXwo=T2FY!h@N+wn=;k;nUUQTKW4LX zZ@&D~d8Xvca4@>Cek0|{c{;H#U6X8T{VVUAv7JjdR)@JV2Y4_1ba@ z<{jLz?onn`las<<$luqK(hHHX1i{fb7mk5cK2pm@#-Kw5xI_MP++0!Y7cdWrLwFuh zi3*$DP{O&%jRQ>q7Ial`L0IN|dLY{9l1+ijjHldW+4+fyAYE2V=wm)$H~P<@WGCNL zk4Jw&otm2?{ejM^#i1KQhKL1(Uc>TlR}jL4hZWXB#r|zk8K+6wOY$-?#t?9`-}~Vm zQ81fJYJ4s@_X!1@b;#N&zHFo7$rC__;C_jQA_W`Np;ULg3Md6 z5Xm4@Hj9UP1r*p8wK`%X^rFZ~o+SaIzXBZ(=W$>g+j#X;(%8`Zq}7;PQ%-$>+YWU* zju6k=;5uhA!VC_S3WAO(x<4|&>Z(h@9J5YC4;bTv1+WRHLIAKLu>RcSRWXXLIe z`$G(jRnK@92Tpuxj8Z&4#`~&CGuEIsV2st>Vv9C*qiTVjng>Rw(Ds6HYSyL}X6|V^ zg~_jM)dZxyKI5P2q4UKQR0D%LFhOR(&P1C_E7XYzXJze8QBBGru|z>qOJlv&Z|2x5 z!=JDpE@4`;?bM+UH;oq;z0 z$Rn?&}B zf>q$0IdACMgVzXANEKA@&$+&$&G_hws)FeHJp5m}C z=g1x}Iz4dYW6|$p+j7T&Mzn{X?F7M)?+>|MmpuhY)8HkuxNtw$bgfTGmy>+vwAuXW%LhDiceC|AiT;NvmNG_9&7~1l1FBhXLp+$xZG7JmcM3tT!;>5jBm-M_To3XjBe4@4 zsPvSRzqGD904=HcnxTx|!DEY2@vZpF^JL^$%;ro+P+n!&m-HS>c^=TFXu)yTUfga1 zy}v6!QdAnqeBgi18syYCB~2#*Wl69=&ru8ZWT?;tx2^j`x+b<%sZ@=<0KfV%YMkiv zh2BA~50|l zq}?MAAnx_V(sTS@LDu9&2Z159ngZ>?fOmjbG-0dEi9Je^P^dyN>LCY9RGMqFt&}c( z#Z@Xmg}i=C`W`(~q8eyDo&H>6?Lkjm$I3u3LG{$u;WDQ^OTWjs++7Dx#9^&BgM&an zS;75)4RCkxW>;zTqlaw|jLiy>fZ@;HnSXD?uZiVt;MOn#lbmO~Y}87>#_A1orKX7D zz>Xuv(sLRDqxgwXFU)TC(roM4mp;MLE5d3iW*88x8*sA5N959c)n-UQeGy*KsMVp_ zU5&L1k7v+48J_0-PL400+NiGaAw*O4^4yXVZ4S*_H#kD@_P!98TXdP=8J(3S98=mf z@&fa6bN<16umZn)^(Lck3pR4T6V>&yM`F=k4xD}$10qb~mV(FOa+vqa8=Qg-_iA`V z5lGa0CG`Vd0U7cUjVgC#4ew1ij;}mJR^dZwtlcXi z16iS91WclmqAW0zpOV6AnRs0Ee_%Xe$A652LnnOqhE0748tOfGJ#oPuQ#B`4^+vto znn&p->u8J?=b zwHizb>b=fr!>E5{l;iD9Zo zbF3nMoMdNUo2FV4I+FHN=!^4o=1Cqyoi`RN3fsymd)41@-pyq-B^aYFPYAM8E$}=e zY^&#@SDBfE1Ej?1S|H2SIik7sSpDYf9p))-V9P=RFXNmnt=)uspI_=1X8yDgl(ZNZ z3B3$vrySh;?baqs38)>*#8TsUXRqwg8^V9x4rdO1XJ=Wj!b2)8k@}x*w(5p`ScR2*=Tw9Ge|dSqbw%hS@6KwfE*G`QpOFmPwPWqmT{39pb4vT~-s2N0|ZYZonC56$?>hj_ zB{+U%Of-4=YUCpz^$`!0{FXQ`upa&8-?7Xl%Ow5uT_%^xaaGf!KA4$scV1jqK|eT; z@Zdi1I?vYWrOB4gL`;I0lnoTInp&a#@3nb~MOKA2syLc!BJV*BL})xN*`W?s!%qwp zV$2UiYgSXW=h6T1(qVUc?EvR4IV#&mQUV*$Nf_c6e6>T)8P;|MWXPh#{$s_CZR(YB z+kP-m*#7n3yB(;OeI%<+hwMK39X#80Zw@_hy}!oF(Jg#?w%*LAM-4uyGJ3@&HUrGq zO4y0Tu^O(1En?9jM7H9a)sZ=YIh)V+|2_vFXm@e3ZBA#$pjif)nArWgd+=oF=%mUT zP7@0g8qSgrKt|G@w5;E%gQCOl=C|q3+@5jV`H!6`(R&*_ct6M&52)=bAQvpS8Z@dA zR&z5r{Opi+V)vi~y;iX;9=|K6?%DUH;Tq$#uk1g*5z~n6>qC_xXm|d%(;9?q^mQxA zcHV`GH*Pg&)M};Sl4$Z!!_Yk6Jl|`IfQC^Paugw9XJ&e&$K?v=Z06EG8~y8qsFywV z>kX6l2n}w$ji)3d17uSS-Y0h_cDIiKl3Y<6pD%v3VcqfUs7HezvgIwQ+%$2T(}gJ{ z9XU)}kgBvb)DUCJ@uA8wZ%&>+wilipm*Otb?T7T;rBAr_SiFc?GTPm+Uh2}=GWQHp z1WWK?c_Gw*7W1tEW+|UM#y#ynwA#ZsA*=2bM*=VF%= zelmveoraa4{mup}OG@P=4Cc?32-})@*;8-VEdvEeTV$<~13H&AkNlu>~N9*ZRN0(_0PX@;D3H`MnU(zSS%GKXig{$F#n7Ftw_KRNA54Y{frj8v? zQRZR`uar_yUB3~sVgwdZSt(wO2)+S2_5=>5IMd0Km2!?1z%=w4I>4B+?WSW4vfKzx zVBGk2Tv-i2!dr@v;e9fXCGnox|ITR6}KY0zf$ ztTFO#mb)zf#zt3z%3Y$$35ng|@9Vkve)Inzef_qZYkKgv3AtlmNPrQH)t=9*9R3Ab z)L+wo{WLPT5X67TYEpGnh|%K2uuS)k zE=M$AopLST{TX?YOJts;e|qvkPJu@byo}JUV}8*!Od#BD*j~^!F}@_#`x%HGaIG%4 zN;_IjdHeZ&cegq%k&$zeJa-W&unhhqZL_JX#H-&g=hn*W4D{KG38bDvt@{Mn&{pFmBhrnRmhj;%s25&p-L# zcc8v+5^=dPO~5jdTN~s&AZe(dHRJ+ZRxwL4K|uEqIR9h%@5(yLAUyyRGt&W^(@qmw z)8qgI;>?jS*U=WM#~@y6ihkVo=Yp_|^bs`iKM1q$2V66hKQd`$w&w`s+9+>Jxu)-f;lP1|E5z@Y*p4vrmypa>FX%#kmdL<%D**$SroBq_o9h_JS(WJQ#=m% zPVCM+8R1Sd_&K4^b^@iQ7+6>s9rrVhf)p=xXu;8A z!)bfWp zT;nA~y;@4W&Mw#pI}ks#{m@ow6)Z=RO&PPdhCxab{^fGG6hS(xru=$#`(@J8jPeM& zsA1>;$~EbgT?22{yaso{7F&T~M)fgi_pSWHKhI*d+!0&WcI#`Iy#mSm2p%MP|(4KGzBRF0mMN^kuDt)1*xHk z)FhCIh=r;mM!JCX5&`Li*dWwIYUrqxKmtSvAtWKm{Q@}U_djQ?bJw}|+;i7DS)XgE z+4**R_ug-Np6wQc6{yw4QW@yv^F7pKfPfk-M7_M!n?0i>TGj*xqM9);;y&B`7d6!H zc^{6mgyXgWV}^4-IA~}!bAF}_oQr|e zHwV!5EB$wa3jR~1>9<`cTR#cx%#J0-lQ#=#_M`Vv{Ij*C#-MssN#pMqcg^ z;GjuPGuL|1_n#sl{~0Yc7fnPhqM8QqJO`C(%?+Ds7{zDyhR(cPPD;6>jL{hj`F)5q z0CcW(F)Eloh3?%OEU>4x1>xh;iv%XvfDlmn;9#NLhKadh^~8Rb^6^3*LoS7vSks%w z7iM`;XOre;{QuuD5&yroXTd=73UH!10STBxj@eh3+KjM>&kvg`GB^AO0CGY8GmxeB z@uM`5CTdvA%omosul+~K0q&ar`7S}a2O72p)WrSwP5c@`1{uH+0o3p(Q)`4{`T3!n z^M;E6fF;>ZEYFovG_3U!6QoC|{={yq&(=^kXK27biku1+OTRSmdWPOrYrRHJsi9w+ zTcJWMv7!YOZaD}PcMsYq?ze+6_^MKSP&c5*P@{^Ha+P^*WGD~=%MmV)wk_hVr-?73 z8>|$dGDH@!;u|DosT2b0PFWE$(5UirUaocvv;7?6R?0=FZ8Bn83eRdLs!C&DC^Z3r-$|;14~Lz_SnUGr#>d7gMrleS|-i$QmN3Jqm+^g0V8BBKy9@}eist&?GU+- zCKXMB3`;7`X&ihQ3yCaU@FedC!q9N3!nLrqVYXR~Wn6DbZ=bYHcS=m-ZAQZ$)tYSw zWKs<9!yZU7ej5~XHbqJ)M5{7@G&=M^t;9K|+<$^K$c1o2+PO{@-`1%o+QGKaT`o>F zK+56xxYrzQBh=ra>6RG2Pw}#q1`z=7+?Iv5?M``oktP*uPTv)VFOiJ(q9V%P<(1T? zJwnIsak)HYsRxj1fT@W*m6H0>{JIx5?>E5)Ccu}20eh)C0{#*G z%{p875PJ#UXR?vvBUdsh+4Ck?BLf3~n1Rj2`6-}kY;p(ikj`BIrDMBhk(f5`&eE-J zSP9Y%;C?E_i2o`HM?vbBCfK%5s%uYH*4yn`2AU!2>js@u>6@#6Ua>#sQ&=vPV}(JU zURFtZ06;qXUoK+=G^IJWzHn1Z=NdzO3$#;GTl_P?qr!+dm)!ueTOd5|zWqiyYX>Cx z3&Mn@dRVz?=>EZW&7Q(~=5Cu;ByRirgKlR!CcfutI*rHIW%;x=BmkNGKj#}uMc28U zKa8;t_n#}HRsmsh&&wCIYZqI#nCkTTf^&ozg!$;Sw;Nr4e%~YxKy3mD0MhB>I~cJb z&Ga`pXS!xvww$d>)U>(;@W8dIB!QKMI#nC0vs6=E!t>A3P2P|bglyV2PttNtA#Gc9 z*5h_2zX3!`1_HPM5GMoj^&U4=RpB4Zf3OV%U_L-PK%&O!*`|dRR$p!=Ib-(an!F*e z1_bpk2Ly-?^1tfZw}3M<=mX6qz0~mvy3J4#kjlt?C}bXx!>rb>oGsuSGH+ijH$ICd zUz57ATWUa(+NEL(1m9y^Vq$hlMa%d4jexK&^0$iZ>&gK_NPmF73&mPWwHqUf7nC$M zpz=nt(Aup@lSS0LsPTuMe`;!?sfw^YEU(2oMy|1lE1{INEV+43oO!rm7YeR0+bm|DHmf2Wg01 zH>~F!qLz+yKY;HuHz3@$cY@`SBrrH`h`&{C#_Z$yGyp#OFfL)Rnh6gKd2g?c@Foy} z7!S2A=a56WAS-1ssdhgAXG5fVnQwdNoOP%WIyosVNxdvTMJ!Hn{oUHYW#=JTl5!~F zI!u>&TckgLd;yYyF}&6?kQYgl(V^{I=o(Ee&4AB&FpF}3eIDqelX!T|DgNfnKj!H? zFi%dzzRd;mxG_<3Q`yw5S-sVeP&&i7NP4k`XPAHu{jb>GZ#;^zdyUY8orKwpk`v6?@ zZFx?h^HCbG{(C~IkF6~WfDz@TJb-x?@E?#&6w{Z>IFss~ZUrAh_{tA+qqU)402i>4 z$}VbbK;6!py+xB5U}H~M-FvtC$51mhugMT?PJM}0I8+Jg{!Q7ZKR^bWG63)e7Z7c* zjN5%?Pm8_OeCNs-jGW=*5N=(rFyYSruK znwR^A_I-H!3lkr>AX&$09jACBTd+zDumIs@glvFxJwORM`4jP;9+oY0(l3TdB$pFu z@LouKOK14*TxN=qWdDJ__QH`Uy_JORjuB3aXKn*v;!UM3+|v&{@a2_o`JD`LW;|<2 zS}+WEr+^CkG8qZ~#yh*5{Uir4#^i-$fE#$WL7pKjAYzI$or@|41>wNZjfnI4C{aoW zP(2e40966;EaCYcs!lkpWHoY*C?y}=3MPweAgeM0L4RlQuLw*W4LJ1lN~aUnV#=gsacIKhM_ zz4Hdx9|S{Wedz^&;&icFog{g@Ct^|Tm})hN0S1}6r?F%9NoF!a>M^GPzj|eI1w-1L zg#rCaOl#S5<5DqI7BG9qxQ454J_{17_dN2LjFov*0}h_v&^=U}d~Bn^(i4^S0ibO@ zyNt}G&%OZ3x(DQ`m0H^0Y;7J${;TaU!-5JIfIO3TtNwFpC5Zs*s1DirIc7#UtN~(4 zV>)zuxODpmdg<5S#&T*MnGDaKePjm~%(TNdiT>ik$9EiS8 zF$c&&*hnB`+Zu2si~Re8I4voM{5gPh*V~cOZWD|X$;p0KBJx0_ue)s$`mLDbyfhi= zNd4;RY8uZ82nh1bYZ!AOz1^TBmy>Qg+ar;>BZ87UR1dh88v$;x(E#5!zxITdvFsAY zagtDB=iQ0P7_Q-5!05}HgGL3mT~_mlA}eIBg?`C36~!7AJb*ha0QMF=y$NDD-wEP7 z8^8UgAdvXz4DcaTjffoR`g!CYx~{6Q*Uqeg0&^WgAHXPDywycH!v14=T6BX9hYScm zwVX(W8*aOvJf;ASZ&6{-i@U3(YTdTOX)a&SmG^o(@4y>O2&MIXsM_sw&KC2=COGgY zxUgh*|6z%-cHa5RSin`cJN(lhK&mV70rZB0l+eW>nb*KDdz~RnIQobeGa1&L zx0nJ%Rsb=cS#Vi{NSeQn-v-sp*W-*BdCAmfH-3(X>{Ax;P9Yhy^Qz%BYGtO@yQ2&UqfCcboJ_qfa?Hg|o>!p1d@YWw*|t zRs-n>(jW49+(51Fd)a`JBlNZ!*qZ&BMR92-Dquc0do0ghL#(M)ihHmq^*ytLd+qvi zJN<$%996(Yb~dSav3@skE_RC#E!GRmajCKdkA?MX?G>@m{s>Ta3W)5zfE>Q~dqq7B z00y`%=S@)}zzcsij;PH9GzJsKTzknRdj!aMOdg%sfxB*)ySbyUx9~8@?Hoj@10jJs zxA#T~xO<;OTFLN3f5kN9@5!ofyU+!g5rCUS`;eWPwavg7cO|x;&U+t>T^tVGM}qug z{u#JcY)%%|$ZE?gi-put0OV{mSq;E*zw!o`_Ye4|vaZKnD?AW7Su8~uhr4ER`er*S zMRkE`j*_4lLa2eghK<40YxTS(wp_IAbrQRO<=fY4r5A_MiK;zOPt{99xjiIz@oJSb zf%1DmRWh|@CVAEl)GB;NVStaz(G0K>o(NXSCAa@-60^oxF*mm2k|m==Q{DTsrH5U) zGxb}rXB+0Gt(_t?)#Cv$yGNH-KMp?jvyj~yq9_^ne!=t8jd;_HkC!X0E?bDC*RhI2 zu4QcOr1rqVjRIWq?=1?yrEwahFexqfuo=n=edEHPu*}dbRWm?;yi+_aD^`6!(;O_vBwbU-$cd~oj_gne< zgWmh2J8p%@ZS%2@GQS*$&o2x=+dt3HB-01%g0WY<-rnKrXr5;x5*^a_rGum1+tFv% zEbvXmrE(2fK3}FX@Z&_>i_JAH?7=FjxbVx+qkyr)uRlacM+iW)X0FJ*P0Q1u; zt%tR7y8>aY<}RQUI13#e3}}c-w-kR#cTy7cy?Yz> zTImNBom>oo9jh(T8$hd7S1)xL5` zZT{Z`X9KJ6*ajRqyVM?+B{fIG$xhd>2APHCK1M~kXt7XfC?Ee#&wJoe={g9zysN9> z&bn*#69~cyESdq%c8qcCCkZU_#OUCvn(tVFh3}Yfpg#W(-MZUg5n zDQ(q8F@jgSGn9Q71VJ7YXKrN>RFQwqnDkILi0}!(vUI}79^vLe`$aj#Pj+!SCZL9d ztalm?R8Fhg5^B}M6aGD{nl|c>@?cc(zZb8xZCAt3D;Fo;DgP5#NgHOjwRjs{ll^|> z4HK9ljN!Hm%whUTAUaofQbDyXZ1L&MeRxcy0B~KmMK_6ZU+eO}e#+KlYi9YD<8;{F z*Hg+5^{BwHfgya)^mjk6DV=LOpYG5~Y}&fnw4NsNHikBxxUQA{KUM%p;!)xJl2i2q zY#sC?BU}|olkp`JQay!|06s7Rkh)WWtBP7*s+-)B0T)^|a5>ESw?=EUWj>-merri8 z3Ek`Dv&6-kwUwIj4Y}3-8(^)^F&^ry-n0br z3w04|z<`&T8yM?X>n8Vgm7UW~c)Y7cOd3FYs7qZ_bS(k!i$a8dP*am*ky#aveY!#X zaH?7U-16K|uhaZ+jd}6RW##u{UkFZa_e)X6nwtU21dJkR* z0DkgPLNIyncAD=RFxz2uC=vakw&uPM3mSbrO>^irpz7YOS@TF09cjERzcm>4QZ10P zd~b8r((t!t?Ed|#LINt)mzwJ@dHSCi>y5t4{_)?&Sl@lIc9l)hCfJh~dKDqNv6itw z7R0^!$9oZwP?h8TZDUaar1lMfqZg<}R#!4}66l3EeD1}E`jEHaa>18(2iC|4{HwKe z4Z>`-=cXKg_Lw=;=&tTF*5D;>1QdX+ZNxwUEdaS@f$-W&Gu;>ELC9p_Sj zSGNmL*qc7TU;H8$;l{FsM~S@#azRWm>b8no5sfB3XUBk=-rYIg1I;K+79C&0Umg$t zh>H>KxR~ z#q)@^PN`+CoP4QtbKfHPaOcX(FnOUjxDxs_8)~<$O{=F)3>|wo z5`c4;^kJBNl7%|BASJfM@bFhZTnvOH#V@?L&{DCy4UrX!|Mnq=FVBMoOe!+h!N>Wr zn0On9)fQAFRf-#Tko!$&U{@e!V#z@$}u! z+bz~`==pP;Ud;t6#1)VA%e=YV?L7m3A;6uo<4Bvap_d6wH%1llB~AGkDfg=esT!ed5Fh&AwFD~$JisRG>CTBgV+(LRJcT9ThOPLMCI6T}Nln(mtz|mR zl5ZN)0QFMok&s}AF00*djL#VYaN51HB!E1ZHRy&tXND1{^rB>5t%H>*c~_;GgW|Ie zI%2dr#K~m~3aR&vcajM$YF{ZXsWaqPDdxx{+hcT#T_NnDem-GA=H>_u#k`!`Mv3+L9>(1wkIZz ze&OWWrOoH4EOp^}iY>ehW4_r+RCxUh)QgK>2B4`qVDWrrBSHsg<6Zz1e*ETtg2HXz zR~&@S8cI-hyC};@Y8+PP`Qf4XLTPr_qk1?bwRM|&s`vQx$8B9Ia^ZV+Gqj_m-LH0V z@c^&Y)T1)M^8LxHZOG7f{{*Ve8THT86baCNNx*AIM%wKjA7zci6lK&bbmQESvy*X- z5a%&ooTwV=D(THG4W%9l{;Zo9xOYs%Y*^8wWj;;#1b*Jo<6Id=r6_wHx4pKDjJE<8 zqU8t86=$a+l>wI~iCu0&{Br}5Yw4I>!5H+a1tx9W6ZST0uag?F@#I6DM_kV6V=j%S zs;wQQ5;;Eafp0i0mC`02nbQFuJn8UJX}aFTM=ROIkGS2pta=akw+Og7uhE+wh5aY* zf+n&KfN7I))^@uYO%Ebb7iRZl4nV~flXa2;tsHmkbCNRleBK@W&v650H8^ z>7PP@p0&}XTu=tqS`Z}5dAJ%WO-Sd+5mL@ekqJ{!pb0TOZTGN`akIv&0xbHs|HPuB zNxW>ZIYy)hs2qIS=xt(+wVjz4A7Ams_j{03`yt9<5lY3dBeGfR^ALS`VOokPo~te) z^_t6(<&?j;GSETGvHfYNE zAIR*v*kJ*BJ=07SvZ~k%ypNJN>+{Q;nv#~%Sq1=DBv@Xa2|e+7odm%nqPGIN+pJRc zr2>FCs)I29f4Gru%4J_)jzoys6@2*c$QisZE>)0kI(VJaaoI{b_56eikmcg_v*YR+O)!-QW z)F4jp%k>%Cwe#r2X6#jL5J21V<+~59BpzLsCA(8dp@F$jbMWA*7J;{H!eH$teT8Op zHaNvgFK=*N9ELOoFuL5Z4QP)nhPT72I>%0rvkSf8U1 z<$xIQfz+|}leb_ce0fzv6f3LPz(tSP2b)_qmHi@(9*7+HYc6or_Yi-kY z?qxopmX8DUQoaK0znEl+<)C*(X=&*i4S-;sPbw~6JgcPi8zhur5dhI}g)6c+0BpcI zBwnne7~^vAin{jxDzg{n`Cl6o_1_*9sKUArWgo!v&E72cPEY^w23=`)eCq?)tDr)K z&juXImNm+&ceCpjz+J~T=zA3aoj`=Q&@cT!Mme>2JN#n;$;jL{N6>FTfn}|0;G6!3 zk^e>h0%P^=f!~F@%-*Ove;T!d7~V$!mP5j&k?gyQ!e8&O=-mySPoF+Mg3-`WEDUKi z3=_0^!zg*8WdaqM&F&0nyse0F3`ScuU=B`Mq(iJPVh&CpxF`rePpw-T_a-uIvw)`G zhE=04ea!~aC}$7IZVm3et;jhqOz-mBD$v`D*QlzjZzgR;?EZ+on*Bz>LS;hs`e>Sz zmDJQ^7pnkTQIOwE+DL}vo z1(+3)8_55=%WG!Hnc_=d9ZG?(-8N#5n>PK=GH(Gd`#;eBO;`UbYne#L=g+s7n8Kfc zoSwq5G1-bkvW&5wICB(OZl>OO<4%;jdjMP0+P-dCe;CLG9Tt$LM_g%>A&N;!#R1m` zOw(}SIZ{5VD*1!*fJPIJQA)(wDcb)zMfn$4F1y253AAU>fe-#)gfZ;5BAvljy)Bu#mEzU~v=mdw0YYFE+F zb*?{k8WPdq4-4|NjvpNw><{!m22DwotxJ2!4^ZTS)}TbK$BI?9m6r|!7p@Ap_kw^h zyZz{4YQb0Ic-w9O0YCrfq7}`^D;AV2RV4$4e8++To$dP!0QBW8Oeb4atBY9e@s8Uq zJelt55df-`j_nnmv;8GwbRf0UJT!)!wIH8AT+XO~_Rw1+ii(YR;#7c(+Q$6Q2)HgV zVeorXMUih{&Thsw!gcn$uj+lIW2ZJ)2mRg*hl`6CpIk}^L~a9GwGOcksBO{%VP$IT zJifM3RG>~nPix;y^dhJ?(4u#Erw>ad z&nSC9V!L?sGp$mrNTm1hP?E(gkYMp(Y_52IL!gU~^f*-ll#Bvw?KQwlfA)9i(Vsf6 z0aZ;BQ^z@V7A*T--O~|9euuoOf7ipdn3QxbC6U2bqz|P>qQOO5vj70R=81`nFYMqs z&%U?1`=t5e_Drqs|BcrJYy#Cl$^bexUual+a4!f%%_2+fIk)Qyzz+b3CBV7aOTzRd zrA-^*MX8M;woSDriiUFobwDiPvpgv1qaUMNWlvLSx>uZh941x0g!=UoW~9I7@W9(- zx>%UC=&!r$AukJ*v2kgm!QAc>(7NN}r1AdP(P^2YaZ)cp=5TK0dX68Qu#^EFlh7$~ z$a3A&q}$i`r#5?TenzF7(`#N6coJMPI$n1V_jpD#J?^6U{Ln&hK0FS2h?7**s2=PX ztSiT~^uei>zxd|DN!AY7a}y9ESL>IM$qqpbzq-(>z{LX%B=R^2>{ zAGicS3k{VQ-B(uH0I@bQE<)5+UK;C*890|FqucVas!s36m8}8tIJuu^({_PttJ2$J zjZcKXd(ev@eJ;HhvVY*5bX`t7YS}dqA67g-E_iSy{-<$@ig9DZrN;xs@e3ca2F?l> z$Hmyml1@aV571S6j2VgTtn(cJ|5Gc1X2hwLWv*KG^+%cFhoxb>PYJTQW?lyk`rD{T{r&Z%0pUVMGiIIxHp^%)U z_VhItiKMCD`Lp@8s*TW-JIDLIEx>hbmTbD;<#1wR#ADD<9R9`ve2-)ah*omLU7H9n z4zK~b`TaKp9I>KX26JNZl^$O#&NrwRC~#IQfX*P)0vxdQWB!J}IL1#gp)1qNwqL4& zMT4a4?XT4hSQ?$0tySImEN-c9xdWi_E;v5|VDP^1$3}6DZoVTd9!lYNmIsGI`?IFM z)Bad1L`=3Tt^F&;_bnfH<(!>zmVNNEs8bF7%1@E3IzmuTi(-ph*WQJEcp7D% zX~5o5n|_gG{?G#2%ghJvvA)~*C{r>rGY=h;33lj;g!_?hO?|jOi*hWMDcjXrkh`z( z+1t#tnnPJZ?w9C=cNJD<+K!zdu14->SbC;DiLG_GUHPhT2O50rYQW97qSKtDQ=9>G zOf%15DroQf0jI}pmITQiimo#nN87a}<>dh06u-2eBMK*#LS9chpK!f3`x7_mf|&L# zWcCVkP-tujhXb!J7A+$~B#`J;JNGtfk*Bh&bQ7_8*H(H(awfRNd7Teu6L4!_-owmO z6z8lwEA*>8bi(A~mpzv=_R7cx8z_TaAU%ri-zba^qQ%T3wU+)2Ao_`BnNLKfXr%1zK8Q zxYAPd&BUuiech2qFh1y`Tm(50j&BzWN}jl}r1T7{KyesVgtj)EC@>3~F7lMJ&oziq)#Lr5 z+KZuj@~S9f6}J%eM>_VRWa1fP-IBCoSy#1IoguwZol$v&5#*Tx+GT?MaB4xpq=#ze z69r#r^V!Dilo*!7YZ`Sbn?)TF?XbCdQ7PmI^61Aeqrt+5jZ#>o@l5b-O!b9<;P%ym z=&M_Q*G%2|TYX6@^zH4mU2!jA-d(<8y;_xzT%)}3stDTnm@%=e4L@uJWG@p8Y--U~ zmhTCf|LBRcDeYrc*=i-RIoGGoJWUHq=x5!&6g$d4fcbSv(QY5}1e`NA0r9^;tNdwz zUcxV=y7CPhxK`{S*$KDZjhT(F8Upkc3w>IcPWUnD`pnU>W?aZeNE%9`?p)>qBh@izGJ}qBs!eyv&7+93? zM@|rio(mb1PzaRyy!rHjM<4VYDcl8(>#!IR8OqG0>(1L3OxwX%%U^|mWMlo`6RsIA zA>l7r?Xs2WLOjCvqjc+Qt&2*on-pvc`Wl~4UN8j$8*4f$rX8O-Dn%pdYcRRJqlY?O zgSatrd@L*CTJuH7qEP7N4xi0dLCsY<#gZH;M2F&&>=yx(AH&P+(~rXNipPAlottGT zyPN&kd>_7ELjk%oX!+{lY^zAP0|jAfyoBvvy_ZtH`Fmg?@`|C>R3@@dR3-~&bYQVzx*DMFUjLgyHPi1ANHu$MG zow}KEfHA?8oxo^w#aQGRAGQvv+Uf-*^D~OCc6qP6eIN1yXgT}gqzYZ)b)mBZEbv`Q zHrh0^TOWHP(H;#!*~w=akICQ~DBMih{fyckerB4Im%>0^l@`qAvm%0yrOEb%SNTPV(;FZwZ^l4*fn(_(-_NZ(x>Ue)8t04P&oh5D-e0G6V9r zUXJyCrgHHjMOl3wX2n(!(+Aa@*#40FZQzUa$tZ^h%IZ-@uGyZ5kyY)Z)>i&ziVKNn zy{vkia_-jLx{+NZrrP#lIL`OGWW}{5rxKP%jl+X_=HcfVH!ETfi^qFWgIav+2xc4P zX`4(wgve1^5SkN1=`3J8Dm5otelhQ_(>O{T33|BN>&5TYF^|gx&rz(c(m3DS*L!ZeJoy%;P8^Q- zSmx{ejiF)i@K$eDEN4@o-I3boi_90r313HImA6%xVOma8WUTfyZ^QgqZ7-=aQ{Qs) z!+F@%^w8Wf;NrUT-pHZ%qoT&g8A>l<-Ku_OmBYy^DO+TnKhBpc1;KDdojSSgr2JeG zTOW6%&ACl>>O$mNo;R?xuf9a*4iC!$TynD8jfjte@|IY2*v(@& zPnDq0bxS^riZFdFaoErlb62D90KY4LQXzW=QbwmP-Xa_BHOR^%WwQovMC+6g%dX=7 z$a3{SqaNINTt2f=$EN87m_v`>cN%V4t!$M!CAwH20)Z6Jmt!0WBT;AKLK_(epLy6= zjq)Nw7~Sbl6}8p9kx$9bR#*2_M9;58upvx9p>Rj49$Z#Rq~4k>VE86qP?C@69NZB~8}QxG^DY zWe-@rD>7jc*qc-zW6ND9;&W<4kI8sWt7Kda$5&9&hDOK@-^Wdm(A_Fm>yXUk1!J-J>}Mg%-Ms8`5FtXBvUbXo z%MF>tJ`QG)AARV#nxbK|A9Lg@2uHZrsb@v4T^u#FLEuqOjZw=6W&RT|*xbnXYxPuG z!6f}l)r2ZGnvdU8^V~ko;T15&FuR^^@Aj6Sr=YThuwJEeZ_r-Uy9k{_p%H+;VHOyOsM}{nuYu=}(6m7(S_6bzc{t9BLHM zV4j){XHIjX61NlG=E_F_>I!>qyEE|*5PCQ|Q&6$PUlV;pGMR**)~Q#ZIn*9QFJ-F> z^`S~|R2{xqr|6M4zf#Tvd&D0<&eu(Md0!5K){TL`n&-9)KW(~SAM3e){dyRn(Yj9R zm*Un{Zy+OK{UiSIEPTJdsiK6w!1cQJ`Ft?8GwW0&eBiI6ZPVSR+tHHxraf(!{;G4+ zxd%>5ry?`Ks(e4?qn=mu9(MB&&HbV1Y}zF5H`DbtWaaC_sgp-W zLNMfZ0+`;%%rW_^)0<*y-~pDS2%7Om3H#cmlBx!^h|Irx)zcV7YaSj>Vf}JDnaD_y zwJL#AVe$WH>7~%`?^A*>XLll+l_1pfwXbqXKG-z1$NzXoibC+Taw0IOHF!z;DRU6H zDMn&S5>A6t1G=|@V*V~UEfz{FH9+TJm6Mki5PDNiOYB0@_z7AD`ZeEM;vWs)WcJU0 z*v9_-icO#I{5=7i?!Nd-@dJkX?V-O1ebcMSzdNyMllo-vhSk zf1s*A8B+WOsfaZ)A~%us8g-)!-K^G#tnRWmUKeb?wN^sSdw(RgLLdcq+3P*JtFK@a z-K}3MOrE-BG#SnuLo+duIDLTVURe8k8vW}-DR|52?`wGT+Ko1gdIP*&5uwCt$h$~Qr&8%6GIRI|DANluiAx|c{a#2`5F8Hd#d6D_q zM4e{k7BYYN)#`$y&rSsGF#K8$kEzoJ4GPsW;>RUh@b>W+760`pyhA}u`3;#oP&pg8 z__Gc7RL4+KCF|a3&0?ld2NC&KCSFmwI5A%8+5 zij|oDuLs8Ct@hc8PIP6v0~=3Y^iJ_Wu2=do5h{10#R@PT(B>I7ZGi9HF=4 z=>1>yn(q;1;?<-$xXF=yxTjQFib_`aXs7G61Cm)L-k**pL3Cu}T?t8z9S7nTN_QK|XH1%qfk2BTN(h=*Vw>nnqnRz1U6 zp*f>521>ByIYLAb_icTiRdndMk{2@DL7kUr`wRGqw9hB2Zv$4V3FBW1OO)JT&I4X3S-HvS%LNrkooewYokHhwk@fs^5t&{k4b7JTTm{K3&4qKlq zi9?eX6&7riL5f2+E)1Jvy0^kuFzgNf5lr&fqxw0%5$5M9wV&*M)>^Wa?j8BE3j}&Y z;tzgT)}jCNq2F5_&@?dSuF}T1#&>Sc9>wG_rTl2gd-Z73h^d*((QM#=`t<2_$`$*2 z@gF1wQwcOtN3=Kim8en_TzTqh@YBx5_))P?c69w~rg3uoTQJoyqpOQvx?9A} zl(zePreZ#8^-9exq_i41J{%sw8j(pni`$8nxs3mfhU z{`IWr!R`5lwESFR4}#o?ug^?tMy-V3mqY}y9TJhQNtqkXic-f`<4@TkGllCtIZuIv_wUvd1d2@|c5A*NJwq&|e5{rbNZrh;Z($u(*G(!o+mZ z4@9(rzK>;JbP+Io=#IJ0(fx@CTJ-9|cZ0>%exb!x8t;f=dd-ne`ur++J|0bY3High z51y1oz9}IGxOR5?C3avHTk@ynW!cwz#=-D^$yFOJiZ(V4Vkl^n#@c)+f*C?zGmTSb ztjmv0bSJY9YKt_b>7QpWO0CA1fmg8g{wvJI`UXlSkM*7ENId>J5A7XxfH8G1g1aiU zPu49x5?<;vO9=!0TR4<;6j#!-m^}y%sSTSsI@~*5wFlwk?M;xSIH;q-{u*~J!?x2D zL&@c+AVhlT6<)CRV#MXp%sy>yztA23Rz9FB`wJE1sUe!K2lsxMt=1^Q{^Dc=2>)u1 z0xuE%6{Na?MzQ|+1byXG53KayYO9%3<73|=;iNIZ%tLgSKydR17=%=W-HG4yv1PcRR>1?Y4>@k zj`rHV_9jw{UR(cK3IzsvBljuA%}JnZF{j%=KL#}9@u<0vBf|@9N;8hX+;J)+h_{rh z(=r*P*fx=kX%OdUDiW=M)lsYrUw+`>8a}ei42TZ@l1oL~xk}2~;zooSYSrog$j|ZE^rmNu!1_mnlXnu^t ze{bVC4>!j8Ca9&a!~&Zm_RP&ozjV~8BIK)=aug`o`P6LYwymJ`(?i5Y0qk!vKtbE| z#JjV`g{D3g_P5yhEiRwsccok&eWQ|AxHx!$4i=RTNT*`j42XGSIzf6#xR-7zX>nd> zFj4Re7mOzxKc1Y({)n1x&-a@exH|D5O0gm2QjY8B3ljhKX9Sk69imc$od4st4+!+x zcoh1Pk6Ln-B>VpBNvFjjm_6qFG}}An^=upuw>bZJWN}^!H53nkAOU@-EV(u>m-rbm zSE0QBhXmd4WOi}BgwO8FEU1%H8;&TZ0?zNkprv|G!2wM#Y&c&^x=~Tm zAcMzlm6}T2l4~~q(JGy4j{m5<{NqH??&MHr${9J8E3Q-r7~NU?sHrL znx67sLd z;ZHtg#g)Q6DMs4bz-RHF8sHG^va=2-a*^249vYiL1z(`08qk8bf(TvGYkTvpg+J00 zgJ!z?jpsA09IUfy7Li7s3r!X^nb7m6%MJTZ`c1=zwne>NH`zM6(eHa|e8(RMOt!`M z+ohT-25c1BQ`rsQD$3Mg?Hu#RbYHT!^ba;^>jPt0uY2vo1Xkop>KC) zro}20VNqPTNq@HvNndoA*9F?u%vZTm1|m!Ac7X2L4efUCBavnQoANgO-TGDDJ3ZMI zZ0<9Ff{roDHcP2}g|}JrJDD8bP?&%A+-rE!w!eF;nq5DgFbD{YS)(e~-r-F$7Orw? zdceSY>cCR#>R~~9tY?3i95;pBYu|`yIsrCG__s6#!pwB}ynh%`9 zf&$YXV<%2*T~mMagg5H@!}EfC@ta&?Nt6iFGqX`sf@A&mB^j%IM;PmNLD6>IEPH-& z-EtGzYj@r6z_R~df(UcTCb$+bZD#eego44o#MLno`Vv%TT3|Kla(}cd-m>3Mgn1n> zoA9!W`i>}{bhO;mz&-F1ZOytW$XK72sOxY0MdasaGE?N|2R1`2XX(!xwRWx#jMTcF z_~wy*ly~K?u2G0iK>@$+NSKCT4DQbVZPVTD_C2uD^;>8O6)X5^)5wNl`n`JR9gWV+ zRM`^J^x7AcjsE;Pe){u)-GcvmCAL0Wn-c9C>)h4~b^Nb4YuD=iUt>Z4pQ`?ExgaP? zL5400#OC()6yQc5X*{aJMpe7krs+fnhH`u6jc2`2%`Q2y=f_WPVS2uhR-P(FCJ)m> zM`Lgr`P!ipsR?I6p>gxi#?c*^sc@tnbUS9_+~;<%IUoaF(Nlg6k&zzpxN+I1D4^;KR z>s}{qZ>Cjt+^$nPs!75;56gB*QLZ)$Y`7^&eX|2^>+lPiWRCY()vc(MTH0EtO2cn? z;UH>Hha0(AKz!#Puu6}CkjZi5J$I&NeB&Z+fSLJ&*`qu}#Bv?MNyRiD|0JQWLUS>;;^-ypE>%}aP+QE3l=d^&q|n zw>i7HW$!BK2YuPn6BKG-*+!^p#|+LFgUVVp-;~;`O4hj`ZQ5Cf3kwzF zTHDIP7!40wJIWPwprUPk`HUe*TBT1q{q{)gtg1=z)RW8qnZ*Xb;emhbO3Oy%i+z3UPRoGlw?}(m!9|?1%9Xu@ynK~!p?9Tw>3uE%lq4cXWudah%&=L5d?o6zvcvbaDIZB)N2sZbuqKtlzHF@ux za2!JJ=VF?9hhnHar8l(b#OdNxZwA}E)bN*Bnf=DqAyeg!UL|%sguSw4H!*F z&XL=F^e$goatfV)TY>!btdwRIX<}*KbU+<$onw$Tl{WB;QrqbO69~+PKs)SX90hlf zv&;3a#MLu0P7XGN5{Ll^_UFKXl}|5I6#Va=`%q$Bk~wOJxD|nvtN3miYGc+z#aJ#K z7dJteVh(t@gr2V22f8^l`nLQ5IS98UU=+x5u@p4mcnTz-(t%Ma<*>BX zk8>j6<@Py4wec{^QtCY?FhoOcNb+z37NfB<2*x4H?ki(H?kJlxiEkWJeHn)bi985o z=1-v;L>Lr-JaigW1qtNg=c=VgQ#ZvHN1=aOf}V%S!SfB-E4t#qFeDC63Lnwf_KKmV z%e&DvawR*@>eDyMuuH%_7nItwS_K%ew1LdR zpmryIdzVR1VH8Nec6@0Xyysl+*rLM4PO^~%AZWiR9G$S1tPFpy@ny!*hCOyCEggNX zCCOExm|f%`S3bNxaSCNd+1Oo4RNYz!Qqn0(qCx6ysYv7->dJ?q%P+i!=x?(+?BzTW zdZs=Z&>&OHai`*v_U8UA?&%o$+G(AMWkbpt;lbp-IDC}G@i;}`uABiKyC`b)c99%7 zhbHfVGmkr3l{mA#Mgd`O+qvv6IqMguodGba;x63T!&?-8B|&Rl=-ex&bl5UAEF+-o zIPCn6(AGtEg_8P>G*|CoNtc>k!D_x17@X`P=cLErmG+cpSm~!YpIrJo{a@y9798KU zxBKBG=Z?=N6DrbW-2sWa3a3{eon|D>rs8|Lg*o)Oj4d)uFPSgB4nQakA6l29)~nMn z08y2_bh6zilhA6;oWWndHT#T}kn&u9bTP!b$!zCk<#EKVkD#w1;4;r*&z`oXvw6wq zE88IG+11EmgRHN6YNG&(_?)gi4K?j8ENR58m4@3O0#8EJdG_SfnO?N1Db@A+VD`l_ zO6P3bvFC}dHeEV}Hr_e|G%RtpkU|?^si~jN%_3g0tMn6{{IZXF>s0NCeicpph0Rw+ zhJ>2Stq|r38$-JcZ)?)&7X3KLQUZ8E-m*v~iyymFamqc-m9MVs5AnG-^ug@VvMzQf>npA^xs zw#rGxVsxf+iZz`31eeU0k-XZr{_^Yo*4z)yVZ+(mNqr{i1Ur5HjfK)#Cz)%E-Lr42 zl!i7MqTM_dbFC|;CH;=BgWv6E&pJ8$c?mqTg;pcH>AnfxlpHx=u;b|mTrsD!9ryNZLEsEq%Ad%4Q%TKSdQXOYzsKwhFIM2{s(bC z`y$0{O-4*ih)t*D)Mam*iEZ`E z5!7&wOYwa(R0X7JQ08iFR@PJYVn$Y+d}_PQ7fzV_+HJB2b9N`)4 zKqT#fJG)#pCa6|VA<#gbv#F-NYOU0R%<=a^+SfCthz%u>oqHIjIc0stw;pw0F`6JV zD_igPXO4I}F)l>eJtwAqy=CHK$`mNU?c+RsM_cySyKVMOx~`NrGfja{6xk;KnCg>R zF&w>G#OH~y$8(_fid>P`+$;7YZQSQxv`<`-Vc{4GFR56U`Ji;%mC5>b6|c*g?|*|> zh2**Hm+o~A?l1%IR;s^FKAS2o!9}4cXL8MlGcX7W_C^y=l&YJ_(VS@e{oO_@1ET=+ zHBQ@}y0Q<5KL_O$at;#|uy=OcN3o^O@PU$;Oy>=#BeyNEO z89HUw?{T7;VQEb+mbp0`e{+PJy=Vt^blEd~ut`H4+7Y^8-{rNgb7?iSHDLY#AQ9`; zxkV|ap9!4PslSW*w5O)ebPAUG^gRIM^Nu5HeUzVWjm<~OV|5m)?Q=9!e1w9aXKhV; zrZ>~x=(8Aa)Ctwg9_fd@M&r5*yBNd@AFr*VpNyVL<&&N_c>;1+^K&1L;iJQSf!QL= z1YQS$w-1+Cx>xMAs3{g?-PhYEQoio}EmX4Bi!8X|5|KG`PyN{Lu?_VWJd1prWPGnw z2*T3x@Wa=ffXl-p)$2PZQFpt9olNGR`VY!SbjPW0ovKqwh}ZDKFz1fcEDeR=|6TI9r)35fUIPAftGCFC!0 zM4!cR*SbYZFYWcB&`Jhl&Z43oCq?3Z%$pjn5zqtPdq{anBLc~Xz@LUEzhXuNLtVzd z4LsGQd5Mi3`is}rGw`uCFy2I3liMWmhhQlbj>)y3eZiA(UaqDPR*tLw)ilt84`Y7+ zY((NTh22SIlBg#r{b8R+2FsZ&+0T~>$H39H1RNAD1>4LKSon8sFA z-;uEOu{2L~h(lfxt*U(Ie17QRn5eUF{RWw!b!{o#!Z*ZiIUG}3$1+__#m*@V)L@32 zzVzceZD_n3guggIs^%8>sIdFIZBKC98cTU=-}81zHHr|e6s^ZeL#AmEqzC;5VC!wV zA(W>=H}}F<88x4Cp&SqWB5>geuzJ?M{uqqqhs9q+y!=4u>FFyL1rmE#4Lb%ZaJ*yq zVYUN$`bXVC!LUoi^Ijqmvpj|@$#d*a{Q1A9_Hexo(~k{S8dDTEv52Yo34b&X?!H%Y zSL8PX?ykUQg(Y*TsJh$SeIF|Q?G!Xd!fHL$`4fATdbI!e{296THOpRZoqwitaWQu( zhLPzVSdo&an`T4IuEyQICiKoTL*exC)CG}4%u=HkTH)CJDa8`nX@#X(EX|&9o_u(+ za9pNUGOqgwMe-24!+PY}5bElr`u*eCrlJnPLVG#2&ZEtoqIGY^?!p8Olz8dV-nreWx$&e zCTS(%E~=;;`OE-#8e$v7Efs@&+IFX0wInAvtZ5Alw*A8xi1i7nMU6?*%ikDX$v2NRR(%%2>rUxmA4)~<*pVeQC9o+ zz*1*S!)tr1$~Q}H8(@OQzA9ha>}P)Z0txN`WxAr)GuynjC8efv7VeR@Kjv$8=MVX@ zBLy$E4EQ4H%~!hmKYOfnxddL(%$?^qE!3Nr1p};|PE>`~sA;F+;@aIjLvT{E!xj4E zO;_=y?AFsKT`mdU5gKn#YA7RMDm)zC?*f;COqP%6xO6q~cA)kFEO(Nwm>=lNxKN1D zE^}UTP{5=f{}$qslR>Fnxc#cx%Hh-DwHl}~i{AGlN_F5$vGn9#6V>o=6UlevvC)2i zGUWtV`xSlp13dI=G@7dZcJt&}e``NVd3e#Tno3$ud!!r7mGNM<4~mjXKKFbM?ffnx zqxR|0}ab7+QBCdGp%nTzR7Q)gKOLqC z_Y8}dQkB$oLNMK3I9*^bP6-%%gpwnDE%a+@>sjDI!L~%6xHO9PA*p8BpeP^hQh80# zwPUaY!@T!2yLvc}T3gGjY7#kyYmN&cdz&%aD?jnHfv8~$tHSNNgrl0m5SD_~gCuf+ z@a11*<36dqW5Ed)U&Czbi{zfxmtriT2wh5ZOSor;VYT1QWg6^ z8s{lFAt$Fbtw_`_Mw=uG6ZFraN! z?pb0q`QMW%o3UtUu&Mn^9)7!?$f$pfo7uGtqo4-t)J&n`MvTpfmv(`$t>Kp1^63L& z!*)%&=AoGu#Yr&A$1sY8gOrqqY*bjRvEO^JNLt+O-tQASIg=m9bq4!_9|-Hr?6*FR zc=i=)mC}fRcOv-W-x4q6*!g6w;J)HVBHi4V-YA*FXZKj+y~O!lxV1FUd5=f>KsP31 ztbv|Ek1s>OC;R*iUzDvpFP_0vSkK(zJEpb#!1CWupZ<2Fon+b*u{PCT8^{Q>49Jq6 zF?PTf_i0}~Cvn|vHvq5KdIJ#0e40L6B_eu|QZk*Fo|!GBCy{z^?I2z6Q=TecIJwTU zB-;ETiMpyhShaT6Hd9izNdy1F@pdcIkc=$3#g!D&YL;C}c1$UJoy%%REOX#txb)l_ zKQj0zHj=sOb~b!jjvHOZB;AMm>21>LBC2LEB@ijG(OGCBC-xrDxg4hps$(YWSUP2T z7Ryw6xJjA`-HNVo{xN@s+2Yf8teN$YeY;Xbg8q#@R~zgz?8$s(5rVe}Xw%fkX3xxQ z2Vy@?>fCOeojF;TD#|&Opi-A87^$3!dph2+l?u4XJwA>@7k9bFB26|Pe1Tk;l+hR< z{~>A1`;b*z5`hJ$d(qDgpOegDWU<=kA57sc7owX-$)77Az+yzj`K^|Y$bP?yDDJ~% zP+`?@0;KREJd=hjR*G2JLsUidLur$+{mH0YkSu=hY9m{|G?n?_t6DGCFo6f+hU_#39sFsoT-E3o%x%$7OeJm=twZ! zCBcLW$ zx}Etn^{bltRL9aZ(^DkO z_2dWM#qjXo9=Y;*shq&E!aQf&>g++eW!o~MZE+UlJYM1+tmS85AclP#B{|++XtFq* z+uAZLHGs^XB3@#v*r^-1qI!vp-Qhi4MlHTx0;|19(3wPj_}bkvK#_$|tsw%nSY{K) z;dm*Il&oVmfhH>PO}Y?_a~+7CwC)jAer+l znN#E?p6rMYMBh6h+3j4fjVv;`F^a|IN!*i=nQ)r|plee?mea0;G?zE{1dZ7PD_>&) zKu8U=XMHu~c2~uTnoSHdv}>=9jI|m#BP+{VeKq2C67w}mI{u{g!(=txFP`Paxf!eW zY#rO)X%Zxp=je$jXkcvi+MCMqy7MGLL?vRsvkb`RF+0GhyZMvDFNz?4 zSPNd*gA@rRYsy*A8IHovWSb=rOoiflJ=rl~6N}OEBJjVa=(kEk^iH412O3dI7t=yz z31dV;jD+Nc|af>F&>I07Pk%E7Z`GnL9Xu2pf9z;tpeo++8=$8a-dDuk2 zc8Tu@30dHCwEess?>FD-HjTsYNCI~%dodLk(F%hMn$dR3dMqGa6Lt%#bK_$9r%ma> z`{8Z70}^GG+JtK!8aTQpCt=R^MHp7Rc)`tJ^t+b_4+>DW{#pHW7TdZ6?)6oPxI@n{ z@8WKgfDEaax5PyLq|OOin+qYY^*;j@@{BgNmy&SDW{|#{)7Ghpmela5CZuWSCNn{S zKHkaVEp7o}^tef#gFGI?PBD?*rbW)$vT#H7o($0;?uigny*++V`W9>R&Vx1ZQ5&0USOo

xDw$`P#CGdDJTKC6IsHtH_3+yMd#RzWM7#qb;dB!`(e6z2cEu3ubTEo<1 z0%)&HPE(KB@~iZA(xX3wQbOiih>yk^T{1>EO=L_ga?fH~?KJ$lc9@9#usb)m>!jQ# z!3F00V%lkIY+=z(7%X1qaR=Ip-A(vInT7;Z=DHCt2y?%>%d{R#~ydF5&)T?Lj1_Ah3i@4br~;+UDc>r_F?#1P^v+Jf_Lk2YmDQTwx-=A79l~|=qSz77hPT8RN4iZL zQAm;>+C}NETko_nG~wTXV{|XzrtiExKp)$)K0hJgv_loD;aVJ|VM83{@}k{Rnst&+ z`R0EG8o}6o8_yal*e(6Pq|f zL?*l0>-Ww_-*-5T?pc($r0gnqhp;PN;m3tc^q3*}Az3*xl`|`PGm8?kl^!%5p6%Tj zjGuDF9@=TU+^pUc)&1E@xe#eeZ83Fd6GNBRIhX1< zXRl#jOb#%SElYucy_sg@OWL^vO8Sr?u` zr$B1BZ(likM1!Ep9xQg^l-WIRzY`X7wh55)iV%`5L}0W)_4-(bPMy6?y2{5CA^B8M z5uXnVK;eV0zY#aCu>2Y&MdtN)l;zg4N?IVs#wb&N-Ap*HixqnEWQLV)(R5&ywN5iS zbE9}-cW`a)9d}iZ`5<}x8G}>*G*L+K^&g^$BB#!#a#nGRTkQ6i^3B}~_8SX7wpx3# z7|OEmAA>}kr*q;T?F3;`LN;#r*1MHkpTz-4gt0f^_$@>@Wgho6X2lrBi76!h^hk!t z?#67(4v!PyS|2~_sM-mB7%1g6yfkm%=T9Uo_Zo8*!(A65F-KsSE}qbGwgCvB;PC{(tJw#?rchRBmIY$AFyZM$?pGQ;P8><-tenty}=i zHC{+i(Dg7|DIU}aLGPqvD19qb8Lk_7+v$n>CBF*(B?olP z$4ie@NsCf81=%_`@KHVj0iNtCFY&Z`h%LqM5LjBcB3YrwZ>_jW9fl(Cw%VWYnk(8I z|2Kuw0pm0Gs?i{sj(itPXz*o?tINKWas;$;cjpAw`s-X|in`fkysf979L^ltsyvkX zR@a3_Kd`CI`m`=)%FVSqja-zS)p7YKN1*^Va{UTts*`dKZ-g#aufENGUHZXFGsPkM6|Yaomk_P8dVElR-Cun4|x4*66w{+ zokdi|bQv>;Ra|k2F!n`wc8`xCsOuD*ic)H<2*O>UO^)0k0WfMUWaD?UL$ZjoLp0 zv)olkYZW$#1?kXdsyRz5&6Wu!ad6km=jFgZyC3IFe{0@X^*o%G4f_$h)k~iGiPH((?dCS%R z>878tVOFZ?18z7hw>g1RVPTG@APV4(!Oc?ziuospxdywCYx(`0;32Fn*8~75()LdX z09;7jWy-MtFm}@yO}hZRMM#^bs?|t&Q&v{soaHnB_R#^8+`X2Ni*&xFRBU%1*|zc# z-qa?|IDWBKATku#rZ2g^t)kfDc&`S52kCy;yd(FkME5uUTcSa3c+pcp$JAM4XTP<* z6mG!&Q60HbzU-IZFuKDiDJiR2{5zn%$P~hd_Ik{Z7o&U9Us8m7z9TYS{g&(}{?kIf ztB3}ngKT=It;v~XkR%W~_+WkLo1DOIr<&fn3qy#=?WZ|7RQ>d!`L<#*cErS4iQhtIAosJhUjU=qHnntwlEku>$!)R*?T;FKgIr!B1^IB=GP5MAf187I{ZTZzfna zKKEue*3HX7F6=s{*ZU%UG=lqyd3VO?_xgT(6FI&P@|C*pa($y{rG4jv^@s}iZ)jPA zyfMN1=ojO+^7bmHkU(v)!=F0NPyI7EaA#^=0b(v|+*)oaq&ePE!K~4@Z-_q{V`ILF zI+`_kQm!G2veejbn2Du2`mg=w!4fAIw>9nlX8`6Wh#a;~Xz#QrzcnO=8?o@-{D}l- zTA+i3;k8OcqbzucEdS* zmsV9V?OcPR?LQa`NxuSg>gKaML>=p@5@c?uja3~VUs3-yXV~Kz9ncE?H$~NT8MB6m z-Ta0{=RbnL7L=jt_~Xqc9{&?dBeKnS+q=CtZI;bELh5Bh+UCp|{|2M|Yp&XnS8^-Uw`Oj4HwPGjS32fxNBNGt+ zr<-q}3A9s_->1qmSF3sA;S0}AfWof*CLoi#ab2yg}84Wrbq?OTXWOp7h7=MM^AKli*u=(^VCQaLjhkGp##eW@>b;t zJ+fdOQpC7`SL0Wm+BU<1g-65VP!ngOX2MPbL|szT(O;4d!Zz;nU&{1>j|Tj9x8B9F z3YL^K$|)Iu4^Hlbds>?{70s+&#orR_IFK{mhXmz;n;~Jk!0^<4WH$Z5SA<6eZPso) z=O(>5s(Ijj5)R@iMoG2L>G=u4@^>@@W@f{yVZ7^X)mH0%U3v$GP4Wo1>X+l%7ca+s zEeEkOfZuE*(e|FwyOLcnZm#hSra9+OlQi61m6A1o%0}foCAOR_UshkN)#{)2q^Fcu zc2ji0%Q!hjiqSMyXpdW&(j_(i^&2r{3KgDxR$zBPQdU()z9o1Jdj0EO(5!vty$*Z>V@P-Qz(h>%R{uQmwyf3DfTepwZgQfD5?h;JM-6_H43&Chi0e@|`%EU(U~W0&JXR*p3mUYl(nx&bIb z*S6MEBv3O>f9wQRkSeb}Zq<3~S+&yFhl_y+1c8!+0xmEesu%fMGE->mSH z)V@L)V||#$>W%bc*}yrK)=$cEdpE0QisB(KjO z%zoc}^Nmy5qfdbR8^QNKT*I#UQSS_;BnW-dR zudqdbm7jJKJ`t{G15w$G?WO7ox)z5q?W!I5l9s|(xdVjfZSt}EfR`aAh3xM|+CT^W z%2bCDB=dWlHfE?!KyNcWg7NNvAN9bz0lC(X_E#_(?TOsgmnF89y$$3VQ;jO~LS=weEb( zU{H29_|}S#O~~{n651*qZCZxnyoX8EODu-C?hDt4=hh;Av{|Qor{6!?Q~&l_+9;Bv*eFR4Z?L zLzNtD()L+KTYpD7=6F!1r<|9b@5A~hZqr0MHx8v;q}+0(bUpg9&If~;xKbyFA1tuo zXj`X)lIB=u9c0u9rM0UZy`LW0>Q<$L4*krR6;4enML3<@_;M$pApZC}Ny|7h>G&v{ zea1E-@no2Gjq{v2!L#E;*@O7H13P7Z@`uTnt-gM8EmvwmIzC761h?rMAfN1`M-WtQ zW*gzQHtWp;RNlgAr)xfbPclzH%1!Y1Az-~Cv`rbLLaVc~&F5MyMfo7_zP7GJYJ9YI zP8exM5nI^A4Qq_&ugzSQ{o^471J~s3pD*dg?_$?Gj5mKR8qnzXoM&GVO~xv`d(utB zyPdmxJD-p^t9n3{N>LaYHL$1>?2n*^Mi7p{hqG@1VoVnWF{xfntP%xjGT0} z-ZX>xr5Q7Lh)w^ZJXV<25~56B&G^qvKK;ex>-j&mUT=SULh=X=9xBQHK;N0L7kGep z4rO!|4PknCQ26nnz`f6N)dHd|IIlB_w1=f+^BN@n+d(xi7O`JjMRoa?m zSwV^ssL&g-Hr1-I6#y4PR^)#~!T@fdMt=0SwRVXrznE*O7inHM#m<~f$leie$G%TF zvoa^;@0+JhFDfZcobQ2AKe9c)ofXuICX_k$+l5$?>~tmX#tv zgWae17_g?t-(b1f7Z1g{3wOGJLjZH{sRtr5>k*hPaz%Cc6=xY+m&@-Z_Y@OH? zyZwmYN0n~4DAXVGX;xa@9HnsvcCJ%gyTXR2bb{w5N>G1M_OwM?Q4}EH^yGNSX?<<& zH$ucO-S>7K_NEXVM%(?v^-_WL*0=HJ2je+KGv0yZ{O{kHc}u`BrBN5&Q1yaj&*#=M zw9||bsC?bd$$5MJN1U5lb)Z#TImwrmX^n+csC6=*l5E6qZZ(Kg664?)o#iPRLSZwc zl-lM*o&K|3nJnEU!7W(dz`Q^<*p&U-og-eybd@g;^VbmBEiiLP_G*f)jdNKfZ*x*d z&!l&L6x_z5&M>Ym>JS&JhK@)=Zjl7J-rSxY)SOTc#%3j z!7iEJ9B6KuoAsV;t9&+wuAoSK7wx-vqa<-P4AY=v@UuCPeqkz4V(qH zS=R+uYN-O|A;T(OnMXgRj8hH!aNyL6#2Uh!TZo50Vvr4d#;V{{JN(CO>zr;88KjCo zqCk;D+uyrLXdU5z=O|m{r!zrCrROAIXa(6O5S>Q`h>i2lZ|5RQ51vAhpZ>(XhgbT+ zVp-0+Oi}t{0jdx$*ty>AWRgj6>vurD5;0{DKQ^8zHK21_>BYnQZ z&fC`j?a&0FkDignw2m~QuC?nPCE#C2j0?t8MV4u6XzWx28Z@m|DyCjGEoLrk{L_XV z+O{cd^=ym9P|C**LWa@Tnp;UcJQ4}w@n0^U*IW;n96K7axe>K+)ps!j|Ig0H6vQRo zO2$rU;b^rPOSYDo3^5EFMJbJduU;LqEi26Kxn{A5FWnSqWtbH_q;!mwpRo9Oc}8dH zwv^OD4gVT5`W3bPD|B~z(O$4{R#S5pR_guN%dh>DTL3P_Wqzhqw5h{(fD~NX(jM&s z8o1{jbp|mO@Y^*dgyw=8epV!$?<`ic8Qjwt{_R50;7yI`<0T2L<0Cr)>0k$`bDYf| zuZcwhS(+e${p>@ z!OW`WmI|&vEboLIJ*40I2WK%lnye&T<9v|M#898!(0<}eeRNaCO^)txE4ouPUGqnV zt{7|s#D0`_bb3!+Csin}8#x}*1~xr3Zs~j@BUE8au|Fqqx++BbQt=7(cIS#B=a7Ni zkt_|{UFwtTe0=2Aq=|oSqP1BQ>q!VQw-HbK{PrAUiK=w#2ppAD==X1_U zMvu_P;xF5{O6l_g$M`*eur3L{TFrPQPWuZ?^+P^lrM`XmAv@bDG@mupTTkG6YKcicvIn3&BO{U3@|3$Gg&|0L;ZUM#;z|2{bQh;&`^%5+t2#&cLyfI}=J zxu_xP{#*V}<5Zm!8`UR2P~KfAVQ76>`}I%yU$=?>|IHizztNLXx8;mJ$DD-pbO8IP z{G%{XNr-k@qJ67xY2Va!{O?)r{|Y-h`3_2!UG9CXzk)yzW7|`DK!i=_NLcmi5GZgL zvn~33`$z99h9lnMaEfs62k8jq>;Iu3$_pfz?UQ{mecv7!&>Rzofd;M~>39&o3sdxl zW~s#2>rcdsXWqnT2)f5H*NR}%{0`CV{X=d;$+5Zlz_O{Cz4ck)ywFEDg`F;Rh7MGe z0nY(?BO}tbk(2q(yh#0^mJ-~AQ|4`Rw4$Nfw!lB|&2AX`n*5zw8Mo;?RQ2Q0;nV#} z%^@alHk#^P>lxvM-6Q@@SAndJal9yS>CyhF_qbnnZRfWGNzWJ=+p~A{n-BDvl}R`+ z;-;GxuHUS)IQ+}X(4Ort&GQo>>b8#Un?z*JKC{g�NjIK=sI>H2|SsQPjQ=_R)1v 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

N_i|JXoo| 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 1 else default_path + + test_ffmpeg(ffmpeg_path) \ No newline at end of file