From 7126d496e1195c7469e417672329145e326d5c1c Mon Sep 17 00:00:00 2001 From: hanhxiao Date: Thu, 1 Aug 2019 15:05:06 +0800 Subject: [PATCH] refactor(preprocessor): separate resize logic from the unary preprocessor --- gnes/base/__init__.py | 2 +- gnes/preprocessor/__init__.py | 1 + gnes/preprocessor/base.py | 13 ++++-- gnes/preprocessor/image/base.py | 2 +- gnes/preprocessor/image/resize.py | 38 ++++++++++++++++++ gnes/preprocessor/image/segmentation.py | 6 +-- gnes/preprocessor/image/sliding_window.py | 2 - tests/imgs/test.zip | Bin 23159 -> 39433 bytes tests/test_image_preprocessor.py | 28 +++++++++++-- tests/yaml/resize-image-prep.yml | 9 +++++ ...ngleton.yml => img_preprocessor_unary.yml} | 0 11 files changed, 86 insertions(+), 15 deletions(-) create mode 100644 gnes/preprocessor/image/resize.py create mode 100644 tests/yaml/resize-image-prep.yml rename yaml-example/component/{img_preprocessor_singleton.yml => img_preprocessor_unary.yml} (100%) diff --git a/gnes/base/__init__.py b/gnes/base/__init__.py index fcef4e89..9a6d70b9 100644 --- a/gnes/base/__init__.py +++ b/gnes/base/__init__.py @@ -28,7 +28,7 @@ from ..helper import set_logger, profiling, yaml, parse_arg, load_contrib_module -__all__ = ['TrainableBase'] +__all__ = ['TrainableBase', 'CompositionalTrainableBase'] T = TypeVar('T', bound='TrainableBase') diff --git a/gnes/preprocessor/__init__.py b/gnes/preprocessor/__init__.py index 63222041..66a16fdb 100644 --- a/gnes/preprocessor/__init__.py +++ b/gnes/preprocessor/__init__.py @@ -29,6 +29,7 @@ 'WeightedSlidingPreprocessor': 'image.sliding_window', 'SegmentPreprocessor': 'image.segmentation', 'BaseUnaryPreprocessor': 'base', + 'ResizeChunkPreprocessor': 'image.resize', 'BaseVideoPreprocessor': 'video.base', 'FFmpegPreprocessor': 'video.ffmpeg', 'ShotDetectPreprocessor': 'video.shotdetect', diff --git a/gnes/preprocessor/base.py b/gnes/preprocessor/base.py index 356c728e..01952112 100644 --- a/gnes/preprocessor/base.py +++ b/gnes/preprocessor/base.py @@ -28,14 +28,21 @@ class BasePreprocessor(TrainableBase): doc_type = gnes_pb2.Document.UNKNOWN - def __init__(self, start_doc_id: int = 0, random_doc_id: bool = True, *args, **kwargs): + def __init__(self, start_doc_id: int = 0, + random_doc_id: bool = True, + uniform_doc_weight: bool = True, + *args, **kwargs): super().__init__(*args, **kwargs) self.start_doc_id = start_doc_id self.random_doc_id = random_doc_id + self.uniform_doc_weight = uniform_doc_weight def apply(self, doc: 'gnes_pb2.Document') -> None: doc.doc_id = self.start_doc_id if not self.random_doc_id else random.randint(0, ctypes.c_uint(-1).value) doc.doc_type = self.doc_type + if not doc.weight and self.uniform_doc_weight: + doc.weight = 1.0 + self.start_doc_id += 1 class PipelinePreprocessor(CompositionalTrainableBase): @@ -55,11 +62,10 @@ def train(self, data, *args, **kwargs): class BaseUnaryPreprocessor(BasePreprocessor): + is_trained = True def __init__(self, doc_type: int, *args, **kwargs): super().__init__(*args, **kwargs) - self.target_img_size = 224 - self.is_trained = True self.doc_type = doc_type def apply(self, doc: 'gnes_pb2.Document'): @@ -78,7 +84,6 @@ def raw_to_chunk(self, chunk: 'gnes_pb2.Chunk', raw_bytes: bytes): chunk.text = raw_bytes.decode() elif self.doc_type == gnes_pb2.Document.IMAGE: img = np.array(Image.open(io.BytesIO(raw_bytes))) - img = np.array(Image.fromarray(img).resize((self.target_img_size, self.target_img_size))) chunk.blob.CopyFrom(array2blob(img)) elif self.doc_type == gnes_pb2.Document.VIDEO: raise NotImplementedError diff --git a/gnes/preprocessor/image/base.py b/gnes/preprocessor/image/base.py index a557cbe9..783b2eb7 100644 --- a/gnes/preprocessor/image/base.py +++ b/gnes/preprocessor/image/base.py @@ -46,4 +46,4 @@ def _get_all_subarea(image): index = [[x, y, x + 1, y + 1] for [x, y] in product(range(len(x_list) - 1), range(len(y_list) - 1))] all_subareas = [[x_list[idx[0]], y_list[idx[1]], x_list[idx[2]], y_list[idx[3]]] for idx in index] - return all_subareas, index \ No newline at end of file + return all_subareas, index diff --git a/gnes/preprocessor/image/resize.py b/gnes/preprocessor/image/resize.py new file mode 100644 index 00000000..34846c1a --- /dev/null +++ b/gnes/preprocessor/image/resize.py @@ -0,0 +1,38 @@ +# Tencent is pleased to support the open source community by making GNES available. +# +# Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +from PIL import Image + +from .base import BaseImagePreprocessor +from ...proto import gnes_pb2, blob2array, array2blob + + +class ResizeChunkPreprocessor(BaseImagePreprocessor): + + def __init__(self, + target_width: int = 224, + target_height: int = 224, + *args, **kwargs): + super().__init__(*args, **kwargs) + self.target_width = target_width + self.target_height = target_height + + def apply(self, doc: 'gnes_pb2.Document') -> None: + super().apply(doc) + for c in doc.chunks: + img = blob2array(c.blob) + img = np.array(Image.fromarray(img).resize((self.target_width, self.target_height))) + c.blob.CopyFrom(array2blob(img)) diff --git a/gnes/preprocessor/image/segmentation.py b/gnes/preprocessor/image/segmentation.py index b5a6392f..d66978c8 100644 --- a/gnes/preprocessor/image/segmentation.py +++ b/gnes/preprocessor/image/segmentation.py @@ -28,13 +28,11 @@ class SegmentPreprocessor(BaseImagePreprocessor): def __init__(self, model_name: str, model_dir: str, - target_img_size: int = 224, _use_cuda: bool = False, *args, **kwargs): super().__init__(*args, **kwargs) self.model_name = model_name self.model_dir = model_dir - self.target_img_size = target_img_size self._use_cuda = _use_cuda def post_init(self): @@ -68,7 +66,7 @@ def apply(self, doc: 'gnes_pb2.Document'): for ci, ele in enumerate(zip(chunks, weight)): c = doc.chunks.add() c.doc_id = doc.doc_id - c.blob.CopyFrom(array2blob(self._crop_image_reshape(original_image, ele[0]))) + c.blob.CopyFrom(array2blob(self._crop_resize(original_image, ele[0]))) c.offset_1d = ci c.offset_nd.x.extend(self._get_seg_offset_nd(all_subareas, index, ele[0])) c.weight = self._cal_area(ele[0]) / (original_image.size[0] * original_image.size[1]) @@ -83,7 +81,7 @@ def apply(self, doc: 'gnes_pb2.Document'): else: self.logger.error('bad document: "raw_bytes" is empty!') - def _crop_image_reshape(self, original_image, coordinates): + def _crop_resize(self, original_image, coordinates): return np.array(original_image.crop(coordinates).resize((self.target_img_size, self.target_img_size))) diff --git a/gnes/preprocessor/image/sliding_window.py b/gnes/preprocessor/image/sliding_window.py index bef92f53..40635531 100644 --- a/gnes/preprocessor/image/sliding_window.py +++ b/gnes/preprocessor/image/sliding_window.py @@ -29,13 +29,11 @@ class BaseSlidingPreprocessor(BaseImagePreprocessor): def __init__(self, window_size: int = 64, stride_height: int = 64, stride_wide: int = 64, - target_img_size: int = 224, *args, **kwargs): super().__init__(*args, **kwargs) self.window_size = window_size self.stride_height = stride_height self.stride_wide = stride_wide - self.target_img_size = target_img_size def apply(self, doc: 'gnes_pb2.Document'): super().apply(doc) diff --git a/tests/imgs/test.zip b/tests/imgs/test.zip index d4cb17d894e49d5041458fd78b40e9601d1d87fd..417fec0b10977f6d85fb714f595ff3fb9f6d1a94 100644 GIT binary patch delta 24233 zcmZ6RV{D)e(5~C9w%yw1*0#;9jje5)PijPs$vv6e z&kdkq4L<;RDKKytkpGDvE}(M%xB1@!0fGqPWbEifuc8hM@)RwkO%frb9kL@8|33&S z$j|>0^#4O({~uyxYf5KfXZrt0;{PQxnq1XU&X?(M%OsQn2?=xN{s16_B9wUzGr}@I z0*Pe^!*Y~C2Wim*XUZ`>pbO@N#YN#nVf7@0#0vw-sD3PUHV<#VcO7Q{UHo0wn&)jx z6{#BaCRfL8%goQ$soU2e;&|40R-j&9AikdjP#-=BBmQK0{U9KiupmD`K!`zzx(q@5 z__4MLpl&EZ0>7xCKmgoV*^lgv0J(F(;sw+<#LRc+TT2}DYyIN;_@g`K$D-GFvmxvE zchymH75Yo}D}evr3C7?O(Sq(u%eJ@ns1)pB3Ob|;&mh?QZS$tv`UVt=uS#mvxB9a6 zPI8Ax@q0D5eZFG}NCxVr>HhVVgXp0jBqtnveka{I$len20=|vE@AX0Wd_V<>c7=&U zLo!2JAjHmJ&gUfkmeSDhz?egG({cgy+3x^*fEN18@IhjO>?makHKNLq#3_FyFj#&( zRGYUeH(2#ImHcSK14j`Y*5o;;q{N8ce@tE?fhc%1uB7Hq@RU}i7zAmvu zf;iRV+`Ss-95tJ3V7JtDI&q(~UVjHXW+8Mx5Vqc#f7(SplN`!Z%oe94sm)?{dulyH3p()%LpS|#zl9-U)m+zyc%rr4B++nx;ROdAj_yZ92V^NM0qy7ZiuW`8h)etU{;FwF+RdzInrEsWn ztztf7bRcSCY5BhXg-bRN1lo z(zC|T=gfnF4NKP5Bz3IfcvtDo+hQfbTJcK*|FvLdc>Ez6AS`@OB-KE#83Z2LniBYu zs;`IRy1zMCJH#v!>%47nz1D^K7mtTHdwKcaMz?Czf`6^T@uuSF8hE!O6Q>eIe1NI* zlU&VwsYR6@1)3d*kHmL1E5LW=lku!&Ff!!)s-BFBa)$2BB{)J~>>DY4~ zc%;o7^fHs#f2=rP3n>@Q@&&Zpk;tE*vqUJRxfO9mIh)24;)Lj~zvilj^q+ zd*yNaY()#k-=P)ri^6dX-{15zfc5jmWOaXrbhv?3ZF7B_F87Wot5xNduU5DuznXpV z_-bEG?S8HBQwp+bu3(R@0x_=;947kbvyIydbBNpEVxpW<4-=587(asyo0PSiD|G%R zYe?0$kx`b-(Nj@+QY{XO`Z-h79knKP!f;d!d;X@x^$be-5)Z|3LSWh=*AYNNJVxtF zFDM)!jab4{NvBE{^KolFt-d5#=9Kn?Nu<=U1tA}SwrtjbhVYX_Zh#Zqpd^H}koCEx ztGZ1kVs`{(V+QP%-*8@6@%XBTy$()Clyx-=c7|*IlZcv>Z+MiVg@obrsD;2VY`vQK zk!YM_ufZ8i1B69_$JQQyvDeuzi06vp@dD_>VXeB=gnRIFW_fFCvX1Da=<29y$lTjb zHv*#IZ1mUJeDXm$0`?mgjg;Gkgf<&0qV?~q6iGu*av+v$VN;o__)$V-?hi}eqovXl zcU~Vvj9=W9DD%PU&QzwRgvDHtd4EfrWKG{CNw`D|R3xI!?*a~s)>VKyQ!#pdvZM9% z>9rbWo6qJXiO9-$BWyHY%k>QfYFwtf8Vtle34e`fnwlD;1+2U}dv~5U{NQv5M?Z3Y zA6a$>9H644^0R5WZwdRn_4B}#UhF^eDU7ww1%a_Wt?hBCLpmVyV#rMzqh&{k7^6}0 zNiig+IC@fRC|0?xmNY>xCFUxy36YRsG@AOiA(j2X-gH=Ie;amU{w5--Lj1g-cIJEkC{W8Z?ADFHypT4y zYZm#MqTuJ_AKgn_bO=h_I`xcya6op;oq1OgCpGzdYpOwUPS3*oAzadb&EC)HPtg|YPhn&&*}^ouyruyH&1q)_Ed4Jh~GS{D6yt~XvSR+@YjZ;|p50tM?9H2Ty z0i`0wwp=!+N(D3^nJxCPpkMQ=If%#0c0hKwi0c*qiCWqLB33iW!m5SpZNMKC-wOTivyi&4XdArcOYK}1nFsYI2I^CLE$+0Pz)w_V`*igjMc zJ#xfDUl}UDW(iK=5=9_dTxqo6$?K9l{(JOz*hH3j7kCI&-3D*F8g^=%2O$6RA5Z>; zS7(@NU%dG{LR_Iiv2sm`p5}hL`tLs$d1jDh}%3-4maf{Yj;IqLn zU-Ev(=L~>{OHE}Qn@odWB#Yg^V@lIp_^0U_=~H5@#q`M$_s_7;Qph^?8cDh%Z##o6 z8JJBpX%?9e4~!rg)(kQ(0#uyUUUJQb+mLmJD&{UPdT6YkQE3M<7qd@Cv_)Yktn2p& z(1*!>?taN#x$59W#YW^Cq?#m!`097ZDsGRwt&?cdtsuXoscxN5_6he5#+3@_3kezG zFY$b(FftDuW{I=gL~nV zfr=spVf|#`E49UeBckx2tx1~;D5)RfzK*heDQ{D2Pd^@?E_P&0ZkAq49=*N+%+un! z*eU+$G$r`3!}4M6>A&P9XE^MSU!E%!$@#d*q9`a~kNhe*|9ZtFR9ckB!S{yHE`N{FFC^s}!@>>joCX!G-W zc*tphHN?x#Su?9E7?B@{Qu&Fc;hr95=GdAp7j?OS;t2bDzn()(F!sq!|2rP=7<;xN z9C8GpHG&=~APDQ{lX_A}>Smumz6!}pQbCHfQLnk(g?^IdH=vFRaEj6$O<-1`d9p%E zuenhGbH$OKfjG0`kwhQHSf`Te15jbVWd%NaEH6^V9Hv)nd*TdGhN9Y)!j&wvQLgy9 zJEEa3DRpPdacgm#hHNoqa3M}pW!gWK%^0ENKHz?-%qka6b8Y>-nk~zq8ypxVxB*=ihkobrbR%Cces5q95IT;_XABmh#ZB_(oPg6dk6Ly(QMX-Ud0*Xha?y@Px?Rzm2M81Y_XuwR=Iuu)v z2!q2rtmAbY0{d~!jkBKS5Q2~GK6WL9@PS`G@_PWx;o(Cz^nE?<1cbt&i@yPn@}*if#-4s{|I;zcE}Dir>NP9fg}D86k9_c}Z?8#yx0ciZ!D>LCMPF%-4YU zB<`>sF(LlZE&Co6_RS=Lw}WDPK?wJU!o8?r-Sxihzi}Q;MmMu~DNjY6d&CuE;Krtv zrC({w^l4V2tsy|1Nr-r0Z=@i(aYZdYZ5&~Zd^LJLV65uk+OIN4INDO?{kXn6m}(%qx~kBG6Z^Osd&xPkrgJ7(@Or_ zUe-JBTQ&v?(iWnfQrh!bTyp@$ygBJQLHn|ID-gNx_8$D|<=OrQ*c}bbQd643 z$))*heTuOkk6(GCo04?$LH`ijjm10rbarVTYU!0*+05!)sc^DEcPTxDLsJLG9>Gnn7FxQYv{65$_ZScaix~WQXA)X~hM9pI|65`Reae^Aw z7qZy$s*ppUk|&I)|A#IMqzdzT-~YYtgoxHdgVBsiZXfr)7oP;f*HPU@I4$}zAcog? z*PzvshVesFC!OCF0SoIYo`XE^(XYUeNnhTGE|)sHh`Q0zEJ27Hn{~+`PQgt26gFpd zZD?*_IU^Y>crDgOt3=JGiY%ow-t@{}v>U0VewmfEi_lxkUq(Cvpo*8aFXFbkY!|xf zxLk|)z)!*cg`{Uk)@)L1l_{cb^s{%s7UwENziqf=^)7GaHCA}qh%U=-q&JUg{3+g} z)X|sGk#A*CS!>H*LCYOb6W1Fi?3^MUP+={Dj=nFNW|yu^pjj z_zphiEzd5H%N8Mtyle|e^n{A(v6Gh`%g++#{F~hux(+^V z`jXgQ#KMV)%7vbI08E^Ke}Am^6w8B)<$RpcusZ{wk;yDw2zN8t8baCOPvH8d9tlZT`4}Wzwr8~m?BfyD&X`h5Ji%4U^_T3%RF-)?DYPXY*i|dXa(nI$0A10^z;k3*0OP|-bFJ($Nqk+ zXiF(aEl*>CX5U50;$~-6o2i+*Pruh;hIE>Sux!T;e<%m5qUQ<29~#CR?|0HQjuH6G zsTpJ{1p$%Jv_-sAr%&5u9X|C2x5#8oHkbrxL-|twB0bFgn-XuN3%*Cu^AVoAxa3GH zM_zJ42m*gq+)13`ZEeM!PwbQz14UeKWdA%m;^`rwenYW12&s}%6 z<>A#1#vdLYF}&uU7!brJwS-ErD4JzD;2O`Xxl`5g?4=JayC>GL-m18AsP!N7|E&!S zHrWuKWM_5Km`?*#{fD3&SF;l4cnfHf<8fx$Lf11FK2Nqi6}`=fMjJ9Lb4>i(`=90N zn4G4D$F`Fr?{^NdrHF-<;Tev5d-kaRt{H{9>Gf%$L8|c$UQhxJXg(qr`VVfcS{-l*Kz0ERpO5QFZ`O>Rawqws$KFyzr<7RA)7@IZef#LH`!@_rvkv zkBxeVH$?9lE9N1dwIU(4BgGC^rdN6PU_0jf*G#3r9FHGrP_i?waO2Sd^nNEj;bbLTZdz*P( z0IcEgpZV88jBZI&eLhO9wECl0Fqa;%$9+dcSI0+b%07G;Z=R&Ro{SFfO4s(;$3A@f zI5erFl!38jxq6Uvapg-&sAJ=$h1IV1K zlo)!x0)_khjI9p($c)b4mAOMZ31jcvYh@YdIPezRxP6}RZq>-?n2l(D`!=w^kawBU z^L(RCRgGM0i%V&O4NRM(D`~rah1oGZQ+I9YO)0aD;_{rk>KihBv=3k(dU&>u$Ky7#H^@p1X|18$39r`MO-rY>>MfZRmV^M5VyhYbDHnk<9trFu1ZCOFI zE@hyqvNrB7{jkgcJ>{oF_+Y$2QPEpVT+be4Ss~c1hzgCR;91Jx{sm?6{^diRnn?Us z={ZaLAXXUwDh8G?s}Cd~bzC;y;44XoXj42hX}0ha7rkKBUwqI&D%pg@l!f6VhnL{p zOY6gHeHeTZgGygk#3q=Xke@3Ly-^L+Go%0Wlsk9#h@slnO=b6Bym-m;=6Zr>dlC87 zG}O!RkSt_+mm~Gf(g14N?XQc)@82sA-j(Q?2<3<+3Eg;ICJ9I!WaCz>Y(_wxOgGIG zydch1^am%y4SwA=Nx_#W^Zo|syn`GN>)+s&SR{RFbmstFx`p(8;%0N2kKdrB-6=c& zr^>sSPmQ&OxCp`tK`A-T5D3wWZarIf`pXV4MJ~3!$(w=z`bidP3tq-JkQ?vMmE|l6 zY?^J;x?$8>zdgVkelGbpnEjWPN0S2b*zmE$(&i!VkXEG81$>0ha?M2_`;gH*f5x&@ z$^_kxG_fTS{a}Y{8zzZ?IsRp~X~!a&*x4L;dsmYAh+Oa50LKAV(8XLDDGIX-dL7g06(Dj!br9Fg%lQ{^C;-nWB)2Uv|vmFU}95pbiPZAebhw?TDg) z7oZQ1ka?5{H=LW63OVV&vu^Cp05g_>N-l$qynS`TO1Ff2h(z9WbJM=%%0J92Xjw^1 z=q@%ta|hlJQe>un$8wlTVwBpU?k?8TxU0P2NOz&1u_Auc zmc!rOK!z?02bYzM2Xc&np)K3_t{qQHXoGyZAtXPewPpN;OLGiyUy zu|7)YYS90^CY-&yrLAb#j$8Nk^JH(A*Et{L{x4v$_T)}o(2tC#7NuFd+D0Ozl*{mh1yOvug+}KZRL}26#}Lq zKivSg%xovQvPi(VUl#k15gmoBEu z>P?#S_sz(CRiu7))Jkx(RE!GWF9;DUJowxdzuxxrr;ag3*U%9E$w{3i=8-ilMZ?dl zz6NbD8{RtCwR#_jXQ~Ass{QE)^|CQsbeLbPL!&h@TI}N2T#_7rf`+(`4Xuo%SC0YM zI*nYNW82Qerw|#s=M>L)v$EgsVRkz69UQ}M8u_b#@qZ>vO1RCZJ`cT1osVuOAONf6 z^*&(44x68{G?||V7vs#^bm_L#O_Qs4&3}ew>5xIYvs|aeaw$lvXS;1CL=>h36Fa0q zHpU&{5ug9K91GS8(+ss;jA6&rfoubP3;YET|5&tSNY5|7V`qkkn%$ab`UO~WED7k~ z6tz!4szWeYm6hy5LMvS)pg?d3*x2veiE=_X*{ptJ2S}NX@?d=4h97HgENj&Q&CzN6 z5?jMo{@qD6mZ5d`ysCZWim#FmT?XULT1tv6a7@3x%!-M7_mdYI2>Wv}i?Fdrh&!Je@D zNb(5^!3#%jEc3*K*$kkl%lE*PV55;=IN}=I4)Sf*A(!_*Lj9#a1loT4UT}0AQOA2L zJ)9zDqMn;RhbL=#_ta}@q6>5WKPb`<-N)5yWZ%>q*+M^MH(*~HGfE#_^=-TN(FIPg zzThR}uCi@sD@JM^|A@J`BKwTyVJsO;mF1R9x2HMj*+5)SW;OAmT)+a7pzGc%a-{<( z_B^yWNRDaao4>uG%Q|9P@R1stqio<|eX!yic0knH8v(a&D)G z#&d_-Bpnh%N&<5DF7ZH>M*NQ^f`;)B_~9>~dQ$@-FBIz_wG(uO`I7z;o3(mS`Wv^P z5JVEUQkIxG;k7dgsvUkBdnXvtmY4}RWt)@rurwR7)c#(8P3p?5zQ z63&)~{&}mxH(Bx=lHiM*(R~}3qbZX7EZc)ib1$9i0zJ4?+6^1+t_Y#6JsuoeJqA?X zRdGQ(&{PF(cay%@(!7YvKU$9Vv0*#iJ!oJUuhOFppzT1e4iBdyNL|zNjvW;PmVS!x zBv1Ic1eET<@W;oGCAJDP6shm~dsHEANw*_P2C;fL`l!XIxVpZvdySe2MgKPx*hYxg zd7*+{BIzGWWgNC4<35P34E5s{mN5ES+_<~RAY&nnxaUV>nm$Ax7EA-W#N5=O;t7QE zo6!E$HS)l!Ka!Kq7|*dy0V`&gSA-2TjihCkD0BO6frq+DL_rg;sKvjlxl+LZ=-r71 z6NO}xWsO#XCpKQ!0_k#}`YyZ3w3*ZQ4l=*`CD|(;|MtP%skaO!wJ#w~D)E{V9Fd8Q z0NurYbnP{_G+g@?zmgFjfu*Is+eajv(BB}O6emC`&O*b?NkuGk`a?x-n?k6*J&(-> z|E*Hb-unbM)end9&#D?dPSNV4ft|#-h`~qX;=GULFy&&F2`2@#Gv{hMoK~wN;m`Ct zm8nl*Hx!CUrhTYY&(SCPB7(-``5w!{%?kchnyb^5C_Disk{#qH;NS=7kJou1t0 z{drhhwDoSuhn!<&gWS_&Yw=C$&Ct(^rl3WY9054aa%8~KN76_ zK`2!rN*MUjTo=neHk11&oAl0a>wejW8R4Em@i6<`3B zM=@!QXcoQ~nhftTa7@~aEMNBu&AdOaZ@&2t)%7CK+qc~6t6 z-ltbp&s^2a`&YosLOw^?-!>Woo-GZW7!Sj|ozlkg9K0bdkUTFiXggC-EiZ1S{}~qz z0<`Pt08GXG2tw#b+3cw79CM#fxSe<{8es`$JT(PNxpq32540rY-tqU?ch55^ToD{% z{OV03mxl|`(aego6jifb+Iy7x?a&Sv?ZO*Ws?pzc$l%vi-z5I5UMnNgIC2JYd1*&# zfYsN$0mr1FIJ?$ESz^h-NH)$3(X6aSzdOrTR&%mCvi7@SpZ_ywyHv!;{#`*lcFQv; zrdUXkx;83XZcBx}<8O~#3#GHI!JzgpehGy(hQ+Ft3ZVfCJxwt`oH_tQt`GFa<$>U` zNrz4YrkUC|xKpxgcHww|kr=^<)i~pK4wLoVA)H5?i6oHpNg_B^N{VXs@$0tDCfsD)R7 z7OovqL&w|vuK>jdSPbyeEof|&Web~N$?P;;3t!5;dQi7-%cLA( z+*a>!PZ2ys!z$a5**~6<=}#PT`{mzDiAa=SF$$y33vm>1PIis_z1COc0a;UML=E_n zI~YE)e|QR0->!hZ<&^`2`pBk0nigi<+|3NZCoM&h*hQ7k3mmAAHvArg8i29d!%(?3 z5$n@2jxs5T06zL@`4Uyvi?{15{nOad9X=BR-u^nxH!7GB{f0r)h4TRaGX7V1qbW!kN3>tgaad8P$b*`-NeWxMj%dlu!Y<7?S zOk&O*O$x@~vMoSHrR`!h;UW-Nu>8Z)D|;CI!zFRDwVFE4B$RUT>Vb<7y)w9)Pb&<$ zY>eXt&JAc$%Xb)iR}aU+Cwt0z>wP+3#Bm_shU$33%tY(DL_J&&3a9Zxd-xn;X4l<%=4zdW(GZb$|0ueV9h^PXvm{fDrftyrgcFGc)Z9>y;&MajJ1C zpH@j}(|#d)uHScirbrTQgawnn7@tLF(gv9-qgpke2=BYr1U5nsoqQ@$`FmElD6u~%l|G{j9*F>iexd2>sDjR(iyDDR`NyrJdAjIz7vPI6 zp+N3dqV1I3+UeKkQ0onkt{RF1I8ApqzGx)-9$PSpdRVy3yQNd$Uw)08P>aAA(3?H9 zVAD(SQXS$)(+_V>_a4B_Ul`vug1eq09!e)87E8(lRqTZ*PfQ&O%i%UuYvxNHVE9(f zB7Z!GD+kS9Up+p!w-JD43`ip#fZJuJ;&l~r8B!wGF1zJc;V-u=rV72*2K30822o%h z9l1QuN7DDmmW0q#Qh7Yma8kszxiUF5Dvh<*p~42?U24)pT1g;oMconZ5`@<=CtOCg z{c7*uxkR-d?suVRaYYC%s8013!!A)cIf)NyW}WsX_S>y@*5UzELjS%QKo97!Q#XXuWfm z=~HnETNq)(V?a+&=iw8ndUuKPQQz17T`7;h-9#F=w5I1`CnxAg z5);*cqM#iQdy?k(hRj)_YRD?U8WC6e%m2!wNTE5*3b%h4gbN)H1PmjmAHY+j#@cBg zdql^KC=If#&G;=6FDcjvzsbX*B1dR-{pa(#ayRi1w)SpPMauTZfe+U7QCZ(|3fVL6 zi86F|dXIW+*DEu>n@2SL@fV%;5oc&hUURbF8+$iOu#A9)LBgwX%bL|v;!G?Dj zOtK63Tko$qpiQ0!_<^>R@t@-}U$O6?+5f^ygcMyTPEfi&@c7vhLpi;w$H#+Bh@U_j z%|;a%$_2u6Kl@v*GOZm3=$9KLX7wGmLn2DJv6wa-d|~nNj7D?=DZpSf)CMOwUEz>1 z&ph@>92;?GfKN{+b~Mc`Y5$d1R%{tU;6zjZl5N+--P-R5^lhT}Gq2W9q7RxzrH5*+ zQuTs1*feM#LlTYn)t3izYiG(ir*6dts>RNQ08=sm}COeF|T*7*Gdl1K77LyczJV^BrrZ3 z@WG^CA3igHiHZiN^e@e~DJhMgoDFc*71o*GTc;L9l19H7yU|yoOjWg`9zEW4n7>ok z;CimKymOzE0#m<6gri0)xAHS9!W)cdb;ov^rk`!!Ehw*OAA4MT;pwlTC%~W2vIGjN zGrW2mmQW%xW6rTeDym%PtFWM+5xB4NJbYSy6f2|vJ=_5S0`IWeD^-w1*@m^GX$2&K z6+*_ES+Xjnk4dP#wv}4Y<#q{2O~Q#Z&=`B6aonw==f{0n@5zo_Z>5D7-jWe`Ji2*y z2g77ccO1>qh?x0{;r?T;Z(Cur;0`IC4mlv;;&j1jf}JD%w@V)YH7$D!K4KO&CpTzQFwxFM-}ouay9q+g8~g zK3buqv{KiQ_Zq6jEp_Yc1AAwG7`cc3%qE^3(kpLgac}vg zP49}(KvB?~rY6Z_FG>kaBUsr#UHIzNzt7?S4t8{rh_S67Qw6x_ahGvvBZ#89J)&5A z%lb5A2+^jD2AL_s9@YJnF7mA9jetW08C%wct{~_S{R-(dyvyUF5KoZs4Be+oHd9Vu zTS}B!hd|Z)=fdAAP6Q{7qehO%qM_wjpG4!mnyj1gP#Q4Y7SiaIQ^rY-jR76yK;_sNEJX11# z8J#wEMz`#Q59Bu2+3NupJPBc>_tw$C8l`B#Z`uN{nweib+%`j}Q;j%*7{l$a%a#cJ*rdl(3%k!xAx01lJ5d3^bygu#_b1Q?N%!XKjLP|&q zUjZFY-XlDS8%j@9-B9M$8TOD1d-kDO)SIN#lnu%S&$`^=j%)bvLBzm=;kFN9jkqi|aZEJ;zH)xXF`w@^ zgNdhUAg;Fki;3QjBq`i&k3*Qb1vBL&qI)A6aOgaKK1ddQO5}#f4%E<5a^z$PBHU)` z#MAc);niZ8zAM;qb%T>Xqf)u)0PDhEh4%K(WyTh8wvqRoII)(WRDm#_@T!<>1oH^<0F%)dLR-RMbCJT!y8;oOMM5&u_2M%2a8Is#Ok?BE)IJ z#1^xWKYYOUR``L=xqTy?B8#dcyVV`%DH`LwcZF@uVrLN7rm_i5<6f2UVZ; zvYEKNTSXlJ63qki{7FbyUoM4lpx}cS#Oon zFikI++ZRB?;vLdvlI!;y65}1ny5H^I_M+@)Q?3YJ=w%zTS)X3#^nvE+tbY1o(&7Ic zIZ!L5g_mwMo3orJJ4}R$B+U~Y8wLIBc1$?+&4HdClSteyp%r&6rTZx&RSXaP{PkwD zn)o5Ag~juSVO;>L$-~Qo9gRwyPb)GVBc;GW7V>$y> zahPcEu03VEr!-rC?0xB--f%`3?~gqwWYqEfXPf(^de;w?`z<}k#h9N^W*}~Eijt05 zIm@nWxcRmtlOxPzPrfH zUkYecB>Y`RgGtNxBUZLbx{0`>nWdx|XAxf?x9;0q9f&Kh2y8!rNxg%YL>I^I=wt^E zzn-tCYPDRym8q=T=Uia!tn*9L6q6rC-NhtYmcfSf13e|r#wd>N(H7VuO==tzZzY8s z6FQP7ksH!r9Ug$Z6jKcNtcnP9{LCBFX8`(k!Mjw_y)NR#i1>dP)P<=BYb~;J8Esh+ zg>QfUauad&=e}8De%A5bt-4tk!j<+>uH|VJ@$?_DqzpJ&F)R<)X&x5Gc=M9Z%QUBF z5Y2HW|2c$1fo4gs4f$hAU{eMK2$&xZ>ad$6l*_AtMRVezrBFKi>D__vYhx587Xrxr z=kAm9!84q}3fpz21_G0-kwx!DqpGKi12|Q4 zLrFeFTHbH!2+-KeD~{;ZIRe(V&#v=>VdK6lAZwNQCW>mRMv(KPga9i}d&rA3%EHql z?e85rvzCX&G;1g@9nR%KcqeHer!w+SYH`_U7yQu3m527+-@bZ=I(WUagR4S@%3Nc3 z%kh^@cJh@_!>AJ5j>YMEGB0$f#7Or%Yt+&T*5XtP7Ts3VB0^X$XaM8we}6l<6;Uoi zgzZ<>3dt5^%^ZYIclca|8PWCWdge6NKGs(6&an|L^eEF~t~1QF@5rvm>op*wDMvmy z%&DS|?lLnz0sLH!tsSbsUm{le?&wLIqNpnP$m=TZ4@HdRqe9@iu1A+>hC5+TxWq@N8dg<514QckY%Cg*UNOB5p<&$}UHVY-!S z690{a=Ewp*jd-9n2~7c>>AWX)>;Q&qHy=Vq#9BqarSKdJE)OO{55c~_8u4Q(p7S@W ziJr(ioy}i=q-WxS(mPSg^Ve-p81EstoLj?Ty$?+v=F3A-ZGJc zSoW-|3-3=VmdZ2XplAl+d~;Y3DFj`oBa+h~-g%?P#MugG-Yl6s!+6ea`F?FOo;Uc5 z27lh8S3Y;}dw?g}s&dWo4?CBD(j%gcxUJj7HL~mx9(6axa);;1cHXsC`2OWVmZeW~ z@PciThny!Q{!G*9IPZF4NR-Fbf!VO0YPC3}UysR;m*&gNX^C1Z`UC<3$Pu}SoZI1; za3)&=20=z;;F4|NT>>(X93%7=*~>Xl^D0bA8?V635)jC=r<#S8bDRj#$CY7(cHkAk zFOKPW;o3{)bJsTCWA{8%WrhSE5RU{CVi%j+KOaE8F93-~-LIt54p~w5bVRhS?}PYK zm4EDU3cu*Q=~5na7_%=5B!w}GQwA#uVmANmhz7Aoly`&}FRQE($cqRYLBzFS&acxt zUV1R61Kx))1UZF2IZ4BFAJKdpFhoX&_pl%q>7=h&BdZVTpX1q(w{J7Yzgkod6ZNNg ziWN+lR$dbISk)gOcy10`kk!!`orQT8Q8Ztuh6Lio{a#>=;vh#KJWN}`)}sI7F7g@m z*&AlNfvfN4lx-IOo^6xX6NtHC2N_O*7Gz(q1Tw3stO&sZ4W>o6?hl)S!fiIvKV}%x zMyY4dIWz#X@Q_9+UziZi>N|wbxcOzpfsKGAicXGEpI?_E{jWoBgbOLTWoh zhcYE*SI=k{n%DYNc0Aihw{!EnmlC|S8q!lc@DY(kM;{fRbI;w3czUK>^ewS#lRusJ z;soKqUEmO!mKkHoLA%XiA#I~RjIdw%7ih0h$e9fhC_g?&G;ryXlhb{3YyAPP6b}FlpO<^_8N7dI3Jx%wD6o1WCYpVJ*7nrR$ z8{E^|C%E3Jm}g1wj~cgzgm(Xv7-(yp;=X`3A)_5iUB=|3K5$#!SUe0m`ids*+OYtu z?@kLP;PRE|2z&Ibe521g(6XtXY2dM7ounB!_Fi7F&$pQ)@#&_1qIsS{>|~V%Q2<;u z(W>qqm$E3P`=9211lomGUav8P1H0>3f&HnZCnlF%m|4GuD~83GF9W4?(>>gSmd%C9 zj@d@~)X8fNL^sDe6kK?$w^oGgvCDMEhhf*42(OiJ1|^4YEw~8X9-5jjZ9({6D=Dg1 zCTe;rHQ)Pc~ar^xh^bx&b2;lN>k0C-Bp$u6$? z8HBiqvxxhS+KCU;5tacMaHOd+%e{h;V}>3$AM{(_0r9F;Eivfm-4*U})Jri4L4w^k z8quc2AD@BgJDZZRpu-efec)&6ns4GRKaaFPkLbp=9$=+?onmiNZJrx0PWHsd;Ni79 zC_ii-*?VT#R}&mx16}9ffP`INsS+ygB$wA#nW(Mw;EF7(R$MznjDfceSvm&rlis5m zV;%TMvG>a2Y!4M^vLU5Zy1nNeSqyN@D0@F0IAe#UsBQD}W| zUp|j62#S~0?>;1+y5jJgx;z@fiRFI^I zGYR+bengY@XF2kVy?!?|zZJcF^uz{uuWKAsFhnIiZuH+ifE7v^8k45Emk>hBzR?cG zv|A^!(`|@<23@oIhPv*Le%Gq~(6O#^;{A@<<=!&79~Fdnt(5 z=Xr4I(G=d?KwYy{@wuWZa)i18e+OHZezw!qH@kdO@HT{0vk74|1U&@l1PA19Dt)Fv znJt=y@c!ZgaEMfi&sVAje$o>WTB=2!ph7-iHkSO&6b+jcU==8k5{ywZ8Uh?K1_kis_HI5Q*{5}pQiR#^vrak< z;BE4MR8oj|Ze-OaKYf2o+buKw4f8bQd6e-wjLX{hRw3~SEc8akZrC*qOKb`aiU2M$ z7&DNi0J=m_3FgA_nsY&^&K%4I0e-FrTQ$6?b8VtwNu)Q+T}XVV(G45EQLTc3eSXZ8 z#1!aW4554098xErhJ`#!>{yM}xd>5=;fGA(kxMxYI4nxO@+}&N??_Z4E=Ul%y0pH&=X6A{vZnd=I?diUu1-3h`RTKMO ziQj&qAlj7;VLtuhWSl8;6Ktv!c9IPPewA8ma1pw7_dxgX9ZfV;eqnZv;J%iEapr1N z%H&qt)bx$xu@?<*#j|_;b2C^%f-uxY;6w!CeOsd(9OT6(V^g2M9$RF8Q@lQcnu$Va z{Qj#CDtdZ8UPEL@JyK_1DUhr02CvLQ)U}aZi(dFK*c=Qt2-V?ul;7FltNtjfU$HNY`%0zg=uz>x)XB|=XYtf89h8Y9D4^tN z5U2d%dtSPX=STX!FJ)%?!u^&4a}`%R{o1m7ax>>7YJyXHnNNk$!t)wPWg;Cl6l*(L zf!euS9elCTL0E##R{#kt2BC!{HW^KQn<+7bV<3}cl7ynn!YaWDuAfN)_s$X}7?JL0L6PxT@iJ`b98*R*;*#=f`Kx+eo(`XiqrQ2P9qH2` zKV}@&m-Z8JSJ=^g!SbHaZa=H1^8AvAMhuGWtk`W?kye&$`~eBa5K%H_U4k@)=2mZh z6%416si}m(LZM95no9&6#l{MsQ zbAX10eoB$-4RBH|O0GjHFl_e$FH_8!aqbZT@dJrWJH6xG4*gPCfg|g=Vg5j5erTs_&6^&bQu!D61o}F4kN)JiTZGY8U>8OCeaCLxBEP8FqAXDr0qYp7V$vIebYy6N0xw8XqOZoO{=CR*P% z>xV_f`)tnUepin4&LPh6Yf1n010f0Ll&^C>+usdWt*z7P2Mp63Xai)nP;JFU#2zmz z3DR-A2>~|U?;7oX>#euf*GO=KOR(D$xP}m7CI+AaFB^VsuS7~G;zebqcFaQ#Q_$U- zIId<-paxB%FxuJ^6p^xV^J7zfi<26r53HJQPNF8J0`nJ0)00#hy@=pEa3X7byNAfF z(-nw~5NRW~GZIClz5fDJVS{8ILd88(ey5bc1T6C~iDzU|I&#Qv$GwXq{>i9^OF;o! zgbpJyPs*9;s*Iku3#wq!jKe-`tFR?nxsUmtzia)2D(lM77Qu!2Z9;^=P?*C-q2{R~ zZbnX6yMbGIOIhPeZ8(MO0+unoB%d7b%ciPPA88SYXMrob?KP^99>`xH=FA2oQ3x&= z8W_3Z8?pR#JG7ny`8+eUD!+SVo)euPNndGB?bdv4-!^5- zE*6>*M!pJVF?fS_&1aNbdKYb}qOI2k&>4??zOLkCnHR~7Y;J_RuZ@;w~pHv+q9%&1@YD$t^6)HlVchJ||*hDHfU9~CYJtR+7_ z#(m%wa+GN;&w)&4KYvyTI!!u6o3uw{*5_mJPXDH@s-06)G8s!~^GJYKVZ;od`58Dp zhH9%BuMu2yp{IM6b*p@8?*+~*ModNoD~$zl{8bILl5FNB2am(m@F&HJNo7SHUUNE7 zmUp;>@21yRs-af}3Z2Sv7vD3B`Q5yOE3J`jIDXM@Nw6aO!|7JhuFqnZgvl;%V5Aqr zQXTUAM7|qWkDdfIjw&Xyc%UmIzy%0R4l4Iw@!1qE5-cy|KI&!Ma9|`qLtnAHC9&}L z&WfGSIVpLf{?3or&BZm?JPjFJAvh-~VYD=_QnwE6m8RAy@W9NDkXh>6<$&f5Gk`zC zZNwMRe-BqRTNU#1JW%u-Tye^3z-KH^?S*gOH8<%sw&tkL`xrP<<^xoZYu^LP`SsRI zoMJ-lWoL`{SMF|Ir=Y~#Xr1S0Dm}397__!d;?claXaQ{N>bZFwF5SGEN2c${OoiAm z3my+=ogGj!Pj&21GyXZ6e{KA1Gb4(1pc3=xBBUEB+_B-Sr;Hwey>y8+$rtS_Ic#-k zx@gYT$+blqrEjX29VNzBBMk`U{=CmjQlz_|5_A}D0^6`OST=>gx_3P+PO91e2-s~? z?V9Fc(C*lr*}Nt_$Z;mjZ;CQO3-Q9{!b8p^Jqr?x&`xiYJb&99o46~h#k%^dOZ#aU znIl5!%NKh4hl(6&9_d$0P345dQDG?8)?0jxN5UuDA*UhthD_rXL8*ZH(YRs5L%jW` z6h8;_O`&vydt5tp8IN@;w+P3f%+C77wlD3(yM(+cLc`FC`BGLK{2x#X1Yh1ZytpG4 za~P{}UzFro>z@w(aFj9;wnigXU`gFI3kK$D$C~e%tU7RW_p# z9jqMg51aSXxcWfYrG0C0#4{u=KT|s9Ckfv&>Z+9f8V0F6iCDB|ybareE0t8Vg;=d; zDZx|rE^4m@80j4>R?Bz(MIz|YW<-ND$)e5fu}+@d@o33S9i?8(V~d~OcE&jZ@NNXY zk{L8A=Cs;h7u=cS>cKj%u0AtLQWnOf9r4oL5aD~f`kDc5Or`7}UqY`$;TmEX`?0P% z!+Nz2zhLGYwE#PrNu(mJ7HC?EWg9%0C3zke7~*cQLeM{cZKM?lzIRHk^Hg3fF^ID- zpJ4!}!kI`!5jr8KUt;yE*G4Sh&%Dv)9SijFd8Kg>DJm{2SS;e%+HEEUp7mc4enfi`NGDRD&5pFC$8kA5+*2@|mvA0grL2oEmTX)L zk+Luvu9z@Aj%pV7mIKwPA`KLGOb}EjJZxsg{tzm4fZMmt`mrZc?1s2*Tn*B?Ec&UA zVhp=moWL+Xb|RWJNqJzHrkiAz(nS-u#)casubBcwe$e-8j1@!m?sIzHJZMv(?^5uL z=XCZw$GBZt=im#ILTI5>o-3jGq&9Y%l%VN7WB5r{U~U;D*>`_Xq$+eX&)fn<_H5J@ zE`9nlM-RgF$OVlL?pu`p8G2J=pEX3d`gy3m_}o*=8EOa6=tP+Zrl@_b~0?YX@&%PWza$&yv*`TwtjfLANygK z3D4)aHyt*Xq4er1Adv#YEv=wOXr)z$^HvNR4U1$WOU;S8p3*BVaHF$LyZdtb=*`NGmD!adU!m#E_&>Qt~1pAUm1UW}XAuDK!t!l(KL z>7uunSC!#3J&W(PsdY7}tuTu5zqRNm9N`TJ1p61Nb!Ak%M8`8?=NXpfeHKaq(;3pA2i+t>&U_a`K%-KoFRQBG zArEI1C&HZz9B;Tj_01YswMx6raBrA>9F>CNQzwSxD-5Ig=h3E?yU35mdqv%c`G%`@ z5P^y=9D)M9(74#)@fYt*j?FE#oFK5c+O@?hvrSW5`?QGB-MBGGWK*Q~x7{FMv6xer z79I`hGdxLwVQF`OE(M-^$$V%ThN(_cNjh8#?P@p93bC209wG+iq{zpR3m(D1XGjrl zQc@T~c75jC8e^O{OHuHd-i*lTan)W{5xGX`=U=BwC$*exBU1$JEi@2t-#5(&N!1oX zwJ0kSoHpxwXu7V=a3ob(2oOELku==Lf z20q7xLchaLcSgSP{aM~j`t?S`zrMt2T)qLw1Li?_8pQf2Lx@+REVm_R-|VffBgI9c=fT~-HaNafcJK5w7Kc4<}q`=yCghLpK#f+w=btAh7H6~HjcRE z3ER|G+sy+#Mp)&!F;_LU>UBBku5+ZbpmPTbCNNv3dmy(bcjOY0C|-f;=LC5kc2ug? z;d5!lQc+gN-9C?e^jq&|Ye&CzlXlHy+O#6FxxbZa}uM@!0mxn*w~ zWFP?lHKh;9_myM*ydX^$WoR*;)))+}j zRr7)^8}5&AoTe1u#HF6#?x#Hu##SnN z*Al_nj1XL_hjlDc?g)lsZt?>2I5boRVIkii;rl{sA z6spSNUKhGG`#P-w(HUn@>IR}8m7SN+BX5K98^qhynU}v%%suw7y)(nR=v?BTJg|#_g$3#Q_(ITZj zJNBTLo>65bAYm1BZQ_sA&0mBr=F5%hWQS+3P7fdh)S5H^stQSwlNV|p@$=$5JyYBf z87DTqvEbl4bF_-uLMhhD02)82g1F)nIf4%v4MbV0CN>>U?x49Kr`JqFZ&BGak+3W5 z8#OBu*{B}qV|kBJxKdqgaksy;!8ru4>+umOxL-(eO7PTu)vjuh+_+mf=HnLbrV*PS zA*U>?0z1V3-v?0wa9Y9U(kx1!wC-L#z7BDhjcu0?(HL-fM9enA245p&ajim;u4$M+ zCFeIvE?i34N*;z25FBZNyHQEco^GvSUv|R7E_>&^#H^`s;<(rt6U3#OY}ECU55Jb~ zymxs`jNQ7@4?PS!?q7r_Ll^K!K#s-#!ge=}pqn}dzg^@HVoX;V{4}4_i+m97`Y~MZ1v2juccjmNjFRaRHUj$2*Oi z{F8KdC@ox&F(ZQJ6m(!V^A2`|yx;ytJ-70Ui7MX0p4j*Lu9Bq9RYQ?V4h1}(;`1M3 z$hY=9zy?ox47T%NLz*uS-=1U%r82I%55o=q*3}^~#hPOXJm>&CwCI_&H;O;9pBYGK zB-5KEY5?=S(|BjbZAkiJDoWx#_0~$|jH90QnbFH=pHq#o9q_T5*9AW03H^cQhY0nV z$kL$&gfH#YQ)-o{S*{lxdgXp~7(CY9+VVTl6@}BC( zaaRo$Y1MaEFy2mK?-s5%Y4_AjLnfLx=zSYo=m#8jS~EGN+SPrdzYOS4RnbxE!{~ss znGa;@8-qvoz@i32DsxK&<*8<5m-<*h>>1tVYY(F#v6HVeLFKBbH7*n_4vykGOp@wH zIM~msQH}c7-;>|tHBx)0&{WVMc z8=)UrkofJ11^XC46n_r2`!i!+52I)h=q{|=PmhC~${rM$0k@}dzR6@dNf;NOdn>zQ z!&#{I=0iQza>}Ck>L=~&*^r)HT8i^H1Vvy=EqtsutxGH3U*J?FSri}9?VqoW@+Y-B`F8ru{<>NcK zXL`QjW@inw%6Y^lnJ)jw-FB)I1y=N;J z65JXOp4?VRcj05SA(Fu0qFf4VNQ)HDOi_nKk4&3lQcT(fGa?*Q8Z{3IU0ZsPv2k*t zYkf6@)tJsG<8XeK*)qRwLlDOD#kZXdDR|Ln_xeTw&4Sy59r|oEY6>QIj>jn=smRfM zYt@3Ga0KluHzLx|2yifxBhM}Y5Hu%J2#zmU;*?vM&kuwPaeku5fdHL@_SJ6ADU~d4 zv~Rg0#4ws!r@CL{N zP3d|4thWfsWjxyY9L3HCdaTS}yx+gEF7J&%nawUhnilMI^;mz-&ps?NBjpdIs2hJN@)JPX^_fz^<`lXVf% z=>6O?O4F@OsgDT_gz{lsTjK5&qFpI$LASCTk7?4+WMIL4N*Mk7Cgj|^!raAJlu|=* zWUtG#Vr5;tNf3I4J2DI8ottM=ZZv$cVQox>)w@kA06!mPMhmaL3jI4vmZ%DX=190# zq8pQYwW2Uf<7tVvEYX5l{=QfC3_-}EYG9-4dT?6?zF7Q6W=pOtbNVd)(pXIa9^Nfl zgkAON;ImS`b=Wr5lw+)Rk`Kd;EC;SePPY^VBUV&nf?;5EA|>l$kNy;;w`y1wC}?9_ zf>-@4fS*EF9cWKpQ&6Cg+pg`h%ERyAz3cOp>&5GD8)SWS&j}*Tk}$eMazXFDkUx3v zZXSB@9!a+FMVNEz4Q|}>A8L1DoA2&Q)p}X^*_0f#GuR-eyGO>s2-s0R4P`mK_(`X7 z%8{BMeNkAfI!M}z`yCw5nlhKKO_A@IU5=(h=?hvYW;@* z{hI>)ZvymR(CGgzL;thvAN1%yYyYN41Ah{y3I10xv*G_ik~THMGqp4_F*;R{*Eq${ z2vhz59ZLv9he^W5%32JvewpJ;45QM^Moo>_4}EB42E|UB$6y>9j>5_+O|!rMjRqfg zr|{b@H9nA%h+k4%NS$2x3ISGZ5)%qL4Hb0{(Y>vV=94hjCynK9=C_|@%J|L>)A+2* z%9d&2c9W9I@XFwpmzRe^iX~}^%P=`?0)nYwg6R7r`{xGp2Rq{@I@RFM97?oe%MS?b zO=akQDFh?rDyl^5vA_!%olrLA}z36>vs-Wy`Ubh{Nc`w|%~J z1^g4dpe0!1Oq5GBRHn#E1y%V-rufLnN|^9>x&MH{f64u+CV+(Smzt=EJ|TdBXb?xlqe}<^1^gPya!}BCF#i{u9QEI`748-J z{ra!0A^wL{{#W_mN&7SRsGz^g3j;X*RSvNHCv1Po{Kl65Ig9@eJJKIAk0P?#pg&K{ zUlPAl|NoOf|3d!um&ouGbsALu0n(0(w+tqu$7NA4=@>&fl&IY4BG1uQOpd9Qft z?1JCg_)vXWg9Lgjalr$ykl0i305Qn05Fd_oEQ*dPaDN6&b#K#Zu7zi?hm9IM< zj~%fVaOZ4haul8SQ%`W0L6!(CU7g+UR-Az2Y(l^b{ZGbsu&J5Zrm4yH)_aw7Sg#mu zX`uYL4V5(R!OiVG@i0kx?1I7eulluK+1a;%p?HY{L<~yV5kTLh(Izi$?aYm=joa zGcg4_kS7x4r)I5Cn(T=>9VI%p422kql8@npgG|(3?7b!bfFWeu=?j%1`KlC@p~pQ@ z#KVW(6K|Iz6!Z1{p`DDj*|d>T&&3#~lViw&S?`-fnhU(i-E}U@kAC};bFY3+9rT}6 z;p$6<`ImIBGy+%jW~rGiXJs0jef@@fOFLimINH&>xk&5@&A<&Jm-EBvQOqA(`d0b5 zKnxtL57A8N=lD9BVl)>kF3k)gKziBvo&cY(j7A-()@hF5{%$b-jb7=UDE({dyq!tK zo4Eqt4?fAGVt0Y>>5hG6ses)xG)^Cnkg-#0`Pj{#Z%EbFjr!&_skGsnxD_hRA<2u* zTgtS*7zvCzby>@#qxyJND4)DpT~HS`KvS$DG-N79_))~@ZxIGK9Ey4lqu&jV ziST5W&@}A3TsMEUSFYd{;QD!@J7pg51zPKHnie$l>N+N=^7i*=#Uy3H&$ZhKf1N^T zGTKHkoI*g%zBuO&9p||{vkXOzZ5Kpj{Or3MnlZdEA@M%)wS~0pyXNwwiF$;DGN{$F zl=9Ipmt^hm48t{0_cl5bOm^#BAD#lq+99($!{FodP zpGPE=^eKyv06t^`4pdoBU4lB>b*vkd7D@7rmDUB_vn*pe`ncM?ax0#lNduBYR;j|g zJO%=iYLJk@a}MKY>C^a0#wF)a3s zzr&=&PPgnaX74a*54rCDh-ZTlh+@!sguZppi);}!I+AcIAXS(GTV)*5K!-uhH55nX0| ztZCwu_&ZVD**|!&yH0#*pL8p9T`c>W1s~6~=mgtN5?Ch2YT$mV}-z@GALBdJrh-Gb%CG4cbd8k)5>d3BT8FsnI+OS^Z zW4ksGvW8!kElYZoX~g^$`J7==Bo=qJo|U}sZ{-)YlJlc1r>+ny;JM|x#(RM~0V9`e zp!4AQkX&VrEjCTA%g)V-Rv2vAO{(%1Gg`svdH_p7FSZ}ZnO2x(yLz;UO`FLc*qnXc z`8WQt+@a{I_pnrs+CG58`gv5mrkDymaJLvZu1r(eBwujja)7NG`1y4@{7dqoD~VdU z(H{qgAEvt69O&NO84{e1e>DXE25};y4)2`hdY|Zs6XX`QRJIrqO5E?J>9dINDI=~BEQ!$#Fegb)|xqov~ zhpLtPG6|B+Lm%%O(nm|g95nQW#jn7V^6Ym#KA(6&rev@`iS!{+8$*bqos)furw0&* zh;lOs2Amzi>$rg!lg)s+M}PWX5wD80^H`7gmJXOxIhmtOExZyI?S-^TS}CJYcdFkBv}2p~E10+}*SQit z;>(i>gmHWYnRkusssrg?G24wbdjt+ji~`o8)w{pL>mx*4ZAFs^6=Jet;icolf3Rbr3G_}fy=$*z|1>K^{F$K%hc@m+MFxP-OP zyxlJoSJtC56bp%rRqpMPx6?@VbzmQ&Noph>&oVh=p)N;z>?P>gm-{LHiIMp@}mz1NrJcbb0JRyGYkaJz`tNpY@YL zbOnv@vjKCkPTa;m*;2(G*JB%~IVzmeS=RUCQ&Ew$7DUsRH77Yd9DjiO&C1#Nj~$kw zp$TezOlG?qF3JdjO^xgUuPobrX&Vn-PLpo%c8ENRCDf^Rl`MFDpFp>gCq1m8#@O&$ z!P`QED;{@I+Z(ne1@R0AHb=Ypu7H(*FszLS`ytU$>lVY$dO-;IHGCGx}bX?^C?iYtwaTqU88 z4Vl$<&f_8zEj{W3V0<@u?if$w1#N=y*}@N6^!nwKg0isT%j-Umguc8hBOHF`FER)3h4dzq;E8( zZT%%43bB4o@n3yp=IXCasHtL;gHF?78T3S9gR+(4IU_+$R#I0AaazB+Tk=8NKX@9J zu{?aYT+6B70S_UTNX1{ohhk8x@10SMQoF(*sdwWWPsi&U^hq#D?&Ejt3~>@CKj$8w(whZF$k9sXqF!@=#xR6!3qC8!7EnZ>`C#V%b~p(MK| zK;XhxogxsFVZ3!Qgy;;f2zPaw@Gc^sIF}}eCTgfCG(XJ*~ zMM^T0>&-#&tETQWfBE;Ze7Sf8|!t?9hN~^jn~#Swh`&2ljL)30^O!>1#S-os-9yj8dkZwe^g||Z5`2^~ zvAwd?#p&DW0t=f=rnz1n5-Vj6PMH*Elf#P-u{LinM`%V&4H9-|hogN#X%{H8cH^31 zyFH-!SZ3Dq&ehcrn|gP5h9x5c2(@ObjfB2Z#JHV3_NCQ6LH3_{*_DxXp~8ePWxY}V zMhqXo3!a|dI3EGdA-SU71@;yHUmQDZ$` zW+v}9=HIBI+VEe@0#WHALvBx9JE|r^B z2d=&Ni+Wi1Qad|q?N#Q`-5v-Uu`9ht*M0B%Y9vQTcgt)I-MD_tAcR<68wl@S`YKS3 zhd~e>G}U(~BHxwXh1*RVg(x-Kg)2n%lnS0+oPV3e+Z?n9^~@(-ehnkhv>;F(O;`e1 z<`k2hNy({NMrWN>uGFWx#W!K9Vpwc_;g+P4^#6vQ5eXG0elX0}?;$m5;;!Ka>gZUD z^$1)Ia3R@>9dawL2jw@UP(vr{blT~aejwl$O%#+o>viBtJquxBH=N;+2xOO9@--0u zrpmJ~KAf3ORCSKSFn0%KQ(pYR6r=!|)E7GsG33jg;>@1L+q*WCbl#qp3SU-Zal?9Gv zbde3jSR5JrhCSE#LwB;c8*~PKyH$g=>o)8rOV<@K9f!n33?uVLa(Bk0*iwCdt79pR z71-w@ry7lNUH#|*anS+=w77lOQd*eo{692ubD(G^wf=Pl!7a_`h4XFIJs zZTf`SMrkXC^rOR@U-yW$MRR)!(;SvMHcO_XFLR{TCfkYC$)dOL1z|pHapu0W&K6>C za#F5_X#R!a1Rr^`yXpvE8u}DN_W6)DLG(1{epagTw^VN*7rI{34gY}BdB5-neX$d7 zM-+%ev%XkStect?V^)SdaG~F)&r?y>l?E^~2oXCR0PY300t}{b&hzywy&+P1A454aTKx>lKKf2u))p}drI$_>WG*Z-^`Zzm*u@Eb9*w~kt!S10wwRR5 zExLSmU?!fZy#!?eTHW`kg*A1_M7FtZGatwp)PH<^ZAZca|6v$bsI`hnh?YZjP$c~} zoJ&|ZOvQg#s_GIJf-KA21z|INZ^-eobJiYiI2wqa5{Q1wibA!`yfWq+*5QaOBvYl2?dG@vHy*5`O^7= z?oCZI7|KpCZp8g9*F?c(`y=$~df6-*183olO`b#$mbShfULcGnNwqYCr|s12ELz{> z3o(0Ol^`}|fR-WXVTO7h<~6CzH#o5?l`oLHHG!8HnYywkDFD<2_Die%YTskAaa5f4 z6DlCa{x+N2?sOa@UulMAT_51r^P1T0Ud%Y3&!_MO96`B5{U_CgU}gASiw@R~(o!~s z=qE$8^jVMtaNROpKUwGNa)XZzIm+RvE)bY9>2dBAHS3Ltt)}H#sA1j|RWOpGvrQbJ z?`=Ij51zefiNUv@|KYqg&qj}S?*FM_n;H{68V^*uyT5-#g&Hp6<%yQfrU6fSAR%X5 z);L=f*lS?J?XCXg}L>fAy1fCpEQ1qEMVry%?a)R{&S~1mgLcbBz|17DS51g|D(4` zG%M&bW@$h(*n{OmzOt#qc%6bb9U?a``c7?1-WexD;=mHMV;mm=xP26HG#yFw(^y#P zgKhfTU-H-~!F|*_u^Pto)*>n`T>^_Ij);$IKgQMq4VBYaXZCP)!pY0pd9_&hl}dao zrU+i)UKpU2b!3=rPWI7}@P)JEb|K4QkmQyhM`6c+6ZHI%ZkV#oYeQxA)vjt_&^C#9soiyZC@(o4bM!1+tOvs3YdVBB`cKuSk5&hb_7I~^v?&Bnvu{beB{`k|>J zT;@b!k#67I8u+k)rf`Rc=hNpZP-`M+wZ++q|0ucYwpa=EGfDg%yEUl2`a_XHeod?P zi_#Y4E!&r8xvAarJ@M)_ z`Oo&Y!!t}fVIQmxm%WI}p}gjs>VfmWH4OE9-ct3=2Fas%tzw{@4UTJeR`mrSB6|1l zmzc!1z#MH|MLVo|L+3QNzKf)BS*lC_(`-um-~zhn9~|v(>DkF%HSx*g+pMN{e+G#V zzT4rSXiVLf^kQvEiqj43^wKDI5Oj>}RjzedBrJDfe>V_@XUQ@@M`P?sdr-n03XMN) zr<%by>!5`RdNsRLJ|BPwMH$r z4dK0@sJ|s~i631)mgVc;?{*Z>YFwH#2_d;@-%n*)Cr!!cchtL2b-%pf-Ze6>KDa^Q zF1eFdyZyy}+z0qc{FxX4!8su2!;e%I4jBvcL{6h@ZEWy(O53g`r6W?;W!os2Ht=fY zO?2@40Mk+oMv^~OJSOAJ4Uv29AIL{Psz^xM0y{*{Ulx}0)4 zl(|i=Uj>gvyVrZZH@ZefqcnN07cP?zQCz;0kd{3DYCyGb+2`+4iR|j4a*Gq(_h|pM z2^ShZ=xc<^p*odiTplFO%~#fI4(VV$a;CtrK~WTHRj|8U+0oc*&UsQ3U65YqN&n@S z7z!wHckcnvHY6K>=Hy3GlJ`XfRH$yJOf0g>ge>qu6A(4vR|7b%Xv}YHMB5RaL!1J`}5%C-V8N#L1W1*=F0S5${X$TZ}w~7BOD@h6G@a-`3w`QZ&KaLF~Lkx=^t4M4lW>S=~B3{9cUybYPgHUT)wgrBI zhiQLEKm1q+Aq_cr*NpvqMuFwKC3MHK9sW_T*nDPa~!Y^}JnEM_p+p zQ%+S{l zOTl>8d6ehRBAI;yc3rVvyr=REX6k38XALXNU=3Y%e~|mS`yMHsjkPG)I_ksn=sNN&=LJp2va=?%`)D4J&*|>SM6I5mhiC+x z%p--9zc5+3!JtbRI!Jsz<`t7U&(UhyKDd3+I0tR4>VY8&rox3mrU_ zh040$`Xkg_|4+g8=uatqizDXMCjtO|{*!+x*nbHB2@pg^{BH$Ymj*H<0)2qNhyDNA zTeN?y?teegf}j7I|KoCz{>SC2$p0_k|6c!}07O#2e{}gb!AnvX$?!iB|M&d=M4*fP eH{#!A{!RE-85MbWgnyj}`_J$DXJ+dDYx^$%>;d5b diff --git a/tests/test_image_preprocessor.py b/tests/test_image_preprocessor.py index c224a462..e7286567 100644 --- a/tests/test_image_preprocessor.py +++ b/tests/test_image_preprocessor.py @@ -15,6 +15,7 @@ def setUp(self): self.unary_img_pre_yaml = os.path.join(self.dirname, 'yaml', 'base-unary-image-prep.yml') self.slidingwindow_img_pre_yaml = os.path.join(self.dirname, 'yaml', 'base-vanilla_sldwin-image-prep.yml') self.segmentation_img_pre_yaml = os.path.join(self.dirname, 'yaml', 'base-segmentation-image-prep.yml') + self.resize_img_pre_yaml = os.path.join(self.dirname, 'yaml', 'resize-image-prep.yml') def test_unary_preprocessor_service_empty(self): args = set_preprocessor_service_parser().parse_args([ @@ -116,9 +117,31 @@ def test_unary_preprocessor_service_realdata(self): self.assertEqual(len(d.chunks), 1) self.assertEqual(len(blob2array(d.chunks[0].blob).shape), 3) self.assertEqual(blob2array(d.chunks[0].blob).shape[-1], 3) + + def test_resize_preprocessor_service_realdata(self): + args = set_preprocessor_service_parser().parse_args([ + '--yaml_path', self.resize_img_pre_yaml + ]) + c_args = _set_client_parser().parse_args([ + '--port_in', str(args.port_out), + '--port_out', str(args.port_in) + ]) + all_zips = zipfile.ZipFile(os.path.join(self.dirname, 'imgs/test.zip')) + all_bytes = [all_zips.open(v).read() for v in all_zips.namelist()] + + with PreprocessorService(args), ZmqClient(c_args) as client: + for req in RequestGenerator.index(all_bytes): + msg = gnes_pb2.Message() + msg.request.index.CopyFrom(req.index) + client.send_message(msg) + r = client.recv_message() + self.assertEqual(r.envelope.routes[0].service, 'PreprocessorService:PipelinePreprocessor') + for d in r.request.index.docs: + self.assertEqual(len(d.chunks), 1) + self.assertEqual(len(blob2array(d.chunks[0].blob).shape), 3) + self.assertEqual(blob2array(d.chunks[0].blob).shape[-1], 3) self.assertEqual(blob2array(d.chunks[0].blob).shape[0], 224) self.assertEqual(blob2array(d.chunks[0].blob).shape[1], 224) - print(blob2array(d.chunks[0].blob).dtype) def test_slidingwindow_preprocessor_service_realdata(self): args = set_preprocessor_service_parser().parse_args([ @@ -144,7 +167,6 @@ def test_slidingwindow_preprocessor_service_realdata(self): self.assertEqual(blob2array(d.chunks[0].blob).shape[-1], 3) self.assertEqual(blob2array(d.chunks[0].blob).shape[0], 224) self.assertEqual(blob2array(d.chunks[0].blob).shape[1], 224) - print(blob2array(d.chunks[0].blob).dtype) def test_segmentation_preprocessor_service_realdata(self): args = set_preprocessor_service_parser().parse_args([ @@ -170,4 +192,4 @@ def test_segmentation_preprocessor_service_realdata(self): self.assertEqual(blob2array(d.chunks[0].blob).shape[-1], 3) self.assertEqual(blob2array(d.chunks[0].blob).shape[0], 224) self.assertEqual(blob2array(d.chunks[0].blob).shape[1], 224) - print(blob2array(d.chunks[0].blob).dtype) \ No newline at end of file + print(blob2array(d.chunks[0].blob).dtype) diff --git a/tests/yaml/resize-image-prep.yml b/tests/yaml/resize-image-prep.yml new file mode 100644 index 00000000..b1015b8d --- /dev/null +++ b/tests/yaml/resize-image-prep.yml @@ -0,0 +1,9 @@ +!PipelinePreprocessor +component: + - !BaseUnaryPreprocessor + parameter: + doc_type: 2 + - !ResizeChunkPreprocessor + parameter: + target_height: 224 + target_width: 224 \ No newline at end of file diff --git a/yaml-example/component/img_preprocessor_singleton.yml b/yaml-example/component/img_preprocessor_unary.yml similarity index 100% rename from yaml-example/component/img_preprocessor_singleton.yml rename to yaml-example/component/img_preprocessor_unary.yml