├── .idea ├── Visual_Tracking_api.iml ├── misc.xml ├── modules.xml └── vcs.xml ├── DSSTtracker.py ├── HCFtracker.py ├── KCFtracker.py ├── README.md ├── Sequence.py ├── example.jpg ├── examples.py ├── image └── files.png ├── pyhog ├── COPYING ├── Makefile ├── README.rst ├── __init__.py ├── features_pedro_py.cc ├── features_pedro_py.o ├── features_pedro_py.so ├── numpymacros.h ├── pyhog.py └── pyhog_example.ipynb ├── tools.py ├── tutorials ├── DeepDCF.ipynb ├── Discriminative Scale Space Tracker.ipynb ├── Kernelized Correlation Filters.ipynb ├── Visualize_deep_features.ipynb ├── img_ScaleChange │ ├── 0001.jpg │ ├── 0002.jpg │ ├── 0003.jpg │ ├── 0004.jpg │ └── groundtruth.txt └── img_common │ ├── 0001.jpg │ ├── 0002.jpg │ └── groundtruth.txt ├── utils.py ├── vgg.py ├── vot.py └── vot_demo_tracker.py /.idea/Visual_Tracking_api.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /DSSTtracker.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import cv2 3 | import numpy as np 4 | import utils 5 | import vot 6 | from pyhog import pyhog 7 | 8 | class padding: 9 | def __init__(self): 10 | self.generic = 1.8 11 | self.large = 1 12 | self.height = 0.4 13 | 14 | 15 | class DSSTtracker: 16 | def __init__(self, image, region): 17 | output_sigma_factor = 1 / float(16) 18 | scale_sigma_factor = 1 / float(4) 19 | self.lamda = 1e-2 20 | self.lamda_scale = 1e-2 21 | self.interp_factor = 0.025 22 | nScales = 33 # number of scale levels 23 | scale_model_factor = 1.0 24 | scale_step = 1.02 # step of one scale level 25 | scale_model_max_area = 32 * 16 26 | self.currentScaleFactor = 1.0 27 | 28 | self.target_size = np.array([region.height, region.width]) 29 | self.pos = [region.y + region.height / 2, region.x + region.width / 2] 30 | init_target_size = self.target_size 31 | self.base_target_size = self.target_size / self.currentScaleFactor 32 | self.sz = utils.get_window_size(self.target_size, image.shape[:2],padding()) 33 | 34 | output_sigma = np.sqrt(np.prod(self.target_size)) * output_sigma_factor 35 | scale_sigma = np.sqrt(nScales) * scale_sigma_factor 36 | grid_y = np.arange(np.floor(self.sz[0])) - np.floor(self.sz[0] / 2) 37 | grid_x = np.arange(np.floor(self.sz[1])) - np.floor(self.sz[1] / 2) 38 | rs, cs = np.meshgrid(grid_x, grid_y) 39 | y = np.exp(-0.5 / output_sigma ** 2 * (rs ** 2 + cs ** 2)) 40 | 41 | # Gaussian shaped label for scale estimation 42 | ss = np.arange(nScales) - np.ceil(nScales / 2) 43 | ys = np.exp(-0.5 * (ss ** 2) / scale_sigma ** 2) 44 | self.scaleFactors = np.power(scale_step, -ss) 45 | self.yf = np.fft.fft2(y, axes=(0, 1)) 46 | self.ysf = np.fft.fft(ys) 47 | 48 | feature_map = utils.get_subwindow(image, self.pos, self.sz, feature='hog') 49 | 50 | self.cos_window = np.outer(np.hanning(y.shape[0]), np.hanning(y.shape[1])) 51 | x_hog = np.multiply(feature_map, self.cos_window[:, :, None]) 52 | xf = np.fft.fft2(x_hog, axes=(0, 1)) 53 | 54 | # scale search preprocess 55 | if nScales % 2 == 0: 56 | self.scale_window = np.hanning(nScales + 1) 57 | self.scale_window = self.scale_window[1:] 58 | else: 59 | self.scale_window = np.hanning(nScales) 60 | 61 | self.scaleSizeFactors = self.scaleFactors 62 | self.min_scale_factor = np.power(scale_step, 63 | np.ceil(np.log(5. / np.min(self.sz)) / np.log(scale_step))) 64 | 65 | self.max_scale_factor = np.power(scale_step, 66 | np.floor(np.log(np.min(np.divide(image.shape[:2], 67 | self.base_target_size))) 68 | / np.log(scale_step))) 69 | 70 | if scale_model_factor * scale_model_factor * np.prod(init_target_size) > scale_model_max_area: 71 | scale_model_factor = np.sqrt(scale_model_max_area / np.prod(init_target_size)) 72 | 73 | self.scale_model_sz = np.floor(init_target_size * scale_model_factor) 74 | 75 | s = utils.get_scale_subwindow(image, self.pos, self.base_target_size, 76 | self.currentScaleFactor * self.scaleSizeFactors, self.scale_window, 77 | self.scale_model_sz) 78 | 79 | sf = np.fft.fftn(s, axes=[0]) 80 | 81 | self.x_num = np.multiply(self.yf[:, :, None], np.conj(xf)) 82 | self.x_den = np.real(np.sum(np.multiply(xf, np.conj(xf)), axis=2)) 83 | 84 | self.s_num = np.multiply(self.ysf[:, None], np.conj(sf)) 85 | self.s_den = np.real(np.sum(np.multiply(sf, np.conj(sf)), axis=1)) 86 | 87 | def track(self, image): 88 | test_patch = utils.get_subwindow(image, self.pos, self.sz, scale_factor=self.currentScaleFactor) 89 | hog_feature_t = pyhog.features_pedro(test_patch / 255., 1) 90 | hog_feature_t = np.lib.pad(hog_feature_t, ((1, 1), (1, 1), (0, 0)), 'edge') 91 | xt = np.multiply(hog_feature_t, self.cos_window[:, :, None]) 92 | xtf = np.fft.fft2(xt, axes=(0, 1)) 93 | response = np.real(np.fft.ifft2(np.divide(np.sum(np.multiply(self.x_num, xtf), 94 | axis=2), (self.x_den + self.lamda)))) 95 | 96 | v_centre, h_centre = np.unravel_index(response.argmax(), response.shape) 97 | vert_delta, horiz_delta = \ 98 | [(v_centre - response.shape[0] / 2) * self.currentScaleFactor, 99 | (h_centre - response.shape[1] / 2) * self.currentScaleFactor] 100 | 101 | self.pos = [self.pos[0] + vert_delta, self.pos[1] + horiz_delta] 102 | 103 | st = utils.get_scale_subwindow(image, self.pos, self.base_target_size, 104 | self.currentScaleFactor * self.scaleSizeFactors, self.scale_window, 105 | self.scale_model_sz) 106 | stf = np.fft.fftn(st, axes=[0]) 107 | 108 | scale_reponse = np.real(np.fft.ifftn(np.sum(np.divide(np.multiply(self.s_num, stf), 109 | (self.s_den[:, None] + self.lamda_scale)), axis=1))) 110 | recovered_scale = np.argmax(scale_reponse) 111 | self.currentScaleFactor = self.currentScaleFactor * self.scaleFactors[recovered_scale] 112 | 113 | if self.currentScaleFactor < self.min_scale_factor: 114 | self.currentScaleFactor = self.min_scale_factor 115 | elif self.currentScaleFactor > self.max_scale_factor: 116 | self.currentScaleFactor = self.max_scale_factor 117 | 118 | # update 119 | update_patch = utils.get_subwindow(image, self.pos, self.sz, scale_factor=self.currentScaleFactor) 120 | hog_feature_l = pyhog.features_pedro(update_patch / 255., 1) 121 | hog_feature_l = np.lib.pad(hog_feature_l, ((1, 1), (1, 1), (0, 0)), 'edge') 122 | xl = np.multiply(hog_feature_l, self.cos_window[:, :, None]) 123 | xlf = np.fft.fft2(xl, axes=(0, 1)) 124 | new_x_num = np.multiply(self.yf[:, :, None], np.conj(xlf)) 125 | new_x_den = np.real(np.sum(np.multiply(xlf, np.conj(xlf)), axis=2)) 126 | 127 | sl = utils.get_scale_subwindow(image, self.pos, self.base_target_size, 128 | self.currentScaleFactor * self.scaleSizeFactors, self.scale_window, 129 | self.scale_model_sz) 130 | slf = np.fft.fftn(sl, axes=[0]) 131 | new_s_num = np.multiply(self.ysf[:, None], np.conj(slf)) 132 | new_s_den = np.real(np.sum(np.multiply(slf, np.conj(slf)), axis=1)) 133 | 134 | self.x_num = (1 - self.interp_factor) * self.x_num + self.interp_factor * new_x_num 135 | self.x_den = (1 - self.interp_factor) * self.x_den + self.interp_factor * new_x_den 136 | self.s_num = (1 - self.interp_factor) * self.s_num + self.interp_factor * new_s_num 137 | self.s_den = (1 - self.interp_factor) * self.s_den + self.interp_factor * new_s_den 138 | 139 | self.target_size = self.base_target_size * self.currentScaleFactor 140 | 141 | return vot.Rectangle(self.pos[1] - self.target_size[1] / 2, 142 | self.pos[0] - self.target_size[0] / 2, 143 | self.target_size[1], 144 | self.target_size[0] 145 | ) 146 | 147 | handle = vot.VOT("rectangle") 148 | selection = handle.region() 149 | 150 | imagefile = handle.frame() 151 | if not imagefile: 152 | sys.exit(0) 153 | 154 | image = cv2.imread(imagefile) 155 | tracker = DSSTtracker(image, selection) 156 | while True: 157 | imagefile = handle.frame() 158 | if not imagefile: 159 | break 160 | image = cv2.imread(imagefile) 161 | region = tracker.track(image) 162 | handle.report(region) 163 | handle.quit() 164 | 165 | 166 | -------------------------------------------------------------------------------- /HCFtracker.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import cv2 3 | import numpy as np 4 | import torch 5 | from scipy import ndimage 6 | from skimage import transform 7 | from torch.autograd import Variable 8 | import utils 9 | import vgg 10 | import vot 11 | 12 | class padding: 13 | def __init__(self): 14 | self.generic = 1.8 15 | self.large = 1 16 | self.height = 0.4 17 | 18 | outputlayer = [17,26,35] 19 | numlayers = len(outputlayer) 20 | layerweights = [0.25,0.5,1] 21 | assert (numlayers == len(layerweights)) 22 | 23 | # network init 24 | 25 | model = vgg.VGG_19(outputlayer=outputlayer) 26 | 27 | # load partial weights 28 | model_dict = model.state_dict() 29 | 30 | # absolute path 31 | params = torch.load('/media/maoxiaofeng/project/GameProject/Visual_Tracking_api/vgg19.pth') 32 | load_dict = {k: v for k, v in params.items() if 'features' in k} 33 | model_dict.update(load_dict) 34 | model.load_state_dict(model_dict) 35 | model.cuda() 36 | 37 | # extract features 38 | imgMean = np.array([0.485, 0.456, 0.406], np.float) 39 | imgStd = np.array([0.229, 0.224, 0.225]) 40 | 41 | 42 | class HCFtracker: 43 | def __init__(self, image, region): 44 | 45 | self.target_size = np.array([region.height, region.width]) 46 | self.pos = [region.y + region.height / 2, region.x + region.width / 2] 47 | self.sz = utils.get_window_size(self.target_size, image.shape[:2], padding()) 48 | 49 | # position prediction params 50 | self.lamda = 1e-4 51 | output_sigma_factor = 0.1 52 | self.cell_size = 4 53 | self.interp_factor = 0.01 54 | self.x_num = [] 55 | self.x_den = [] 56 | 57 | # scale estimation params 58 | self.current_scale_factor = 1.0 59 | nScales = 33 60 | scale_step = 1.02 # step of one scale level 61 | scale_sigma_factor = 1 / float(4) 62 | self.interp_factor_scale = 0.01 63 | scale_model_max_area = 32 * 16 64 | scale_model_factor = 1.0 65 | self.min_scale_factor = np.power(scale_step, 66 | np.ceil(np.log(5. / np.min(self.sz)) / np.log(scale_step))) 67 | 68 | self.max_scale_factor = np.power(scale_step, 69 | np.floor(np.log(np.min(np.divide(image.shape[:2], 70 | self.target_size))) 71 | / np.log(scale_step))) 72 | 73 | if scale_model_factor * scale_model_factor * np.prod(self.target_size) > scale_model_max_area: 74 | scale_model_factor = np.sqrt(scale_model_max_area / np.prod(self.target_size)) 75 | 76 | self.scale_model_sz = np.floor(self.target_size * scale_model_factor) 77 | 78 | # Gaussian shaped label for position perdiction 79 | l1_patch_num = np.floor(self.sz / self.cell_size) 80 | output_sigma = np.sqrt(np.prod(self.target_size)) * output_sigma_factor / self.cell_size 81 | grid_y = np.arange(np.floor(l1_patch_num[0])) - np.floor(l1_patch_num[0] / 2) 82 | grid_x = np.arange(np.floor(l1_patch_num[1])) - np.floor(l1_patch_num[1] / 2) 83 | rs, cs = np.meshgrid(grid_x, grid_y) 84 | y = np.exp(-0.5 / output_sigma ** 2 * (rs ** 2 + cs ** 2)) 85 | self.yf = np.fft.fft2(y, axes=(0, 1)) 86 | self.cos_window = np.outer(np.hanning(self.yf.shape[0]), np.hanning(self.yf.shape[1])) 87 | 88 | # Gaussian shaped label for scale estimation 89 | ss = np.arange(nScales) - np.ceil(nScales / 2) 90 | scale_sigma = np.sqrt(nScales) * scale_sigma_factor 91 | ys = np.exp(-0.5 * (ss ** 2) / scale_sigma ** 2) 92 | self.scaleFactors = np.power(scale_step, -ss) 93 | self.ysf = np.fft.fft(ys) 94 | if nScales % 2 == 0: 95 | self.scale_window = np.hanning(nScales + 1) 96 | self.scale_window = self.scale_window[1:] 97 | else: 98 | self.scale_window = np.hanning(nScales) 99 | 100 | # Extracting hierarchical convolutional features and training 101 | img = utils.get_subwindow(image, self.pos, self.sz) 102 | img = transform.resize(img, (224, 224)) 103 | img = (img - imgMean) / imgStd 104 | img = np.transpose(img, (2, 0, 1)) 105 | feature_ensemble = model(Variable(torch.from_numpy(img[None, :, :, :]).float()).cuda()) 106 | 107 | for i in range(numlayers): 108 | 109 | feature = feature_ensemble[i].data[0].cpu().numpy().transpose((1, 2, 0)) 110 | x = ndimage.zoom(feature, (float(self.cos_window.shape[0]) / feature.shape[0], 111 | float(self.cos_window.shape[1]) / feature.shape[1], 1), order=1) 112 | x = np.multiply(x, self.cos_window[:, :, None]) 113 | xf = np.fft.fft2(x, axes=(0, 1)) 114 | 115 | self.x_num.append(np.multiply(self.yf[:, :, None], np.conj(xf))) 116 | self.x_den.append(np.real(np.sum(np.multiply(xf, np.conj(xf)), axis=2))) 117 | 118 | # Extracting the sample feature map for the scale filter and training 119 | s = utils.get_scale_subwindow(image, self.pos, self.target_size, 120 | self.current_scale_factor * self.scaleFactors, self.scale_window, 121 | self.scale_model_sz) 122 | 123 | sf = np.fft.fftn(s, axes=[0]) 124 | self.s_num = np.multiply(self.ysf[:, None], np.conj(sf)) 125 | self.s_den = np.real(np.sum(np.multiply(sf, np.conj(sf)), axis=1)) 126 | 127 | 128 | 129 | def track(self, image): 130 | test = utils.get_subwindow(image, self.pos, self.sz, self.current_scale_factor) 131 | test = transform.resize(test, (224, 224)) 132 | test = (test - imgMean) / imgStd 133 | test = np.transpose(test, (2, 0, 1)) 134 | feature_ensemble = model(Variable(torch.from_numpy(test[None, :, :, :]).float()).cuda()) 135 | 136 | for i in range(numlayers): 137 | 138 | feature = feature_ensemble[i].data[0].cpu().numpy().transpose((1, 2, 0)) 139 | xt = ndimage.zoom(feature, (float(self.cos_window.shape[0]) / feature.shape[0], 140 | float(self.cos_window.shape[1]) / feature.shape[1], 1), order=1) 141 | xt = np.multiply(xt, self.cos_window[:, :, None]) 142 | xtf = np.fft.fft2(xt, axes=(0, 1)) 143 | response = np.real(np.fft.ifft2(np.divide(np.sum(np.multiply(self.x_num[i], xtf), 144 | axis=2), (self.x_den[i] + self.lamda)))) * layerweights[i] 145 | if i == 0: 146 | response_final = response 147 | else: 148 | response_final = np.add(response_final, response) 149 | 150 | v_centre, h_centre = np.unravel_index(response_final.argmax(), response_final.shape) 151 | vert_delta, horiz_delta = \ 152 | [(v_centre - response_final.shape[0] / 2) * self.current_scale_factor * self.cell_size, 153 | (h_centre - response_final.shape[1] / 2) * self.current_scale_factor * self.cell_size] 154 | 155 | self.pos = [self.pos[0] + vert_delta, self.pos[1] + horiz_delta] 156 | 157 | st = utils.get_scale_subwindow(image, self.pos, self.target_size, 158 | self.current_scale_factor * self.scaleFactors, self.scale_window, 159 | self.scale_model_sz) 160 | stf = np.fft.fftn(st, axes=[0]) 161 | 162 | scale_reponse = np.real(np.fft.ifftn(np.sum(np.divide(np.multiply(self.s_num, stf), 163 | (self.s_den[:, None] + self.lamda)), axis=1))) 164 | recovered_scale = np.argmax(scale_reponse) 165 | self.current_scale_factor = self.current_scale_factor * self.scaleFactors[recovered_scale] 166 | 167 | if self.current_scale_factor < self.min_scale_factor: 168 | self.current_scale_factor = self.min_scale_factor 169 | elif self.current_scale_factor > self.max_scale_factor: 170 | self.current_scale_factor = self.max_scale_factor 171 | 172 | # update 173 | 174 | update_patch = utils.get_subwindow(image, self.pos, self.sz, scale_factor=self.current_scale_factor) 175 | 176 | update_patch = transform.resize(update_patch, (224, 224)) 177 | update_patch = (update_patch - imgMean) / imgStd 178 | update_patch = np.transpose(update_patch, (2, 0, 1)) 179 | feature_ensemble = model(Variable(torch.from_numpy(update_patch[None, :, :, :]).float()).cuda()) 180 | 181 | for i in range(numlayers): 182 | feature = feature_ensemble[i].data[0].cpu().numpy().transpose((1, 2, 0)) 183 | xl = ndimage.zoom(feature, (float(self.cos_window.shape[0]) / feature.shape[0], 184 | float(self.cos_window.shape[1]) / feature.shape[1], 1), order=1) 185 | xl = np.multiply(xl, self.cos_window[:, :, None]) 186 | xlf = np.fft.fft2(xl, axes=(0, 1)) 187 | self.x_num[i] = (1 - self.interp_factor) * self.x_num[i] + self.interp_factor * np.multiply(self.yf[:, :, None], np.conj(xlf)) 188 | self.x_den[i] = (1 - self.interp_factor) * self.x_den[i] + self.interp_factor * np.real(np.sum(np.multiply(xlf, np.conj(xlf)), axis=2)) 189 | 190 | sl = utils.get_scale_subwindow(image, self.pos, self.target_size, 191 | self.current_scale_factor * self.scaleFactors, self.scale_window, 192 | self.scale_model_sz) 193 | slf = np.fft.fftn(sl, axes=[0]) 194 | new_s_num = np.multiply(self.ysf[:, None], np.conj(slf)) 195 | new_s_den = np.real(np.sum(np.multiply(slf, np.conj(slf)), axis=1)) 196 | self.s_num = (1 - self.interp_factor) * self.s_num + self.interp_factor * new_s_num 197 | self.s_den = (1 - self.interp_factor) * self.s_den + self.interp_factor * new_s_den 198 | 199 | self.final_size = self.target_size * self.current_scale_factor 200 | 201 | return vot.Rectangle(self.pos[1] - self.final_size[1] / 2, 202 | self.pos[0] - self.final_size[0] / 2, 203 | self.final_size[1], 204 | self.final_size[0] 205 | ) 206 | 207 | 208 | 209 | 210 | handle = vot.VOT("rectangle") 211 | selection = handle.region() 212 | 213 | imagefile = handle.frame() 214 | if not imagefile: 215 | sys.exit(0) 216 | 217 | image = cv2.imread(imagefile) 218 | tracker = HCFtracker(image, selection) 219 | while True: 220 | imagefile = handle.frame() 221 | if not imagefile: 222 | break 223 | image = cv2.imread(imagefile) 224 | region = tracker.track(image) 225 | handle.report(region) 226 | handle.quit() 227 | 228 | -------------------------------------------------------------------------------- /KCFtracker.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import cv2 3 | import numpy as np 4 | import utils 5 | import vot 6 | 7 | 8 | class KCFtracker: 9 | def __init__(self, image, region): 10 | self.target_size = np.array([region.height, region.width]) 11 | self.pos = [region.y + region.height / 2, region.x + region.width / 2] 12 | padding = 2.5 # extra area surrounding the target 13 | self.patch_size = np.floor(self.target_size * (1 + padding)) 14 | img_crop = utils.get_subwindow(image, self.pos, self.patch_size) 15 | 16 | spatial_bandwidth_sigma_factor = 1 / float(16) 17 | output_sigma = np.sqrt(np.prod(self.target_size)) * spatial_bandwidth_sigma_factor 18 | grid_y = np.arange(np.floor(self.patch_size[0])) - np.floor(self.patch_size[0] / 2) 19 | grid_x = np.arange(np.floor(self.patch_size[1])) - np.floor(self.patch_size[1] / 2) 20 | rs, cs = np.meshgrid(grid_x, grid_y) 21 | y = np.exp(-0.5 / output_sigma ** 2 * (rs ** 2 + cs ** 2)) 22 | 23 | self.cos_window = np.outer(np.hanning(y.shape[0]), np.hanning(y.shape[1])) 24 | img_colour = img_crop - img_crop.mean() 25 | # Get training image patch x 26 | self.x = np.multiply(img_colour, self.cos_window[:, :, None]) 27 | 28 | # FFT Transformation 29 | # First transform y 30 | yf = np.fft.fft2(y, axes=(0, 1)) 31 | 32 | # Then transfrom x 33 | self.xf = np.fft.fft2(self.x, axes=(0, 1)) 34 | self.feature_bandwidth_sigma = 0.2 35 | k = utils.dense_gauss_kernel(self.feature_bandwidth_sigma, self.xf, self.x) 36 | 37 | lambda_value = 1e-4 38 | self.alphaf = np.divide(yf, np.fft.fft2(k, axes=(0, 1)) + lambda_value) 39 | 40 | def track(self, image): 41 | 42 | test_crop = utils.get_subwindow(image, self.pos, self.patch_size) 43 | z = np.multiply(test_crop - test_crop.mean(), self.cos_window[:, :, None]) 44 | zf = np.fft.fft2(z, axes=(0, 1)) 45 | k_test = utils.dense_gauss_kernel(self.feature_bandwidth_sigma, self.xf, self.x, zf, z) 46 | kf_test = np.fft.fft2(k_test, axes=(0, 1)) 47 | response = np.real(np.fft.ifft2(np.multiply(self.alphaf, kf_test))) 48 | 49 | # Max position in response map 50 | v_centre, h_centre = np.unravel_index(response.argmax(), response.shape) 51 | vert_delta, horiz_delta = [v_centre - response.shape[0] / 2, 52 | h_centre - response.shape[1] / 2] 53 | 54 | # Predicted position 55 | self.pos = [self.pos[0] + vert_delta, self.pos[1] + horiz_delta] 56 | return vot.Rectangle(self.pos[1] - self.target_size[1] / 2, 57 | self.pos[0] - self.target_size[0] / 2, 58 | self.target_size[1], 59 | self.target_size[0] 60 | ) 61 | 62 | handle = vot.VOT("rectangle") 63 | selection = handle.region() 64 | 65 | imagefile = handle.frame() 66 | if not imagefile: 67 | sys.exit(0) 68 | 69 | image = cv2.imread(imagefile)/255. 70 | tracker = KCFtracker(image, selection) 71 | while True: 72 | imagefile = handle.frame() 73 | if not imagefile: 74 | break 75 | image = cv2.imread(imagefile)/255. 76 | region = tracker.track(image) 77 | handle.report(region) 78 | handle.quit() 79 | 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Visual_Tracking_api 2 | 3 | 4 | This is a simple visual tracking interface coding by Python2.7 5 | 6 | ## Introduction 7 | 8 | This repository contains the following contents: 9 | 10 | `tutorials` folder contains the implementation and tutorial of various famous tracking algorithms written with ipython-notebook. Learning these notebooks helps you understand the details of the algorithms. 11 | 12 | `pyhog` folder includes a implementation of HOG feature. We copied this implementation from https://github.com/dimatura/pyhog 13 | 14 | python wrapper script file named `XXXtracker.py`,such as `KCFtracker.py`. These trackers can be integrated into the VOT evaluation process. There is a demo file `vot_demo_tracker.py` representing how to write the wrapper script file. 15 | You can refer to < Usage > for getting more information. 16 | 17 | ## Trackers 18 | 19 | Trackers that have been implemented are as follows: 20 | 21 | - `KCFtracker.py` High-Speed Tracking with Kernelized Correlation Filters (KCF) [[PDF]](http://home.isr.uc.pt/~henriques/publications/henriques_tpami2015.pdf) 22 | 23 | - `DSSTtracker.py` Accurate Scale Estimation for Robust Visual Tracking (DSST) [[PDF]](http://www.cvl.isy.liu.se/en/research/objrec/visualtracking/scalvistrack/ScaleTracking_BMVC14.pdf) 24 | 25 | - `HCFtracker.py` Hierarchical Convolutional Features for Visual Tracking (HCF) [[PDF]](https://www.cv-foundation.org/openaccess/content_iccv_2015/papers/Ma_Hierarchical_Convolutional_Features_ICCV_2015_paper.pdf) 26 | 27 | ## Environment 28 | 29 | Python 2.7.12 30 | 31 | scikit-image 0.13.0 32 | 33 | scipy 0.19.1 34 | 35 | matplotlib 1.5.3 36 | 37 | numpy 1.13.1 38 | 39 | pytorch 0.1.12 40 | 41 | opencv 2.4.11 42 | 43 | ## Usage 44 | 45 | ### Prepare 46 | 47 | Before using the tracking interface, you must do some preparation. 48 | 49 | First of all, install the necessary library (mentioned in < Environment >) 50 | 51 | Then, if the algorithm requires hog features, you must move the folder `pyhog` to Python site-packages by: 52 | 53 | ```buildoutcfg 54 | sudo cp -r .../Visual_Tracking_api/pyhog /usr/local/lib/python2.7/site-packages 55 | ``` 56 | 57 | Or the algorithm requires deep features (we use pretrained vgg19 in general), you need to download model file 'vgg19.pth' by url: 58 | 59 | https://download.pytorch.org/models/vgg19-dcbb9e9d.pth 60 | 61 | and place it in project directory. 62 | 63 | ### Tracking on you own sequence 64 | 65 | You can use this tool to track on you own sequence: 66 | 67 | Make sure that your video has been decomposed into image sequences and each image is named with a number 68 | (if the current image corresponds to the ith frame in the video, then the name is i.jpg or 0000i.jpg, adding 0 in front of the i is OK). For example: 69 | 70 | ![](./image/files.png) 71 | 72 | Except for the image sequence, you need to provide `groundtruth.txt` file which represents the boundingbox infromation. 73 | The boundingbox of the first frame must be give, so there are at least one line in the `groundtruth.txt` file. 74 | For example: 75 | ``` 76 | 20.0,30.0,50.0,100.0 77 | ``` 78 | Represents the boundingbox at (20.0, 30.0) position, width and height are respectively 50.0, 100.0. 79 | Of course, if there are other frames of boundingbox information, it can also be written in `groundtruth.txt`. 80 | 81 | We provide tools to run algorithms on custom sequences. 82 | `example.py` is an demo for understanding how to use. First, you need to configure the sequence by 83 | 84 | ```python 85 | sequence = Sequence(path='/YOUR_ROOT_DIR/YOUR_DATASET_NAME', name='THE_NAME_OF_SEQUENCE', region_format='rectangle') 86 | 87 | ``` 88 | 89 | For example, configure a sequence of vot2016: 90 | 91 | ```python 92 | sequence = Sequence(path='/media/maoxiaofeng/project/GameProject/dataset/vot2016', name='bag', 93 | region_format='rectangle') 94 | 95 | ``` 96 | 97 | Then, run tracking algorithms on the configured sequence by 98 | (If you want to visualize the result, modify `visualize` to True) 99 | 100 | ```python 101 | Tracking(sequence,tracker_list=['KCFtracker','DSSTtracker'],visualize = False) 102 | 103 | ``` 104 | 105 | Also you can visulize results directly by ( `visualize_gt` controls whether to visualize the groundtruth. If `tracker_list` is None, this function will only visualize the groundtruth. ) 106 | 107 | ```python 108 | visulize_result(sequence,tracker_list=['KCFtracker','DSSTtracker'],visualize_gt = True) 109 | ``` 110 | 111 | 112 | ### Evaluate on VOT dataset 113 | 114 | Now the interface is compatible with VOT dataset, use [vot-toolkit](https://github.com/votchallenge/vot-toolkit) to evaluate the tracking algorithm on VOT datasets. 115 | 116 | You can download Visual Object Tracking (VOT) challenge datasets through the following links: 117 | 118 | [VOT2015](http://data.votchallenge.net/vot2015/vot2015.zip), [VOT2016](http://data.votchallenge.net/vot2016/vot2016.zip), [VOT2014](http://data.votchallenge.net/vot2014/vot2014.zip), [VOT2013](http://data.votchallenge.net/vot2013/vot2013.zip) 119 | 120 | Then set up VOT workspace (http://www.votchallenge.net/howto/workspace.html) and integrate trackers into the VOT toolkit (http://www.votchallenge.net/howto/integration.html) 121 | 122 | For detail information, please visit VOT official website:http://www.votchallenge.net/ 123 | 124 | -------------------------------------------------------------------------------- /Sequence.py: -------------------------------------------------------------------------------- 1 | """ 2 | \file Sequence.py 3 | 4 | @author Xiaofeng Mao 5 | 6 | @date 2017.9.27 7 | 8 | """ 9 | 10 | import sys 11 | import copy 12 | import collections 13 | import os 14 | 15 | 16 | Rectangle = collections.namedtuple('Rectangle', ['x', 'y', 'width', 'height']) 17 | Point = collections.namedtuple('Point', ['x', 'y']) 18 | Polygon = collections.namedtuple('Polygon', ['points']) 19 | 20 | def parse_region(string): 21 | tokens = map(float, string.split(',')) 22 | if len(tokens) == 4: 23 | return Rectangle(tokens[0], tokens[1], tokens[2], tokens[3]) 24 | elif len(tokens) % 2 == 0 and len(tokens) > 4: 25 | return Polygon([Point(tokens[i],tokens[i+1]) for i in xrange(0,len(tokens),2)]) 26 | return None 27 | 28 | def encode_region(region): 29 | if isinstance(region, Polygon): 30 | return ','.join(['{},{}'.format(p.x,p.y) for p in region.points]) 31 | elif isinstance(region, Rectangle): 32 | return '{},{},{},{}'.format(region.x, region.y, region.width, region.height) 33 | else: 34 | return "" 35 | 36 | def convert_region(region, to): 37 | 38 | if to == 'rectangle': 39 | 40 | if isinstance(region, Rectangle): 41 | return copy.copy(region) 42 | elif isinstance(region, Polygon): 43 | top = sys.float_info.max 44 | bottom = sys.float_info.min 45 | left = sys.float_info.max 46 | right = sys.float_info.min 47 | 48 | for point in region.points: 49 | top = min(top, point.y) 50 | bottom = max(bottom, point.y) 51 | left = min(left, point.x) 52 | right = max(right, point.x) 53 | 54 | return Rectangle(left, top, right - left, bottom - top) 55 | 56 | else: 57 | return None 58 | if to == 'polygon': 59 | 60 | if isinstance(region, Rectangle): 61 | points = [] 62 | points.append((region.x, region.y)) 63 | points.append((region.x + region.width, region.y)) 64 | points.append((region.x + region.width, region.y + region.height)) 65 | points.append((region.x, region.y + region.height)) 66 | return Polygon(points) 67 | 68 | elif isinstance(region, Polygon): 69 | return copy.copy(region) 70 | else: 71 | return None 72 | 73 | return None 74 | 75 | class Sequence(object): 76 | """ Base class for Python VOT integration """ 77 | def __init__(self, path, name, region_format = 'rectangle'): 78 | self.name = name 79 | """ Constructor 80 | 81 | Args: 82 | region_format: Region format options 83 | """ 84 | assert(region_format in ['rectangle', 'polygon']) 85 | 86 | if len(name) == 0: 87 | self.seqdir = path 88 | else: 89 | self.seqdir = os.path.join(path, name) 90 | 91 | self._images=[] 92 | for _, _, files in os.walk(self.seqdir): 93 | for file in files: 94 | if file.endswith('jpg') or file.endswith('png'): 95 | self._images.append(file) 96 | self._images.sort(key= lambda x:int(x[:-4])) 97 | 98 | self.groundtruth = [] 99 | for x in open(os.path.join(self.seqdir, 'groundtruth.txt'), 'r').readlines(): 100 | self.groundtruth.append(convert_region(parse_region(x), region_format)) 101 | 102 | self._frame = 0 103 | self._region = convert_region(parse_region(open(os.path.join(self.seqdir, 'groundtruth.txt'), 'r').readline()), region_format) 104 | self._result = [] 105 | self._region_format = region_format 106 | 107 | def region(self): 108 | """ 109 | Send configuration message to the client and receive the initialization 110 | region and the path of the first image 111 | 112 | Returns: 113 | initialization region 114 | """ 115 | 116 | return self._region 117 | 118 | def report(self, region): 119 | """ 120 | Report the tracking results to the client 121 | 122 | Arguments: 123 | region: region for the frame 124 | """ 125 | assert(isinstance(region, Rectangle) or isinstance(region, Polygon)) 126 | 127 | self._result.append(region) 128 | self._frame += 1 129 | 130 | def frame(self): 131 | """ 132 | Get a frame (image path) from client 133 | 134 | Returns: 135 | absolute path of the image 136 | """ 137 | 138 | if self._frame >= len(self._images): 139 | return None 140 | return os.path.join(self.seqdir, self._images[self._frame]) 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vtddggg/Visual_Tracking_api/d5cf7934861950ca1eedede198382fc468dab941/example.jpg -------------------------------------------------------------------------------- /examples.py: -------------------------------------------------------------------------------- 1 | from Sequence import Sequence 2 | from tools import Tracking,visulize_result,precision_plot,overlap_plot 3 | 4 | sequence = Sequence(path='/media/maoxiaofeng/project/GameProject/dataset/vot-tir2016', name='birds', region_format='rectangle') 5 | 6 | #Tracking(sequence,tracker_list=['HCFtracker'],visualize=False) 7 | #visulize_result(sequence,tracker_list=['HCFtracker'],visualize_gt = True) 8 | #precision_plot(sequence, tracker_list=['HCFtracker']) 9 | #overlap_plot(sequence, tracker_list=['HCFtracker']) -------------------------------------------------------------------------------- /image/files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vtddggg/Visual_Tracking_api/d5cf7934861950ca1eedede198382fc468dab941/image/files.png -------------------------------------------------------------------------------- /pyhog/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2008, 2009, 2010 Pedro Felzenszwalb, Ross Girshick 2 | Copyright (C) 2007 Pedro Felzenszwalb, Deva Ramanan 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /pyhog/Makefile: -------------------------------------------------------------------------------- 1 | NUMPY=`python -c 'import numpy; print numpy.get_include()'` 2 | PYROOT=`python -c 'import sys; print sys.prefix'` 3 | VER=`python -c "import sys; print('%s.%s'%(sys.version_info[0],sys.version_info[1]))"` 4 | CC=g++ 5 | LIBS= 6 | #FLAGS= -Wall -DNUMPYCHECK -fPIC 7 | #FLAGS = -Wall -DNDEBUG -O2 -ffast-math -pipe -msse -msse2 -mmmx -mfpmath=sse -fomit-frame-pointer 8 | #FLAGS = -Wall -DNDEBUG -O2 -ffast-math -fPIC 9 | FLAGS = -DNUMPYCHECK -DNDEBUG -O2 -ffast-math -msse2 -fPIC 10 | 11 | .PHONY: all 12 | all: features_pedro_py.so 13 | 14 | features_pedro_py.so: features_pedro_py.o 15 | g++ $^ -shared -o $@ $(LIBS) 16 | 17 | features_pedro_py.o: features_pedro_py.cc numpymacros.h 18 | g++ -c $< $(FLAGS) -I$(NUMPY) -I$(PYROOT)/include/python$(VER) -I../src/ -o $@ 19 | 20 | .PHONY: clean 21 | clean: 22 | rm -f *.o *.so 23 | -------------------------------------------------------------------------------- /pyhog/README.rst: -------------------------------------------------------------------------------- 1 | 2 | pyhog 3 | ---------- 4 | 5 | The Pascal VOC Toolkit comes with a Matlab/C implementation of HOG features by 6 | Pedro Felzenszwalb, Deva Ramanan and presumably others. Since I'm not very fond 7 | of Matlab I replaced the Matlab-specific parts for their Numpy equivalents. It 8 | works, but it's not very efficient because it copies the array into a 9 | Fortran-ordered version. That should be easy to fix. 10 | 11 | See an example of here: http://nbviewer.ipython.org/github/dimatura/pyhog/blob/master/pyhog_example.ipynb 12 | 13 | Daniel Maturana - dimatura@cmu.edu 14 | 2012 15 | -------------------------------------------------------------------------------- /pyhog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vtddggg/Visual_Tracking_api/d5cf7934861950ca1eedede198382fc468dab941/pyhog/__init__.py -------------------------------------------------------------------------------- /pyhog/features_pedro_py.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "numpymacros.h" 9 | 10 | // small value, used to avoid division by zero 11 | #define eps 0.0001 12 | 13 | // unit vectors used to compute gradient orientation 14 | double uu[9] = {1.0000, 15 | 0.9397, 16 | 0.7660, 17 | 0.500, 18 | 0.1736, 19 | -0.1736, 20 | -0.5000, 21 | -0.7660, 22 | -0.9397}; 23 | double vv[9] = {0.0000, 24 | 0.3420, 25 | 0.6428, 26 | 0.8660, 27 | 0.9848, 28 | 0.9848, 29 | 0.8660, 30 | 0.6428, 31 | 0.3420}; 32 | 33 | static inline double min(double x, double y) { return (x <= y ? x : y); } 34 | static inline double max(double x, double y) { return (x <= y ? y : x); } 35 | 36 | static inline int min(int x, int y) { return (x <= y ? x : y); } 37 | static inline int max(int x, int y) { return (x <= y ? y : x); } 38 | 39 | // main function: 40 | // takes a double color image and a bin size 41 | // returns HOG features 42 | static PyObject *process(PyObject *self, PyObject *args) { 43 | // in 44 | PyArrayObject *mximage; 45 | int sbin; 46 | 47 | // out 48 | PyArrayObject *mxfeat; 49 | 50 | if (!PyArg_ParseTuple(args, "O!i", 51 | &PyArray_Type, &mximage, 52 | &sbin 53 | )) { 54 | return NULL; 55 | } 56 | 57 | //TODO fix warnings 58 | FARRAY_CHECK(mximage); 59 | NDIM_CHECK(mximage, 3); 60 | DIM_CHECK(mximage, 2, 3); 61 | TYPE_CHECK(mximage, NPY_FLOAT64); 62 | 63 | double *im = (double *)PyArray_DATA(mximage); 64 | npy_intp dims[3]; 65 | dims[0] = PyArray_DIM(mximage, 0); 66 | dims[1] = PyArray_DIM(mximage, 1); 67 | dims[2] = PyArray_DIM(mximage, 2); 68 | //printf("%d %d %d\n",(int)dims[0],(int)dims[1],(int)dims[2]); 69 | 70 | // memory for caching orientation histograms & their norms 71 | int blocks[2]; 72 | blocks[0] = (int)round((double)dims[0]/(double)sbin); 73 | blocks[1] = (int)round((double)dims[1]/(double)sbin); 74 | 75 | double *hist = (double *)calloc(blocks[0]*blocks[1]*18, sizeof(double)); 76 | double *norm = (double *)calloc(blocks[0]*blocks[1], sizeof(double)); 77 | 78 | // memory for HOG features 79 | // TODO there's a way to do this in one call 80 | npy_intp out[3]; 81 | out[0] = max(blocks[0]-2, 0); 82 | out[1] = max(blocks[1]-2, 0); 83 | out[2] = 27+4; 84 | 85 | //mxfeat = mxCreateNumericArray(3, out, mxDOUBLE_CLASS, mxREAL); 86 | mxfeat = (PyArrayObject*) PyArray_NewFromDescr( 87 | &PyArray_Type, PyArray_DescrFromType(NPY_FLOAT64), 88 | 3, out, NULL, NULL, NPY_ARRAY_F_CONTIGUOUS, NULL); 89 | //(PyArrayObject *)PyArray_SimpleNew(3, out, NPY_FLOAT64); 90 | 91 | double *feat = (double *)PyArray_DATA(mxfeat); 92 | 93 | int visible[2]; 94 | visible[0] = blocks[0]*sbin; 95 | visible[1] = blocks[1]*sbin; 96 | 97 | for (int x = 1; x < visible[1]-1; x++) { 98 | for (int y = 1; y < visible[0]-1; y++) { 99 | // first color channel 100 | double *s = im + min(x, dims[1]-2)*dims[0] + min(y, dims[0]-2); 101 | double dy = *(s+1) - *(s-1); 102 | double dx = *(s+dims[0]) - *(s-dims[0]); 103 | double v = dx*dx + dy*dy; 104 | 105 | // second color channel 106 | s += dims[0]*dims[1]; 107 | double dy2 = *(s+1) - *(s-1); 108 | double dx2 = *(s+dims[0]) - *(s-dims[0]); 109 | double v2 = dx2*dx2 + dy2*dy2; 110 | 111 | // third color channel 112 | s += dims[0]*dims[1]; 113 | double dy3 = *(s+1) - *(s-1); 114 | double dx3 = *(s+dims[0]) - *(s-dims[0]); 115 | double v3 = dx3*dx3 + dy3*dy3; 116 | 117 | // pick channel with strongest gradient 118 | if (v2 > v) { 119 | v = v2; 120 | dx = dx2; 121 | dy = dy2; 122 | } 123 | if (v3 > v) { 124 | v = v3; 125 | dx = dx3; 126 | dy = dy3; 127 | } 128 | 129 | // snap to one of 18 orientations 130 | double best_dot = 0; 131 | int best_o = 0; 132 | for (int o = 0; o < 9; o++) { 133 | double dot = uu[o]*dx + vv[o]*dy; 134 | if (dot > best_dot) { 135 | best_dot = dot; 136 | best_o = o; 137 | } else if (-dot > best_dot) { 138 | best_dot = -dot; 139 | best_o = o+9; 140 | } 141 | } 142 | 143 | // add to 4 histograms around pixel using linear interpolation 144 | double xp = ((double)x+0.5)/(double)sbin - 0.5; 145 | double yp = ((double)y+0.5)/(double)sbin - 0.5; 146 | int ixp = (int)floor(xp); 147 | int iyp = (int)floor(yp); 148 | double vx0 = xp-ixp; 149 | double vy0 = yp-iyp; 150 | double vx1 = 1.0-vx0; 151 | double vy1 = 1.0-vy0; 152 | v = sqrt(v); 153 | 154 | if (ixp >= 0 && iyp >= 0) { 155 | *(hist + ixp*blocks[0] + iyp + best_o*blocks[0]*blocks[1]) += 156 | vx1*vy1*v; 157 | } 158 | 159 | if (ixp+1 < blocks[1] && iyp >= 0) { 160 | *(hist + (ixp+1)*blocks[0] + iyp + best_o*blocks[0]*blocks[1]) += 161 | vx0*vy1*v; 162 | } 163 | 164 | if (ixp >= 0 && iyp+1 < blocks[0]) { 165 | *(hist + ixp*blocks[0] + (iyp+1) + best_o*blocks[0]*blocks[1]) += 166 | vx1*vy0*v; 167 | } 168 | 169 | if (ixp+1 < blocks[1] && iyp+1 < blocks[0]) { 170 | *(hist + (ixp+1)*blocks[0] + (iyp+1) + best_o*blocks[0]*blocks[1]) += 171 | vx0*vy0*v; 172 | } 173 | } 174 | } 175 | 176 | // compute energy in each block by summing over orientations 177 | for (int o = 0; o < 9; o++) { 178 | double *src1 = hist + o*blocks[0]*blocks[1]; 179 | double *src2 = hist + (o+9)*blocks[0]*blocks[1]; 180 | double *dst = norm; 181 | double *end = norm + blocks[1]*blocks[0]; 182 | while (dst < end) { 183 | *(dst++) += (*src1 + *src2) * (*src1 + *src2); 184 | src1++; 185 | src2++; 186 | } 187 | } 188 | 189 | // compute features 190 | for (int x = 0; x < out[1]; x++) { 191 | for (int y = 0; y < out[0]; y++) { 192 | double *dst = feat + x*out[0] + y; 193 | double *src, *p, n1, n2, n3, n4; 194 | 195 | p = norm + (x+1)*blocks[0] + y+1; 196 | n1 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps); 197 | p = norm + (x+1)*blocks[0] + y; 198 | n2 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps); 199 | p = norm + x*blocks[0] + y+1; 200 | n3 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps); 201 | p = norm + x*blocks[0] + y; 202 | n4 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps); 203 | 204 | double t1 = 0; 205 | double t2 = 0; 206 | double t3 = 0; 207 | double t4 = 0; 208 | 209 | // contrast-sensitive features 210 | src = hist + (x+1)*blocks[0] + (y+1); 211 | for (int o = 0; o < 18; o++) { 212 | double h1 = min(*src * n1, 0.2); 213 | double h2 = min(*src * n2, 0.2); 214 | double h3 = min(*src * n3, 0.2); 215 | double h4 = min(*src * n4, 0.2); 216 | *dst = 0.5 * (h1 + h2 + h3 + h4); 217 | t1 += h1; 218 | t2 += h2; 219 | t3 += h3; 220 | t4 += h4; 221 | dst += out[0]*out[1]; 222 | src += blocks[0]*blocks[1]; 223 | } 224 | 225 | // contrast-insensitive features 226 | src = hist + (x+1)*blocks[0] + (y+1); 227 | for (int o = 0; o < 9; o++) { 228 | double sum = *src + *(src + 9*blocks[0]*blocks[1]); 229 | double h1 = min(sum * n1, 0.2); 230 | double h2 = min(sum * n2, 0.2); 231 | double h3 = min(sum * n3, 0.2); 232 | double h4 = min(sum * n4, 0.2); 233 | *dst = 0.5 * (h1 + h2 + h3 + h4); 234 | dst += out[0]*out[1]; 235 | src += blocks[0]*blocks[1]; 236 | } 237 | 238 | // texture features 239 | *dst = 0.2357 * t1; 240 | dst += out[0]*out[1]; 241 | *dst = 0.2357 * t2; 242 | dst += out[0]*out[1]; 243 | *dst = 0.2357 * t3; 244 | dst += out[0]*out[1]; 245 | *dst = 0.2357 * t4; 246 | } 247 | } 248 | 249 | // hack 250 | //PyArray_FLAGS(mxfeat) |= NPY_F_CONTIGUOUS; 251 | //PyArray_FLAGS(mxfeat) &= ~NPY_C_CONTIGUOUS; 252 | 253 | free(hist); 254 | free(norm); 255 | 256 | return PyArray_Return(mxfeat);//Py_BuildValue("N", mxfeat); 257 | } 258 | 259 | static PyMethodDef features_pedro_py_methods[] = { 260 | {"process", 261 | process, 262 | METH_VARARGS, 263 | "process"}, 264 | {NULL, NULL, 0, NULL} /* sentinel*/ 265 | }; 266 | 267 | PyMODINIT_FUNC initfeatures_pedro_py() { 268 | Py_InitModule("features_pedro_py", features_pedro_py_methods); 269 | import_array(); 270 | } 271 | 272 | -------------------------------------------------------------------------------- /pyhog/features_pedro_py.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vtddggg/Visual_Tracking_api/d5cf7934861950ca1eedede198382fc468dab941/pyhog/features_pedro_py.o -------------------------------------------------------------------------------- /pyhog/features_pedro_py.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vtddggg/Visual_Tracking_api/d5cf7934861950ca1eedede198382fc468dab941/pyhog/features_pedro_py.so -------------------------------------------------------------------------------- /pyhog/numpymacros.h: -------------------------------------------------------------------------------- 1 | #ifndef NUMPY_MACROS__H 2 | #define NUMPY_MACROS__H 3 | 4 | /* 5 | This is from the book 'Python Scripting for Computational Science' 6 | by Hans Petter Langtangen, with some modifications. 7 | */ 8 | 9 | #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION 10 | #include 11 | 12 | /* define some macros for array dimension/type check 13 | and subscripting */ 14 | #define QUOTE(s) # s /* turn s into string "s" */ 15 | #define NDIM_CHECK(a, expected_ndim) \ 16 | if (PyArray_NDIM(a) != expected_ndim) { \ 17 | PyErr_Format(PyExc_ValueError, \ 18 | "%s array is %d-dimensional, but expected to be %d-dimensional",\ 19 | QUOTE(a), PyArray_NDIM(a), expected_ndim); \ 20 | return NULL; \ 21 | } 22 | #define DIM_CHECK(a, dim, expected_length) \ 23 | if (dim > PyArray_NDIM(a)) { \ 24 | PyErr_Format(PyExc_ValueError, \ 25 | "%s array has no %d dimension (max dim. is %d)", \ 26 | QUOTE(a), dim, PyArray_NDIM(a)); \ 27 | return NULL; \ 28 | } \ 29 | if (PyArray_DIM(a, dim) != expected_length) { \ 30 | PyErr_Format(PyExc_ValueError, \ 31 | "%s array has wrong %d-dimension=%ld (expected %d)", \ 32 | QUOTE(a), dim, PyArray_DIM(a, dim), expected_length); \ 33 | return NULL; \ 34 | } 35 | #define TYPE_CHECK(a, tp) \ 36 | if (PyArray_TYPE(a) != tp) { \ 37 | PyErr_Format(PyExc_TypeError, \ 38 | "%s array is not of correct type (%d)", QUOTE(a), tp); \ 39 | return NULL; \ 40 | } 41 | #define CALLABLE_CHECK(func) \ 42 | if (!PyCallable_Check(func)) { \ 43 | PyErr_Format(PyExc_TypeError, \ 44 | "%s is not a callable function", QUOTE(func)); \ 45 | return NULL; \ 46 | } 47 | #define CARRAY_CHECK(a) \ 48 | if (!(PyArray_ISCONTIGUOUS(a) && PyArray_ISCARRAY(a))) { \ 49 | PyErr_Format(PyExc_TypeError, \ 50 | "%s is not a contiguous c-array", QUOTE(a)); \ 51 | return NULL; \ 52 | } 53 | #define FARRAY_CHECK(a) \ 54 | if (!(PyArray_ISFARRAY(a))) { \ 55 | PyErr_Format(PyExc_TypeError, \ 56 | "%s is not a contiguous f-array", QUOTE(a)); \ 57 | return NULL; \ 58 | } 59 | #define CHECK(assertion, message) \ 60 | if (!(assertion)) { \ 61 | PyErr_Format(PyExc_ValueError, message); \ 62 | return NULL; \ 63 | } 64 | 65 | #define DIND1(a, i) *((npy_float64 *) PyArray_GETPTR1(a, i)) 66 | #define DIND2(a, i, j) \ 67 | *((npy_float64 *) PyArray_GETPTR2(a, i, j)) 68 | #define DIND3(a, i, j, k) \ 69 | *((npy_float64 *) Py_Array_GETPTR3(a, i, j, k)) 70 | 71 | #define FIND1(a, i) *((npy_float32 *) PyArray_GETPTR1(a, i)) 72 | #define FIND2(a, i, j) \ 73 | *((npy_float32 *) PyArray_GETPTR2(a, i, j)) 74 | #define FIND3(a, i, j, k) \ 75 | *((npy_float32 *) Py_Array_GETPTR3(a, i, j, k)) 76 | 77 | #define IIND1(a, i) *((npy_int *) PyArray_GETPTR1(a, i)) 78 | #define IIND2(a, i, j) \ 79 | *((npy_int *) PyArray_GETPTR2(a, i, j)) 80 | #define IIND3(a, i, j, k) \ 81 | *((npy_int *) Py_Array_GETPTR3(a, i, j, k)) 82 | 83 | #define U8IND2(a, i, j) \ 84 | *((npy_uint8 *) PyArray_GETPTR2(a, i, j)) 85 | 86 | #define U8IND3(a, i, j, k) \ 87 | *((npy_uint8 *) PyArray_GETPTR3(a, i, j, k)) 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /pyhog/pyhog.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | import features_pedro_py 4 | 5 | try: 6 | from scipy.misc import imrotate 7 | imrotate_available = True 8 | except ImportError: 9 | imrotate_available = False 10 | 11 | def features_pedro(img, sbin): 12 | imgf = img.copy('F') 13 | hogf = features_pedro_py.process(imgf, sbin) 14 | return hogf 15 | 16 | def hog_picture(w, bs=20): 17 | """ Visualize positive HOG weights. 18 | ported to numpy from https://github.com/CSAILVision/ihog/blob/master/showHOG.m 19 | """ 20 | if not imrotate_available: 21 | raise RuntimeError('This function requires scipy') 22 | bim1 = np.zeros((bs, bs)) 23 | bim1[:,np.round(bs/2)-1:np.round(bs/2)] = 1 24 | bim = np.zeros((9,)+bim1.shape) 25 | for i in xrange(9): 26 | bim[i] = imrotate(bim1, -i*20)/255.0 27 | s = w.shape 28 | w = w.copy() 29 | w[w < 0] = 0 30 | im = np.zeros((bs*s[0], bs*s[1])) 31 | for i in xrange(s[0]): 32 | iis = slice( i*bs, (i+1)*bs ) 33 | for j in xrange(s[1]): 34 | jjs = slice( j*bs, (j+1)*bs ) 35 | for k in xrange(9): 36 | im[iis,jjs] += bim[k] * w[i,j,k+18] 37 | return im/np.max(w) 38 | -------------------------------------------------------------------------------- /tools.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import os 3 | import shutil 4 | import matplotlib.pyplot as plt 5 | from matplotlib.patches import Rectangle, Polygon 6 | import numpy as np 7 | from mpl_toolkits.axes_grid1 import ImageGrid 8 | from importlib import import_module 9 | import vot 10 | from matplotlib import cm 11 | from numpy import linspace 12 | 13 | def Tracking(Sequence, tracker_list, visualize = True): 14 | 15 | if not os.path.exists('results/'): 16 | os.mkdir("results") 17 | 18 | print 'generate images.txt and region.txt files...' 19 | with open("images.txt","w") as f: 20 | while Sequence._frame < len(Sequence._images): 21 | f.write(Sequence.frame()+'\n') 22 | Sequence._frame+=1 23 | Sequence._frame = 0 24 | with open("region.txt", "w") as f: 25 | f.write(open(os.path.join(Sequence.seqdir, 'groundtruth.txt'), 'r').readline()) 26 | 27 | print 'start tracking...' 28 | 29 | for str in tracker_list: 30 | print 'tracking using: '+str 31 | import_module(str) 32 | 33 | if not os.path.exists('results/'+str+'/'+Sequence.name): 34 | os.makedirs('results/'+str+'/'+Sequence.name) 35 | shutil.move("output.txt", 'results/'+str+'/'+Sequence.name+'/output.txt') 36 | os.remove("images.txt") 37 | os.remove("region.txt") 38 | 39 | print 'Done!!' 40 | 41 | 42 | if visualize: 43 | visulize_result(Sequence, tracker_list) 44 | 45 | 46 | def visulize_result(Sequence, tracker_list = None, visualize_gt = True): 47 | fig = plt.figure(1) 48 | if tracker_list: 49 | assert (type(tracker_list) == list) 50 | result = {} 51 | start = 0.0 52 | stop = 1.0 53 | number_of_lines = 1000 54 | cm_subsection = linspace(start, stop, number_of_lines) 55 | 56 | colors = [cm.jet(x) for x in cm_subsection] 57 | for str in tracker_list: 58 | result[str] = open('results/' + str + '/' + Sequence.name+'/output.txt').readlines() 59 | while Sequence._frame < len(Sequence._images): 60 | img_rgb = cv2.imread(Sequence.frame()) 61 | plt.clf() 62 | gt_data = Sequence.groundtruth[Sequence._frame] 63 | if tracker_list == None: 64 | pass 65 | 66 | else: 67 | for str in tracker_list: 68 | tr_data = vot.convert_region(vot.parse_region(result[str][Sequence._frame]), Sequence._region_format) 69 | if Sequence._region_format == 'rectangle': 70 | 71 | tracking_figure_axes = plt.axes() 72 | tracking_figure_axes.add_patch(Rectangle( 73 | xy=(tr_data.x, tr_data.y), 74 | width=tr_data.width, 75 | height=tr_data.height, 76 | facecolor='none', 77 | edgecolor=colors[tracker_list.index(str)*number_of_lines / len(tracker_list)], 78 | )) 79 | tracking_figure_axes.text(100, 20*(tracker_list.index(str)+1), str, 80 | verticalalignment='bottom', horizontalalignment='right', 81 | color=colors[tracker_list.index(str)*number_of_lines / len(tracker_list)], fontsize=15) 82 | 83 | else: 84 | a = [] 85 | for point in tr_data.points: 86 | a.append([point.x, point.y]) 87 | tr_rect = Polygon( 88 | xy=np.array(a), 89 | facecolor='none', 90 | edgecolor=colors[tracker_list.index(str)*number_of_lines / len(tracker_list)], 91 | ) 92 | tracking_figure_axes = plt.axes() 93 | tracking_figure_axes.add_patch(tr_rect) 94 | 95 | 96 | if visualize_gt: 97 | if Sequence._region_format == 'rectangle': 98 | gt_rect = Rectangle( 99 | xy=(gt_data.x, gt_data.y), 100 | width=gt_data.width, 101 | height=gt_data.height, 102 | facecolor='none', 103 | edgecolor='r', 104 | ) 105 | 106 | tracking_figure_axes = plt.axes() 107 | tracking_figure_axes.add_patch(gt_rect) 108 | 109 | else: 110 | a = [] 111 | for point in gt_data.points: 112 | a.append([point.x, point.y]) 113 | gt_rect = Polygon( 114 | xy=np.array(a), 115 | facecolor='none', 116 | edgecolor='r', 117 | ) 118 | tracking_figure_axes = plt.axes() 119 | tracking_figure_axes.add_patch(gt_rect) 120 | 121 | plt.imshow(img_rgb) 122 | plt.draw() 123 | plt.waitforbuttonpress() 124 | Sequence._frame += 1 125 | 126 | def precision_plot(Sequence, tracker_list): 127 | start = 0.0 128 | stop = 1.0 129 | number_of_lines = 1000 130 | cm_subsection = linspace(start, stop, number_of_lines) 131 | 132 | colors = [cm.jet(x) for x in cm_subsection] 133 | fig = plt.Figure() 134 | plt.xlabel('Threshold') 135 | plt.ylabel('Precision') 136 | plt.ylim(0, 1) 137 | max_threshold = 50 138 | gt = [[data.y + data.height / 2, data.x + data.width / 2] for data in Sequence.groundtruth] 139 | gt = np.array(gt) 140 | for str in tracker_list: 141 | precisions = np.zeros(shape = [max_threshold]) 142 | result = np.loadtxt('results/' + str + '/' + Sequence.name + '/output.txt',delimiter=',') 143 | positions = result[:,[1,0]]+result[:,[3,2]]/2 144 | distance = np.sqrt(np.sum(np.power(positions-gt,2),1)) 145 | for p in range(max_threshold): 146 | precisions[p] = float(np.count_nonzero(distance<(p+1)))/distance.shape[0] 147 | plt.plot(precisions,color =colors[tracker_list.index(str)*number_of_lines / len(tracker_list)], label =str) 148 | plt.legend() 149 | plt.show() 150 | 151 | def overlap_plot(Sequence, tracker_list): 152 | start = 0.0 153 | stop = 1.0 154 | number_of_lines = 1000 155 | cm_subsection = linspace(start, stop, number_of_lines) 156 | 157 | colors = [cm.jet(x) for x in cm_subsection] 158 | fig = plt.Figure() 159 | plt.xlabel('Threshold') 160 | plt.ylabel('Overlap') 161 | plt.ylim(0, 1) 162 | plt.xlim(0, 1) 163 | inter_p = 100 164 | gt = [[data.x, data.y, data.width, data.height] for data in Sequence.groundtruth] 165 | gt = np.array(gt) 166 | for str in tracker_list: 167 | Thresholds = np.arange(0,1,1.0/inter_p)+1.0/inter_p 168 | overlap_precision = np.zeros(shape=[inter_p]) 169 | 170 | result = np.loadtxt('results/' + str + '/' + Sequence.name + '/output.txt', delimiter=',') 171 | endX = np.max(np.vstack((result[:,0]+result[:,2],gt[:,0]+gt[:,2])),axis=0) 172 | startX = np.min(np.vstack((result[:,0], gt[:,0])),axis=0) 173 | width = result[:,2]+gt[:,2]-(endX-startX) 174 | width[width < 0] = 0 175 | 176 | endY = np.max(np.vstack((result[:, 1] + result[:, 3], gt[:, 1] + gt[:, 3])), axis=0) 177 | startY = np.min(np.vstack((result[:, 1], gt[:, 1])), axis=0) 178 | height = result[:, 3] + gt[:, 3] - (endY - startY) 179 | height[height < 0] = 0 180 | 181 | Area = np.multiply(width,height) 182 | Area1 = np.multiply(result[:,2],result[:,3]) 183 | Area2 = np.multiply(gt[:,2],gt[:,3]) 184 | overlap_ratio = np.divide(Area,Area1+Area2-Area) 185 | 186 | for p in range(inter_p): 187 | overlap_precision[p] = float(np.count_nonzero(overlap_ratio > Thresholds[p])) / overlap_ratio.shape[0] 188 | plt.plot(overlap_precision, Thresholds, color=colors[tracker_list.index(str)*number_of_lines / len(tracker_list)], label=str) 189 | plt.legend() 190 | plt.show() 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | def imshow_grid(images, shape=[3, 10]): 199 | """Plot images in a grid of a given shape.""" 200 | fig = plt.figure(1) 201 | grid = ImageGrid(fig, 111, nrows_ncols=shape, axes_pad=0.05) 202 | 203 | size = shape[0] * shape[1] 204 | for i in range(size): 205 | grid[i].axis('off') 206 | grid[i].imshow(images[i]/np.max(images[i]),cmap=plt.cm.gray) # The AxesGrid object work as a list of axes. 207 | 208 | plt.show() -------------------------------------------------------------------------------- /tutorials/Visualize_deep_features.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "# This notebook is used for visualizing and understanding deep feaures\n", 12 | "\n", 13 | "%matplotlib inline\n", 14 | "\n", 15 | "import matplotlib.pyplot as plt\n", 16 | "import numpy as np\n", 17 | "from matplotlib.patches import Rectangle\n", 18 | "import torch\n", 19 | "from torch.autograd import Variable\n", 20 | "import vgg\n", 21 | "\n", 22 | "import utils\n", 23 | "from scipy import misc,ndimage\n", 24 | "import tools" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 2, 30 | "metadata": {}, 31 | "outputs": [ 32 | { 33 | "data": { 34 | "text/plain": [ 35 | "" 36 | ] 37 | }, 38 | "execution_count": 2, 39 | "metadata": {}, 40 | "output_type": "execute_result" 41 | }, 42 | { 43 | "data": { 44 | "image/png": "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\n", 45 | "text/plain": [ 46 | "" 47 | ] 48 | }, 49 | "metadata": {}, 50 | "output_type": "display_data" 51 | } 52 | ], 53 | "source": [ 54 | "img = misc.imread('example.jpg')\n", 55 | "\n", 56 | "img_m = utils.get_subwindow(img,pos=[212.,212.],sz=np.array([100,100]))\n", 57 | "img_s = utils.get_subwindow(img,pos=[212.,212.],sz=np.array([100,100]),\n", 58 | " scale_factor=0.9)\n", 59 | "img_l = utils.get_subwindow(img,pos=[212.,212.],sz=np.array([100,100]),\n", 60 | " scale_factor=1.1)\n", 61 | "plt.subplot(131)\n", 62 | "plt.imshow(img_m)\n", 63 | "plt.subplot(132)\n", 64 | "plt.imshow(img_s)\n", 65 | "plt.subplot(133)\n", 66 | "plt.imshow(img_l)" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 4, 72 | "metadata": {}, 73 | "outputs": [ 74 | { 75 | "data": { 76 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQ4AAAEACAYAAABCu5jVAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzsXWlsHOd5fubYmd3Ze5fLm8tDokTqtHzKVxAnPtrEKYIA\niZH+aZH8SNGiv9r+CNogP1q0BRKkv4oURZGmaQI0MRI0SJ04h2M7suNTkq3TEkWK97HkLvc+5+gP\n5v00w0PcnVlSK2cfwJAlLmd2Zr55v/d43uflDMNAG2200UYj4O/0F2ijjTbuPrQNRxtttNEw2oaj\njTbaaBhtw9FGG200jLbhaKONNhpG23C00UYbDaNtONpoo42G0TYcbbTRRsNoG4422mijYbQNRxtt\ntNEw2oajjTbaaBjinTrx+Pj4bZtkKpUKUqkUBEFAqVSCIAhQVRWapqGvrw+hUAiJRAKyLEOSJPA8\nj+36bq5evcoBAMdx+9KUYxgGR//v8Xh2PKeu63jooYcwNTWFhYUFPPfcc/j+97+PwcFBzMzMAAA6\nOjqwtraGL3zhC/j2t78NRVGgquqWY5VKJQ4AnnrqqR3PV6vV8Oqrr9Z9HZ/+9KeRzWbB81v3ll/+\n8pccABw+fHjH8xmGgXg8jj/8wz/EI488gm984xt44oknUKvVEI/HEY/H8eMf/xg3b97Ed7/7Xdx3\n330oFArQdX3Lsa5du8bu6alTp3Y8p6ZpuHjxYl3X981vfhN/+7d/i+7ubkiStOXn58+f5wBAEITb\nXqP5T5/Ph3K5DFEUoWkaXC4XeJ6HKIrI5/MQBAG1Wm2n786ucXR09LZrNZPJIJPJQBRFFItFiKII\nVVUxPDyM6elp9n2CwSAGBgZQLpe3HGNiYoLb8o8N4I4ZjtvBMAwsLCwgHA6D53m4XC6oqoparQaP\nx4NMJoPZ2VlwHIeDBw9CkiRomrat4WhVGIaBYDAITdMAAIqiAIDFMLhcLgDAxMQEOI4Dx9l/1oIg\n1P1ZnuehKAqy2azt84nixtKq1WqoVCooFosolUrgOA6qqkJRFOTzeUiSBK/XC1VVHT+/7YzOTrh0\n6RJkWd7WMNYLnufZ8wOAfD7P/l/TNGiaBr/fj2w2C8MwoKqq4+eo6zpWV1cBbKwVnucxMjICVVWx\ntLRkuYeSJG270TQDLRWqkJGQZRnVahU9PT1YWlpCIpFAoVCAy+WCz+fD0NAQnn76aWiaBlEUkUwm\n9+wG7QVEUUQ4HMaFCxcQCoXQ0dGB3/zmN8wAEmKxGARBwNraGjiO23G3uh3sLNKRkRHMzc2xl78R\naJqGarWKXC6Hhx56CJFIBGfOnMHp06dRLpeh6zoEQWAGpFQqged5SJLUkHEzw+Vywev1oqenp67P\nx2IxiKLIvk+jMAyD/cfzPGKxGADA7/czz5j+zvM83G43OI6DJEm2jSNtnEePHmXPRRRFeDweTE5O\nYnFxEZ2dney7AGCe+F6gpQyHrusoFosoFosAgMuXL4PjOIiiiEqlAl3Xkc/nMT09jbfffhtutxu6\nriMYDDqy4vsN2l1nZ2eRTCbx5JNPYnJyEtFoFKVSiX3uwoULiMfj8Hg8bOE0ClrcuVyu7t8ZHByE\nqqq2DIcgCBBFEV6vF0899RRqtRreeecdHD58GOvr65AkCZVKBel0GtFoFBcvXkShUEA4HLZ1PmDD\nq6nValhfX6/r88FgEKFQCOvr67YMB8EwDAQCAayurkIUReRyOWb4DcNAsVjE+vo6MySaptlep6Io\nwufzIZlMQpZlAEC1WkWhUGDedrFYZN4IsBE6FQoF29d3O7SM4RAEAZqmQVVV6LoOURTR3d2NeDwO\nWZYRjUahaRqKxSIqlQoAYGBgAIqioFar7ZllbRY4jmO7UblcZotpdXUV169fBwAWhtHnFUVBb28v\nc4EbvUZd11GpVFAqlSwLajf4/X5omtbQS6XrOlRVhSRJ6Ovrw8jICLLZLE6ePIlQKIRAIABRFBGL\nxRCJRHDhwgX8+te/xuLiIsLhsOWe1AuO4+ByubC+vo63334bZ8+erev3RkdHce7cOfA83/A93ewx\nVKtVdHd3Q1VVBINBuN1u8DwPWZaZoacX284a1XWd/a6qqsxbAoBQKISjR49ClmUoioJUKsV+TxAE\n5HI5217cbmiZt43jOBiGgWq1CsMwoGkaCoUCVldX4fV6US6X4fF42K5cLpeRTqexurrKcgGtDEqS\n0cthfqDnzp0DYI3Rw+EwvvrVr8Lr9WJpaYkdo15wHAee5yEIAmZnZzE9PV3372YyGciy3JDhMAwD\nlUoF0WgUg4ODGBoaQrlcRm9vL06cOMF22r6+Pni9XuRyOfzmN79BuVxGR0cH8vm8JUyrB4IgIBqN\nsmRyPfjud7+L3t5eXLt2DZFIpCEvh4wGbQL090KhAFEUkclkWPhTrVZZiEJrm4xAI6Df5zgO2WwW\nV65cQbVaBcdxSKfTyGQyCIfDEAQBbrfb8rter7fhe1ovWsZwzM3NoaOjAw888ABu3ryJ0dFRuN1u\nFItFJBIJlliq1Wq4//77USwWMTAwgIGBARZvtjrMD3Z8fJy5nMBGcjSZTDIj2N3djVwux9xgURQb\ndnN5nmehXCOQZRkul6vhRUcvis/nQz6fx9raGqanpxEIBHDt2jXE43GsrKxAVVVMTU0BAEuMlsvl\nhp+hpmlIp9MN3ZfV1VXMz89D0zREo9GGvQD6PBnmUqmEWq0GSZIgiiLLK7hcLpTLZZb0FgTBtofD\n8zwWFxfh8XhQqVTg8/kAgBUNCoUC+5MQi8Wwvr6+Z5tqyxiOzs5OZlEBYHp6GuVyGbIsszCmWCzC\n7/fj8uXLEAQBxWIR6XT6Dn/z+qBpmiVx9ZnPfIaFXMBG2GWOkaenp/G9730P77//PkqlUsPxPy1s\nt9ttOc9uiEajADZekEZeSEpwFgoFeDweXLt2DdFoFNeuXUOxWMSVK1dw4MABaJrGkpixWAydnZ3M\nDW8UtVoNU1NT6Ovrq/t3XnrpJaTTafj9fng8nobOR54DcKuiYRgGyuUySqUSVFVl3kC1WgUAFoJR\nlaVRiKKIWq2Grq4uxONxzM7ObqmqKIqyxTs0DAOhUKjh89WLljAcFKsKgoBEIsE8C6pR083p7OwE\nx3Fwu92QZbnhOPxOQtd1LCwsMAOwOR6fmJhgnwM2dihBEGxfn67rqNVq0HWd5VDqwdjYmK2XmOM4\nRCIRJBIJ6LqOJ554Aj09PXC73fD5fIjH44jFYlAUhRkKjuMcJyeTySTGx8cb+p5OzknGwuVyYWRk\nBH6/H5IkIRwOAwALYcj70nUdLpfLdlJU0zR2fJfLxQx7MBhEMBjE0tISAoEA82wIXq/X9jXWg5Yw\nHABQLBaRy+XYjXG73TAMA+FwmFnvarUKnudZTBeNRu8awwEApVIJQ0ND8Pl87JoAMAPBcRwLK+6/\n/37UarUtC6JeUL4omUwiEonU/XvRaNRW2MdxHLxeL+bm5pBIJODz+dDT04NCoYBSqYTx8XHcuHED\n8/PzeOONN/Dmm2+yZLHdl8owDHAch/vuu6+uz3u9Xsfrhc6p6zoWFxdRKBRQrVaRSqXg8XhY5YRC\nL5fLZasaRuA4DpVKBUtLS8hms0gmkwCAdDqNfD6P0dFRLC4uYnl5ecv33Eu0hOHQdR39/f04ePAg\nCoUCHnroIfT390OSJCSTSQSDQZTLZaysrCCTyWBqago+n4/xG+4G1Go1qKoKl8uFzs5OS+lQlmV4\nPB7IssyuZ3x8HJVKxXZyi1zmQCDAytv1wC5BSRAEuFwuxGIxlEolyLLMKg1LS0uYnZ1FOp2GJElY\nWFjAxMSEJcFoB3RP33vvvbo+Pzo6ytx7uyDvWNM05PN5VnrmeR6VSoWFiJSTokqT3XUqCALy+Txk\nWcb09DQEQYDH42EVnMnJSXR0dFhCWcqx7CVawnDwPI9EIoGFhQUUi0VcunQJ6XQauVwOPM+z3bm3\nt5e9EJqmwev13hVJUQBsEV29ehUDAwOWUMXn8yEcDrNMOQAsLS0hl8tBFEVb12gYBnw+HyvH1gtJ\nkmzvyhzHob+/H6VSCcFgEMvLy/B4PDAMA+vr6zAMA6OjoxgaGkIsFttSBWgURCRbWVmp6/NTU1Ms\n32AHVBmp1WrsmdB61HWd0QgoPKEwmyqFdlCtVtHZ2Qmv14tkMskSsplMhjGpk8mkxTM1l4L3Ci1h\nOMilS6VS0DQNgUAA+XyePRDiaRSLReTzeXR0dMDv92/LwW9VmHecd955x7KQSqUSEomEZUG/8MIL\nLDSzA8ryN5IYBWCbhKXrOlZWVhCJRPDggw8yT6NSqSAQCCAajUIUReZl9Pf3O/Y4XC4XhoeHWUJ9\nNxSLRZTLZdv3lLwJqhz5/X643W5LSKmqKgKBAEKhEFwuFwzDcERtJ6YtlXlpHZFHQb1cFOIDG/mP\n34scB9W9OY5DqVTC+vo6Ojo6EAqFmMtLCVIAWF9fh9vtZhb9bgC9yMAtQ0nI5XJQVdWSi6hWqyxJ\nacfNpdi6EQIQvch2iUrJZBKjo6Po6+tDNptlbF9FURAOh5HJZFAqleD1etHd3c3OZ9d4SJKEWCxW\nt3HkeZ4ljO2CXl7DMBifSFVVFItFdj3r6+ssSaxpmqNz8jyPcrkMr9fLqouU21NVFblcDhzH4ebN\nm+x3yuXynm+qLfHW8TyP/v5+lqE+deoU4/YTa7JSqaBQKKCjowPArarD3QJBEFgZslQqoa+vb4tB\nIFq4oigYGRlxdH0cxyGVSmFycrLu3zl48CCy2awtQ2UYBtbW1qAoCo4cOYJ8Ps88ps7OTnR3dyMQ\nCODIkSNYWlqqu4t1t3OOjY3V/fmvfOUrLKSwCzOZKxwOs1AMAOuzoupHvb0ztwPlSiivomkastks\nRFFknveRI0csuamOjo7fj+Qoz/O4ceMGcrkcdF3H2bNnkUwmGS+/v78fnZ2dcLvdyGazCAQCrNvw\nbslxaJrG5AGAW1Ti7RCPx3HPPfdYeAONolgswjCMhnpUIpGI5UVoBFRNWFtbQ0dHB0qlEvMKDcOA\n3+9nG8PPf/5z1vHr1Phv1xK/EwYHB217VMAtb4O8iEwmwzxlYGOnp4Qtkd+cdsNS02A+n2fEL8pt\nFItFxGIxFqqZma17XW1sCcPBcRwj41BJixKfhmGw/g3DMNDb28tuCpXG7gbQAujs7ASAbWnSxPwL\nh8O4dOmSI8MhCAJjFNaLWCyGWq1m655Sl2ipVEIul4PP52PG3e12o1wuo7+/H+vr61hdXWUNik6S\neLVare6KCs/zOH/+PAB7oR+BqkfARlKb53lLVUPXdUb1J2awk3XqcrkQiUQsoWs4HEY6nYbL5UI6\nnUYkErGsE6rC7CVawnBQZ9/y8jIqlQr8fj+SySRLFlLsqKoqq1dT5vpugaZpEAQBhw4dAoDbMl57\nenpw/fp1uN1u2zwOszGuFz6fD7Is2zJWlIOq1WpssRNBzzAMSJKEoaEh5kmePHnScR+Fqqp196lI\nkoQf//jHjrwc2skpZEilUqjValhbW2N5OBLxoY5dcwu+HdRqNaRSKdy4cYN5N1TKJ7ZuIpGwfMdg\nMGjhCe0FWsJwCILAXM6uri7ouo5YLIZQKIRisYhMJoNkMomenh62Y5NIyd1CAKPdIZlMsmsl1xOw\nNr2Fw2E888wzrPRmB1T+bQR0P+2GKqOjozh16hTee+89aJqGhYUFVCoVVCoVrKysYHp6GpFIBMVi\nEdevX2esSCcegPmluR1isRj+9V//1ValyQxBENDX18fuEZVfgVuehc/ng8vlYn0rTq5PkiQcP34c\nACyheSAQYJ6Nee14PJ59qTa2hAIYWXGe57G0tARFUZBIJJhAiyRJ8Pv9uHLlCuMKNEJqagUYhsFK\nZ5IkoVqtQpZllvwlyn21WsXKygozkHZ3ZU3TMD8/X/fnOY5jcnR2XGuqFAWDQdbOXSwW4XK54HK5\nkM/nWRcpz/NYX19HKBSyzVMBGlM1i0ajjLBl98Uir3dtbY1Rz80t82R0iSvSjFBa13XMz8+zigqB\n1ks+n7dsntTbtdch/B33OCiZRApQJARDLjOVlkRRtGStzTXtuwHU9pzP55lknllqrlqtsnif53k8\n//zztjpGAbCXo1HGKLngjd5XVVXh8XjYi9nT04NcLge/3w9FUeDxeFjYVCwWkUqlcPjw4R11YuuB\nJEkNharHjh3DP/7jP9oyGoZhQBRFBAIBVpUho0XJT13X4Xa7oaoqYwCbKzB2zlmpVKCqKnw+n6Vh\njYoElUoF+XzeUsan9oW9pincccPh8XhQKpVQrVZZfFgul1GpVNjiCIVClniRrC3gLNG1X+A4DrFY\nDF/84hdZ0oooygRziED8ALsvlaZpTJKgXtBLaKd3RBAEZDIZvPHGGwDAVL6CwSBjxZLq1srKCsrl\nMh5++GFHFZVqtYpoNFo3+7Srqwvnz5+3Lb9Yq9VQKpVYQpfWIxk/4lsAYNVB4m/YWaOUqzAMA++/\n/75lkyENE9pQqX8FcN4cWS/uuOEoFotQFAULCwtM8atUKjGDoqoqBgcHmWCJHX2JVkCtVsMvf/lL\nLC4uAoCF0AbAotsQDAbxsY99zNZ5DMNArVZrODn20EMP2TofAEa1/shHPoJPfvKTrGejUCgglUoh\nnU5jamoKbrcb169fR7Vaxcsvv2z7fAAYm7LefEUsFsOXv/xlWzsxbVbUa2RmgpL3Ye5qJgUwwNnG\nls1m4fV6LbIKdHzaaD0ejy2dD6e444aDeiOIBbewsMBKefSALl68CI/Hw0Rt7ha2KMEwDKRSKdy8\neZNl3zcvKNo5BUFAMpnERz/6UVvnIo3WRhes3eoNsPGd/X4/Tp48iWAwyEYBmHdi6vAksaKZmRlH\nHBwS8an3GOvr6zh16pTtzl/yxChPRccxhy6Uh6CqCv2uXVBylZTigVtt9hSOkG4qGa69ppoTuLuF\nQNVGG220Du6urbuNNtpoCbQNRxtttNEw2oajjTbaaBh3jADG7TLL9fDhw7h27RqAjcTd7UqLsizv\nmF03fjfLdbfzKYqCcrnM6u9UjyctEJKap++xU7nLMM2O3e2c/f39WFxcZIkuc8s29UEIgoD19fXb\n9q3QOXmev+35aNYJid1yHAe/3w9goxxrLuvRz7eDruuO5vFSyz9px1Iv0k5o5J7WA/OYit3OKYri\nbc9nToian9Hm50VJ/Z0IfaqqNvUad4P5ntpBSzBHt8OhQ4eY4diNj+CEQkygTLlZ/5P6S6gTstlY\nW1uzGCAzO9Bct6efORlzSf1Am2fTVqtV1oBnhhNy1m6gikOlUtlzpart0MxxobROAKvO5+Z7R8rn\nTpiyrYSWDVXMwiT7AVmW0dHRYWmso7GLw8PDtpWxbgcaSgzcYifuVGp2utiJ3VipVBj7VlEUKIrC\nZoAAsHR57iVIg2WvBgbtF+plr5L3+GHBHSvH3s4dCwaDTdvh6w1VfvcZthtQ6ABs1NPNOpP1nG+3\nc+4Uepi9Dq/XW9fsz9uFKiSIVKvV0NnZiaWlJeZNGIbBJuNt9urInd8uXHEaqgBgym71GI5mhyr1\nYLdQhTgdlUqF3StiPm/3XElaYKemt7stVGlJj2O7G7+X1HIaXEQdjcBG96H556QY3ohwzO1gVsM2\nn8eMUqlkWwiGDAOFXiRd6PF4WF8FGcfN3tR+UJaPHTvGGvmajb1uQzBrjwK3BiPtJCxF3t6HyeNo\nScOxnfu3154RDfSlODSdTrPFQUKx2Wy2aTE5CdmYQ5DNDVH08tvtdQBuMQ2JbWhe7PTvm72NnXbN\nZkEQBJw9exbd3d17cvy9/O7kKZrZnNRav1M4S2Ho3SQ8tRtaynBQO3AjXY9OlY5EUWTZfeBWssss\nv2b+Ps3KdWx2WWlBGr+bAgZYpeDsgBZrPp+H1+vF6uoqE74lT4bGbG7GXr18FCZ5PJ661btaCfQs\nzLR+Mv475aGcijK3IlrKcNBOZ56xuhucVlRIobparWJ1dXXLLr/ZC3DqcdBxqYpDf/d6vazPYHPc\nb7ctmxaqJElsvqiu6yiXy5AkCbIsM0GYzd/RqVbmTqDvUKlU7roXie4phXrmxjPCdveMOqHvth6r\n26HlrsSJzqZdUMnVLDxrdus1TYPL5XI8QAi4JehjLuG5XC5LgpLjOMfnIs/JMAwEAgGEw2H4/X42\nczcYDMLv9yOfz28RNN6P+3+3KLeZYS69UgcwYDX02907JwLJrYqWuhqe5/Hxj38cly5d2vEzjepo\n1gPzgzfH3bQIyLDshSQbZeTNilHVapV5Uk4MKS1sEoTZnIjdzOvYL/zTP/0TvvjFL+77eZsBCjEV\nRbGsm82ehjn8u5vU+OtFSxHANicLga0vjl0NztvBfI6VlRXGRKWqAy2QZnlDbreblVlJ54HOaRgG\nqzYkEgnbxC8yQiRanMlkmOHjeR7pdBqqqm57P5sdohw4cABTU1Ns5/3KV75yRwyW0wHQBJIHNHtN\nNHDK7JEQPiwJUTNayuMAsMVF346B10xQss4copgXVzgc3vG72IU5JqYX17zQSqUSMyxOXHqKyWVZ\nxsrKCpvNoSgKKpUKvF6vpey8V5icnLToV9BQrf1GM4wG6Z1sNhw0vvT3BS1nOHYbINRsQ2ImfHV0\ndDBdyXA4zPpEmnEeAsnQmasqlBSlOLhQKNRF/KoHJPasKAoTCaIJ6wC2zFLd6xyTJElsGPXdCKr6\nNboePmxeR8sZjomJiYY+73SRm+e5mhOF6+vrbIdyOhzZDMMwWEWFjrnZSBiGweQRnXgcJPpMQ5bM\nXhUpZG8+x15zDarVKnw+3x1JjjaDvEf3x2xwb0fsIrLYhy3H0VKU89t1udrFbpTz7boYHUro100d\npnPfbpdvpJPTTDmn4yqKwrpgM5kMKpUKfD4fS8jWarUtLvZuhqMZlPNG0GqUc5fLxdbpbu8PeSe7\nhTFtyrkDPP300/t6PopXgVu5FVVVLWXZZsMcFuy06Mw7o9OOWON3sz/W1tYAgI2d4HmejZkwYz9c\n6v2Y+7EZzWoVIAX5egldpIj+YUPLGA6ap7KfIOoweRjBYJAxRndqQHOK7WL7zedq1mhLnufR19eH\ncDgMSZKYajzRzc2iuzt9l73Ak08+ue+ue7NGInq93rqOZd6QPmxhCtBChsPn82FqauqOnNvMEqWY\nFNjadOY0a07S9rsxDc0K2U52ZiKtiaKIYrHI8h1Uht2JlNTMhR6NRi1/5zjO8WgEO2iG0Td+NyTJ\nLL0AbG/oyVMkgt+HDS1jOCRJYjNH9gObX0gaWWh23ZudwNM0Dfl8/rZMQ3OPjFNUq1Xk83kUi0VL\nyzd15W4XBjWbar5ZVcwwjD0fiLwdmlUqJfEj8zFvF4oQefDDhpYhgG2nCbGXML+w5ge7lwpNbrf7\ntsnfzT0iTo1IZ2cnZFnG3NwcMxp+vx+hUAgejwfpdJoNbSZP626kgu8XKK/RyPq4nWd3N6Nlroj4\nE3cStAs77UrdCbuJAVEDFRkyJ/eD+CLJZBIej4d5Grquo1gsYm1tzTLpnZKobewMuqeN/s6HjcMB\ntIDh6O/vB7A1n7BXuB3xiF4c+i7m5iQnD59+t6enZ9vzUwzcrAVmGAYURUE4HIYsy6xvwuv1Mgr9\nXlD3bwdznN+MZsH9Bt07EkDaDpv/nQalfxhxxw3H/Pw8m3C+H9gpVCApOMAaxtCu7yR8od+l4dqb\nQbtYMynthUKBkZTo+JTDIYO4nzuheafei2bBvUY2m4Xf77eMW9yMzf+uKMqHMr8BtIDhADZemMnJ\nyX07F7CxA5rFWEilyeVyseRhM3cLmqG6ORwwsw+b5daS6tfQ0BAKhQIymQw6OzthGAb6+/sRCARQ\nKBS2VDz2A3eqT8Up3G43isXitkZvp4pNNpvd6691x9BSzNG9wG7M0d/9jD7ruFejXpbjbudpJFG5\nHXOU4zgMDw8jkUggn88zRqx5yLfdzlgnzFFJkhquqrQKc1QQhIa+O7Uq1HNP7zbmaHvodBtttNEw\nWiJUaaONNu4utA1HG2200TBadnbsdqCyIuk90n+34x/Uk+MAgMHBQczMzDT6lXY8Xz3nNLM3Ozo6\nsLa2ho6ODtY7MzAwgFKphBs3btR9zv1EX1/fjten6zpKpRK+9KUv4d1338Wrr76K8fFxXLp0ieV3\nhoeHoSgKPv/5z+NnP/sZJicnty11LiwsNHRPAViGSXm9XoiiiHw+zxTXADBdlO1ySdvljbb5DL7+\n9a/jO9/5Di5cuABgY3RpIBDAjRs3UCqVWBWP1hcxeLe5X3Vfo8/n2zIitFF8qLpjzdiu4Y3ovoIg\nwOfzWUR/naIZRqNRmDs2qXuVaOKGYcDr9SKVSu3792oGBEFAJpPBq6++ClmW0dnZibm5OYuUQK1W\nQy6Xg8fjgcvlcjzqArAyf+lPWZbZnBxZlhnJzmkVi45rfkZmw2cu/e/U/2QHTo1GM9CShmMnRfF8\nPg+O4yDLMnK53B1nmjoBz/Pw+/1sdizxKrLZLNMgvXTpEjOMzXip9hNUyl5cXES1WmUDjMLhMPr7\n+yGKIh544AEcOXIEoVAIFy5caEp3tHlmDCGZTEIQBASDQaTTaQAb95v+axREO1cUxWLcieJv7mcB\nbqmG0f87wZ2QJNgOLWk4eJ5HJBLB8PDwtj+nh0CGw+mN3O92fmDju3/xi1+Ez+djxCLaIWloUj6f\nZ7yBZgsc7TXIq1haWsLU1BSb55LNZpHJZOB2uxEIBNDd3Y25uTlEo9GmCBg//vjjGBsb2+J1kGgR\nAAuJy+7mw/M8Dh8+jDfffJOtv1QqhXK5jFqtZum7Ip6Sz+dz/Bz3espevWhJwwFsDJ6enp7e8u8c\nxzH+QbN6ShrZzZtp7S9duoRkMolcLsfCFkmSMDw8vEXE+E4Yt0ZhbgLTNA3hcBhPP/00uru7EY/H\nWU4nn89DEAT86le/QjweRzabZd6kU/h8PnzwwQcQBAGKorB/93g80DTNIuMnimJDHodZ/FmSJHg8\nHqiqyqQf/X7/jjkTkht0ilaZP9tyhoOYm5lMZstN4nneMn+E4DRkaYTU0wxrT+709PQ0qtUqeJ63\nfIfZ2VkwM3mEAAAgAElEQVTUajUmKAw4UwLbD5hn0KyurmJ+fp7pml64cAGyLLNnds899+DEiRNY\nWlpCLpfD22+/bTtsIJAXSslX6schFnC5XEYul2ObhMvlspUfIylAl8sFn89nYTx7PJ4tqvHmBH4z\n8nH71ZqxG1rOcOi6Dq/Xi8XFxS0vCxmSWq3WNCk4Ol69aMauWCwWEYlEcOHChS2t9BQjJ5NJy2iG\nVtlpdoK5+5aGWkUiEZbbuH79OrvG8+fPY3h4GLIss2dNVRgn5+c4DpcvX97yPI3fTc8DgHg8zpLq\ndrpdPR4P6zDu7u7GysoKALAB3pR8JXi9XrhcLuatOF0/d0LLZDu0nOHw+/2WkpkZ5od8p1rAm+Fx\nGIaB7u5uSJLE3Ho6rlk8eHV1lf1Oq7e8k54HJUKBDfV2TdMQj8dRqVRYInh4eBiBQACxWIwlu+1U\nODiOs3hlHR0drFpCPwduDfriOA6JRALBYND2cCa6xieeeALhcJiNzyDvam1tzeJ1UD6lWX1IZimE\nO4mWMRzmZi+v14tQKATgVuhi/gzQ+q77TqDFE4/HceDAAQiCAEmSWA6jXC6z1vu7KSFK7rh5Fk0+\nn8fZs2cxNTVl8RKnp6fh8/lw+PBh1slrB2YjbhgGOjo6cODAAdbMRwln806fyWSQy+Vs79xkbIaG\nhizr0Sz+bC7PCoLQ1OpfMz1tJ2gZw0EvTqFQgKIorGxmnphl1uu8G5KFm8Fxt4ZJ8zyPq1evQtM0\nVKtVnDx5Ei6XC4qisBepFcpujYDjOKysrDAv49ixY4hGoxgcHIQsy1haWoIgCDhy5AjjzezkXdaL\nQqEAt9sNTdPw4IMPYnZ2FgCwvLyMnp6eLaFgOBx2NMuV1h/NvSGQ9kkmk2HhCwAmht0stEOVbaAo\nCsrlMi5cuMB4DR6Phz10M+uuWa47zWndD3Ach0gkAgB44YUX4Ha7mYzf2bNnUavVEIlEMDAwAEVR\nWqLsVi9oZ/d4PHjkkUcwOjoKTdOQSCQwOzuLfD6Pnp4euFwu5HI5lEolHDhwAEtLS00RgQaA//mf\n/2EhSXd3N5aXly2VEOLJmAWpGwVtYqIoWjRy6Vml02lLO72maTuq5jcK8sJbAS1jOKrVKsrlMiO4\n6LoOv9+PYrHIHjz9ScmmZmC3kZPNhK7rWFxcZO5muVxGX18fSqUSPB4PfD4f3G73XWc0ADDBoN7e\nXpw5cwaZTAaBQACiKOLZZ58FACwtLUFRFMzNzeEnP/kJPB4PlpeXHe/IxWIRwWAQ1WoVsixDEAQs\nLS0xhfFwOIxYLMaEdUiisVEEAgHk83nEYjEIgmCpqNCm1t/fv0WMmnIcTkFeeCugZQwHwfzSmGPf\nSqUCURQxNjZmGR/glMK73xJ6hmGgr6+PLaS5uTkAGy9ePp9HPB7HX//1X6OrqwuAlXXY6jCHBJIk\nwe/3Y3x8HOfOnQOwMSrB6/Wiu7sbhw8fRjAYxNzcnGORasMw4PP54PF4UC6XWUKSGJx9fX34i7/4\nCxSLRSiKAr/f76hKRcbBHDbQWM3N6vFkOJqxEbTSOmgJw0E7Dsdx6O3txcjICPu7+c9SqYSpqSl2\nA+3uHHcap0+fxr333gvgVrKNyEovv/wy/u7v/o5VVGhoVKuDSp602968eROKouDixYs4dOgQgA3q\n98GDB1lCOBgMIhAIWIhadjE6Oop77rmHVWnMRKyFhQV87WtfYypepVLJVnhEyWq32w1BECyELkpo\nb0fyalauqpXWQUsYDrPWZyKRYIxRerhmnr+u60zg2Cn2M8Fq9oxu3LiBqakpiwoYLQqPx4OxsTF8\n+ctf3rfv1kzQiytJElZWVhCNRtkgcVEUMTs7C7/fjxs3bkBVVSwtLTWl8Wt5eRlXr14FAGYYyCAR\nye6RRx4BcKsrtlGYmcqqqjJPyVyK3s6DbQZ/o9XQEoajUqkgGAzC+N0kd9qNiTxTrVYZ0+/48ePb\nUtHtYD9Lurqu48SJEwCAd955B5/73OdYCRHY0Kfs6emBpmkIBoP4yle+sm/frVlYWVnB3NwcBEFg\nuahcLof5+XkAYOMng8Eg/uzP/ozxPJwgFosBACYnJ/HZz34WHo8HQ0NDLD8WiUSg6zpkWcbLL7+M\ngYEB2+eimcIjIyO4fv06S4663W7IsgxFUbY0Z7Y6/8YuWsJwABsPhchQZMnJTae2awB466234PP5\nmnLOZh2nXphHXFKL+fLyMjiOYxPkXS4Xfv7znzP3/m4B5RRqtRr77qurq5ZKAM/zyOVyePTRR3Hl\nyhVUq1XW72EXtMPHYjHMz88jGo0il8shl8uxnIqu65ibm0Nvby8WFhbY97UDSZIYX4M8ZeJqCIKw\nRaC4WcQvqsa1ClrGcHR3d8Pr9aJWq+HKlSsQRZElR82CKDS/E3AeO+63rsHx48dx5MgRcByHn/70\npwgEAoxTQKMaC4UCKpXKHZujaxfEzgQ2Rl74/X6cO3fOMgJS13X4fD5kMhk2z7ZUKjmSDAgGg+jq\n6kI6ncaLL74Il8vFCGipVIqtHQovnObEenp6GGOU1o/5mJvXFAkWO0Wr6bK0jOGYm5uzJMloERJz\n1OzymQf63k144403EI1GLXNWCPTy0NQ1c+XobgFxYiRJgqIoUFXV8tyonHnz5k14vV4sLy878jhE\nUcTKygorxZIH53a7mRdHlQ/DMJDL5VhHrN37SnQBCr+AW1oqqqpuSWA2qxTbamgZwxEIBLbdecia\nUxntbgUNfAqHwyzsokVNibpwOIxPfepTGB0dZb93txhHqnBFIhGsra0hk8lgYGDAQrCjkvPFixcx\nMjKC9957D6lUyna1wDAMhEIhxONx1mRHM4hdLhfK5TKCwSAOHz7MRowSh8buy/zoo4+iXC6zqhcJ\nBIVCoS1lV7/fv0UX5MOCljEc+Xx+29CBrLnZHWzGQ9jvYUQ08Onq1atbdthIJMKapEql0r6S0poF\nwzDQ2dnJdDfoeVHYAGwYRr/fz7yR3Wbp7gZN05DL5VjnLR0vGAwiGAwytqimaSiXyyiXy44rHMTT\nIE9KEASW49jcW3Q3UgXqRUsYjr6+PhaSbH6oFKaUy2WW82iG4djvxOjjjz8OURQxNzfHOoDpWiuV\nCvL5PHK5HC5evIilpaV9/W5OQWQomn5HEn3z8/PMSJLwzeHDhxk5jNSy7JRGqR3B7Xajq6sLIyMj\nrFs2k8kgk8mgVqthbm6O9c9IkoRKpcJo4PXCPKHe4/GgWq0yI0G9NtTgZ0YoFGqK2FQrhjot0SmW\nTCZRrVYZvZywedpZI9PNdoO5z2A/8MYbb7D2eUEQWPs36TgAt1SiAoEAMpnMvn4/J6C4//Lly4jH\n45bhzGZFcb/fzyjhZDjtegAUGnm9XszMzLCXW1VVKIqCSCTC8h40Q7dcLts6F31PUg8jw2Oupmia\ntqWi0qxB6mTwWgl33OOgBxsMBi0Wmx6WpmmMqEXSb83AfrqRgiBAVVV85jOfscgE0PcolUqsryOZ\nTNpuM79ToJI5GQdFUZDP5y35DcMwsLCwAFmWWROfE44Dz/OoVCoYHx9nHgCNaKxUKlhcXEQqlWL6\nIGQw7Hir9DsDAwPo7u5mFHq3241oNIpYLIaVlZUtxr5ZvSWtGPLcccNBHZU0T2QzNE2zVFGalWTa\nT2IOnSsWi8EwDLbz0Q5pGAYKhQKq1Sqq1epdRxpyuVwWUaJyuYxSqWR5cURRRKlUQnd3N2RZZl2q\ndkEvE5V7i8UiBEFAJBJhOTEag0AGxe75iItBpWTKQVF4VigUUC6XtyT3m8VMbiWqOaE9O7aNNtpo\nGHfc42ijjTbuPrQNRxtttNEw2oajjTbaaBgtO3S6GYN1gVvDdV0u147nc7lc+Id/+Af81V/9FYAN\noZb5+Xl8+tOfZhPIXnnlFQC32J47Jb5qtRrLwA0NDe14Tp7n8c///M947rnnAACPPPIIrl69iuee\new6lUgnLy8v4xS9+YUkGj4yMbJs4nZ6ermuwtiAIGBgYsHQX/8Ef/AFOnToFwzDwgx/8oK4emXoH\necuyzKodZm1RVVUhSRLcbjckSUIymYQoijsmAc0Dko8fP77jOTVNw9WrV3H69GkcOHAAP/jBDzA8\nPIzJyUl230ZHR9HV1YXe3l6srq5alOTNuHjxYl3XSKMs4/E4FhYWkM1m0dvbi1OnTiGdTkMQBHzz\nm9/cNeFtvsbbrVVZlnHPPffg3LlzqFQqrOvaMAzMzs7CMAy43W4kEglwHGeZpWuGeZ3aQct6HPtZ\nt65Wqzh27BijgudyOYyNjWFlZQXJZBLr6+uoVqsWxqfTyodhGDh69Ch6e3sBbDSGnTx5Evl8HplM\nhrEcCZvZs3ZAJCzzMSuVCtbX1xGPx5smV0AgoV7DMCzVIpJKyGazbNh2MysHZCg6OjowPT1teVYk\nnKwoCkRRdEyu8ng88Pv9SKfTTDeWBm53dXUhGo02tUpWqVQwOTlpEQwicmRnZydj5QJgVbu9QMsa\njv2YWGXuLejs7GSty+VyGffeey/W1tbw5ptvsodkFk12ck5gY8Hlcjk2g2N9fR0HDhzA8vIyfvvb\n31pEYoCN++FU5tDj8VhEkDweD+tQbSa5jkCt9kSSMg/UMivWA84HTtFoSQBsKPmBAweYQSL+TGdn\nJ5NuTKVSjscNeL1e9PT04PLly4xbkkqloOs6+vr6mmYQiYrgdruRTqfB8zxr5pufn0exWIQsyyiX\ny+yavF5v03hPm9GyhmM/tECJ4u52u/F///d/bDc2DAPpdBq9vb3o7+/HjRs3ANzyMpyUsIlNGQwG\n8frrrzPD4fV6IYoiOjs7EY1Gce3aNcu5vF6v49mjHR0dFsasKIps4PWrr77q6NibQQbPPOjZbJx0\nXbfcT6e7sqIorJEwk8kglUox1x3YMCyBQADz8/PsexQKBccv1kMPPQRVVREOh3H69Gn09/fD7Xaj\nVCrhvffew9e//nVHx9+MAwcOsA2vWCwyA0zXUygUsLy8DACOuCu7oWUNx06xZzMhiiJkWcbIyAh+\n9KMfYXp6GpIkMfp7f38/YrEYSqUSU5oC7FGJzWK2iqKgq6sLL730Et5++22IoohEIoH19XX09PQg\nFoshm83i4MGD7PfNYwztwuVyMSEbYMMQRqNRBINBXLx40dGxzTBTtMk7EwSBLXKzFCT9Z+eekhGm\nObHlchlutxsPP/wwKpUKBgcHLfq0gUAAr7/+OlwuF5MXdOplpVIpGIaBlZUVJhjU3d2NaDSKtbW1\npsxBMdPyKT+j6zo8Hg8ikQj8fj+babN5jewVT6slDYc5Dt9LVKtVRKNRPPPMMywp6PP54PV6sba2\nBrfbjffee4/9O8FubwWwsfNFo1GcPn2ayet3dXWhs7MT+XwePM/jtddeA3BLFo9k/Z241RTPmxPO\nNH9kYWGhqUIxZAgosUyoVquM0QlYmcB2FjjlfVwuF4rFIhKJBEKhEDo7O7GwsICZmRnm8cTjcQwO\nDrJJa4uLi46HQQEbAlRdXV0YHx9nHoCiKFhZWWnq8CSadZvNZpFKpeDz+VAqlSCKIiRJgq7rCAaD\n+9a82ZKGwzwPdK9ArvPy8jKeffZZtphpaFI2m0U2m8XJkycBbKh2A2DyeI3u/mZ3vFKp4Pjx46wp\nyjAM9Pb2YmVlBZIkMc3VK1euANjQKqnVao6Usqgd3PyiqKrKhkI1q+mPGsHI46DwgUSMBUFg4wnI\ny3AiqhMMBpHP55mBqtVqyGQyLGFJmq7z8/MQRZHNrllfX2ct904gSZJl2HWhUEB/fz/GxsYwNjbm\n6NjALW+DtEynp6dRKBTY+qPnWSwWsbKyYsnD7SUrvCUNx1439dDCpgVMiUoqDT722GMYGRnBxMTE\nloaz7dqn6z2nJEnIZrPw+/3o6enB8vIyZFlGMpnE+Pg4Ojs7ceHCBTZrhZqmaFd1gp6eHotx4Hke\n0WiU6Zw2C2Yvgowz9RtpmsY0NOjn9KfdRU4NgmSEDh8+jJmZGbhcLhQKBUuu7NixY+jp6WEq7E7j\n/3g8jlQqhfPnz6OnpwcrKyuIx+N46623UCwW8a1vfcvR8Ql0H2VZRiKRgKZpzLNIpVLs3uq6viXE\n/73KcRw7dmzPz0FDgsfGxnDmzBnwPA+e5zE6OorOzk4WjzdLJJY6ZMmjeP3115nw7T333IPOzk4m\nbrN5LmkzBg0fPHjQ0r4fjUYxMjKCrq4ux7kTglkGIRwOs13S5XKxsIC4BZTbcGK0VFVlOZSlpSVL\nRzVpcJiTs6SFutnzsgvDMDA4OAifz4dgMIjBwUGEQiEEg0FEIpGmVDTIEJfLZUQiEeZ1UpPgZukJ\n8+/tJVrScPzmN7/Z0+PTzSYh2f/8z/+ErusYHh7GysoK/uu//ot5FsvLy5YX1+5LRpqUoVAIqqri\n3LlzqFarLB5/8cUXmYYFaWMSmtFl2dPTwxYylfXcbjc6Ozvxve99z/Hx6bgksJPNZlEul9nLS5P4\nNE1jxC+nPAPS80yn0/B4PJAkCT09PUgkElAUhSnHAxt5omKxiHA43LTSczweZzoc09PTGBgYQCgU\nQkdHB2ZmZpoqyEQbDBHqRFFkw6xCodCWittelWHZ8ff06DawH4lRiq9XV1fxxBNP4IMPPgCwodiU\ny+XwqU99ColEAvl8HqOjoxZjYdeSE4+gUqlgZGQEv/jFLwCAlWM/9rGPYXl5Gevr62xXdHpOAnEW\nzNdB7n0ziXaUyzBXKxRFYdeoqioT1CFPwIm3U61WkU6n4XK5UKlUIEkSSxiaeR0knLSysoJIJMJG\nUjiBKIo4fvw47rnnHui6zpKU09PTuHTpUtO0OGj+Ld1bv98Pj8eDTCbDjC/NsaHvBey9aljLGQ4n\nCcB6QQpVqqrimWeeYcbq3XffhcfjYbqfoihaHgpg35KLosj0Gx599FHmxczMzIDneRQKBXi9Xhau\nmF14p7uH2+3G7OysZQes1WqIRCJ46qmnmqa/SgpnZCAEQUClUmF5BkmSoGkao0oDzoZiVatVhMNh\nVl2hgd1ut5tVNKiKMzQ0hFwuB1mWWa7FCSKRCK5cuYLXXnsN4+PjTE/24MGD+KM/+qOmMTYNw4Ci\nKCiXy7hx4wZEUcTAwADLe1DuY2hoyMIa/b0zHIcPH77tz5vhgpkl/AKBAKuDUxJP13UcOnQIFy5c\nwPXr1y3KTnbPTwOngsEgarUaY/XRvwMbBK2zZ89ukdl3es2RSATd3d2Wf/P7/YhEInj33XcdD30m\nmDP6lL+gqgoA5mZHIhGWx3HicZDxJSOhKArW1tbg9/st4Z3P52ODqYnw5vTF6u7uZnmbZDLJRkLQ\n1MFHH33U0fHNICLX/Pw8yuUyKpUK3G43XC4XZFnG0tISotGoZYNrVt5qJ7Sc4aCwYSc0IzYlZShR\nFPGTn/wE6XSaxeY0hpKMxY0bNyw5Dru1eUrkCYKAyclJVCoVZrzMosWGYbCqCrDhLTgNJ3w+3xbq\ns8/ng67rmJmZwcrKiqPjE8zVFHN/Cp2bKizr6+uMN+Jk5zd7FsBGq4DX691C7BIEAUeOHIEsy0yJ\nzOmLFQqFWBl7YWEBhmFgeHgYPM9jamoK58+fd3R8AoWTY2NjyGazEASBafPS9Ver1S3NiU5Zxrt+\nrz09ug1kMpmmlge3A8dxiMViGB8fx7e//W0MDw8z1+/48eNYX19Hb28vG97TDCKPpmmIxWI4cuQI\nfvSjH6Grq4slE8fGxpih2jwycHOFxQ4ymQzLMwAbxDLS/gwEAk2p2myGy+ViFQFizdL1ulwupkfq\n5AUmxu3c3Bzru6EXxpxjIONs3hicolar4bHHHsPg4CCADaNPg6+9Xi8eeughx+cwIx6Pw+12I5fL\nMR5RuVyGz+djnjOtFScDp+pFyxkOYO81FhVFQTabxZNPPsmIXgBYY5SiKFhdXWWx8+YuVTsgz+He\ne+/F7Oys5Zzj4+Pspdo8MrAZ/RSqqlp2pN7eXvT19eHo0aMolUpNMYzEFiWDYZ5hQqVoM8cjkUgA\ncOZBFgoF+P1+VqmJxWIs7t/cyRyNRhGNRlGr1eB2ux0lnImQ9cYbb2B5eRl+vx/33nsvent78YlP\nfALDw8P42te+Zvv4ZtBsGEmSUC6XWWmb53kUi0WWn8tms8xz3KlE20y0lOGIx+P7ch6qvz/zzDPw\n+/1YXFyE3++Hz+fDmTNn4PP52CT1Zu3Gmqaht7cXQ0NDAIDZ2Vn4fD4cPnwYb731Fnp6epBMJi3c\nBACMTmwXlIk3J0ZLpRLcbjc0TcMbb7xh+9hmkFEgY0HxNiVEadcXRZF1dQLO8jdENQfAdt3V1VVM\nTk5aPBlRFLG6uoparcaElJ3syIqioFKpIBAIoFAooK+vj83FpfIwMY2dgoZMraysQJZlRuMnz4qq\nR2bZh/2Yw9JShsM89WsvYd6BqQWaJn+Nj48jm80imUxuG6bYteSk/t3d3Y1kMolMJoOenh7ouo4D\nBw4glUohk8ls4TY4zc53dXXB5/NZhj8risKqDZcvX3Z0fDNompqqqmyWCS18+k/TNMv8HCdG0Zwf\n8nq96OvrYz0o5twJeUGyLCOfzzNiml309vZiYGAAN2/eRGdnJ2NraprGRmA2S4OD8mDXrl1jDOJ0\nOs2Yr4IgoFAoWEZpOp1WVw9aynBQvLjXoHj3xRdfRCgUAgA2dYxKk+Y+FTPsxuS0677yyissFk0k\nEiiVSohEIigWi1heXrbMInG73YzsYxeUEDTv7MQLMM+saQY4jrMMDqd/o5GQlL8xv1ROXGpBEBj7\nNpFIIJPJQNf1LTOGiarN8zySyaSl0c4ONE1DOByG1+tFMBjE7OwsFhcX8cEHH2BycnLLPXACmnqX\nTqctoR7lNsrlsmW0SDMEn+pBSxkOauraa5AS1s9+9jPLdC8i1JC2webJXICzhqyhoSG8+uqr7Li1\nWo1VN1wuF3K5nOUF93q9jnktpE9hflk7OzvR39+PUqnUtIoKcGt2ChkI0tww74BO+lI2g2jkgUCA\nTW4LhUKW8jLlI4LBICRJQi6XY/kWu/B6vZibm4MkSTh79iz8fj86OjrQ39+P3t7epjZpGoaBI0eO\nME9UFEUW7tG8Wr/fz0Jq8sJ+r5ij+9HcRujp6cHMzAzjUdCgIEmSMDExAcMwLAlFehiNLjizjoau\n65icnGRhEHEBPB4PlpeXUalULC8yeQtOFvnmIVbU9u12uzExMdGUUZPE2zA345GhICNC98Cc8Xfi\nThMXhio23d3dUFWVVR0IsiyzJLBhGKhUKpbJbnZQKBTgcrkYG5c6jAOBAFKpVFO0ZAxjY0g56YqY\npxluHja1+b35vUqONqPsuBvIbc7n8/j7v/97cBzHchjFYhF9fX3I5/PM1TTv0pStb/SB0AvF8zy+\n9KUvIRgMsuy4ruvo6upCsVjcdkThZnk9O+jp6bG8SOFwGC6XC263G0tLS00hf1GHra7rlurJZs9j\nc5XKyeIWRRHZbBa6riOXy8HtdqNcLrO/E3w+3xaNCqc5AApT1tfXEY1G4Xa7Icsyy600q0eFwlvz\n8cjrIKIi5ZHMa/X3Kjl66tSpfTkPubOkR6nrOiRJYqW9arWKo0ePbnH1nD4MWmyJRIKds1aroaOj\nA6lUCgMDA1t+pxlUc1pkBCJIFYvFpuU3qNxKBDcCvaC0e9LidnovyTCRt9Tb24t8Pm9JlhJIo5Nm\n2zrdifv6+nDixAkcPnwYa2trjDm6urrKtEGaESZQLsjj8WBubs6ipEYle8MwkMvlLNqp+2E0gBYy\nHKTrudeg7tO3337bIhwcCoXYrnnu3DmmPt4MUDVhYWGBVU78fj+r5Oi6jg8++KDp6k1dXV1MMQrY\nuPZ4PA5ZlrG2ttY08R5ynyuVyhbjQLumpmmWCfVOPCkKG+lcFGJSR64ZiUQCsVjM8sI5QX9/P1wu\nF86ePYuBgQGk02kMDw8jGAzi8uXLCIVC+PWvf+3oHATycGdmZiw5IvJE3W43I70RqW2/DEd7dmwb\nbbTRMFrG42ijjTbuHrQNRxtttNEw2oajjTbaaBh3bHasx+PZMbmi6zqq1SqeffZZzM/PIxgMIpFI\n4OrVqyzRRvJvp0+fhqZpO84FKZVKHAB89rOf3fF8xOP47//+b2SzWZZsy+fzlmSax+PBX/7lX+Ly\n5ctQFGXbRNvzzz/PslNjY2O3TSB94hOfwL/8y7+A4zgMDw8jkUjg4YcfRjabxYEDB/D6669DEARM\nTU3B4/FgeHh4WyrzBx98wAHAD37wg9ve02984xt45513bveV8NWvfhWSJKGvr49d82Z87nOf4wCg\nu7v7tteXSqXQ19eH3t5ezM/PwzAMLC8vM8WuBx54ACsrKwgEAlBVddu5IACwvLzM7unjjz++4zkF\nQcD169dRrVYhCAKeffZZnDlzBt3d3XjwwQfxwx/+kJ2/VCrh6aefZlPRNuPMmTN1zY6NRCK7jpbo\n7e3dNRFtmGbH7nbOv/mbv8ETTzyBiYkJfPDBBxgfH0csFsPIyAjy+Tz+7d/+Dc8//zwAbKmqbXc+\nO2hJj4NKT9euXUOlUsH777+P2dlZALdKfCdPngTHcQiFQrh8+bKj0qIoirh58ybLTFOvxWbDoCgK\nXC4XyuWyY36FYRhsDgiwUX0YGBjA0tIS1tbWcPPmTczMzDAS2sDAgKO+FUEQ8O677+76uXA4jGw2\ny5rgnMDML+B5nsn6kQCNJEkIhUKsMcxpGVPXdXzsYx9Db28vcrkcvv/970PXdcRiMaTTaRw+fBjx\neBxHjhyBqqp47bXXHFdZRkZGdv1MM4eL0YZFeifEiyGe0cLCgmXo1u/F7FgqmdFim5qagiRJSKfT\nbEwBlfO8Xi8Mw2AMQrsvMt30paUlRpQij2ZzeXRgYIBxI+wucjOTs1qt4v7774fX68Xw8DBGR0cR\nDofh8/kYiSkcDgOwLzVAQjokrQdscBu2440AwKFDhxAOh1kfiF3Q80ilUkgmk1hcXEQ6nWbEJVmW\n0U0pW/8AACAASURBVNXVhZmZGbb47b7EVILM5/MIhUKYnJyE1+tl5LaJiQk8//zzmJiYwMTEBC5d\nugSe5/Hkk086Nhyblb4298kAzZWJ8Hq9rJmwXC6z+wmADWwyD93aK7SU4SA9SjIcwWAQmUwGR48e\ntezMxKYbHR1lQ37tLAAyGr29vRbFJmpFNytvEVV6ZWWFaYTaAQnZSJKEkydP4rXXXkM0GsUrr7yC\nM2fOoFQqoVarMe+HDEgkErG1e/A8z5qx6JjlctmiMkYYGhrC//7v/yIej2Nubs4Wl4Vo3eZnSOMs\no9GoZQzk9PQ0k7wjyT07IAq2z+fDj3/8Y/zxH/8xNE3D5z//efzJn/wJOjs7WScydZOKoogLFy44\n3pFp0h9hs1pbs0WSHnvsMdx333144403MDc3h/vuu49tdOvr68hms0zrRJblLZKRzUJLGQ56+ACY\nRBotLPMk8lOnTmFubo71k0QikYYMh3kEYaVSYSK2wC0DUSwWLT0AXV1djOVpl8BEv0eK3D/60Y9w\n9OhRLC4uYmhoCIODg+B5HsFgEJOTkyw+VRQFmUymIXIPkc5UVWUyAbuBOnOJQm5Hfo6IXsBGF261\nWsXIyAjW1tbYvNZ8Po/+/n4UCgVEo1HWJm4Xuq6z8QdHjhzBt771LXR0dODcuXN49dVXMTo6iscf\nfxwf/ehHMTw8zLxa0kZxgs3DujfLMJw4ccLxOQikE+vxeBAOhxEMBiEIAhRFQTgcRi6Xw7Vr11i/\nE93rvUBLGQ7SGqB+h3K5jPPnzzNKM7BB+U0kEqhWq4hEIohGow3reJA2Q61Ww+rqKnK5HNspyKXf\n3P7d1dUFj8fDqNV2Q5VarYbBwUGcOHGCCbA89thjLCk7ODiIK1euIBqNst1is+p5PTD3MeTzebYL\n3Q69vb0QBAHVahU+n69hw0HNbLlcjsX1Xq8XV65cYbM/VFVlz3B1dRXVapUluu14jfQsstksCoUC\nWzuzs7MIhUK4ceMGJicn8dJLL+Hb3/42jh49Ck3T8Pjjj2N9fd1RXqWrq2vXzzSLyen3+xlD1e12\nIxqNshDd5/Oht7cX2WwWly5dYvdRVdWmCVFvRssYDmpAo5eFjIKu64yOzvM8EokEHnjgAbjdbvh8\nPqytrUGW5YZcTuripF15c9hBczLMcLvdTPGJGrbsXCPP87h06RL8fj+Wl5cxOzuLcDiM7u5uNutl\nYGAAyWSSeQA0Z6WRRU66FPl8HsvLy5bM/06t+jQpjLouG5EUpHyTy+Viw517e3uZt7S6uso8rlwu\nh+HhYTZ2k66t0e5oMlSUGDx58iRu3rzJqOUDAwPwer04efIky0VcvXoVwEYIQYOh7KIeJfNmiRaT\nl0GdyCS5QFKIfr8ftVrN0uBHOih7gTtWjjVD13WEQiGsrq6yXS4UCrGFvL6+zlzRfD6P2dlZxs8v\nFosNZ/+r1SrW19eRTqfx8MMP45e//KXl59s1Q5Gug8/nYxoTjYDyKRzHIRgMMtFcVVXx+uuvY2Rk\nBIlEAqdOnWIPm/IQoVCo4Z2Rem6eeuopLC4u4jOf+Qz72Xaq6aFQCG63G/F4nM0qaRR0fRQWCYIA\nr9eLzs5O5HI5BAIBJJNJdu/IaFAI0yioyYvKx7lcDr29vejo6MD8/Dzef/99xGIxvPjii1hYWLAY\np7W1NceCO7sZHUmSmqLnyvM81tbWmGzgzMwMa1SUJAmyLCOTyUBVVUvZd68qKkCLGA7arYBbi/rm\nzZtMO5JuACX6bt68CY7jmI5koyCNC0VRkMvltp26tfmB07AbuxacBGfz+TzGxsYwNzeHe+65BxzH\nYXl5Gf39/VAUBefPn0elUmHeh12IosiEfK9du7br56vVKiRJYrqZjcKszk5dq2tra8hms0xYt1gs\nwuv1IhAIoFgsIhQKOe5YJTGkhYUFnD59Gj/84Q9RKpVw6tQpVKtV5PN5HDp0CKdPn8Z//Md/WEZh\nOMXmkQSbDUUzwhSz+PLAwAC6u7vh8XiY4evq6kIsFkMsFmMze/YDLROqABsGhJI/sVgMgFVXUtd1\nfOpTn8K9996Lrq4u2xJw1FlJIjpmpS/SVNisbE4uopNFXqlU4PF4sLS0hFQqhZmZGTZjJBqNIpVK\n4ejRo5ifn2c5CpL5axSGYcDj8aBSqWzJ/G+HoaEhx12rRNwjBSy/3w+Xy8V2dpIvePDBBzE6Ospm\nhjgVY45Go7j33nuxvLyM+fl5VCoVXLlyBb/97W9x//3347333sPp06fR29sLWZabJnLz9ttvW/5u\nNhqyLOPjH/+443OQyjmwIeZ96NAhBAIB1Go1+P1+RKNRVCoVpNNpzMzMNHWk5+3QEoaD9CiJHHT6\n9GnWUvyFL3yB5QZ8Ph+ef/55/OpXv2JusZNFp6oqurq6LBPpt9txafjNdqSwekFucq1WgyRJuHLl\nCnvJyuUyzp07B6/Xi/fffx89PT1MOZti+EZBBrdYLG5bet2MsbEx8Dxvm0hnfh6iKDKVsWKxyDYD\nGpB8+fJl/Pa3v7WIGdsBkQFzuRxeeuklXLx4EZIk4ROf+AT+/d//HZ/85Cfx85//HKdOncJ3vvMd\nLC4uYn5+nu3iTlCPN3HhwgVH5wCsA8c9Hg/zTElflTzWiYkJlr/ZD7SE4eB5noULKysrmJmZYW78\nSy+9ZKkQUMKHNCbtLABFUSCKIsbGxnD27NktVZntFgUl0uy6n+aqwbFjxxhb8oEHHsDY2BhSqRS6\nurowNDTEwgZgYzi0HeMoiiLuvfde+P3+ukMVUrGyC1mWIQgC1tbWEAgEEIvFkM1msbCwwK63UCig\nUqmgWCxCURTbiWZg4zl1dHSgWCzi4MGDqNVqGB4exgsvvIDnnnsOPT09iMfjOH/+PJsjw3EcIpGI\n46QhEfN2gqqqOHTokKNzALAweGu1GgqFAnK5HFMgm5mZweHDh8FxHNbW1hyfr160hOGgXZUWkCiK\nOHfuHAKBAK5fvw5g40EcOXIEwMbLRIkxu7RoyjksLi5akmTpdHrLoiKKNFVUnIA0Iomv8f3vfx8H\nDhzA2NgYExY+ceIE5ufnAWzsMnZDlWKxaBk4dTt0dHQ4ZuBS8g4Ak/AbGhpCOp1GsViEIAjweDxY\nWVlBR0cHADC5PzsgD0cQBASDQfh8PnzwwQesF+XChQs4c+YMurq6UKlUGPdHURTHOY777rvvtj/X\nNA2vvPKKo3MAG2xYYtpSjw8ZeaIN0Nza/RLxAVrEcABgJSbg1gzQgwcPMpq0x+NhC5ESbkSNbhSq\nqoLjNob4XrhwYYtA8GaQOhiNK7ALMo6krK7rOvL5PObn5+FyufDuu+9iZmYGL7/8Mnp6egDAdnhE\nvSHXr1/fNWFGymBOro1UuOm7kjiwx+Nhoxdp6PP4+DgzMG6323bYoGkam0+r6zqOHTsGSZLwp3/6\np6wc/ed//ue4fPkylpeX2TlFUXTMb6AxGk4/sxuI7fuRj3wE4XCYsYpJRY5yNjR3eL/QEobDHB/H\nYjEmImxmL5J25OzsLNu5stmsLStLO1w6nUYsFrP0F2xHeiIdSafuLdHNibvR0dGB7u5uGIbBStFe\nrxc+n4/tiHZr8YZhIJ/Pb8n87/RZmpVrd/GRF0XNa5IkMY+HeCF0LeQF0ZQ3u6D7ksvlUC6XMTEx\nwapIsiyjUqkgm80iFArhrbfeYpMCydtxgjfffHPXzzSzue3EiRNsTdAICsojUbj3e+dx1Go1VKtV\nZLNZrK6uQtM0LC4uWh5OPp9nw2kkSWIumx1QAi8SiWBpaclCmtkODz/8MMrlsuMwxefzwev1olQq\nYX19HS6XCydPnsTg4CBSqRQGBweRz+eRz+dZXsKuDmm1WkVfX9+2pebN6OnpQTabdcSI5XmeudXU\nO1GpVDA/P49CocAMP7nYNITKSfmQ53kEAgGcPHkSly9fxujoKDRNw09/+lP09/fD4/EwN16WZTaW\nsRnciunp6dv+3Jxwbwb6+/vR39+Py5cvsxGb3d3d8Pv9OHnyJJaWlvDCCy809Zy3Q0sYDlEU2TT1\nvr4+ZDIZPPjgg5b5G+QKHz9+HKqqshfQrhtP8fxu7qQoiixZ6WSRa5qGRCLBWKGhUAgTExOsqS0e\nj6NaraJWq1nCJbuTxzVNQy6XYzmi26FUKrG438mu5Xa7kUwmWQUqHA6z2JsYjrIsY2xsjPEvnBhj\nWZYRi8WwuLgIr9eL8+fPY2RkhIWVHR0dbCi0eUdeXFx0FJbV87tHjx61ffydQMOmiUpA1T7y4vYT\nLWE4aJAOACwsLODBBx/Ee++9x0pRdIP8fj8mJiZYkojG4Nk5XzabxTvvvLOl+Wvzi6OqKhv8bK6p\nNwpRFDE8PIz+/n5Eo1Fcu3YN4+PjuHr1Kq5fv450Oo1arYZisWipFtk1jhzHIZVK4dy5c7t+dmBg\nYMsIAzsg4le5XMbBgwdZEjQQCLBSr6IouHHjBgtDnVSqyCB1dHQwjkx3dzceffRRRKNRlMtl1tuh\nqirzQIaHhx2xKusxCs0oxZpRLpfh8XjgdrvZBkNd3JIk1dXE2Ey0hOHQdZ0pTkUiEbz77ruo/H97\n3/bbyHme/ww5nOFheBIpUtLqvNrVHrzdg9d1bCcu7Bhw0BhtchfA6F0LFEWBFuhFgaJGbvIfNBdF\ngBQoil4ULZAGTROgaB3HiRs7G3t3veddabWSVqQokeL5POT8LtT33Y8UtStyRlr653kAw7taiSNy\nvnm/93vf532eep2/BuxG+Wq1ykUvImoNcv6vVqtMcT/Ig0JRfRCqOcEwDDx69IhnRyYnJ9FsNrG1\ntcUBkIKnWE8Z1CC53W4jkUgc6P0FAgGuS5jhN1B6Hg6HeYpYNA8SNwLqjJmxY2y1WshkMmg2mwiF\nQvjNb36DbDaLmZkZPv5tbGzA7/czD2h2dhabm5um3qfo77sfrLSBDIVCnGWoqsrSBPQs0NH3KDEU\ngYPMdRRFQbVaxejoKCKRCKt+Abu6Dpubm9jc3IQsy7hx48bAzNFWq4VWq8UV6+7fRYTL5UI8HufW\nr5nK9fz8PLLZLDRNQzabRSKRwOXLlxEMBvno9Ad/8Ad8bDt9+jSAwYyZdF3HgwcPDvS9ly5dwtra\nGqe/g4CIWACYkRuPx5FKpVAul1l4Zn19ncf8qXho5prVahX37t3jMflyuYyf/exnyGaz3CKuVCpQ\nVRXJZBJOpxPRaNRU4DjI8c8qt3oAeOedd+DxeHDr1i0WRaJsa2Njg+tIR4mhCBzAk4EoVVURCoXY\nF5RAYjRAZ8t0kEXncrng8/kOtJuLJr5mKedOpxNra2uoVCp8o+v1OqLRKHRdx8bGBj744ANuFVKr\ncRC02+0DE4JEHZRBQSxOp9OJZrMJr9eLcrncoX0yMTGBSCTC9Y5B6zcESZKwurrKg12BQIDNpVOp\nVIe0HhVEPR6PaQe7g3yuVpp5A+h4HjRNYxIjsLtOvnSBg1KvXC4Hr9cLh8OBzc3Njqr12NgYT//p\nus7Frn5Zh1QrKZVKCAQCPacju18vEAjwIqcJ136RyWQwPz8Pt9uNx48fsyDLCy+8gFarhaWlJSa8\nybLMRVjS6OwX7XYbPp/vwAUzsi4ctGBI05mtVovZoJVKBdvb22wLSQpmFLTpeDaojytJKjidThw7\ndgybm5vIZrP8O7z11lsYHR3F+vo6PvzwQ77XwWDQtJH3YWlc7AdZlhEMBpnYRi5u1LYvFoscqMxO\n/B4Uzz1w5PN5TE1NcepFfA7RhJoKP36/H+12G+Pj4wP5kFI6PTU1hY2NjZ6cje4FRTRqM9To+fl5\n/OpXv8Lc3BwURUE+n4fD4cDS0hKi0SjC4TBisRj7kBKdWdO0genmDofjQFoQPp8P0WjUVOG3WCzy\nPaGCJxUmKWuimRQKaKQxMihWV1dx8eJFjI2N8WdUq9VYMe7+/fvI5/N8TKlWqwgGg6jX66aynKPy\nOBaxs7PDk85ElKSsg6a2SaiJiIOHjeceOEKhEFKpFKeTmqaxMhbtgLTovF4vFEXBo0ePBiZjqaqK\nQCCA999/v4Ogs1/aPKjWp4iNjQ0sLCzA6/VicnIS6XSapd8eP34MXdexsLDAxWCzDEAyKz4Ih+P4\n8eMolUqmWs00dNVoNBAIBHD8+HFkMhn2kxW/Z2xsDK1Wy3QqPzc3h+XlZfj9fmQyGbRaLSiKggcP\nHuDSpUuo1WooFAqIxWJQVRWVSqVv+cVeOHHixDO/xwqCmYivfe1rOH36dEfdpl6vI5lMolwuY3l5\nmdfyQQYarcBz1+Oo1Wqc/lOXpDvFjkQiMAwDkUgE5XLZVMWaGI6bm5t7vt4LoVDItBWCpmk8q/Hw\n4UOur3g8Hvj9fhSLRZ7cBHZ3GDPnfyIIHWTEmrxjSMZgEFDLGtgNwMSTyOVykGUZsiyjXC5jdnYW\nOzs7CAQCph/gQqGAdrvN+hqkht9sNpHJZNirJR6P4/79+wgGgwcKpM/CQRTDqC1tBSi7aDQaHZup\n2+1GvV5HtVrl/wPWkNsOAtt02oYNG33juR9VbNiw8cWDHThs2LDRN55bjeNZ/pg9vp+ZiaQATlOr\nT/OOII9MRVH2vV6r1cJf/MVf4Kc//Snu3bvHsxQjIyNYW1vD9vY210DOnDmD27dvs1VCNxqNBh/e\nL168uO81JUlCOBzGzZs3kcvlWGKv3W6zWtbS0hJf99y5c/tOk169evVAPqdWgT7Tg1zvtddew6lT\np/DDH/5wDw/mK1/5CjKZDP7yL/8Sf/d3f4e7d+8+9XrPuia9/tjYGP7wD/8QP/vZz7C2tgZZltnf\n5G/+5m9w7do1fO9730MgENi3Zd3PeyS1cdIGEe/RmTNnsL29/cxJWfE9Liws7HvNYrGIra0tnD17\nlnkqy8vLAIALFy7gzJkz+OCDD/D9738f//M//4Mf//jHPaUilpaW/v/zju0Foi8TB+M73/kOBw+z\nUFUVsViM27Ok9yFJ0h6BYlLIMlvcoxseDAb5OlR4IyMj8bokE/dFw0cffYRisdiTlfrmm2/iwoUL\nphm5BHoNkioUg0Kj0YDf70cikWCGq1XsTtLCoI6RiIMEjX4QDodZVFpV1Q7bi5s3b+KTTz5BuVzm\nAGb1lC5hKAOHKA4sIpVKwe12Y2JiAj/5yU94oQwqd0fX0TSNTZAA8OIiTZB4PM40cILZRSeaBTca\nDdTrdTSbTTgcjj1V+UHIbsOEfD6/p93rcrlw5swZzM3NwefzPXNMvR+oqsqkNuCJuxzpgYgG5laA\nhJ7FAOFyuRCLxfDuu+9acg0AbFIWi8Xgcrnw8OFDTExMcFdS13XMzMzA6XRiZmYG//qv/3pohLCh\nDByGYeDMmTN49dVXAXTu7uSj8fDhQya9DPoQ07wKZS7i4h0ZGUGtVkMqlUIqleJsZGVlBZFIZOC2\nF413u91urK2tYWtrizkqhmHw6LTYLvZ4PJwOHwXMtp9FyLLMJs8i3G43rly5gmAwiO3tbZ5utQLt\ndhsrKys4ffo0bw6BQAAulws/+MEPWOuEWuRmIEkS1tbW4PP5OtahruvI5XJ4//339zXA6vc6kiRh\nY2MDgUAAoVAIFy5c2OPQd/bsWUxPT7OT3ZfCAlLEyZMn8etf/xpAZ0bhdrs5PaMHaVB7RNpxHA4H\nPvnkE54wVFW1Y8YC2OViOBwOzkQGBY2Xy7IMVVXRarVY7p5EbsTrA7uEoqM8qlh5HVrk3eK+kUgE\nf//3f4/5+XkW2LEqMEYiEayuriKXyzFhikbSZVnuGEE3mzkahoFoNMoZIQUiIjQuLS2Znseh35P8\nVVZXVxGPx3HlyhVmWnu9Xpw8eRKtVgt/8id/gkajgUqlYlrNfT8MXeCgIJBKpdibkx5Ul8vFehVU\nCCOlqX5AWpgkkhwKhTocxmjITvzQSdofAFPj+4EoStRqtVCv13kgrFgsMgOSFroYnDRNg8vlOvTA\nQYNuVjmA+Xw+vP3227h3717HzrewsIAXX3yRH4L3338fgDVHh8nJSYyOjnYMfrlcLp71yOVylhDB\nCGTuRPMvYsCPRCKoVCoDmXeLoM2FsLi4iH/7t3/D8ePHOeOQJAnr6+usV3Pv3j125zsMDF3goIfj\nypUrSCaTHf9GO1K5XMb58+f5zDqIPWKlUkEgEMD4+DhkWUY0GuVru91uGIbB8zI0I0Pprsjg6+d9\niVlOLpdjERbRCIl0I0Rommba8ewgGB8ftyQ4UfAnFqeu6x3BfWlpCd/85jc5EyDGrBXFypGREYyM\njCCTyXRcMxQKcd3DCtMiWnOBQAD5fJ7vjXiPxsfHTetyUFNgdXWVRxKy2SwURelYg7/7u7+L8+fP\n49e//jU0TcPy8jLK5fKh6XQMXeAAdnf37igtLuhyuYyVlRXelQeZWwkGgyzhd+HCBWxsbHAGs7m5\nibt373Kxq1gsstcstdz6hWEYPGwl+uCS7H273UY4HOYJYBEk9nvYgePBgwemtDGA3aBLAT4QCCCV\nSu0J7A6HgwvOZo59dB9ItwTYHSgkvQpN0xAIBHjgLh6PW1JvAJ6suXw+z/YP3djY2DA97t49Ou9w\nOPDWW2+hUCjgzJkzXNBfWVnB4uIi4vE4D8TRujoMDE3gEFP5sbExHDt2DJIk8fGAFjR9nxjlzVwv\nGAxidXWVv06S87Vabc/ORLYKg4DSWKfTyUeUdruNcrnMAcXhcMDv93cUCc3qZBwV6F50FwivX7++\nZ/G2223k83m88MILLPc/COhaotg0FSlrtRrXjTRNg6ZpLLtnJURVs25cuHDB9OtTDWN6ehq3bt3C\n66+/jkwmgzNnzuDGjRsclL1eL3dbms1mx5o+DAxN4CDU63VMTEwgmUzCMAyWtwc6lbBEWcF+IUkS\n7wRTU1Md7U/yau0W7iVndTPXJG4GGelQUCCfD/JWFRc3SewNisM64+4HscD58ssv97SwnJycxLVr\n1xAIBPZ0BQaBeP9IVjKdTkNRFMRiMZYuIO1RK0Gv53Q69xTpqbhvBo1Gg/1UKpUKUqkU8vk8rl69\nyjYQwK6vrK7rHCCt+FyfhqEKHHQevH79OjweDxwOB0ZHR3kCkrQxJUky1WYSvUrIwYwQCoXYP1ZM\nP62ovtPrNJtNxONx/lqj0cDU1BRyuRyq1WoHqcdsu7Cbf7IfRP0TMxAfHkqZxYxDkiRsb2/j888/\nx7lz5ywhR1GwImIUWSKQcBCpZZHU4GGA7qv4/s+dO2f6dam2Ra+r6zrC4TAWFxfxzjvvYGNjAwDw\n7W9/G1NTU7h8+TK2trYOrQ1LGJrAYRgGZwG1Wg21Wo01K4HdB8jv9yMej8Pn85k6O2qahmq1inA4\nzMUtArmsxWKxjgeYxJHNgAyLCoUCisUiZz6yLDO7slvgxizz76A7jxWj4HQEI1AaL9YVIpEI2u02\n/vu//xuRSATXr183dU1N0zoc2pLJJCqVCur1OmRZxvr6OkZHR1k4yUouDAX1iYkJJlqJx9mDCCk9\nCz6fD7du3eKsO5lM4uOPP0YoFMJ//Md/8LVu3ryJfD6PUqmESqViGSdmPwxN4CCQEpjIlHQ6ndB1\nHRMTE/izP/szlocza8kI7O724sNVrVa5hSbulIZhWOIbS2lkKBTi1/f7/dzibbVaHbuF1an1YaL7\nSFUul/H66693vIdYLIbLly/jnXfeQTQaNd0aFY91Ho8H7XYb1WqVv050dtGK0iq0Wi3EYrEOVTFN\n09ie0YzGCYGkF+koS10oykDEIu0HH3yAr33ta1haWvpyHVUkScLc3ByneCQKSzdlaWkJ//RP/wSX\ny4VCoTCwuxrtUNS9EdNXqj/0IpWZFSsms+BEIoFarQZZluHxeHi2gNquYsAyK6x7lBB/b6qtfPjh\nhx3fE41GsbCwgPfff5+PFmYgBo5oNIpqtcp+u6LjmSgYZQVoTZLcI712sVhkbpEVxzBJkjA+Po50\nOo3JyUlMTEywCh5hdHQUP/rRj1hiUJIky46e+2EoAgfdBMMw0Gw2WdafFoXotnbu3Dm89957pq5H\nvAlqf4pFUAoY3R0Vovya4TnQdRVFgc/nQ6VSYUHi7qlKghkt0MNiDR4EZP9IzmOEtbU1zM3NYXFx\nEc1mE0tLS5Zdk4IQdbDE+gYATvetAG0glE2JR2ey7bCCl0KbVT6fR71eZ1lNag5QzSgSicDlcmFq\nagrr6+uH3o0bisBB6uNk3vPmm29CVVXMz89D0zSkUil+yN966y389V//tamHgrobs7OzuHv3Ltcy\nqADrdrv3dCPMno1pAlZVVXg8Hty8eZMXHU0xkrcKgQbgBsVBe/hOpxNvvPHGwNfphcXFxZ7kp4WF\nBbzxxhv4xje+gXQ6benRwe/3I51OM9WcvFzOnz/PaudWgor3NBIgIhqNWjJgVqlUMDMzwxyO1157\nrcO8i4iKzWYTpVIJ5XL5wH46ZjAUgQPYfXhIrZkKlHTel2WZuyl/+qd/iq9//eumiS1Us8hkMnx0\nIZZeL60NYn2aIUhRUVd0oqd/03Wdp2QJxFg9bOJXq9XCL37xC0tfs1ar8WIXsbW1hTt37kDXdVQq\nFcuyAGp30+ZD64lU6ufm5iw/9yuKguXl5Z7ixMVi0TKGKh2lq9Uqrl69ikgkwhocwG7gP3HiBKLR\nKEszHFb3iH+vQ331PkCzIe12G7/85S+hKApTkVVVRbFYRC6Xg67rrOQ8aArfbrcRj8cxPz+PXC7H\nN1hVVei6znaTIujMbIYARqk5eX7W63U+GlFw7OZwHBUPw2qGYTQaxdWrV/cEYJ/Ph2w2i0qlgkQi\nwcLMg0D8ObfbzZ4pNH1sGAa3Z9PptOmZkW7UajW4XK6eAkTz8/OWfKZivU2WZS6ki1hcXMTv/M7v\nsJI8cPA2/KAYisDhcDhQKBTg8/n4AatUKjxt2L3rEv/eTL3B7/cjEAh0BAjyI6nX63uUoRqNhqmM\nA3gy10BetHQ94InCu7goiC79RUD3veh1byRJQiqVwp07dzAyMoL19XUEAgFTVheEycnJjslUPs5C\nMAAAIABJREFUmial7I58aq0EEbN6YXV11ZLAQcr4VLSn9rKI8fFxaJqGc+fOsYH7YZtGDUXgICr2\n9PR0hygJqW8RYeqVV17hYiG5ng2KEydO4MGDB5zKjo2NwePxoNFoYHt7u2NB0JHB7Hi0LMuoVqs8\n81KpVNgDxeVycfpOOCrxHqfTicuXL1v6mt32E8ATVu79+/cRDofx85//3BR/RKyh+P1+aJqGZDLJ\n0gfk8SJJEvL5PI/vmwVlnvl8vqelJLFUrbh3iUQC6XQa58+fR6vVQqFQQLlc5izZ5/MhmUwikUhg\nZGQEt27dgqIoX47AAew+JORwRnA6nfB6vXA6nWg0GohGo9A0jbsQZm4MPaibm5swDKPD1IfqKQSx\nDWvG66TdbndMNVKHpV6vc6YjVuKPqivSbrfx6aefmnqN7kys1+8eiURw4sQJnsK18nhEQkhUGwN2\nNx8qutfrdcuuRxPNxBnpzmSy2Sy3hM2CCp8kb0l1OUIgEECpVMLKygp8Ph8SiYTpzPggGIrAMTo6\nCkVR2EgHeDKsVC6X0Wq1kEgksLm5iXw+j62tLWSz2YF0OFqtFgeiarXakUlQVtCtgTk9PW3qRpDW\nZbPZxNjYGBdB6TqBQACqqvJxiH6X7nmZw4KqqqYXGrX/yFJSXNwivvGNb6Ber6NWq5k+OojXiMfj\nyOfzcDqdHYNn1OY+qI/uQUDrTlGUngHy2LFjlrCMS6USXn75ZbTbbdy+fRtTU1NQFIXfn9PpRLlc\nxo0bN1Cr1bgepuv6ofN/hiJwUOGq2WzyEaFer8PtdiMUCsHhcEBRFGSzWfj9flOyfSRC/Oqrr/LO\nTyIsiqL0FOkRA8sgKBQKGB8fx+bmJh+NqOJPoj7UWSHQrmYGB108VtRRiO1KXaNeD1QsFkMwGOTp\nVbPvT/z5aDSKYrGIVqvF60OcdLZydkMkf/XKYsQNYFBQXU/snpD1I60TUhnzer1Ip9OYn59HsVgc\nWPqhHwxF4CCtCnH4jBYABRTDMJBKpSwh1QQCAayvr+Px48fQNA31eh1TU1MolUq4e/fuHuNecWx7\nENCxant7m7U9PB4PZyCyLO/hNHQTpwZBP6k52QeYhaIobGvZDSp8x+Nx6LpuWTodCoXY3HpnZwdO\npxPz8/PweDw4ceIEms0mEomEJdcCdt/j6OgoTzoTwuEwZFnuGGAcFGR5mkgk4PV6ua5SqVR4Q2i3\n25iYmMCpU6eQSqXQbDZNz/4cFEMROIDd3cjpdDKHg1JO2g1JYs8siI6rqiqSySSL6TQaDUxPT8Pt\ndne012h2xgyIRZjP5zm9JcZqpVJhoR6R5m4F86+fwGHVQ0ytz16vt76+Dp/Ph2AwaKm/aiQS6Rga\npAE3qg0AsFQusNlsIpvN7qmzZbNZ6LqOx48fm+ZREC+l0WhgYmICwWAQhUKBLUIIL774IjRNw+Li\nIlwulyXckQP9frZ3rA0bNvrF0GQcNmzY+OLADhw2bNjoG3bgsGHDRt94bmIPDodj3+IK1V3E2Q2a\nYyFQAZW0FrLZbM9iYLvdtsyQ+WkmxcLvztWy2dnZfa+p6zo2NjYwNTUFv9+P27dvd9gWejwejI+P\nI5/PY2pqCpubmz39VwHg0aNHEgAEg8GnfqbEVP32t7+NH/3oRwiFQnuKhpqmoVQqYWpqCuVyuSfX\nIp/PH+gzFVXTZFlGo9GAw+Hge0mdAvrzfl2kg5pOA7uF0q9+9au4ceMG3nrrLeRyORw7dgypVAq/\n/OUvkUgkDtStoms+bZ0Cu52UfD7fYchEU81+v5/b/NlstuO9doPW6UHe40svvYS5uTkkEglWlff5\nfPB4PIjH4/j888/xq1/96kDvb1AMZcZBHADybgX2dgho9iAQCByZSpaVJCLqs9NCVhQFhUKBOzC6\nrmN7e5sFds2IM9P16IGhTkMv/ga1FynImAG10UWDqXa73cF/oD9bZQD99a9/HS6XC9vb2/j444+x\nvb2NYDDICvJWU+sLhQIHDQp+ZBhWLBZRKpWws7PD1htWEPrIBRB4MmjndDo5eBy2wjkwhIGjm8vQ\nqxVKtgl0E8hd3ixOnjwJoLdAsFX0b3qI6EEhrQUKDOR6TuJFMzMzLBc3yO9AD265XGZVqA8//JC9\ncQlerxdnz57lNilZVZoBMXFpmE/8XBVFYRavWYGk7uvVajUoisJB6sGDB1hZWUGz2cTDhw87pk0H\nBdG6yVxakiR4vV4mv5FEAwDOQqyYPfL5fKhWq5AkCZFIhO8XWT+kUqkj2UiHLnCIcyH78RAMw0Au\nl2N9R6tayvfv3wfQW9HcqjkH0alNURSsrq4imUxidXWV30epVMLo6Cg8Hg+SySRPeQ76OxBnZH5+\nHsBulnPp0qWO76lUKh0j29Fo1PRIP3FgKHiJn6u4IVh1D30+H44fP47FxUWcO3cOMzMzuHDhAlOz\nAbDID2BOuZ54N61WC1tbW2i32ywULJIY6f90RDH7Polcp+s6yyE2m03IsgyXy8VDlITDGlkYmsBB\ni4f+owgOYF8bvZGRET4+WPEBWSEu+zRQWl4sFlGtVhGLxTAzM4NAIICpqSlOP8vlMtLpNJLJJBYW\nFhAMBjsU1w8CmsuRZRnT09PsZUKZTC8V7I8//pj/TMelQUH3g3QxqLYBgF3paGe2anGTgM+Pf/xj\n3Lx5E6qqYmVlBZqmIRwOY2RkpENFvl+zcuCJLotoSh4KhZiwKHoOU5DoZSQ+KEKhEKLRKLa2tljX\nhaQKScRIJJ8dFk9rKAJHr/MfFfOATj1HEmoBnlgzAtb4jlohLvs0kCiLYRg8QTk+Po5cLodCocBT\nsmfPnuWs5P79+2xe1M8ioCPRyMgIpqenMTExAcMwcO7cOUxMTDx1lsHtdvNo+qDoPnKKmiqUstOC\nt2Jxu1wuHtZrNpvsU0MF7StXriCdTncccQd5f936tM1mE+VymYOCqCNDIFdAK+ZHNE3j8YyNjQ0W\nnyJ9maPCUAQOSuHojEro9UFTIREAKzoD1itYWQ2SDtze3ka9Xudguby8DI/Hg2KxyJ4jd+7cYZUz\nGvEfhMKs6zri8TjOnj2L27dvA9g9BtVqNXz22Wc9z/j0e4p1mEFAAZ5mUmgXFmsdVquOj42N4be/\n/S3P/8TjcZRKJYyPj2NychKbm5umLRJonoq6JXQco1oNHSm7s6juruCgUFUVXq+XTdDFuZVisdh3\nZjoohiJwAL0/2F4LV3S1omj7RYDT6US9XofP50M8Hgewe9QqlUosWlQsFtl7ZXZ2lt3OSeC4Hzgc\nDvh8PmxtbSGTyfCCevDgAQqFAtbW1noeW2jGw+x8Dh0b6D962KjWYcV5H3gSfDweD958800YhsGf\n86NHj1AoFHD37l3cvXsX5XKZDb4GBU1PUxeMCru6rqPZbHLWeFgbGQUo+uxqtRoikQi8Xi8cDoel\nM0BPw9AEDqo8UyHvabuReHSxwpnrxRdf5D9boUzdC7quw+fzIRKJIJlMwuVysaZIIBDgxUjt5c3N\nTWiaxmPS/UKSJIyOjuLx48c8GappGvx+f4cDOn2dsgArCqLAbhpPx08K9GKNA4Al9Q36bCYnJxGJ\nRHh03+/3Y2xsDJlMBtvb25wBmX2wSBu23W53dMJIxc3tdsPlcu1Zw1YEyXA4zEG+Xq+zsjkFjWKx\nuK8OitUYmsBBhcNndUqoaOr1evkGmoWofnWYkmv1ep0Vx5xOJ86cOYNms8nvuVqtcm2BLCHn5uYG\nXnQejwe1Wo132Xg8ztonDoeD60PpdJqzO+qCDIpuvxFqiYr/RgJGVmUdwO4Dff78edy4cQPZbBb3\n7t3D1NQUFhcXO2pLdMwdFKRGL0kSi2nT9cmOgYzCRCKjFfD5fNzyJcEpWZa5FWu1fMDTMBSBgwIF\nWd11V+C7v7dcLnO94CgUsqw4i7fbbayvr8Pj8aBaraLVaiGVSiEej7NsvyzLCAQCvMgDgQBu3bo1\n0HskyfyxsTHW2gyFQmxHID6w4p/NcBvo96RuiXjWp6/R72b1UWVtbQ2fffYZ3G43/H4/3n77bRQK\nBWxubnK24XA4THkO03sxDIOlCqnWQJwYypzF92emyCzC7/ez7USz2WShIgpMR2GlQRiKwEGgs6Eo\nkNJd5xBTYeDw2k0irMhqdF1HNBrllpnT6YTT6UQqleIsp9FoIBwO4/Lly2g2m8jlcj3Nfg4CSZJQ\nKBQ6hGu3trZ4ke33uVmRbXSPBnRnkN0O9mZAr3Px4kXk83k0Gg288soruH//PlqtFi5duoSlpSX2\nGzYDsa0sZqaFQoFrH6T+JVowWqXGRUcwRVGQz+ehqipUVUWz2YTD4ThSRfyhCBzUvqIgIc44iLut\nqI1JFgNmcZCbaoW3CWUTJFR08uRJrK6uwuv1Mn+DuBe3b9+G1+tFOBzm+Y5B8OjRI6iqym26dDrN\nrMPu302E2SyOdl3gie4nHU2sYomK8Hg8iMViePz4MUKhED799FPm/nz++efQNA2/93u/Z4l8IH1W\nfr8f4XAYwBPGqpiRiMLbVm1ulUqFg5+qquzLEw6HO9zdjgJDEzhEKbluViFB/DrtpGaPEQdpOVoR\nyRVFQbVa5eD36NEjnDhxAlNTUx3va2xsDNPT0/D7/axNOgh0XUcymcTx48c5OFLFvxviorfioRaZ\nksCTeygS/KyEz+fD66+/junpaTx8+BA7OzuoVCpM3U8mk/jtb39rybWoeF8oFJDL5ThzpGMLQQyU\nVrxfUosjHgdtrtQ2J4W5o8JQBA6RRQg8PQugMywZNZld6C6X66nFq3fffRevvPKKqWsAT45hpIlZ\nqVSwvr4Op9PJGcfc3By8Xi90XYeiKHC73QNzKWgI0O/38wyO2+3u+JwBsF0Dfc3M5yn+rJjF0H2i\ns7+YkVgBqntls1mcOnWKjaZ1Xcft27dRqVTgdrsxMzNj+jpit4QMxET+UTAY5KEzoJO3YgY0KZ5I\nJJDL5dBoNKAoCiKRCAKBABqNhqVDmM/CcxurF9FrF+q1y5PKM90kUZN0UDyrnfvP//zPpl6fkM/n\nYRgGSqUSa56qqopEIoFqtYpoNIq1tTW4XC4UCgWW2Cf/l34hDnhNTU3x16hNSrsjWTICT8hNg05x\ndtcxxK+J99jqIp6qqnj48CEmJibw8OFDLC4uYnR0FFevXsXk5CSWl5eRTCZNX5OmX6mDQoVWypZl\nWe7Y9SnIWPFeg8EgIpEIH2drtRqbk5E1wpfuqAJ0VuSftsuGw2HuYVNb0SoQIaobVuwYqqoiGAzy\nmT+fz8Pv9/NAUjqdZnp0u91mG4hyuTzQQ0wEsOvXr7P4MhVl9+MXiCbYg8DhcHTMf+y321pZ4yCB\na7Ke2NraQiQSQTabxenTp7k9aRUdmyw7yL5CfL+6rvNnG4lEONBYUYubm5vDyMgIS03ous6bKFmW\nfikDx7MmYgmU6pN4iZU7137kGSvmYBwOB9sitlotLCwsMA2c0G63USgUUKlU2IaSxGAGuR7xQ0ql\nEnMZRM8PUSiJWqRmmLjd3rfdxlYEK+/Z2NgY/uiP/gh+vx8///nPcenSJWxtbUFVVVZb13XdsvN/\nrVbDzs4Oe+F0m2jR+s1kMvznQYbpukET0mQm5nA4eICQmL5HyaIemsDRD6V6YmICmUyGndgGxVFZ\nLNK15ubmmKqcy+VYUwHYpZ+Pj4/j1KlTaDabGBkZMcV3aLVaGBsbg9Pp5DS9Wq2y8Ayw2/6dnJwE\n8ORos5/K2EFAqXmv9Fysb9D3WpF55HK5jvfgcrnw+PFjqKqKnZ0dFtGxAsTfoA1LDMCkO+J0OqFp\nGtc9Bj1qihBtQ4Dd47XIjjUMg7lAR4WhqHEA6OvDrdVqqFarphfEUQ7GNZtNrK2twe/3o1Qq8SQu\ntQhplkSWZUQiEWZ2DrroujUhCN1HB9qJ6exs5kze/XPd0850DSsxPT3NRyyfz4disYhwOIxCocCj\n7gsLC1heXjZ9v+no0Z29UKeFMiy6p1aZhtMwHQV1WZZRqVQ6SHZHPeT53DMOh8PBWgXPSukoQyDr\nRKsX4WFhZ2cHs7OzqFQq0HUds7OzcLlc0DStQ1PV7Xbj9OnTqNVqKBQKyOfzA3WOKHvoJSHXzUmh\n1ybpuUHmR8SOiRg8xL9b0bXp9Xsnk0msra1ha2sL5XKZBwWJUVkoFJDJZEzL9tHUMAUjsUBPg3vd\nXRcKlmYfalVV4fP52KIU2M0WqRVMR9CjxHMPHIZhcC/6WR0OSm9FX9WjoJybxcTEBJaXl9FsNpmA\npes6CoUCvw86w1arVUQiEVSrVZbX6xf0wPZyL9M0raNVSl2pr3zlK6a6KWKn5Gn1KiuPDU6nk+eW\nKJWfmJjgYJvJZFCv13kC2cy1yQSazMIpE6RsQ+SsdL9vs2s0EAhw0CItWOL4UIfsKFuxwBAEjoO2\n5kRl7P26H8MIeoBpMc3Pz2Nrawter5czLGLNjo6O8lTs5OSkqYVO3ZOXX365g2q9urracfyh3+va\ntWumH2qqb3QfUai20W2ZaAZ0NHjjjTe4pkGb0OzsLNc3iO9gFvSwhkIhztp66YpQrY60OawIlF6v\nF7lcDrlcjluw4XCYSYXNZvPIdDgIzz1wHLRIRtJoR02tNQtJkph4BTzZKYkuDDypQ3z66aeQJAml\nUskUuY3SaofDgVAo1DFX0Z3V0U5VLpdNL3KRGdp9fLH6aEmkwcXFRX54aD6kVquhWCzyuLlVTvWG\nYbBSG/1dHJUAwIJLFMSsCJTtdhter7cj0zCMXcnCYrEIRVEse48Hhe0da8OGjb7x3DMOGzZsfPFg\nBw4bNmz0DTtw2LBho288NwKYdEAv10gk0kEFJzYe6RA8y+7O+D+PzGddb3FxEZlMBul0GhcvXsS1\na9cQjUY7aNrkfyrL8r5WCkYfPqcA8N577+HatWvI5XJwOBwYHR3FK6+8AlmWkcvl8N3vfvdZL3Hg\n92gV6Hrf//73972e0+nEP/7jP+KTTz6BJEkYHx/vKWv3gx/8AN/97nfxV3/1V9xq78af//mf82fq\n8/n2vabL5WIdjOPHj6NYLDLNPxwOo91u44//+I9x584d/PSnP8XMzMy+97FcLg/8mZJWhqIoGBsb\nw9ra2jN/Rlw3T/OrJbIXTVFTxwXY/cw9Hg80TeP3tR8JTfSqHQRDn3F0v2nqNtC8ilXIZDLMCLx6\n9SqAXZ+VRCIBSZKws7ODVCqFer1uqS4p2TMWi0UOTtVqFVNTU7h3755l1zlq1Ot1bvuOjo4iFovt\n+R5ZlpHJZFgA2Cw1mzoaoVAIHo+nY30Qnb5QKOyZarUa1HVpNBoHChr9gNrn1OoVuTpEQqNNiOQa\nDgNDGzgoYMTjcYyOjnLPvN1uo1KpIJfLsb6CmbYl/SxZFHg8Hh4eGh0dhSRJ2N7e5od6ZGTEsklL\nGomu1WrIZDJIpVJwuVxMAHv48KEl1zlKUDtWlDsIBAIIBAJ7WKuxWAzpdBrA/m59B4HT6WT9EgDs\n5C5mqh6PBwsLC3j11Vdx5coVAE8sHMyAskT6swgy1bIC3QFOURT4fL49a98wDPj9foRCIR7EOwwM\nbeCgDyqRSHQMZtGD7XQ6ce3atY7vHeQadLOJxdlqtfgDL5fLTDEGdlPhTCZj2c3wer3IZDI4duwY\n/H4/S+zruo5EItFhyfhFAWWDuVyOvXjL5TJWV1f3aKdEo1HcuHEDL730EpaXlzE2NjbQNVutFqrV\nKnMZHj16hPHx8Y4H1+FwYHV1Fe+99x5bRGSzWdODju12m48F3YxRK2ng3WuOnAC7134wGES5XGaG\nq1UK690Y2sAB7D6oohWhJEl800nUxwzEaU0imDUaDSwuLvI06fz8PN8cIhpZFTjOnTsHAGwgJBpL\nk7jOFwk0Vl+tVtniAdjNCDY2NvZ8bqdPn4au64jFYvD5fH2LMhFTtdFodBxzvF4vdnZ29rwezbAQ\nTdwKLdlQKLSvMryVgaM7QLjd7o6jGJHtFEVhVbdnaduYwdAGDhpVFjUdKA2mKUGzw0PG/6lRU5pM\ni4ko2oZhYHl5maM2HV02NjZMXZcwOzuLer2O9fV1ZltqmoZQKDT0lpa9QG5mVLwjhMPhnvUETdNY\nG7S7JnEQEGtTHHGXJIm9VcXPkLQsyuUyBw4rpCcLhcK+tRmyojADYt6KmxwFy+7P0+Vy8VGemMCH\nRfAcmsAh3sBgMMh0Xb/f3/F9Pp+Px7/NgHQTstksJicneWzZ5/OxytPU1BSi0SiA3bSahptE6Xsz\nOHbsGNrtNkqlEtsJ0sTs86DVv/POOwP/LM2itFotZLNZVgAHnmQG3VhZWUG73WZFrX53R0VRUK/X\noWkaB/VQKMRHQAoc9LnOzc2xehwdDa2WZhCzj15Dhv1CHIKka1G9zeFw8PXEIUlapzTPcxgYmsAh\n3sB8Po+RkREAu+djskYEdm/MzMyMaTk/4v1rmoaVlRU+mmSzWfzv//4vcrkcEokEGo0GarUa0uk0\ncrkcDMOwRE2K2q3kKD82NsYFL5fLhf/6r/8yfY1+8ZOf/MTUz6dSKbz88ssoFov44Q9/CGD3ob1x\n48aewcTTp0+zrQFlDf1uBpRZTE5OIpVKsUZrLpfryAIMw+DsptFocGesXC6b2oB6FXTJ6tMq0GAg\nGU0DYLlAcUoX2M2IiapAuipfuuKooigsAa+qaoff6M7OjunWnc/nYwHhxcVFRKNRNj966aWXOLMR\nh7+sVAwLBoO8Q+i6jnK5jGg0ygXCo9ZXMAsSOSZpPeLXhEIhhMPhPcGWjg0+n29g97h6vY52u81t\nWADcEROVx4HdND4Wi1lao5qdne35dTNucd2gowrV80TdFODJmtR1vcOx7rBlBIc2cND4MLBrRCP2\nxgHz6l202xQKBdy8eRPpdJqr44qicKZD6ktAfyplT4MkSXj77bc7ahkTExMIBAKIxWJYX1/H+vq6\nJdc6KhiGwV6+GxsbzHUhrkZ3ILxw4QJzKwZ9kEmwSCyCUmeMFOJkWYbf72fxYKqPWRE8ututxJvo\nRwbzWaDfk4IgBRHRZhPYLQhrmmZJ3eYgGNrAIbq4k/EMsJspmK0xUJRut9uYmZnhD1qSJJw+fRof\nffQRGyWVSiXLeBsEwzBw6dIlTqtJzZzk7+nsbwUOupubzaaoeJfNZjuCHpk+i/D5fDh37hzXJwZd\n6N2CQU6nk93igSeqamRnQA+aVZaMn332WcffaXMTMyCz6H6PpBzfSwsknU6jVCpZmlXth6ENHJVK\nBaVSiT846km73W7TH0q73eaWFdUtJEnC7//+7/Nu4Xa7me1YLpcRDoctvRkrKyvIZDJwuVyYnJxk\n1/GLFy9aWhg9aJZkNlBVq1W88MIL2Nzc7Ajs169f78k1oM+WguUg2NnZQSwWY1o51akoAyGT6WAw\nCIfDgWKxaOmOTF0gkSuhKApmZ2dNmXeLoCKnmFWRBCWAjrpHrVbrcOU7TAxl4HA4HKhWq/wAiVqk\ntVoNqVTK9DUoWJBylGEY+M///E+mmz948AC1Wg2Li4ssr2+VMjewm85St0HXdbjdbiiKAo/Hs6eT\n9EWAJEmcoYn8ml7cjK2tLaytrUFVVfj9flP2AWILV1XVju4DtSzpGBUIBAYqwj4L4jFsZmYGsizj\nwoULlrx293qjo1Z3sKXRBRKJOuzgMTQq5yJosMzr9XYsDCLwWOFTQVlHKpVif5F4PI5isYhKpQJV\nVZHP57mdFQ6HuTBrFtTyLZVKqNVq/PCoqopkMvmFJH85nU6USiVsbW11DI6Njo7uGW6TZRnRaJTP\n64Om1uQxQqBjDx0V6DWJ9CUqrZvNCNxud8+g+ODBA1Ov2w0qOlOgUFW1p3E4NRHy+TxkWT5UDgcw\npBkHsLsjU7GJgoeo0WkWYt0kEAhAURR885vf7GCpSpKEer0Op9OJTCZjmSBsKBRCMBjkAh61Eeks\nbuUQ3VGCOipi4Egmk3u+j2oRbrebs4BB0P3weDweFr4GwJ+p1+vtYJdasX5En5NesIrr0w0i1nUf\nLcXs9TDbsHy9Q331AUHemOKDahgGIpEInE6nJcUnMV2t1+toNBr4h3/4hz3q1VRUGx8fN31NQiQS\nwf3797kdTK9/9uxZBINB3Lhxw7JrHRRmHyZZljkr6/56N1577TX8+7//OwKBgKkJZ3KqI5AtIj00\ndO9CoRB0Xcfnn39uqovT6/rdoNdeXFy05Brd2fXOzg6zXkUYhsHB+CjkQIcycNCZlB5iuhm1Wo0X\nh1mMjY3B7/ezWa94g7761a+yjgGliVSQsmLRUQ2DvD+pB+/xeLC+vm6pXMBBYZZhSLyM7kXb6/Nq\ntVq8yEUfkkGuKW4i5I0rtuwVRWELBeqqWNWx6lWLovf/m9/8xpJr6Lre0VERj1siJiYmUC6XuWt0\n2CMLQxc4iLjjcrk6SF9EPyfGnNlrtFotzM7OotFoIBgMsqM7AHz00Udc+acj07FjxwDAkmnDeDwO\nRVG4nkIFL6fTiZWVFUvbeUcFWZZRKpWwvLzMXyPeRDcmJibQaDR4fmRQfkz37krmRKLNZCgU6mBY\nUlfFil25lzCR1aDuHwA2fur+TOmIS0OaVtRwnoWhK44Sxba7YEbDbd3DS4PAMAxks1kUCgU4HA7k\n83lmNlI7K5fLMUWZhHwkSTLN6CQGo5jR6LrO3Z2VlZUjl7q3ApIk4fHjxx2KbIZh9AwKk5OTePTo\nEUZGRtBsNgeeUhWJViRH0D3zQobMND1Ku/ZR+gabgTjhSsFgP6Mrel6+lMxRGm8nBSmaWI1EIpBl\nmbUGzECSJJw4cQLxeBytVosLTn6/n+XYotEoXnnlFS7QFgoFU4bMhJMnT3LRtVKpQNM0aJqGiYkJ\nTE5OYnNz87lMxn7ve98z9fNOpxOfffYZD3YRIazXvYpGo7h58yZnXoM+xOIErs/nQyAQwM7OTkdx\nlIbf6vU6ksnkvgN3/eBpylo0V3IYoDqGGBiJUkB+L+LE8GFi6DIO0hkQjWfq9ToMw+DHTdnYAAAD\nN0lEQVTdwwrcuXOHz4uNRgN+v595I+VyGaFQCL/4xS8A7N4cqzod4XCYJewURUGlUoHH40EgEGBl\ns+eBv/3bvzX181RbEM/j+wVAclcz6+cqvj69Drmb0dc0TeOvWZW+u93ufTcvqodZBbFoTa714mdL\nx246jh3VpjN0GYfH4+HUXQTtXlYMEL377rt87PH5fGi32x0TjZIkIZ1O81nSyiG3WCwGt9vN4890\n3g4EAtje3n4uhVEzMAwDPp8PiUSiowW5365HHINWq8UkuEF2x3a73dHqVVWVuyhUh6JjpqqqqNVq\nrBViFlZMRx8E3W1VmgUSPy/R4ZA4Tl/KrgrNFogRnVI0sVBk5vX/5V/+BdFoFM1mkyvRpPdJIsji\n2ZJG/K1YdKOjo5BlGZVKhRcFjdIXCoV9VbeHFZK0a1lJpsiE/dJ1v9/Pkn0UOAfJOohCTvD5fCgU\nCmi321yHGhkZ4b/T9x4FHdsqdL9H6paIgYHqgSSidBQcDmAIA8f29jZLv9PouSzLmJ6exs7Ojukd\nud1uQ1VVuN1uLCws8JmXuhqtVgvRaBSnTp1i0dd+Je2eBsMwkE6nWYhF0zQEAgFsbGxA07QjqdT3\nwre+9a2Bf5bG6ZeWlvhr+6XyZ86cwcrKiiW0enHwkbIMuld+vx8+n49njLpb7mbwtBqHVQLFvQby\nqH3f/X3BYBBOp/PQaiu9MHQ1DlVVOcrm83kWJ65Wq4hGo1hZWTH1+o1Gg8VcKLMQjyJerxfZbJZ3\nxWazadlioIp/rVbjc2koFIKmafD7/Xj8+LGlWg79QGyj9gsaLhOnYvfLzujIaWVXg+aaSGOFiuh0\nTCmXy2g2mz1bmf2CPFP2g1UdMcoaxEJorwE9Sdo1GK9UKsxEPgrYptM2bNjoG0N3VLFhw8bwww4c\nNmzY6Bt24LBhw0bfsAOHDRs2+oYdOGzYsNE37MBhw4aNvmEHDhs2bPQNO3DYsGGjb9iBw4YNG33D\nDhw2bNjoG3bgsGHDRt+wA4cNGzb6hh04bNiw0TfswGHDho2+YQcOGzZs9A07cNiwYaNv2IHDhg0b\nfcMOHDZs2OgbduCwYcNG37ADhw0bNvrG/wM/cYeFZwWj8wAAAABJRU5ErkJggg==\n", 77 | "text/plain": [ 78 | "" 79 | ] 80 | }, 81 | "metadata": {}, 82 | "output_type": "display_data" 83 | } 84 | ], 85 | "source": [ 86 | "# network init\n", 87 | "\n", 88 | "model = vgg.VGG_19(outputlayer=[3])\n", 89 | "\n", 90 | "# load partial weights\n", 91 | "model_dict = model.state_dict()\n", 92 | "params = torch.load('vgg19.pth')\n", 93 | "load_dict = {k:v for k,v in params.items() if 'features' in k}\n", 94 | "model_dict.update(load_dict)\n", 95 | "model.load_state_dict(model_dict)\n", 96 | "\n", 97 | "# extract features\n", 98 | "\n", 99 | "imgMean = np.array([0.485, 0.456, 0.406], np.float)\n", 100 | "imgStd = np.array([0.229,0.224,0.225])\n", 101 | "img_m = misc.imresize(img_m,(224,224))/255.\n", 102 | "img_m = (img_m-imgMean)/imgStd\n", 103 | "img_m = np.transpose(img_m, (2,0,1))\n", 104 | "feature2 = model(Variable(torch.from_numpy(img_m[None,:,:,:]).float()))\n", 105 | "feature = feature2[0].data[0].numpy()\n", 106 | "x = ndimage.zoom(feature, (1, float(100)/feature.shape[1],\n", 107 | " float(100)/feature.shape[2]), order=1)\n", 108 | "tools.imshow_grid(x,shape=[8,8])" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": null, 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [ 117 | "" 118 | ] 119 | } 120 | ], 121 | "metadata": { 122 | "kernelspec": { 123 | "display_name": "Python 2", 124 | "language": "python", 125 | "name": "python2" 126 | }, 127 | "language_info": { 128 | "codemirror_mode": { 129 | "name": "ipython", 130 | "version": 2.0 131 | }, 132 | "file_extension": ".py", 133 | "mimetype": "text/x-python", 134 | "name": "python", 135 | "nbconvert_exporter": "python", 136 | "pygments_lexer": "ipython2", 137 | "version": "2.7.6" 138 | } 139 | }, 140 | "nbformat": 4, 141 | "nbformat_minor": 0 142 | } -------------------------------------------------------------------------------- /tutorials/img_ScaleChange/0001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vtddggg/Visual_Tracking_api/d5cf7934861950ca1eedede198382fc468dab941/tutorials/img_ScaleChange/0001.jpg -------------------------------------------------------------------------------- /tutorials/img_ScaleChange/0002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vtddggg/Visual_Tracking_api/d5cf7934861950ca1eedede198382fc468dab941/tutorials/img_ScaleChange/0002.jpg -------------------------------------------------------------------------------- /tutorials/img_ScaleChange/0003.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vtddggg/Visual_Tracking_api/d5cf7934861950ca1eedede198382fc468dab941/tutorials/img_ScaleChange/0003.jpg -------------------------------------------------------------------------------- /tutorials/img_ScaleChange/0004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vtddggg/Visual_Tracking_api/d5cf7934861950ca1eedede198382fc468dab941/tutorials/img_ScaleChange/0004.jpg -------------------------------------------------------------------------------- /tutorials/img_ScaleChange/groundtruth.txt: -------------------------------------------------------------------------------- 1 | 130,132,31,115 2 | 123,119,32,105 3 | 117,109,35,99 4 | 118,100,27,95 5 | -------------------------------------------------------------------------------- /tutorials/img_common/0001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vtddggg/Visual_Tracking_api/d5cf7934861950ca1eedede198382fc468dab941/tutorials/img_common/0001.jpg -------------------------------------------------------------------------------- /tutorials/img_common/0002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vtddggg/Visual_Tracking_api/d5cf7934861950ca1eedede198382fc468dab941/tutorials/img_common/0002.jpg -------------------------------------------------------------------------------- /tutorials/img_common/groundtruth.txt: -------------------------------------------------------------------------------- 1 | 478,143,80,111 2 | 457,121,83,116 3 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy import misc 3 | from skimage import transform 4 | 5 | def get_window_size(target_sz, im_sz, padding): 6 | 7 | if (target_sz[0] / target_sz[1] > 2): 8 | # For objects with large height, we restrict the search window with padding.height 9 | window_sz = np.floor(np.multiply(target_sz, [1 + padding.height, 1 + padding.generic])) 10 | 11 | elif np.prod(target_sz)/np.prod(im_sz) > 0.05: 12 | # For objects with large height and width and accounting for at least 10 percent of the whole image, 13 | # we only search 2xheight and width 14 | window_sz = np.floor(target_sz * (1 + padding.large)) 15 | 16 | else: 17 | window_sz = np.floor(target_sz * (1 + padding.generic)) 18 | 19 | return window_sz 20 | 21 | 22 | 23 | 24 | def get_subwindow(im, pos, sz, scale_factor = None, feature='raw'): 25 | """ 26 | Obtain sub-window from image, with replication-padding. 27 | Returns sub-window of image IM centered at POS ([y, x] coordinates), 28 | with size SZ ([height, width]). If any pixels are outside of the image, 29 | they will replicate the values at the borders. 30 | 31 | The subwindow is also normalized to range -0.5 .. 0.5, and the given 32 | cosine window COS_WINDOW is applied 33 | (though this part could be omitted to make the function more general). 34 | """ 35 | 36 | if np.isscalar(sz): # square sub-window 37 | sz = [sz, sz] 38 | 39 | sz_ori = sz 40 | 41 | if scale_factor != None: 42 | sz = np.floor(sz*scale_factor) 43 | 44 | ys = np.floor(pos[0]) + np.arange(sz[0], dtype=int) - np.floor(sz[0] / 2) 45 | xs = np.floor(pos[1]) + np.arange(sz[1], dtype=int) - np.floor(sz[1] / 2) 46 | 47 | ys = ys.astype(int) 48 | xs = xs.astype(int) 49 | 50 | # check for out-of-bounds coordinates and set them to the values at the borders 51 | ys[ys < 0] = 0 52 | ys[ys >= im.shape[0]] = im.shape[0] - 1 53 | 54 | xs[xs < 0] = 0 55 | xs[xs >= im.shape[1]] = im.shape[1] - 1 56 | 57 | out = im[np.ix_(ys, xs)] 58 | if scale_factor != None: 59 | out = misc.imresize(out, sz_ori.astype(int)) 60 | 61 | 62 | if feature == 'hog': 63 | from pyhog import pyhog 64 | hog_feature = pyhog.features_pedro(out / 255., 1) 65 | out = np.lib.pad(hog_feature, ((1, 1), (1, 1), (0, 0)), 'edge') 66 | 67 | return out 68 | 69 | def merge_features(features): 70 | num, h, w = features.shape 71 | row = int(np.sqrt(num)) 72 | merged = np.zeros([row * h, row * w]) 73 | 74 | for idx, s in enumerate(features): 75 | i = idx // row 76 | j = idx % row 77 | merged[i * h:(i + 1) * h, j * w:(j + 1) * w] = s 78 | 79 | 80 | return merged 81 | 82 | def dense_gauss_kernel(sigma, xf, x, zf=None, z=None): 83 | """ 84 | Gaussian Kernel with dense sampling. 85 | Evaluates a gaussian kernel with bandwidth SIGMA for all displacements 86 | between input images X and Y, which must both be MxN. They must also 87 | be periodic (ie., pre-processed with a cosine window). The result is 88 | an MxN map of responses. 89 | 90 | If X and Y are the same, ommit the third parameter to re-use some 91 | values, which is faster. 92 | :param sigma: feature bandwidth sigma 93 | :param x: 94 | :param y: if y is None, then we calculate the auto-correlation 95 | :return: 96 | """ 97 | N = xf.shape[0] * xf.shape[1] 98 | xx = np.dot(x.flatten().transpose(), x.flatten()) # squared norm of x 99 | 100 | if zf is None: 101 | # auto-correlation of x 102 | zf = xf 103 | zz = xx 104 | else: 105 | zz = np.dot(z.flatten().transpose(), z.flatten()) # squared norm of y 106 | 107 | xyf = np.multiply(zf, np.conj(xf)) 108 | if len(xyf.shape) == 3: 109 | xyf_ifft = np.fft.ifft2(np.sum(xyf, axis=2)) 110 | elif len(xyf.shape) == 2: 111 | xyf_ifft = np.fft.ifft2(xyf) 112 | # elif len(xyf.shape) == 4: 113 | # xyf_ifft = np.fft.ifft2(np.sum(xyf, axis=3)) 114 | 115 | #row_shift, col_shift = np.floor(np.array(xyf_ifft.shape) / 2).astype(int) 116 | #xy_complex = np.roll(xyf_ifft, row_shift, axis=0) 117 | #xy_complex = np.roll(xy_complex, col_shift, axis=1) 118 | c = np.real(xyf_ifft) 119 | d = np.real(xx) + np.real(zz) - 2 * c 120 | k = np.exp(-1. / sigma ** 2 * np.abs(d) / N) 121 | 122 | return k 123 | 124 | def get_scale_subwindow(im,pos,base_target_size, scaleFactors, 125 | scale_window, scale_model_sz): 126 | from pyhog import pyhog 127 | nScales = len(scaleFactors) 128 | out = [] 129 | for i in range(nScales): 130 | patch_sz = np.floor(base_target_size * scaleFactors[i]) 131 | scale_patch = get_subwindow(im, pos, patch_sz) 132 | im_patch_resized = transform.resize(scale_patch, scale_model_sz,mode='reflect') 133 | temp_hog = pyhog.features_pedro(im_patch_resized, 4) 134 | out.append(np.multiply(temp_hog.flatten(), scale_window[i])) 135 | 136 | return np.asarray(out) -------------------------------------------------------------------------------- /vgg.py: -------------------------------------------------------------------------------- 1 | from torch import nn 2 | 3 | cfg = [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'] 4 | class VGG_19(nn.Module): 5 | def __init__(self, outputlayer=[26]): 6 | assert (type(outputlayer)==list) 7 | super(VGG_19, self).__init__() 8 | layers = [] 9 | in_channels = 3 10 | self.outputlayer = outputlayer 11 | for v in cfg: 12 | if v == 'M': 13 | 14 | layers += [nn.MaxPool2d(kernel_size=2, stride=2)] 15 | else: 16 | conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1) 17 | layers += [conv2d, nn.ReLU(inplace=True)] 18 | in_channels = v 19 | 20 | self.features = nn.Sequential(*layers) 21 | 22 | 23 | def forward(self, x): 24 | output = [] 25 | for module in self.features._modules.values(): 26 | x = module(x) 27 | 28 | if self.features._modules.values().index(module) in self.outputlayer: 29 | output.append(x) 30 | 31 | return output -------------------------------------------------------------------------------- /vot.py: -------------------------------------------------------------------------------- 1 | """ 2 | \file vot.py 3 | 4 | @brief Python utility functions for VOT integration 5 | 6 | @author Luka Cehovin, Alessio Dore 7 | 8 | @date 2016 9 | 10 | """ 11 | 12 | import sys 13 | import copy 14 | import collections 15 | 16 | try: 17 | import trax 18 | import trax.server 19 | TRAX = True 20 | except ImportError: 21 | TRAX = False 22 | 23 | Rectangle = collections.namedtuple('Rectangle', ['x', 'y', 'width', 'height']) 24 | Point = collections.namedtuple('Point', ['x', 'y']) 25 | Polygon = collections.namedtuple('Polygon', ['points']) 26 | 27 | def parse_region(string): 28 | tokens = map(float, string.split(',')) 29 | if len(tokens) == 4: 30 | return Rectangle(tokens[0], tokens[1], tokens[2], tokens[3]) 31 | elif len(tokens) % 2 == 0 and len(tokens) > 4: 32 | return Polygon([Point(tokens[i],tokens[i+1]) for i in xrange(0,len(tokens),2)]) 33 | return None 34 | 35 | def encode_region(region): 36 | if isinstance(region, Polygon): 37 | return ','.join(['{},{}'.format(p.x,p.y) for p in region.points]) 38 | elif isinstance(region, Rectangle): 39 | return '{},{},{},{}'.format(region.x, region.y, region.width, region.height) 40 | else: 41 | return "" 42 | 43 | def convert_region(region, to): 44 | 45 | if to == 'rectangle': 46 | 47 | if isinstance(region, Rectangle): 48 | return copy.copy(region) 49 | elif isinstance(region, Polygon): 50 | top = sys.float_info.max 51 | bottom = sys.float_info.min 52 | left = sys.float_info.max 53 | right = sys.float_info.min 54 | 55 | for point in region.points: 56 | top = min(top, point.y) 57 | bottom = max(bottom, point.y) 58 | left = min(left, point.x) 59 | right = max(right, point.x) 60 | 61 | return Rectangle(left, top, right - left, bottom - top) 62 | 63 | else: 64 | return None 65 | if to == 'polygon': 66 | 67 | if isinstance(region, Rectangle): 68 | points = [] 69 | points.append((region.x, region.y)) 70 | points.append((region.x + region.width, region.y)) 71 | points.append((region.x + region.width, region.y + region.height)) 72 | points.append((region.x, region.y + region.height)) 73 | return Polygon(points) 74 | 75 | elif isinstance(region, Polygon): 76 | return copy.copy(region) 77 | else: 78 | return None 79 | 80 | return None 81 | 82 | class VOT(object): 83 | """ Base class for Python VOT integration """ 84 | def __init__(self, region_format): 85 | """ Constructor 86 | 87 | Args: 88 | region_format: Region format options 89 | """ 90 | assert(region_format in ['rectangle', 'polygon']) 91 | if TRAX: 92 | options = trax.server.ServerOptions(region_format, trax.image.PATH) 93 | self._trax = trax.server.Server(options) 94 | 95 | request = self._trax.wait() 96 | assert(request.type == 'initialize') 97 | if request.region.type == 'polygon': 98 | self._region = Polygon([Point(x[0], x[1]) for x in request.region.points]) 99 | else: 100 | self._region = Rectangle(request.region.x, request.region.y, request.region.width, request.region.height) 101 | self._image = str(request.image) 102 | self._trax.status(request.region) 103 | else: 104 | self._files = [x.strip('\n') for x in open('images.txt', 'r').readlines()] 105 | self._frame = 0 106 | self._region = convert_region(parse_region(open('region.txt', 'r').readline()), region_format) 107 | self._result = [] 108 | 109 | def region(self): 110 | """ 111 | Send configuration message to the client and receive the initialization 112 | region and the path of the first image 113 | 114 | Returns: 115 | initialization region 116 | """ 117 | 118 | return self._region 119 | 120 | def report(self, region): 121 | """ 122 | Report the tracking results to the client 123 | 124 | Arguments: 125 | region: region for the frame 126 | """ 127 | assert(isinstance(region, Rectangle) or isinstance(region, Polygon)) 128 | if TRAX: 129 | if isinstance(region, Polygon): 130 | tregion = trax.region.Polygon([(x.x, x.y) for x in region.points]) 131 | else: 132 | tregion = trax.region.Rectangle(region.x, region.y, region.width, region.height) 133 | self._trax.status(tregion) 134 | else: 135 | self._result.append(region) 136 | self._frame += 1 137 | 138 | def frame(self): 139 | """ 140 | Get a frame (image path) from client 141 | 142 | Returns: 143 | absolute path of the image 144 | """ 145 | if TRAX: 146 | if hasattr(self, "_image"): 147 | image = str(self._image) 148 | del self._image 149 | return image 150 | 151 | request = self._trax.wait() 152 | 153 | if request.type == 'frame': 154 | return str(request.image) 155 | else: 156 | return None 157 | 158 | else: 159 | if self._frame >= len(self._files): 160 | return None 161 | return self._files[self._frame] 162 | 163 | def quit(self): 164 | if TRAX: 165 | self._trax.quit() 166 | elif hasattr(self, '_result'): 167 | with open('output.txt', 'w') as f: 168 | for r in self._result: 169 | f.write(encode_region(r)) 170 | f.write('\n') 171 | 172 | 173 | -------------------------------------------------------------------------------- /vot_demo_tracker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | 5 | from scipy import misc 6 | 7 | import vot 8 | 9 | 10 | class Demo_Tracker(object): 11 | 12 | def __init__(self, image, region): 13 | 14 | print 'type of the image:',type(image) 15 | print 'shape of the image:',image.shape 16 | print 'image content:' 17 | print image 18 | self.window = max(region.width, region.height) * 2 19 | 20 | self.template = None 21 | self.position = (region.x + region.width / 2, region.y + region.height / 2) 22 | self.size = (region.width, region.height) 23 | 24 | def track(self, image): 25 | 26 | return vot.Rectangle(self.position[0] - self.size[0] / 2, self.position[1] - self.size[1] / 2, self.size[0], self.size[1]) 27 | 28 | 29 | handle = vot.VOT("rectangle") 30 | selection = handle.region() 31 | 32 | imagefile = handle.frame() 33 | if not imagefile: 34 | sys.exit(0) 35 | image = misc.imread(imagefile) 36 | #image = cv2.imread(imagefile) 37 | #image = io.imread(imagefile) 38 | tracker = Demo_Tracker(image, selection) 39 | while True: 40 | imagefile = handle.frame() 41 | if not imagefile: 42 | break 43 | image = misc.imread(imagefile) 44 | # image = cv2.imread(imagefile) 45 | # image = io.imread(imagefile) 46 | region = tracker.track(image) 47 | handle.report(region) 48 | 49 | --------------------------------------------------------------------------------