From 66d2a2ebca440866495d04b7dd5de614f0a8bb2b Mon Sep 17 00:00:00 2001 From: Farzain Majeed Date: Thu, 26 Apr 2018 14:13:15 -0400 Subject: [PATCH] deleted DUP files and adjusted jungle script --- Data Scripts/retrain_yolo.py | 498 --------------------- Paths/find_paths.py | 134 +++--- Paths/minimap_example.jpg | Bin 0 -> 49160 bytes Paths/temp.jpg | Bin 1791 -> 0 bytes Paths/templates/blue_red_buff_template.png | Bin 0 -> 7339 bytes YAD2K/test_deep_league.py | 15 +- 6 files changed, 89 insertions(+), 558 deletions(-) delete mode 100644 Data Scripts/retrain_yolo.py create mode 100644 Paths/minimap_example.jpg delete mode 100644 Paths/temp.jpg create mode 100644 Paths/templates/blue_red_buff_template.png diff --git a/Data Scripts/retrain_yolo.py b/Data Scripts/retrain_yolo.py deleted file mode 100644 index 9559925..0000000 --- a/Data Scripts/retrain_yolo.py +++ /dev/null @@ -1,498 +0,0 @@ -""" -This is a script that can be used to retrain the YOLOv2 model for your own dataset. -""" -import argparse - -import os -import sys -import matplotlib.pyplot as plt -import numpy as np -import PIL -import tensorflow as tf -from keras import backend as K -from keras.layers import Input, Lambda, Conv2D -from keras.models import load_model, Model -from keras.callbacks import TensorBoard, ModelCheckpoint, EarlyStopping - -from yad2k.models.keras_yolo import (preprocess_true_boxes, yolo_body, - yolo_eval, yolo_head, yolo_loss) -from yad2k.utils.draw_boxes import draw_boxes - -# Args -argparser = argparse.ArgumentParser( - description="Retrain or 'fine-tune' a pretrained YOLOv2 model for your own data.") - -argparser.add_argument( - '-d', - '--data_path', - help="path to numpy data file (.npz) containing np.object array 'boxes' and np.uint8 array 'images'", - default=os.path.join('data', 'data_training_set.npz')) - -argparser.add_argument( - '-a', - '--anchors_path', - help='path to anchors file, defaults to yolo_anchors.txt', - default=os.path.join('model_data', 'yolo_anchors.txt')) - -argparser.add_argument( - '-c', - '--classes_path', - help='path to classes file, defaults to pascal_classes.txt', - default=os.path.join('model_data', 'league_classes.txt')) - -YOLO_ANCHORS = np.array( - ((0.57273, 0.677385), (1.87446, 2.06253), (3.33843, 5.47434), - (7.88282, 3.52778), (9.77052, 9.16828))) - - -debug = False - -BATCH_SIZE_1 = 32 -BATCH_SIZE_2 = 8 -EPOCHS_1 = 5 -EPOCHS_2 = 30 -EPOCHS_3 = 30 - -if debug: - BATCH_SIZE_1 = 2 - BATCH_SIZE_2 = 2 - EPOCHS_1 = 1 - EPOCHS_2 = 1 - EPOCHS_3 = 1 - -class TrainingData: - # the dataset is broken up in to "clusters" - # these are npz files with 20 games worth of data. - # its not ecnomical to load the entire dataset into one npz files - # our pc would most likely run of ram to allocate for the massive file. - # - # all_npz_files_clusters is a list paths to all the npz clusters. - # i load these in on a need basis. - def __init__(self, all_train_npz_clusters, all_val_npz_clusters): - - # set up our clusters - self.all_train_npz_clusters = all_train_npz_clusters - self.all_val_npz_clusters = all_val_npz_clusters - - # keep track of which training cluster we have loaded - self.curr_train_npz_cluster = np.load(all_train_npz_clusters[0]) - self.train_cluster_index = 0 - - # keep track of which validation cluster we have loaded - self.curr_val_npz_cluster = np.load(all_val_npz_clusters[0]) - self.val_cluster_index = 0 - - # 90% of images are training, 10% are validation. - # images and boxes will simply point to the images of the cluster we are currently on - self.train_images = self.curr_train_npz_cluster['images'] - self.train_boxes = self.curr_train_npz_cluster['boxes'] - - # set up validationas images/boxes well. - self.val_images = self.curr_val_npz_cluster['images'] - self.val_boxes = self.curr_val_npz_cluster['boxes'] - - # pointers to handle the images within our batch - self.train_batch_pointer = 0 - self.val_batch_pointer = 0 - - - def load_train_cluster(self): - - # to fix #TODO from below - # left_over_images = [] - # for i in range(self.train_batch_pointer, len(self.train_images)): - # left_over_images.append(self.train_images[i]) - # print("Leftover...") - - - # first figure out which cluster we're moving to - # mod length of all_train_npz_clusters keeps us in range - self.train_cluster_index = (self.train_cluster_index + 1) % len(self.all_train_npz_clusters) - # then load it - print("Loading new cluster... ", self.all_train_npz_clusters[self.train_cluster_index]) - self.curr_train_npz_cluster = np.load(self.all_train_npz_clusters[self.train_cluster_index]) - # then append proper images/boxes - self.train_images = self.curr_train_npz_cluster['images'] - self.train_boxes = self.curr_train_npz_cluster['boxes'] - # finally, reset training pointer - self.train_batch_pointer = 0 - - # do same thing for val as done above for val clusters - def load_val_cluster(self): - self.val_cluster_index = (self.val_cluster_index + 1) % len(self.all_val_npz_clusters) - self.curr_val_npz_cluster = np.load(self.all_val_npz_clusters[self.val_cluster_index]) - self.val_images = self.curr_val_npz_cluster['images'] - self.val_boxes = self.curr_val_npz_cluster['boxes'] - self.val_batch_pointer = 0 - - def load_train_batch(self, batch_size): - while True: - # print("TBP.. ", self.train_batch_pointer) - # this means we have reached the end of our cluster and need to load another. - # TODO: this is sort of bad because we waste the frames left over. - # ex batch size 32, cluster as 63 images, after loading first 32 images - # 32 + 32 > 63, so we skip over all this precious data! - if self.train_batch_pointer + batch_size > len(self.train_images): - self.load_train_cluster() - - initial_index = self.train_batch_pointer - end_index = self.train_batch_pointer + batch_size - images_to_process = self.train_images[initial_index:end_index] - boxes_to_process = self.train_boxes[initial_index:end_index] - # print("Boxes to process... ") - # print(boxes_to_process) - # processed - p_images, p_boxes = process_data(images_to_process, boxes_to_process) - detectors_mask, matching_true_boxes = get_detector_mask(p_boxes, YOLO_ANCHORS) - - self.train_batch_pointer += batch_size - yield [p_images, p_boxes, detectors_mask, matching_true_boxes], np.zeros(len(p_images)) - - def load_val_batch(self, batch_size): - while True: - # fix pointers if they extend to far! - if self.val_batch_pointer + batch_size > len(self.val_images): - self.load_val_cluster() - - initial_index = self.val_batch_pointer - end_index = self.val_batch_pointer + batch_size - images_to_process = self.val_images[initial_index:end_index] - boxes_to_process = self.val_boxes[initial_index:end_index] - # processed - p_images, p_boxes = process_data(images_to_process, boxes_to_process) - detectors_mask, matching_true_boxes = get_detector_mask(p_boxes, YOLO_ANCHORS) - - self.val_batch_pointer += batch_size - yield [p_images, p_boxes, detectors_mask, matching_true_boxes], np.zeros(len(p_images)) - - # total number of batches to run for one epoch - def get_train_steps(self, batch_size): - print("Getting train steps...") - steps = 0 - for cluster in self.all_train_npz_clusters: - loaded_clust = np.load(cluster) - steps += len(loaded_clust['images']) - print(steps / batch_size) - return int(steps / batch_size) - - # total number of batches to run for validation - def get_val_steps(self, batch_size): - print("Getting val steps...") - steps = 0 - for cluster in self.all_val_npz_clusters: - loaded_clust = np.load(cluster) - steps += len(loaded_clust['images']) - # return int(len(self.val_images) / batch_size) - print(steps / batch_size) - return int(steps / batch_size) - -def _main(args): - data_path = os.path.expanduser(args.data_path) - classes_path = os.path.expanduser(args.classes_path) - anchors_path = os.path.expanduser(args.anchors_path) - - class_names = get_classes(classes_path) - anchors = get_anchors(anchors_path) - - # custom data saved as a numpy file. - # data = (np.load(data_path)) - # easy class to handle all the data - train_clusts = os.listdir('/media/student/DATA/clusters_cleaned/train/') - val_clusts = os.listdir('/media/student/DATA/clusters_cleaned/val/') - - train_clus_clean = [] - val_clus_clean = [] - for folder_name in train_clusts: - train_clus_clean.append('/media/student/DATA/clusters_cleaned/train/' + folder_name) - for folder_name in val_clusts: - val_clus_clean.append('/media/student/DATA/clusters_cleaned/val/' + folder_name) - - data = TrainingData(train_clus_clean, val_clus_clean) - - anchors = YOLO_ANCHORS - model_body, model = create_model(anchors, class_names) - - train( - model, - class_names, - anchors, - data - ) - - # here i just pass in the val set of images - images = None - boxes = None - - images, boxes = process_data(data.val_images[0:500], data.val_boxes[0:500]) - if debug: - images, boxes = process_data(data.val_images[0:10], data.val_boxes[0:10]) - draw(model_body, - class_names, - anchors, - images, - image_set='val', # assumes training/validation split is 0.9 - weights_name='trained_stage_3_best.h5', - save_all=False) - -def get_classes(classes_path): - '''loads the classes''' - with open(classes_path) as f: - class_names = f.readlines() - class_names = [c.strip() for c in class_names] - return class_names - -def get_anchors(anchors_path): - '''loads the anchors from a file''' - if os.path.isfile(anchors_path): - with open(anchors_path) as f: - anchors = f.readline() - anchors = [float(x) for x in anchors.split(',')] - return np.array(anchors).reshape(-1, 2) - else: - Warning("Could not open anchors file, using default.") - return YOLO_ANCHORS - -def process_data(images, boxes=None): - '''processes the data''' - images = [PIL.Image.fromarray(i) for i in images] - orig_size = np.array([images[0].width, images[0].height]) - orig_size = np.expand_dims(orig_size, axis=0) - - # Image preprocessing. - processed_images = [i.resize((416, 416), PIL.Image.BICUBIC) for i in images] - processed_images = [np.array(image, dtype=np.float) for image in processed_images] - processed_images = [image/255. for image in processed_images] - - if boxes is not None: - # Box preprocessing. - # Original boxes stored as 1D list of class, x_min, y_min, x_max, y_max. - boxes = [box.reshape((-1, 5)) for box in boxes] - # Get extents as y_min, x_min, y_max, x_max, class for comparision with - # model output. - boxes_extents = [box[:, [2, 1, 4, 3, 0]] for box in boxes] - - # Get box parameters as x_center, y_center, box_width, box_height, class. - boxes_xy = [0.5 * (box[:, 3:5] + box[:, 1:3]) for box in boxes] - boxes_wh = [box[:, 3:5] - box[:, 1:3] for box in boxes] - boxes_xy = [boxxy / orig_size for boxxy in boxes_xy] - boxes_wh = [boxwh / orig_size for boxwh in boxes_wh] - boxes = [np.concatenate((boxes_xy[i], boxes_wh[i], box[:, 0:1]), axis=1) for i, box in enumerate(boxes)] - - # find the max number of boxes - max_boxes = 0 - for boxz in boxes: - if boxz.shape[0] > max_boxes: - max_boxes = boxz.shape[0] - - # add zero pad for training - for i, boxz in enumerate(boxes): - if boxz.shape[0] < max_boxes: - zero_padding = np.zeros( (max_boxes-boxz.shape[0], 5), dtype=np.float32) - boxes[i] = np.vstack((boxz, zero_padding)) - - return np.array(processed_images), np.array(boxes) - else: - return np.array(processed_images) - -def get_detector_mask(boxes, anchors): - ''' - Precompute detectors_mask and matching_true_boxes for training. - Detectors mask is 1 for each spatial position in the final conv layer and - anchor that should be active for the given boxes and 0 otherwise. - Matching true boxes gives the regression targets for the ground truth box - that caused a detector to be active or 0 otherwise. - ''' - detectors_mask = [0 for i in range(len(boxes))] - matching_true_boxes = [0 for i in range(len(boxes))] - for i, box in enumerate(boxes): - detectors_mask[i], matching_true_boxes[i] = preprocess_true_boxes(box, anchors, [416, 416]) - - return np.array(detectors_mask), np.array(matching_true_boxes) - -def create_model(anchors, class_names, load_pretrained=True, freeze_body=True): - ''' - returns the body of the model and the model - - # Params: - - load_pretrained: whether or not to load the pretrained model or initialize all weights - - freeze_body: whether or not to freeze all weights except for the last layer's - - # Returns: - - model_body: YOLOv2 with new output layer - - model: YOLOv2 with custom loss Lambda layer - - ''' - - detectors_mask_shape = (13, 13, 5, 1) - matching_boxes_shape = (13, 13, 5, 5) - - # Create model input layers. - image_input = Input(shape=(416, 416, 3)) - boxes_input = Input(shape=(None, 5)) - detectors_mask_input = Input(shape=detectors_mask_shape) - matching_boxes_input = Input(shape=matching_boxes_shape) - - # Create model body. - yolo_model = yolo_body(image_input, len(anchors), len(class_names)) - topless_yolo = Model(yolo_model.input, yolo_model.layers[-2].output) - - if load_pretrained: - # Save topless yolo: - topless_yolo_path = os.path.join('model_data', 'yolo_topless.h5') - if not os.path.exists(topless_yolo_path): - print("CREATING TOPLESS WEIGHTS FILE") - yolo_path = os.path.join('model_data', 'yolo.h5') - model_body = load_model(yolo_path) - model_body = Model(model_body.inputs, model_body.layers[-2].output) - model_body.save_weights(topless_yolo_path) - topless_yolo.load_weights(topless_yolo_path) - - if freeze_body: - for layer in topless_yolo.layers: - layer.trainable = False - final_layer = Conv2D(len(anchors)*(5+len(class_names)), (1, 1), activation='linear')(topless_yolo.output) - - model_body = Model(image_input, final_layer) - - # Place model loss on CPU to reduce GPU memory usage. - with tf.device('/cpu:0'): - # TODO: Replace Lambda with custom Keras layer for loss. - model_loss = Lambda( - yolo_loss, - output_shape=(1, ), - name='yolo_loss', - arguments={'anchors': anchors, - 'num_classes': len(class_names)})([ - model_body.output, boxes_input, - detectors_mask_input, matching_boxes_input - ]) - - model = Model( - [model_body.input, boxes_input, detectors_mask_input, - matching_boxes_input], model_loss) - - return model_body, model - -def train(model, class_names, anchors, data): - ''' - retrain/fine-tune the model - - logs training with tensorboard - - saves training weights in current directory - - best weights according to val_loss is saved as trained_stage_3_best.h5 - ''' - model.compile( - optimizer='adam', loss={ - 'yolo_loss': lambda y_true, y_pred: y_pred - }) # This is a hack to use the custom loss function in the last layer. - - - logging = TensorBoard() - checkpoint = ModelCheckpoint("trained_stage_3_best.h5", monitor='val_loss', - save_weights_only=True, save_best_only=True) - early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=15, verbose=1, mode='auto') - - print("Training on %d images " % (data.get_train_steps(BATCH_SIZE_1) * BATCH_SIZE_1)) - model.fit_generator(data.load_train_batch(BATCH_SIZE_1), - steps_per_epoch=data.get_train_steps(BATCH_SIZE_1), - epochs=EPOCHS_1, - validation_data=data.load_val_batch(BATCH_SIZE_1), - validation_steps=data.get_val_steps(BATCH_SIZE_1), - callbacks=[logging]) - - model.save_weights('trained_stage_1.h5') - print("Saved!") - - model_body, model = create_model(anchors, class_names, load_pretrained=False, freeze_body=False) - - model.load_weights('trained_stage_1.h5') - - model.compile( - optimizer='adam', loss={ - 'yolo_loss': lambda y_true, y_pred: y_pred - }) # This is a hack to use the custom loss function in the last layer. - - print("Running second....") - model.fit_generator(data.load_train_batch(BATCH_SIZE_2), - steps_per_epoch=data.get_train_steps(BATCH_SIZE_2), - epochs=EPOCHS_2, - validation_data=data.load_val_batch(BATCH_SIZE_2), - validation_steps=data.get_val_steps(BATCH_SIZE_2), - callbacks=[logging]) - - model.save_weights('trained_stage_2.h5') - - # yad2k calls for smaller batches here - model.fit_generator(data.load_train_batch(BATCH_SIZE_2), - steps_per_epoch=data.get_train_steps(BATCH_SIZE_2), - epochs=EPOCHS_3, - validation_data=data.load_val_batch(BATCH_SIZE_2), - validation_steps=data.get_val_steps(BATCH_SIZE_2), - callbacks=[logging, checkpoint, early_stopping]) - - model.save_weights('trained_stage_3.h5') - -def draw(model_body, class_names, anchors, image_data, image_set='val', - weights_name='trained_stage_3_best.h5', out_path="output_images", save_all=True): - ''' - Draw bounding boxes on image data - ''' - if image_set == 'train': - image_data = np.array([np.expand_dims(image, axis=0) - for image in image_data[:int(len(image_data)*.9)]]) - elif image_set == 'val': - image_data = np.array([np.expand_dims(image, axis=0) - for image in image_data[int(len(image_data)*.9):]]) - elif image_set == 'all': - image_data = np.array([np.expand_dims(image, axis=0) - for image in image_data]) - else: - ValueError("draw argument image_set must be 'train', 'val', or 'all'") - # model.load_weights(weights_name) - print(image_data.shape) - model_body.load_weights(weights_name) - - # Create output variables for prediction. - yolo_outputs = yolo_head(model_body.output, anchors, len(class_names)) - input_image_shape = K.placeholder(shape=(2, )) - boxes, scores, classes = yolo_eval( - yolo_outputs, input_image_shape, score_threshold=0.07, iou_threshold=0) - - # Run prediction on overfit image. - sess = K.get_session() # TODO: Remove dependence on Tensorflow session. - - if not os.path.exists(out_path): - os.makedirs(out_path) - for i in range(len(image_data)): - out_boxes, out_scores, out_classes = sess.run( - [boxes, scores, classes], - feed_dict={ - model_body.input: image_data[i], - input_image_shape: [image_data.shape[2], image_data.shape[3]], - K.learning_phase(): 0 - }) - print('Found {} boxes for image.'.format(len(out_boxes))) - print(out_boxes) - - # Plot image with predicted boxes. - image_with_boxes = draw_boxes(image_data[i][0], out_boxes, out_classes, - class_names, out_scores) - # Save the image: - if save_all or (len(out_boxes) > 0): - image = PIL.Image.fromarray(image_with_boxes) - image.save(os.path.join(out_path,str(i)+'.png')) - - # To display (pauses the program): - # plt.imshow(image_with_boxes, interpolation='nearest') - # plt.show() - - -if __name__ == '__main__': - args = argparser.parse_args() - _main(args) diff --git a/Paths/find_paths.py b/Paths/find_paths.py index 7dd4a6f..d092710 100644 --- a/Paths/find_paths.py +++ b/Paths/find_paths.py @@ -25,9 +25,7 @@ top_side_scuttle_crop = (85, 115, 70, 100) -sample_image_path = 'frames/126.jpg' -red_side_red_temp = '/Users/flynn/Desktop/temp5.png' - +sample_image_path = 'frames/372.jpg' display_image = None @@ -66,60 +64,80 @@ def do_template_match(crop, mini_map_img, template): else: return False +# returns a dictionary indicating which jungle camps are "seen" by the template match. +def camps_seen_for_frame(path_to_frame): + camps_seen = { + 'red_toad': None, + 'red_blue_buff': None, + 'red_wolves': None, + 'red_chickens': None, + 'red_red_buff': None, + 'red_krugs': None, + 'blue_toad': None, + 'blue_blue_buff': None, + 'blue_wolves': None, + 'blue_chickens': None, + 'blue_red_buff': None, + 'blue_krugs': None, + 'top_side_scuttle': None, + 'bot_side_scuttle': None + } + + display_image = cv2.imread(path_to_frame, cv2.IMREAD_COLOR)[minimap_crop[0]:minimap_crop[1], minimap_crop[2]:minimap_crop[3]] + + # assuming a full 1920x1080 image coming in. + mini_map_img = display_image.copy() + + cv2.imwrite('minimap_example.jpg', cv2.imread(sample_image_path, cv2.IMREAD_COLOR)[minimap_crop[0]:minimap_crop[1], minimap_crop[2]:minimap_crop[3]]) + + for template_name in os.listdir('templates/'): + if 'red_toad_template' in template_name: + template = cv2.imread('templates/red_toad_template.png', cv2.IMREAD_COLOR) + box = do_template_match(red_toad_crop, mini_map_img, template) + if 'red_blue_buff_template' in template_name: + template = cv2.imread('templates/red_blue_buff_template.png', cv2.IMREAD_COLOR) + box = do_template_match(red_blue_buff_crop, mini_map_img, template) + if 'red_wolves_template' in template_name: + template = cv2.imread('templates/red_wolves_template.png', cv2.IMREAD_COLOR) + box = do_template_match(red_wolves_crop, mini_map_img, template) + if 'red_chickens_template' in template_name: + template = cv2.imread('templates/red_chickens_template.png', cv2.IMREAD_COLOR) + box = do_template_match(red_chickens_crop, mini_map_img, template) + if 'red_red_buff_template' in template_name: + template = cv2.imread('templates/red_red_buff_template.png', cv2.IMREAD_COLOR) + box = do_template_match(red_red_buff_crop , mini_map_img, template) + if 'red_krugs_template' in template_name: + template = cv2.imread('templates/red_krugs_template.png', cv2.IMREAD_COLOR) + box = do_template_match(red_krugs_crop, mini_map_img, template) + + if 'blue_toad_template' in template_name: + template = cv2.imread('templates/blue_toad_template.png', cv2.IMREAD_COLOR) + box = do_template_match(blue_toad_crop, mini_map_img, template) + if 'blue_blue_buff_template' in template_name: + template = cv2.imread('templates/blue_blue_buff_template.png', cv2.IMREAD_COLOR) + box = do_template_match(blue_blue_buff_crop, mini_map_img, template) + if 'blue_wolves_template' in template_name: + template = cv2.imread('templates/blue_wolves_template.png', cv2.IMREAD_COLOR) + box = do_template_match(blue_wolves_crop, mini_map_img, template) + if 'blue_chickens_template' in template_name: + template = cv2.imread('templates/blue_chickens_template.png', cv2.IMREAD_COLOR) + box = do_template_match(blue_chickens_crop, mini_map_img, template) + if 'blue_red_buff_crop' in template_name: + template = cv2.imread('templates/blue_red_buff_template.png', cv2.IMREAD_COLOR) + box = do_template_match(blue_red_buff_crop, mini_map_img, template) + if 'blue_krugs_template' in template_name: + template = cv2.imread('templates/blue_krugs_template.png', cv2.IMREAD_COLOR) + box = do_template_match(blue_krugs_crop, mini_map_img, template) + + if 'top_side_scuttle_template' in template_name: + template = cv2.imread('templates/top_side_scuttle_template.png', cv2.IMREAD_COLOR) + box = do_template_match(top_side_scuttle_crop, mini_map_img, template) + if 'bot_side_scuttle_template' in template_name: + template = cv2.imread('templates/bot_side_scuttle_template.png', cv2.IMREAD_COLOR) + box = do_template_match(bot_side_scuttle_crop, mini_map_img, template) + + + if __name__ == '__main__': for image_path in sort_files_numerically('frames/'): - display_image = cv2.imread('frames/' + image_path, cv2.IMREAD_COLOR)[minimap_crop[0]:minimap_crop[1], minimap_crop[2]:minimap_crop[3]] - - # assuming a full 1920x1080 image coming in. - mini_map_img = display_image.copy() - - - cv2.imwrite('minimap_example.jpg', cv2.imread(sample_image_path, cv2.IMREAD_COLOR)[minimap_crop[0]:minimap_crop[1], minimap_crop[2]:minimap_crop[3]]) - - for template_name in os.listdir('templates/'): - if 'red_toad_template' in template_name: - template = cv2.imread('templates/red_toad_template.png', cv2.IMREAD_COLOR) - box = do_template_match(red_toad_crop, mini_map_img, template) - if 'red_blue_buff_template' in template_name: - template = cv2.imread('templates/red_blue_buff_template.png', cv2.IMREAD_COLOR) - box = do_template_match(red_blue_buff_crop, mini_map_img, template) - if 'red_wolves_template' in template_name: - template = cv2.imread('templates/red_wolves_template.png', cv2.IMREAD_COLOR) - box = do_template_match(red_wolves_crop, mini_map_img, template) - if 'red_chickens_template' in template_name: - template = cv2.imread('templates/red_chickens_template.png', cv2.IMREAD_COLOR) - box = do_template_match(red_chickens_crop, mini_map_img, template) - if 'red_red_buff_template' in template_name: - template = cv2.imread('templates/red_red_buff_template.png', cv2.IMREAD_COLOR) - box = do_template_match(red_red_buff_crop , mini_map_img, template) - if 'red_krugs_template' in template_name: - template = cv2.imread('templates/red_krugs_template.png', cv2.IMREAD_COLOR) - box = do_template_match(red_krugs_crop, mini_map_img, template) - - if 'blue_toad_template' in template_name: - template = cv2.imread('templates/blue_toad_template.png', cv2.IMREAD_COLOR) - box = do_template_match(blue_toad_crop, mini_map_img, template) - if 'blue_blue_buff_template' in template_name: - template = cv2.imread('templates/blue_blue_buff_template.png', cv2.IMREAD_COLOR) - box = do_template_match(blue_blue_buff_crop, mini_map_img, template) - if 'blue_wolves_template' in template_name: - template = cv2.imread('templates/blue_wolves_template.png', cv2.IMREAD_COLOR) - box = do_template_match(blue_wolves_crop, mini_map_img, template) - if 'blue_chickens_template' in template_name: - template = cv2.imread('templates/blue_chickens_template.png', cv2.IMREAD_COLOR) - box = do_template_match(blue_chickens_crop, mini_map_img, template) - if 'blue_krugs_template' in template_name: - template = cv2.imread('templates/blue_krugs_template.png', cv2.IMREAD_COLOR) - box = do_template_match(blue_krugs_crop, mini_map_img, template) - - - if 'top_side_scuttle_template' in template_name: - template = cv2.imread('templates/top_side_scuttle_template.png', cv2.IMREAD_COLOR) - box = do_template_match(top_side_scuttle_crop, mini_map_img, template) - if 'bot_side_scuttle_template' in template_name: - template = cv2.imread('templates/bot_side_scuttle_template.png', cv2.IMREAD_COLOR) - box = do_template_match(bot_side_scuttle_crop, mini_map_img, template) - - - cv2.imshow('frame', display_image) - cv2.waitKey(0) + camps_seen = camps_seen_for_frame('frames/' + image_path) diff --git a/Paths/minimap_example.jpg b/Paths/minimap_example.jpg new file mode 100644 index 0000000000000000000000000000000000000000..33122e6da60c5668be524bda04bf8d3f65a6e7c8 GIT binary patch literal 49160 zcmbTd2UJr}*EbrZ7eShIP`XsaCBPLUJwrVJ85saT zM*0I>%mZ`)m&pEJf7?rcFADO%EhPm7IRzCZ71ciFF@i?*IyBN|x(#x2agoo>B|?vB|%F zmro;fr>c|Pd>k#T;PfJbmhLJCCl|MfsF?T-2}LDk6;(BLox8ev`UZwZ4Dgbs$N=R3Qj667Z}nm( z>2>L^4XFR>MRqBW^dV=apu8?e#d6z>`l%nQp!{nZwma|gt2$|g6wJ}=PA|skt_mwI zi(vk$_K#-&-xQ1Z|E1agQtbcM3jtstCnFh;oEZQF;0}+lP(ofiRV>(b1(@*a>_=v* zNROAlr_|nfIy!|l6BMV~N5kt(t+ehyj@fTGcZ5vd2tcJ!ZZ+_;Pkw1sQ9#<%en;4b z_uQKwS~ujp21s0sWy zE^C(M8BN)1iLx!0^<|SK$eo#lt{oeRm#71;g`s&~raj6MFt8n$RUD+>MWmmQvh3wv zu^^7rGbi#sN(-Ss^NXR#;tK$(tPfawB6~(Nb^(BX#Cs4Y zr{J4HKmyShQ_%*?S|jS^nGye>E&xBUQPZ^@`x;uf+)}3(Xi-BGJ zx7P~JP0cR=pA# zVO+8*Foz-f_Fe!!?4^2TYaFBRr?Rr#4GsR`A-M2S|r{}_{$|L7Dl zIRN{*g@HiVLrt-xA1(lQGD!{OuPx>#ft%+4jCWE{!~eTVp{BCK_^1SgW%P^Xx3#-{kU8@z=8B3qw8 z_g~#O{}0_jW`_?i0M`ln=l@9af6z}eJQ_$%RKvT6*5WLd!a^Ocl9}vmeb-E&+TCO5 z4!_Sx_2PNhWx4{NL8zmjd}klsVNO=r1zl%S% zq-Q}Q2BJ6tRBxc_T`#NQFe=XrKqwxze*p-0-9;mhPM}MUgsRg2SS(+OQxm|=2me|E z7I_x{2CH)rbetLkC6d8$TB!oIv2M zqCf5%O7q}Xg!e=akI@SN8&eeiazPOPWVh{)=K7d?keS_luNN=B&Zu+HgiE8s;In~B zM`;PgXO}VrK22d1BuiqW`6k;8B@c@$>#9wEFB}`5&EJ01g?gG^-g&!G1xaMugxEnn zK?29(pu9g@aNM^#w`Q5rr(o0Q@~0lsfd#!>)EeRYrWIi+OGpabOUSVCSI>6lN#gHz zwwxb_zx3Cmue5X4|K31WfI-U#ILtI~r~d-rZcE^2-S9fwe7XMNW%c&+EUnXwv#oDO zN7@lM`>#fDMnw^tR?q|J=B4<^-D{j606giJ@HXUpZc+5s-~Ac z#xb@PRIF2hzl~We3+C${xL$22$3^#RO^j)-mB( zSeHw7N0u%t^r<8FMEtHz1q{5L^oM?tuD#OIEA=|gDy=lZgJGZ`Q0hKA+_J3i0^qwx z-I=|yo3TQTiAq-(b}r$`V~l&0M)=M+Px(OmNNc#4zl*N%ZP}%A-RC+g{Zr*>IcZ@m z7XYCWN_DfohXGH=_+5i`4kRnI=8f%+0}=CiAAqEvU4d_2AsK6}T`|P80zU4!USfUb zEhtFgNvVc))hdhjX=evt*+M(-1)!4-%)A1NX=X|~AS&S^jq+^9D}0M5_FsD7hgGsg z-gceG=M30^h9cknd>pPks`zE4z zEi8V>S%-Mal}c`(*vqcwS=C|ZtO@S1jXLZ(s4Y{ee7$s^(kAtx8^d!$W<)X3PwB|k zg7TcgHBkh~9=Mnb!15mg@BJE*ovq=xW*|!70uX~d_ER4t(mIhMg`KH77$S5wENRU>@#Sad*&4f-mti^;B2zdF6Idf3OsnS~%u3UIDg*D~WRL0?)d z99fZr%duZB0B=Bvt})iwc?H$OE_+%gH6rUL*X(tIpN;%3&0i94zGOy zD2SVZqK!E&0343)kkMRqjBg5_TDc6>C8Ir7YUo|!MGuJeoEdtT(L@6v-*8XNAhYhV zzh(JA17WKC!wTPu=QtJMNbI)kSdn=8O>vD@wcWB1c6G8gW75i2slc}2Xp(rHG-P2H z0Dac-3&2vQEKw%qzl|V)9+n66xd0$k9B?{?AmnAREUH4N`z$4~b_XYQ(q^{Ka%U)k z-@9I(O#VYgx8euNM-<@H42DVhPvzer^vAkSo(FyE=U`Bx__X%Lq)+l7E#6V3a@g|S z5ge~`0YIM;y3AAHFZ^fLul9A)=cEC0*DZ55-zDV- z1BILNYgC5qwVTxvwH~KtC&9g*O|BxEiWUc64fPAh(~b=pV>;dV5?K_!uZRKBdis># zmFCXAtx{-jsf-@#I1yH}c7Nmd43rCUbvyS;R==%miej6-aa7L>dr(&CONzo9SBRVS zE~iT}oU*k1Ipz0F*yb)AO|j|5T2 zwex_VO~TzIkx3!#`MhUYDa#oz117?mi^}EFT*mPcx238~zvks6uz9?unY)c@zt)UX zE5$4SY}G=S-EGwtwJWk`ocToty}Yjf{tqLSo$%#3!1EU6Z_8>Jn^Emg%beXAwXhQ9 ztv0*5j26iQ#Mtja9DTGO=VM*Zx&;dkv)f0ZqNiet}rPpk}Q zkL^S6McRy?7|3}0II-N;G{00?=)!ENJ??t}(3&KkvG))=v01iRowSuF^Wv^LD9LeW z$vZSboey@KxDf6~u~;or+mk#tP>72<<<$4BpF&dl4Bx*B9m5K{9|fS%WnTWmd)q04 zN6)y26^w97GOqVuB%L1)mYpmv{An7Sv-3ox4vOX!)t2%WEBi?#>uH%bl9nzXSgr*Z zwQP)}35x7C5ve$)Qq#}lzopf$G=k&6b`x?}eroClQzr|*_aF`3D2H*AZd09pvS>EK zP{?}UnQ@@l;KN&AODXn9OTCX507@p@^Y0WG;Oi4RjV!lA-O7TeQVNyJwFSd|q=4kc zlYxA@7ZFQT&m^4>&u&O$n75r_$M^i|FKll&RzA2TtZ#QRC={mwEj5@I`@lx^* zusX7r?SR+9b{CenUb3Tq*YB}Jhdh@QUR)-Vh>At+y2)HolVT>PV?}w)#J@fXIqR9oZ;+=zOnUd*cx5LMG ztAAq^ip;Nqlkv?(A|q^T?(SzAisP$}SDsZIA4GBQMSby(7hqZtl>K}~2@-+}C29tk zo{=2X+zedz0svx)1=hHC9K>*AKVlSCw6kQILcjdB+X|t4(`EO;J@cw{4a?Q%Me+C_ z?9~48_)YX5e9(5@$;t(wrXuuRk%YMs>8aC94!O0U(8gX{EJ&lSZZ9-Y>)=1)Kxo&d zF&-d!ovMFThG>$?F)c%nC{iK#4wK4l)Os#>V1;OS!}!j{EP~wP6V#HwOs%z z`%kT43($f5A|^whTXdX1*8prQGyq>3sn0`I%1EO`u<^xgo$Tzi)IF*6T5aoBEjU9-fFQTC5kD-UPN*ZU%aH7V*&@G-m zcKfFJ$T7%kXY!R6FnY+DM~n76%i)=3e(=+kA^FUEvhL@6+S1 zQ}|!|{lI$xI~s#=#`2;265N;Jj3i$Ltk``?(4Nuo;*oTWv)W?2@+Hb({BDqi1puH< z=6HR_u|WmT%8JTM0EeMrC|Nv9=EUfM!8Ap4m#09jUW;dg1dFLC$|-Ybvryj$@?i6F zkF^^_GLnot=+|uDBhSZ`S4xUjXif0u=9a#hvYFdv>me0_xz-_yn7LjlCF5pKcF$2} zANfxpeS_j`Pu|0XvSE>jbG={Zv(2sGPDpw$Qr;^YiR|USiQzkrnB1Mf#pq5cVM=cM zx{Ca8Qe#^h;SJO;ZRd6|@_!nrTc zT;IqIjj?1|q=Fu?pXLyuHF@u8RbX=hRSkGHXRI_1x-@J9CL?*bAL!YLR-Vwx&Vb8f zaYW#DcA@Eu_;(6~Y-9Ql^txQy_cZ*)(j?}~47qHVf!>Y?6(TJk8fn{xIr)QU8MDB^ z^in!x*@z1eaftgthQ4X}I#XEwZ6Z`0?fX<`0V z%}HA(e7c!GhoM_LKX63g+nji}KI5qN=7jea~!+VW_^^1MAYktYAEoycOU3}U7qTaFQFij_W@&ERdh{$nYM^?KTu zd*PS!4G97`|8pxyHV?vhu@{ zM$V;;TUUQz9&gh=mtpZ$yi_RouD0ie99}1Th)@OeoddcsacwPf&q8QCxqFieo1=Y4FT|jv4cvB4MI6j)Fzqr;LI8U&b1b)p$h_afdixStj2oA1 zb|05!DqInLf|RI!`$@g>Ma%2+7Hl%%Q#&7V5_x$Y=Z|5Tx%IIJ0V(i$t(Ca+#E|P+ z@|%c~Xo}=Up|PHa9m`e2pod|sZSaae+%No&v>%c(jzydmF=_q*3Gv7P;(EjA&$%JL z)UEcSmhIhp5bojowF0|gx21D=5~dqj_Dwn?DIPQbc1}zVUdcgI<5}jhN@%TgKS!`; z6NYjqY-cajB3mE&?XGjep%IoT3VvCe8840A%>>eb#R|5#U8nkjq18eb$)KC8!LH~1 zbX5L`n%?PQ5tDChh_HE5D%5%7#C;4}dw^#b>ju{N_T^YDP4{=vkGOe@YWOQ1+fLjm zpXlWMMd$SFi2=3kX*V{P^inupj`01Zi9n|nye42Zq%HSrH7NV_wE5QO2B+dv%{*aR z_KMW|w5K-AeOzp_zXWOn@vLRoKtjREW&J+&$`7H1K_AV?kUiY4E2;ZcZ|-2!JQi+Q z43JsB?q*nrD?>f=e*Hngc8gHyV5KzqWOgJA)}=DOgIm&>GTg5L;xib`?)7f@HBxxG=vJAOq+j zC@>^!tA1KB;lqr6gOiF76~IT&A%^^IH?Ip(w%?Z@vv#Yc#oNK_c#=p*+So=HA&E-2Q~$w73rXdLGoZ+Ped>`L4xr70_7hO8c5qLeg-)H)CmK4dQ zV%#z+iBsIZF}3sW*Rt-Si&|Bd1w?NYW`ZJa?t&YV+LI-Zy_Oo{gfY<01KOZwkj`K! z&a-P@k6H~RJw8T^dMv*_y!90G$fBg|fZ!3#exs|^^?s09l$UXD- zcgw^LiUegMb_bF}@db#t>@bsi6Xh!7o|G^^+f6MAZgmAWORj0_%6DA1|s@`VKum7yWlp<}WJ^ZV0 zxpy7CTU~Rad|pT7Hk0Q>kfw-TsKE_8-7qLDr$2hkznrf0N9xWWV_r7knD!~AQK@s(D3ds8GA-3 z^R}f5zhK0el_}E>%hr+LK)H~wHaU;?(Iru&9DDX%^ZT#z?#1_8O?^qdEP*MhiGC({ z54T9|d#G>LQ5m>`j3!Fq#+Djl+xhScxZtY=&@xbQ&wO_%gP!8J zZ=wKGMXPW#wA+NbX9rrcC0l5ByT}o_b!gkGX7Gc9MY8m*TrzM0_})cA+j10?tB7_% zP{+!`0+5jn5V6|n;~?qd>)ry{^{Tr1^-iBZx-s_M7T=bO9>R>{LvRlPrH@F3cBla467R_v+@f{z-NHRq%bIlHY0@NA5AbX6ZLn4OPN;d|%ZECRGw- z>q|~*{qKFeSrXgV)pqqu0Z2~~l~?no^3x-UBZRc@$1FjsT6Wk;37+Nr+*G#(!x4qI zf5BsA%COmCD7>81rK5y<9#fucmZ{{V%Kt3550h7jcYg=FMIHJu?|i!v8SYrq&_N3F zv8WAO^qZjo$$8fZ!^!}ln)mgttMv^FLw8*4acWM_X%VrdGq~I{5*qzD+F0`Innm69 zL?0UJz&c1|*7Ek#n{oxpAFbxS?VzRjy56~ZR?V^PmG47Ftg922z6)=h_Gc~tj0aXN zKoL^d`|XMB-t5;eiDXZUmY9OB#p)|Ky$yzS5re|+&*#95%;x2W;IsC%G=fjG}%G^%Zf7& z;z}N?GfwF90a^aZ;dBA1yLT(%?nzk!KYjZSI0oJfI>0*N%1|(-Z8$3!c&^?Lg*@ec zY3m!Oq!@8zJ&sO^7_eVyT#OY`2p@mH{4Tzl1mHE4^+3IS5fcYk4>UIJGmhgew4*ETfKa_0`ZG&tBo>~S5CSX(BQsYYn zLNsBEk%oSnf0YUbI`Q`3F9NnCKO$^ z0_#8E4@&9Df-)(}QhAtfRro$#=DTDd?QMV6y4bp6Ddy!>lwMSWb_e&#ny{46fzFmprS6_RTh?_Gr9M}Xwf=Dv$V4K$lAwEl!8kwPINyCCK*mWq$MqEG{Z}pkzaBohHFl9h#uo z7dJVzu@b@jJnWR_0is_{-B4Q|lQm{}5*uc?$EVIeCjG`sf#Z#i!YHD|FyB3X9{T7C zdw;Ve8z-$#wXKg!7tU4GB)ifolsLrueDzsZF93;VLF-7={ZbG{pu5{}x?;^hbW$jC zIC(;KAKxr7yamabLCVDW@xurr3IJA1U?D~|E9Mi2MZ5(tsDJ$ldG+gC3+@bG7C z@Nb)7okWH3O$#xlLxJ76b0Nr20e(ERV8@a1ctPjRq_^zE5)R=Nn7r7V|9l<=(cH=i zN5uNK@Wjj5`=g`^Xrnv39&LcN435k{T8`V>RHi1}mrriquFM9i4Eg_fANgZbaPNg8Np* zch+z_FK4yRt^0&`KwtQqTnUo;P{>rSEG{zNtDW8#@H!2YG{(J2fOp3 zO6zB%#5cPSJ49|NIhxPBgi_;PnRxZK3pdnNf3LqB_HEn0pThGSK%P|0mk)7}V zv&4~=;rVfE=QNO?>OOhjaPEETiWSR`+9PQ9PUx+K%mW{6)8}9N`h+Qfx(Y8HgaqzCjbmWFeCZ2Ym{El=sKZRmYxLrQ%7ze?&KA zbn$YPhc@dLcS&LZ)dT0J^wtPj)$6Mtz&}<#(Zp)WOPC^bh_qs8eN;f<*+!=gM`y?u zQqZoC+eCS7z=^#;xR%Si;3ZU|68h@5^HISHb*E_@oUGMLmP7-Hhp;zJoQSWm=%qv_ zqlDgy#LY?yve5i4Oe`ahC}h$nix|R~8Y-V_$PPo_eOgmJ)^4jJRFFy$=ga$9W#FgH z_04D!!0nZ!kIL%ayJWEEI(1z1GMoOVn44FbU07wcosf^1b3OhuC$d(o7al|8MKh(q zv?;x)jd8{+cH|{NI-P&As_r*ue$F!9*jBdOIUnW}^l)3;?{!ao@G7%AX$38o{hx%)ud)A zwe3}SVWml6i2zX>Z|s<$0H&U5)z{jHE*amKsgRN>l#wdYv-}~P_s*K*cR_@K>ZsxQ zA5=r&SB#XeuHvH3 zrNBO|xDP8ZE)Uy^{GV<$-%aF-S;`L$nWcs>;cSA915Pix&x;;2E)XVW#%ZhvFCSGnumU){y`Q3`vRh!GvdCMj` z4;t(5J}-B%GY~x#iMKDjD#XB^{?;OK_;P%agM9Dp!4M<{eo8w^C%qusjZYa8;0S8y zfoNh#fk0xbW7!C_bUV=0FDzJX*jVUO+NPQ&SvWO-%n?Sa&zQnF3|Aa~t#pKx|A^W< zhJIJ>6T5`aZ{QjWt6~HeLGFTYp;Ln-djq-oDvM=z<8}RuJO!y@9j(W>N4#GML9^wg zSoE*yk)>;MUZR1GLvcbJ2`3A7ON{WPbAsTm4TxXJ{ri+lQtJnb@#{g%RS^S2_Tg&$ zA*&3D=QdM9nhdUdX^#FsM{}kS?!`fwG_4Alm zPfh8<-#d#UGv3dn%9lWum6aRTEAdJcHF9DA0Fx3XYY%$<$PsLWx&HzEXUR8F%4*N{ z3!?k0V}G@zUNT#*%@2QZk%6~@YqUMyHCdIPe3WykvNY?$*!PI9j1Vorwt}hUMS&i_ zt6EQ&PMupaKGqKkSEhaXXe9R{X@A~R!i2;l&`6T)eI>U=WSl9loLG2dZ*TKks+5)A z;M8!KHCOWIGbt%WNhVQVVGnvkpz%pmvhtm`-Alb_1Z$wLwIaR%FQhj(L7Jp)F7ng4$ z<8(A%BO+#p0ApGw|<7tLBBD1NCom8^wmka#FCZtEr{o*3;-AR0k0$;jYlU-!r(+%T~)<<1t@S+L$dUGMf659EQ z7q~Z9x>A>!2|T0-7!LZkT0%1vC*h0Fb1S_7IAl8#W0RXwn4ta1e{V@4>g;_YwBcfdUdZIO_!?a7qqnlB$?ON(Y2jz@awC4)N}u0AVMEK=bb<0!Wh5 zP{r1%0Q2h^^HZYx_6DBYW@$MTXC9mn&i7sM(8tH zI+IE-2Rhto8P2XVWF{lvN}XZrJ-9o(A@kafu=n$~jZfyM(<`aiqBXv2yQ_6Ndv)*J zjC>xs=3Dd1%j8TuQ>8SaTZffbvp9YAqj8zhAUtDy_vBOvGlq?-#x0XfOYt3-xq%lalbc?JWtIIoK9JLiSq4GW?^b~|5{nSGva>yT=W4>ngj;-=?{D2?_RDs;Vi z?yqMCAI>I?F;*FQWAoP$44n%IrlV+cMP|5FMg+b2)KUZN|znAgc zrs`M4F+bw>8mT0BLI*R@!6V|vsyxt0YG`PXB5rJ6(gy{QV_n)Nccqz$}01TEZ> zHR~a6W_{YWXPoG1+qm(LQTaq<(~bMgspcU!N)4677e|N8e68!VjrXdVSgU^*$h@hE zHOeufZF(_TD*W45CdB-W$dBAgm2_7=wv`|i*@-bkgDu*p1jBj{{U`hcbZ}fjZ<>p| z4{xbYCwOm(tV>Y5qk|5xWVJrF zH;IN@;JnXuy#hIWHS4qS{f{R1-Jkc)Jp8~R^x075GvA$Wxv2Jo1l(=cPpq#yAAu)| z%+1qh2B%s;_F$FThm$0yj=}eKY5JNbf-OcA#0UG7mePfUn@YG}Oqka+XniUXf%SVK zs7s-px1dV}!{-KDrfBFge_r6zWy)q7CHkGQ)G+&kXP?EH_LmvRqn_1VdGkiHzKxtn zJ72M*%|nWI<%x?35)2&>T1LO4*3LEz3Uz%xZbA{+lSQGf{?_94=Is%%DSCE!BTicq zw_}0}UgmTT(|Oc*`1$D5w`+hJjVrWt-^%*|pKf$$Nn#bSoP;PMD_)h|O zT+p$P?AclZ?)5i)9JKNZa{>v;(R?2d-)o`#s0v6`g{2P$xs&K8D$x;-rV2PjkJmq% z8M`fRV{K&{2?81@eBSk5y=V#V>2!%lX7Md+C0s@(+o==MdIwftw#O3SP6s%N{-jog zieF$QLeYukjG}RzP(wXN^A|@5bm(jGs|!F@>>-)E?_$!A-n=N-cT$N*fCo>H_Km^c zSUHFX(!!UOK>=&^=xS?FW>%WMpLA`_>AR0{%$GR#=Bjy8tKQPm>2EmhrJb`dgTps(6YKU z4ps~B^DVnw(arL^UrOZmttw-|Dz^X&vg#il0ZNA(9eMNq*jhq0)Dwr-LZ>{M#LF(7 z(;&9bx^@hcH^bsp$Y1V5{1v0d<#_rE8W!9sQpa-p)KaGRNPzZ@p`J5hsq!J3!-X|y>R#g)rmU!~*=fJUbY zMuci_O;ggI%-4WpS{l1@S*LrW>uBrG7oMD-mn~UECJjCZdHX+v{C^8RupA0g)Qlzw&Cv`fN zO+Y&ph%89FHx331Ql3RwmMpJ$FPF*`r1p){PWR_sQqTCnc_~nTpz~C6Hw7oD-~Ew^ z3oSP^(C2Cu=KxnF)rc&yn>WfB= z9r$gE4GpN$mdY5C^4w5?M;=g8Bt92EhgT`Z zb$49BrJ14oqhF42&m35}d923|kwqhbl}3R zp@~#+1=Zgtq`)RFMKnDi3P0gb%w2EDf08+n==7A%m^#>-3xIQum0l_amDi(QkrDOv z&Wlwe8BV{S&t4zpY6P;M-l^O~-F4#Rji!DZ=9NYnp6jYRRc5w{u!mNRh$vB4op+;1 zK)r2Hcehn8Z_AUi0LG~-ic=po{iVd^aMdx@-k`wf10@(e(h-y$l^~qC+V97Z6kbgp zuK!i@cw6K6nhVScRaC!bQeo(d*3PfxkhQ2O&z=>yzth;dM7&7ThwGD2ff1^-M!up-Pt2_yF}LV z+hKLl2YpO20^II!On)~|>HI=%?bhzV<1vy$ED}Hz)+>AuX;k z!L^a$1r^ugVPokmyFj4+@}gL`Ydeoc3FU1|6NJo_jht*FQ>4k9k+F4?O=#u39|_CzIbn!h&cHU&=EU=BEdnRJ?}Fz2_tgQ!UDmlhnHwDk{90&H4lqGtY8Qmf`hoWh=-_ zV0AlR$}&^-xn{2*{P&GLv-#G{%`jk4{!|Jo2`Ug;$$?Fq)#O6{VU`3*z?9@eQn!! z;BT`&Sj+ZLgS9JRYU#ek$n2UeAWclCc&jf6)sl(1)nxa&Oxfgo`!Ork@6?CF0C|me zQ={_|tO%yPL8Z%XSUO~A-NanW`484jlRh|ibjBvFgfrXF5D47Fg>hLW zIuy1jukXW7kjZDOzxEOh0txSRv1uP{+z#U|e@}cuhK21`_${^MMKpWiIP|=D9_6AW zV;4_kZ1&CF4Ilp~VZ9n42Bp}@czBrde)-hAEGfO?tQ&Qm@mv&q`@XD~{Y;Kdi7wP~J)_TmTT;PEy5U*IyAehlYxS+@@G#jR$NYnrxe zod&nwbnx%_!r!g+J_TeUyZAO&&BPfg9;b~&Yy8NU}5t040V1dXv z(y+W3t$aMObU^I*@&yNya$eq#rB6Af?EELASd#4iGcj3)pZml1%>rxomy0g(zt0*% z2($P&1n_)T;Oq+Q8YTvxI14}dMS=;>vp*7$?+$|m;vIoM&L0wKM$V0h6Yv^fT!1um zS+B`EU&pActFt^jY7CNF zsv0~|eeP`4b4K?I^13<9WDF|3AG3xCpV&#Wv}jYOY*}`!;XZ+#P43*BFps*SWNxBs zhfw0Pv${VU(%=&I0b_^#Mp7#PMDiDVzI`DiUQLFKrXoW$l*SJ1JH5BktT$cno4j?M zQRv-3j==*iUIuTaLyitvgfo$59CN-R5JNi807Tzf_SSBKTn{ZcH^;r}ZIvim%koWC zd6s04U3K@7SI9n*MHF<0TmX{bOP^G71<;RFv_;!n(8bB#jPJap4m|-) z(?tv7M=0UaXLT?UyD?G;vqWruYFH?1TWkQ{?lyn<{J0b<U!wS66 z&`t&(hw(bmIjR=lbCaOVpYskfc7p-5q7#pN^cIf1AynPDY1kdYS51p^QT+94T*@+# z+GdA!!3%9|kTucVdr96(@`nM6-Sjkc8E_+fCzdFk~@%@w40e z>R3Dq&J0R_+|NM~zLqxhgi&vuePg*s3M@vK!M7~tot5P?pL1EaI0b}LJNJQhYHG{j z<;L4ZHj!wPtI>RxlRvy(;fB9eMO!$f>NobNc)zm`eB$ooH}ViSjsoIDIr?Bs{mv^e zoCsxZlXBj=Ns(>HfOu{|=oR00w?89Agd|lz^-0Uvfl43afC=b?&Pyt*@=jSAR z&Br-&;ICBslHVYyjtzF;#Pu8(Km(~@ z`Pk9|U-}FY8hl<#Y3+t3t1`nXS!~WC>SmsF5_0WHa}9ybR-wMG?Ij2NeCSHa$e^(0vcdc1s~f_3%^wZL zi)Zgo-O{4wb?!xq-IEk1!Dx@p2@UNI%nQO)Z@ z3%GUx2nw}YD-p_@fja<~a%Q#ZXNVNYpqMTsi+AgT064QL^xOMdy~&ZNA8)(3rHKAGzWT{Ec$8&frUhIp@|$D+=> zbw-Jw7vW#_Q+@`!s+LIe)$QQ&-iFBbArU2gul&FY=!4!A0neX{I#>fm9%&z}pJu4v zYPrP5i#<*R87F6-{ueXJ{mizjL_pv6=WWo)B7%*DVbeCIY##$uq4f^tcJwiXgIK%?s?FbeocqC70EkuIM^20 zqCUuTtGxUTfPSB$BqhJ-{ql_mk8bWQA~YQB7p7-FY`Uc1Y)nKY%01sGxgk7}7JVig zEas)5Re>~q?^v~j3pf`BJ2hj3a1vu?RkQc=ia}>ZqqUIvqG91`{hM*3=GAfZ^QjcE z9YRr;b4m{Kc!M|$F{7XA8-D!!eLJ(JCE?&sKktfWGL`=7?RB^YiMf#ny{yfPLt@*1 z9w?czZX-teUVriAo{pL+0bAE%K6-g9{W5#_#JJQ|Lk}DDE`gGv;_T6nSQQOQ{z6o1 z$Vu5x7+t(?jx-ixhulC`?PA&(O5H7PJoS+91F7j6XRCyq*Bc6F)Fw@z{XV28fq{<9 zf;Qt)&wLWe5)}vqphnICvQQPD=L&m+>$L)$k39mb=RC6=(>q>kQnB(@F(>CY;hA8z z^Jo=38wb`37lnCsv{e5ZLvh)X{$e}bf7ee&SfIO~EY$pP(uCc-;x!3& zMz3_`s)zGAbwAsdV<$Ax%QM|t=ogE8HwY{0!f4AU=`y^0`KrcfBzPXSG`j@xjT_h; zQu(}#zW`J)3Yg3&F4zL)yY>VU(?>bO^lfV%AH-=><950yl@{Y4S~NV&2z?=xbBk+@ zw)2?BP!ZtwV!Hwf|>&Am%ObqLI~H_*R->gU&XqclVdvUdR>@2rUG zSzh?23)^Y$53`1-qN!sb^0+`N+-=myhM4;^wZThM4x+m0I)d^(iH%SA41d#7T>3n7 zSAcD8RJ5+%e=~Ij?`bIe(v;V3(K_g(VNxyO&R}_^)2rXfiJbBcczf?J4~h-r#Tj;khrTY3L~*HpL!!qVsck1o*f<*YGMe(a z=*=eY_B^PMT))xj0g*1J02g?hket`O$Wf60R zrx2=2d%Bu5b6<=0Tss+Jvp`_4rfCEU@ykdNG?X2&7Fb^~zc^uDKV~8b`=Z|stsC1T zD}+os){-)bUMSP1zcuI&tDT*7?~Bt+gQxV|153}#JKQ}69ZF%o>P*B2~S~!8j-(m0E72%-*W$`JV z-ETFyWE;dMoa{(H?Dv0h_TE8Fh2PdNDu_z&ov46_^p3PBNEZ;0ULqhMAOz_pBm&Z# zw4i`Q1w^{^o=EQ^U3y142_*yw@jbu$*S+sIcjlY-9|mU5y(XYc*2z1G@XiDPs7 zM^DKqyMG%w_0uings$2#0puehpp(FXN$pk;QhZ~D*JWXg-iq)+oSjg8CC)0)RR&6H z)vhrrtW)$veNgN+P{%yKHXz#I#eca{Xa3;xF_zo}$Dq@_Em2Gw))hZ1)JLpMo!M3- z;?#_7KxUGe!s(kO7xQjyp;>+qlJ?Xx!gCbnw|o!wVS=FfZ(0t78aklEdJ+OS1vR5ByQIA+&SahjK!=FzXo*x z@_Wop{+tJ4!^-A=fsVuQGO`G#gm1!n6nv`bp4FaUGetLQTO!Hpt~U^jn(z9siwW;^M2Q+1SpjYNH>M?3OlFzxA;V;lbpX5ifO2lk}7 zg%Gwh97*511GUc^DE{>YZONZ&!pNwUVF9A7{}Ae)3S6f~3?74Xz=F^O>lLN(@SYyl zls0$aNHRBi>7sMEl6}Dy3QLBLw7*5BwMp)B}sVJmoO%%P-2V zIKv)ZP@wM++cWg2GJRG8sI@U>uor4B_~6@>N&{M8O}Nh?v8p0Oz9zy*Ied7519x5+ z<9p#XME(LA`%#`bHs3h4kMn((*B_q8Gt1`fm^w^yizzO5aXGT0O@&wZKTPV=Xx||# zLymsupyTmz?NsYG_H{Dvw|=Qw+I&vuR=rWtf55h#tNMeGQoN%ojz-txo>+|^Zr4j` z8*eD#ggGoS+RPa}`IDm1dEpJ2GJQT)8T$n{H6J`fCyI(S>$q3=kBvd&S5@iz1E)lv z7m8V>&o$o5m~)ztA>FU{e0654bb!Asj8NA53cEc4wx2~!pAQ&MoP>tf3LC~vc`fL< zO%9IgLOng72gSm91ulEjJyXWl?#x3T1%`C|)x?%1N6D%fubRmr8DFmX6z7NHkD-=o zfyGJQiI#erq`{!xQMKdMFn{ESW8MtBAx|O;{$Vr)cHePGP(#hIRAuo~W;4L2M4{GI@4fkE z$llIgsGzy9_oVRaR=@$!Wrf-RmDxcUn*1_Xm2qp41`cY-nj~{-!rb<3ZG55Jm(8*E zo($ApTr$(#bT+&S>cjvp5Y(#dZoIb%R9`H&n+6&AQPWeuTZ(biXHg0x=`A1HsB*o$ zZvIpi#VV1g6c36L>G1Cf9xSCEVvBnSYCh86`UBAoe}^mEriV&*DmLKaOQi$RJ~}2F z`1hHu99Bbx>$Q`=hU<{8NF_^fsdgOFpwcKVQX6gAe!3odaX1iI*IY6${T5iP&qQa! z-V$hL|40uz+y!EC<@7*jJNo3$ShzE_UEL5^*o)ciYN*H_lzrlOJUwV8v<)Og)S`N)918hju&7e zRrMHAAL=6{XXO%8#*bXoSM%XVzxGUKR)2WA@)>-4|FwKPk&Pe(e9vq`Hr-nojicC? zVjGmyA%5Gzp*Du0DJcKpOa$L}s@hwNw+L&n6KGm`8MB+cM1ie{SY`_@7%kapBI3eS zh@V3ZdehUYB)mSSdUt&|Hcw&_X;+l0`77!-J2i0hvfNeo!eXCD`7IEmh5b~JoZO;+ zy`q%D8H1^H$7>qws6Vq{EX5~+Ht4~s8fhiD>_|(9QwqSVqB(KY#tn;~t z(Sx@@t}5~#Hs^aRKLdsFlLXV%io_wTwEpjw$Iu{wOQ7f|wY6*)ftHsJqFzNwrOEUT ztPMU!-k*4$@)(I8gk2;=QfMRUR%-% zJ1K#3ZX&3URTMAur1ad21it3Y?8mMKn1OvHn~T8n&O9_QK-5n`54>>6^Dx6KfMfGh z#dR6mzaL>URW?gkf`8?pVz&65-$$HpAB5V6#=w~fwio6^sR=Y_mZ+W^Dpax1V~%QX znkkKy(PFgfA7}{R%Cqmiy-Lp6Pf)sGCJkb)mEMVNL*4T2gWfsIwwU;A-Q4q<%zEfO z&&L_s@mr03WG|;Y8C$89FAH9-3sh~k0EH0+RQkbKneBqN$EipCX&q2q;NE7^w5&1p z3Z6~X!x(|@8x0Wft{M4W^7S0L2fOWE)5YBhX-KCHWB-vV*|%&%8|)={Ub%vZ1WHxq z;GtzO8-c3*ygDE5=z2Qi*YYbq&7wBLUeq}{lz*w=U`%Qyc50RB}IWrmNaiRPtu%dk?efS%}#Z*3cKRsd{DQGAv zW~lrjmX94QGB|yO78du%%~0Ccypd}bqfZK@VMYlIyEGC8E;zPRKkk!6iITaR>{8$u z8N4Im;Crn&b)ftTZ5mq~{K}3h&v=zz!2;UV9s34d_rA%yQ;%Y1D7xao< zP|ro#hh!o(3{w^;Ph`I|NWffnhlTIwcsk!s$tB}HpPH{d z3?Q;*lb-6C|0DZB$_88>N-S_>titf~4XGv}8V+<$CPR8ghH!gY0EZW+WG70|hw z>PKvB8vBpJ*z*Df$b<83mSGR64zbORZ zZ-DZ%{-Eu`G8i+2($UMF)*69p<1Q-Bm}S0fiS~jb{%7s{zq9#6^*nk^{!088o&Q|t z|5FtI7Ikb_WyZ07u`eb>bZ~~AOlC@EKW%KOO0pDLQyAzRyqVa*T~2ou14b+@CZ92@ z3jQPeqo?;^P6Z%eF1kg^wg`=yVSxSV#n}lmsOah>|kmH6-v~i-y9En!qhnUQj+}ddf$-!mLXG^Z&9C*kKI)51V)XkraYvO$U=#~hePc# z6mEf8&t#(9}_?~+);VX@N z2f!|N^7yXdO)W3|YHWsIO4w@w7G`vV?@m0mm8Yr)!s(T}hBTxOckV7z*BY_<0c<_Ex%KJ-HWz)liXEYalGH z%v){jaAwp;B9#>6LpSYKz_OJ|gYN;>U;P0D*eh*To|#A_N8;KCRWRXUi)BqxM+vH#APn5PYDclradq~SO+ncUY{-Amr(_?qsBm@G$o zKL4c#y!$92I?ns1A#nPDCI$l|MWz(chr%}b8A0?^{an-`6 z+YMV_B2np>nOsn%XPJ2H{dxy!Z5Ch zyfofZCdrR+rj3bAR(hd3GGxS&!MBM7WB+Z!t@Mpp+!W)&DK4X%@2l_ewlY`$diT9C%M#Zc>JOqrX_=tj01+dDp? znzb37fea0${o~E#-F=oMmk;ZQUCQ8tJbfLL>P(E#*tgPDPowzn+X`EO?W0v2F5ltp zte~o{!p_*1n`N+<$!*WJt?~gw!_QJ%_)0_mR1e0vZ1T1DuT{U9=FXZ$9peHbZYafhC9`IWPgK+vXn?tF60xrc+G=m zt+UDtWg?u=aUmKwX-NNy#1YG8GUh4s$2m&IDB_NOs^z69di4HjrnU^(mD9EEIO=jgr)Z|Ya20W zzkxJpMG&qfNTeWb@(huT`W;BS6O>5f*;%&1TwmkEk}WRV;4`FCuGhImsk%4bJHji+ zxM{<@#+Dy|@H4uFGxY_y+97;9xeq34AvD&3(wdh@k$sn_Wyc>QwNzBl-IFGyp_pv_ zPhsgk{zy~4XVZ?&P?K#)tqYO)nl)Ci{ZbrnO5j+n8yEBa#4D=Q#rv>^OLcUMT`TVQ z!TaKq>(6!r+a*B{m%vc*C=4@+NyDW$@YE#yC9bA=+v@h1ANk$sJS0b5KTVd@(Oqhh zgg~%l4_J2U=$mbLo>f)}e^%>c*|&@BlgUfv`D4}iPMa0>-Cqj9vO%jl3PgzreVy(h zO@`WZw+1fk2}jGX&t3k5CDz+Ej*6YAD7bO%mpEVCyHHZf_tLtpl-HLy-P^|Zf|8;Q zkQ0ZCsHP)az>N8{C=UpyuIF>{q4jJ%s7TsY$~`dWA&4MK*Fl5OCv*TiI)2MxD1SSP zJJ{XkYvs)V>eNOph4%fVCl92N(}EzOa^E7eVXNnSM%4}UR#=kmTl4{eQN*h+G;t2B|F@8BX0MFhj8*0BIUf4ipH^j9mM* z!XEksyJ%AbV2u|lud;yUIga;_?zW>3w}lu+e$a?7dEpepy-#&imE@CjvJA#rgvu@a zBg5;4Z}@vn+KE+X(Koa?GUJZ+qMz?MEyrxsqPXV~zD@R88EilVj)6zrbqiI63u=uRn~voxU8$7zq$ zZ+}CVUZ$bt0py--cdUS@KyYZPhz3jmx-zpePUDvJ#R6x>c1c!UoHjDQxQFTIn-=#g zsHL;c3Rk1WS}pDj*1^xLXSh_$Er?tFX{4@TI9^*hnMwu^nWSV zq~|fu`PDSmxR#Xos9g$-kiNisP_=T+IM42}<>W3px@}gHF|rToE1o z&XM|8mhBW09nI^fN1-lPqIZ+Es9T?ZE&q7&d8*rmEC zQxlk*3^Q=S ztD5qU?8PSL_=*N>2(qF#L3({23~5jPN=#V`_i=)R91Who_@eKP2u41~ubVM?OeksfjXYeT~z&=llX&Cwd6=N zs^r$&zqD7ux=U~>5)gc6CH`z^CY(kb|5!&t+IlK(FpootBW{T) zi6#)T7jfaVgLyROLf@{8+-dAY(2Z$^up4vr5m z8jo%~t^ZpWQLzJOo!nE_S#C93>3LS!V7YrB>LkQ3CDdQ_V~t)TSjPK7<-6q=0~RK; zv;3*Qb87|hd3Lw{BX3zWU7JDp=);nm;Xqn%A+zi;_Qwr569Vo0dlZOtL1V7a$c=qB+4av_?GIj!@CS_&tGKhS5L!% zawKw*9t|eM}k1 z=-NJMvwbgM??2m%{4yWmHhg^!#!HaKQdy0>KJmR!EuINB3=Q&Q0Lk1*p zsh3~q1>zB0glK^^Y@>^wN$y;X3GVq*Qzmu$NU{8HN^HAkJkelKEo?jJXCLTh;24R@ zeYL2z5E6}{sQKVK;Z46|UNxXx@&|SQ>lam&oQi%hyJe`;(P)QoPuVVJsmjX8s&^FF z-LK)Ds9JE_a3U*$y#-n@8wk}1Rb81f8iSUzREXQBNX7B#FIX^%7zAWM=K=KRqP|O2 z4BHbXLxo}q9OyI)Hp_mMT@F3iH@lz5@pC}(TG~&ZzMmC?fLdjdmG83hADKIWXaqP0 z>W#G+6$PGloUgITbM`~)E62LC#PY&EndUq4WOU|Q{AH|@r*N()0}@st=Gr=;=R(vi ztM20)Z&|tx+`D{P_s3%=#+0n-GyNQ>u6`h=eBsrtR`A-F_0D1a%iX%YDce1N zjfZsw`?W3w=X=HVhPJ-qU?TG>UO?Yw^Y5zSK3)AVr|4F+0*MgKe!69sf^fy^;lB~A zWh3TZk;Jg*;)FzjnjK}0Xxq|4$=yqp;44~~Kig$ym_(6>5JGMGJ zQ_;?8x)b93YWicH-yI(qN-6d5KRQS6XUvfRWmV;AIIzZL@*TnE@h91dD!w!?=32Y* z?F*38W3MN-b=uj485YzX2R7Y*BA|4ffj-LyfJ5UjhWfanrj@P(|KRRe=^?ZBKw9qZ zERS~&|HqtEL)pH>;T?5h{4;2-nc>&qE%+s|^dh%Tz5S2py!6JXd*OE4M-~SH*AA4R<^`i+OoR?!6f$|1o8>Xs^2?)J*SV+1h!q?*%#l>{5}n zT=g0}@x$o(5;8?E)VL-0@W`WnW?rfW%(ZQ-g)gIgrgQgw2DBQ!3;upbF6~%3#2gI= z5?(E!RaOQq7r}4s$p0lVs6edhFq1X+M{j2`b}|1b;ima~<|B1H{XR17ewAQ)VEryM%{pqOd^Y|Wld0EgF?!hH zd>UncH&rt@iS+dwGfqKU7kw+gs5RR>zS>#|9IRLy5^Sv*+)$j95ghomoxY6qEU#uY zuy4Ag>(F2%VRUi^k~IV#6`ws=;z!iU?@kmDnwJJ87q)YGG7fy>pX*mBQ^?s^0YD}- z>Mnij41H@C+-f_^tnkaW^yc}mPbvlTHDx?qwS)THDGCY?`hU_tUcHWa0$$36)Zk;6 zPt)2!^e{t@LW{*+)=bXG-Xwvhwj0eKmi3Ai4}%U@_vbFd$_Q-Bn53rxBt9%Wwv;}i zFALw!l_0d{AwtKEKPk`2Q_Ka?^eNcd5>b#$$VWl|^IBS8b?omA@A#TL!InJXn#J60 z+Zkxl*#Uq;yqZJ{1HA;++98l8)^`5yJHL1`+1BUfy{|p(seh~DK}tLaAY`H75N)9D zF^${eYEL&^qA^jFQ0a zTrLD|Hth!6-Ley2aQk)ccNnr6^pC7_m~hX0mkq!QAZ!2>{RK*tB*(OH8WgNp->}S7 zjv@Xz&FH4Yfr9a%w+H|QUqXS%hzbi=31D1Eo8n8F+wP4UIBHdoxwi~xodQ^I@am_M zocm;95EdYnsv_(a!0w59CWK_1l67l6w)OWk4 z?>%rV-(yJN%WQ`*1cu%tP*%(lLM-B*T<5Xd+ZWlV|FPJx)W=1^A1*~Jm%%aDykbg= zu#f4~sYS;POr85bkgpw8Egtv9yj$dM;lt}24|FVu{Vsejp<_&SVdkY^f12q<88PnM zBpSnam`>L4^V*LJ(aRK!lObR)2B8KoQ()J3+}KXj;>)0xNc!Xe)!&~f`ldO`3A{@@l%tN z=*&9$zRW95zd4#Q+H5|CJ$j(8VVkRR;SKNLRk%d9KvXgL)^pprRO4bCHA60*8+9T& ztfvYWRkUFQOG-XUZ}Hz?>{GgFy(O1!y6Im>-KwhC$vmI$m^jZnuDe9T@6%CFqG&!B z)ONnoo|{`$KB;iI{D6RB-ePK#qte>!q!r;QE5Z%vVZ?HxQ_2@K|BD!El*slRX_vT~ z6Hey1hoU91!;}byUx$_#qwE-~Mtm>gr<#$&hRHAOd?Kmag2mJ-u8}#0**3Jm1-BQ? z%UHiWzPBpEs zAL))1J%8AgrPg4QoXN5J@Mi@X@D7h5`eNOH0}^NqaqJiB2oXg5kFvbMOUk=v40@Jg zQe<4KrI_kzXg2GCG234pLM3tjZS)d>SaEL9(9KZM< z)b^8$4%98z=z5e-^2P3dm(Ye%)z&CKW$O_~ZNO6zZ5YtueHcK`|AEW|MgZ))Z-Hi; z##WM}_^>YB!|&?x5(}@aI<^$QPk2n~L4ItD-tv4c^wDDeu@ieB+|M#6+2#sNWo zm83xjz<_v0pf)G zNwnf+4V;3gc?p2Xu8RaiWod6D(kiM^tafSMxc@fe)#{tD@Pr%l3@?ofAM`(8rG5^R zD&DcAwCp|rP-ea#mzF6t`iHHVs?H^0yz`CKY}f>uk7@A5t3H7I(q7V-A$gm3n22$*5Ux&n`X`?B-Bwgjj4cEFE8bdlqS`OH0W(wi{kYFPhMk- zCfN+(TK?n7#Wl2Z?v6Paq@kOA1h@5a=E~P=zxPkKh zn+1#`Am`-T2?gWb2w@?9ianfx!X|USeqPJAy#8?C=dDs&93dOe06Yl~m}M)W=o`%I z^6N3%sPFs(tQ%{vX5KxhELE+?NKFi_gT{MGd%?jjwY>+OQxl8WXW*-Zixv6ZUD3ZC?<%Ne};sSP*}AsanNgmp@s+i}Rq2V1)#a`(0%s*;`G-m~c0- zzpJn+5lNZhoCm3IHidVRr+4rigo-8hT1Xsc!Cqovyl>221)6(q%kFl4Q#Eprd=R!5 zmiok*UQEwp#=6{H7*7d&?Vz2-n%n}mPDCs|fD;ks;lf8LIZ)b^BV)u{LeEEc|M)&i zZX?;d=O_4vDl23MLUnvF11J^BYQz~Ku4%83Gv{ijzpdw$-&AXF!(u<0st=GoYqRh$ zQ;J?#W09J(Q~btT6Eu^2#9w=AyI;Cczu$saHH>?(Vey*zfxVk|NIyOzZA+lK$zVuD zWUSiV=a^|}vHJ!|#LDf>e4y*`Cv~k>YY(-qTCR}Kk$INPvA6v}eo>}%KW?u%k`vi3 zLN34VtRm}0lGL+T@^b?z(h;l0wRq6(P3%rPf(`*32Cfe+yK5v6;x!}e#@Xdo@!8{S zjQ3WNP6>VM-wuB8q+rpYqbp-o|S|GDX|4%w2Zd;ZQHM%_n0^IYDoBx~v-Mt7| z3@cHou@1YOu^x3Oo8FzD9^aoProBYHN2$#7r}vlEZ&g98Vb?40OAo%~fVN8q8m*4t zo!L1;UI*Ko`dOLjT}GjR*{f1kYL*cQpE+jqLAQDhQ7Yt-B_*7;23_(x;vy@oIo z;RpJ4W~r=+{4;Slme?DYf>F4KvYu=uly)*Wne;{eEZzLqGwYrM*}F5RhZ9V>rh`9V z?$I3%5=XgoY>Hl{pL1Fh3hrwZb=@2HJP5HG_H%J0-FyRtHd}(^#Veu=4gy5@R|j!S z1i2MaUA+oA%dTQebg`)O$G1t=FEiGuKflmQ{?MDP&NyHPL_~_aK-TradtH9^L~{@s z2tkO?u2%Z_{?&iB57YeKh&W!~egbX$D>i+VI(@#f)4Mc9&=41aM*t|A3qXiL8ph^| zq9=|4oPN@Ok%s6BP;uW?6||01(X8n6Gf3B9A4tzIdBqg|TA-JKG7^VZfP-~)7u!_? zlY5n$$xM5Sz?cs5rRj~rTZH@wlV;t%>2Db{y3Co&Q4PB5eD5TVX~jf;_ACu2 z5wng$#2E$31C4-zu}!8 ztk-08n!WRgogv&(hqvtY&v1qE=B0{VwhLjR70zi#J`v{OSm=7naE7d>|A}^J+BZFd zwUh@c2sd24(sp}llPHwFQnJp}WvmN35gfQtvn{Kym8Hv7Q_(~;9Dtb*|MESW?*6C(c>8Dc6*GrQjxHs#f3pL^8`+ zHo#|^MA2j1p&-F{WeBxDXK0+V)xBsBp}^;8Astg2fh5H;=@LZPvBeRD9O}o09bMYjP7KB*FcZ=E3EKzgAM6so&ngtsn+n{p89rEPROo3*?wQmaoKQ`HlG z2vW1v0MF3~=0k{B8lExKC1^O64$DU?8;BWS=pxldG^DKi9vE^09m5TY{N$GWze>*| zxf@41F5PL&_JsFN0aItmqi<&~lfS^AKs0zkU<+EBevRf0yy*0|I`%?ULEP%;hoK_5 zsog1eZUsAO3+F6XM$9AH#64O<@<_u?n0KoD+w#@hXb=wlrC^w6Z^ytRLHO1O(^v%! zk3Uq0huS-K!#KpMdbpoFD*Rys(UYZ zDH}nr)s-A+Cq>9Xin`_ACS;dDy4;4+ZfXCJ`l8ZHel^rR$`ogc_3^WI+a5ZdJU?f9 zRrD&9?*FdkTaQ-2J%&_7`Ki0uiC^)>+x2^|#QxTf-cFpEnXA4#Ao%evwKRX2srS%& z9rM!EFbHdr=%a@N6PYG=I-7WY=MFrZkjc(|I8M(Wr*kcj`n>3R)lUQl$S;ga#V;hs zCB`ddU3F!)A*uSZ9Nx`(8N0j3{(p!eBfwZLQ9xY;5dsF!-NKhqksdcyTn2(JY23Sf z|2r?IMC~8hd(RTUbqC1o>QyHB^R9CD4oQPYoYXvz*le>?U!^p$}ox2;Vw?tdh?-S%Pt5cmQ8W$5aRGftJ9@@+2i3^qt@EtxdTm8RU7oVt3BuUz4C4hhLtjU%z7k z43ppv3en-hhDMX9IKFJ1XYLld6wez}4iyGgjp{)&@2QO#a^3UtlIUutd++lbl;P}QV7B(u4Iif!H?|+Uzm(Fwy zfqIbE(Gw~&Q4d6+t+KS5Hs6Cyb)h!Ch~HV~>OU8;=AfB_wsQ#Hvmz8|w=z_rQg^IJ zwbSXI&MH5;)D|4iI$lgdVKw$b4B|Xo@PUM=rquk`tLix3KZOp=Je6~|Z*(Pj@i<;f zs9c-s#xSWe!GM7yE0)T4a9V8v=l8zwYDeL(el?{whj~g>eOte+$Z8T1?!LKfE;g8{ zqdJT@l5+DM{;X^Z)DVG}6)-(0x1z>W)bVv4^_nKl1k0RjpQ87qbmejhWEWG5;7Y@u zVQvzzrLOLx1qc^E6Td)oeE7+}O0~h`*w`eZ&)#{Ywxr7Qy-0y@Alg&_)|EMkMI*EO zAo={s`#}2)ml2tpdi+g4luJj$!jXTAlQ<3NZahY`!XE%lgr1+nbgrt!8BJv0>zWN7L{Hv$Ic{d^3#>#V{L~#6)oqRYYOX!c4$!?F4fB!Si+F z(lX}q*%AJFXyr7cPM_>wJf!NhOLZr!m!xG{V3Jzd-dBq{v0w& z*=8(i1s;Km{yDjzjNQvvFJ)t6lRLEBmi%f6RcY-+jy+lC$P^Ya7Bl^5Fc<##dXr?r zq5DAOYv)_}c{g<#r>7fQd4CPPZui9!z6&Z8iB62V+ZAse@BBHUi$yS!uERUQ6iG!z zcek=s@{f4rb?VXw-h}wcR7mGLXo~C~Dm6=UQzQ9r5qk-rF3f=O4aI0>D?;pXcG;u; z1D7uZB?I#;g|fNu-(SBE^tP$XG|X8(=29!J05IS-4Y1?ztaw+h-~{|3A;PvA7ZvI7 z(p`^-Z7@myUN6Jw#8J0|Mfa%WjOCB(8WjY7;2oEJgDqKsb|Ipy{6B`Q{t>;7T7EZS z{n~8bA%Nn;%e{cQ@wNIg5l|?f)ao#z5pwcYT*7ElOF%KD}0M z6u6#awFPaT7p*PNs;*o^FsYIg0TY_aYTEd4XqeeBwC;Bi(uzZ=4qjy z6P%?u_FTk?)hFg(;l0WsALJFWu@{7Sl?)6kcpI_GkAn{bq$pr)QMH#M<{& z4tf0$UC-^)D%c3z?r?V(cJO`YoYd%vl%JB%^wLVB_R-Z&u6R7w3MC3$ej>Wy^g6x% zYzA|mGU*)M?R-n^a_|yMM#7zGHMXM~lx2aCeFV(fnk!f+D~2qvaCa8R6yjbCg0IrG z2LH-l9*$n5Al?c6{;Ekt(I4gLDlh>U$prE6sSOVHh^^gM(NRsG;MR8)5cdmGs$UPy z^h9fkfs?(T^2lM0GTd6Z^ZLjyYT>Q{RqOiu!|^0h_%Mo|#2sk(q>|vFS|u*JhE%bY z2|?X83%W|d{=$cImui?#sjtsFG<%sXob9LT)v@D;E6ICbr@4$jc|ScRr#Y17wMa`# z>#K-|OCj2xfdQ#lxO4v(_ExA7*m;BlDQswKYT4Wm#cVd{67tB zil>CcZ}TD`#umzgZDdzEM@>(N<$ITn*OGodNpV+w7RSzA!mB-L``kkO9xrd}(nlos z+&DZj*2_KgJo0XSO_ISG-dtJaa`MHpw>*_hFETB$RBH0$-<}!qottOj)bSoolx1PB zD_^MC6K4pNZd;IuQ&6^E7L_*Dt`E`yjDLSI`OCjh%yGG6e83qiOxTawdD#+CM~^%kLz5(r?y1P}s?6R)SGbCa3ISP=KVE zm$j$1A9+F*>oq=prnqC>ONT)2XmCe}xdDr#c+G$Ohpw8XB4nl<&(KKMt`@#TJVS{& zoiWJ1Q2^LsN?nB{R+QE_vHkKZ7&t=%$-DZ~;n0fGbM#30J;`Zzq@$$vpI$AyO4Wxm zbKE=yb*ZY2mme@opU@!8W%^V%f)kK4cC_OHvpZ6U%D3?5!46i|u1^Jh=T7CSWh|py zGGcrmINgqPy4QMOK2igv-x_^Ix;?TdzPmB|*9u~lKcW*wEzcKT7DaZb7-k26`@s1S zQ#MQLZmNrLES%#iGcm5IY;CMHqhhH$64vevoq!5CJX|{a8Ss;6lB__-m1$y;(|Gi3 z!)74palnK-^Z9-aIt%ri1PaWHT+!(f9r~IVgP^MYVpL{aKb@OdH&o$2Jg*u?$i(g} z7w&?@N;~!bDKmSD>F@(-4Fjygu zX3K@31a3ps=?KXT3j?M4Y~XUaviTAu>PK^!YvKHM02NWpIpuD`rB<5w<%wM@u#0`o zAk$KtRVgU81%{NVQ@N8}4AXy}YZ!(dE(9=1*wFk071>b3CqLwn$kQse+LuSlT~car z%Uji|(ixY_y$;M3pIs|mk;wROn>gz(iqG@~tyRVOcOJD<*s)#X_Gd3GUUeN1kKz`I6>IwNsTNS(Re`9HG4R1fg^DaBu5 z8{((`=L($mzbbGzBzYQ!kqLGIu=h37G0aXs_>$a6>wo-ZCqj}7@ggv6m@gg*yP{5J z{sJXD%Nnl)APUVoB{hTA?J%JL3#RTP;AmftIF_`jXa_}SiFW~0Bs&&qlyy`(gW-)U zdLepKwO&;>KK=B_55LmwrLAx*yEi1a+mcL0B}?&8C1Y_ev=Uei32f`oR5a1@yW7n3 zm0#|?xxXwO?gDM~+8^2-dm_?F(Hs@^exMo9(ncii7C2HdI_7VLX$Yb83aP94g3-2* zvyZ35?gn}=O{$N>BJ0uLp@b`$&tL~Dg3*NpEi9Yq7@(6563Og9^GI2~nsMqxR@wZS z9b!K9_tImmNg0-{@##+ee8j4AEz0byC%mF3`E4egvUvotY)rtNh-w~+2Cr&|TAMVE ze?ziF=An~~-I36Hl2C@D$z`Lf)JyNVwxo#sg8FUaMy`J7if?^ep-T9tgr~ETMJLG~ zx1>^Y%JQtl$`a=vqhzCoQZI~!F zvQpJMty=bnnHglnIJ2HM1VNiy+Qw!! z^!L^IM>CVdXm&mlIk^2R=SFQ_n$rpwp9poc^$^)U`4qIs`5Y6nDLCoo6%24KDfi1F|Gf!vwy`%zo9s7o9012O@Aj>ANEHFAtJhn+W zn5`2;xfQ$@ZX?iXmQ|J>z;Kgozrmc`B~^M;ds9Df9C5X@#pn$HuCXLXGEUyaVmn7O z$o)OC-;$m5Jay!8cUF<_R;5Utvod@`mm02w5PSOFPFsUKgYPmTY0kXQQNdUWrOIi8 zHo%{$jO-Z=2RXW>U~;3YF0McriOx9rBsgGX%X@F!uVOk+2gj+2gKXMKjMix9PtEiP z);*-1P7Pf45vc3*OEGf19MK}bbiuCnqy{$KEInb4_1qg*by#*zz_JVRhtpBk^G#jb zNG9CJ5?Zk z6u*S2eX;%iYAhHm>;A3Q)f)QTE+i_-FI872Z#YDze9BsB#wbKSbSh1`ume*w5&iQZ zK6t8!j*ZCqyX6k=M-LgD{4G|wtx`g!abv8LTz$R7`{M7_>VEeCskcatb#m5bV#tai zkriXxI?6h{3~GBN0?oPOz87L7f0KhGVBzq5ev`f7ADKT|l2Ev64T(W3x8TakvqN7` z;nY04^S(a@W&+>6F>sBdtmaiSqXhGvhtzu9xMN_#TQokPj?jTSG>oz1`;ipN)h5~F zJM#2%=~R^`>-J;zJN_-XL!AC@$3C0OlI;BrGYcnmo27MTy292w-}dl|-Z1Glr}6Li z4{I_eW~a)_`p(^J>;7DFU*-~R0g*BIhA)JL=#15`k8V*9@<5+Ht^M%hF;V3CTBn75 zOVyx>g5d42H54Vr5gLQ2KG<`qAQ}+#R^}Opmk;8iF5NqP9t&kXl=wyd;Pc(c43zzR zWRjU}G})Bj1z=5P1>P;hS{<#ZOyrwqrH_f`MilCdQ*jR5w>_jF zqoCF%dt-y?!psCL(f$0~hcjRKmXSXv;x71y(@}IbwCr4W{v0jP+E|B~5RDSMlgMYxD)^5K`Fb{aP}RPEov#cA!G@4-u3wmw zMywrDEQSmow7GWHYf-wh@B$gCSzFhXbr(k~1<*agg>zKn^_rOSMZ+~U~v zWPOgL$~TFU?0r|lkdo2sG}rvLx8e(CDVY-yMKr`EcQ0JQnV~4wlj@ha;zI_wG6n-A zWU%>_65v3o-p9Nj%lpsFXpt;a?{ZSBw~Ct@^5oW?lNBzTeE&|Nh3sHYY6&WN1mz)$ z>>aroP|O;pzOk{t==X;}^Lc}6Y{s5`3s+#RXYvuc$MHPk1 zoX@La8j4J-eQO6nTqQ+Lm`zuIrhW+s190BSK;0I ziBJE@@lJYR;Af5eyW;~2!dHN%q8JtV-D)8aeLge&rG6~c`h$Hqy`yNf_bAf*walwI zTKApgSPVOflgL$BISv;HEanm8(ZqRp@FV3t4jFR&uhl+UYiv)F($ok{s$E80b(0I0 z3tIwR3#?UT->EYTCKZ(0x>xsbkIbnnXR?_G;1p{zSu60qaWlqq!t|ws=?S2_Kir$(;Kd)|DuU!tU`QX8yU|9R8AL*o&RzP;KA+O$^>&!RZFzUg8E^3~jhz)5Yv$PWG(O?4q|`p3O@iwM>2xxBdHs0!Gf zm7xd1^GZhbuRQmhDQTnUUW6;$2_vV!ar>?N3WOk-xu^eT3B_nlq{5-|WU^E%yqO-S z^w~T4?{(Uha3=)Ql1D3W%}39_0E~+8Iq?tM-|$-QeZ@2@w7%Xe4jr>9-`PM&zJXA0 zD6`~-T6qUsJ>MvA%nRjj))lqPKTaK)&RYjXUwJRn3^@|}MMVm8^ z#>=3u*9BkB3gm(-2j!}y?*|V2pZ4A|F3PX(7e!DIP>==@5NVK{0hFF0h5?50T=)Ii=Y0P6^L(ED_Pp5p-7J_3)>Ui$;v1ZU zS;WwWt4t*_jkW&oz?$tX3}j6rUo+%|E{C>7_^K;Ya$!|0FSnlEW`@X6>|LsaKRz!d zP^hAtKs5Nfu*eLask)1Zi2k@g?ZdZwVh$>YX!>5T4nYGb;&*Lm(9Hyjq|29hG4ut_ z9#^W5xltqCNhyP2hyT`f|N4sNp=&%>aKB9(1%^W~j;g7B^-rhFS-yyQA^-l3;o38k zsgw9e2FZ;%g{OSsjY~FM1E#h%xyiE)D8Grs>dflN9QZ%)HXEEij;L&<=5RRrtRZ@{OM8PLe#sJ=9glu?{}0}KYI@-@5MHm zJ?!d{nS1k?JAsB!d!COmTA%IKkIj*2zyJV#4*Iduoms)6vqyqO#-OH8pNPN5z*C;E zaHF_qx6SZ0X9Qh_wmlahUHQq07{UBf`#6{^**d-AUwon9F!CtzstI@%&Ma`{#kfan z74M6;W#%1!`II8)EXo@!$~W&U>NXaBft5sUrz>+zl5xC z)EEv;WE)%qbNdtUi^#7vl~K!X;pTDnI$Dil*O^=-|9I$C<2J&1$a38cD)rdA@*>c?aqS@>GZ? zhSeOGN4GK>VAKbSkKKV$D_hFd4X26qF>h`NHEUUP!19ygW;k{6feGB)h z7xo3%q@WbRFHW6=!(FiiNTO#-rmpMK$+$D3)&I-_?0vlW^4tEaLS5~NXqyu|H;^Pk zm_txUU%vHWNFfkD7p{*cBYE4#SDv>;@#iAPH=5(TZG}`4>1n&QwQs(C7WH=UDeCJo zg6JwyQlAy)-9`JfaIvS@e7i|KJy)Z+)0=H?CP6v-p~HDVm(Hfps{5Va1wsJ!cujPs z%$yCRc{A^oy+k6kP-;ldFVkRq`^q20Rh4ykeff|;tx?icT|I5nr)65hc6Zit+QOIF z)La;4di0XGY$MB^tmr%_Xht|9@+4bD5i@M?ZY&jb{YnwsP2eU>=ZO78@>)QIa2{)q zYhyhu>t(`+cgk%Y3Y;;6n2?GKA5}5a-V+)Lwz7|B@q)b+#`%Vj(hFoXxZb}Clp~dH5sT}xgkx{wpnXA2AO~_VbVO&6SvQ;av!6cu4-O`cf7C>E zcFN8?o0u8ot!1_IHxu@{h&Z%}1{~TwmfmHjw8p&kjOPxlm_8K321z!%p0w^I$rfqQ zDD-;y@(m1TM8DXa8L{YT##@zJ)^1EEo{Y%%-gWG1W1F`LNYvu|xL>dwXb?UnlCLAf zwQ>8HeYm-EQ|h>_ zXP?Oc0}9W>HZBN|I%tGRF5asaFU9)HxtN9R*Cb!_e_NMoh?`*e3@| z_U}G!$~=)JD)c@g)*k_7U~EXu;sg~XVY!zlm+|%erM+T4pkR11ycV{%woW~liApU* zmtY(kYLBm9+@3SkUCIhQ*Z4XVXYfP3m6e5A>f&Fu;dV?w3W<7lwCGT<}MVHxWTICo4CcXW{4{crCm94y6PKal&F-oD`V# zb#sA|_g|ut_?whoq)dGIkPZxtKuL1>7)Na=C(~8VWQvIyfB9g{-{t$nPhL+>EgsgT zv>!B~?xSQ%J)KB<0275FC7puJBM zl#|knSH(E?f^hux;U|g@lv!~a-N^gnkQ>44Uurcwf=UHU(gp`QrEzt=gLwPl81=wMtU@ljb z#_0NP>RVl;jccXye$CT+3wL&i)e}^|R9+UIld2?^>D;+vt1)SEZ2rmYo2aeP-U>#} zB&XHb_W#$ZWUH5I4?k(J%d@)Fd#SK#s&@JK)9d0kXA)Co{pNSSV2a1{>9{_=5J%vC1Yzq>FkLQOPBX@?p$v2tvc>)UGUW-IE ziVGxIP*n6T>KTzZ_wU=l^S6OcN7`QFnpFt#DCj1RTD#Hqp33^xa<_H=r_#n8bt8pL zmCaw(Ie9s?eE_`ePg8}j{_S1ApzN@&W{aJ>Arrc{JKed0nrO=;s8#2Yhn)&D%q>vC zyM0TsDFh*{C2Z5=o0A}TY`|$m@m1RUqHjKhlACS_28V4SnVsgGrx?fe8q4XkNCJ64 zbcf};A1xC+wQ^x$&DOegPTG;F*A0i?e~Imqb@)y65tM}#28=FOV_!Kbao*7g;)XRXG_R>5A<+as{cIY~;w?(+}pP>#}89Vh~qGDVHZN3IymvLa2qYK)EPtK<` zs(ywi$TL=U-8h*x+ryD%GBK)oa*N{X4bZ|c#6flGup&Q2c~lCGr#ud@pP%;4v)>Oq zr6D=t+6CpnC}8%~Y!DDGDL;U&gTVju(9M^!Hg4Cvwza)FzI1SA@>7La?z`u<)^m5H z&CT}s01eph5E-;m0{XJcK*ITgH}iIEVzWa_(dP#nIfW$LXHw_9!Ji_kV;L|u`>wMk z&QfO_g{DK82J-w*A7in|5r1777NOIaoF@548Mx|8NjxKNvvh73PT#^MQtVwgKj3D+ zGJp2=p?Gs?1Np6@#y8J~?D$@Pc>9(5Gi~eKe>9!ESXtA&SKke@9%kU^2$YAI>&|deh1=*RnHLv*k-SrRUHApz zSK=u4&?OSM2}06HINp&%t-Jw18Xy_&vV=nHl7;|0RlNGa|IW zu)YYo|I)u0`={9c%H+z$-WZn{&g^s=aGDc6X9QmDi{tpeL{I4r{&Q#+EgG4#sr)`j zwEtLGm7ta_52OdMKRU+B4j$)J;kkfU)xVO;E9tu5J4d!{BHZS1_-;PC#;hJhwcnm?XcaB%UKlsyc%Rm0 z>E5$*g{Hwhn-$h#&_s?7(UnHtV}J zAIPH)J8*}qGP+uBmhur^>WexAX8|wXoRDM*7rKq%nS;D)e*wcCFFj8)|1za|S zj$c=5U2D~UiLQ&bzW_Qbr*k6nTd!}q&eF8ja5(zeOSrQ3vKFQC5wC)%YijX}h!yiuOngXh)7R@;+t7F%kJ&Hx6++Cx>wk8-a^ZpWz zJ!j8Zf#(CFTh9O)jM?^;XfQ^o{i2&j!7su2?5*W5_yO7Iw= zJWo_R(qZW$fBi}|6Cja;EVc`v3xve4Iq;)(@(Xq zR#>r=)q=@g4>@nwLku}JUSbE6K%p0e*pRg)zq79SEGrRLk&XHW{evcPQ;siEmp%C| zj2CrjZYA@wP8H=7PG9^@Uv!;F;W{pzwuQe#XO!>kWzeu6GA<9_x$~AgC6Dcjy-lv4 z7iF3+vwJ4b;c3E~qyzx3J{S-HMpL(FgdeblA!2Jo^=Sh9h%;Ey+hs0-;!~KE1J_8L zNeC993j1D)`luB=T{ZUA)iw*7(jPI@lbfuxWnqre8Je1w~o_ z%=r)}6ww6>y|@~CkEF!8D>J)DmyIvEj0J8wqcv}`z9KL&s4zHNRpc8VlvE) zmN~AAx`NuY`l<0IE25TQj{0+tRtxuP3zp{~ygw_x!HqQ2jHg5q8#!PEf1^tK=g4?* z!g|qTmeCs!fd6#k-PJb&CtiLA&x-!N))yxwOy&3?#OOt)R$Jn-1?d|7{Th3=uVsB? z_p{igXTg``fN~TQHU(pS*aeDia*zK8o=8dR0$kHOpM5y@Jnp)jV?NJ4B%&UE^p4^f z0sMVHB?zJHQhv9uPnqiQemy2U()Cq1Yol~evE_lteMg|U?_M_Azv7eT;7ZKG!0=Go zKIhS4P#kp z^s)W=#SEygoDw@S-6HS34vJ&vo^4Uhb(R~R2@31P6)0McX-(0W3|#MAcDAej!a@V7 z|Ft5NUsX%CNtBHkoiGI4!D_zxDV(q5BO22L@!zA{siIBdfeKzl7_NuDWft>0k zTX~IokD-R`LEFt1fmM%r5)?ytJwVnGodPzXLw$5TR-?Q1(TjSg@|LIa?SoC;ma_8` zPq?p-o#h(dp?EMJ)L3DqJ|N}a)*7rnu-j(k_oBVCjyOMX+927@ zsd?FK{}0s8Y=Wbj3{NkQEyQsas(qbPm5ISiITK?RTg99ewkgi50|! zO2uqJnOJ!>ZKdjWM@qcGtb*jS#B{O)0-{~|kV5;Niq*Bv8~xlKR9(tJt%W^sdL>%C zkWR4ZJ-iC~FnmU{+L@}fq~i@6Q+$HGL;pQ0Inys?3%5UAEjoK6lqY6k30=&xyWU$X zp+NJ*95&b7`&vjjGDxX%b+*&&rA#3_^`<=JOMh8Fvechu0+li7EG|1ilqJOYJr z?1D3IH*IBd-MF+#_p5$@oPHf!gO4K-;QwWt@L4E!wuSSOmoS?lHD#8%dnk{U<3s`1WE=`KWS;_i@ERiarq3$HVCO`>Fs z#jjL%D-v!r(Y}qaf0*al1C+?f5>()kn)eZ$K%rQGw*yjrus*FCCdo7vYvFL*A}`L@ ze^=)bt9_W}H52uXS7aB{$%(@j3-Va&t15{9<>O_*S8I%JSLtqcJUn;ot#P}aemcpk zb4Na&wi8wm%;BUXQM57?W^z{UeI~%p_^S5T2pYXJFKdrgN zAP>h=e-f2q!FSzEbLvuwzEvgRZoO%z zdeq+yecvhDq8oIwObZ=-yL1v`*z6`eD>!YjwB4)=bVg~`#F&Tce8iJOYWVEGyAi!W#q@5CH(}3eA^+FWMjZgCG*wgXVR_q4 ztV;Cu7iZ~uh|qfLR%FqxT;{gePSO%(a^s9gXi{Gj*sR_;Af4UWVEM7#8^2f^CA)-s z#XCq=aE=hJaP=Q49~X@F5Lw56YSNa_*L@%!As4nD=Zv z1XFBE#5Y57hx{g{)sGCfs@)`$QZt}oYqR7IFF85^P_q=?!X#@SA*Ba3?=-ARzRKp) zRoRg-N{TUi%7o^S8@_3R2dG^nt>z<4%T(<&(NJ;vEgYM2w9&Nq*;f_Dq*$1(Vm{br-$IeC$-i}M;@IUeub|NoPeh`D%K@#HdS1w!tcZoR zCB{D`e<+w|aO_Q-=2q;Ra5W#_8y0#`$-a@cv9_&N!|#w$J6PFgmi1p?l6k~Ze9v?ODc;(j!vkFn8x~6GOuya`wN{BsJhRi*(Nv-~srpYw-R@Ay zO6^J80D%vk)*%d9hs7xvI_?ugO<Ix`3-I(PLTjb|pBEEEEBb7t#Qb5{^3a0$Py!c34_k6fTkQ6q=2HRhR|i(yHESD0gftwWYnOIWTz znAvmjhbm`gck;WbJ?;-SrP${5MIBTB0uh*WT~h*m?e` zx@S}HJ$cO6Pkrx`s51Wl5+Jxp@PITOC;szl5b~)&W2oF!+eBHCjEXVgBo)TU+ z$(>JhVJC3%wkUz}`?%~}9e>~A+9-~_-SPbNXwoB~Pv6qoj77x*;+!xJsi4i39@b}- zRg6tkg(W*?rGDxkzxU8IM(>T>)tgk9(4cop)F(bTCWk3fxRY@?|12FYLxf0H&(@^@ zir3W%#?2jGutvQ95woYIvrZhzVLQbP5YDyIeciW6uwI~GptMuI~TNbz0 zJr-{u^7KO;|!1 zCzu>0T}Yv8R}z4D68x#~g-LFryupKnCFmJ@me~rN^tAEB%>Y8{NZj7LHOThP)$3W7 z-B&^Lgk2LTKNM(OLgKW$?i`v^B1nr2aVt|LEMG<{^(zo@QzOuX^RJ8t^iB%mc% zvZ!RAd*9cHyGe3ff3HlE8@rT|U^IwPDHu2anhUNLKoj*nW*N zz1kjg8!zPz_CWOST3L;#9I?skRcCB%ZJ9(Z&(AX^YGua}Kj7#2lJkyO7y27wj5Ao} zh=r21;5;#+Ppx${kiXo8W5!r`?#cb|;Y<#H;dS%mk1j+AMI8>SJlm_iG~StiQ;rVq zmb3gLTCgKW_6Z>0iMZEX`ZRoeeO{c^eG9Yr?kF%yQ(3~!>7+rA-ZOK5 zv84qpzeX*pd2fPqria% zO?Te*?{xk8AhZXYLl7wb5@9IUut{X0P(_@V2jjd-e**aP%QAn=Gua1E%-jZg#kAD< z;|oqwVXPVda-YE?JO6H=pWS1Q_vR(9_rZ z7QH7GRc3In)k?+4cJMql$R+6~_O{1*d;~-u9fv|GReSh*f7= zz3il|I5)&L)GxYdJHBwqBGOvMs)>1!)Q}lc$U??d;cWxVma6J1qU}$@B2cQ(0-*eT zr5iu0hNiEYlJ?z~9gBJRDmwuBBieXKd7cc#4X1=Yz^M#%eKhq#GGlOw$aRteP}wXtR(VG5qzdw*L~iYNSzE2(b-jGFXdjdj zL)Jz8Y5C#LHJb(|j|(tqn}$%**!6fFu3I$*jS%uXLU~EfyK_ z9_g?NmS@Uhtmh1SE$XaHhgUY;c81Dz5Eam89^V}IHILQ+cB{m@ z(rj#+o92EG8jfx)#~G!M8aCw{Xw&Yj=S}L(&Zgx1&L|9k?d}axTh-73Yi~lOapSS))RR7x;!FbG|2l40l|GaVrcbB>B4V1o-*MFK5P!< z?4I;hibo?vw9jbjhbITQq6L|o+16V@fV8!0V8zb7Ug0{3?U!>#VT65C_!KUR#r=Ua z=UTGX9g3Yyo(%QNDAqtnQ)5vPv(Cdf8zP+kb?RIkTvg7s&aNKGFt3}tV^}@v<;l|9hg|PYFan< z99{-S<-d!4SrDtfk(^6DQSD5aL2)t_rz`-kH|S6vOqdNT4BSlsuj$>ZrzWw_?fRqhHSLCkNRU8XO>8=dSzizrh+fde0ZD$6B@T}H zaN0t-Hho*Mu3l>5$>xo{di9Xu;ZhRL_g(-duS$-eR;a!k&H{5D3!UGp46!YSm}1W2 z_I(=J7gV1*kY?Vh^jK(lIe4s9|3>q+OW7hZ9Ulj4Y~ihD4ux9?KE1(h8_cE5oPg|6 zJyY#Lc3%9?s>MtG;kSuBwqPT(3}a1{1^ z8MOOuTIV^$dR=p720NrT>BGRdo0r+;lUA+6S>HGBQTK50s0O^r?b(&ZJPnfrm*Z+N zN!=?y4s zFQpCyEkM&2+@PROk8VlJGu&KTcw5MB!dten;FVn7q^G~6cU0Aqy_9RG0g#Dn5m%5x zLa5M!f+Dab$Q51%0NRhcHhlkcv!S9S7$7L5AF)Jq0t!VZZRD{2%0eK4$@&+`IQ~CI zEf$dHX99T2ai~Uyiz++QB-xepzmpN@M_}nvgmOR>ezrW$g^n`;wE?MY)^03;EpAe` zdh+yHHRFEew`22u=BgIkmu&f7H#_ddkHs1h)MEc%#1gXlDhrN{|EWZWgL)7{>?YTL z0BuX>N#WBma=^YJuU~7RE^&*=sW*^<>Zg|Md8Pv1sPp_cg9Cyr#dss5+I`zb*rMFX zvKiEm{$Wu^$ydH#DP|PyW^r~2-v}n#4|GO1uN6l>!`VSE3W`yO%+9mYpPxsvyPX{I zW*XSXMu}|mfL{$<);Vq3uYOh(?FQW@ut5SQ%M?OxWu(GA3sU1P>n1#&)7n^yk%FE5 zVp`nkWro7t8g5vnps7jl+)5lX8yI*@i$iHZVK-ejt+k^~YjEU;m^I@@d1J>gWC|oRDyx4$S6v_d2G* z1y@(px9GO*j@>@G(v8)?ie6>aT1#EOVlBLcJ2pEFZK$JA9p$DmYGd>_V#b}ZY%%iSw}C*8=f;7yb`*z2x*9h9>q z>JN4)F`47&);z-FLm|Rz!HTTUfV9P@+17I8U>{($yhND~_ zl2KkfUUaY1gKy|BQ9-bDuxVke5+hEg56V`JE~qcqpDYvk-cVOv$NAtk^8+a-Oy#)N zEr$0m24hG~H2;)FVoQYT1^?8V^llfx1je46aF?_RWrW0+?*ku>JQ5^;9zsd)@%>1I2HHj11@gZ*o6B@ z!}Gt%L4htZsX@G7CdG_3bGr>b#;m^0JvSQyF}46!poAeCmUxs#tZ!y4n~zR+<0Sjw zv$&n${Lx>pXi940R+}U?1Y+}OUrTHu)iKm~#&{I85+<60{;dj@N=NpmnbFl0Gos!N zmz7w#U*mkjSMRdbiz*JbaSSnBssn@zM;EIFdd4Vnb%j}H#-nBjxa&?&|3DM)@>OXD zxG>7zMazTKm-p;k_EBv0NTm<0Z`!ydJ#Zp?`;=?RP6{#rC|vr_Dyg%uD7$*!8GN5h z;W&<{v<*#Yn=d3p!rsBqq=0-yV(D?qZ}RwIOwNS3wt$6oDcmvbRsM&rBdw)+KWMet zsB#_kMFNE73t-4Vq~Njz>Aw&xg9B6l=Agu0Bvm#l3gX% z@xz|-t#5gYR+dYQ4oCKhye1F?*Y%GHV+`k@|9)=qI2dT(=r7T;wsvJ439w5Hm<-%Y z6Aw*JG_yQtg2Y8o*-t9GeBV=s3^^V^NHaa$M_*m>cZ2Xw{sh9!p zOTs4AFgy+A@!w`)4C?~uelG9}9s(dr+0ME`&_;}d*a9NP1XO%-{Z^Xw5~nZauizK{ z9DPVQQH$srlb%?%T%s2L<#b6a*S;GJR%?M!Zw6z%2tA-XzO_>|9CyGq+@X)07Ub9LhRFwLv*_Z)`P0Io^LdZNYToaK&vKBrN&EoFALdSrl zt_#!FbvL8^v?jmoZ!S07p8KU@6T&`g29;MS-}qskbwX0lw+)IwE-HYGYj_`>xnngp)XUd!(CpkyfNHzE-E}!@&_Az1V5Wc{$2!J-QjnvcjuWR z%Y3NGacNxydi2$PMRe|;9O3DT3is69qT4zI3>BeCOP2gE!_K(HSB(Pa*SQQ>17{NF9+Ik zY$3C|_vv?Bbk{NmEnST>J4fuWU0nFa(8X`WBuCCxy3Hfj^yzZuUU4tK+Qhmc%~F?? zlbtc-&>!t^(kxcDqS8j`rG0;YVSf+r)5$*MH8fM~39zb!?y6Vku-ZG{7#S>*UKJu< zi(l%j_6lH;LL+*|0Kq)8HhfOh8aag*`rg(JVodBfDHVRLtFkcC?>4cdJx1^NmPP!~ zSMZvREdCN#eyI=rxpI$OKU$duqFSM1on^7?qK|cW?WNtBr{0MA9{0|Vq0_87O#yX7 zMKVY5W#ybuaNAmPu7HL=y5V5HdaAcVC)KQ2FsT3`8Z~NQ2#CYPpjcchHW}x~>uMBB zv6WXX1O@1<+4@j#Q;nrrczFS>GxE;()2QCXcPh;y-jZMVetW&Os0$@?3n-^Id-nQ! z&0iwv71M=gw^O&ei@Kn6loy!8kjx=iQ|79Fdg-bs$!ZCfv$4gF)Lc|N~{+E~x zvJA(GL9L#JA!(iNk~X7ug@T*i6iowIJ^APO$cT#VajnfjnxlUmsO8l8+uJ{R;Bl0MOx2kzq}&{O=A zZw_0LfMKMT16Z+rS7`vjLC69#yh88l%VzERa_$e-Sx8sZFlbGBM(Nk%ANT0+b{b`*wGcpPDze_7-#ziH0bM4T z)Lh&}bxh=vuY2tLdV8dw`ZYF|0REV+BK9p}c`-*<7*50m;6y9Gq~Y z{x1=O#+(u_Y-|*W*5A7UgblNztAAWur~jB(|0RL*v=0yes8DeB40G_kq$yb@?b_2{ ze3{V2ge?0k9|nLm4 z!*OuJyCVV|Wz+W0%aHpYXI2fGe0sY5mq_R`ed8a8Xy7@Ke1OQ2LR%qj7V$ry#@_77 z&yXP4AYm(z>>r1W1IQ5%V9WkPExJvR+65rh==iH!-oU<1{+H@DqQdg23Ht?gi z#+;}BJWBE(=K^fD|8WQ6t8f~G@W;T7yZ)CbmVE@KOEz1GNBrLl@c-`CI zH6oHESfUpe*+)bJ5TE{O747jKx3S>E@2()<*bP6+n(6^hkIjs~KIM&n{M*Mt^`YP$`{A~LfJz*=Ay3&4* z<$0j9Jp(mM_dfcGEj!(?$Xy5E(XM+PzqhYUfY|;eYEy|4SW2+xn45>;aR4X=8upKm z^ag&6Pp`6pg<>p5`Ros1a`yjyRAaLJKldP`{XN&z=jB5Oe*gh545I)5 literal 0 HcmV?d00001 diff --git a/Paths/temp.jpg b/Paths/temp.jpg deleted file mode 100644 index 79f8d8e2134faff70390c36ae8f9cb1b6e8fc479..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1791 zcmbV}dsNbA7{`C`p zW@UMc&C*oTjPizAwxuS5w!D^WPWPjgqS+YKIom(`W83#V&-c9Vd7t+^-_Lo}a`hOn z(%aL^6M(@00H#@hdIWF>;4rPU4%Z3+Y8{C{fCv;4g?d+L9SjPsgGQk+Sd5OYR+^n2 zR##7Z(0=m%DI5ucNL@4v{jTLdnffk(!vL0mB?uz|a2yQ8!PK3AnI7byPRhs(?r|G0|{iM&Tul`PRBNqrg6h zWEaKF)X`mSj5i@ztpC{Z6D#{opE_)I+~U5?!*lx%FYlmT!Mo`pj8N9z=zZ*%*!>5S zk`JY%rX9{Yej+>PtCP7!r-f&VOU|Au4Ord}*gm_A;J28BJRlpD>zHG_AcZTw0Ux0N{c8XI);8?TE zL*B(@ipmk{7!&7|v|rF|#YL7Y5^qz8i_gt3`Ri!(iNh?W2PrFgbL4IpZP?jf7* zfAtWQ5^lHJHf|z+V1>v|Oiv;H=*(q_dvm!ij^&alHIR9V*XGabJ^!7V!5_w5&Jyfk z?I}uvb>;Sc8(&7G^{CypyKC~j*4EG+hr_NN)ISq_^Ssp+TlTaX0H%f0#`}23c|$Ej zon|X|y%7#dbo2qj_J>S{^pSCt@CFFZ~U{~29M?@7xl>%+b+E9!GCZwTpkh?Bp6MmlM?r&varI( zqQGEd(ulBDGSzx!Bs|$QYx9Id>7yzig`$h$^hyn6RV){YgI<3zpP^z@9gR~_CYUFN zzrl2b^haldL=-Lz)?%_D^}cs?$4?$c@{4j{O?52+|HOB-js4S8P*{+wsbc!ERlU<$44|mWXM7!tg{@Or(Z(aJrZ+L#2D~;u6 z8lE@H9$R0im(C(=yYb=|o`^%*&&PQf4W4xzPpscHCHslFIMXa3?0V7XpWBxvR^6&+ zB6)ZDbwlYEX$@xShpiv3d%%xNYx&BQm67xkri!}FmC0n%VJ9*47lhn5Jccq0hvZe<*{(=S-S=7aUvgzL5BMcH^O}=?j3D{mic5knd=#GRSjIVp$3LtP%Hife!~P; diff --git a/Paths/templates/blue_red_buff_template.png b/Paths/templates/blue_red_buff_template.png new file mode 100644 index 0000000000000000000000000000000000000000..78e099ed1e25d3862477ab5f9d23d5fc50e98215 GIT binary patch literal 7339 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 literal 0 HcmV?d00001 diff --git a/YAD2K/test_deep_league.py b/YAD2K/test_deep_league.py index f9d0d6f..5ba118e 100755 --- a/YAD2K/test_deep_league.py +++ b/YAD2K/test_deep_league.py @@ -103,6 +103,7 @@ image_option.add_argument( '-images', '--test_images_path', + type=str, help='path to images to test. These images MUST be size 1920x1080') npz_option = subparsers.add_parser('npz') @@ -200,12 +201,12 @@ iou_threshold=args.iou_threshold) +# Save the output into a compact JSON file. outfile = open('/output/game_data.json', 'w') +# This will be appended with an object for every frame. data_to_write = [] - - def test_yolo(image, image_file_name): if is_fixed_size: # TODO: When resizing we can use minibatch input. resized_image = image.resize( @@ -242,6 +243,10 @@ def test_yolo(image, image_file_name): size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32')) thickness = (image.size[0] + image.size[1]) // 300 + data = {} + data['timestamp'] = '0:00' + data['champs'] = {} + for i, c in reversed(list(enumerate(out_classes))): predicted_class = class_names[c] box = out_boxes[i] @@ -262,6 +267,10 @@ def test_yolo(image, image_file_name): right = min(image.size[0], np.floor(right + 0.5).astype('int32')) print(label, (left, top), (right, bottom)) + # Save important data to JSON. + data['champs'][predicted_class] = score + + if top - label_size[1] >= 0: text_origin = np.array([left, top - label_size[1]]) else: @@ -275,6 +284,8 @@ def test_yolo(image, image_file_name): [tuple(text_origin), tuple(text_origin + label_size)], fill=colors[c]) draw.text(text_origin, label, fill=(0, 0, 0), font=font) + + del draw image.save(os.path.join(output_path, image_file_name), quality=90)