From 32aad039e9a128a7297faf17310184a8d19909ca Mon Sep 17 00:00:00 2001 From: KUANG Fangjun Date: Fri, 22 Dec 2017 18:43:11 +0100 Subject: [PATCH 1/2] Improve the HDF module. - Some test cases are added - Two tutorials are written to show how to use it. --- modules/fuzzy/samples/fuzzy_filtering.cpp | 4 +- modules/hdf/doc/pics/create_groups.png | Bin 0 -> 19502 bytes .../doc/pics/root_group_single_channel.png | Bin 0 -> 28547 bytes modules/hdf/doc/pics/single_channel.png | Bin 0 -> 28666 bytes modules/hdf/doc/pics/two_channels.png | Bin 0 -> 31100 bytes modules/hdf/include/opencv2/hdf.hpp | 3 + modules/hdf/include/opencv2/hdf/hdf5.hpp | 215 +++++++++--------- modules/hdf/samples/create_groups.cpp | 60 +++++ .../samples/create_read_write_datasets.cpp | 135 +++++++++++ modules/hdf/src/hdf5.cpp | 137 +++++------ modules/hdf/test/test_hdf5.cpp | 212 +++++++++++++++++ modules/hdf/test/test_main.cpp | 7 + modules/hdf/test/test_precomp.hpp | 20 ++ .../how_to_create_groups.markdown | 85 +++++++ .../create_read_write_dataset.markdown | 73 ++++++ .../tutorials/table_of_content_hdf.markdown | 24 ++ 16 files changed, 801 insertions(+), 174 deletions(-) create mode 100644 modules/hdf/doc/pics/create_groups.png create mode 100644 modules/hdf/doc/pics/root_group_single_channel.png create mode 100644 modules/hdf/doc/pics/single_channel.png create mode 100644 modules/hdf/doc/pics/two_channels.png create mode 100644 modules/hdf/samples/create_groups.cpp create mode 100644 modules/hdf/samples/create_read_write_datasets.cpp create mode 100644 modules/hdf/test/test_hdf5.cpp create mode 100644 modules/hdf/test/test_main.cpp create mode 100644 modules/hdf/test/test_precomp.hpp create mode 100644 modules/hdf/tutorials/create_groups/how_to_create_groups.markdown create mode 100644 modules/hdf/tutorials/create_read_write_dataset/create_read_write_dataset.markdown create mode 100644 modules/hdf/tutorials/table_of_content_hdf.markdown diff --git a/modules/fuzzy/samples/fuzzy_filtering.cpp b/modules/fuzzy/samples/fuzzy_filtering.cpp index c7821e0c0..6ab552019 100644 --- a/modules/fuzzy/samples/fuzzy_filtering.cpp +++ b/modules/fuzzy/samples/fuzzy_filtering.cpp @@ -11,7 +11,7 @@ * using "kernel2" with radius 100. * * Both kernels are created from linear function, using - * linear interpolation (parametr ft:LINEAR). + * linear interpolation (parameter ft:LINEAR). */ #include "opencv2/core.hpp" @@ -26,7 +26,7 @@ int main(void) // Input image Mat I = imread("input.png"); - // Kernel cretion + // Kernel creation Mat kernel1, kernel2; ft::createKernel(ft::LINEAR, 3, kernel1, 3); diff --git a/modules/hdf/doc/pics/create_groups.png b/modules/hdf/doc/pics/create_groups.png new file mode 100644 index 0000000000000000000000000000000000000000..f49157b00c85b1e4de9f206a151786e07976e33b GIT binary patch literal 19502 zcmeFZWmH^2(=Lh!g1bY2;1=8=Sa5fD861MUTae%|$lxx)gF|ozA0&7P?(TLc@B7_z z{@nZf{5i8`)=cm2-CfZzxydrge0vJCorlJ_t$Fz9l!QtB`;u;kE>6*3a^$^xnI zBJ>O4tAv~;GW6w-Y#9wL6T3_6x@$OFyL*|rS;5#iIXhahezkD3vU2)r>+F68-y;eG z^8rRqN?g-B=XlM_8*iSZcXPu*;hls`C9*FzmUQu5g6`*%=zIp`!Ve5efY~%A`4sB% z{St<9%>yMCWNO__xLKhCk4B-dZ?X-RDH99V{%2RmDdRa=TsAf~-6DI(|_q)2JT`-N6=!SW>$g649fs2HFrTS7I2c>FMbT-|G-Mw0*p40(`GW7j7+D zLEez?a48l7R3K6;+?O!eHyB*;kdS@%Q;*Mk^PS;XOZ}CV`5K>Wfsv&ZY&y~3R6a6s zMq=Eh76H;4G+A+EgXLs8^>!RI$=*#FS?y)nhvT7Bf9l<^GEPe3(5&*rOk>t5)txCM zQbI}|C!nUL5ePaZvzg2(k;T}f4IdG$w3CK#r`D=?PP+@{LL!PR{vxaK&~aCZ&wqf| zPbQw@4v{aDqJkAiR5L4a_J#0L&B;_w|W?q@^t=9aSiUm~oTD#Kf4r zy@u`M@yJ|`oh54qQ9Bt@FBBH}W_?bM!!ea!FREmc&!EqFmm7XB70MzvZtp`Wz998O zpL0sh`GfY^@R6Sijx*=xne*6pon)<|fTx!7O8lyMppUk%h+uBs$%>a5=V1d6yG}Kb za#M#L+kT!wZB9xy$=o)$^Z=#DHO00uq8CPNER`iz`fBTT;%I;#c*d=ZkC-z0c?Jfa zs;^HMpH{M)m>A`HF!sk)V%#Eb_5{3sKPF$1vStG994>GWA?jiuErMObSsG| z@Y$Cd11-?0M@9tzl+fkIt6K_vRpBi%PAepmr9K{4707DUo=MjuJhdIyMHAnB$aGA*N@35=B zkwTScfc`a{U*cd9CI+<}1RI-NM#3G9KuNNUPqiUBueTSAg$TLF z4(c#Td~Bhhm`hrTXh>*ur*DW9US;|<{V4m0cK5!NSn3#grf?G@RmB-O&EOee-J<6`__o)bOI^;fy^wvuuXS1&Xuo8zSX~ zOK~a5xm;$G$5z`h%H}jA#e#@=Le55zR1v@`)CQTV;#>`<-eZQd2jB4dF#1{ z>H@k^54UgpKQ~dkA0Ief(^X00jE1W2B<4;5oevL)I1;3seiN2sFI}19(3KnN5!}*N zDU8G!Oh766jZYA^luG6usz~r_%E4@j7nL^A(d_B0RSoR5-LM+PMaW~*$*=)NaPF0-ly)#+aVtq?khqg|K~dU^ zl%*V{zC7jv`qsqc<*cz%vVLZd^w=xPyyzh+Z7!E<{?O$}%tMtv7Wt8dpvuTNBqZb` zt=&)<5H}CH_9l-9>J5B$%l^&4E>Kf z=GUuNs|Il=+KFR*OdM)4nev)t6JY17OG&EFXhARTD8bb8RXCv^Oo}{=ms>_F*O=@<|OLXBBw1Yp|kFiDX+BM-z&y=x%{Pn_2 z_?6slMRwJVIg?oZ}`Ww6s<5HRWUR7EE!I_D`+gwdTEw8aFL~Z}(_Ughr;iLpQYewH9YCRw* z^Lp+*Avj@FYoa)*thHLo(ybI-3;qj}aoccLiBtefEMGuSAp`QH4F*>ei)7vG)ucK- z5nMg#6rEaN;$B{f{!$^^VZvw7>E=y)!N7i#wQ`3MpD#BWld_Dr3y0Ya3fyie#V%&F z^*ozatp5%JHPV=Gn8i=`6q>N+fzQb=ty<6DfaN6jwXKS>75AGP#uR|MoU8ZSS8 zUyGFjvhKTkX=~s`{QQZ4x1$Ux_Q+nyB4A_UoNY07a0mr=!WhKUcg;N{T)IWAfCcs0 ztllas)7|a@FBRtxjGpB_ehg77OZGOSTw!Y|^BOcIX{wr}-{TSFMuz2nU4Evi1Vu_i ztbRAu`?E5^s^;Z!rB8D?n%7_AaWRB%2o7y#t6W-E$j`NP;4JvP^xk3 zo)NifQu_WbUHJo*RnkB?f1)UEk{Ezw(q7QFE1|KTfP%a7yBqB00YawVQg0@bR=GGD zEk(tzYtR&DcW0X4vqn*9LL-XywO|;lbC30Fbin zcZ|3E!hDs#e|A%@N!%%6Zv>~0DbpUsKR>H=ztAGReGV$*>?A~2nxS|8w(|VE#%`#h zfih?Q8nu)!nSs}bDMRDkV8KD*Fb~IyHyLolv#D}jsC~8DR9hYF3+xISw&2eEQM{sD z$nzax>|$(g(bkyEJRx8*9P_palLI?358+$VeWuGZn(9Y}@8%)CPGk@1jPJC~>h1RB zxho%#FR zX)N62GuJR}ovkWAQLP#G8j9eTu#lt0T5R3sSctvdEad3Mu*Y+vrZa*3X~K+x;2SOE zAJ~tYi@$#iR;4b|ECa)a4~Lqg{Yi3Szp^XZwpXh?Sphq8Cg^Ii$o66B4x}emRv=_P zM?ZL=iVdyHq~rsy{Wt_pZo6x8Ak~>Jc%SiE7M0y1mNh?S_8y7V2R z)4<};3H(#`gI8Fe3|VM0$=)pdOe{>hh~&NY>+%cXmm+!d_LnqMp8Pcb=wUk&*z=w{ z#!{C3)3iCCq_|XL zx}w%j5QZ)&XxF%vv!pu0l*P2(y!p3@jYWLC{Hu&w^bFc~m-VlyDFj>3i}8((L@t|2 z^)6o?JZabVwYh7qvZ>888~3cm6NA>SNpKoke1;-?b7W~Fj*keOLWMbzx()pow~ONq zvh|Lz8nHb3j{_iY&fEUqzV|1)`Y6|!e-9^>o-e$E?1(>{mN`EN%*Jvs>@0qK=TSWy zctK_)7~xgtUgVc8xQc5xP_7Zv_R7AfDFQ6bw?!!jMi@OgpW80ZO1(a!wsMnS1d;tk zn=enl7cvO^ChmOMpL6=6uQ~lLO|k9EyK~G^TOc^$TKZ;H&^z^;FZO5R(gHnJ z+rEPPqj4!?ktq|IK+}~4$~J%;f<`oV-FsLii0X0+VE1ypN~$r-Tn+_|6&t)3IWX6J z#*+UL)7f{y{BQq{57alXna^=qyCwPs5)tniLq@x~bmiKlKX7m)fiu{PG*Zb%!Ngiu z^t_k6)>=ij!I2z0@&-y=Sh(czOXh@yWZbd9!; zqL)Zy{pZkGNba-oVi6F{lr*XuaAY0s(UZ9&-b>^CMGq+lCjGoesNO|jv2&Y@RW5M< zj`6LAu?UN}%uFttY6KH5EL2W4EIRRLg7l19L1v8i;P?8^pQ~w!zRvMPiM_l7e!gN4 z5jf>(oCqO$Q6xf7z?^OJp#`QOW4xy>mPMvZK`EM%RaeKc zZpxd}<0JayBhtrs z$78#IX**p`9fc{DR-m{(QnOoH6p%Z%c$dl~v~FPivJO1bP- z$O)2k+MDhFoa^{b=qBwI5z)J!Dv*9)lAnF~H6e|$qnOoC8$pHYlBrB342Dz5{`Zf9 z{vA!85EJcAr45;E$PXVI!`SyU23#7gkD}=^Sz?Z@=deW|i(v3_$?o z7-OGD<$Sp!J?_Pye#7}CGecqz;vt`^6A`vMez5``+220+0Z{!q|MH1P*e7cr`}hi^ zpy8rhnn!=c=`c!nd$KVeN@0_=?vcSN;(B2b*YUu8#dl&e47}hLotdqS6(iytA5B26&+8RvwbQi?9^K8$(@Lv1NTQ29zo#FmX-aF2npslKX_U4orVZkRd*-*IpD>Nhri?KnG-;I z`9$jLC}iTXQ35ICGIm= zB6|>Hd#7B+Ae}|woixL7^uT3_q~Y(|lK?*L6t0DwTvo5fgq*pX2V2wrog;J)ykLEN z`@{?$0|s^r4NEP*orq<0eMSY{m5m5BbdrDCp5W}{00_OU#tjz&W4iB2vp3FmH1f0v zQ}=eu`47=#vpDiGT#KC4e9UO;uKbHyLl|)}T{tFVc}}UHhEy6Af*Q+002X&{R12{@ zw{U(Nd65Nsq%GHDT-W0iuVUV1c5cMrC0FHnL29M2$1EC4JGP#)i()q98kR@LU@U*`;WYiF~1K{O&c`kKQt8 zrkZCsriuV!XIZBf+o6l_a*Iq8It#9$HDJO)nseqSFP1Q&L|VaOBv#+L$bhM(*%u^d z(_c|3#4}dZiwxkf+zqq-KSGJ!^M{@BJD>wJ+hq09Quyh3y~!01e`kFGrjDx1pYkf% zd+#t!_QE)(T>iLI7u@lh_5Sh-c=9)#b6*vpn|XN?2;jf zc@~fP_ANz4W1Dya$g}mQFRaZ7Eh(O}=78IIT^N#vLB{{xXveh!?O;bUt3ko|_eP%E z-ql6vv3eEb@6^mpqK9zlyqQ92)Z4QS(8?gzl5A)K7PZW%_y2Y&mRL|q~l50%WLQMb^P#-MoPQB1MJ)z44cK8;h4^5ByVeLTk9;w z$;k=6VzWJyiLU-$;Db*=k*6R%x^`lt!_}DIMM>pP8z9K|emPLL)yXKqfu25l2--V1 z=_LAi=j8(v^%VGY*GQQ+UZXW_g~xSMsN$fQq5O%8US&4tx3>68&U;)GLB`A3K;2mv zl+8EPJ@2QjYU3`!)^90g+a*(S-t^!g9duUX)l*vGtAk#ZO>yM;ST%qjt$w=Q=r<#i z@e^~DB&&v|Qe6E`erSN@2|3u?Nxg=h(jvb6#kXGuFGeda_rcSi#aB@?pFpby{o$#X zsy%go48Gr<%V&IhmCd?#3>B1eu9S{QF4IRV%)+8F=edgF+ZOPt3@Inv0|fzFmWV@> zv<+B_?veU5Gtkr6;bwiy*gk#`TVjQiz36;vCsaY+^QCCUDNtQK8SEi)(m0ynxO2Pr z(Lckg$9U(9Y$PIgzf9*>?X*1-gAjUMoV_L>sBP#L%nrJ znTx%(TRel?5DwEEl^;f&BgB89LTLA00(SkD+@rJM*h}s!Enwt{$R@7ez)0jD5)5gAPVq$Qa zDR0Fr5?Ro*Y;S1ynv*Zfno2yAFGgKkjOV0Y`FnD7=nu_;_KPdW4=0v=J(*JJ9Vi2*X(r0o?F=jlp478$U^Ts*z z`lnd!jE)Aw=NnzT3K-zHUy9uUbpUV}I%(UOq7&|qJ!3{*+7n>%pKWGNC2cGV>`3{z zxj&=)V5X5j6igcXvcAPowj#Lv09O&=hB9Vp5{RILmm%bITRhITXc=hNT%0^?9ng|t zjNbm)Qb3o%BRlo@D1`C}jpI_g+Y<~g;Qfg(VwP6n%6o6P;^ng=TW-(5$S4h?TNg4Wc~JBeg-=97 z>N_XZ)WRH%$@;DVPlH}q{@XQ<%4fBR1#;%V* zK6utf* z*R9-N|~9;W;FXA97q1XO-c*oFKi?-PlrBSOba2l{{oG>9c6cI_-P%VdOTAkV* zN_sfNxzxz2(n=!^)uJcdyHZ^K9_WghUS^JdqI-BtFVR;&2C3RP*9}H|A#7-Z@YA0G zlAp@LY1r%qeW+Y4=UZ#Jd=5z6Hzoo;yPeqFWIjJIYk7_;KoMCvkCH~9tef@fZy>{Q z5rSYcp&0!pvCnNa)RXNz0F{*)qo2$UW=jyA3=l8AGC6$QFJ`cPDllkuYT<}Qoo#u) zyr)UDbThrX%~wRz*7Z`Io_$6yEZo^EVyl9OHg*6hehTfrZ~8Klr);2%XZ zR(2S^rw3jl!c44)q=RX(6UctI34jw`=BD%~Y4@Nt(r(i;N0!1V#|xW%!om*bR823f z+h({8kH0g%j`F|ybVk+h8A=FrxIWsBcTM;5nwOzq20 zFl9H5VXoLG?W&kY56^BkDd(zKMF#q#EafU0nx_Or#r|vga{Um{-Q{9(vC+}rPrOSE z!=lYIj7lSA{C$47yXe`sC4fPpHOZuU$zy!;^%IP)7V(+{p0ZxRvCvrECy+_Px}O^~ zm3M|0OrS*4@HVaTkDj1H1@lS;39T@c59b%%W@(BP}9>^D_Tm^ z#a;x3?p{n=1I=pT2(1O?J7=lW&*!GO&5@*?Yz1^3hi0R5*W11dUncT+Z_-Wk4kuc zqGI#Y3Vr+ZagCVs0MTy$G)IWC=auN|;>qS2GlzrR-QZE>_+-nQ!N$;Wo@?}`mHOnx z%9Xdf11dic!icxB;4-D4Y%QFCZ;2Au@cQ5DTMn#ypri3@dl^wcyFh<>(C|=Q`fSyR zgt0gKSbE;Q9^p`~Lmw%)XKSUI%a)L9=EMWmSX3_q_KNp%V!=6U{l;YXW;tTMvfgJ; z9ESHycw{&C-5OI*V^h;x2h(D4fkFkhC9#Ra+HmFFJB_Vl*|HTZAA`p!mYd&hLo*y1 zdVv*J>4st0T4a>;c@$wHGH0|+PwoUxzeh`*#cW344TyNJBVXFCoVg;|mVOPbMRBfK zTkl0To-FELGx! z^PvP@O;tQid9|lO#OXk!bZiY+-A>MQDxqaHH8oX6$q5SZsxhJILY2}_1vOO;^~wj9 z%tsc2=6Ke%T#WXnop<-vPq0x(9R{_ZKU@ys*JixmuAQ#r_C83F*uVyX1RjsOmjZ*? z$bmwJuh}Q>ast=0JU+g`YHISZD7Q2#@#7hTE@do|1M`x^I za3aq>$Z%~BuwW+;nN1Dp09j<6`iKVX_Bc$hjnV`dXL)@TV*LH+_%y@KEQf9LTcm{2 zk1eiXvTGFZ6kLfy`UP)3ztJf5K=Jy9ttN+kcrbC}FKN(4TrGH)m8tjUkM(Oo9 z_N=*EACkP z*3H2j%w##qogj+fDMi?Fo3n1qvq9j)6`UDLdTR5@R4z$Yj@N1G()_JLw<~39o*vk( z_I%bWM0sjd@yR{sWMrG0{IPHzz;`9G;6c%sR@2B(#J~+pvHmkV3U9Daa2lCE->%8^ zeAlQZTd&RGN)x|POS+DKqp@})`{j)dtkn(Q^~Lj#-h}*h)_f-EB^{up#rE6!_)e#Df1bk|^%IHGKKgsPq_@kfhCt7wj;rn*)+2|f^btKp z)SwtIdpp~B?b)Zjz2BMng2MD%E0jnu^P?!qqBZx|rBb)+F^?WXsWLsLl{fIBK~Iv( zMm)HNUBrpESvCeTi(3j`0y%OY;B8#>YNU$x=v{;4fmI${%sPjiiy*)7i-6_J0==zo z&UZhJUdLxO`M^A-lS-o5uPidJO8K($bnZdUl@Q11PSz^14#mW!@2@Pt&d3tVt}&Z3 zn&~yI#08O!k^;ivKhbhsnOv>xM{M*ss-`BcfIdl#DLI)P)uD02!kfUBs1-!K(kTih%uolMRV{d{ zA$D~AK>l*Yjk#EzPP;PVOxATbrBj#YS0}ZZ4t0T$z!3=sNP^}31O`3TdtkNbE%}a4 z<`lck*_st70Si%2`T8K|zU*AIeP!lV*2x(EuPn>^R%kQGo=%Z(8q0)Ok84wqhMk~* zF#*+OIJ9EN&<*OX3su1}yJZ1LPzW86_r_ug>~&Wn_qZ=bYYNC_ zDt5x_U@T~fMov!7lO07^;-i8=zv~TGyUX|qQ2EgZv5ii%&bVupEp())IC4v<12i5F zZA{400(g6z!O(#cb#uO4eW5Xoo5T67kt7-)CNUXtMrJU>C2yPOFrSkVS*AB5QO|ph zC8KnI=}^VZbp5R)y7ax(#~b;M!tXBp1GjEEUA{N`PfHBGO?I+H80=hkI{dAVj=n7b z*?tW6#y>$YWe0k9a!()?6Nh3F-)iePypDJbL=&#S`(s0TosXK#B%-sKGA23Wi><4HD}^vAqU0L_=z=jB zp#kf?%=*p*L0-}1jc^a0L6}JTc@`JxXAh@qpS7>=amt5cod^$}lz?eC>wjgltXh@_ zl4xX(?Fbg$M8|qHYJ6<1)TYZC!X3IvmhEvKJm2RQ-O~nmX3yh!eDlTDo*i z1<|O0{KK)CTdw+5gzop3xDB7niVzeS5np~uc>Nbq^S(|ozl zQ@qOI6UwRJ5>l=@VF=b8;KI^6wD z%7odMK|-$vdj`gW?@X-@PxV<`FZa(_dj}qGD&9tDgw^XPZVqn|ZkJ~1>p*dUJM;C! z`M9CA$i{p6Dj6%JpNmeF$bv?iLlM_>vD)Pdt!?0#7ysgagJsNlckVUO2yD6_j~S6H z;}7z(HxF&eZ6pXWu^oUb7KF^+*d3gHejlwssf>*ca6d-o7V^*hl4a$2XMvXYNEB9& zb1`Mpbjp(>-ZKg27TX|({hGeZa9P^aqb~+;}VBYk!Sg31kOJGN)>$lo_WpFMbwe_~35lBaW` z^J41L{++C%*L=QQS(XE(=1>P8>m_-c@-UyL#m6bKE8xlB-tk8#9Ck#T@)sgz;)dP7 zy&zEjDf`Cxk!c&I)^>M&WBT&Yuq&SFtZ`Y2{!`M0JEwdHWgis0ny2Uj&PQ8z(>+6o z!`xY(wcrv)I*L_>y@*2->eV0h3fN`?bn1*nH0ME)DPA_~0xVJ#A9SdnCfPzQ7hg1L zM_3W5#gd=~C2Q~nnSG>AcReMaCj8y@I`f9yH1;oe_5O?86>*uoK~^cww!1MtbqMw^ zRQ0WkJOpwo;SbPQ{6EeAVGI=Xyk()OVF^XS@ccP824(BL<6NChVS}g(KW@%93>A>KPv03_{Qer|-7ObW z%}}c|n=nk%>W=5&c9wRwYG)kK{lZ}4Bq;sH_;m08G;_?Rv5V7PI*bc4-RDkEGr+jK z>Q)*LD&h(Bd)hJ1a&gZ0Qge|7B{&-{xQlKCrUpcvp*bf#zp43pdH=TBRd}~xF&Z4D z*X~|n5JCLugU@(;6s2d*-A$lI^z*)saVredb;Z0U=wJTd4IHE zoNTGDrJKROcxX!`gC}<`xY6HVAwAI7>?LcdPXU$_LjY;dbM|ZwZv%x6_%mJuRk}i%$=LC-e3V|Lg=*ToHW}JT$l5gtN}q z8@nfG9kOo!9hUQFxH(y_+{fw8YYQ@VvbEIr;=U`_aVisvIco`H;=b47X#hSO>RlBP zQi%8EcuUL8%4AOX!@(YbmMXE44)*$*6{I;L=L|FD&lPdapHlr z&rf5sk5Ih^Cow0Bpa?7EzED&CPI2x&l)ry;y>{4$eq?R`V{EAW|JWadyY;Ls+2DheG5z9!_tZ&# zif?=Z`^U`aXXVqb%Um?aO(cM5jOJ|F|ClZGVm#EKJ3sJHoc+h_|LKt1@nY3=%bVw5 zjR9kW{mQTA)kptJ&VTyw4+JEkH47E-jhqs z(g#w%>l~r1uWTqmvlBs?=(QSybfCK^A-Wra&(1Kj5glH`K`)luLXQ>f2Pz`xy7HnA@uulPc<3- z;^WLqaK3~x-VQ^=vf;2zh|%y*0*b`LogbuMRd|Y5uA3F}gd)CRbNM)jEv2^{zzG}l zkB%|!8aa({;l6?A@Uvk*1w9DcJ9*|$Ro^HAdGchOCM}0D@*@)O;pakGoqFoEq$nlHKSdx4FF&XXbQq!v zt|O#W2NuVhw}ZZjoRz7msBYD{o<rbRzY z99E`<@D)R0e*EXBm08fIUiuzGDmAV8FQC1tsVJL~ttwhESFXNTo=wgmHXlmKGT4?T za|@)xY>wmrdNv%3mO}lln0A+GtF}B_8`J(@lB;p8=J7oPXQ;ac&Ov)NLI1|34-i}j z>p=H5E{n5vj+1!ncyBDGB4hktrlfHcOMX^q+dqy2%oKu5cd?Y&NrtaWHi_itcS9x> zOy9tsKAk>OR0~}q*~}MInhu;fGgFxkfY`UKxJ;bhmYc8le~P{i0H{(c7c!OmMZNT` zHKx+s4Md{S`fYHfVUz`mh&{d-(bH>x26*&^wI3lO*2#2Uehj=|45Wb1we>0URhF zbpBU^SaIR4@~`vnP#*KYSj#_x@(;+9pnQh#pSS%7R-d5&`CqX5{~ZP@f9`+C4G-CW zI{V*12aUS^%XXmj>Ys@JSCIeHD*|Pw@l+?_Qm$0~G<)CeTk=zuX~Z12gI8G+u^!TM zBu_bD8dAc~lc2vexRoGo9as|4zxuJD`{gTESQ2>Dmq%&;_TUk7uQNQsO^VRB&AdBP zM}Erdo@iD&AS?Z#8mL?n9uZ-+FN=YWPU~or_^%PTsTFb{a10qeiDrOsvO*AT1Vy5F z$f?3d*#6riagJ74I2LskF-$G}YWB(wbSxLEXB7^pXhMAc-OsE2ZF&?`Qn0PAFN{!? z495~sgz7I;67;{#t}lnHOi@^cFeEcQq-DQOe#He$!xHi2%Ov2-BCEMf7`F31yjGpt zc09~>;z(G28y`xZOwWguDrFy!@T$lguEc2LD(`x7G>nUF8O#8T%$*1&@nEuy;4=$+ z5cBnv&v%|cKX#&eev%P>-g#$mrJE?~USq%leqU^~-aocO`rGH2n-3SRT(^Uf9CV(oUT^02v7aNJc#$))3Hy_e4J z-MS%}+1izEMLLhuGTn1W66YAzXnJGq>F^jz90oLd4Af0`>?iH;I`d@y{o7wG^Qwm6 zN)?y_(V|$%BOb1gEYiK4@N5Zc497+fvUuiNaX5hQdB2tCWTE>y;pJd-2U{m{4KL|; z++5Og>47lsr}!tuxwY}2@xYz^i8job|L+nh;{A&UUAYD4xOQrk-s~}2-Rrhby2Q_Z zv+1m_sOtgGfnx}ys%Rmp-K~rwLxE-rUUUBPVrR3F2c5U>^vj-yeWX`+SXtH#yV;U9 zxI8u}0l-zI7#Mg#iw`AL3uQ;=TAmL^ubvqFd44$!Fz?Z?Ap*$)PFUZ@idyl=8?zwL_d2jZ^Nb%KM`w9GQGLBYWL{gBwUU}Esk?59?*1qk{ogyiA>P!La(&BV|q+Sc94M)914#!IZT>rKM!-)*-d#y`E-`~4`+8Me}iOK(?7DG+A0*SU_~ z$(axfA~&^ULzt|b7E-|)%^|21wO3cD{Q_bxC?<|;eF-%t^_G8pB70o;``w`WZN2Ap z^x*BzqTzcC{$87>H+;0#U3waeFKjgM!X6esy1 zGsv5eTJ*;Rs=8Jogu8?LB#3ySQ7jD;hSfo%PAj6Kr-Lmg`Ecia6*mCmlbX zOXqc5{%xm0{P>H7GNb?`^Y9;i+H1R8aNVCn|I0#>e!r5l%8Lsti@CoF}kl^;l z25mgYyEpyp6@`n#oiy2M=Tb91hAu(z+m&rDY~UxSJw57irb>Yvu9e}CjodQHTZ4vg zJ+(v0#d;hvCc6WX*s7kXtS^r(DDJs<6U;|8mzEE4u*35_>J`mnP0irz=JB5ZnNLcc zQ$=^Q;T>eW$Am3>^>%|RX*RXDOt+acm=;`ib(z{2ZtzXD#FGnYLieoc$7@}! z)$y-`&iMeQMMlZo1PeW(5i#&s+G_@zi))v13Zf>tShPvAN=3Fh^Vc+)VGrD&pSFx1 zCtfm;V_m(vZaW)m!&n2XbB|cEmNVdkqzl-MVg!{aY-TDSTds{8F=0R592hAoX{63~ zc&Z7a)>h3lnGiSiFrjW-2ryHTssGF98>#%v$N>3e#vkbr?)%mK!?;EfA z7UZ&jWHxP@c7%2r+==A^!+(p@f9tWgrFEtngX+)MMEPQOx|!16X7q`wGlg}c>Y39$ z;^v--4)aia&(qQ8`5}pA*@H&1yP_4fIDj^TC&@%d$#NP(4uI5?hS@m&t;FoNwA|ev z`NnNBmsQz^gx05Z&{;09CqoXtJV8#BK?FgA<%b0fGkf<3 zV2;!gqvsIGd+$O1N}_&&M`;t^35U`387ilYd2&$sc{<|+ z8U$>b3oEN7v#qiOJst&~#;5vxJ#nUD@OJt+*f3OWlqJl4kQ{*> zw+4x=jD<@rG=9S@xN#sl@pd5dI*oDLMpIpIQr7!Z4e1OY0XNX@7h@Qy30Vo=gv$s0 z6(DIhFJH^i?ltdy$%kP=2;og@9_*vVM06V>od0}8s%m4i^Kxsh*)1+rvYk<@p%wq8S7JWrmn{1Zow$*5XYCa*s`_2m0?dNIOX zk+tx2=d|nggnoqvwNt03?K(pVOG|esml`F?C?e= ztu2gF#;L62qYqa5vE5g#7sK=X)wT@2G~YRbpU?VF$@~vcA5&XY{BCY$<0p#Mhg~2!I!KFjWER*L%^=%m#LXJ|6qA z*Sm9FgU?>RuKS8`gOz-x8}BqYS-5O1a2$@MmVL)g*E(jr9zvxL-?0Q(hz1$*YHW*5 z>@PUy33Uu}dFY%6ev?x?pzsCttqIQ3?T9w4CKKL9U`<*oOamaL z=rwuEX`4v{h!5fSE~{Vk1Ddkd{JKavmU8#{{TgapFAr^s0R#zkBvapvgr*DZ#fItx zLwHD?e&8>$uhkh93+YiQTA|GzM$^IXv<&*{zjfTu z9d)I*xrwGLPGOgj zb9PR8b^bpAvj|N0_QtJD*LRkutFk)V7tUn1O;`JG!mt(GJ#-Q4zuUoA9}c4Czhg4w z?W<2zcsaumA1kwbJ0kmKYe#bN5eq0Wc@*coYxbyt)B?}`MJFyj2iyF`crVK;z-E6z{XT09PHm;8& zAFeSoYEla-1{p8}B@eq{UNhhA_p*7}5Zc^ZieQ5Q#aN8{p861udw1J}tFk)V7v6BS z|7=5qM49m`=6r|{M}wlJA0W*U8RDbh$kvDLDN0Qi3_nT7mGSI)DbVBfE2P}Pttdq@ zOzU*^&k%2!Vd~D17VZo~;p%oKUx(e${{5B@mu%wD?-#k9U`d(l-R^f))|MHjG9TW< z9j^ADyF-M44DlhdIp_fBJAcY!tzY8Mi>vMCyE9CMC!7sw;m$A=>4(`!&(*LSuFQRD z9>qWSoTb%D6XI_`r(C6N+dg*|P*qe-LzlK3j^?>hR%iRd<$iatO;`KRogqR}$q*kR z+pp76lu(8)F3GrYm+j`eGfagioDFH==`a+IxEglDl^cMc$sd0|J!;f>e)wpBUHXH- z!FXKGbBD8i;cCA-+NP`h=gtrzsbq)`ku%RSzi%F+{_0HHUKDqRsqloeAuY0E7z$Uz zZn!dSW-rFS_aZAVCK6D-CM~-!0iL&|kAJBiuRYz4$!5)V@;RF44rlwq)qZ!hO;`KR zogqSeCPRD_7zJu*3n4__AwydTA@T$XIzosXB|%3Bk)tH&2qAKm1RWtnj*_4wgve16 zbc7H&N`j6MB1cKk5kllB2|7ZE9F^hTD^*pQz5L7ED2ouH5U>k6Bl>qEG&GcHBL`%* zybwb8#4hNZ3O7J_bZ(VJ2vG<~pNHk zDFeW)!F2$bz5Gi?^zW8yh03c&E;vqusv)*C)wj-e`b85sTMZTqX3W_!9oI*$O#3mrn4RBQaop#tdp7I?<~cf=26O;rgCKX z`CKfK5P3(ooSm8$!M=~C^T~I|xkg#)bnMHRS6Wic_EgDmcF&*0^6h^S9e6KIpB=@X z#r@d1bEnI^+qG-Qsu52zcl9oQzhb7?eXZ#|aX6hzD9JS(7bLUQ%WyubM zpZG5`&JAGP-6=C}PhiDE^U;0(xNR9tOXSr0>HNRFvyZ8&31H6jlh!`?)#kQW9Q!Uoaa67dEay7t4^-;df`7emiWZQ zJg(8m6^HG!tf2*f{#)N+sxHTL`XaZjdY+mYPSl1Nrf6-)^$y=wWzg92BR%7vB_J%# zF0Rq^UG*stC`FY@r>`Q4oNJ+E*EP{pmrZDQ4p9|-gYwE~a_>e^aOfCc)NEqj`FKV4 z<1fr2srV30#}1Ld^b)&PngHaaWi%S)ZAY=Ffi5PK8=DSdkt8I^$~WsfiApxu?PG0P zPSK_L#O`XMq;e0T-sNPJ{rwz}?SwCDXpy%8QgT}Hej}UBpB$&8x|-&}bHtI-#7RZs6A~)EG*c z|3GE%S@mt=)(11u%N^}_U!ITN4W^yL8@xwh!CGFP>Wki8ht|Uf|IpoVzEGAoG@6NB zAB3M?hns#fvr|$P>AT`nAW(`b-K%!M{w?1@z(>Fa0NQ{gJ9!}Rq0{!K-PbkJXuQyB zM}AV+Q7mdPVOuaiy?u;Mx0B2$-8Mvi3TTTAp%w{AlE7-k(a#}W{+#;SqkMn9i{H!x z_gV*a&H~HrG!_E+PV2kkQy@@^D*v89pcPmox3mItE4pnidB-(-Y4I`t`P66I?~rYfx*KajrJFkR}8*~d?s)P zcvWsO*_m{t?}|@>Kq+?Js|^VeW3qr(a=4DXtTwuOELeKFXx*BJZdvl6JVM8|9UWZl zv0&-BN=I()KlbTrEY9HPd4Ys%6R%7eCVPy4M&rh74XuinYFkA7_2(nR~}|nWd?00Va~)0S0?c4?eyzQz(;#iOLFB@hS(8Fk29&BT-8 zb%LGmg%KK+Mx)pJe7fepGa@1qk3ir$4$pM;5|2NV=J_)TfL?0%9ah1Ide{<=Kp;?t zQ{{zNO>lU`eF=d;Isdx}p8|p4;YK4#($IAn1OkCTCH?{!f1++0HScu*0000XY8_3VrwOss9JjA-o*?2L@8?M-bQKv3O$5D;G=#D4xzbjdhb zcXq)TeCxTm)0UtJ*!c1DnPf@U4U|Kf^EC|V`8ykG40-1_LWB?ugztVOl$2kD%M@ev zg%nW_SxIz)VsZfyTzpiBU#2(*i=mBn|p-l1AZr zA+W;J)UYH0sDc=z{=L6Op#-}>$7A05DO8SP&E2E>IWRFo&qb2R8RzX^ssU+W~K*xzNRdMI`=1`10udy~``Nt)Ibglmbom6&iTVKevj?*jS{54i^gji)Ifysxnd% z1QW^_i_~b#L~*8QjAaS^h%%7}O}AC+5<~fkNW|nqA`tIVl8X+_rt@%PV?V3XU?~Q7 zfEPm~KenKNw8?7KS6c3htU7NpFUam@+bph<%bE&5YLK!w4OKAYIKHfmQj7SSsHdQ? z2wku7+;~hQw5HI1;)qkU)7Q`ww%VFgylmvBX$h5vHdeaG)2)@95URsvM8nEm28eV2TFigQ%xI7V1s4aYhMrsH*Tg-Tw z71q&qr4uo?5ucS6Rptg5GjGgWJT_Zv;p2a-#k;{MmU=y=%EdRL!#i8f9Uozt9npuntdQ}BocJS)WvLnNSjG2Z`O3M7wuSHumAuqZJ?K6}TwYEkK|wQZwu~1svs+h+ z(o!?UD$UdwRCMF|Dna&Jo@QwB{|i=bA9Itf2$-oenwYy0~IMEZShcSX01f?+c88loC~@FbrD1dwU8o(OkDuSsIehw~u5+ z!8wYjJpr#$p`$C#5eY4=Br+%{M#^t@EtV1#Qpp#bVvPZM$fbiRw$Ta-FsgCCW`yOd zr12l6BlISFK4q|sB>o0ZrpFa7*sIdb3PSV|q z?2f~6)?jH4FhLepqh+Bt+8!Q*3kVEay85ouMRRy)CVi#oAts|jrIX78oXoyURRK=k znXJCHgBEnh2yX3?lle8HrH9UAK|{A|VX=tMk2L1L%*>nJGd{?yNnJ~29=@IHiBxff zif{rQRk8;81d0zBMK&gV8&=Iw)%}WSzt`-CD+J($<)=il*r>z%l(#q8YJUiCL|o50 z5zrEXZ1aa%`aRf}Xvq8K{5H$8TuP_(%`v6=c_J!N;=vAtD__CLg3}t3Y$4c(67PCC z8(%QUVz_?^m%z`Iom@mjN_CATLW?g$OXiDY5L8FS23Jq@EhLmdEtI<{NcgxSfvY9x zrd#_*W|)hndgD)+iK#+3Q{uF5E@qmt1M#a2v-i9 zmxKXgcWI#sag6ptF1>}f95=#IsJ8FUvdYUc;twl|@;(Xt%bw!G*o0@l*TylT+3XI+ z0906csM=*{VptmcB)@hw^N@rY4XdhiO23n&b~-f$P?Ih>98{Pry^Sdm(dlHsH;(0= z@?##9g5l2=>Jqod6fa{cb7%_ zwes?5wW=gQB%LX`9?A0;rODlkw0yeA&xOPOwdGUJ)PiEe#BGR!e!*SiF!1(zj>GL) z;#R4X!wCFXpJ*!OsD9Y$ZA|$%mCOIQxpP9a8VRrjS9YylC<9X|@?QCI;r>m^Yh;NVEAou9TY!hpS zMt82XAbV}JP2tqF&W;pmWxY@I&CJ0R^^@uI#k%zzDpGmTm~um{K4BSzZ0v9m?O(>> zfo)7GYuS)?j~LJ7wy8n~%(ISC_@=a6U8zq(YG+-IJlJP62umDoI~-diy22w((Nsyl z<*6oS(I01PAyuQi1_uVaQE@Rv%ABl-zeO5vzp9P2d@{Q}TZvg6Rn9j~Wtm}85tI1^ zcaQ<5TrIX{cpL7z*KUNv@s(>viX>OY&Tj?1C(O_yEF~heag=J+cnY^fCJw^*?7_yu zI9yT1%TvdQw;eZbgm7@7KSi|hgF?b1Z`8zu<$j5a|EQjIpFlWwp>kZXb9VhSYE~77 zCbm-xI$wD+Dm2bsD$mtx!jn~qYps-Pj39~%P{@#Q)}D1lmXr*LnhG(U(pb7!mvifK zpw_!{%303-e)G*|E?ezJ0WEhcm(*|-8%b)~Bw-Nq*6R%&n}Dmhp`e=)-{F8pW%=ex zsm+0AQmaxuXRs%fzMK54H*3ypBV$1cQ}#`7ELJfB8eKWrZpLb>KrRcLqEqk+Eym$o zVW^`K$ul8T@wz}QvVeT>v%Zi1=PeABMSI(~@8s5RAIqhLRO)c^H$9S8BY!_8U(G;$ z3qb9o=L?RBfQI59QfsNNfKL4(Q1}OSh_#|mO3b8&oZ`2|PsSnEj?g+=is|uGdtF3$ z?roNcB8v|Z)4G5A9c(K6{Kwa$*gVH+DZ{FxOK1N(+ffX$~6+DQV zo#Up3@`Vl;!vN=|jUO-;$#mvu=XH!#CD6o|vi%u8=v@ESLs5!s97Ax%m-R%tE0O$j zp%>HmUF@p<4L_5~9qJ5N+b#WkP=R z)mo%Ne0B)B^FbLHPL(Iyv#R;HRfwBEPEiGB0Y7mwN2eL$;&k&5k3CJbA!ocEQa;x= z+i5Gw>d(Tms!!!vTpmd9Qn|V@>r}iDdj7PXYrwEf^F?FDzgh|h{BHmJYaOu2PnLtL z72F5r=^-a9QvEq@xI#>UI7_KHQ=CeUqOBQ z68dV;lQ=|e@Uk~wQj68_l=-|s?+`Knv+s0C+?id+j3sn~yz^{p(`TQlG!#Z#>&0=! zMC#SeJ)wRB2QwU~gq`aw#)>Y>yqhUA9yRWusS0Y~)GxXrY)DY?jKvr8V;VVlvlND-Yi!1# z7tUUzOG;$rNBC^Wjx{;xn?Dob^|nTCUamp`d12W84lV&Kqx9?PuCX9u{BS;iY z$Y0mIpKHCbj~CAN<5&qL<(L9U-~BMHD7V3Cb{tqAGc#cvrZID%!<3qic&MhKqwzxy zB5+3;-DX$>wHl=#L;pG@+1y_|@w*W;=Onsq;`r)GadENz{y45fSRB2pYrc6d>99_3 zh5655GaP0RL%A7r92z>KhE+h?9~Vgo5j1r4TH_6M{^NU`8?LW{tdd2)E%`1Thb<8( zShv0^BMC~u4}Sex!+CwX1WUnr_7zJiSDrCYQHZ0=n3RkaDV-CkJT109Pk|CWx8Iz& zN-jiZx~7Qi^JmDd&#?uip9{1*eUzrVZpYyAcOge4h>lu%MiV5{_mVSrE$$E;tLG*E!Q z=^s~IEJin&3aS52&>UF9K0jb>&oP9GF5>?L4PdNxs1N)T0-F~mtjCnffENi?n&ZaI zaBy&>6vevmjQbYaV6+UNd{$HvHd`+%)%k#Ecx|m($yUKeT}ErrA|!_aR;AGXME<=# z+rV!@@c(^&&4X`zIy1sY07SxC(@OTgLYt31AM0O(mZU!5%}w$%_S=VucikUGkPQ%g z7s~;ZmMlmZQf)f_B>eymsn@^fT>HLR2)LtQV)_|2TZa954)#;I z+|7RO1s7h2Ug29wMKLB|r{av}%119%qCz1lFTI6*^C&@u;Fu^vK=K`8vkvR54S{Dc z=P3&{dP2>|rRI42o(vh zQwPRwrloAqxo6_BS^i12F`2G*=T2iYpM-i#A7D>b40h8t>aW0d%`y6>zVSx0!}kU- z;@@6kC?<|>Fq7P2i=MKXgx+R4uC4L%R5tEXo?mAv+YITvz0;BJe(lRRtxn!KtK@qv z;NCamJ^%2{omZfIDAM%m7yo_*H}PB>^keF8&1Sv+py#^>)vV5HvPgBl9*KIk->k*i z#1FCm{#vcQJtloGlFm|+ZdTf!e`r5gpi1x)4oekrc&T4W8{Hj{c`HTq&RS#D!Px6HBl9zoL%8n{fMa=jwaGP$WM0 z%RLilqC6sfEh#-5e>yNZ_ca{Un^wwMrTfzS^}f`bCas8xf-UZ18a*T;6G`Bt{3$fe zJ$l#*8^v-`YEbF`F;>x>8GoGl)T}-^1(&&%Rp~1t&eyUT<0HyqD-%z1<2n4wQga#N z(1+OwJIDUBAs#MVpLqWn(`Van^4i=;0^6q4j=|7x+jKTJ7ZmGq7%?svG|4-&aG|9? zj+5<(Is|S3A;&<{DZoJEm3-hryr{wJpBJxxvrBIjA`)LDwe1;6%I#zJ^xgfS#0?u1B}iq3ZbH^%XHseVR_VLNk<&S=46iJIZGc0G$;xPTPUCnFsmHEsu-xXP_4@z-ij&|Jg71D-Ru( zAJlgrUEslP-}TY6B7f7u2$#c)*IQeBuq;iC$1w!7e?6|0H4jwYoteToJBe@20b~{+ zc1q5>D%oWKdnk}337Rt%sMsv2ikGJ%O=qH$Ar><}p8t(M|s>WuCU5u=eBid8a; zO!jlS^(=29xT1VFb=>j$=gK&%;@P{7cBt3o0PKtBW6Oq?X<_;d5Miq~eq>XCfF0Lm zKI^YV6fnDHFW@MO!TST`JI&wVCwyeNf0N!{T}+EnKA$oCI6g7YZd$pmSys}CWAz6o zZ8{=9R9^I4dig|%_4T$VMf*93v0ilM?lqQFWIk^&eoXfm{q{PhiyHUp_9L`2;jrtd z?c$4p+A{m@is2S_u)&Kxyl0x>La7c@L==kcpbce?Y15YeSq z87@ZF^eK}K{8ny$J~!Rx2k>fZE4x$g5EBBjsRmnix)>`wrb&4jz(|onNkv69!xSQz zuU2n-vQ(R&Q~sW2j=5Tbn(c74kH)v!?BcCH+Hoe-+*8TC6xZX}@Vx{+?-rcRtbPSE z2W&DbDlv5gInxHyX+NvgCQJv`_ZhK6>jAq3YTh5X(bPWjWC%HFu)#fF_xd^>?vYhFIo! zFEtPW8%!e<{_YJA55w?+j;*}ko9p|!iYl+Esrf;vPaGHy|;)-&@TH(*Y&-y{R8=x=dQt!zBFp&(D;{ zZoD#J3n0IJkSywRAi!eL6j~qL^w>l$7#{yp=FJLwrc03L{ zsYeSnAG3J$@p}W!dfdjk8@5B^`#1&3N_ZI<$;45y@sF=J9M!yRk+MAIa~VA9buRnR zW)fK97RmF6f07-^t}n!VTXmjkG-N-+ao|Q5k)%8@$13Uj{jal*oKFmAkcllzOM=q~ zJ2DVTQ%DBN=W$J6BVqeO$wKjjlBOGSqb}Q}p1JGshmBu_7uD8lni|g6RW|oZGAhKH zMe?W3S_kznTCH;IH81lj{P^Cjgb&Wq3RFbxqd3ltMTOmA zt92fswQ{yw^>Lz?!wMr;9k$YRBrqIkuUTbL9a3zhW{3X!V?E73cK^ZEffz?dNVFAB zd(AWa4r_oTNl7)RPLtME5AN`;AfMLWI$^d(pLx*E`HvOl4qu*Nu{Vbo(YUvwD9Qkp zidr99)UzY$U#3WrZ%OgW&1Shb=***=*n3Xg-hLuBMn-#}(Z-I91TrvI4Q;ESNWvu# z@h?OR46vI1IYo_Xw>hcmWIDTFMcuz+f2z>?HvE??0d=M(&*lz_7t1oOeFMu158dQ zf+kw3iD?urT^_aAU!!q38G1@1N{A^uJ~ql;=nap%Cg+ejnKv&F7cG_lT=Lu*c7()b zD;+PfqAKO%4U4-(fs-pxBmc?Q&}bAE*tjV7AVC84P$5HLQsOEg+x(k8lK zqD^lg<>SqoF*h4cq8tlcLTD|M@w)9Y{6rsR)Ru0u6M6j}ZXmp&QUv^x?S<)Gd_632 z@^d9inRtG`L9KZWpJu#*ScJ$CbC&}3*F5j7`Ks=|?v=o5lBStTQ&Onv*mZ(;Ajkd7 zkFpo#$OS@mVA=N#^3(m~smIU{iS+;2_4_pKFvuy_Y(H{84Q$f9NDi2GWXr7;ZQnr2%lF#NW!l+bFGu!vHlmLBCFXZRaR=4X2RX`x&Dk2=dY#! zF}u@1IF^!gm6a~a1o*UBf)sFXuF6^95OJ&$<))heZzy?!6tlCI(nsFiwXBi@S?Ogq zcsV^*%i3@un;u-ERm|3SJh%Ufe0O#B1gf*&l-|&~qI-;cjNqh4?j4&p6ZOaI*lk{u z{QK#jpTv^<!izQH#o)=_B$WHXekOY^ykgkhb2ES3%E)EAl%hnn_G|pG zBsm*fQmsuyp?+XkgXPQ&QO7gip)>M^eGDjR?}QPuO4FSezCR|8{$H&3fv6U2)Amn( zcH&^$xqWkH>x{=z(2yd)3Y6|%g7dU zOvgwx38_qc;H-@3ew&KOy9^r!5v3gSx=@AK+y;B|NP?_8Ijgv(Z<@>PZP$gp9*(fp zkVG%NjxK z^+Ez+P!yr}6n2`RC70BqvqPy<%)?4tI?{^^!?}EJI29BL5uv2Xy`lfCN?|h!-#`HO z-i81KKyY;A<60hC2L?1a68n6{t=34g7Q5g#Xz}zUlxE|?or7zCc!(fz&58xDV8?bn zOcBFJlHMbv^CAY%`b{@8>X>Qjddr{F9V?X87)4iL-JSRMo*kwLVsEkezR!h#JR(JQ zN{P||e#aH8;bPw$K52dUhm!)Bk3=cYcDzKvVBphsHqwd)ih;%1Us9hDhfc!av-Ng# z^!S&fygcoN8>e?{*o~MyS$cjRI+Om9Olbmk(h|O zQgk-+)d*p0R)xOLMV;r4^A8Kr`V}Oz z6r)!k!l6_HoC=5WaquVW-0Hu(|8j+3E68STBvn90ihcM1rE1@yQ(#-a%Rb% zV+X#|W4`ao*6#b#X-UIa*9dAHcs#KZOaTdROu;d`xpVKPNq=f5Omtdi$Ni^*<#n2H z|ECtfPAn+qGFx&R^Suo*@{4``)b|Xe;BMTw?}#@pFM}Jd1^WNfJ}yKBTVu}gyI~k= zz1II7l|rLmRWP_;Im7>S)Eq&Sg8wYgXgU*P#eIkIAMPUue%wB7GjcA==(=ZVjksiC z0$)pmv4TbExCwZnS3T9{(knw}zTWXUy%}`d8dY9lrP+MvfC%miR>yBW$Yl)hd?jz} zQRBDXJ)Bpqp8b?fB%aM?bX7r@)nZ{#e{Y`cxpxe>1?NuLtk*yJu%5!dS>q3eqf{0R z7QxtS*6Lmshtrn7R;^mDwT+p%A69`4i&c1F4Jgpk>JfU0zUcD{L~JSisD8qK^5BNr zq@2C0rp04xsk7Q>d2-Fy?09bSG)_Vahz~q5E5a#j%tVZL*H_|($}uw|cr`==dDn+~ z$K~I5H+AB&wIJ+akTLV7z7R`o=g+%8EXs#s0i=vo7e~FUR!bcf_y{gxZD=)y=}l-7 zz;9d=@MlODVsJHD!y6JX_&BC}Uu$_|XR5P1uFzTV>fF>3zJ{1xsa(wRCr>AK2j9w| z00W(j)|0^Nus!s5Wx=pfq07a*o2OCB$`Mbu1^Ed>Q#fmdAo2?{JGH0YddB9Nd2U9_B6!ogk-1*Qa{tdV=|YPG?0s2%^CilS`8dj~6H|$$ zamVjHsWbd#$k!v7`^B!~FZt~w>(K^joDWQI6~rI5s+W@0lz9efywjLr^B~IaBTTdBx6L-SSSK_U^?~eyf=^ zNX=T&u-RL5y)$CtZww$U51UWx!4 z!|gFFIGhsQ6l_0KP_up0ovh0Ip(dOQ_Zx6T+@c}3W(gJPb6O^X&(009(2%*QqgPNG zXT$8=e);pb!46#HJ65WN;th8AYdaybU2DX%YD3DqAO)sf1Ce7I8uj`U%0!*rg)fc# zk@dxnS#$^rZ}TqgX&@`lMiWpcL-_V=GNY#p?pHy?g9Q8QgSFXG%Zbu&BsE-5-{1a9 zh?a6#k;qVZ2t}J=oAEbD4#9ghf!Zp@*IrtRN?G*fZ#*OJd3RnE=#%s=8<3tIsXo=S zW(Ld#FI}H0+}>one$9B;VL!LYz?C?1MfQI65Hslck?R=ZJkmT_&7u=hAE z*M8a5>Ia#`*DP6!e%t)o$xP03)nSwr>q6f67qVYGTYgCf-1h$THrn{-g0 zZT7i&qW`|ryzs(yQfeuML(sIkFjL8H~U40?1IS9qyd4B@7~xJVdY zezU!TyyO5EX90UO9v~yWSK#+H_`8|MT6WVE|69xR`>gS_ z^>9p+88~X4bZ9MXNSO!A;FrqxAa(bYv|?k^TjDDMd)MjAFN4hStGLzqX}yd3TM_+K zmPbeMCML$~lfyC)g2iSr71>#d^6v^5lwq5|nEDRBAIk|R%-IXHZqH_ovd8LUz!My@ z2JgiE{j|;oSLm0Q^whunu+x8m4oHCW4ty@jHuky!T0^LDKi%HZHR`{hH%^q|R~7UQ zgAV^6-u?$;5lP^{FSn8k{d$o3N54Wex=yN$V5%ch{e8x|i8Z`j3#D;^005DCPez4U#09p)i0IgWX#H3v6?j%5J?6_b3As zjI||lltZX5V3xjt=vBqd$jAsLJD3hiV9W>t-W$zO`HX~_2cR<84GHS&>qD-Hyas{E zi%rX`OcE!9pvzzDCCVuuFz5XF`~4P#2rSClgaw!u#Xr!2ZG_N#yAy}__S2_sfAD=k z9M(`c`O^p2-vWM<^D=@TsHxKmG-Sc6Ri)uehO_-<*YR~^v2Z@-^8sS2J4bann8z$8 z;(CIvmuF9%Q6i3+4s)r}&N}ZlzGiw`yOwA3JEAlIh5fL>%ay_Z#+~V=8}UzvK0Sfu zfHk}LD)!zrAHTaP_!aG2&}KHh5hfP}K;d)R98Wk%NgVD`uvoyPX19jNwZo{2k2kC4e1*Pd+RGjhbY$p3qh>PPS z+r0@Y9GBIEjGw48pi1AfjfX#N@r)YC_BLNSMZmUwBComqG_ zyUp0qsShI>7iKFu3{2(N*ZclhLQPt4->bUsSsUI>t8_5s3?1L5(o~$K#oKT2GOpsa zf2C$~`4H93X%qQ3i9DLwW5|9jSCczw;}&rD3UtV>TkM?3@2o3?;esJ&zuZRA2A6nY zYeS{zUU%?#RFF|8BNfXB5cxDTb6q|zhkA705`MQ$n=ojbzNp+kIV3(}_I!JN`G8bh z&^a3_7pkDv==A}UqyLslOAW|$N-_2LkiY7SnUq&R83zBY~ zANFu7L)DdzM=E;-RU*NLjE27Pny-`Mr=0Jn#*qf9>xWa`5juyyDHpk^BQWZ5)9kW= z%JCGvZa@#n%4~jeNgIpJuG8xa#7b@`ZQMP_bjmLm+qiT|(K+Iw8m6<0p68G?hQB1r zpmg)ZpPc{RR0+6u-*JwmGw-B07E?glDOCR1-Q_Lr*UyA2E8p%6zcFKJOwE>j^rz53%_H^ay zx#A`LlNDI{W7f4acjL5;HFE<~=sit;Ws+TA@qw>eC)+kHs0hp~!uW|pBYP~gXFBJj z4Rn}JE9sZr&Oav0*ONzZCm%Yx6CZM}Q{1__C#7fM#Q!)WrUJ*vy&Hb_|5yBf_{3OM zZ=(JI-v{PYf(?T9MOsuz?gE6>XXR5VAo>(H(ORXGP!S7aQG{=x!QUT9;3s@w9{45e26hqmpSk=bM zb4))2)25E4?LnPd_A&A9;Nf|HGC+=ygTx^jaLn->Go}m+D)W6eEdVGAhJb$eWCRF8~u9KCC*txWhmjEo}ca@kv4kzThs5Sl6}Qp z#8ryOp*i%LWoOfQT03h+PJKO+d&)~Wj*@AdVJh5_e+l+lfxROY?zH2e z>r#65?L*`nS6->B<@*3+&EWtlA(yTRU9>#wd`Hk;iG|Lj(#uLUZvO@e{fypEnjvw= z*6KTPf+q`zt}X-S{I;mw=mJx4j1P5u>5%#RDP{97Br^KxtrT|UuL3)Mv1{bx1C?(@e;fozN%+M<8%$$L3!nEbr5vcgj1$(;iP;f39A#F$5lxzoSq<-6fZ}REtxoope`e{^q&vZVPTcD5EMQ@8&~+VL=PlfnzwDIW4cZdQxSp=m176!~`yZu2 zo{1okTh~k@mxQU~0STKb+pIjeSUT4%{m`|9RcE1A&I7>LeXO{20&lmDaLHjxjomNA zzo$m1c||V?T&uM%_n`$J2AAv9@C&Kov0qo~XwEidWTPty%~GdSf~&;wNkn*SQXjr) zEUCaT9-aD1b=pLlii@OTY3TOGaW50^s}4HKMMPiw?H#5YEC1wXb)z+K?;hG38*Hub zr|cp;dAMBa*e|c@MT_v9tZu_%?M;E&Im`~Q0EO?k%a|DuRgISb-VjN&QS%ckV?%R|!*`fIk{I^>9^q76b{;u4FC3hjT7aBMu; zQFs?(>@+pK8C;)NC8*@#a#JQI)hjG*@Q;^1H?RlCv6-T5u|*C^;n&;@^YUHF-`CiB zil42G`GZ&PG}kfSS8}s8+zou;DI89&veY%!3PXcq3B#|uy=Jp?bw}oad1UJi`rI)? zvO^wEo6gp-*l9EyM1%iI$;_uUOkX@3S~{O)QWQ~OPeo?GxxK{k&f;xH zlfH4Lx4F@6kmjuO#XAF8s<|Yk(p4eI;`OaE?Ql<3%V>-{-}S9Mn^W}D+0j}s60h`N7G2ZfB+nc(T7ZjD9tJ{A3K|;a0tzJgDKp{A(53n8Vswzt4PaJ>T4t!-%fzciW_sf289wSbj&)q5pvs`rLTL zseR;5%-`X88ssNKU^yx(Au69$f0A)aiPF6o;Rpye9GiU{ghITxKn+ih$S-4zg39KT zjuxxD*K=WPfWcO19SkRMe}F2ZRC}9x+eyS!N^O^_2X4t|M>hvgiYjr<%T7fh4k}L% z{oz+!SS!nU;Y8rFygF9>Xg)^`vOV@=MJ&Vhk8{cBq=we3)o#CwHAt0tab*Whc&3hi zHlDO5?qQC$Ea)$az^5+gyn~2GBfq>Db*3ESyd6K8BzKTreX=LG^ecK9N6*_kl&oX2 zg>5heW@N!xuW=iz#LynZ62onQHtWLeNs-Gfj@U7_e9sRWa0{*ggz({p$tFoTj92#8 z;G6`T*$U~^Ve%q{uirl|S)yof$Bp0Gu?oW+)TmZkU)ewF9ENKGsD2`Ud{i{=dA0mj zuQYKRkAHS)Ke6h#oQwcH)!;@I5r$ljHX)_85hyg9*@Vl|MrI`BP(qSu%Hc-3FJs&~ zVgJglg3Hv#iuvtYRjRP1^RB(-3~5{$GrW@Ao{MO!5D9tD#wDnmn=Y?R3W1W|;;o%A zT4KObiIa~e(hUJWj_sP%xLo|ig}S->=t*^Vmx3UK-#g7-Wk&XE`Rw^JnRPT&PeyXO zJYSzr;%{;Gbhv%o+iPb&X6Eagq6D@>St-m?b`Q&@(-%%;*UKvq>NsYx?jIfs*Uau} zgPR2=W~kw&0?iO))&i!;p~#+&@rNt+{3olQ2bdA8qPeq#CXfA%Aydb2mAvpNpto8F zg=T>_Wk1Ia|A(D}zIm1>nFY;cq4H{82DW0Ok~F`(gU+PWlNzqq6n5y5A zFmQ5z_Vx&H79VI^CjhHlE1@>3E%*4hFHUhw4HoPe4w}`zN`salR}n{j*r2p%?^_<;YbuQD+4LF(Tda4MnzyUk{{sOl+>!uE6mC9V*nJK)Ov` z)|*`oo=+u~AjdY0?&!kyHqNeFpbeQSkhj`!Pp1&f|B7sn?7~*B1#lg{m#`I|mM28j z+;vcfL0THGQR52PrRC+RAWC0Z+X;b*A0%o7Cj(ac9Z3Pr>o~X+DZ80z^O!mQJx%m- z!}K!%XNn4VFx^@_>$YlD=6P9JWc5LvqtK+&8gi}v5$4P(HQUx|{Z_J?F*J7=hA|v_ zS=QjQry>%9x{oWSiRmuag3e1YLM}R-LO3JkHAXzT%BAxz26Wr*;Yb*(1}_nI^LMAe zwIqZGxy!$Jnj%b|kbu*U3hVn@blnw3NIsctiN?1EZSy;~UD2h^z9;!bWXum0p}BiX zG;D7{(SEVr%?3&+OXdn@R{YBR5EGq57BZktLJ9AqeZZ!8&7`|_?UJH39d=-m89{f< z%R>++?Pi(rS_z%SQ~H?jvVZapb{vNjUo80ILptt*J}-uVQe_V1k7bi?s1$H2WoD}i zzLk!FVu6@TLV-# zu|_|6)U=svq2mwOiq5BaUIf75RrY0^(WSX`1+Z4fddPC8ToV54ts%cTN>< zj*L?lGg#(`rXrk^bIsh7ppug{eqk2~%>YFM)?H!O9|jyt>S{F1vT z$y=lVhnrDENV2sniq@4lRw&v8W1Mc}SsK(LQ0dHP1Lc&IsjIt5;6vG1sMf>K+Ol3N z(&rI?W9H(}jp5|UMg>Q@wCTeN%RwE4%ZgbiZnL$?*;V{Kdm|3(O2qDIafI}tT6Lqe z1g?!|c;UB=t!jJYuQp~P#T1(kbhFp+P=Xp4Wq=P$wrcOKE?49-`!qqU-YbYaW@a8CQ zG>ynQ7b$R<z-v+>yqKYJk+e!SNn;f}G83T=qT>vYqUX|d zesAhtE|Bku$`=-?o#@#MqFHwhM1 z$Tg!1V1~F4eOq5!+G8_|v#@p8>$n@t8KB7Nb&tD#KJt5-G@T}QsSm$$O>r{FbiAZ* z9*6Y_%_`3x=OWNNa8+9q0JCk{8hhL9(4%*!$yQv!IV6mq>jZ%IqSV71S?nZ_PP&7= zYp<5b7bf#zxb~k0n&RN;dx z^??x2nU^D7_?yQ0F%^($^YQ+-h!)|i%Kp8G6S&~qj}c~Qojsb!<`upN2xZ+&f8`EK z72U-d{x`kl64i5MAvfWb00!6@SasC~$?u)nz48%H6{-WlLT`kh{j%ITYqCA51IsiM znEgTL&VR)9fvYDh@1W%AZ^n4~oSH!L1%#-Sf=cGTF|<+k6^jqtg4J{& zrKBA{%+M3q$-LRSF3|&r?LVjA>+a%24P)iI#ea}=6Q>7)p#L6rbWgeCHfFsr$nZg3 zCEFBeN5*+orq5mi>2*Xq@fb@?DG%gir>Amb9)1sxa~hZ~Wy@sP;x^JzK4sAJGQ9HT zNQdNob+*^#ky$uk+ChaV4_e#1Q~I?5?ovYX*cZ`~_k0CXiaLQ8{c8(P=AElVGNkJr zd&g6?glk;2J+8`S2cpa+kB=>T7U)=h+V3nB_hHRnQoEx~;P?xCvF;ko(Ec!c^rQj` zG%L8|u4Q9)l^{icNH!TA%^~C&HG`93#sFMY)tU(WASq2G3*<7JCPwQBd5&Arxc;v`Wy=c8P zAdC>p-k38_{bDN1b1WTlLGbLrgzrR1#@Ybgc!vL{I=lOX4R)r%Am4Sg*7lw(Q2Sox@iz=20qm*)R>FEcmx{1OO6X zzAGe;3M|T4T8{=o<-AYF`ca-<|HLz`%ymy3c4k14gz11fpvbo$zc-^v~yJ`Vi$yCLSup6j#_qOCljx+_2ex>Cv;W|9k;jws{tEZDncA1UZA$W6LAgj5~Gj}@&E3AvSTcS-Foj=~mGyzH$KkI{w_DeM7u15$`{vtR*pc56v z#+5Pf(j?eIoWw$igdEFeK{K2M1=ukMV0hl`@VMUXSkDsB`#L#uHtaA7yR+FK{7)@F z=vJ1A@oxY#E{N3%DLA?DqNd9C`|0DQM|;!U+v(j0I3IoPddORJ`tq!>7S+b^DJ~AGd|iszxH}KSas7Kd=LFf z3ih0=u9s%h>AoGd)h4SkrH$7j?2qBB?hJahWw%)v`O_leWgt#QwPW{DfSeu=vS3Kh z?r}eXJ$C?xB=5K8I{i^Y=|NGsRxcfBe(i!Hs8GnbWP9Bv>vePQ^*SSo;=5P27GWlz zS|b#WzJ3<)^wY3K-jl+j_}`NP906%p`WNS#l#5L8JGS5g4RRfgQ$Md?`T1zHk5~HP zub;G#E7g7@fsXPHTWPcCM1S+-jAiFr@10K2 zU-Q~ThyX37nFdoI)LqUsI3RmnLL+o9Pw0#cwp5-fMBqI8%$~wrhPOlP+jSs?BkhZb z8g|K$b6u_|2}g+wALEKr+JDWp@vv)ha5nk_uHq2RcHoBnob%iUJ{xgc53(}(8v{o} zL?jfDG%!71eLm9@W$9Yq;F(#B%(@jH9V{Ppb&~Olv5w8I_hL`7eA(c&?7#SWIQ&E$ zQIN(Mm7RQ>2W&{P#7%m0PH9rDCq`K`!YUtXrB)Uf#^-PzG}qDYG8}sPM8*r;vu*`5 z6uE0L@Zk>f?jQk<&ar3vl(#W z7HFb2A;7vEK!Hj+12}Sb?-?lL>vDa%>!RMg&KWh# zN2DXJs>tx6|CqP=1?kYmLJD3jJ z`V{Yazc_8{C#&x*GIP51nLm^d$}E+;$0ZC2^?TA9pK%J%VL#|>|JHO;wx3ofx%&zI zt2yCUuG^@pc>(3DG{`0DHE-Tg(G=k=tr`Xu+Q`u^kVk+FR3O4UHe zlmyrcaI2owh!z8Y4j#pPPR4Y`erfrr_5G-s;#GuLAJ%(4`-|ALaf<0kr&P}e;{su=Q1iUOIO$mt7_W^f2zDcj(P_)5Tpa%u2V#oMQ>b3=xnx6%`# z*qn2>uthDyop?oHH%49ntP+l-9g=V|u;0#>P2y_Ar_GROERwKI1|8w;rH*-oSfh+T zmgZljsfFsI0uCHG*GzsCO)0G5yZ4=Ngaiz()l^!1o3QDZY|JI?9{#30K%?}YozlFV zr{}|hrXrtDYSZSatB#CLkynNIr4w*oD04jqup)Psrz#^Si*#aa#jeE~$(ze+#wwc9 zN{VYN1}N^#dpQIl4pI-t)hiW2^XsFW3-$L?@?I~Wuy8B!SX_l%9KWZkdB;3h`eLcV z*3C2S)_!+>DYM{2JlyAj4EF%eiH2)cO5DW03)}H~^Q1=%7MdS}bdyQ58#G*r)d)yP zl*b!lNJo2nJy+&1O3gpLvd|Svi-}0Gu+YrS1^;CX@?pTl(9UxhoKlJxIE{rF!iSMOKGAkA<_;UyLwE2rNTrpMvo zc^Q6kd+HeUrV=^>e))1v)S&^y#jLtCT0IIow@;C~OO2qIkgcT+%V&gFu`NM~Z{n?= zzatKpG2XHMaJ!v1@;C&9(>quH6>spze@0T%5yQ&Oe3`7zrhgecH1wbL#k&K3`{}=W zr042CLA>#XhhArk0u-8lUTtwVntZV4W_13|`@!Gq$DU8bR4>$I=aeCyRTzt)Q%k4f zYYYUK9HStDWdYO!4a4Q6vKQZ?7dRT)-q3i?ODn}dN0_Qcv}kgzId;kHN5I`m>1=%G zMzgfOMuKIk-W8=Vnhn0ela%tx=4+OX1J<$-Cd_pXe6jg8sJFBugcQ-FsvLA|#z41- zBOen_NJF!;f1_Y5rNn{)98ZHIE?f1eOf`aZnK)0_9`4JEh+@dMH8`d5abe4b?6+SE(DF&{W0Fi&ubJEfI z{e|B1WK4RxMIT^7(eF`YKp(Zgne(5KJ}gWB(?{OfSW4;&Ehkuf01=BuXd;KH$OxiZ zzW?{^R(PR7LAL-i_I+hcfj){rA8hnIgkq3uEP9>;SyR!*(D7<(Elybt@0Ay!`Pln6c+yfBK^)J6|1k36nSiXD zS8xzL1+c0gRrH+aT~7$2Mn@h3EA#k2WYp})up~dsFRJKe(Ta7l`m;}nAwTB-t4${$ zZ-D;*Ct3fc+B2(BQN(TVuR~3$0l%JGRh^^TH?z~lceV}rzx?k<#O&TtmpaD{6jDYs=1Yjexo@3-2nd*|(W3IN%>uR=y{+7#R88(?xV-b%f*`dBAJGfk ztn}KjHz6I!ANNy#{(OlzO-f4g4rSO&J2QQ(s8zId?X$aPGf{YsfJ6*mCY{U@7M#ZM zqPYMF+pjgq+1iN+`97RtvOyqKqz0rqYv+SSlE}l$8&_MKe_IeUI}W~wgPdt8(CQkg z8+`QO=%OEKa-Z*S5|uhmW^h2mrkWxp&EwyWN4Km53-2SF_v)J4qMw~=9uQUC=!t#Q zz|K)SF8n|!LV~Ay;--C9`EOZOcWg|@g0PSl{G6q9GCI@~Ra2o>2~@ni6#3?xi8=3>Z0g zhX3pea9XFclq;u$tW#Xvzim86CojQ$1rN=~M<6VwThBR7)HG2H2;&*Q1yTOjO{DuR zq-2~ofa;G<3u^v|U$7|Gu4}v`d+KuP#^9&g^X+n6YjuM#`oGKoTGVcG(2NoLV=z7d zdIYHN5-r8PQ^wy?gl1dz6=28Q=)nqyAM_QI`S5+mzqkX2@R~|_K~Y}*xu~c}~y%=QeXiOeB?V4O}6^@TetMv&^%tq?(* zMB;XQ!2UbbAlD|z`#XiNGXaiZuGliHrhuCr5QlEg?r9w-) z|Dpbk;aZDLzyC?xqjwq}4-W(`*l8?RS6W-jBuGp!pShJx!L{CR7D_Z_;7$Yz%@s90 z!bO|sm-c`~48Lh?hgbA ziKsEGIfx5t%brSPUO$cC-G9!t<@Mv+VdeAobN}vsU?nUbMrr5nJoulk6W8C_?=L@Q ztSMfHki7ik;DP1MQuzoQsa$`d9a@d{JdqVox?oHIk+}Q3o#(&Qt?+1QYZ|LtPM_}* z5v!=>XiT#89MKCf0Jt}2IQRL!4s9r@vT zX@p)+=@ouW67`TlUZCC0DdW7Rf!}4p zbz<=yT%ayfCUI=-wv!#6Ma|-1be|AylEBvN*{@B@zc{J8P)TC$7Q7|+!M7pubp=9g z&wg{)!0Ga!e}dN-pln>LRUU1I8n_87Z4m}d48 zB!Dt#Tx&<&=)NF&0(WOTrH`L?bR8H0XLLsP4Xa^ye@jKvddW1=3GE?t8|FQ}tY>eKQ6|k#cKc^3vk> zZOu(9Hq~1n#tSeZM6i+Cib^r38GcX{X~On`XGsn?fmCh_kvLp|3*SAM9{s_L9Vnqc zYYS*u!Md|y%S4(yNlht6FnW%crSzP@;MnW!j7Bwr5hDxxj%+gr+_ZLyLy3utvfy%|8YqyyW$|tTJ+(P!LTdpJxrGTbcot8=g?=ZfiBd*>h zUAb1%>)1T-*&t=z6(Q^MIBQ4A6#2cB!}hJ18!X_+7QLV*K4aBIn2VQK^z}7}y)Eb( zr5DgnZPtH)WdPMMs#ArKpaTx~?+xMY_lW3EiLq*M{R8q<5dD4rekNa8em!`-qmRTQ zeHPwC);Ax!BD#D;sOR@>UdsSJD>(+!eKLpA8IAWEtRsYL9yL88Mq4Wha2h6&&A}Sm z^4BWkUtJw*oXZHs5&r61r*bHU5Pya&h@uDj!|pw&#&%PM0Z;G1>T*=6`)!W6)OBqV z{*6u_aA(S_mY)>bsef2z zo;L(GYElah}qhz`{r_%uWl6kkNMTej=MQI~hRuM)vlqG+!$2B3-<95(jcpe zPO0htcG(A%K56jfg0B3>3TC|{OT#}o~iwjoLRJ4S{M-KT0!x| z2FdA+8UTUQ`VRd+;VuK(H@4rR#p^Xa_dgEv%P)G)mKCb$+X9@7H%KAJ$!v`5T`4}pgqmV1%$^mv@h+pHBI5H!30R1CJRpX zOd2QHb=s8S;=WvPrK0q_K90PUl&F>sch{BCV0!&V0Ywhns69@kw6VR|HHJ4nCi%(k zh$nnTl=N>nQCccvz|J;~2qeaSdG2hikg~lcU~~F7IOMHQa1gaRwvvVhF%StdLi05{ zwuIl-@qQS^%Vp%_JbBj}F0#849qY;2a0!0aEz&DA=-@R(HAzUg(vuIJcR8{+YsX_dUSWXiTO;Z!4)oHvDv$$r2!8wn9(y1fQ1CvG)Dm?J|Q4vbHD`^uDS1*F7isg_Tjadp{j&}Xw zz_PA!H}j!w2E$5v&X@-RbS9HDsuE*xc{(){Ws2!;bIKad=XELV`rQ+OWqH7*ePf89 z1O0x$W7S^J*X78r0XS?;X~55MjkJlw-(3Zw<*IOqOEyddmX^92*otxI|F!^+iMXEl zt#xd$Fs3U9;Ih_B>`)nk*^2YN3@Y!qmfXMbQbLQ6p5GL;mZl#n$+M9#Hi-Uww;aWE zBA?7RwB;BZ+Yq~Y8yOT-I~3ottg19_HYhEcbt_3nXWsQlH$^(T>c|HGuQockZCqE> zUxI)axn}Vi9q?*pYDeBhHzb-Tw0sE+%mPpnfLDM&RNdw;qg#j@?`r@c=MU1+6dvxq z6OZ)1ES_eV-*dS9WT^3XI{<^O4LJILQzb5SVX}^?p<&UVZ-6!c=mZ7HJsSfwh>niX zrD!m~LJD|c#E~ojqI?rB34CbJ0i>Cc|9O)!cUMz{;J&3Onlz4L!#Ywl#v~xXmH>20 zN{V4!8;4Z0RDZiv2t5WB1tDqM{E{nk2m}K&l8jc*;%+c7SuBI?yxOm}w%=#WFb@L> zLIQ#)Uw!~QnKT*^B?8~8p?tip^mN0nIu%FG=6z!HMqFpWK7r2x^7vB4N(Wsq)Zy7} zdbmrJ>B-LbIdh0b+d73B{k3E|#_DVtqHINoKrH_Fs+3<(dQ65g-+O%eAen*(kuyBH zUl(wRFz9meeHVi@nq$B)LY-mSKU&KD2<3B_*fZ`8zJp8_$^|GOwtVF`Q)LyyxyZHK z=0|07%;Tob1}%6T5w>s$BTTy7@oYHV(GQrdPFd~}Al^O%+dr_il{v`#RHPRxRarbV zvgSOC-1!v|+?c49^WCwyAy(Jnlf&!BX$s0SJvY9G1 zYs00bN1J3ZHgrJ1t^7280IwFh1Ckg-&$(>rvVy6(fD=Xp2}`$^{qmMe%PrY z@IS<{MSv8Aq(Y8d$NwO{P=J zwVgRMlNY1z8;_&ZIfQfRUgZUjEiIR9|5|{s%W0Rz_t%VpD<-MxbJASl-X?hMf$*su zzOT#5NXJD98t}}E=?r0vq>xH^3UB;R zg}UXM#j=Ol40&BiBA55%v-I9J=f*`AB#iOdJ+MjLuDVWWnULPJQ%?CcQF$@U{Oa3J zXj+hBb3C(E4IJu&GhU9!!Yf0fP$i)kl%lW&XK(QY#htp0bJ3-Y7B^{KgU4Pg#`XYd zj#}kwEq5CBHsA2fYqunl!_J%Fb1pPO-{zI61y%wioZqm;Z+`hgJe$|}*uAslxKRbO z=iWSI;Vu)!g8&ob&2iR-PNRw}QL9IU#?*N?r|5Cc7;$IE#(;KrGZ zatzSjV;j$_d*ht$>>N;-np>x+hf->1i|lRR?t~p93&-7;uPoP}>99j zZ3_CsUX5}wR9|IBPa^Ov>S&&4{-{;&4lTz0N<(oMIF#Sv^m6wQU$H!d3-9>RS7BkU ze6_#k+9mfs9x0DkY5Rsa8;^Qm+G7s$-dnYlRG99du2_?lQ$^gAKw+lkvr_%TGwrz7 zl{QzWj*~Z^0w7tb!p5kj6K-iKscfOZgh-FU*%mF?!N?Fl#wtQQ3)k9}q(RM2<+GqR zOQ#+&=fsH4yjQk}+Zk3lrGdp1Gs_-s4u^|9Qk%q?F{?{GSuG38O>$WM85dbpT?Ww% zc-fqG4RI>-TD!s4Gp4F=X5L5aQgtoT*#oq7X}^{%9|lPI=F@|8Uctk;9;(pPF{+tE zj^OKpgJ0utSKGnto$Q146h+Z+w50;fV}ERCGmo=|psSZ>xUS%wWX6V>*emzxF-5Xh zpNRcmg)eJVr=TGUSO*&q5g>lSv;iJVI=xbAdX=girnf_%JH)q!WiK9b(Q`d*-XOCS zk|i+GCv$M!j(r5)5K(y(vXvs<^3&Y*(TP=qZ~kc>S1y7NPdx!~y3bT(ST~(# z{hYj$MYl%0H=cdJgcA1qY7{EvBNoQaY|fhrh~(RXwFNKU3KD+`yZ4ediCdk3JV?MB zaSINIADMVCK^%qO~_7hzJS&mOxp6miwwegLhtTjkGz z1R)2^pk=L}c+sNXx`y4~ovfqe&XiOdF|klujCDA9CnG}*a0tD^8J0v^)r#+BU{XdI zGC&KL=XgEW?Bqw2R$zpTeAgP^8&N@a+Nk3V6vZnK7g;mID z-W`!wKF?mrS(vhhO#Mck2B2y==3@YVqVE;mB3ivDXct7T(g~x~X|{QVxpNk@W{MO2 zH4iD=s$5PF{xsoP1cr#FZL#GcV?@NnGiG4^0vHjKc%Nnl^%Oi$qdD*E+RS^~FFPNn z69cJ}#h7~*8`FkWw)z=;9OZ;+`Ak#Q3VutR2q-7{L|RbuM3#*9%eVp~=`oJ6-+S4F z5$A1)CGz2X_9$C3u=lEk3dMaNv9$Ar31dl+kq_j5rCG1T9odbH6Xrm7Prf zifdODb$94?^W^;BUgUF>@Ai3lc6zw^W^Y&#*p`g_W-P1S+b{aEb7IR;P{7S7Wh!oO zzSe9<-DYQ>$r2Z{O{nkS1vFadP#_`DWlIdOb$18yu$82w?nT+s{OC&Ling5JDGS@* zdO!%HHC{+zR}h^X9%28fHSn!kS4=n5+izRY@|^Cy1zQgj_lkE(iA9Gl>Z5MBJcDPC z65^!#Fynb<>xw?`#PxiQkQ9+YUol2D@IuSB->HMafWjAT&K$c!(&sSa)~FlU8TWNeZ?IW?*9&wXY)a{hFB1}vVW242mwCn2bUaEmI z>w@{h5Pxdut|qtH4uz{1ILK_xi37?#6(&fm>xeMTSCQ*x)&o=Sg&J&mq7*F9{NyIm z({VbPe_5a+f$0r)XWY1wx*eF%r~1+0f79zClz}8RW`cB0qkYDD-Xn>G~#t%#x3oh8quEn)!XJeDRES*4410q>e9^(dQT1k8j`}T=tKL zBLKLZ%?06k@VyYu$di@kNuBxv!<6y;8&R@=ZAXC8HhUv$ph99T3z*c|HSa+I zi9JA;Qc3Cu3RDG?w_n+#A};@jgLwL?^xqNx4-{a@sSNqQ{uNP^7Z#)VI>7Pszy|iw z2U=;3OB#l3`_=XiGr*rl5)uBwjEYi`<*JB4S;uNk zRw8zUF4y{NUQ@%8U$xw*HF5yt8;V2lLOVtyb^M#3d;Hd^@X+FK^l1#SMryP1e~Xip z7@V%Q)hUX_!+r3K$oXGk?*hAtTUHl>{x!C=x6&StWiP70VJ#ad)lY7|m_Q-DYtQ!2v zXzjtmi|^^h=sKNj7a`e1&?31?rw!$d-1v9#0$`2$_>7BpiXwcm zY;sU7*HAUypzYMyI?6vCXPu68+H#tn~h7WEO-2N9#S`cmR%`jTp&frxZEwS*tJ>CYTNe*sY z7wtBuSu^nqi~%vVT6pY$8it@M{r;9Kar&px&Ek8mVH8Sv<3t)4eaPU=3zoevU3uE$ z4kAmih@}+J$Kf(tLPeZ`GhE1#^5|hM@9ue$*C(MAikewjC0gf0)q*1cFvoBkRIJWT{ejCjQzfX$P?vS%hzPhT9Ll-OBY`=Y>cKekIzC2`L%ud`s%Y(3%Ki5j5Ye7KHY+A;6wfQ^=wDiy<kUc`=s}A;}no|Q`gcwKP)6M9&M4i zqco^J7+Fi!e4peep#|!P&h_S%bu&jPSn(^35~W1M z{|tKQ6G-^vfs5VB?i1kBh7t{Qf;hHjaSsl*2-j}A-YD#ut?CIR5ElII%o=?=^-|OR zN&v3G^E;wlvIhXf_d=WmF%D_=KA=ol8DjI{S*uaD#0=a4QC)+rSl47x0OQXWV^lrb zgjy$sm%yKvXuLD=!ZJO1t~TPwE3`T6K!dqS%UcQOS`{GHKoK_yXzSgz~drErCeWML%~LeV_8h zX}PDf+%b{(2n3w06MRxaHH4r9tp--|*Oq|sJ&vu1Zh^dAROD4@WrFS9gkB650$q5DqG}O^5F8AL4=gya4^19$hl{LZHG%V{TrGsry25(G;Xulz}<3#NBVy*4Cmphw0jv&|Jgx1ES17?iOwJE~;cX5}t zLj&f}fM*8tSm1sQCk&BRnSCuLmIsI!=#0THsg5tDedN?xnV)s?+G@-ZTBrr1MA5`ggp=s|p(CNEMrGQ%`@i0C@}eF( z*xW09mm(cPNB@-7NbIkn*5Z7nMbVvc<{`(m$69ZyqvIlbf9szv z;GPX>AWqD8-;bF&D3%m`WUSf*e(uVvb^InhRUMFAS}!N;4ao&^SaN&|mGQ$n2*10o zh~4{4^^hw{9D|#3?T=3g7q77EN=*0T3f1%~IlaYb19ujnI{6(?pGP*kggtb)xNW`- zqWafDp}R=)p3Dia{S3*g^fc#ksBy-S-=M2!6$VpXWAlqOV&E7LR)!bh9F7epeEK8w z?tpF1rEi4qBs3WxLJ*)=ex@`*;8HuBEKPdB4fEI^Q1(N_zaJT%(NApTa2SVS}*8a=q&|^AQTC9-a8l%CC{D0hY&GW|%n8E^T?pK0RIr+HaVMCv*K6UpwM^>ZY$_&gp1YGg{Q- zuhfnm!WfGcZ@aW&Zm>=G*^9fe)Ohkp3Gc==ezw78kZPtaT+3q`ihSthJ$U1~jr3J8 zG0`ZYQrMxznm*Juf}zSIw86IUKq|2m#rYY@?c{^Z{A)4cKjJ}5>DQxbI%_aqzRs%) zoc75dwa0E7?TQ&U7X=VRa}D`mbY9^!{EJw1V!Kt=xnehfz%A4O7P z>Nu6{7ZyYbiwj+jH`E#D&PM4mP>g{+k8n?JW0k8Zb%*N_E{Ky_j@xI&_6#@`j}h7M zyYh4HU<=hr8e!GASC8wmuD!k+^JR|`vAXlnC*?zAoS<-azRj|S;*e43-2Y}LTie(w zMR;^vg?1 zLO^SQF~ZBKH|Z$mG3<@$G0U}s+^Z-u-h>#y3bw-(8p7ZS@=8u&Y)l}89~KO6DUD+2 zQ08`_u2NbaT^;eM>!6tax8xGEGBGzApYhj@56$P*2sQhLbcql>7F zIy~gh7v3}s@)_S%Lfci%!Q9p3yR#XTg}sBF8KaAdvzeK_i=~6>IZU@86x0_e8F3MH z&#aSm4^J$O)xTStwsO=#8zLW{EjhiVZ~z@p)aA`d8inG&?@GC4_epHfv87!o0BIe?Ki ziqnUN9#No$CJIj$1W)y=M|woHsI^Ly_^hS=gJj`{Y{gJ#hh>5cV>UFEZVa-4uPk`T z2e*h{5W?SE{8Z>O*I?DF3<}0B1e^HLkYYwVJG5r#_OH*w$A*iS%zm$5`BR3^2a$~? z`Z4vVVvP~U0c%NScns|brV@W-kG3G3nmS%yT#h~M0ZlMOq?6d}ADtfa#_w$S;ho;W zeWCXzEGZC4YAl(Q_~?)>#a~%V^jPp*iC%WNKFgLOWR~Y!v5AvIM=I5*3>bLrS zfZ>JB)S!&xAwmf3q zx-ZX)Afxzcos9BZcF+R_@1|}}%KWHGr#AO@D&jKpWK0Cw6MnCp=pkk_Y+?~l?b6M< z?VZF@l^xA;%_`Z;1Is8X7@YcKo6E~*pDT)?*xWpA!Boos23Azg$)Om6teTy8qIY9@GEf7li~F8{3+VPDXKwLZ+s;q@+=; zwsQ99%%Lk@v}8g;!cvzXHLt`(YFz|xB*AE-oP~7LQK=3mQKU)&Gepx8zsk#L1V}Ej z<9-(vV}5UBDLJ(hUz+JLzcnn_5Uvvpdb1UfEguoLETxsx!O0vHxOWxTuv@a_JeN4U zJ#h8rv|S1sDIJ@wd#<%2>(3Svt5Ya6Z3r(l9+-wwn8oxsWD7T^-}+9L#E2cOfwXnB z7x}w5vj+7iEh1H7-~kJaN__kh7NuOP4n6|vytGVx0iI~7j$nf8O-$oR3QM)Y11>#c zvYZ5lMU{LF`mX~5m!ClD@x4V?lN@TwjN-UBYjVadfMi zQqF7GGYpy>Sn_0&bOULB@Eyj*RUlg{puBt~)RrGes-hA7@IXP;+?M=j&Zbn5z^|Rf zkAR#&eOIXcwa1d-EUi7ioL~_n;b5s&;PCLUwEVoTQj^}hGiQH-u-@ysiJzVx%}EQ3 zB<-3EH8td#UXsOW)=acO*7;VJ&f_y+{P^utS$oN4?f|v)Vsi0ICi{UJ^^y0Jk<4~) zcYu@8kYv}Y|Icm8Ug=p0bdOu_R-IBQ9lr)R)VYPez1z>kbp&c7jx*Q-BMU4E3dm{g zwhU%6W{bN$9k_at?ev6!tCs4FLBE7bevnc8GZ|-6PzzHC!f;!is_~&G(2N}X5xlhX zN&9+P)xS0C2M*3&LXy55YhF^hRJYc7;Xu^@Yusosz~g#;i2jj_5nWH(G-rT52(92^ zhGW7*G$A8H5{wwHK2!2tPGL;jT!Saux=3^G)-a?SV?rH;9nF0Bz^V)bL-x3C>qJS` zWqegyL7~ujZ!{<KUT4Z|mmgn^*`D-1&zSdu z`k%uvIzIt?6^$u;a$2n&`?iIaOrO=N?`x~a%6$_*?(rJtC}JEQiRDh8@Z`Ai%^o#Q zWMrk-gA+3n#9NytXprJue;@~}vf$sdyzMCKO6D3L=d0huqib^zFJh=k%gOhaTOHiM z$zso9l8^*r!#HVG$7R`5()CJ*Z%BK(coi$olg;8jwL3iw6_~o>l00XJc6!Xnvt0`^Ho%3{GKLe&F+1&GdJ@gg(AT< zKB;0c@B^lC+RKWlV)|~XAq1S*uQDNLD$`1(4zFofeE1~zfoou}WfK!mlRm+FAnG~8 zXzR2)0Z)%QBV#LFOh^-{N-q6jOBVWjK3PoNXezUuw|B-EKWf0ttj4Go4e${O_c>+0 zN^g+?T%%KuqqonJSW*YQA>D`y< zczvXuG0tE!S@s-#yA)(r;1O4~RFGsMma?M^JeNW>Q?tb|SCdr5_+E$M;ma7gs7LE& z$w=tm8lih>SUm0-R8H1tsctxwF?MK4MV=RzkRaM>pUsvKuLih&8vd3wz1~~s5WBy1 zvqqWM;&p%UkQEs;ma=ysCP4wHuqLX11v- z@Z{DK|0qzNM+=#^c1f~p)I~r=9Wt&pKhB_zD8L{gAyC$Fa&j_^>DzCt%nDOzFjnaJ z7_o}>EA*?JIKhrn7KyUc#FGAlI?QJjGWLuDXCk(_*@{ zn)Px(__tMM`X)+p($?O)iIT90urEBzZf`C!-qwPTIOM#^A1f&+wZj;65>bGtA& z>+G^0=0q)i}EbI2RgRgYTf>?^&*_!vx%#}nV!{^nGC*XB`I~P>SLc*D{CubZ|rQ)YP z1)2|z=Ja|%8#K%mO2kHOu_sqLge(40HGOg*FgJ=wWCKfocK#ibW1L(V~%W6 zjdpH1S-Z5IIySMeIPy^{Cpw@VPUXiVP#|lb<0}X3Vjvy+Kw+TjA)gNR^GKb>(@H{FBe*qH|8OBQn$+y%6EqgnpILR_y6B;X6`YjBfwlMK zCgG$P-FCM$@$jEXgtTEdSdL`{J|Op;?#rR1j!s4az*i&YGbXjKsl6h{7^B|E-NT` zfL?Vi56xo_4b0?G;&Vv1<|K!0+bG(Tff!P^=BDSxw4s9Y$H^6h&@1*gB4H0tjbZDz zYDURm_h={fU*{VeDu&1O;ZhdW#^WJ8D-KK#TS^n$;^}57wWFje$tpIh-)8m+IdKV9 z_u}wm{n28x7wcjLi^tsYwtI?&i0QaWd?|4ClLUAhzX=*;3QP;Fe4EQ* zJ>ZTv7pr0C9$h$gqoAO`!1wB06=LFCaEOa2h)YWPX;^ZvgY)mUr2B3!XLOV)wUtDc zVq&Oc?D)+Bkm*)X5fZX&Evz6@tXOZ2FMx(n!jpT}Rs<$WNP{QNN1x7X{^K&C;@#~^ z_#K&V_DCst5MB7_gXT+;xTPlzf=-NtnA#1#n^Q9B+dU?3Hg9ah?`~E?*8_&dCBJ_J z>*tJ<*{3Uc1Aj+qy2#D~H|A{CGww97m1ApfW7VVJFg2UKXY96qtK{HPcZnWj#JL{0 zNCMYm1V_c`yk{yURTH=2S%quE93w)HTN?tfG2MMb=8N;`mm^eMa*DdpC!c*qyrqNC zwyE|!h6gQKF2u5e`9`hdmP@rFqKs^3>DCflacrESnsF&VZ9d)`_L*5Gd|)-1YQ_6GkL<#j#yGsIS@S+NKK|1PsjcvwH}&zA`0 zz*DI01QFJd>C(mPjx~lIT|(R@yFEJ7LvmV@d?R?~eHy8G^-aSgxoKrG9;ywri)0B$!QTdb-U(s7bw4+f|T$DZ3uDI zVJSJIl?!1R+i+q#<}wp3nlAR*Hm9Vpq!i?Oit%YwDtKZ$A2@EKg^&EZ9%(;J90=Y} zKs09#@v-6Nhti^H`~Oyd47M*r)VLwAq$|ELEg#5s9mlVw>{ppEmQ+v@Q?tuE;7%|9 z!zZC+`WyXQy{%eU;8F{>EOjy!ML(9ZXdVq>Sy@@XVI(yj+nR;WL3O6TqatnvLH-}}yvY5s&KrP#g15=L!xa>?v;KZowYfHxWPek_rh*7cL< z)4zU{KbFW(y3yp;?shC2ZSstE?kBoUILd#wXV-9KeyEx=aILWU_@!Y%Og?Rq+@)%C zp7{E4_yTV|a}a8eyWWGNq9)B!$dyiMA)0K?pHc7@tHQ!>lr|H4{)+&`yT76OavKMg zCMuThHa!^RtogO}=X}V-kvB2Ap0ns6rUaP`JInFU@HjNgyw+Xb{CqMZA^)El-)!<| zB7okf$FZb*GL~V}4mw&j`V&q%ucr8Dml^$5mb`?QxHaDy4TygEIeGJ!C_VRdRc+SW z1G~i~f)ivHyh!ivd)->2FQ(up9iC$V=jT!aFuzX>24XKcqtMCHS1qE8M(u`zqCXYznJD z1!HPrp7%?l)u8ra8Q?&DJgNeJJE5CNMJU5EU?a}7y=Ab zT8oET;jz~Ums`OlU|_Q9*#%_`w=nQaVqjv{nj6Zl`};>hw4YBvrN+%EwC8Qy1o2)S z5h~#Fse+Bs(U-EkO>Bb_i98S@iquheqnl|H8P2MiR>D3pE9lW+Zm~vb>8LVsziQ`eF&D z$y8W9UDluA3zPD2fqlm)GO8#zAaa_E&Y?L{JUY5Kc4V(9+jkCvT5X2333Ch#3~5Eh z9xUVW#s7(p-_$iEtTwC7gb@%-Zv?fgI4e2n%Nb2PMO3g{d5PhEk^JiUf$@*g7|}-O zokSQ0_&ZdCWF%o~?&~;xT-g8bv52{xKtX7*laK{`|Ay=; zqd2fY=05EG9x+Yi3jLioz~YdBfUYRK%0KA#uNm)OiIX78qRJ#vDLh;bk^Vwc>}gYp z=}YYOwGHOsgtU0be+5xpotn+lPTKeiT}zxftVT5%7}ivY6A@9viwi@w2m9xbrv6cobN4 zetL`#<=-Vk^})OQbQg zo*0kI|12}-$%SZ4EwcW$u+_ZbWO-~ng|TU*cfe50J%3I#Ya(^NzY|=e{B34@+3wfD z?LR7#rD^mZH}~zVS66tNzV|5D++$6boe!kEL$77mXBNAUtPF4NRMLS~7 z6P_yq^-`lPOU9kfl(8FtWZ0s&HOeqlx6b$xp3X7mG?65K^{3{ECo9F!C1h1;o}XVw zi@mP*nNR~ZGF_r;6M3C&oXFcQ*f{*1K2&BW>_8C$qyLp?nM`veFW1ILMUp6)o-jZe zzt!iKdLeh5weL1Atf{UAO`^#Zq16B7@2j<6pf;COvQ|olhY1NQcTHoPG22Yf&BT9n z_bKOHi2eKQCE3}=b>7QQhyO$E*R18M6}zgT5RC9P4ZQ~{@@s6W?*Qhow}p!Ec9DyI zxDqu*G2AI&)Qb&TM~M6^H}S-us=Aw5!&1l!Yd=`;M{>S5QlHP~ryZGAT5|}P zY6_QQ>J)!*N=dk0}9Tum{%?d8R1KqZaG9VBuytTaQ ztE94m<;|#qS_YE}ciid!YVikFdN8@{&Zsavd!mzDknC=jrrh|ueFbve@nsLw-vi-= zBXJn(Zqpt8HzxPES3aEQSvl-+3qnp;FkmXwmiGf=lS7HjevJ_{?NIZoXY-AW;fer^ zw}bF3hpr$>wnw+LYU`uDOz1u6Y-|Nuk0Y8XW-rrVp&5?TiogJJ9n~l8n;Ukk41de4 z-%1Rp${5)y@nS!;@sG?H4bmsB0WJ9r?F4knrE9cb^rkW0EZXXWkCEE${389^OFl7b z-tPtaQf1|~cQNY)a<&TQzT;POUM%Bh-%f^_=+(cuscN<5<`_z(<`X9yt#tKI9TEB@ z8NI|W?6^1GzmoE`N}mjqiBca|65*(#)_V@I2%C~-Z3(}Pn7tgw;-_&K)0ZUJEhQ+` zx-C(Bj_usn2Dp{y;Qx9HVV!3;CPMD+d_@VN)o>w7WPaVgY~Ij0=dpQw)Mw*VkYL4w z8rX5~NmvI4U4!dPPCm*jp^VNxyyI#|-hJEwscw(48_qd$)&#Q!16IX)#?TXRY@SQM zYMZ~j6v#3f$BI>eFNI!&0$Q)r36+FCC1dS;U@WwVHWFS$r;wa4o=BAYzKR)hO9rFK z_LP0uF>S(tOEZ*hF!aY+c=&dt-s5xM6Ug+bc{r-fEWoudmE|O64Rz5oVjaN$y1}wDj z7dIs(rNrw@q-dc=h33gxYkgiH#4yAIJMQ8d?AKS075UsxhATXwV|ony2a>c9`yMB& zg*S4u`k6Y(Ab!=g3WP=7t?8!WSZT28yE|P);{E(?IOH)fAQw@rmb|r7t#{M(mFxd( z71Has$#;U9tWM@V26#6|L4|3wXX>>!OQ zQ%rcvziR=LL+Z9(dY%ZeStk@^BkF0BeZ9p<;b6WP@_VB1hciX6#ESWFsHA|Y?>MEs7lIJf zgzw6uq{zXDZCP3potDFp7FC}@&KAIz(TkV-xYeH^9!(}^wX81nC_wd^e)(m{;#nd# z9o5**=KI8=jk9{JA4d7KrK^s8t@iU-<~q+;FBL73ZY9W?TuDy(o2j97ehH*2HOvs^ zp(ckKiKqR%QXh2Tok|mvBWqwsA>fx%qyXn^Gg|fAUakl-yvLF|gqUYXiq%{E=s^aV zz!I~bxLmh)Y;U_VRju%;3A;YdqJlFUO>*+`JDj2H1}_~WnjVzqb$Kz{wxON%$45bl zQv_dIrAo1E`iI$UdX#J)Nz5Hyp{4}ZvnmqHl;Vi;tvFxK1QRVDZ`f?vr5o5|gc&VR z{^#18j3yq(c@mb7D6nlG>NVB;?r*K`WpmvlEQ#QC*beM5gCdWAF%`6Tt_w zVz&|P^uw~e84?SimFVwa3U0-AzYe=+OBSM!(N+w(rW z0L3E=$WCp5ofA{-tzwNu{!;6x1ntacx_wC4=u;C;9mr|>_%g97D8G{x6@(#5pm~$c zd3{iDI#*q6vFJqD%~ai245TwzTTcJD2W=x)DS?D!-?5Pp+!5zbl@KQ@!?U9GUqn2( z%SA@3Qt)wwC$9qzgiTYEPZP2E)5OGtSVqP^A)d`_)bmA>6y7AAO~1{xO!;MW&~H*& z@@s-3fhLE1TCCRY1H#g)j8%;P68=tEgt%(T$32D$*=pmd-y**4SF3pgiMb>2KintZ zKtsn}32_kG0-5#?8DWWO(6y{&;lG=e7eG<(tpU=t>C8`-icyA*#cot#+%`x0X6QV0 z$rWO~!%kCXpM{Xx`k^tr*{`%_w4pg1+iZ#P79m1nL+-?nw#ybG(e!oz^&D!d*2DKq zlamROt!hQ=Zr@J#?lYmI$ViRD25uq~hMG`F4-DM(bV985%c`~a@4MfXnlbRb(}hy( z+VFA=pW@P^^i9f|l2SvvAkw48KZVpJBh_Xzy1i5M=jnQZdbYxTX_+uc*vh{hzUdZI2R9O&@-+iBXu7Cs_fw|^!GrkK;0va)gXLwMI0iPo=5OPHh$*b2`KqC6zxFgi3*Kq!*Dqfw6G(JDq}Z-5 z{)Qed)sbsXjOL$)aCBsTKfSkxdvc& z)__yr8_HXJ7s;%I_(HW^O3r$$oKhnr*TH(^eI5` zIZ-k`TARh_gV|BiR=A*&a;>ohRdkNHtu2Ge28q}JBDU3PK@~avqusUxk+oykH#ryg zWlxW@PK@C1@I$2UVa4`fQWao%t#m}%pARF`sr8;ucS9tT){aK4;4}4o?rt@X$MkL> zRT6_rf=5ZW97J-!^X(_0^XErzwuC;PrH2GkG0qJtU~z8vxLXZmHNy&+|B)mR3gFX* zIc!MzbBk_%0y~R({5(2W2hBXmJwM+98;I9ZR9TB?G>Wi<9A4AO>tinDegUp1@JE>M zJ`M@6j@Yw({U3}#q#-C8vt>Z>)bB2`2&6@m0T$91YmYI zx%cKjAkX=`ABUCq{FGvV2ke7LMdA$-En(?;2V-X?t*H<1HcS-ji6r6KnLH;{A`)la z(QQ5K-6X-wNE6vNTu)VKF&i7#y?PgyF1i(VzBgoR8ONCbI?Ux@xxVyh#tmWO;!5f3 z>vt_ctefa=lJm(DD>9pXNGf2{Ss*+zl78wbmQ;vj?&|+-Z>9gU&18rX`QqUiu*ARK zEw;UL2qv{6mg{H8eNf8?zNS5o@)Ya)2)C&$pbp-~F=2^^kgcYN08mqHYR%9wkf>BV z&)rrZv%!6O14Wwec(ow5dstffrtM@M+RzjL;&XBI5QVgVwC>xA(p&x1Zjj`@AO!P6 zL@^1{;TGD7yUL2r8=JTEjdKBH?C|YI&5<*cPt;;$=LX(E~BSV`lV|2jrCl8uTi(1c}vWbT4#f>NWMZW;0pc7Hi|xG?ZUyd5v4a%~Lh3lX>ftoDUD^RmWccBMP6MS*BoBGG5{yMxE`mXfln_Kj-)i(LD0-P&NGNy^%V zQ>6CpG{5{-UnjFNZzJF1=eG z7ze;uo)&X!Q^i}u_I$97PN!GuB5!A7Zr4!_*PB_>&0`QnMZ|nYZ>o~5@zfe0f79Z| zWay?$)jDsZGr!in4_^C`;m}>=X7?b(OUp|r$WzUD@&ok zQ<_wCRu~DHp3&17V`&~uDIV>*H?O=eQN>D6tzaC2$mAuAZ~OPqp%<8FdLl@^IVQ5G zuMABsn+oXfJFA3<1Bt1lOS+FY?UzJiC#wYP-%dk_1y`XR5_JP$=_&{`N!8e4>~d4O zgobNlqqo;)UIaWH;56MLOdglCGE^m1pskzNnlE*_Yq_oo8+U^zl>TVnW98mFGt;{* zjyW=qfq!d~2KsgS^JSR#IiGT8Qu7a`sP(lLY7Vy9{cQuUT*ed!7U!4I89651!(23! zf4_DtcsWHm3`rGN^0@O@6$6c(rUc6durC{JBELS{l z{tp3SYnld%tr=eI>E*gtk39Gs_LftV(_bhqYod&2r{#WYGf)6{M<-Fi`=x$u(0I{Q zH*m0W&qB{F4?L+aFg#3v)u-}J0-KgYfONXIl+D zYb)iYlD+s#G3KZrfMuF96&Tvt2LMfoFW3(F`R#MxO$$6xigl%pw=>IwDs_X+m7)t3 z_jv=r-jQQnCqhab#_G8-9VQD8#`7sZcJ7-Lky)y@JoNWxM`G*WaycS29D2yrsGk1k zcw?S)9~-XLm->&Bc1KAd8Sc-TEKX@yN`r?simgyqO1K7Z&G#!Uw4FG!|6I9Q&6uE1 z4LP(ESdMoc39()99d*@X7s3&ctu^P-E1~d;r+RsK+13aQQuevJl!8G~{&;p> zH1@%Ay^JvXyi)n^G{hQSSoCeVoGi737&S)Tx&Q6#faxYK6j>(_DJv|!bUn#sHekB1 zd#9h*VA~CDc&M+acHLAf2>yMVsvWD)0gp@uu z#uW6=J`Us6wT9PoKwj@PfY{FaE9=EdoVlw=0woYveJI7I$-8vKs{fD4&wR$KL$CfX zYZ2s_xG2W?mHzNQ;cJkf{WUT71rZ|7{%XgfyEj=MPXRIy$3#+GNzCfsp%l5c+`iR$ zVh~G*{^gKtBw+Ny3Tcnw-F6oUA0qNXL2e-2G!^n;E~_bd(8X!Af2VJCZtd=vZv@1V zv1dpOx%m_aVcG#32aAzuX?WY)+laha5Km}}J7C#GM*2i20u5SH9w06+FW+<*|0V|M z2=BhTmMv5qo|zwc)|5*GGNY|@@TN9?9eQshzFgP%%nmv(?obpZ;3->eyW=I!OX}(? zKZN^dpZ&BJ4+8vMdmEI6#5T*Oxmpll za@oyup3qCR8f|;u#o`_xmRMA{j)Fq6kW~(P+bSK6iyQ=4+tpx$#t#r);fxurzY0d< zO0@%HAn|_ehCU*@Y#Oi5OH2+yCaep1d;O~CZ~quk^JmC#Zr2SMR$p-O38I3Z1C(T} z1ku`xI>a?%gg&po(wcy`nR<%e%D?j&hzK}s)6CL4n7d$iRw(+w76Rvxh#8HFnT{yn z8s;fk;VE5Ol#UX%(b(z+5!H*ij+sk<(!|g@=@U8vnfPRsR~h=w##>olA3Ouu^dv10 z-I}{20=Y%aZfIfZw1%zxJxJ>E$!`y8ktE@Lklr_i^3R^cpOi|}3N||Vh5R^BQ|eN& zhT6)+Jn!4dDJ19e4K^<;b1(m-hHzx(rbO*$WAiW{~w(vH< z&RhX{;kYHXpfgE79j{FS+}Q?|1ITXZOz^o=F1~ zV@Co`zWV%R4ed9LBW;H4A--G$8G9eejRUS+-dsD=@wb6I3L;P_Gu($UetYo6w50Fl zR)u;bn~V81{msFh0P0!irymU-?JR6gh!va+zo9LbSzA9HcRm|0-)(|Dw`%e$wOQkj z2D$y855Av~&W8WmiFk~f@A!=b^X3`32k4xr%GculV@q?PBYf#b^e1@B#JtW{U*>DW zM}NR0hu@RX-<|<8=#|aQrz{e+JU5|*R-U14QmB#u>g|x$f+N)U5_74P{LIp z@vS)8+H4bcL_vOe-fUp8m8t%O*o5zaGL}F@ae=W|-w$ZtZaa&>!P979P=8JoH;~dP zS7=)fE6($fA$3$rxky}r_wz+o6qGO9Z^ro#EGm0B9$Qr;!jgqNFXZfrC{2`w-35rJ zR0=`Jg#sz;7Lr7msP4sZP-d2#|6p2tzD8y4voGLZC*rwW2wFoc=STv5_v<#aeECvP zoL1OE+G72nW~pH6Vg7eMU(EQt({0VSfp4St^*M#Jt&AtWou0yGn^Deuq5W#8BmGhp zW))HKV@ms__GIE)=-B4YUfF5ZlZGG4qxnd)V1E=ZE8oCD5^^dWSQ9^rb(C)ry(>s6 zHX=l(^L4+;iU-9d89w;)WS-JDhHxr==BCAeyY<}nyAX6xSO<=jF5KYrN2g{S3E1n6 z?P;*OwtXC`Q22|MdVBf-h%lmSg>u)3XT+48w2 zjFsKV^i;b&t!)DTXL(@qxX=v_?Y$>-dsjVB0~c{W>Az0UKu$C1Gw%%$m4n8RipQj05A!Re8Y35 zt{DRgN~lb6AWNt?U%VbG+v0<1BYNvinlF0N+#%ta`w5P}fk4u#kGzzW;?by&f44cv zRg_*BcvyjdEB@&ynJaJuYPYg7-|sUAX=8L-pRP)v%SjeLhJ#jTz{`X)2T5%VGhAb# zCanOig}=#hoeV$AE`6?B$EE5=1%RH^I_o|6ejJDI{_KzKc6fC}1WJznABm_KkO8I6;%>S^R2pN5AwZ=i$h zI+7cG&IO0A_Z0}+JMOds@>U=9YUaT~?xCdHK4Um5bk%S7-|Ub&QJX;(-kcHV>$r~c z!4qd+h799CB6_VKNuEY5J}-weQx9QCe{PLm+~c#^;P{h!XOpoXM!o7(Mv!6HW2+(4 zyadmB6v)^iR1j^Fh*r1}U&`?`8CGmMXH{#pTtQE|dpL2s;ouyP@3013=$@gu46ySw z8yP1`#(Y}w9#kUfy(ZrHLROA)3T6^BxUUOYLXSpOJ%+Cqi(_kE4syk~u+ z8v`s;>fM=%j&;djXxCj(LEMy3tuLDd4kfQ>O9_150Dfz9e%pRUx~K@BUie%FA>xIOs0Y5)A!6=Q&~FNahg#WB|Pf= zOmg6^UL=PMdm5X+M7M%>**?0miq#-wAqqPEkR5qK@Hh%>uh){&(`c-Wp>(km(W70t zNZrY|xC{ceYqOl2(MhH8G@GC)-LFZ56o@}a>p2QxVp3fkj(X6JdEHGO3{bf;fj>Eu zT>UJ5>D6u|0Tde7(*0&W8f_Ufd3ZSXpaJUmr>!MME7m?eJ=?A|WkR@>dZ$(URFkXw zbeCANnpoCu2K;rwF*XSmPy03Vq2o1Qd%KKzv$v&wS6T63C_SPPY5jy{m73xMC}Nz7 zc@wY6fQqZkMIA;fZ~^}$=Eww^qW#U|1GS<-{dYP3kz%sM>0AF=;cYZ7E>QckxyRC= zzXL(N#>CYyveI5_>S@p-?w#}&-n8>5$N8P#j11Xe54EA(aYt=we!YzodFj!_lmaxZyW)e?P=wljCy7| zfKH_ACGm01WK4(RX-gHgnUI`bTa9pHi=x+n=iJCF{@%a`Bn$3lb!DHJ@ z^bq3W>wHVL;&cy^^9}U%uLJ+DCTmD1nGEGz_)lCUQEIyUVJ5N`@~sgg3hs59T^=J+ zE^UaE+<56r!Ioy7t%lh560H6Upjg`JW4zgWw-|leN#bm&t93%EH@?H+^=&;=vLudurJo{%z4}LpzZoW2Vh~Hr6YafnR2J#j`&bgVb_}-* z_KuzrdB06IbL5K7dT5e@tV&oHCmQzDGWiTk){gD)=9zR949N!S+)W#1Lm;!cf_ zFfg(pB zwtRp_-_MkZx(Id-X92=$;vM6IkG*ropHT!Re)lIo6Z51mu8rQ953JZ30wblc`L37Z zCi%;krTM#(EIy&^#NVp%r)-t+gX^gNGG)rr5Am$;y9K;~=9*843X3V(53|Da_cR!q zqMkCgUtb;EX~)h%QW`S#D^q*}=QgBDth=|Y`_z#NdkfAgmKlTLkvhzr1P9=w$#Qf3 zhw-krs=We8^>%oq3{iL7FyzAz#wgEPbi}-JP30t_#q?CfP1N{c8t4-NN}lhHWjZ92 zSw6BSE)pleb$|~&u+#S5EEV#TsxZjUx?+TSHY9+pwx3uWMo|>0by%oh#&T#AS#F39 zYR9Po41K@L2)Vs!(_Q6wFH2DCFFOU9Zj;kz64hW9>#Y7cwaJ(5eiIBZOD4{@xGe47 ziZ0i-qr-;l+aw3&lj@9|dkr7%%s<*Z-s*ofufWbM06ORRW?O*Flx|sAJXRDZDIgtY z8=p6X8{VVtX2G4t3&wZ56}D>3R}Zbs{=0e2HD3JPY_NTO|5%26B2x$ZntqKYW=Oh2 zZm`;ByL0a5PC9uqG7JD=@K*z^h`l+!?6kT-l61i7`b%H#CV@np=Om{hO$m`6j}F)( zqd`CrO~FqmVvGucwcJ%$)f_NqP3-B!iH@I+6I1roJDK4}c9AsZKI^dB(*dHsQnF^X zcE#)A^NV`lFO5t9mPy`J=8*2O|KVOFQmH{L3s&C2BKfCjed2;#X3^ABbq%~?%CQQC z7Uakkh`KwVsm>Sl7i)`-NhF7L|8yYL?f&19cg95#ID*Na^Xa{^<;E}dvKMTEci(TB z(C1Jn0;DWm4cUFpf(vgl!tMCyE#D>xxZ3Vji3%|OJc9lv)t8golT$*x`T&0JxDpo| z6r2zD7$?Z}Z(lwa%rPE5Hu-p-E9Ow+qTgM72~PK{Id)Nt#fGgYzg#`EnJdFOttA1s zNmhtRh=`?HfOxr*wq|j73ol!V}8u@Upk?ddP#KhlYLCy^m#w4tT@EtHtt7hWYJH+K*WyEa>!K=(7GY-h6z)nj{-gngPLn!I8|c-Hn{ zs*skBfC@|fFXF)z)>OU|2&P_}zW}*A%e~6wtVD{<*zHNL(NukvXaJiA)Lk1FO_G2d z3Y>nNMD)T)LX(iO2gP|lnO_}b0id)6OS?>T@`S*QQJbzo7MPkO#Q{U!Ni(^iDIb?< zH$A|=b3k(qS0J2b)xYiGV?4wsycdI53Q+C`Vj~F^*R_|wC*zShI>M<=Vgd?k!l^1v z5hxO~Q&_?!I1ivnObD^^^^^0p4PiwtJ%xFR2SwbK_|`PDF(zpY-bDD4vAP3mSh(NK z)$4lR2B0Zk@|!GNgPo!f%sI23QXv6rsU^*Uit_YSewKnAha!8b}Wqv#;L_ZQfRHVKX%I1TeokoIV~q3(h%x z10D}EL(=T=$UQA6;fy~VsMd>#5Usr+%*fJz^7i~u!fz}gG*!E=oOBX{2RO;Wt>9o( zL1_d;|2F;pwjfUH(hR9aW5@KLfH z5&YJufB4UzNg#yq64Q?tE#tLHeYjT?I5LLf_OOsZpH7UE$?yOy-nGztQOLY2fL4G8 z$&5bw_I+qNmD?RVe=V@vp~*Q}`%<%mQ7|+QSF<_L8!^NC%NLiKaZ8El?NOjl z(6X{Pol|kaFNA+ARgBi_UceTvg`AfEs!nWd(*%QBD^L>AR6_6khT1E?0R;YDrMqZ% zT{|#olb0@UJB28m51o$Q^1ZCQHQpyZtVqE-IM1qmjn1dP5BlvpB0UM1@P?@~O1Kve z>m70sHCB>5+1Y?>d6U&H@thN)XCoX&-Cb!}ewkbk<~}B+-ArVp(tiA=DE~M&#rsbu z+(t-!L*u5)EeYb52+v(tR!=tpKYm4y{Rw#Uj}_zWx zj#zM^+}L-@?)W5%CCHN_R=0J*6FcVO($F%k0xH|?qrQWRE9yTETbF10;>ni?yHG`Ep81T{ zDInRtDXEiMA`e9m_DK*0zdSjm#&=z98j}x&f&yclS0wN%4-a-;?E4Y=xl*ceiMo8y znSs?Yl!OKgO8-9*`_L`2IyUx{nFT2s|FD9Rj3F{Eq}(fmn9qIJ(@%tzfS-aQeJo0n z5GPXbqK@D8yv!GJunH~^O6mWNOd>BeIb`m5xqqxrHI)XyOoTcVy$&y^?=q|oM)&Y^ zw)}r%;z6^wkiRLW(J|<&trlMD`zibB8MkF)m342=oPWNR)uG5LW~AyBvhq5HlVrl~GZNIF^6fi3>THCrMDt^4_Xx zI4%zH#ifg()+ZFtF*CzbE zR|5zFe&p5v*OmQv^u5c=;RF4B&c8awtfOes?M5fR(u(#CpNn%XOi%E2Hjx6Y-b0ey#>Re&-jO&9xpI zvB7#%?a%1C7*bl@y>d0{V+%Y*#H%+vYd?X4#Iih0e{6I~y&0`~Lj3=WwQgx@NC}5O zk;dd@u*Sdu_{Gl4?3SO*)th3en;A4wI|=N^7&N1=wW^XLI2~Wh=W9EBN1_&yoi?90*HZMbU}+?Nf%b(%@h!!M_7BpW`_xcFz--`Swbbz>W~=Qv zN_LG=(`TDnuT9`8+oK+Z_!fD$1NK+KtLw#;F6{rS1u*11TeYI@-dUHhS-L*9s)o4j z;ceIMRT|je>Zw&{bz#m6%YW4+wIXQGx3bT2AS=vHsehBxd8Fh+K=^xkZKY0&ISRQj zjY#L21%6(JSHb%qKwor|UTe-=cVMD4_a6t7%VOdiiB)%1-+@TD?%gxz^UTA7c~W|yl&3Rh7tbgETN$0 z@Tm!k4J|okf}v7_r%EKz+wMLwe5f09hj63k+wu|EHlz6N3OON%)2aL^kK6QScz1VH zuWeaB2h6E1$AQld8!jIPaK=CVm@jVQ&r1Ce)u3&#z+8pZHzUicbLYf7Hp>RSdKOgl z8ZP`Jga`vkth_7jT~X2L5AZuBx1ibjvLKC8x#Q3c*r!(8=HGXU#r*8S-D(jk?X${) zeuHj!gRlCBcVsSh*E;++e*OX$#oAT-{5Rtb(DiFR#KxIw9_p)95X31Hi7y` zoC5N_@c57KLDMtN!La6_*>XJFJ_1ki(-Jb7rH>qWQAoW*+kn&aImc zXWrQD=|Z&u(%-C4AuYB$9k1T$-qvoU!>j9`%=wy(u2ZzzJ9 zDlH%-B@IeU&xJuLj3=uI`{ zi0BQ5n(ty|DeoEizyknAhyQvF1kzofaUEFJ_@3=Jz}*-QlewvcT}UYPQXTQLek!3U zD>6UDThJ{JF>i{HM`Pg7YRnq5dDFu9P|d(n!m=ebh#?0=7x^4WsOk&7X~yp#rjKuU zHd49Y-`i@gZIGA>JcP&2RKuP|Lqr!DN}m$nJk?@WU!POG<3J-v@1%7p5Vm1ub~2{F z+B$T5Ci@L#-dXoz{shY8fLJ0!LOAzxb*zYNx35S43MYT=!$FR2=I0`sXe|#z7LT~{ zOpipb+`+j4AdN;&Izd?h2tQ{s&At!Z-}ZTRksST!;z3fXcMk2dNbt?iwBg}j5_Gfv z8!F&b0wf5LlQe{Mjex%(cBqe%o6(gLAFW0V(P2q~uw;yqka%9y$HW+X9xL4DsiuX> z)E|B*yA_YVTzGbQAEdg3Mu(k4pB6vkC$SJ_zK|6Aaw}=(&Y-87gc5P>&aPB4Z~Oi} z=+CtHy4Jyn1S7+hMEi<$W3}s|mYPxZV;^P8xfb4d=I(tVo+#KDEC~^K@m5{N{>^nO z6k&NeNR#vwhyxDTKBC|ktLFEokc(4bkEMCC ziOrG(gIm4&NJnRe7D%XS)py=ZAjnWxdix=Aa$kx(Fk`qq){(n#FrCTzKT1=a6<2Uj~|V5`48Z&~|Tt&Jhu zUhlvlyw1(K1A5&W$@=W;GuB7yHV1G5+qv=le$^yq$EDHUPY<*F0E?I;Gw1Jm&;aX- z4GLD>U0N9~$Z@G14-j84WgUh`zt_E(^M|}+PaV)HZxb8LK`Y0Z41D}PJKi7lMl27{ znF13NQzUdTD8BWpLHsjSVq8QFn5~ zoOs0}gm)Kn@80u;ZosoEOuBw?oH{PY+n345-<}vrj%E!9fQC;luhfrkrZm(S^5tc_ zwDTs#Pz$)}8oY58U7Ti5o7b(UhqwFh@vSsUw9vjjj5}m2Ja(x(+}I`7K`h9Q$S$d5 zzqVdOKz6~TWdVc%(U%S?hJ9XAIqQj(G#jh+TvzUfY(977GW7IB*oFD!S8g{7;jEfi z5oP+^-3^u+XW&@zfVf?!lbg7`MqhuSd%>sz(X&RiLL;cuU}zOsVsOuCXYMDbXoAu2 zhzjHU5dNUE=}QFe*5h3CITWNZ; zkG}HA$AAhIkxi}zDJjVM=Yo;N>d4}r`0#M_P$D8i;z>TFL_o31_}`~-220lEB40p? z@`WLez!4r>z2O7(>%G)F=|pVn$i`PH>l^@G{8Je}(7ZrXlAIr65=KvtlCLjF&x|m~ zjgZtC{=LtWaF|O&M405-Rs}mx{<692LQij*?u!(n$PwtOG9Ve&9XM=bZJzLX+4t%J zCQW<+q!5Q*qkY{l4PDC=P0kY6;woHaLz4|s{uwpKQ9${#rHIMmu73iGbU<*~EhTfb zUXOa%pAjscQ~jZwHrvA0Y*8B8i17bcm6!?{Ee}}{P0t-Ih*8<@;W}>!BEms8x2g;? z%gOJwbT`{5*+g%_B@0 zbF=?}hOC1NIZKuu?TDFpii$j-qr}G-(NDZ+h=PX9i8!SL?n2(U97*`NfE>@PDMQ9b zQ;{R>>L;J9k}PiQobZ{UF6ClUD^CXEAgeM!4NMUI-8bZH{+%mhP^x$*gN$%Xjv|#n`m!LTFu?P_GFoYg5p&)<7qa+o%)4V zCG7ToZ(XH8Z{hNA{nWc&0P;Fy^jZn2N4B_%U#@?$+|Mk1AgnAmU$pPE`EiS}CWYns zCXbDQK_((R?W*E3MSm`CB?Th%V{7WDw#`@Iw((wQ#x#W$P&LUh^2Ecl<&KB8;!Pw% zMs}T^+cC8ni(lXmgQ)g?LxuWVPMt1e7e)E`bkFPBehn@45KbB*NC0ww7x7=d`eLP|9|{N%rV&s8TycFbYL9k=^N zYny%_so&WQND(L=w4IUvum^!=v0f@Ukg{FYw;kPG8;~%&EwBPQ#pZ<@LAr?zB8`e^ z5{t8RftkD}x=Qx<200c0D@pR7rTib)Dxf50i9PYj;!GcxH1vHyjwE`(up!!MLGw1Q z0;8cIqem~5)KS?Kv0lY8pE9wqXtelx)TNM3YWYYR=Sd@1-ld=X4Ns^A;3#%WhJ}Z7 zNjp+y0Vd%~E~gP8+Y9M^uXI3RlLprG1Va-#p_${}F0w?549p4j+xAqUgnK^>`b;mr zqSQj9+R@I1G1=!-V~uyysLytxUUx@oi2A9B#cdVZv54Y_@Nao75&<|YS_yi%g5z~G@v)@?zbroJ>^8O-I`8VX`H24cvdTR4+YI!Ap>2x5 zdUZhy-bxNqS==qw8RB74xfwLpQkJ9FiD%n%djU;uvU{ z4Azm=Ulj!kYv>yuPUu}pR>i%t-*+l2F8^z{2?q2){Q@sLY?lI8*NhwTRLBkL*y(q!S@cuSvwuvboSr$Qzs|vlT4lMh^6(1b9(zpY~+!Rs` zg7c9!^G;`wIa!CDaDj+;G=8i}*)8GG&RNDlsZqrR>0LWVW1#MqEk045CQY5D%^M{4 zNkOZDI0+>@=rl#@7ZD-b*l7>GaVc-*pg1S>$ESasQB0S#g1-Xr zMVmYYPJDB*G`S=`y<%1Ivn#aDU%<(Zhaxb2r@l2c;yyI^s250)ej6t+r3DxrjK9=@ zKa|m(T!iJ)Y4A;P(ptURPcGu1jc(y{=E^nrrUX)_cE-NaozdE;s1(5z4DWb?p1c)W zn1BApeNY}45J4Gx+Z4aJWmCtYE`r5nIKi&v8i;c(BKzBnU{{nw$f45<*nUQqL!iWs z2)u1F=?|^o$!O3!H(jZ$`RDPIfE83fASc2C1Ft6^{T@-#iWcb}OQmqy{d~fERFFvajyV2#g6EaF!p9d*J%0Xoop1|E5sQ1z_{Bj#nV9BT z!8#Rxq$DJWq$ZBEx#yt0-8D;?L38X{)v(~w@R1x z@Z7h9?*z{p2L9T@zl$KhTAVI?INO7hKQ=Yw6Dx%J0JGZ4)JdvPe+tG`SPMxIzLh%YCrl=j9? z{S9aq;GI7IO;h;+7W5ByeFZ?_|Ak9xNYk&Ooh65dBhrurh(Gkd2rK~cgbD4))d&C? zP@H3BM{@K^*YHf}9Z8Y)P9H7giiap6(M_`(u-AFANQ&H%V?P^yp`J+OFDV$fpk=22 zf-Pa(a;mX~KlaWq!Q-hxP{e3UU7q)~E)w8&D6qf_vSZp11C4G_LU?i(Ce(P%b2+(I zGoO(Oaau!Y*Z&BK!W>?JPa^fKz(P(d{E_D)dUbTk~~NIolmE z2In+?+E$FgzU9d4%*=^+^DXBMvO|ogvaetJX~M@NH&Lk+z55-3Os>?iw{wX^NV8FF z%~Q^^` zTC}Obg@UPTf9+MJxxzu9zf!I=wmv}d3Vju;N5y~y?+PeZ2oUKtt169b{^{f$o9xLe;E$&4|Lb!A z{iyXmiPDA;qkGwz#3)ok z2rpn{LKj0P#Cis>>c?!V$1H@c%nSf+Q38AyPNZ2P21p7cFxmGQfJv1v4g-kaq7&fn zNCBK3@oWqHV%4=fVV<7KV%!r{!+HpY5Yrhto-De zS*Dq6&uMZhG@k|rhbzqTi&Qy97;7NM#%hbM#HdBomLU^el9ez)UG0;eoPYeh29lX0 z>qQS5LbCMFNKJet!9Y*{7t|St|9^Zdj4is~T>6BgkvkN>M*(ej&caeWc-ewtH`50)AXvtyedoLaGn}Y8EFf6hkD_%;w9_=Ol z^>F@v1ao%EVfA;%v6ljF)IXOWHsYFG0Ej08h#3}c`R6kva0_q*!_;UH@!NNL9d^4GFc}E~nzq@J)XM*FD6$X>nu~uSQ|#ik#!C zS@^C{`dRPs^bTJ%vBijY>^a9b8;bcjtnG&jONBhqHG4+9p_2P%KYF_og?*^Q@Gf@m z0z}TmF9@2tCZhs2+4H*=WY4e(W#1K#aPX|J?!6efebM7lwAoVEa6?2n?W)-ToUcoj z16yeGcemd2n$B1H{AfV4HS~(wsy(m2(2TXBFX618rUoKg$p$F04JUEp+flBqo;x?i zWrSpz^?BY1q;mnBdkwu9ceWCx6s)kaYr0k0PKJJqZ()s1)^Jf#UhZpzhK?f!Ih{t6 zl9qx|jD?tOkD4r+1=1>}wk7}yH-A-`6&CWy*~kpMamCHMtFM;jw$dlr(Hc$oMrzj5 zaQ%i2KaiW$$?JYjgrAag-io-i~KKW_a z1nnXbcnuw9CgI%XlJn!W`+ZV&08r<)8(V?~22yHS1 z4+=L1zskNy`dL`j&l*`qdgy~?45$_34+Cb#PsY^JbCgjOVkg9{asKh=5#P4oE}tQw zH=kZ`AmfKVqa@B~V2(G}bdX?+2XKDp5ysATZd}~3PDAep3mE##xJ@3cU3daU_!eZ9a%T!QzbT|6n8L>1Dg) zqE-7Rc-v@zk)*`91nO2!SpQ~oob@=0x4;%@sUCa#XCB59i4PF$sk+p%o|yCB@#DmO z$_i>h{Fw(bZQmv+alD)5AA{XRq-Y%^ljxSO~P~|2aIo}wN)0Fs%E#2Q^4;L2WmL+=1Mrq~YrhgyHvpJjMI`>W- zi^N2>M^eo~?7amoEK`*sSNfw{&?rW_zYD@^ghIq3N4%@Z884*lp~wa8EO(?}Kg)JD zcn3IT_@Mdul5lo))Umk}4#ESlKuU>&G>Ih_G?vqU)b2SSl-gSrFP83In@1Ry`_gxD>R)su9Y-E#S+w+ zS7ti|$}xFrrk;(G$N}rX0mGxt7yuPQRwk^7Hrk@$S)Q1#8S*{VOw5fNKs(ITUJ@k| z`%QEE*@!#p$r4T!Qlb@AY)!cN;A|qJVJEMd#{`)pzX&cWX4Hq?Z~in^7$w$@8zi;d z3a{hOzUS1{zmZ~`o;s5eS>++tS8T=)Y7@$WogIRo5GJf(D@Y#??HK=L^CY*0J8ZP1 z@cr=`RlDll3_x52UZX?0V%DYvGIcJl(C3U>A|(yIF8w8HKoAFX@Xw)*0srpDe~O$V z879q6F8mnMiY@@ljjj#IOS0ZLalABV5Ey}H-%z?R#!nGD>ULl(*~oV;X0Ki#jlTKZ zSmqX=Z)I$Qe$?l%9P88`npm9b(#&no*Y?zB*~zV~0d_yX2=M`oy-!)n*4ww7yW~As zf}^Tmm3M8v>U+`I9$2YuG-;hzfpkPF2&nby+G!?2jlQKGc7MJ-s0cc0BXIsArw;A4 zAtaG8XzTwjmFgAPDg0_H8fLT18<|>l`z1aww-1cfv}j~8@9itG)U%~JfAuN-qUXR# zHT_hh73%9idM=sM_X|3nuzX%qUA+)^a`6tf_e;3vb|x};lXB*V_~+LIaSTqYy0E)?nY;c? z7js4Z8ZdBj0(b6#{8h{Q$=zKv_xmfOt6>%E&Yme1M@SA^_Hk>8HCH6V<~yp1aiQvi zx;*~7YUPA|R5?gY5p>7-mrzZRv*p5Ts0Xo%&h6uQH?2_2hzcNYSAW zPjk?;&QK|)kW2bp1=flL6v^*&0?%iEXqGB|TkVtpR~FYFo%#-SX^o4w2(goh9@>3M z+peDtHdiJva=L%Y;UEWEg%A6w12)L$C5*0rVBnX_0s1wBX@bw`#qfCTMa9&E2Lv|A z-w4Pgoq;2)m~PkkY=3(5?3Zo?#Ba1Q#a}x%D+V)UK5z?035>hUd3lejpMiSvK?=IU z(`wi`gt0qwG>Z~@>vAVe?KbSK(dyy+ce9Emd(*MK2E$J3U9LsFqj(DqUsVdD7d$4) zFx&lwlvL`7L0~?7OCb(+cKUbeJVJEYOauM@E%{X4_ZSwon)AOIdF=A6-JR5S!Q6(L zT59jEGYo2cs?gx6sO|#;pGPyeweC){(s%hQ1vC>44n36Y&6i5gwQEi5sFn zlOrDVDT_(6{#bTE>Sp1SlRBTW;)|Z{JL0yqUE>T;+w!zY&LLkMLEHHHpD%vrP~{UqDuqHapq+2_`4LrHK<~&U1{pT{!C8)@v-wR| zHkD|$%*eLPURM0t(U-4tJrlb;om2KF7Z0@g;CSmB?=bJjKVf)%DmXH`+S~hOClq(= z>kEnuS9p&Byw>^uwDrp_e{KCR1kU79&zE=N-Fnr6?V*QnEmwKWxf`?hHYafZD@CRw zf*(4WpN>t6^Cox(s!up|O$_$G1WW&y22>;WZ>TQU zwR|Nsc8gw{JGBIm zCONiby;C)(ph*O=>obyb1>QF=GvNkR zW&3#Gl`|1xLbYm+JKdiQtxsQO2bxgQcJdwr*IokqEl8IYAG!YOX!jt3Rr5xtXoVy9%-( zht1YlFfPv5=E`M)qYaMPL_&8(UIQ3+ilsx)UpuHyj&RH!q45<&1sRYMCfkQ~3n#|; z9o|e|Q#4pe?55$u?#gesFI|3o0ygJ@uymx=z3$xnr|b!f7W|{mCK*g1^RsPc{1UYG zuT=I_g`Fsr2|# zU^#ZGI6@Eu+;#L!81wG}5B}ffP4WC*d@P(`bg2f1 zPiqM0ag?W{j&l3Tjx(I)s$~r^mrn7#$QhwG!HVdf4uzVh{thUE!^l^{%?C;^bh7=i z%D{2fFGrG8bX&nG44kI6H*=^SVsP88F(|akMqp}B%^x~5F1{o^Ru(6H(1X-c&o=pZ+6jOU;O{`D=amk@Hu9uJjCNJ-hO zhkV_S4|4Tc&>5fp&f>QC0=Kv+A&`s8!_WUM?DUEKTgSIg{`Tw}*mx-B7M9q06reS_ zCzI_Q=Hns3XXNoRxZh{+;$xQfO&~SEieo?E+qF2XIB?R6h2Kgp?dJSm#V^$ke|8+3 zr&E5NPU`n?>iJCZF(mJ$UT~vK4}YOmKAygX>eDz1mGjJ+>}%2?k&Z(i)e^(0(7JJw z$+#RW%AzO2f#Yr$8s8GHi*l_wN3Gr*GfYO-nnrz>ORDk#$y_xxZUl-2fQ3bJ+a z#2$FQx&H8T(K+SK&g1bVouQfZoWp>PPqiQ_)wrt))bz%3chPks+1&KYBN5 z9ugAW>zt}zc_AlLaLgg7Iqvd7ivH4cM3)$<>>m&e=|yL4(W z@~nFgDKRT%(p^#FUiiw&HkGRytwk>)A{qY0tF?91us=6hrB&23^rS~bjruAXuck3w z^xh3Lr$Ak^=o91kO}(mHa`)`$e*ny26D0yhA?|b{4@`8)qM_l0#{#SmXR@{Nvo;f-df1EyqKtJn1X;vgo!XNP z&p32mcI??@3Psuh8hfyl0iJW-X?dM4&`2!Sdilu?!k-U|b}{PRsq-V_+fT6Q@yr;Z zvpamb=T7R^=mq3i6ogvp?4Yg#A>;Ag5?>(X)RPwP^iq2n8C8V0i zqZt|Hasoo!NC*J-egMB{42bI?gH}Mm8u)t!WL5xwAJu@!Ai&lQ^CEsz;Xl+E*TP^R z1#ZLAz#vKl!kq6ajj{pX$LkCjOW7MBE?0f=6E8LgSBo8}gQyT0?FK3tnU$&jGklN9 z7aI=+1O&p_Lt}n`59Dg-VVbQ+*}l=$_&`NbzZ=LU$i*NjO|*iq-p!?CVbep+aX!cykO0+@ZeZt00I1GSIS-zajFN z{qxy^(nm4ERCq>UrRkYGFrGwnsK@<|_`Vc-}d%r zq^QglatZ5hYtifohYz3lCMG1T9xeR-{hOTvA3qFPtzl!p)RQ{+r-}X>tR&l8lNej zdgS@#Oy6{yxtzb-xRbEnBz-VKeIHU@IbyyWbm8*rZG=7C>2+^#c8TM#Bh+}d&YRKM z`7&!FZ9#rfPzI+Q-E)s%T~DpBz8!ZF_w^R1Cd(e4&zX*h zC^AcL+>w}W77Ph%15$==7B7}$+9=9&?w6i3Ll#GsLCzTOnfZa3{78^rB-3Mu?B2tZ zc#;jkG_I~Jqel#-8n`x3=*Z8tILGuWb<5^PpjX-H41TI`MJ?lEd0iFk_bG zr+RyMZc^}ue9tsRr)JwXeoKj6s|WM)MP`1yJkCMiW0J}K9hq9| zGBcil>NL9m`&nIa^8>eah8Cnwz@9a~aYKyH3$Z7N52GaW>q=ImeJf4IBk}8fTc@iM zd)^IEZui6Oy+Kd>{Sjoj2A{E$xoBUFz^`Xd^eR!W*J&U}bEgEtuFMa6)6w?8S6@rk z0~&y6X1LUo9pE%XrDj75_?n0`FE4NLjJ1diOwGhQ42x>@`Q5Y``JkV+`*g|S5HF@G zZKu>+%&Nskx%i|DO6>OzvYa_C@$R+q{s@lXO)nz3lnAA8G?zn z60cpDk3Dzlbx~v&%H*8yw99S*wbwY?nOc;xT(6uLIyf~v=Xn?;F?y-7TqEIwIbFYq>d6r_u+PTh zc8%OAiPYLKZwppYlGp0D7;tm+u3dk#RB?>ze99pKa$*J2=77ApINg^kiHVfOmxp+h z=qtF&Mn=>h?9tVn)R8dI4bz0kD#kFeIr8HpeFCY0g*9K`y84w4ZYQ_6HZc+lvhE2& Z#l~-gU?(xakM})Llv9(fkT&`FzW@#zhVuXb literal 0 HcmV?d00001 diff --git a/modules/hdf/doc/pics/two_channels.png b/modules/hdf/doc/pics/two_channels.png new file mode 100644 index 0000000000000000000000000000000000000000..bba6e52711c04411d97048f1ddcf41db77a06c6b GIT binary patch literal 31100 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�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

TB+}`N^31Wv&74U3V3enl`zZ0;qR56 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 - +#include namespace cv { @@ -50,7 +50,7 @@ using namespace std; /** @brief Hierarchical Data Format version 5 interface. -Notice that module is compiled only when hdf5 is correctly installed. +Notice that this module is compiled only when hdf5 is correctly installed. */ class CV_EXPORTS_W HDF5 @@ -59,7 +59,11 @@ public: CV_WRAP enum { - H5_UNLIMITED = -1, H5_NONE = -1, H5_GETDIMS = 100, H5_GETMAXDIMS = 101, H5_GETCHUNKDIMS = 102, + H5_UNLIMITED = -1, //!< The dimension size is unlimited, @sa dscreate() + H5_NONE = -1, //!< No compression, @sa dscreate() + H5_GETDIMS = 100, //!< Get the dimension information of a dataset. @sa dsgetsize() + H5_GETMAXDIMS = 101, //!< Get the maximum dimension information of a dataset. @sa dsgetsize() + H5_GETCHUNKDIMS = 102, //!< Get the chunk sizes of a dataset. @sa dsgetsize() }; virtual ~HDF5() {} @@ -71,63 +75,57 @@ public: /** @brief Create a group. @param grlabel specify the hdf5 group label. - Create a hdf5 group. + Create a hdf5 group with default properties. The group is closed automatically after creation. - @note Groups are useful for better organise multiple datasets. It is possible to create subgroups within any group. - Existence of a particular group can be checked using hlexists(). In case of subgroups label would be e.g: 'Group1/SubGroup1' - where SubGroup1 is within the root group Group1. + @note Groups are useful for better organising multiple datasets. It is possible to create subgroups within any group. + Existence of a particular group can be checked using hlexists(). In case of subgroups, a label would be e.g: 'Group1/SubGroup1' + where SubGroup1 is within the root group Group1. Before creating a subgroup, its parent group MUST be created. - - In this example Group1 will have one subgrup labeled SubGroup1: - @code{.cpp} - // open / autocreate hdf5 file - cv::Ptr h5io = cv::hdf::open( "mytest.h5" ); - // create Group1 if does not exists - if ( ! h5io->hlexists( "Group1" ) ) - h5io->grcreate( "Group1" ); - else - printf("Group1 already created, skipping\n" ); - // create SubGroup1 if does not exists - if ( ! h5io->hlexists( "Group1/SubGroup1" ) ) - h5io->grcreate( "Group1/SubGroup1" ); - else - printf("SubGroup1 already created, skipping\n" ); - // release - h5io->close(); - @endcode + - In this example, Group1 will have one subgroup called SubGroup1: - @note When a dataset is created with dscreate() or kpcreate() it can be created right within a group by specifying - full path within the label, in our example would be: 'Group1/SubGroup1/MyDataSet'. It is not thread safe. + @snippet samples/create_groups.cpp create_group + + The corresponding result visualized using the HDFView tool is + + ![Visualization of groups using the HDFView tool](pics/create_groups.png) + + @note When a dataset is created with dscreate() or kpcreate(), it can be created within a group by specifying the + full path within the label. In our example, it would be: 'Group1/SubGroup1/MyDataSet'. It is not thread safe. */ - CV_WRAP virtual void grcreate( String grlabel ) = 0; + CV_WRAP virtual void grcreate( const String& grlabel ) = 0; /** @brief Check if label exists or not. @param label specify the hdf5 dataset label. - Returns **true** if dataset exists, and **false** if does not. + Returns **true** if dataset exists, and **false** otherwise. @note Checks if dataset, group or other object type (hdf5 link) exists under the label name. It is thread safe. */ - CV_WRAP virtual bool hlexists( String label ) const = 0; + CV_WRAP virtual bool hlexists( const String& label ) const = 0; /* @overload */ CV_WRAP virtual void dscreate( const int rows, const int cols, const int type, - String dslabel ) const = 0; + const String& dslabel ) const = 0; /* @overload */ CV_WRAP virtual void dscreate( const int rows, const int cols, const int type, - String dslabel, const int compresslevel ) const = 0; + const String& dslabel, const int compresslevel ) const = 0; /* @overload */ CV_WRAP virtual void dscreate( const int rows, const int cols, const int type, - String dslabel, const int compresslevel, const vector& dims_chunks ) const = 0; + const String& dslabel, const int compresslevel, const vector& dims_chunks ) const = 0; /** @brief Create and allocate storage for two dimensional single or multi channel dataset. @param rows declare amount of rows - @param cols declare amount of cols - @param type type to be used - @param dslabel specify the hdf5 dataset label, any existing dataset with the same label will be overwritten. - @param compresslevel specify the compression level 0-9 to be used, H5_NONE is default and means no compression. - @param dims_chunks each array member specify chunking sizes to be used for block i/o, + @param cols declare amount of columns + @param type type to be used, e.g, CV_8UC3, CV_32FC1 and etc. + @param dslabel specify the hdf5 dataset label. Existing dataset label will cause an error. + @param compresslevel specify the compression level 0-9 to be used, H5_NONE is the default value and means no compression. + The value 0 also means no compression. + A value 9 indicating the best compression ration. Note + that a higher compression level indicates a higher computational cost. It relies + on GNU gzip for compression. + @param dims_chunks each array member specifies the chunking size to be used for block I/O, by default NULL means none at all. - @note If the dataset already exists an exception will be thrown. + @note If the dataset already exists, an exception will be thrown (CV_Error() is called). - Existence of the dataset can be checked using hlexists(), see in this example: @code{.cpp} @@ -143,9 +141,9 @@ public: @endcode @note Activating compression requires internal chunking. Chunking can significantly improve access - speed booth at read or write time especially for windowed access logic that shifts offset inside dataset. - If no custom chunking is specified default one will be invoked by the size of **whole** dataset - as single big chunk of data. + speed both at read and write time, especially for windowed access logic that shifts offset inside dataset. + If no custom chunking is specified, the default one will be invoked by the size of the **whole** dataset + as a single big chunk of data. - See example of level 9 compression using internal default chunking: @code{.cpp} @@ -160,11 +158,11 @@ public: h5io->close(); @endcode - @note A value of H5_UNLIMITED for **rows** or **cols** or booth means **unlimited** data on the specified dimension, - thus is possible to expand anytime such dataset on row, col or booth directions. Presence of H5_UNLIMITED on any - dimension **require** to define custom chunking. No default chunking will be defined in unlimited scenario since - default size on that dimension will be zero, and will grow once dataset is written. Writing into dataset that have - H5_UNLIMITED on some of its dimension requires dsinsert() that allow growth on unlimited dimension instead of dswrite() + @note A value of H5_UNLIMITED for **rows** or **cols** or both means **unlimited** data on the specified dimension, + thus, it is possible to expand anytime such a dataset on row, col or on both directions. Presence of H5_UNLIMITED on any + dimension **requires** to define custom chunking. No default chunking will be defined in the unlimited scenario since + default size on that dimension will be zero, and will grow once dataset is written. Writing into a dataset that has + H5_UNLIMITED on some of its dimensions requires dsinsert() that allows growth on unlimited dimensions, instead of dswrite() that allows to write only in predefined data space. - Example below shows no compression but unlimited dimension on cols using 100x100 internal chunking: @@ -178,31 +176,35 @@ public: h5io->close(); @endcode - @note It is **not** thread safe, it must be called only once at dataset creation otherwise exception will occur. - Multiple datasets inside single hdf5 file is allowed. + @note It is **not** thread safe, it must be called only once at dataset creation, otherwise an exception will occur. + Multiple datasets inside a single hdf5 file are allowed. */ CV_WRAP virtual void dscreate( const int rows, const int cols, const int type, - String dslabel, const int compresslevel, const int* dims_chunks ) const = 0; + const String& dslabel, const int compresslevel, const int* dims_chunks ) const = 0; /* @overload */ CV_WRAP virtual void dscreate( const int n_dims, const int* sizes, const int type, - String dslabel ) const = 0; + const String& dslabel ) const = 0; /* @overload */ CV_WRAP virtual void dscreate( const int n_dims, const int* sizes, const int type, - String dslabel, const int compresslevel ) const = 0; + const String& dslabel, const int compresslevel ) const = 0; /* @overload */ CV_WRAP virtual void dscreate( const vector& sizes, const int type, - String dslabel, const int compresslevel = HDF5::H5_NONE, + const String& dslabel, const int compresslevel = HDF5::H5_NONE, const vector& dims_chunks = vector() ) const = 0; - /** @brief Create and allocate storage for n-dimensional dataset, single or mutichannel type. + /** @brief Create and allocate storage for n-dimensional dataset, single or multichannel type. @param n_dims declare number of dimensions @param sizes array containing sizes for each dimensions - @param type type to be used - @param dslabel specify the hdf5 dataset label, any existing dataset with the same label will be overwritten. - @param compresslevel specify the compression level 0-9 to be used, H5_NONE is default and means no compression. - @param dims_chunks each array member specify chunking sizes to be used for block i/o, + @param type type to be used, e.g., CV_8UC3, CV_32FC1, etc. + @param dslabel specify the hdf5 dataset label. Existing dataset label will cause an error. + @param compresslevel specify the compression level 0-9 to be used, H5_NONE is the default value and means no compression. + The value 0 also means no compression. + A value 9 indicating the best compression ration. Note + that a higher compression level indicates a higher computational cost. It relies + on GNU gzip for compression. + @param dims_chunks each array member specifies chunking sizes to be used for block I/O, by default NULL means none at all. - @note If the dataset already exists an exception will be thrown. Existence of the dataset can be checked + @note If the dataset already exists, an exception will be thrown. Existence of the dataset can be checked using hlexists(). - See example below that creates a 6 dimensional storage space: @@ -221,12 +223,12 @@ public: @endcode @note Activating compression requires internal chunking. Chunking can significantly improve access - speed booth at read or write time especially for windowed access logic that shifts offset inside dataset. - If no custom chunking is specified default one will be invoked by the size of **whole** dataset + speed both at read and write time, especially for windowed access logic that shifts offset inside dataset. + If no custom chunking is specified, the default one will be invoked by the size of **whole** dataset as single big chunk of data. - - See example of level 0 compression (shallow) using chunking against first - dimension, thus storage will consists by 100 chunks of data: + - See example of level 0 compression (shallow) using chunking against the first + dimension, thus storage will consists of 100 chunks of data: @code{.cpp} // open / autocreate hdf5 file cv::Ptr h5io = cv::hdf::open( "mytest.h5" ); @@ -242,11 +244,11 @@ public: h5io->close(); @endcode - @note A value of H5_UNLIMITED inside the **sizes** array means **unlimited** data on that dimension, thus is + @note A value of H5_UNLIMITED inside the **sizes** array means **unlimited** data on that dimension, thus it is possible to expand anytime such dataset on those unlimited directions. Presence of H5_UNLIMITED on any dimension - **require** to define custom chunking. No default chunking will be defined in unlimited scenario since default size - on that dimension will be zero, and will grow once dataset is written. Writing into dataset that have H5_UNLIMITED on - some of its dimension requires dsinsert() instead of dswrite() that allow growth on unlimited dimension instead of + **requires** to define custom chunking. No default chunking will be defined in unlimited scenario since the default size + on that dimension will be zero, and will grow once dataset is written. Writing into dataset that has H5_UNLIMITED on + some of its dimension requires dsinsert() instead of dswrite() that allows growth on unlimited dimension instead of dswrite() that allows to write only in predefined data space. - Example below shows a 3 dimensional dataset using no compression with all unlimited sizes and one unit chunking: @@ -262,11 +264,12 @@ public: @endcode */ CV_WRAP virtual void dscreate( const int n_dims, const int* sizes, const int type, - String dslabel, const int compresslevel, const int* dims_chunks ) const = 0; + const String& dslabel, const int compresslevel, const int* dims_chunks ) const = 0; /** @brief Fetch dataset sizes @param dslabel specify the hdf5 dataset label to be measured. - @param dims_flag will fetch dataset dimensions on H5_GETDIMS, and dataset maximum dimensions on H5_GETMAXDIMS. + @param dims_flag will fetch dataset dimensions on H5_GETDIMS, dataset maximum dimensions on H5_GETMAXDIMS, + and chunk sizes on H5_GETCHUNKDIMS. Returns vector object containing sizes of dataset on each dimensions. @@ -278,7 +281,7 @@ public: return the dimension of chunk if dataset was created with chunking options otherwise returned vector size will be zero. */ - CV_WRAP virtual vector dsgetsize( String dslabel, int dims_flag = HDF5::H5_GETDIMS ) const = 0; + CV_WRAP virtual vector dsgetsize( const String& dslabel, int dims_flag = HDF5::H5_GETDIMS ) const = 0; /** @brief Fetch dataset type @param dslabel specify the hdf5 dataset label to be checked. @@ -289,15 +292,15 @@ public: @note Result can be parsed with CV_MAT_CN() to obtain amount of channels and CV_MAT_DEPTH() to obtain native cvdata type. It is thread safe. */ - CV_WRAP virtual int dsgettype( String dslabel ) const = 0; + CV_WRAP virtual int dsgettype( const String& dslabel ) const = 0; /* @overload */ - CV_WRAP virtual void dswrite( InputArray Array, String dslabel ) const = 0; + CV_WRAP virtual void dswrite( InputArray Array, const String& dslabel ) const = 0; /* @overload */ - CV_WRAP virtual void dswrite( InputArray Array, String dslabel, + CV_WRAP virtual void dswrite( InputArray Array, const String& dslabel, const int* dims_offset ) const = 0; /* @overload */ - CV_WRAP virtual void dswrite( InputArray Array, String dslabel, + CV_WRAP virtual void dswrite( InputArray Array, const String& dslabel, const vector& dims_offset, const vector& dims_counts = vector() ) const = 0; /** @brief Write or overwrite a Mat object into specified dataset of hdf5 file. @@ -305,16 +308,16 @@ public: @param dslabel specify the target hdf5 dataset label. @param dims_offset each array member specify the offset location over dataset's each dimensions from where InputArray will be (over)written into dataset. - @param dims_counts each array member specify the amount of data over dataset's + @param dims_counts each array member specifies the amount of data over dataset's each dimensions from InputArray that will be written into dataset. Writes Mat object into targeted dataset. @note If dataset is not created and does not exist it will be created **automatically**. Only Mat is supported and - it must to be **continuous**. It is thread safe but it is recommended that writes to happen over separate non overlapping - regions. Multiple datasets can be written inside single hdf5 file. + it must be **continuous**. It is thread safe but it is recommended that writes to happen over separate non-overlapping + regions. Multiple datasets can be written inside a single hdf5 file. - - Example below writes a 100x100 CV_64FC2 matrix into a dataset. No dataset precreation required. If routine + - Example below writes a 100x100 CV_64FC2 matrix into a dataset. No dataset pre-creation required. If routine is called multiple times dataset will be just overwritten: @code{.cpp} // dual channel hilbert matrix @@ -362,19 +365,19 @@ public: h5io->close(); @endcode */ - CV_WRAP virtual void dswrite( InputArray Array, String dslabel, + CV_WRAP virtual void dswrite( InputArray Array, const String& dslabel, const int* dims_offset, const int* dims_counts ) const = 0; /* @overload */ - CV_WRAP virtual void dsinsert( InputArray Array, String dslabel ) const = 0; + CV_WRAP virtual void dsinsert( InputArray Array, const String& dslabel ) const = 0; /* @overload */ CV_WRAP virtual void dsinsert( InputArray Array, - String dslabel, const int* dims_offset ) const = 0; + const String& dslabel, const int* dims_offset ) const = 0; /* @overload */ CV_WRAP virtual void dsinsert( InputArray Array, - String dslabel, const vector& dims_offset, + const String& dslabel, const vector& dims_offset, const vector& dims_counts = vector() ) const = 0; - /** @brief Insert or overwrite a Mat object into specified dataset and autoexpand dataset size if **unlimited** property allows. + /** @brief Insert or overwrite a Mat object into specified dataset and auto expand dataset size if **unlimited** property allows. @param Array specify Mat data array to be written. @param dslabel specify the target hdf5 dataset label. @param dims_offset each array member specify the offset location @@ -384,13 +387,13 @@ public: Writes Mat object into targeted dataset and **autoexpand** dataset dimension if allowed. - @note Unlike dswrite(), datasets are **not** created **automatically**. Only Mat is supported and it must to be **continuous**. - If dsinsert() happen over outer regions of dataset dimensions and on that dimension of dataset is in **unlimited** mode then + @note Unlike dswrite(), datasets are **not** created **automatically**. Only Mat is supported and it must be **continuous**. + If dsinsert() happens over outer regions of dataset dimensions and on that dimension of dataset is in **unlimited** mode then dataset is expanded, otherwise exception is thrown. To create datasets with **unlimited** property on specific or more dimensions see dscreate() and the optional H5_UNLIMITED flag at creation time. It is not thread safe over same dataset - but multiple datasets can be merged inside single hdf5 file. + but multiple datasets can be merged inside a single hdf5 file. - - Example below creates **unlimited** rows x 100 cols and expand rows 5 times with dsinsert() using single 100x100 CV_64FC2 + - Example below creates **unlimited** rows x 100 cols and expands rows 5 times with dsinsert() using single 100x100 CV_64FC2 over the dataset. Final size will have 5x100 rows and 100 cols, reflecting H matrix five times over row's span. Chunks size is 100x100 just optimized against the H matrix size having compression disabled. If routine is called multiple times dataset will be just overwritten: @@ -421,17 +424,17 @@ public: h5io->close(); @endcode */ - CV_WRAP virtual void dsinsert( InputArray Array, String dslabel, + CV_WRAP virtual void dsinsert( InputArray Array, const String& dslabel, const int* dims_offset, const int* dims_counts ) const = 0; /* @overload */ - CV_WRAP virtual void dsread( OutputArray Array, String dslabel ) const = 0; + CV_WRAP virtual void dsread( OutputArray Array, const String& dslabel ) const = 0; /* @overload */ CV_WRAP virtual void dsread( OutputArray Array, - String dslabel, const int* dims_offset ) const = 0; + const String& dslabel, const int* dims_offset ) const = 0; /* @overload */ - CV_WRAP virtual void dsread( OutputArray Array, String dslabel, + CV_WRAP virtual void dsread( OutputArray Array, const String& dslabel, const vector& dims_offset, const vector& dims_counts = vector() ) const = 0; /** @brief Read specific dataset from hdf5 file into Mat object. @@ -473,7 +476,7 @@ public: h5io->close(); @endcode */ - CV_WRAP virtual void dsread( OutputArray Array, String dslabel, + CV_WRAP virtual void dsread( OutputArray Array, const String& dslabel, const int* dims_offset, const int* dims_counts ) const = 0; /** @brief Fetch keypoint dataset size @@ -489,13 +492,13 @@ public: exception. The H5_GETCHUNKDIMS will return the dimension of chunk if dataset was created with chunking options otherwise returned vector size will be zero. */ - CV_WRAP virtual int kpgetsize( String kplabel, int dims_flag = HDF5::H5_GETDIMS ) const = 0; + CV_WRAP virtual int kpgetsize( const String& kplabel, int dims_flag = HDF5::H5_GETDIMS ) const = 0; /** @brief Create and allocate special storage for cv::KeyPoint dataset. @param size declare fixed number of KeyPoints @param kplabel specify the hdf5 dataset label, any existing dataset with the same label will be overwritten. @param compresslevel specify the compression level 0-9 to be used, H5_NONE is default and means no compression. - @param chunks each array member specify chunking sizes to be used for block i/o, + @param chunks each array member specifies chunking sizes to be used for block I/O, H5_NONE is default and means no compression. @note If the dataset already exists an exception will be thrown. Existence of the dataset can be checked using hlexists(). @@ -526,7 +529,7 @@ public: printf("DS already created, skipping\n" ); @endcode */ - virtual void kpcreate( const int size, String kplabel, + virtual void kpcreate( const int size, const String& kplabel, const int compresslevel = H5_NONE, const int chunks = H5_NONE ) const = 0; /** @brief Write or overwrite list of KeyPoint into specified dataset of hdf5 file. @@ -579,7 +582,7 @@ public: h5io->close(); @endcode */ - virtual void kpwrite( const vector keypoints, String kplabel, + virtual void kpwrite( const vector keypoints, const String& kplabel, const int offset = H5_NONE, const int counts = H5_NONE ) const = 0; /** @brief Insert or overwrite list of KeyPoint into specified dataset and autoexpand dataset size if **unlimited** property allows. @@ -614,7 +617,7 @@ public: h5io->close(); @endcode */ - virtual void kpinsert( const vector keypoints, String kplabel, + virtual void kpinsert( const vector keypoints, const String& kplabel, const int offset = H5_NONE, const int counts = H5_NONE ) const = 0; /** @brief Read specific keypoint dataset from hdf5 file into vector object. @@ -652,7 +655,7 @@ public: h5io->close(); @endcode */ - virtual void kpread( vector& keypoints, String kplabel, + virtual void kpread( vector& keypoints, const String& kplabel, const int offset = H5_NONE, const int counts = H5_NONE ) const = 0; }; @@ -660,18 +663,20 @@ public: /** @brief Open or create hdf5 file @param HDF5Filename specify the HDF5 filename. - Returns pointer to the hdf5 object class + Returns a pointer to the hdf5 object class - @note If hdf5 file does not exist it will be created. Any operations except dscreate() functions on object - will be thread safe. Multiple datasets can be created inside single hdf5 file, and can be accessed - from same hdf5 object from multiple instances as long read or write operations are done over + @note If the specified file does not exist, it will be created using default properties. + Otherwise, it is opened in read and write mode with default access properties. + Any operations except dscreate() functions on object + will be thread safe. Multiple datasets can be created inside a single hdf5 file, and can be accessed + from the same hdf5 object from multiple instances as long read or write operations are done over non-overlapping regions of dataset. Single hdf5 file also can be opened by multiple instances, - reads and writes can be instantiated at the same time as long non-overlapping regions are involved. Object + reads and writes can be instantiated at the same time as long as non-overlapping regions are involved. Object is released using close(). - - Example below open and then release the file. + - Example below opens and then releases the file. @code{.cpp} - // open / autocreate hdf5 file + // open / auto create hdf5 file cv::Ptr h5io = cv::hdf::open( "mytest.h5" ); // ... // release @@ -698,7 +703,7 @@ public: } @endcode */ - CV_EXPORTS_W Ptr open( String HDF5Filename ); + CV_EXPORTS_W Ptr open( const String& HDF5Filename ); //! @} diff --git a/modules/hdf/samples/create_groups.cpp b/modules/hdf/samples/create_groups.cpp new file mode 100644 index 000000000..041e98593 --- /dev/null +++ b/modules/hdf/samples/create_groups.cpp @@ -0,0 +1,60 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/** + * @file create_groups.cpp + * @author Fangjun Kuang + * @date December 2017 + * + * @brief It demonstrates how to create HDF5 groups and subgroups. + * + * Basic steps: + * 1. Use hdf::open to create a HDF5 file + * 2. Use HDF5::hlexists to check if a group exists or not + * 3. Use HDF5::grcreate to create a group by specifying its name + * 4. Use hdf::close to close a HDF5 file after modifying it + * + */ + +//! [tutorial] +#include + +#include +#include + +using namespace cv; + +int main() +{ + //! [create_group] + + //! [tutorial_create_file] + Ptr h5io = hdf::open("mytest.h5"); + //! [tutorial_create_file] + + //! [tutorial_create_group] + // "/" means the root group, which is always present + if (!h5io->hlexists("/Group1")) + h5io->grcreate("/Group1"); + else + std::cout << "/Group1 has already been created, skip it.\n"; + //! [tutorial_create_group] + + //! [tutorial_create_subgroup] + // Note that Group1 has been created above, otherwise exception will occur + if (!h5io->hlexists("/Group1/SubGroup1")) + h5io->grcreate("/Group1/SubGroup1"); + else + std::cout << "/Group1/SubGroup1 has already been created, skip it.\n"; + //! [tutorial_create_subgroup] + + //! [tutorial_close_file] + h5io->close(); + //! [tutorial_close_file] + + //! [create_group] + + return 0; +} +//! [tutorial] diff --git a/modules/hdf/samples/create_read_write_datasets.cpp b/modules/hdf/samples/create_read_write_datasets.cpp new file mode 100644 index 000000000..9893a9b60 --- /dev/null +++ b/modules/hdf/samples/create_read_write_datasets.cpp @@ -0,0 +1,135 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/** + * @file create_read_write.cpp + * @author Fangjun Kuang + * @date December 2017 + * + * @brief It demonstrates how to create a dataset, how + * to write a cv::Mat to the dataset and how to + * read a cv::Mat from it. + * + */ + +//! [tutorial] +#include + +#include +#include + +using namespace cv; + +void write_root_group_single_channel() +{ + String filename = "root_group_single_channel.h5"; + String dataset_name = "/single"; // Note that it is a child of the root group / + + // prepare data + Mat data; + data = (cv::Mat_(2, 3) << 0, 1, 2, 3, 4, 5, 6); + + //! [tutorial_open_file] + Ptr h5io = hdf::open(filename); + //! [tutorial_open_file] + + //! [tutorial_write_root_single_channel] + // write data to the given dataset + // the dataset "/single" is created automatically, since it is a child of the root + h5io->dswrite(data, dataset_name); + //! [tutorial_write_root_single_channel] + + //! [tutorial_read_dataset] + Mat expected; + h5io->dsread(expected, dataset_name); + //! [tutorial_read_dataset] + + //! [tutorial_check_result] + double diff = norm(data - expected); + CV_Assert(abs(diff) < 1e-10); + //! [tutorial_check_result] + + h5io->close(); +} + +void write_single_channel() +{ + String filename = "single_channel.h5"; + String parent_name = "/data"; + String dataset_name = parent_name + "/single"; + + // prepare data + Mat data; + data = (cv::Mat_(2, 3) << 0, 1, 2, 3, 4, 5); + + Ptr h5io = hdf::open(filename); + + //! [tutorial_create_dataset] + // first we need to create the parent group + if (!h5io->hlexists(parent_name)) h5io->grcreate(parent_name); + + // create the dataset if it not exists + if (!h5io->hlexists(dataset_name)) h5io->dscreate(data.rows, data.cols, data.type(), dataset_name); + //! [tutorial_create_dataset] + + // the following is the same with the above function write_root_group_single_channel() + + h5io->dswrite(data, dataset_name); + + Mat expected; + h5io->dsread(expected, dataset_name); + + double diff = norm(data - expected); + CV_Assert(abs(diff) < 1e-10); + + h5io->close(); +} + +/* + * creating, reading and writing multiple-channel matrices + * are the same with single channel matrices + */ +void write_multiple_channels() +{ + String filename = "two_channels.h5"; + String parent_name = "/data"; + String dataset_name = parent_name + "/two_channels"; + + // prepare data + Mat data(2, 3, CV_32SC2); + for (size_t i = 0; i < data.total()*data.channels(); i++) + ((int*) data.data)[i] = (int)i; + + Ptr h5io = hdf::open(filename); + + // first we need to create the parent group + if (!h5io->hlexists(parent_name)) h5io->grcreate(parent_name); + + // create the dataset if it not exists + if (!h5io->hlexists(dataset_name)) h5io->dscreate(data.rows, data.cols, data.type(), dataset_name); + + // the following is the same with the above function write_root_group_single_channel() + + h5io->dswrite(data, dataset_name); + + Mat expected; + h5io->dsread(expected, dataset_name); + + double diff = norm(data - expected); + CV_Assert(abs(diff) < 1e-10); + + h5io->close(); +} + +int main() +{ + write_root_group_single_channel(); + + write_single_channel(); + + write_multiple_channels(); + + return 0; +} +//! [tutorial] diff --git a/modules/hdf/src/hdf5.cpp b/modules/hdf/src/hdf5.cpp index 1886574e3..35cf205db 100644 --- a/modules/hdf/src/hdf5.cpp +++ b/modules/hdf/src/hdf5.cpp @@ -47,7 +47,7 @@ class HDF5Impl : public HDF5 { public: - HDF5Impl( String HDF5Filename ); + HDF5Impl( const String& HDF5Filename ); virtual ~HDF5Impl() { close(); }; @@ -59,96 +59,96 @@ public: */ // check if object / link exists - virtual bool hlexists( String label ) const; + virtual bool hlexists( const String& label ) const; /* * h5 group */ // create a group - virtual void grcreate( String grlabel ); + virtual void grcreate( const String& grlabel ); /* * cv::Mat */ // get sizes of dataset - virtual vector dsgetsize( String dslabel, int dims_flag = H5_GETDIMS ) const; + virtual vector dsgetsize( const String& dslabel, int dims_flag = H5_GETDIMS ) const; /* get data type of dataset */ - virtual int dsgettype( String dslabel ) const; + virtual int dsgettype( const String& dslabel ) const; // overload dscreate() #1 - virtual void dscreate( const int rows, const int cols, const int type, String dslabel ) const; + virtual void dscreate( const int rows, const int cols, const int type, const String& dslabel ) const; // overload dscreate() #2 - virtual void dscreate( const int rows, const int cols, const int type, String dslabel, + virtual void dscreate( const int rows, const int cols, const int type, const String& dslabel, const int compresslevel ) const; // overload dscreate() #3 - virtual void dscreate( const int rows, const int cols, const int type, String dslabel, + virtual void dscreate( const int rows, const int cols, const int type, const String& dslabel, const int compresslevel, const vector& dims_chunks ) const; /* create two dimensional single or mutichannel dataset */ - virtual void dscreate( const int rows, const int cols, const int type, String dslabel, + virtual void dscreate( const int rows, const int cols, const int type, const String& dslabel, const int compresslevel, const int* dims_chunks ) const; // overload dscreate() #1 virtual void dscreate( const int n_dims, const int* sizes, const int type, - String dslabel ) const; + const String& dslabel ) const; // overload dscreate() #2 virtual void dscreate( const int n_dims, const int* sizes, const int type, - String dslabel, const int compresslevel ) const; + const String& dslabel, const int compresslevel ) const; // overload dscreate() #3 - virtual void dscreate( const vector& sizes, const int type, String dslabel, + virtual void dscreate( const vector& sizes, const int type, const String& dslabel, const int compresslevel = H5_NONE, const vector& dims_chunks = vector() ) const; /* create n-dimensional single or mutichannel dataset */ virtual void dscreate( const int n_dims, const int* sizes, const int type, - String dslabel, const int compresslevel, const int* dims_chunks ) const; + const String& dslabel, const int compresslevel, const int* dims_chunks ) const; // overload dswrite() #1 - virtual void dswrite( InputArray Array, String dslabel ) const; + virtual void dswrite( InputArray Array, const String& dslabel ) const; // overload dswrite() #2 - virtual void dswrite( InputArray Array, String dslabel, const int* dims_offset ) const; + virtual void dswrite( InputArray Array, const String& dslabel, const int* dims_offset ) const; // overload dswrite() #3 - virtual void dswrite( InputArray Array, String dslabel, const vector& dims_offset, + virtual void dswrite( InputArray Array, const String& dslabel, const vector& dims_offset, const vector& dims_counts = vector() ) const; /* write into dataset */ - virtual void dswrite( InputArray Array, String dslabel, + virtual void dswrite( InputArray Array, const String& dslabel, const int* dims_offset, const int* dims_counts ) const; // overload dsinsert() #1 - virtual void dsinsert( InputArray Array, String dslabel ) const; + virtual void dsinsert( InputArray Array, const String& dslabel ) const; // overload dsinsert() #2 - virtual void dsinsert( InputArray Array, String dslabel, const int* dims_offset ) const; + virtual void dsinsert( InputArray Array, const String& dslabel, const int* dims_offset ) const; // overload dsinsert() #3 - virtual void dsinsert( InputArray Array, String dslabel, + virtual void dsinsert( InputArray Array, const String& dslabel, const vector& dims_offset, const vector& dims_counts = vector() ) const; /* append / merge into dataset */ - virtual void dsinsert( InputArray Array, String dslabel, + virtual void dsinsert( InputArray Array, const String& dslabel, const int* dims_offset = NULL, const int* dims_counts = NULL ) const; // overload dsread() #1 - virtual void dsread( OutputArray Array, String dslabel ) const; + virtual void dsread( OutputArray Array, const String& dslabel ) const; // overload dsread() #2 - virtual void dsread( OutputArray Array, String dslabel, const int* dims_offset ) const; + virtual void dsread( OutputArray Array, const String& dslabel, const int* dims_offset ) const; // overload dsread() #3 - virtual void dsread( OutputArray Array, String dslabel, + virtual void dsread( OutputArray Array, const String& dslabel, const vector& dims_offset, const vector& dims_counts = vector() ) const; // read from dataset - virtual void dsread( OutputArray Array, String dslabel, + virtual void dsread( OutputArray Array, const String& dslabel, const int* dims_offset, const int* dims_counts ) const; /* @@ -156,36 +156,36 @@ public: */ // get size of keypoints dataset - virtual int kpgetsize( String kplabel, int dims_flag = H5_GETDIMS ) const; + virtual int kpgetsize( const String& kplabel, int dims_flag = H5_GETDIMS ) const; // create KeyPoint structure - virtual void kpcreate( const int size, String kplabel, + virtual void kpcreate( const int size, const String& kplabel, const int compresslevel = H5_NONE, const int chunks = H5_NONE ) const; // write KeyPoint structures - virtual void kpwrite( const vector keypoints, String kplabel, + virtual void kpwrite( const vector keypoints, const String& kplabel, const int offset = H5_NONE, const int counts = H5_NONE ) const; // append / merge KeyPoint structures - virtual void kpinsert( const vector keypoints, String kplabel, + virtual void kpinsert( const vector keypoints, const String& kplabel, const int offset = H5_NONE, const int counts = H5_NONE ) const; // read KeyPoint structure - virtual void kpread( vector& keypoints, String kplabel, + virtual void kpread( vector& keypoints, const String& kplabel, const int offset = H5_NONE, const int counts = H5_NONE ) const; private: - // store filename + //! store filename String m_hdf5_filename; - // hdf5 file handler + //! hdf5 file handler hid_t m_h5_file_id; - // translate cvType -> h5Type + //! translate cvType -> h5Type inline hid_t GetH5type( int cvType ) const; - // translate h5Type -> cvType + //! translate h5Type -> cvType inline int GetCVtype( hid_t h5Type ) const; }; @@ -247,7 +247,7 @@ inline int HDF5Impl::GetCVtype( hid_t h5Type ) const return cvType; } -HDF5Impl::HDF5Impl( String _hdf5_filename ) +HDF5Impl::HDF5Impl( const String& _hdf5_filename ) : m_hdf5_filename( _hdf5_filename ) { // save old @@ -260,7 +260,7 @@ HDF5Impl::HDF5Impl( String _hdf5_filename ) // turn off error handling H5Eset_auto( stackid, NULL, NULL ); - // check HDF5 file presence (err supressed) + // check HDF5 file presence (err suppressed) htri_t check = H5Fis_hdf5( m_hdf5_filename.c_str() ); // restore previous error handler @@ -290,7 +290,7 @@ void HDF5Impl::close() * h5 generic */ -bool HDF5Impl::hlexists( String label ) const +bool HDF5Impl::hlexists( const String& label ) const { bool exists = false; @@ -306,7 +306,7 @@ bool HDF5Impl::hlexists( String label ) const * h5 group */ -void HDF5Impl::grcreate( String grlabel ) +void HDF5Impl::grcreate( const String& grlabel ) { hid_t gid = H5Gcreate( m_h5_file_id, grlabel.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); @@ -317,7 +317,7 @@ void HDF5Impl::grcreate( String grlabel ) * cv:Mat */ -vector HDF5Impl::dsgetsize( String dslabel, int dims_flag ) const +vector HDF5Impl::dsgetsize( const String& dslabel, int dims_flag ) const { // open dataset hid_t dsdata = H5Dopen( m_h5_file_id, dslabel.c_str(), H5P_DEFAULT ); @@ -372,7 +372,7 @@ vector HDF5Impl::dsgetsize( String dslabel, int dims_flag ) const return SizeVect; } -int HDF5Impl::dsgettype( String dslabel ) const +int HDF5Impl::dsgettype( const String& dslabel ) const { hid_t h5type; @@ -408,7 +408,7 @@ int HDF5Impl::dsgettype( String dslabel ) const // overload void HDF5Impl::dscreate( const int rows, const int cols, const int type, - String dslabel ) const + const String& dslabel ) const { // dataset dims int dsizes[2] = { rows, cols }; @@ -419,7 +419,7 @@ void HDF5Impl::dscreate( const int rows, const int cols, const int type, // overload void HDF5Impl::dscreate( const int rows, const int cols, const int type, - String dslabel, const int compresslevel ) const + const String& dslabel, const int compresslevel ) const { // dataset dims int dsizes[2] = { rows, cols }; @@ -430,7 +430,7 @@ void HDF5Impl::dscreate( const int rows, const int cols, const int type, // overload void HDF5Impl::dscreate( const int rows, const int cols, const int type, - String dslabel, const int compresslevel, + const String& dslabel, const int compresslevel, const vector& dims_chunks ) const { CV_Assert( dims_chunks.empty() || dims_chunks.size() == 2 ); @@ -438,7 +438,7 @@ void HDF5Impl::dscreate( const int rows, const int cols, const int type, } void HDF5Impl::dscreate( const int rows, const int cols, const int type, - String dslabel, const int compresslevel, const int* dims_chunks ) const + const String& dslabel, const int compresslevel, const int* dims_chunks ) const { // dataset dims int dsizes[2] = { rows, cols }; @@ -449,21 +449,21 @@ void HDF5Impl::dscreate( const int rows, const int cols, const int type, // overload void HDF5Impl::dscreate( const int n_dims, const int* sizes, const int type, - String dslabel ) const + const String& dslabel ) const { dscreate( n_dims, sizes, type, dslabel, H5_NONE, NULL ); } // overload void HDF5Impl::dscreate( const int n_dims, const int* sizes, const int type, - String dslabel, const int compresslevel ) const + const String& dslabel, const int compresslevel ) const { dscreate( n_dims, sizes, type, dslabel, compresslevel, NULL ); } // overload void HDF5Impl::dscreate( const vector& sizes, const int type, - String dslabel, const int compresslevel, + const String& dslabel, const int compresslevel, const vector& dims_chunks ) const { CV_Assert( dims_chunks.empty() || dims_chunks.size() == sizes.size() ); @@ -473,7 +473,7 @@ void HDF5Impl::dscreate( const vector& sizes, const int type, } void HDF5Impl::dscreate( const int n_dims, const int* sizes, const int type, - String dslabel, const int compresslevel, const int* dims_chunks ) const + const String& dslabel, const int compresslevel, const int* dims_chunks ) const { // compress valid H5_NONE, 0-9 CV_Assert( compresslevel >= H5_NONE && compresslevel <= 9 ); @@ -552,27 +552,27 @@ void HDF5Impl::dscreate( const int n_dims, const int* sizes, const int type, } // overload -void HDF5Impl::dsread( OutputArray Array, String dslabel ) const +void HDF5Impl::dsread( OutputArray Array, const String& dslabel ) const { dsread( Array, dslabel, NULL, NULL ); } // overload -void HDF5Impl::dsread( OutputArray Array, String dslabel, +void HDF5Impl::dsread( OutputArray Array, const String& dslabel, const int* dims_offset ) const { dsread( Array, dslabel, dims_offset, NULL ); } // overload -void HDF5Impl::dsread( OutputArray Array, String dslabel, +void HDF5Impl::dsread( OutputArray Array, const String& dslabel, const vector& dims_offset, const vector& dims_counts ) const { dsread( Array, dslabel, &dims_offset[0], &dims_counts[0] ); } -void HDF5Impl::dsread( OutputArray Array, String dslabel, +void HDF5Impl::dsread( OutputArray Array, const String& dslabel, const int* dims_offset, const int* dims_counts ) const { // only Mat support @@ -672,25 +672,25 @@ void HDF5Impl::dsread( OutputArray Array, String dslabel, } // overload -void HDF5Impl::dswrite( InputArray Array, String dslabel ) const +void HDF5Impl::dswrite( InputArray Array, const String& dslabel ) const { dswrite( Array, dslabel, NULL, NULL ); } // overload -void HDF5Impl::dswrite( InputArray Array, String dslabel, +void HDF5Impl::dswrite( InputArray Array, const String& dslabel, const int* dims_offset ) const { dswrite( Array, dslabel, dims_offset, NULL ); } // overload -void HDF5Impl::dswrite( InputArray Array, String dslabel, +void HDF5Impl::dswrite( InputArray Array, const String& dslabel, const vector& dims_offset, const vector& dims_counts ) const { dswrite( Array, dslabel, &dims_offset[0], &dims_counts[0] ); } -void HDF5Impl::dswrite( InputArray Array, String dslabel, +void HDF5Impl::dswrite( InputArray Array, const String& dslabel, const int* dims_offset, const int* dims_counts ) const { // only Mat support @@ -715,6 +715,9 @@ void HDF5Impl::dswrite( InputArray Array, String dslabel, dsdims[d] = matrix.size[d]; } + // FixMe: If one of the groups the dataset belongs to does not exist, + // FixMe: dscreate() will fail! + // FixMe: It should be an error if the specified dataset has not been created instead of trying to create it // pre-create dataset if needed if ( hlexists( dslabel ) == false ) dscreate( n_dims, dsizes, matrix.type(), dslabel ); @@ -771,27 +774,27 @@ void HDF5Impl::dswrite( InputArray Array, String dslabel, } // overload -void HDF5Impl::dsinsert( InputArray Array, String dslabel ) const +void HDF5Impl::dsinsert( InputArray Array, const String& dslabel ) const { dsinsert( Array, dslabel, NULL, NULL ); } // overload -void HDF5Impl::dsinsert( InputArray Array, String dslabel, +void HDF5Impl::dsinsert( InputArray Array, const String& dslabel, const int* dims_offset ) const { dsinsert( Array, dslabel, dims_offset, NULL ); } // overload -void HDF5Impl::dsinsert( InputArray Array, String dslabel, +void HDF5Impl::dsinsert( InputArray Array, const String& dslabel, const vector& dims_offset, const vector& dims_counts ) const { dsinsert( Array, dslabel, &dims_offset[0], &dims_counts[0] ); } -void HDF5Impl::dsinsert( InputArray Array, String dslabel, +void HDF5Impl::dsinsert( InputArray Array, const String& dslabel, const int* dims_offset, const int* dims_counts ) const { // only Mat support @@ -859,7 +862,7 @@ void HDF5Impl::dsinsert( InputArray Array, String dslabel, // add offset if ( dims_offset != NULL ) nwdims[d] += dims_offset[d]; - // add counts or matrixsize + // add counts or matrix size if ( dims_counts != NULL ) nwdims[d] += dims_counts[d]; else @@ -910,7 +913,7 @@ void HDF5Impl::dsinsert( InputArray Array, String dslabel, * std::vector */ -int HDF5Impl::kpgetsize( String kplabel, int dims_flag ) const +int HDF5Impl::kpgetsize( const String& kplabel, int dims_flag ) const { vector sizes = dsgetsize( kplabel, dims_flag ); @@ -919,7 +922,7 @@ int HDF5Impl::kpgetsize( String kplabel, int dims_flag ) const return sizes[0]; } -void HDF5Impl::kpcreate( const int size, String kplabel, +void HDF5Impl::kpcreate( const int size, const String& kplabel, const int compresslevel, const int chunks ) const { // size valid @@ -992,7 +995,7 @@ void HDF5Impl::kpcreate( const int size, String kplabel, H5Sclose( dspace ); } -void HDF5Impl::kpwrite( const vector keypoints, String kplabel, +void HDF5Impl::kpwrite( const vector keypoints, const String& kplabel, const int offset, const int counts ) const { CV_Assert( keypoints.size() > 0 ); @@ -1048,7 +1051,7 @@ void HDF5Impl::kpwrite( const vector keypoints, String kplabel, H5Dclose( dsdata ); } -void HDF5Impl::kpinsert( const vector keypoints, String kplabel, +void HDF5Impl::kpinsert( const vector keypoints, const String& kplabel, const int offset, const int counts ) const { CV_Assert( keypoints.size() > 0 ); @@ -1132,7 +1135,7 @@ void HDF5Impl::kpinsert( const vector keypoints, String kplabel, H5Dclose( dsdata ); } -void HDF5Impl::kpread( vector& keypoints, String kplabel, +void HDF5Impl::kpread( vector& keypoints, const String& kplabel, const int offset, const int counts ) const { CV_Assert( keypoints.size() == 0 ); @@ -1193,7 +1196,7 @@ void HDF5Impl::kpread( vector& keypoints, String kplabel, H5Dclose( dsdata ); } -CV_EXPORTS Ptr open( String HDF5Filename ) +CV_EXPORTS Ptr open( const String& HDF5Filename ) { return makePtr( HDF5Filename ); } diff --git a/modules/hdf/test/test_hdf5.cpp b/modules/hdf/test/test_hdf5.cpp new file mode 100644 index 000000000..a9659f6c3 --- /dev/null +++ b/modules/hdf/test/test_hdf5.cpp @@ -0,0 +1,212 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/** + * @file test_hdf5.cpp + * @author Fangjun Kuang + * @date December 2017 + * + */ +#include // for remove() + +#include "test_precomp.hpp" +#include + +using namespace cv; + +struct HDF5_Test : public testing::Test +{ + virtual void SetUp() + { + m_filename = "test.h5"; + + // 0 1 2 + // 3 4 5 + m_single_channel.create(2, 3, CV_32F); + for (size_t i = 0; i < m_single_channel.total(); i++) + { + ((float*)m_single_channel.data)[i] = i; + } + + // 0 1 2 3 4 5 + // 6 7 8 9 10 11 + m_two_channels.create(2, 3, CV_32SC2); + for (size_t i = 0; i < m_two_channels.total()*m_two_channels.channels(); i++) + { + ((int*)m_two_channels.data)[i] = (int)i; + } + } + + //! Remove the hdf5 file + void reset() + { + remove(m_filename.c_str()); + } + + String m_filename; //!< filename for testing + Ptr m_hdf_io; //!< HDF5 file pointer + Mat m_single_channel; //!< single channel matrix for test + Mat m_two_channels; //!< two-channel matrix for test +}; + +TEST_F(HDF5_Test, create_a_single_group) +{ + reset(); + + String group_name = "parent"; + m_hdf_io = hdf::open(m_filename); + m_hdf_io->grcreate(group_name); + + EXPECT_EQ(m_hdf_io->hlexists(group_name), true); + EXPECT_EQ(m_hdf_io->hlexists("child"), false); + + m_hdf_io->close(); +} + + +TEST_F(HDF5_Test, create_a_child_group) +{ + reset(); + + String parent = "parent"; + String child = parent + "/child"; + m_hdf_io = hdf::open(m_filename); + m_hdf_io->grcreate(parent); + m_hdf_io->grcreate(child); + + EXPECT_EQ(m_hdf_io->hlexists(parent), true); + EXPECT_EQ(m_hdf_io->hlexists(child), true); + + m_hdf_io->close(); +} + +TEST_F(HDF5_Test, create_dataset) +{ + reset(); + + String dataset_single_channel = "/single"; + String dataset_two_channels = "/dual"; + + m_hdf_io = hdf::open(m_filename); + + m_hdf_io->dscreate(m_single_channel.rows, + m_single_channel.cols, + m_single_channel.type(), + dataset_single_channel); + + m_hdf_io->dscreate(m_two_channels.rows, + m_two_channels.cols, + m_two_channels.type(), + dataset_two_channels); + + EXPECT_EQ(m_hdf_io->hlexists(dataset_single_channel), true); + EXPECT_EQ(m_hdf_io->hlexists(dataset_two_channels), true); + + std::vector dims; + + dims = m_hdf_io->dsgetsize(dataset_single_channel, hdf::HDF5::H5_GETDIMS); + EXPECT_EQ(dims.size(), (size_t)2); + EXPECT_EQ(dims[0], m_single_channel.rows); + EXPECT_EQ(dims[1], m_single_channel.cols); + + dims = m_hdf_io->dsgetsize(dataset_two_channels, hdf::HDF5::H5_GETDIMS); + EXPECT_EQ(dims.size(), (size_t)2); + EXPECT_EQ(dims[0], m_two_channels.rows); + EXPECT_EQ(dims[1], m_two_channels.cols); + + int type; + type = m_hdf_io->dsgettype(dataset_single_channel); + EXPECT_EQ(type, m_single_channel.type()); + + type = m_hdf_io->dsgettype(dataset_two_channels); + EXPECT_EQ(type, m_two_channels.type()); + + m_hdf_io->close(); +} + + +TEST_F(HDF5_Test, write_read_dataset_1) +{ + reset(); + + String dataset_single_channel = "/single"; + String dataset_two_channels = "/dual"; + + m_hdf_io = hdf::open(m_filename); + + // since the dataset is under the root group, it is created by dswrite() automatically. + m_hdf_io->dswrite(m_single_channel, dataset_single_channel); + m_hdf_io->dswrite(m_two_channels, dataset_two_channels); + + EXPECT_EQ(m_hdf_io->hlexists(dataset_single_channel), true); + EXPECT_EQ(m_hdf_io->hlexists(dataset_two_channels), true); + + // read single channel matrix + Mat single; + m_hdf_io->dsread(single, dataset_single_channel); + EXPECT_EQ(single.type(), m_single_channel.type()); + EXPECT_EQ(single.size(), m_single_channel.size()); + EXPECT_NEAR(norm(single-m_single_channel), 0, 1e-10); + + // read dual channel matrix + Mat dual; + m_hdf_io->dsread(dual, dataset_two_channels); + EXPECT_EQ(dual.type(), m_two_channels.type()); + EXPECT_EQ(dual.size(), m_two_channels.size()); + EXPECT_NEAR(norm(dual-m_two_channels), 0, 1e-10); + + m_hdf_io->close(); +} + +TEST_F(HDF5_Test, write_read_dataset_2) +{ + reset(); + // create the dataset manually if it is not inside + // the root group + + String parent = "/parent"; + + String dataset_single_channel = parent + "/single"; + String dataset_two_channels = parent + "/dual"; + + m_hdf_io = hdf::open(m_filename); + + m_hdf_io->grcreate(parent); + EXPECT_EQ(m_hdf_io->hlexists(parent), true); + + m_hdf_io->dscreate(m_single_channel.rows, + m_single_channel.cols, + m_single_channel.type(), + dataset_single_channel); + + m_hdf_io->dscreate(m_two_channels.rows, + m_two_channels.cols, + m_two_channels.type(), + dataset_two_channels); + + EXPECT_EQ(m_hdf_io->hlexists(dataset_single_channel), true); + EXPECT_EQ(m_hdf_io->hlexists(dataset_two_channels), true); + + m_hdf_io->dswrite(m_single_channel, dataset_single_channel); + m_hdf_io->dswrite(m_two_channels, dataset_two_channels); + + EXPECT_EQ(m_hdf_io->hlexists(dataset_single_channel), true); + EXPECT_EQ(m_hdf_io->hlexists(dataset_two_channels), true); + + // read single channel matrix + Mat single; + m_hdf_io->dsread(single, dataset_single_channel); + EXPECT_EQ(single.type(), m_single_channel.type()); + EXPECT_EQ(single.size(), m_single_channel.size()); + EXPECT_NEAR(norm(single-m_single_channel), 0, 1e-10); + + // read dual channel matrix + Mat dual; + m_hdf_io->dsread(dual, dataset_two_channels); + EXPECT_EQ(dual.type(), m_two_channels.type()); + EXPECT_EQ(dual.size(), m_two_channels.size()); + EXPECT_NEAR(norm(dual-m_two_channels), 0, 1e-10); + + m_hdf_io->close(); +} diff --git a/modules/hdf/test/test_main.cpp b/modules/hdf/test/test_main.cpp new file mode 100644 index 000000000..8ff0a0f4b --- /dev/null +++ b/modules/hdf/test/test_main.cpp @@ -0,0 +1,7 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "test_precomp.hpp" + +CV_TEST_MAIN("cv") \ No newline at end of file diff --git a/modules/hdf/test/test_precomp.hpp b/modules/hdf/test/test_precomp.hpp new file mode 100644 index 000000000..fd337f200 --- /dev/null +++ b/modules/hdf/test/test_precomp.hpp @@ -0,0 +1,20 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wmissing-declarations" +# if defined __clang__ || defined __APPLE__ +# pragma GCC diagnostic ignored "-Wmissing-prototypes" +# pragma GCC diagnostic ignored "-Wextra" +# endif +#endif + +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/ts.hpp" +#include "opencv2/core.hpp" +#include "opencv2/hdf.hpp" + +#endif diff --git a/modules/hdf/tutorials/create_groups/how_to_create_groups.markdown b/modules/hdf/tutorials/create_groups/how_to_create_groups.markdown new file mode 100644 index 000000000..5ce4736a7 --- /dev/null +++ b/modules/hdf/tutorials/create_groups/how_to_create_groups.markdown @@ -0,0 +1,85 @@ +Creating Groups {#tutorial_hdf_create_groups} +=============================== + +Goal +---- +This tutorial will show you: + - How to create a HDF5 file? + - How to create a group? + - How to check whether a given group exists or not? + - How to create a subgroup? + + +Source Code +---- + +The following code creates two groups: `Group1` and `SubGroup1`, where +`SubGroup1` is a child of `Group1`. + +You can download the code from [here][1] or find it in the file +`modules/hdf/samples/create_groups.cpp` of the opencv_contrib source code library. + +@snippet samples/create_groups.cpp tutorial + +Explanation +---- + +First, we create a HDF5 file + +@snippet samples/create_groups.cpp tutorial_create_file + +If the given file does not exist, it will be created. Otherwise, it is open for read and write. + +Next, we create the group `Group1` + +@snippet samples/create_groups.cpp tutorial_create_group + +Note that we have to check whether `/Group1` exists or not using +the function `hlexists` before creating it. You can not create +a group with an existing name. Otherwise, an error will occur. + +Then, we create the subgroup named `Subgroup1`. In order to +indicate that it is a sub group of `Group1`, we have to +use the group name `/Group1/SubGroup1`: + +@snippet samples/create_groups.cpp tutorial_create_subgroup + +Note that before creating a subgroup, we have to make sure +that its parent group exists. Otherwise, an error will occur. + +In the end, we have to close the file + +@snippet samples/create_groups.cpp tutorial_close_file + +Result +---- + +There are many tools that can be used to inspect a given HDF file, such +as HDFView and h5dump. If you are using Ubuntu, you can install +them with the following commands: + +@code +sudo apt-get install hdf5-tools hdfview +@endcode + +There are also binaries available from the The HDF Group official website . + +The following figure shows the result visualized with the tool HDFView: + +![Figure 1: Results of creating groups and subgroups](pics/create_groups.png) + +The output for `h5dump` is: + +@code +$ h5dump mytest.h5 +HDF5 "mytest.h5" { +GROUP "/" { + GROUP "Group1" { + GROUP "SubGroup1" { + } + } +} +} +@endcode + +[1]: https://github.com/opencv/opencv_contrib/tree/master/modules/hdf/samples/create_groups.cpp diff --git a/modules/hdf/tutorials/create_read_write_dataset/create_read_write_dataset.markdown b/modules/hdf/tutorials/create_read_write_dataset/create_read_write_dataset.markdown new file mode 100644 index 000000000..4941b7cec --- /dev/null +++ b/modules/hdf/tutorials/create_read_write_dataset/create_read_write_dataset.markdown @@ -0,0 +1,73 @@ +Creating, Writing and Reading Datasets {#tutorial_hdf_create_read_write_datasets} +=============================== + +Goal +---- +This tutorial shows you: + - How to create a dataset? + - How to write a `cv::Mat` to a dataset? + - How to read a `cv::Mat` from a dataset? + +@note Currently, it supports only reading and writing `cv::Mat` and the matrix should be continuous +in memory. Supports for other data types have not been implemented yet. + +Source Code +---- + +The following code demonstrates writing a single channel +matrix and a two-channel matrix to datasets and then reading them +back. + +You can download the code from [here][1] or find it in the file +`modules/hdf/samples/create_read_write_datasets.cpp` of the opencv_contrib source code library. + +@snippet samples/create_read_write_datasets.cpp tutorial + +Explanation +---- + +The first step for creating a dataset is to open the file + +@snippet samples/create_read_write_datasets.cpp tutorial_open_file + +For the function `write_root_group_single_channel()`, since +the dataset name is `/single`, which is inside the root group, we can use + +@snippet samples/create_read_write_datasets.cpp tutorial_write_root_single_channel + +to write the data directly to the dataset without the need of creating +it beforehand. Because it is created inside `HDF5::dswrite()` +automatically. + +@warning This applies only to datasets that reside inside the root group. + +Of course, we can create the dataset by ourselves: + +@snippet samples/create_read_write_datasets.cpp tutorial_create_dataset + +To read data from a dataset, we use + +@snippet samples/create_read_write_datasets.cpp tutorial_read_dataset + +by specifying the name of the dataset. + +We can check that the data read out is exactly the data written before by using + +@snippet samples/create_read_write_datasets.cpp tutorial_check_result + +Results +---- + +Figure 1 shows the result visualized using the tool HDFView for the file +`root_group_sinle_channel`. The results +of matrices for datasets that are not the direct children of the root group +are given in Figure 2 and Figure 3, respectively. + +![Figure 1: Result for writing a single channel matrix to a dataset inside the root group](pics/root_group_single_channel.png) + +![Figure 2: Result for writing a single channel matrix to a dataset not in the root group](pics/single_channel.png) + +![Figure 3: Result for writing a two-channel matrix to a dataset not in the root group](pics/two_channels.png) + + +[1]: https://github.com/opencv/opencv_contrib/tree/master/modules/hdf/samples/create_read_write_datasets.cpp diff --git a/modules/hdf/tutorials/table_of_content_hdf.markdown b/modules/hdf/tutorials/table_of_content_hdf.markdown new file mode 100644 index 000000000..a4f687330 --- /dev/null +++ b/modules/hdf/tutorials/table_of_content_hdf.markdown @@ -0,0 +1,24 @@ +The Hierarchical Data Format (hdf) I/O {#tutorial_table_of_content_hdf} +===================================== + +Here you will know how to read and write a HDF5 file using OpenCV. +Currently, only `cv::Mat` is supported. + +Note that the HDF5 library has to be installed in your system +to use this module. + +- @subpage tutorial_hdf_create_groups + + *Compatibility:* \> OpenCV 3.0 + + *Author:* Fangjun Kuang + + You will learn how to create groups and subgroups. + +- @subpage tutorial_hdf_create_read_write_datasets + + *Compatibility:* \> OpenCV 3.0 + + *Author:* Fangjun Kuang + + You will learn how to create, read and write datasets. From 13508c7618ce1ff5d910fd6ca88f12df07c41c66 Mon Sep 17 00:00:00 2001 From: KUANG Fangjun Date: Fri, 22 Dec 2017 18:55:23 +0100 Subject: [PATCH 2/2] Remove warnings. --- modules/hdf/samples/create_read_write_datasets.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/hdf/samples/create_read_write_datasets.cpp b/modules/hdf/samples/create_read_write_datasets.cpp index 9893a9b60..3f8067331 100644 --- a/modules/hdf/samples/create_read_write_datasets.cpp +++ b/modules/hdf/samples/create_read_write_datasets.cpp @@ -13,6 +13,14 @@ * */ +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wmissing-declarations" +# if defined __clang__ || defined __APPLE__ +# pragma GCC diagnostic ignored "-Wmissing-prototypes" +# pragma GCC diagnostic ignored "-Wextra" +# endif +#endif + //! [tutorial] #include