From 3b5badb770ad2b91cac4e046e34adf163e8cbf21 Mon Sep 17 00:00:00 2001 From: dyning Date: Wed, 15 Jul 2020 19:59:27 +0800 Subject: [PATCH] add tricks --- doc/doc_ch/tricks.md | 68 ++++++++++++++++++++++++++++++ doc/tricks/long_text_examples.jpg | Bin 0 -> 14273 bytes 2 files changed, 68 insertions(+) create mode 100644 doc/doc_ch/tricks.md create mode 100644 doc/tricks/long_text_examples.jpg diff --git a/doc/doc_ch/tricks.md b/doc/doc_ch/tricks.md new file mode 100644 index 00000000..b6852bc9 --- /dev/null +++ b/doc/doc_ch/tricks.md @@ -0,0 +1,68 @@ +## 中文OCR训练预测技巧 +这里整理了一些中文OCR训练预测技巧,持续更新中,欢迎各位小伙伴贡献OCR炼丹秘籍~ +- [更换骨干网络](#更换骨干网络) +- [中文长文本识别](#中文长文本识别) +- [空格识别](#空格识别) + + +#### 1、更换骨干网络 +- **问题描述** + + 目前PaddleOCR中使用的骨干网络有ResNet_vd系列和MobileNetV3系列,更换骨干网络是否有助于效果提升?更换时需要注意什么? + +- **炼丹建议** + + - 无论是文字检测,还是文字识别,骨干网络的选择是预测效果和预测效率的权衡。一般,选择更大规模的骨干网络,例如ResNet101_vd,则检测或识别更准确,但预测耗时相应也会增加。而选择更小规模的骨干网络,例如MobileNetV3_small_x0_35,则预测更快,但检测或识别的准确率会大打折扣。幸运的是不同骨干网络的检测或识别效果与在ImageNet数据集图像1000分类任务效果正相关。[**飞桨图像分类套件PaddleClas**](https://github.com/PaddlePaddle/PaddleClas)汇总了ResNet_vd、Res2Net、HRNet、MobileNetV3、GhostNet等23种系列的分类网络结构,在上述图像分类任务的top1识别准确率,GPU(V100和T4)和CPU(骁龙855)的预测耗时以及相应的[**117个预训练模型下载地址**](https://paddleclas.readthedocs.io/zh_CN/latest/models/models_intro.html)。 + - 文字检测骨干网络的替换,主要是确定类似与ResNet的4个stages,以方便集成后续的类似FPN的检测头。此外,对于文字检测问题,使用ImageNet训练的分类预训练模型,可以加速收敛和效果提升。 + - 文字识别的骨干网络的替换,需要注意网络宽高stride的下降位置。由于文本识别一般宽高比例很大,因此高度下降频率少一些,宽度下降频率多一些。可以参考PaddleOCR中[MobileNetV3骨干网络](https://github.com/PaddlePaddle/PaddleOCR/blob/develop/ppocr/modeling/backbones/rec_mobilenet_v3.py)的改动。 + + +#### 2、中文长文本识别 +- **问题描述** + + 中文识别模型训练时分辨率最大是[3,32,320],如果待识别的文本图像太长,如下图所示,该如何适配? + +
+ +
+ +- **炼丹建议** + + 在中文识别模型训练时,并不是采用直接将训练样本缩放到[3,32,320]进行训练,而是先等比例缩放图像,保证图像高度为32,宽度不足320的部分补0,宽高比大于10的样本直接丢弃。预测时,如果是单张图像预测,则按上述操作直接对图像缩放,不做宽度320的限制。如果是多张图预测,则采用batch方式预测,每个batch的宽度动态变换,采用这个batch中最长宽度。[参考代码如下](https://github.com/PaddlePaddle/PaddleOCR/blob/develop/tools/infer/predict_rec.py): + + ``` + def resize_norm_img(self, img, max_wh_ratio): + imgC, imgH, imgW = self.rec_image_shape + assert imgC == img.shape[2] + if self.character_type == "ch": + imgW = int((32 * max_wh_ratio)) + h, w = img.shape[:2] + ratio = w / float(h) + if math.ceil(imgH * ratio) > imgW: + resized_w = imgW + else: + resized_w = int(math.ceil(imgH * ratio)) + resized_image = cv2.resize(img, (resized_w, imgH)) + resized_image = resized_image.astype('float32') + resized_image = resized_image.transpose((2, 0, 1)) / 255 + resized_image -= 0.5 + resized_image /= 0.5 + padding_im = np.zeros((imgC, imgH, imgW), dtype=np.float32) + padding_im[:, :, 0:resized_w] = resized_image + return padding_im + ``` + + +#### 3、空格识别 +- **问题描述** + + 如下图所示,对于中英文混合场景,为了便于阅读和使用识别结果,往往需要将单词之间的空格识别出来,这种情况如何适配? + +
+ +
+ +- **炼丹建议** + + 空格识别可以考虑以下两种方案:(1)优化文本检测算法。检测结果在空格处将文本断开。这种方案在检测数据标注时,需要将含有空格的文本行分成好多段。(2)优化文本识别算法。在识别字典里面引入空格字符,然后在识别的训练数据中,如果用空行,进行标注。此外,合成数据时,通过拼接训练数据,生成含有空格的文本。PaddleOCR目前采用的是第二种方案。 + \ No newline at end of file diff --git a/doc/tricks/long_text_examples.jpg b/doc/tricks/long_text_examples.jpg new file mode 100644 index 0000000000000000000000000000000000000000..457615295bbfad1f64b52086d38f19fd04ab5c62 GIT binary patch literal 14273 zcmbVzcU05O({JcX5drB^1e7MA^b!)$hb|z!gGx8_UIGym5TpnQC{?A25F#ZYU8w@n zdnh4^^p;RUXyNkw-t*q~ocq_kcfKc+$=QK{@Gr0Og0e2Gsxdc1iz{Z~sTUKEanXn+m9* zUVnM@!xrgb)WuH#%N2kSZ5-&wX}6~^-RsoEiA39ZJeB4T;1F~UIYXN z1&4%&g}-_o8yEj3Au&DUeP&knhmSd5ii%50%f6O>tEsI+);IiUZ0hLj>h9_N-Pb=h zJ~25pJu{2KU~zw!S60{7Hwb(C2ZzKX((%c^xGvx4f5E!s|Ap-Tz{PTji;|j}ikj|U zTojZc{{m;Brnz;WmQ}-;?wKE(kZkmo8xPYySO2;yEN6mef9^j@&mkg@xlQ;N+JBJ! z-vNvH|3db^f&CAzIl!aKQU6Q-a>{>+n(AME34w~5mWG!0KOefQS7_<3($UghVYr;d zf9XRMR;qd|IYkt%H zm#%-~3)EbMwZdu>=wmaf5#Mf9ucUMVUMMC(>=A0XopZDy_ ze`iQ?zIkFfKvp90;di@L+v7ldXM*RsXW-8p;R~;M5N{ODA9WD3udijzb)vzfDs`n@Rsy)J=O3<@Ol-h3*i$@}059TgQ9<+oDG7`R@Z$gYx? zR}j2O>G&gNA8PjfaMLUXsSr3G>^)syuCBTkZcGS?(@Q0Nt&CP@Mwu2)rIPfavb5T7 zDt*J*0t<1DEaXuObnWw~K~yz9b*mk|)-*Eyc~2p?Fg}DMt&9Ae)=5npNx{u6+NL&@ zz0JhD)dr#=CA0{iibI+}mGTXz(g<)TB-gAVBXyBGM^KpRvyY&g2Cvyu2S(@4mCwJlVr2Koqh0iYupUH~4C1GTZd5S7EX zqaLN&YPfB-cUD?u6EFQX6Zb9vsW7G%s&7!%c2YIgfP=Vef?tghZLCdUW^uIcTHy`( zXgq}q?PTiAIQkx{F1gC5)PcSNb;4}R*-V{1!%)j=(rg8X6bKSTO25Jkou{N7uhh^@h=5W;C_ldkqLz@!4)?d4-jb}+E8JPW0y?IE( z@dWt+pVw490aV8^g$t#Z{CRS$J=g+#Hd~`!F$DsV1iBnqS~R`zJ{qKC%phpka14%o z8!2fy;=LpNjAI|rARfT?Cql=!=v8}_E|ax+|N4_|t@ovqlXHH^j5#;aW8TqWKjkE9 z!u1kPxBm3}ovG8W@Rr%_-AKsI(uJKTI>PAX9p3tbAyu6EMru6J1(V7}3h(U(@lL^a z<+pVoS=o1807RBnX#%E|!I{wVFv7PL86FQX`@sbOrHy&r1fPC2)w3rRXy-8-XFd2w z`a7pv(uY~l@u~_HwIz57#%E0mc}#L!0<(}u1n9k?;HARrkAl7B7yPD{v(zHqyb`## z4c=^|qAP3ltP$Ts=i(#tkL>0>{7QX8gjt*}m*m@j zo}8xnHNF%UE~jkOCMJ^DPw(rxR6+wt17GYj+?-Y%?YptOYf3_5=H@Bfnfd~m52)_y z+|01c^q;|8&aH*dqNA%)#GHr!PKfca#B+R* zh_2Jp0{)DOYg0HcAXAZlHz7I?38Fo)8y*Q2FOJd|dy6js9l!EvGd#CXD;}4ONE<8e zxD9ywUtO|9K*i4sF91%qj$F2(*_g+~>88>ww8v{y=-AGF|JKbOZ$6zqvY-}>zS6?+ zb!Yz*(_F}DA{qu>)1p)E#R*?MRrSy>LxwbziF$>ohu9H(;#=fgLcWE*bwJiOe6NG# z&U)W5yZ%t_Bd=)e(*Ahriw2jkwi_0HFF4rlemCL@Ez$ZND10k==gO`lebi_a5D$BM zxSI&OF5T|FEu%Xjpq=|*v_;|hce~c}*J8SfT4@abj2Vu(m_npgL;=(G&j`&A>PWY+ zOtQViG1&DD1N^3Ossa8Wm?L=1cE0!d4v)&y1Q9fsR`1}Px)!7cs%HG`nX$gb7$nQa zUbGcH9Wk@NPDtiVF4yzbyKhi7$mfYRguv^OXQ`D&Z(Eds7HxE@3tuM}e=b(#ucRu^ z#b0rmn|9sf40)Vm5YWTVjqhG8bizu7PYrq5;BD5^>>jgJxk}g$Jc!zVW<)jC!nU-$ zz4H0=35oyYWJR|uml&0z>zX+r*KChU?<&w0&C6K@-6oGZ046U0o)L3!|Mjv_5DQ6_ zeEwMZnPTOh29=hY9L>k)#=ltavk%|Q3FuPRtS{nsy#P?RdH0%;(yw<&)aa_1{Y{KY zs6S^PE(=WMCOJAxAA%X(PYzp$K7VP^{WjaR)c3;0fDkXx*?0}JA!(L!k4}%&U|2)K z;4^v`Z(IOgR}f=hwDVBYZ1ZWLNzM8cV^dI;d^rt zhl)S_s0ju=CEQxNM=5h~mB#wL#&wj@TcA?a3!b>lqD_ke^DTaY?wege^{^g|4lW2{6IceT`!J08r7yl62LN9y$FYA zUI3DTK}v5G?6o#c2=Ose2hlFi8{~vJVt0!hXs>_Q`j#Hn&)e@P)oAktN}(`JTwX-r zS5{4$^k+r!b`3CY7rMt%w3DnXU-@SIasHUZJhovz*o>Kzc~6jMW^>TyuJZcSsX{Gq zy4&jGh)Idoo);P$0gKhQXr3)=0?jg(?fd|zy!bv{mr+^B%w#c-hJXJ7VUhj#k7i;5 zy_;GHrCgFTKcuhSnL#~36CXXM;K+d1l$g^zFM->W22QP|cRIv;Uk;1E4rq4&`HOEe zjZ{Jatzvw($L}D#@aV3#JCM}a;1OO&#+hDVHS;!aMr-E6y%r}G;5*5o6r0t=axK|* z2`_bph9U?-{o|@$0cDDWUrk;SXIu8MXxZn3Yj7<3+L~%Xei6iG0g0S~t34{68x*r> zOncHSL3Q-zi4k9{M^(5J5?W3$#lV^L-@H8zwRjKMSN2{=M$q4C$^SuRH9-l}a}63+bOBiEmmy2QNwuA1K7vtv)q%{7 zHH)0g*pBDee)58A;*)C+oPEubUWV~$LdBQ7e11lONj^R}sej0SnwTcHD^pIScH@Ln zcIIRHDQMTO&r>Rs1tAjyc$OZ`dQ`0*xop_YdeE*)S4+G;w>Eg@6h`I<>HI@;x%fA= z`qnQ1Y|1q-!p7JbvQ^Tqjwdel+njxe)z=)9OW^Mf+2W+>RYd1zJ9Q|G7>* zx6f1K3pP&$>_>!iEgG|wXJ5+-)KvfnUxLf_`1Q9mkv#vMdV*~{5bm%qrs=!0~tP42I`-x zlBxu*hqhl@pKk+82%e%A6IB8o;ikVr!WbEv`n3|->M@^_Ub0aT`6_K1eP`|bZp>$D zh7_BZRM>8Fj{iBhA9nCPvxs{PS}rOYx*myKv4^IX%ENAk+ByQ!l}=$3hD_}OUq4el&)EhE zkOq2)4N>9*WLG%rBFZ{4@7|Bd+TZVXpMSpP$~_>H4S0TtPnR3?jIJ1<1{EiQL#7A$`+NwGFbgY%E)}++ySj>~*l+W*dOkSH zP}`lRzk6kLWhMGP1YykiLWj*<=(Jv#vv0M~3l`R{JOJ&EuEM@d*Py`C1t10n(Pw4~ z-NevLC)RnXVc3)FbIWW(jgdaJVIkA>h~;mJKW}&~L{IZ=(qWC>z+BN>>--#TB;iw) z`E?~$5?aR{xd!4x=7DS$KLv3zT&LQ3r)4CVIL57@njV|i=!}Svl-iDjCW&?-*UI8wgSCjm^1gZJr4jXgqTZ|yHJ(^a-9Sao0SzE$k2AI`G^ zs(;qMy&7Fm_D*@%wle)~wq!xukxy+@c3b_LpR{&%R{rg5uG6}$Z&ubIP{-FQ2QE$|36n9NcxsA~lHs3^4T zpBjQdJd&%bGKj%y$8+boiuIQu#uW9$ciPnzjn6~XH*r3*7Q-DUn#0jvXGTG8OkKfQ zetoVv!L59MEFDa~eU@ICVUb>#ee!`RiIIhb`AnP6^wsLR z{xEtmW@K=qu_;_KoVnPiDeC>p!rdpf^mYXR1dymG$z;;$xsyGT=D94b^IkywCF7%T zUyap?@QL7XVru#Q#7>Kb{LB#cL>H&%q&#NC9^wP}J}G<2@eP!X@Tv>V0E|>~*Z|RI zpTZub^jLjLU!qNrxsrYlI>q%SQ}Esc9T7_9O*CWQ68O3!X|=QPiVvC1X7)_AVA0;n zah-TIBFe`TrQA011uO5NDEmt2?~ZVaRm6d;)#R6zzc}fH;wI^2bk(}t-}`n(rp#}W zZqX=j2A4#^9W`AVEK+NLa@b+!&;jkYT_E;psUTkNw^ypRN?EnKttz~p{pzFyQ|V$; zIYR?}*{}~M0`048J(?nOe41KXK8W}wMfZXJQfwHiP5&vZX?R2CU2o?W!uLj#fiIep z^QQ_<-i#s6j7kQG+giB6tWfeW1Q6mrA=i@olPQVLL0g3&*?R%F4t^n%)Nlb{hT7uQ zdJq^HreA!29N5Qp1Z$hxGH0hGD@xNtXN74M@Atgkqsm&M_xqjOqj$~XwWXYQ-yAY` z{-d8AxYo(y`bxRDe{t31X?;e`S%iS|?1o}aY^5d9(r=LQH@~=$Z~umt2DPsSyEN9{ zsLVTJx+Ch=NQ>>IzPpin6)H?JCqNAC87y?poP5b34{wYq&+mGCadyw$-<5K=M8wvv zV%y*(V}idx$|7J25jZGkhF=~kEO&69_FD|QgB@C7LC$-*Jv7ga@xSVTLx8}dp~_l* znbYS!r8`QdIP!>oLUtf;c0C9~UZh8dq2`Lwv*6D&G zT;8+sDsOQ~UXR!YWeSlRI=x|kpH<_=*1;pDP)GSO+byYUSJ!7QYV^`Po}%6Am5{S03W-% z@HA!_mz>lmRKfa9%{Rav|65FTEFS5TDO&7&bYO`dnAltH{(y{6wi?mTF|irCPl~j! z-#-B1Yo`KRbrU^K^4MyUzQ;Z7V(vtmIP#Hm0zlO@C6`nMyb z9OjT^xW8VGiQh(ljUcvmx7jj~C}Z1=tP4Q=#}nnT?MK3j57r=RzY65>!>MO#5G3&5?46BU2Vc0cu3Y+#qPm^oCbhg(Gs_-Q1q$r_OKd z$DT?3%mioFE23cX)k_xx`|wY7CZGZ2lT4iEE7&vjq;Z9HACSRf=u=_9O`L~%D*G=} zX`+?&M1bX6z3E-pp88`@c(oPEtjN%=_H776e=U+%^M>VzNQ#S*^IxR(rAVrtHWqCH zxN9MxBg@7U?NMkla!4G{b+_QkGEOQR%;T4(KqR**kc3SLtDUKAq_QP+5A&NN^3{>R zKD60%i!SzU0$ib7n-YcMkX+H?I@|j`mx=mxh^QxvLb*v!g!G=?$D~}0WTXSypp7&5 zo!t(qCf217@Dj)956=WTv(lK7e&N#xupz$&otB9CFpgx}^3q&oX`$L{^#jq#)iECd zj7~zWe-LtUj$*nHk&_bV=0mUZseO@LYs546@hvKWD%RHOruZBb!dbd&_$A9r#B!V z<}u{?Bb$h3<-k*hL8m85mP+Y%^Iya~m}LbP{mA_(9jy_UJ~xab2T2Obg!7^wwi^F) z^~TiERE3C7os*iavU;y?#I$H7`A-yOI}&dhFYlxD7=LMz1^IooA%)M-#%!2d+~;O+ z>C@%?LkG;;p%Y9N{fHrO{jm6os;e5)8$wkCS#CGv%yh(QC;oaW`Y^|2B~iEjIP=IT zaBR0|4`@kj8G*gv6D8UZ*xQp^%$Fd^tZ1WHX`%5zwI5aX|BOZ>7nd^){U&ub$Kmc# z6HPwbmI7OI*zy)PY$0Hi`{D4JrC99n<^qQg<8?L)Q(6n4%RVfi}5c1 zHt6J2@i46~$UC~=UXA2ms$M*<&}KPGhCJjTM*5lEc5?!x3d!QB!1gD0hXoo}d~|GmksLOq?mJ`UBMK=L{1 ze*TALFxF_<-A{wSsP+`7;j?$x%$Rp`G0h41~Kjoi>P9 z$gAF7+gaBaqc%fcJ$vr|iB1%_%2!TA*Y^HAjTqQ|a3(pjT4>y!LnKDDDykG%FZ=JQ zcu)Xq*-~BsDKvq@bb9fUsdCe0mZGgWZPAXm{@mw!TfSG~(zdQOWdp8*_DUDwAh38& z0Ur6(q$oK9OD#e-BEHINS0RGow`d4A@O&QM|z>v)yiRY2nvT1b322{b@xKI813z4QyyXv60= z;(yur#BY6iGr&+84P6^`M%mxk&xlT1r4GN-h}Xw(CP2*!ln64Nk(^JaSZ^dKNS>>z zl$zUZQ+&ATBw8w|H=Dv_S!S$;I?_84g=p<8MsB&q_XY5Lu&Hy+vt6!z-*D-y)Fay? z{z6Nz0s^EaG=^AR1QjG)0OA}w7a8#mu@FXrdpV3q%+Kr({EM*r;cI_2*I*zOenN~D86DFkaRG zqQ{lphu(iocfs7lV>Zyay_PPSEJa%F=IJ2KVGDAja@Z$OQ56^*#__v@&3xSXd=m7S zBKaMMi7eN>zfTkQhe=}KXtG2o4s-PO%owLO6Zmgv==l^Kelgd@uk{d8>i)`p{wDNm z^a88&c9tkbNO)@erK37uvh!rQ9?9H`NMu8{jv0FGK)uwu?_1D$zD>%*%T#I509{gT1UoEoS6L~g9!={y?j@{^$v^2Ct8!neJ+jSCX@~S`77)pAGa_E%(aeB#f9G5Q{*<7f@G!SDvfn&wZrgN|aUcQN&CN)gXL6Zm?! zshH!p$<$~P`Z3`y|9z4{MO(g;3+AvUOC^1jm8;=no+3_+TkU}`I@ODmaH@3auL%!H zwKbS<89y`e#_i0`-#55V~c~rtJ{URdGO7lx(=%rQRz5& z%oSAskvKTFS%un@At_|jj+7J$hA40^DBJ26FSiXlwrq7Qvrp1{2Ug-Nb1ib8ARBBP z4NQQqh;8nc#CIg)Rj6D>_wT!xdlD&4^CqL~`6OcSZJZUpJtCt(xeAA&jybh%A3rFZ zjXA<>jjj88P2a8=HE7JlP2#XVj`=mm;$2cJMkWyfmuJT4C|oJ3 zv3ex+yD3FPq+V;G%|V+^W!t`Vku>Nx(KCKhqN=Cl;S;jZitzb)aPQdM<-;UG#ahK) zQp!AiaoX_?H2V|Ds5gqAG)j=@>=LL`SpGA}6kB0yw{F2w-zr}(`{5JCa~eCq0~Rk4 zN<+Wik0gr~0oqUrKDur&GftatTk{B}_#)j@CUezr{ zOPzPYoiieA26o$>Q$H3mxypW%)UG-jXE~#jO#aF0Y0A}9mPXIZ~LYq6H#K%@f z57i%aPpjs~Wqw83pymhMh_=ct1e)3p#IToDYkQ41-x*345||L(%n9K7>4aU-Rnn2c z=qkQ9=3raKqH#h#BQ$n7Cn7f0J?-9%;Hvr?g$*!M6!qrfHNwJl7lIdRHF0KMF;JAO zI2JL-Ya4`Y*oXF*>j-P!&h-%43r^Lr1W`ljM?tjiHuxE=Z-e;yS9|fVc>UD!`TT?AkOy9zzJ24WPJ=Zh5 z_9LcJJ8KsjMz_LXU2r#IJ?TDe0@jE1%n`qWaEZVss~I-d*5k{gXQicC@7($`7(>N2 zGCitCtMoL&&(5s}{kj)N7azDl1JyV$Br>yDZ4`La?}wb{1ZDU4Eca|ThXf?8(s5Pw z$$p|J$n!gw9PY-JW8KGt^}-kXq?^2)BUGZU)ibkO+c)`SXZBmJ!>i`ztykm57Ixg; z328Ow&n!`-L}g|!`S69hVYOqofnH0k=01U~8rIv=S$8xf;v{sgQQUkr5pg;OeTZNA zj1!>T1hI|>y(+VNkz+@E_kv$Lm*|?Vs3G{cYP&eDN6T-*40i2uN}?U60b`8!sraR0ga<<_&^hI!90jqL)eMQM&`Omb0XzGkXkczuWIh|xtDnZ|y|MhmTMZ!A;709q z$`YoL!rhpzurDCyKb$1U3=$n3ew0L}3=LUYEW+jH^Qb`>czv$-m0TZ;@VcpSr{4M+ zH~Vj@&{c1(DUkFk3u7uR#BX-C zdsOR;iJ6DoM`p9W1PJ>cKfD=`VM~mg1dZaqZ7iJm5!&`uQwpL6aWagNg@!%{GvKK{ zqB1dUGRiNTUS%z+nPh-ZpT@ZKCRRxuX0=|b^uq3J)xRe?SatD1# zgo_f9jJW>e#|PWRZh!Wl;y=;^vYS-^F(FT1O*-E1)C+Vm^zwIKXp`O>ZyN*+j9@#R zf2hOb8 z(>u-ukLR*E@TJ;Pjla#QtM9b56Mgwr@JI3T%#5TX;(G#J>_I+k_;Ljro{UX(d-1yz zm}iY?=g^5OVpgPAC^ha(4%}it%#My?AaP=dNO)+`!|E&p+b&Ki>9TK*_Q3t$;26O@ zRtpgUi7%K3y?mR(zuIfFUmdhZPh9{g$%2o(um&m?&-{pQ!Qw9acfu$bS`*pPFMris z`7K;xCYK}seJD1=03PNvSUc(A1w5LDe4M~k+Iu?t0SEPl4a5B5b>U?Qi~6-NneAyhTcky^|wB<8cKG}3h>_W{Sa^{bPH#@wEY0`+MUL>)?HbN&^m zf)Dw{Y15NX77F>r*_3~9!jJT0>KF(KV3lb78F0&OZ{fF7MpE8ND;V3b)e0eK&FSDX z&a@`$5LegV*=WkdZgI#+tl9R9qz0yxY8+kn)t;wAD751Tma6jcyFF17guhMOLlxzu z3KQMcre<^6#YKfVrW!A3neZPsD;fsq$FfIK(CSgv4zLB zsMe^rk{4R1bO8a+ty$kwZf7HRk#7+AN?I$Om?G^x>G)p-x?-;>r}C^vUGy*gu18VXyCjWsj`{c6t8(ay3nM zCa|`cWY!r4&90H%ia!2*zZ~n=p&_oK*=iTOZ-YqN+kFLO`9j(oSW1lndL`>k?RJA? zZC5SafkbN`Uf9kI`@mOEs^i2$40A`t2WXfZzNo{Isqz`&C|co>ViC?D!ZLrmU_7Fd z^9ibWuF6Iy^$+MDZEB8TDwM+TjOkCC`6Cre}WdJsE@4mEM4zcVFjJeZ*|4)Ayft9)Z}!nm8&xJIX5skJ1i2T2aIj+HYkZ zQT-@rnTFrpckwa5qT7@6iGKzKvZC<6nFn5_s4PPvSau|# z{nde&k#W5u)e$6mCE~%OF%~6m7-kCEv@?@P04lRwzh$}r=y`MynkL`Ul>@6 z8=kazsURwC8~Wr{hhw6IHP1_1xJ%Y-GK+&7?!5qe)sK+{H+h;2Ip!psQ_k6+Q015S z>u!#ad;AwEvz};UBr5N4-M_YVB&qs1HKlM|-3B=g|8#1R7JiJG}WLmo8v2?G? z2!GodF;gtaAA#NNIo-wyDYJQ>{C|3JKYd`gnuNPKg{BDT_H$q)=?lb?B}uhdIv&Ae&nCVofKAVJp zP?1}R%kmi6^VpP_jbEuH$PwR7f%r&{ztef!Y1}KYx+dG^+jlV&GSV(M!K|m%T69mO zshrq5FY{X8s~)|9@;TvOkg>a6A%)kfM5l3tBvf$@^r%ANjZ8vKa;)65PyI0u)-)rj zM7Ip^C7!WEg>VSbLGToMlpfkMZZcOsAkw>P`M%s*SWx2Fv+{>=kH5Yr*d>)`ox$f? z@~L5H&E#Fo-rX!IB|TcMAIB6NPE^t3BbJjUgWvD}jI=kLvY1o|f!NRGY}SnR{>467J=&HT`QEeOy37y)Dv?}T-H<9(*_ zjZ_-x)6Hh@VPi_0HS=DMS^93~`)tgUn^_(E(e|8O0<%Tt1c9zbvIG=N8mJ;^o3@P$ zRZol?#$5f}sPi$&tH$|hr4mcp3}GU}4db@8?6(AalP$pB0wL&I6TF*FjK|^LNV7Qk zx_a5G+^ouJW^%Gg4ZffJY_R(`L3d=ou4KzPY&p68sXuyeQK;zjvak9B&%2_nzw@?s zdxi30x7(YRn-7i)RYEO0!OV5TD9274w@z4W;SF4M9loV>YK%~ z;^lQhQU#m9?|GI%cL6rg?ZS9|W`acMIF8;SG~Tj4)iS5|aScZZ$iwMYosY1LuosO! zTR}jEqr1bI6eo-a9wnXZDKQsnv#PD?TYuUEd2?sKVF$-=xX>K7kU#(QI3yPK)PPMF z@0x&7hv8C_5wvlm`tDu|#y*Uuz4~_Eo9)+wj5yEq@xjRm(mQ>hlJ#t!C!~Zjt@Q37*@gJ@ ze;TX?w4h((pY*z2CR1P=e(^bB+&a&lFu@5Jbo?d0&YYVK{I>~wh`3VQL1|!m^v>h% zsFA<#(qcsy*1!O0Ut(kZac9@capqW38O_)KF5vX6 zianTpV-_db+g=UNs>KtTUxOLRLh~f;j#{mCER*7}ehk*$s?lUj7x?M7$gMEXyw!IP zC~G=axu76f)1++4Wgl%w34Hr9d&IicR-wmtM~*$aL#}kbXwWB2$@M^QXKLq2-`R^4 zfsrmLf&o7IZ_AQ5sx>TTsWIan{jloqW#`M~asl`Snhx1Y zUFyNn_LwGJ0LHtJ%>?bG)D$2jz#GU-n6t;j^K7hY(|aE`#As&Iq<;KpmI>u+G`R-Y zePyrRks3!9AZc8_0zDE0qapr%%qf6$}0>`Q;Px*Wg8!Gf$$ECg>Opu0=C4+_sJ27bmV>s^0?@i%RDwK*dqD;t8|SE z0B{U(GEP1^D0Xat4Z}t8uSn5`%e;;2Q}55uB22W8JdDt1;$874Z&ZM%V+G-3*V^-? z9-mxZa&SXT<*ibs44#X(rk+l$u3h#RlQWZWd3WNCt}J8W{vfyk_ZYlMcYIhzJbPhX zHhb;g%pyh5YMZwF<;gl$5mfkw9EhvE{+bDw_|Hz{y6ySXVxvv+jUdk18$oAZpFbFi z7*QpgUEcI?ccfrh2v6ZN8haPH1V)wc!RR%PdQZ_~@7siR&IH?kMj9QI{RY{YvXMaT zW5rR0$&bThW?5wJ25e}r>lbj&?;rDMo}%%>XXQqk7l4Qpp2@9$qHgcA^ewg=k>6M8 z+~mIijHQuJ&S52&K0(KIW0}~K8?3PMGqLzG+&_gI+GB6RVL!iScO*>{K#y{xdyhOm zUHh;lJ~&RM(orQ-os31uX->Rg4HG@X=FiE`oJ4`g3g-xL{@*Ze}y#Q2P0A7wE9EWN*l)mo_829T4 zzB&^@G#>3J#}#W$q3~Z z9TifB1hb)=zp=Q+(E(6ey|KC$RX2Hhu)0o~5L|mRkv?BAK}ILv2OVLIwn<~Qx3pa^ zZ`ULU48>s#)x}|?w6k@A9f1-PlC=rRs+?TOf2sE9wNEh-ZY2`13IQX0G3@Zw#*0|K z*Uv*&e~<%3?*_~ej_G+X0ImW~(=r`{5Vq3i9f~7ykIobB{x()Q&#DO~#O~IFb4Nb(3pon-nnV_)NUD*T~gK L^M(n_#q9qArMBU- literal 0 HcmV?d00001 -- GitLab