From e42c404ccd67f6c315cf62fdb0bb0b04fa76d851 Mon Sep 17 00:00:00 2001 From: Nils Lehmann <35272119+nilsleh@users.noreply.github.com> Date: Tue, 28 Jan 2025 11:45:06 +0100 Subject: [PATCH] Add BRIGHT dataset (#2520) * bright * bright tests * bright * run ruff * mypy and docs * ruff on data.py * ruff on bright * docs * ruff * rm datamodule * coverage * request * Update docs/api/datasets/non_geo_datasets.csv Co-authored-by: Adam J. Stewart --------- Co-authored-by: Adam J. Stewart --- docs/api/datasets.rst | 5 + docs/api/datasets/non_geo_datasets.csv | 1 + tests/data/bright/data.py | 117 ++++++ tests/data/bright/dfc25_track2_trainval.zip | Bin 0 -> 44711 bytes .../holdout_setlevel.txt | 1 + .../bata-explosion_00000014_post_disaster.tif | Bin 0 -> 1554 bytes .../bata-explosion_00000047_post_disaster.tif | Bin 0 -> 1557 bytes .../bata-explosion_00000049_post_disaster.tif | Bin 0 -> 1557 bytes ...rkey-earthquake_00000413_post_disaster.tif | Bin 0 -> 1554 bytes .../bata-explosion_00000014_pre_disaster.tif | Bin 0 -> 4453 bytes .../bata-explosion_00000047_pre_disaster.tif | Bin 0 -> 4447 bytes .../bata-explosion_00000049_pre_disaster.tif | Bin 0 -> 4444 bytes ...urkey-earthquake_00000413_pre_disaster.tif | Bin 0 -> 4452 bytes .../dfc25_track2_trainval/train_setlevel.txt | 3 + .../val-disaster_00000001_post_disaster.tif | Bin 0 -> 1556 bytes .../val-disaster_00000002_post_disaster.tif | Bin 0 -> 1549 bytes .../val-disaster_00000001_pre_disaster.tif | Bin 0 -> 4458 bytes .../val-disaster_00000002_pre_disaster.tif | Bin 0 -> 4452 bytes .../dfc25_track2_trainval/val_setlevel.txt | 2 + tests/datasets/test_bright.py | 89 +++++ torchgeo/datasets/__init__.py | 2 + torchgeo/datasets/bright.py | 340 ++++++++++++++++++ 22 files changed, 560 insertions(+) create mode 100644 tests/data/bright/data.py create mode 100644 tests/data/bright/dfc25_track2_trainval.zip create mode 100644 tests/data/bright/dfc25_track2_trainval/holdout_setlevel.txt create mode 100644 tests/data/bright/dfc25_track2_trainval/train/post-event/bata-explosion_00000014_post_disaster.tif create mode 100644 tests/data/bright/dfc25_track2_trainval/train/post-event/bata-explosion_00000047_post_disaster.tif create mode 100644 tests/data/bright/dfc25_track2_trainval/train/post-event/bata-explosion_00000049_post_disaster.tif create mode 100644 tests/data/bright/dfc25_track2_trainval/train/post-event/turkey-earthquake_00000413_post_disaster.tif create mode 100644 tests/data/bright/dfc25_track2_trainval/train/pre-event/bata-explosion_00000014_pre_disaster.tif create mode 100644 tests/data/bright/dfc25_track2_trainval/train/pre-event/bata-explosion_00000047_pre_disaster.tif create mode 100644 tests/data/bright/dfc25_track2_trainval/train/pre-event/bata-explosion_00000049_pre_disaster.tif create mode 100644 tests/data/bright/dfc25_track2_trainval/train/pre-event/turkey-earthquake_00000413_pre_disaster.tif create mode 100644 tests/data/bright/dfc25_track2_trainval/train_setlevel.txt create mode 100644 tests/data/bright/dfc25_track2_trainval/val/post-event/val-disaster_00000001_post_disaster.tif create mode 100644 tests/data/bright/dfc25_track2_trainval/val/post-event/val-disaster_00000002_post_disaster.tif create mode 100644 tests/data/bright/dfc25_track2_trainval/val/pre-event/val-disaster_00000001_pre_disaster.tif create mode 100644 tests/data/bright/dfc25_track2_trainval/val/pre-event/val-disaster_00000002_pre_disaster.tif create mode 100644 tests/data/bright/dfc25_track2_trainval/val_setlevel.txt create mode 100644 tests/datasets/test_bright.py create mode 100644 torchgeo/datasets/bright.py diff --git a/docs/api/datasets.rst b/docs/api/datasets.rst index 6c5c57ff176..d01a91dfe70 100644 --- a/docs/api/datasets.rst +++ b/docs/api/datasets.rst @@ -221,6 +221,11 @@ BioMassters .. autoclass:: BioMassters +BRIGHT +^^^^^^ + +.. autoclass:: BRIGHTDFC2025 + CaBuAr ^^^^^^ diff --git a/docs/api/datasets/non_geo_datasets.csv b/docs/api/datasets/non_geo_datasets.csv index d1d0ff03a9c..1defcb032bd 100644 --- a/docs/api/datasets/non_geo_datasets.csv +++ b/docs/api/datasets/non_geo_datasets.csv @@ -3,6 +3,7 @@ Dataset,Task,Source,License,# Samples,# Classes,Size (px),Resolution (m),Bands `Benin Cashew Plantations`_,S,Airbus Pléiades,"CC-BY-4.0",70,6,"1,122x1,186",10,MSI `BigEarthNet`_,C,Sentinel-1/2,"CDLA-Permissive-1.0","590,326",19--43,120x120,10,"SAR, MSI" `BioMassters`_,R,Sentinel-1/2 and Lidar,"CC-BY-4.0",,,256x256, 10, "SAR, MSI" +`BRIGHT`_,CD,"MAXAR, NAIP, Capella, Umbra","CC-BY-4.0 AND CC-BY-NC-4.0",3239,4,"0.1-1","RGB,SAR" `CaBuAr`_,CD,Sentinel-2,"OpenRAIL",424,2,512x512,20,MSI `CaFFe`_,S,"Sentinel-1, TerraSAR-X, TanDEM-X, ENVISAT, ERS-1/2, ALOS PALSAR, and RADARSAT-1","CC-BY-4.0","19092","2 or 4","512x512",6-20,"SAR" `ChaBuD`_,CD,Sentinel-2,"OpenRAIL",356,2,512x512,10,MSI diff --git a/tests/data/bright/data.py b/tests/data/bright/data.py new file mode 100644 index 00000000000..61a03423509 --- /dev/null +++ b/tests/data/bright/data.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 + +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import hashlib +import os +import shutil + +import numpy as np +import rasterio + +ROOT = '.' +DATA_DIR = 'dfc25_track2_trainval' + +TRAIN_FILE = 'train_setlevel.txt' +HOLDOUT_FILE = 'holdout_setlevel.txt' +VAL_FILE = 'val_setlevel.txt' + +TRAIN_IDS = [ + 'bata-explosion_00000049', + 'bata-explosion_00000014', + 'bata-explosion_00000047', +] +HOLDOUT_IDS = ['turkey-earthquake_00000413'] +VAL_IDS = ['val-disaster_00000001', 'val-disaster_00000002'] + +SIZE = 32 + + +def make_dirs() -> None: + paths = [ + os.path.join(ROOT, DATA_DIR), + os.path.join(ROOT, DATA_DIR, 'train', 'pre-event'), + os.path.join(ROOT, DATA_DIR, 'train', 'post-event'), + os.path.join(ROOT, DATA_DIR, 'train', 'target'), + os.path.join(ROOT, DATA_DIR, 'val', 'pre-event'), + os.path.join(ROOT, DATA_DIR, 'val', 'post-event'), + os.path.join(ROOT, DATA_DIR, 'val', 'target'), + ] + for p in paths: + os.makedirs(p, exist_ok=True) + + +def write_list_file(filename: str, ids: list[str]) -> None: + file_path = os.path.join(ROOT, DATA_DIR, filename) + with open(file_path, 'w') as f: + for sid in ids: + f.write(f'{sid}\n') + + +def write_tif(filepath: str, channels: int) -> None: + data = np.random.randint(0, 255, (channels, SIZE, SIZE), dtype=np.uint8) + # transform = from_origin(0, 0, 1, 1) + crs = 'epsg:4326' + with rasterio.open( + filepath, + 'w', + driver='GTiff', + height=SIZE, + width=SIZE, + count=channels, + crs=crs, + dtype=data.dtype, + compress='lzw', + # transform=transform, + ) as dst: + dst.write(data) + + +def populate_data(ids: list[str], dir_name: str, with_target: bool = True) -> None: + for sid in ids: + pre_path = os.path.join( + ROOT, DATA_DIR, dir_name, 'pre-event', f'{sid}_pre_disaster.tif' + ) + write_tif(pre_path, channels=3) + post_path = os.path.join( + ROOT, DATA_DIR, dir_name, 'post-event', f'{sid}_post_disaster.tif' + ) + write_tif(post_path, channels=1) + if with_target: + target_path = os.path.join( + ROOT, DATA_DIR, dir_name, 'target', f'{sid}_building_damage.tif' + ) + write_tif(target_path, channels=1) + + +def main() -> None: + make_dirs() + + # Write the ID lists to text files + write_list_file(TRAIN_FILE, TRAIN_IDS) + write_list_file(HOLDOUT_FILE, HOLDOUT_IDS) + write_list_file(VAL_FILE, VAL_IDS) + + # Generate TIF files for the train (with target) and val (no target) splits + populate_data(TRAIN_IDS, 'train', with_target=True) + populate_data(HOLDOUT_IDS, 'train', with_target=True) + populate_data(VAL_IDS, 'val', with_target=False) + + # zip and compute md5 + zip_filename = os.path.join(ROOT, 'dfc25_track2_trainval') + shutil.make_archive(zip_filename, 'zip', ROOT, DATA_DIR) + + def md5(fname: str) -> str: + hash_md5 = hashlib.md5() + with open(fname, 'rb') as f: + for chunk in iter(lambda: f.read(4096), b''): + hash_md5.update(chunk) + return hash_md5.hexdigest() + + md5sum = md5(zip_filename + '.zip') + print(f'MD5 checksum: {md5sum}') + + +if __name__ == '__main__': + main() diff --git a/tests/data/bright/dfc25_track2_trainval.zip b/tests/data/bright/dfc25_track2_trainval.zip new file mode 100644 index 0000000000000000000000000000000000000000..e42936c035ac006ab891935a4290a797debcfae0 GIT binary patch literal 44711 zcmbq)1F$H;w%xI9+qP}n#yz%e`ySi2ZQC~Pv2Eo3_mY?VBri!-da9>;s(P=ryLZjh z^qM^i(!d}n0RK_mv>3Ji(fO|@w7=~yQ)4DpeP>5QV=JcrXe?}94Xx=F{;dtpzqR>? z@&CaR>)%@bkBkriB-)HxrWKgc1b@d%{H+xK*6tsS`c5X!)+Vkd)^yJ9&eBpel1sAH z6l3G`GgGrs>;S;X(lsR32ccwZ!?IKD0RL@X|75YdOG^9?udx4Cs(;I3ZfE_=&c*pZ z@laFJPRLa+$;#4DjZ@IbPRYfv0{|im8`J$y469eeEEs>0Y5rESf6MT9{{IhKZ&?Oy^(N0RLE{zi|I58#zUHl#Iix3{))vaqw&XZS~9;ne>-LjRYAlcAHdi6fn}g{iEXmjbGK2LD%EZO@I{ zE_kY~Ko^pbKSEIWT1IZj|G>9)N`Bt&T2km7=DxC0HFFjI}R z4)t=3I!V<9fwYcuRe6sDO%o0@u5o--qE=H?HK+d9U)Y`p-WmMT9Of5g~Zs zwC9;<5+5#6$9LPgm)w6`Hu6(OPu|JV{8x4_IdPclGPR19?hX*X&O53_OvP~boVR@O z{>*;HIoyT?AMF@(vvEM`f5fe=BN$)aLGAyz8;Y;}>EU@@{E#-iuTT7u)LZ+=@c8Kwjg7PB2{FA4L`&#;#%yc|Q&Y9WyG&(ZcIBY?} z!{_IdPh~b8L+uv}NjJ@JMQ)4p_L1GI((LVe>%K4_-wXC$+8d3~A@el@SKf6Zvbl#9 zRX_O&rSmbJV3t8=qsiCB?&T_t^=y4KtABD-JF`N3M=HCIDf9BaG0xvmUE*_4FVZtq zZ%ChU6idK=a+K}SUH(`z=ZZ%%+}sUxIu1UJkqf?G?4QmNzpX0Q^XaJVjY> zqFKA(c}|*baJA)5A#_O261X!IecN6Md($K;1Qcl%xY`&CE2HDRTlSWuaU*f+*)*xr zLKJ2_Nm@gD6WhevsGSl?+%IP8GZbj4_6bx;SAxn6%(weJe0x~A z9nqlR=zRQQmiwbCDp-EzAIf6A&?THP7t18!@o}rg*3iu9XonqrSle3tb*W>g`q>Ku zV|KL1oNpww+QvJ4@v*?1w&u|@c4Q;nWwJ2_Y$qtHK6w;qS@ibu#o0YtsIJyj+s)N7 znb+evy(Jf|S{-MBn3>AfFT87FgxYG|KAM8`r@4l8P*+A;^z<-A9+i`N#qnWf9sYKx zT`il^uxxoQ1S;f6!uI$viZQ&*w`Ct1$$HSN_~?lDD5)0;8Lh=eq`192!}!FG)l6p! zpRSojErCrvj^#7^h`^n)ctrrEe`Cg%Ytq1$3L=G&)@Vz-#Z51A2z%guX8HDVVD4T_ zv?qN3U~3BQ>Ty{QyXpC3J+ens zyY=MRA15Sj%V#xf>^4udkXvjt?aM)D5S)^ z80nc)5^=RE+?~MD5M5f`^h(tsVN^ktFny*|;`~(?zl`mYd>ZL_mCSY?l`7Ek{*t}U zlm=cFaMBvvHw~-IvsVDBjB)F;bEm9AHis7!yK84UIr*-FmNXp4V^%B8FtDq8Eh12l zoskPsb9C_B)RC@%+YSbYH5^U{AU$T?UEjANR-Fsxop2uLZk7vkf{AXKvWYG$s9q(8 zv&dX{^WGVWkjh?|=sfO?HGf{7ub@yIv87I~)F$g*thV@1Jb+E*8Su=d6AEqcv1JEP zzs0^{yNe;l*3dPBp8h1qav3rm1zX@nUzevV0G1V$D0@QG(KdP%+J|E`^|b34QfV~S z-zl(xghpp$1C_DncbQkDcsL%~5WK65$&0b4;zpwvxs!y61LAPzcn z0xNLUood)l87oaz3wUO57Hq&y6< z9e{#&RxV(je5(u7A3i8eCp;|su{FGM8{SU`H*1Y-DWv)%8?C4HV)4NZEJJVF-O@jZRF&FsYNx-Xza=tBouAswt^JWu5)_NTX^YwiECXYK; zMiwy%TkdQsR{IlO2_dA=oKf<9Na)Gl?0h9Li+t^oH^2&BhB*m(UfXt#7^2!b5*tmL zKDMzMO_Z9F`4$gmXyp89!LQQ2j7mELnhLHKV@_;2oZ#bMFa)Q;teWffkMvR%@eqDL;7STI;3+-?_7pRR&6*fuzS+V4!=FT@v z7u~%n2FKxu&L>#tlDV-+r>P2LmxQi&75$vHG7Wn%1G>r(#moT7NSCFS!HZe~-4rpx>}BY3cy6nfqr&hT87Qzqz$?!NqD-kd?(oS$A? z?_9P;vNTw@*&7}*Xm`5yxHmS0$$BUsT&DcX_B0yox_nj2ET@y<4w_ChXBl;wA9&wv z>npCMIWka=RS_GSSvf`okA=x~Bh1<6yz7!_K#E!lrWmtgA3Y<|vg%9S&S_zi`XMQi zBcQ}k^i3MuAyqhSVTchR=v2~cxZ&{~SM0Y7419Pu;P;bWz zPO(u+AGo4D(8ZWBt|x__>%h8x4B9G1w`tI}Gxs2-6K)E5`pGL?5v(|_Jg?5H!oZ?y z`8BsX?M#X=PnIbiRcgkvAauP&pPdY#qKnFmla&5>aNpMkLD&wV?=6oG-HN*wnEra} zStNVT#c}QL5<*M%(Bw80*?Igbb}3xg15sY7U+xqoBE-@3R**h6{=#T{tXXw6r`n0$ zM(nO0aqPA4Vg^MjCTgxE%GI(Q?WK*J7ok^~$DkxFi4;O@*Dv_*|e3@d4 zxU?4&8W4$g$yE8>{5ybSo@e%SMlL>R7tfuLom?rJrv#>IlEO_SSOlZ&3oA*w(DjtX zaW74ym2UhfQPI#iIHo9lN(=BTNeKx!c;*(fK4 z$g!~-aI(fnqs!MiW=Z-%DdJd44dpFDEU3-xrD4c+0x)g-;ennh2K1bi)fq-g4Dm41 z0pYt(!YE))^}6$xBa{*5fkopAacbf}SaGb8z-1S}P&JQIf;;U} zXW!d@5%52XHpVyyB~qzUCVN)Q04Mi5HVAjYGvjKVmf?Xkdozo*b{;lTDcLKdl`2#y z&TuJ>;_BpzO@QRB!v|6G3$pUX2uNEMO8V>k6tNI%a{SPiPPH%cnFd5yywv0Qu+8Rb`gvZms*Z3 z$|-=9h3j(}vmJwuIk0!)3Uu?CYz?9t$plGC_OAhD>KLjXUl|C|=le1$Po{393#mHy z3*p_*p%Jqh$IciTL5cN5EA*C3sA8R=hd+ONv8WRJ9ifLhwD*&ibdGW0et?nSDhi_o zk3oi)Ge+Y_mT;U}BR}EhVG-D`5wK?phD36dD@$z+ff$(XnLHsh&WfAlDIw87Qm|X+ z(R+-++IGOjk4`PIYNISN_hy*5RNcZB^?~szlba4h&4L`tfxJh_y-rNanwuM-BC9Hd zg=Q`TM?5wF2da$cO>_Ts|&Uje`z6Q`Qa^_B#fu6v4GWzs%Sid&>vwNG{+s$HQ=r zY^HPl0<4EjjIk`~S0&Ra5)~rbCJ0BI(n%?LuGO3{+!}$XILPCrE2_2;YFyfog-w$t zGOq9|RZ!O=B??C$$w(aV?`Y~w+CWI6_wE5x{l}j8oo)`A$06C#1tR<;laNTo)4If~ zdPLzid#V)On&VH~;{;g=K*gofDbDspU)R<|pT#NHjWjLPqOvt`(>Fx2_Nh5J?s-FG zONBRxiRR66N*z4`$X~e5LzBLR>Va-aT*N2-98t-G21Mi5_Guoy+1nPyAN>o&e3nxJ zFQXJ5bR{!#tsRF_GAw`)t^%NIP?>e~0;YsWmk7dm`?9s>QK$)s zh8((hV+dBfN@i6!g6nj(pG-tJcM$A0OVN<8PrpJndCq=GXGP`}!|i{fW=X`E%vT;1 z>0v$!EBg`+BhbzxUU5lA9xcMowE*arAEQ62#L0ioNcMBESc1S;&xz7*LMyuQHUI_b z8@DUjYhOfMGt7%VExCbNu8@E)IpRrKyS6oP)#wvbC@cZ4WeU^?fMDcjel2uqWjWP} zFGu%_nKS0g$v<}-MYXxFNTRui$cUqeVs!?T4NeV0-=rm_=!(C-P-WO^SYSZc| zQA4}F|7Q1GBWtocb265aGVTN?2(7HPrHe_f;V&& zLG3<^-`9AH4uUn9?+l1YWcZnzq*D9Fj$$y0_6_+oXShSAV7Z8F$)spiKXpQ+|7fSm zgKJZ!O+cO{&XP8I3qiNnX}EpQxY6yEZ-5bgZJO&DpMA{5lA*7zdwY(fq4&9$CPjOJDWjN5N|N-ixO%MA0=??3B4Rf%eQP=ECXlmCCcfsy5Z z)f?1d6;RZ3{AznkPCCs>%+lyhI{*>!(dteTPLBe zG&y>k`6C^^gma4Qd~%8bZvCwMg3Z1IdLM17BTPQ6(BC<1EingmOGaI2CgsdJevjgU zD%KWeX{xog_$keX0yS*`*bz5m9N6LuzLJy zCjLh!Jho{Gv-`{K2TXW1_Nxt^U&A&l{k7qpxZvbqHW`M*PxiWc}C57#%)>3sKR`YD4r z%q_T3_)}m0WR@>XouwOz_jp&y(a~u(lsrrNq$iUJ(zcy=ei54}_4szESBCKVrBaR( zHrfYOYHIb+Z2-q{aMM`(erqAm*GJLaTfVmWhRC}O6t2s+wpn;CuImxnWAac+{+;9Qbt37JYQtGEv0mJit zR7(Q(bmAy>mv1Iwh3Ce#{Z9Vo)ES*=UL;PQ)PNt-jm#HUN%QWTxnhJTtoVvrzX2vT zA5Jg){A;Z+*DCBW-^|MQIDfl5Pr%k}Ep~H|tUPPNUSS*0j+5u|q-kBJE`uu^jjj(} z_0R5zr5eY&$t~_O`mX?Elg~?JxhH7bVnH7SO*30-;}sA9ooeK*5BWD(cb7vC-8Xpf3Em(TH@<`kU5Uox&6*Az2>TFv6+^_w;NsL|YjIg>&yc&KMKBdI#oNWC*zxspj6qJ)ZwKzo$ zSJ~qLo9NY}ojuJl{=A;~RnOPN=t*r)Y@xjn2&L@$501OD@XENju*F!ZN-PTUG7LD7 zBz8Lf{aEE6ZgEvtb{uNLBISY0jbF|O>IdAo3pFgA8*Hlf&L2bL z?4)MVGo_Qho!HRM3fLDoN2-c(dNnyvSNidZ$uW}#b%+tw9UjvI=MV1p(zYx~A~1&B z2FCp=v89i#6`YX1irTIcr|ee+^K-Pu2G2KnN7d?M+`AXieebw=f8MeKnH${yOwViz zD~sYH`p!aSeFjw%!iqMwq6HrBau@5>{YVKbf~a69zZ~$YLqu`ik#lu<;evQkmN`h0 zW}cIdoAj9`Ox?)Q@zb0rKKcTx8XX``A4WCt#z}0M_ANAN-4VjcSVd5Cucm}$$<2Lj z2wHai&irPVnSpXK5&t3;8e5p};wqp$zMt^(LSm*US>Z>n-H3xCArcH?l;L9_ql(;7 zVwUZQ-{qJ;gC*FP>(;nq3cqqWN6KB7dhaDrv|;amJRF~<-#kdfCT+=w$o&Q+bwL&= z_jp%wg`+w=aJutR@iO9o~5SbT^sl*5pjzC14v-iq4l zMk1gnp1e3J=7Xy)A+M<_OrZEAhOsoC8miDLa!=sEYGd6oRU4`o^Y?D07?1%waJCw) zvr6vZNbItCB_$!pcJ8R8G$*P$H=}l$X6;EZbu(KI#8$vxdsV9SKBj^b53Nh;ZXU5% zJ!e2jKKi2-59WCBqFzpnaslP{1a>h?Q2wCB8wgh`cOS5r%T`M zICx;9O_gmn70Uy;g=uTzycw{=8}cEP8h9CjzhAGAQ)LY`8VaG=9aJWS)|Z$qQf>LtBZ3yOy`n{{F&0`C4kmyYb5zG5A%KQ+7uKm+KT6vc9`PcP7x zZx*5bC2M}^`yBfHB>{wzXqhLGjQ4Jr>t3&|K;iq&2bB6%nVKV3DWO%M-5FMk=-6L6 zQ!jcbpt{>QG)03MXX-MN^cb~nA%KG#v$VA#aoT%&3!~}zV+|+fOa8Wf)$X~eWLacH z%;Z7;F=r!VT`}6rTlz;1EZk z#DZBWbN1)!tOi5bwEn1;#?aSH}cUF^=ThDXMk1)d&loVSHh z7zVZ9H|6$c2?36;qhFwAC;f&Tb;+ZIYfw6RES9pRUbz2&Hw{Vq+JZo}TJE{Ju z?b+T()u~Lg9EHXvrL;vc?Qn{=W=?A;v83*v-O^QKmn26n_Sy7Z?u-}f6jn|^6hE^> z$toNZ@{BZcb_dExd1dS#df4;kfF}0b+2w)Hq1vPd$ai6g9r9<`~uPP$v^Ll8u}y zH3e|>Yw!05b3C9ZWkij_i;Uk_!1*ro=Y{ObRW4EOs(kaBtPgbMb_#mxM z=r{>X1YCC|%9nqhapmS?VCBx*(oJ=m_htdtA>B4%mR-$O=hU4C*Gc-S-Udmf`dgP% z{&04$8-MvdI>nn!J4&W(eU{j1AJTK7X?E6nsN`Zex{W6XMD1~`C=y~>gTRR~WLbaU zmFw!e)mKdAZfK*YT$YozickGj9oxKnu%ULuTZ(zZ!qs8#jy*;NC}X#|Pd4&Kf^fcw zJ+w5C$s5C$uCBj~dSl_UA19m(<+e?dPp0O6p}7UUN_H9vv=O6~PYrC#8qHQJfugu} zzCvOzM&6g^c`s0!wyhcA|1cWRXB{>$o@M4WKvQ{)N5t_$30h)^I0DzZVxtodYw320 z((Ygs>5*C=GTXZbQh-zm3hyUHFp{`%2JaAJ5x6<<7{RPN%?ApJ@?b!V)OF0d?yW&^Z^RQxJa zy8Qh~mljHCSc$>_ktWZU9vt>lxm_s0H{9Cc!S8Leg@Qkj?Af~(4T0Wee@CX^Gey_9 zgcOZ0diYziSU197-)d;zP|23fGKy^9N=x{2L)X|SG$9gJC)kKNPzs+AmQ!OYf7w8Z zFWFpfA=2V5Kc-qDcKY=dSnjz)m=Uv$TcHv@68{fR`N;*tv9OS3-rM4}##;Zbvt=ev z1h+)NPCUi@u$cFK61$xCnwpM%XMdHju(Otwf-Mu1diGtd^VGt}C&E%J9e+AUl_T^f z<36)C(ew!Yo>4d4CzK&Do4safmOzqiwquZ~SxiB2We7;3S!^wvzY|+*-=DO23jiJc zh6WUn(U{zvVb&Y~G%;oC@o_sTON)na zWJ#W@Jt6;Wu|K7k4(R*UXwM*2#CRS60n;Rf8EqGmTV@z;JNYt!8uhWe zRw{bV5?+Z%+!)mHK(h9l0q*(Ic(8#iC_D2HA*N|o{w6Ikpe)Q586P9XHSCkLD;%a$ z-l74LoeV-{r2#nhBc;_^Vgeda4A~qcpA+hWvCI|lPmIGuxVH{2J#m-5Z#9H9>&cvX z)g{R(H+_hK`O#Zs-D+Y{%phsbjL8(ORw2&);IJ&25%=UgLfJXJrU%rr_eIcbT1Uz| zLl#`Fs}v<|?dUNByu`b3!wUdgxF~i=ao>{cE`i5k;mj?X=#3Z$fV))&3eV?nWXeEo z?Ot-vvHIFqk#Qh<3J6OVNVL~OnoKDs%Nq=wg9IoWA)Zux*74nB76Syw9m&~ZZ^E5P zqW8$L69fJ%rwOGp3#%ja+KD06zp7M6ar=+5RlJqRRXjAsN_|8Iuqrh!RxPV1RFwE|VeI=1uD{bBj$4+1RO5S|(mqVO3V*cnX+#-{Ui z*yx^x-arx#qcE=TtO3&%(x6Xe=Y7LOBA z;3O97R+B(%-QQw-m$P~DNTnOqY;k}yeZ0p;KlBbApYHL{o#v@=*qQXCs9B_Z(0US0 zZApgZGxEj>Y@DHT1r50X8tT_L!#1v@SQKGQB9QSMc!6d#4_ck;ujJ+=KI22T5V4uf zl=(&W%xrrWQ_Gwxyrw2xDqGg`q4?Silq{?Fj8$arDA|1m*x4j)jPc)*N9%_~5*SX9 zz<9K3^a*9^h3+41-Ks`sTELHf%j-Ckg83X!^BY`#ySpKPW!aAqdZ$&XY@}OcRkBdA z2ekY;5xbmDj?pA-F8?b(qU@$N~0 zy5jHrR41Q$Y*(9vNLnFDA&?;26|>*R&W-`<2ol9}<YkDD`w&B z@9DJw>>zJevF>UFhWDf`nW~8~Hx45tTCW>`W`w{DuFQltkda=nj`hlz1Vc>f^tZ-;zv(pF^6Q%$D$zjJ;(~My@J4cVLtrKKmG>2}9awjv+{3H^DoH5u242r-Pr zcRDMqw(v+K!5Bt7vlM0>;23-&IItITjziAKLOQ8WeT_T=#Z|T6 z$G^e)(C2qJ#xcxf`}b1g$?fwE{{t&?w6`TM?oZ8( zao+H28~#Jrq5<^1`%r*5;6sFa*8YzfKofvL+=eiKchx>|sRyU5kWroJhxujPWHz+( zkicZ2=ipz_;ey&L_QT7K`620SQ>Xvi`szWYtPJqB-7h!-`XJ}#Imb%3O?aIlezNOh zx3&Y024;%3)!T6GLG{*pUCT!LNB`&O_BE<1@!)y(zf*tgEm`UJnH+k(z&D+_!3U1_j@Jdn1#7mJP=Zm@WiTY6sS04fBb zclzDabn{GBdhYDF->=4_@_Ib}ETkFrq;)0BxjA;FOm{w}B)&IkGEBbu?)!R;jwzo6 zdip1z=d*OB@y^ex&buC>=hH%QzTrD+HnvHfXZSG7qvd7;@MCltT#h-tK6T;wuqTJG zln8h@U%&0j@bamZ{BGKg=AidlJ4^W^_i3YA`Q^R`m+eyM@uGpx$Mv~e*CY4K;sE_6 zDi@~ULgDQm=ldInW(Ju~BzYUJ@48zS{mx-sf-`kv=`8ibcaMn%|!qTSC!=dY@ zeeG0(*4ViGGp~}IcGS$obg*~G2>1%~Ce~NwY_Ov34EU^jVZE%Aq~S%hl#J0PB6aE! zTuz7Mj1iI4Qd3M@+DLeR{#=FjPtA8tWl|gHlPQhUZK$C7f$dMoSK?8;PnVKuZ9iSJ zoEaV()U5cWY8PWc+|1OK^)+T6_HC7{$b1CZTyF*S_t~@w2(&Goduzm{#}h5r+g@?3 zHo169o0Q)9G#?5w6;|xXVma##C39ikRnRZa<(vw3^i95`P`%s7E|j@bdN?vGKhTr1 zg2#|Ec;CQ^kYt^c$60aLjrOC=m)7AUKJeYiDw=w^qb>fmPeG%vikSp{@0l+>;n;zZ zs`0s*)`QO_YKIQ8(1cfU7d-oi+5Dq5+IC}I?m`lxY^$(igjLW6mvr#Aw|BF>z{hz} zJ#PXhYQ3ix@Od@y{(zPQ{>x@CK80$*$*9_R`VDO%;TB}{d?(#bs0a1CG4T`MbOp1X zE#Y64GYf;zuQlw|vg2-_XIs|2t=bx?>J0fk@$EXje=^^v27SmInX6%xpPBe^Bj_Rh z=OVJN@{W&SC0pdqq(S2jSj|IUNPGi*%TKPP0ODxPTT(_2sykj##Cf+=mduuBm zU%^8hW+R&Yb|l=z*?oG3zKv|>smBja#}it6wMq}d_@~|+)vHd}z0L&C(skE|gW-BS zY1m@B=3<%?RM`Z0y<4hDSneG=AGhnNni#QSwE&!KB5W09bDz8Q+Iq{1IS@CN$KiBH zVUxK&q)!Wke1aO&sAdCHLpo2<95ei*zajX4;i&Qpxw05=cn#9Q_=JQ;RXiyLREKm( zOgL$4wXYS(n_j;s)n~Mc@J!EqliGw`aKQn z3`p_@4+O(4_CgzUvr9;Lsuc`-q`CI(NOECZknY`&0alFLe|xSmlg6*CQy`oDLTrr9 z$jGlw4{}K$ZUu&X5mTGYuZXm9^*zyDitAeYYV4CQ+}(&mDbtn?mAXIRU#*zKSX-_k z$wJ2TC`fLeK7{CDxwyXt)Dy*9n`l_6nat}@jU(g{Bln?t>HFM~BveN*Ia3A>!m z*WjUfeOa7qNtMGAiCi3Zj&CkD)b{ZOE247-fm6&PBLCw=P^_;<1O^n?COPR6Z!Q7@ z!HgOb@=w47l$v&#X&AWgE_IxT7H8gIn)VFz^_4ggxqXPF2B&^C z!K`n6K)DMeK6rX&XZ zGQJy|szyvb9e>N27NN6WR6VoyoCqMU{0MVd#%X-g9x_$QBC`*ov$C5j^n-rS8ptM_ zz2EZbR+GA?1PhS7THB=$?WDa{i-ecpH?2bB3Qj;L8e9A9gXcUSYFE+Eo!cGZpTdh=~2o6|UZ*=c&# zfl)%1@;la0*9;=|sX#7cU1mWRMzPh5j8)mHub4rjT*D+ykaD`}9KL25i((c=B6AdB z`CH~H6L9=FF}7oLai;2d%4Y-*Hm9qpJT^9U6E(S@ltXyEaamlTYaEUjPKjw7D{P)u zk;N@LR$|oJGy!=5K%C!`j+}U9fuo#KmB3{(wuvab3(;ezfDli>oS+r|r z;QJd5y)*p0DHi(JFG(a!+q}%q;sxG_{2pM!@7*QqH>rh=v0`U)f-XYZSQ|TJtXJZ; zhv|PJf-sF0?Sq)O2tPY`8rqH{a-GizD!}^mY3o zQnbTkPSQXO*uce8Qsi9SsV=ZqRv+FIBxDJkNataMDx=b6!92GXbJCi0fFgYWQ?_w< zTHS3NCzzC;Js%9_pyh3yrIcI5N|@b5rsl_ieQ(!2Sj1E^yo4^aiIAyRz&0NZKTIXQ zZk;XQQj;pWrp0eGS7*q89$}lDZc%W_p_>%8R}rh3hMq;kN2u(qzF0cg?(-vd^TZdc z*0e0;+Yf~q9Q!tsUYpUk6)G_C=dH%M2_CHBYFvL682dHWzsy<`6o~3Yj}3deQ|7sM zEm$V1Tu2`w@)#p43> zKrd(#4`lGlSYUZAgOY=epX}dc_6cVg8%s= zhn4no&|l;zULjn0>T)0CykcoE$n{hurdusof$KO$mty?rFBY5sh#B=c2dM(=H$kiq z&%^u+1Q(DaVP@~$d_WfTX`oG+Z9_?W`+zi?BeY!Cw|S;-g_Dwb1{QJ30+-sX57XFk zf#}3|!It`^Jf;2EB-8~5s68g<9Xb3pQZ<~j7!0~|c34kTSs1KDo*cbYIv(J3c5m3< zS(7?RTZ3B)b*(rEPBCr(2zu5;Vl;x>7W+sssAG3FM=Xs~-@8~I=Dk?iS58!8N08h_ z3bzKvSgJa+FzcW$2#e`n7huS~Tsm{ZtWeETX;JRo3!jEz(ATCyP*^^r&!)G5D5hL4 z@#@`ti{APPS`M~N5aNXqf#eks8mDj7&Z$fmuehIVf-QfcA7!my_(#6M-+nrc%LeHe zf^L=B0E5<*TGW%`4iP+|BPK#8^Wk>Nk_#+4^SmK7j$_^WFRF0pbX${|aw7Mpa07TC zcTls6t$AO3zfD5dQpqlTsSLq@)_=Q%N zbx_KUlCh0}jHL{jVxls8CD|L$L&ae8wuQ+4-#b*OUTxB3-xb;z40Sgc<~pUviF?tI zTf}WQv{PAtDeY21WR}pu%w!`u`9srFytr;7!?NdR^DM_ugsbbru30xP3xmxp7R7WI zM;d$QV8CN9C6PFvZ2VycOLghwyaq#~Swg74<*IM9vDoIKV9!-WG$Wu_vj%L~*)IX3 z96UfO9<-Y$sJ9tQN@O3)f@&YeexjsCU?|U&xU#pn&(1kxbPV@1vZ#!MI%$-qptKm@ zG4gjvMYoW7ZNpcR#C{xTyC(-8v0e@Joi_-`SPH^^%fBrH;XiUJ3y+-o5R<y>q!7KvKCRI-ZQ)p+=>OM|*TSBQ6n%>(AiSeD7qd#(2C)+=TuYCB|i$W&}w zcwWVqY@t z%VdxH`>^cq?p2F&`D7SJqL4>0>dY>^jGUj9J6GylFfR<@#x|oeA zr-_j~>r$yS$qb}hk0G>(?IMla;vP1sKf7t3Y?*6cEs3hx{tE&XULu`DxmJ01o*GjB zJQ&^oZ8n~pOW+oCX9=%#=(pk)bE>3_TgGDF4EDtrKe61$L7BLQ;Tkq`Fi z2nVg)4KsClVMZf(;vew;oak`6J4yifca#3>ujHWk|NLWxvx}pZi3hETp`)|8gNvb+ z$v^+CU}0qb->43LDk$nHzO@yd9UYy2kF(uvXLVx{mLUR%cnaYP1O$zP0>%jpR4kH`_Pc9WOl}qulzcsOa>-8~}Yl03v`g3{-%x$gw0G8Fht_d2wGGAa$cS zl6_FDaqgBz4m{8NQx4xnu@??9bddf(^*@BZ&&b~ce;P-S+uBY z1-b9b{`3t75B1^8zWyKs-~wcXJs|+#YrZm2)b9`xehYS98{Wh{>qb4gSAFd}{UL@H zIGlbr9yfDcgp|tNMlsdXdI3uAcI4l43Wk0VenB5u*Z1}2es^)(vGJ>F2cwp&EK!4H z>2C{FU}1Rr=&g{>wU{mO6$0#75e#^Q-eE&(Cl>CR_4 zU%l_9eFXo0a?`Vj%<=eJPr{oDeaCkD$NeHT(W$xcM6+b|d6=sSqVR!9JD2~MyW#R0xqAlXD%bIv!|Hv-a9X|wWp}V^^vCtFGqF6Ee;YXC zrOUfe$*rNfjs28n>YC(hiYFV5)9c4GJ1Udo$vFz^eCp~`Y5k;?-iTZ8>N*$03Z+1F zbb{7d_sdE53R8b?)UA#mE5c=)Ckl<`YjW4aZOVoM9n6KYym1@Ybo0B-BFsi`9^U=Rd39 z6HrxqVvrordZPMVbMY*o($!!R}=97%C7S?wT=VKn?Bz|?bY8EZwXA& z0Ura@k{&0X><3FJN5j2nQBo4W#Ov8huP%$nsKPz3Q7mgt@5o=(yg8u1EC+`RT=RN= zI<93l);7gBz4KJ4aqwrkve;c|CW}?{-Fw0^drx1lMjd}PN3<8n+}*1=o|V+8)}G)R zKz1HMYv`IY;bZT2P(!s(pqMk`#azF}-QYKryegOOQe}XD4z2`*Sc^}rX`t!n!SFbpW;!{i zUnTCyW%fLYDa+=Ph*iAv5l&7_;N9)lH}1yM8B&_b@6YCb<#MUVp*UZKfo7)>+QE9=Vv|^86Y1OX)?bT_01AKXD^y zCG%`)9h)k8P`7%~leeqrijQ|};B8n2s&BZay=t5~)N}hlbgTYD<+Wa_2x!9}B3H^Z zQz^X6UH2Q?E#GPdynpG!^mj6lBw0j{e9+swN_t097Sc1j2~4S8At$cPC>)eedSNrW zj5*f2NeoYQbu~cL0iTm|V`aMD5#ZAk1celmr7i;(IPqn96Hw&i?J7I+URt3E)*4{k z=`dkxa4+(qm!9>tD>BSmmYw1!tYHDYI=aUqBlZqHAS~e3T6AJ>qbz^DOTEGn7S$-6 zh6+*0+25SuY^&GkaUI;vJk?_>8fR}=J>vj1x(Ng(qe(tWZGzVwq99LJS?p_8DB2qU zTifZ4GuKR>WSqC>(?s>MZVYjBi25y+B6I%rB>1QL&Yr;ftBj<#RxWpvU^(EQ%f7}_ zp61u{Ig`;du@c_Uc4)9m4(`Ol&ArE?sHD55P|<2ghotWSgX`a2_REmBt6%;LPfR#R~6N52Ii>M$c&3ad6Flc;fZ+SsU`G&uTJ*+b_puCasqd1lvQr&?Lt!Dr8M~6 zJ^P*V$i%*4JgW!ObZ_ghH=Aa6Ztv#Wj*#*}-(z(XrLapI*;Z1uZB;tQuNI+Sqb8}53dpyqm4B$J~H)lq;Tv zxxRw!d~mq$i_g3x(SsxDz*?632n@cd<4sW4$<@yhx%MV9TkE;rc?Y(I9pdxtvAG^S z(26;CRg;}_vlP0^gHuMh8AO39jxu zoE}+5=LIb0vkt>!uU2yh#1hxEbK#~mZ$5ifpM+tCuY83cQfBRH%V-P9;)`qEZ&>rF zNFOSFsU@`HvyxQ}sw>HCfgWg{P?F2*kCej}E-6 z;Xd%?+2h&l)hV`XkssF0YV8In5wnDPk8g|W4?l0AsgU?T0V|dheMzJjxHie`nM#BWvi_|_Ox2)90%98 z+VM=C1$C|a*1Sh5VqDza&o7F6f9OE)rn|IopCPojX^~UEw{WZ;6pR7@P?PrQ*G$*h z5nN^*s-LkF^xN@sUm+;VWQFy&%U7+}ED}0+l=^UGSI2qE#r*3Q393g-fM;kmIP~aC z_HSc_8Qjk_mCHnsg=hxa68ZnASa#_Y#&npdW{cI}o2Lr4`G-Dh3W<7-U!;~G>^A_< zynEOQY@FtulJw*6*%l0RY0@kPOoP#ZqSC)6`6k?TtY;ZKGG}ibA?D7Z%)YCRgOt!L z#L3mg|60i;rPRcjpHS8ddFn%B##srq_MFK>o|I(}(`h|Bl%&R4@~=~WhfsC{EhHg? z0S$RNKGiH_5D!!W`1`I8Z2Igkh_@vE!0MeEB=-=R`g9MkaOQTFC18$x2=_Wn27e_IQW(w= zVsFCX)kpmx*A8Sji?Iw}ZPYicrkOl#yVo3$LE?~*SCMFfl;6-jAr|;9Q;AiTw?GZA zm1Jeg7#%oEJ7xd7k$X>*`bpG39co~i?g=H2h|P9`)U@+ZD2(GOaXWafBy3%)Tx(V> zQK&GNEJ0}UNrr0%XYTGzjtZtv3Oh?Q`9`5e4}3sj!>(`=Rwg2-h7fgvuqI5XC)WyU zZXe(!UCfrDEHbRuY!4mA2)t;*GvktVKTvJAlI|pO5Jtn$C!@&-c1wJ4Q&4JhmhG z11w(tKzr+;h#uu35*CegFKqJB5eiJ+z&jq&<~9nGJ>3vi)IDj#fI^QUGGI+WFvvU=u3?NQUhFMMY@5>YP`n z2IQ|uf%%fg8X)i!^)R)?()Oz2H<_5f}iHtDYn3v`&vFtO-&_~27uq#ChW zwt1MDr27n(!~|g5aimj<0qxk8NEm+#E2DKC8J6!z2nj|*unGd^6YM3jr|J9C4#{saf9K~Z9il){{u&*%9_8ky*u~kw+`r`r$=KC0wmN>Z( zR47PxeI&Jg34>r|EmIS<8CL&Ahj3fQzjm~ZQDuMk1TjRjB=j4z(Ww$LK=we=X6a(D zqgp9MOx1eAUvfPR^4Ef2Gbh|GYXl>6;(p0{wsVVqo9qB1LN$XlJj9tTQdYCVQJeh) z`qvZ)nc-363!qF8IuubK6!G7R zIdlIDYwr|g*%v1X-*i^mwpD4{S&2&9wr$(CZQHi(O51i${il0ozV7Mi{??i~_vJjC zyVu<>C+;sIcEsvs1Rm^s$LAf;N0ou^C-9*Oh9xmI>=N+Mud5ZKyKr8d!x9H26el`@_bw8!Hgfq zSpW*1cf1xS=Jusv27H1igEOA^!&$X`aU`sfA-*YRwgT9p##pI#Yz^~}H4v&T2{9Un zdZ3ABNfHp7CD1=x1UuXi^AivhPgsJ$LAGvlEm>_tBRVV;C2fbV71@yV(d#Qf;)r6^ zuY(>5I4k60u+}y}yB5j|fn4#%E2bO=g`<$qD6nAP^HwH`R8Om7b-I+uVoh-(p7i8< zNo|;euqe;|y~Uv-WgV;m3;;j{2LNRME56}>MN<49lN|m&@^7OY5-eon)i6fjZ-}lV zeCa@cg@^lBw%jo;>#IYDx6p>Ci~rV;Ky$2{9N)0+paYIqFDwf*4o;zLan`hQ*66q- z(2HMSUNaJ{pk%(EZ~WPO8J)z{aQ4y%|9$^4?eH~|_MJk`!2#I|0I&gUL47lbw=-=e zF?vStY5R$gj}Pic@ALO_gU%RSYLg%S+GOl>9z6X3JG8fzTK$-kFX%jadefXGv^-!t z==Ffvsu4Ovy~zx;&U=1fK)MNcuRsKkYj<=3d#i>_`b>u{+YMr?B;WT{yDSiUoXg_L zk~TrM(CyG)d!w>GxD3iGPp_%w3a8rRgbfATyX)%gXTO18Us-|L);EKl8_>3qW4CzH zL=lP3kA#OzNOf`v{GUFNn$mCU&oF(@mmM+4lyXtv%x&j?fT{M=qAtydeAN1cw>7HV z8hkDdE!}(6mXPdjGIS@r#?DJJ)u5V?kfTd~*6d{@7gb+P(RiqjTi+lRXUFx?+Ck!5wnKSLY*_3 zMI`4rxML`9;jzG8jz-ZM)`#Gs;-yGJ*%PRQt+z`O_aC!u6I8x!PoPWfNaXB2*nzJii~Y8>;ZZ z&s?MYK8EvKA@e5UDHdiw23y-f`|9{1W7d;RtaT2oT_Fnz2D!j;BkwC6$Ynn}T>r1r zx(8^{fH-TheASk6ypc$dREgZ~q9A6&fFfz$Lu{YNB}uW`C`mF^ZOXr_UbZQJXt-+q zeyjt4${DN=(N;jXx)WwUFlKgESydOb8=y-P>v|a3% zm-JM@@8iYPGL$8M6#W9jH$T2Dn^M|XS-fVEGJLyoqVvE7Ew{rTM|A_UtzJ{n(EbNG zn4lkBM}qp!F-#zr8RDTsFB&d^Do{_`JM?r;%zd!S7iaR%cH8fX(C*R;@YyN8s@~G4 z6GIu}AsO;=budW`>cL(BKwz^3Jbw!#}&fZQLGIY`Lw9DssX`t&4)JPYzs}z9!@d_C(c}C*48wQRtjUL?OnCv z#OY2^PAU_Mt_UyBmBuB`_E$ zo@HY3F3}nrK|?y2~z8X4F=i-pzmj zUphEP%b9u4kZ#z|umthc#?H&6f3HPpRSpY&{1eUYgllwMOF6EKm!G)jH>yjeGok?j zT=T5DLxfqEh3u>WafDCNY58|(9Rub;+mI-Z^tb&zv@%ZT;lrqjeEvOH3Lml`bPuY) z9tqP+93lyeMrhcVfT-~&%Tfhms1R@XKI2%lIRB2SX&n#D2Q!;!zlAYZxWjF3oFK3| zIJ|t8bE8Aik#};c2jD^b&}>!(^v{|mRO>Hs;aAW~tm9=|p^^F=I`rd}gh_ATSg+-& z$mGoUE`sEMkyv8gpVefnlgU0|!A;PQClL57@OO+GU`e|m41k}G34-4s|CW#res>5f z{|gXE{|^A-pQCsG8$d+6$;O-EjO3te?|>b;cC{i7PL8wwR^+6S75e?N1}er%c7m3g zaYTR`BGQ#~s@_Sm1C2h)87rU|%PJjru)$#6oJ+H1E69>-(#p~vm-9>2?qh(jd*=E6 zBm4R~&Bpvp&=&wW0r0y5eka(f&*BVE)w2cV81*-XHe8@W^)XGS@d6)D1{{41WKOy2 z0$d#!Zynf8;B$M4h03MhS1g_4(uAeL*f)^NL z8!kuN6lBD#lkZnd`+&vEYWATXRoVZb}X@iXf zVgxVptl4JHl~&b)Wn^UU8&e*VQa{nW_Lgr54ynMpK5w42*i;$r2m@|rrWej=p^OxV zQ{U2??&w8ZSB%;^RGu$B2jw5eg4x-#Zc0lox>vLVPy&8*VS}vl!|SGA=$;2-9Kcs8sDAq&2(>?`pWm zuq-aSzwt)lJ~y$NPkG+Qwv4z>R8To>sE9ShoODAO8YF}sq97?k_Hi z$$y}X8p5_VvS8AlwDTH!UwW0n60|$~Q0Ir8#$3y^8?h8Ut0=O=0opI}4{% zJ*H0OqLj|23;F<$O69d_;9m&W;U+|25WPn5zxj?eeLL1+haL1Fns@I678(>a*=&ExOZ+iLN(O6{4;~*YziitiAdn<9Q%b+J0!=t?!k1`W zKzGNw9;;zDa>LLVW%AQdGA(m=MQ*}qn;C3#4_476Ft3?y)Eno?@XA!8Yq1S=HiY2q zyZ8I$^0Pz3HG7pwdyK(HGkc-XX$5DtymX#{!@Cx6Vc ze!(ybJ(U+}p?q-Ic|p zrIc*+q3v&00cEeSStVObSqI|)T5)NiY4+KE`g&gZ*0WX-gQQH9_U@|V_(I!chCylS zDuqV(QQJS)HY#j!>7fvgtzdZwY9m-_I&A6>MXT^gu!om}jUbX~dCD5hZCz#3du;8! zZ0X0sVZ0=&9=uuJV~@2^U8ZQGd9^JWD%nmG6TtqGyvoV7P1!)a)e1=nMLt6q5$72c z!XC*7=P&Lp!(kKxT} zT(i(Z+B(p73%x)aoY>%C4+W_>nx4IRf15%EDJaXk2bzs&wh-$+LUy~v>(or3;$FG; zq8eFq47c{myT$grC@?H{AXcd!i-YZ6#8f5te~I2w1zMk zis?j^<|0up%Bp4C#3BlPH9a%W;XH^V3Xu z02xI=@%Ta?R=7v@uJR5__X6f`nuw3ZZZBW7#W~c6{*Um(J-)|PHdW?=Cv?7tb1C+N zLj+vbGI3VXi1#jyZg8xl@wT!|fyE2taxMn}iE_;MD<~wH?KY1_^<^pmGMBk`OiJ=^ z+6Ca%qrol*@^|tNcWe*6yqm4GNqkFhF(0n2--ljmDL<*3O0UE=8>s%gSZM6k2g`es zw41!~?Ui9l0-HPuCw}L;@1-QAiv;HrN-rqHu4Z9P2|&q5B!G5=>XAz^wnWb|9>Xo2 zTl`VweQ{l=xHFnyNjoM4g7;}{PhJ8_U5(OgG~R5G`bcb_<-)`F-Ub0%Bek4RWmir! zFgQ^Vq;lEljEs@y#h2%p5(YO9-Zo0%RG}@>elE zywc)S*hH&%;(_yo`3WVxZN7%6%8ZigyNknJSuci}I*baXowRr)UE-;XYLpRO(~ae6 z1Rny)t2b|FbUgw~l=LO&k!{rqGk!i5W?Jz#{VuIrvkrvxTF!?GV)<*RvS*vED~6$D z0kKsHM5f7_$ZOjh=9{Ui@TbL{^Q)0a-@ZIly0bLQfJG~O3$oJGKr zsI++0v#cqi#%S4NMMFLl5?-~XMA7SDoJh4pzxIlxY^@DeX{Pppig{~y^>4{d1)v+r zCeqQ$B z;rVrU%R_Rv0eQThx(N5ktvA5uNCHy{O|3%`{P0EV$-*j0i4sK5lyxR>99uwC3c zlXx6evVm#YhJDS0l*$y+!$LhXqA*?0=Ii^2Ed-u!YrLNaOd{WPx*j+=cEwc8%bHJ* zjU$8yV3MVOR4a%>h0h=(N*`_=?guzi4kJ#EhqHJj)*-rVtAeV3>Kofg=i2U%Tg@W zx0eZ(KygTeA{!q4efLV7&e~4FZ^RA(4Vr|(1nlNZ^%G624zl6id}z;9a^g7hr(Xm6t_Y-G zBXOdq&rAcM0`71U0K(d-c@Dh8zOpp|oGUJ0-s)Js&g@6qX7_Nu_qujo!V`#=Xm!m} zz%$Awp@|j}er`fS@4GV92}T@tC^xG|mJayFKi)O?jHo~y?VD9oqF1;8)YmA?>icYX zW!Kn^Hx^sO~qeqRjlP{1Bs{PRO@6 zF5Q$ks)oAOC84frbmlv4x29n^&|NO&2Jn7T-eA!t40+7tg!7T(%yk%MJs(=I>wI($ z&~Xpa%@7tzzK*`tPtR7Y^)Pu5L2c+<86hh$g>OZZznAPcUFRgpmDh-|jY!&$N@|CG z{>~TOt^S=@Bo=A${`I>Di)P7jk8;9^<(G$*48+WI0gq-JPrhppr9qIvx1v~o01K&e zi{>zv>)04ug1LKwVhU&5tZakfyVH%B?8I_dzi+0;!!?w1EYhxrCL~9?Ms&akz53DYCSo zl?SWr9VZVj;1->I&R^)ahd{fB!G1}eGp@OL!%UGzB2RDEB<+9A>v%?`%uDLl_Clfv zl14FMV zqZ+^3r?=TfhbJDMB9`dHE&G2 z*R$}}V*g}ih=7~I*b9hZK*yR!@m)h?wEY|}zw1JZP2X^#%ZrmB8uP@xZX}Yr@bJ3G zTy8^=6`jIgLtlCY{UbQXy(H^9F)g{q~u z_#Fk0S7QmZQZP{vEJKmWvppU4FN3*&d>%~7#E8NT7(4Q692B4xe-H@c za43_#)xNK#06*f1K=~GPT8=lZrG&7S;EjGGy*;9~B;@w;kM|)2I^T>ylR^zCL>-QU z^(IJ4;p|M9@C1=D^;MNb|7{=7Phw820KyfRS3Yf7J_@f^7jo{QzfC^`x)IrxxGQAh zI*&b{>(YI5wc0GZ2___)gTa&)*0?pFtKBCb8EO!nuH>Af=fEM?pA3Ujr1uV**w_R5 z@~}%FqrwgV?JA3~e9S~+PhYn6m^)A~gXbL)Kn5ufFy~fJhQ;>xT*5(VC3^P~L^7r- zc1tMR0+E)U*Ud+5FF09<4jfRK&m>|_jfemDLqmSaHpo9}1Ik}A>A#AN{@2{;|C7?7 z?__3aXl8AqW2k4PXJYiw@(wAyy>wBMjLqmEXU;qFQ0MG~6X2m-BZD6eb9U6=y>dyPEZi6WZI#0jOL!O)4W}1CG zTNuCl0SL7}6B?r&T5esaCXcecw1}yY09MXGzH+FIYeDUZ>yy@hSKEON1|V#wBeHtN z!61n$y+n0qJU_{z_lKJAXSJRJa=$u@Ek^iRJaE^9+ytlPv}uuAYJm8KF8hk91UDn@ z6`zZA)QEPq39VTVgQ9-6@0MIkb@1nW&>wX$F+m%3$R*S%$Xu3BcA#lawVco{Jl3ZW zclh=891XwWOaCg4cHk5<(*#=Ru{v2>ty(gYk3j3#VVB%Wh|h)8Sd1+wsxF^iNNOwv zKhU(qCK4z4L7Pq1zxMvRYcJaDa-Zv+Zq@o@ySC-rz2M5|rFHu1N$b(P+#pr+$9(}; z@qPpRS?h|1;}5I9fdfw%^Y=MP)~_5Qwq&|{*PW~H@Cu@_2V!IHk1W|;l^2*J_pz3L z)^i!qne1L>@1Lzm9|k46I=X2aNP6~$_lGLd^xp3AgPyySLa&cwE8Ig#`618eykqd` z1URnsuJCtkx5HvYRNBvL1Af8Cd5g))5l}l}q@jn0$M3)OzE_i%mC~o50T1Iso`jbQ z$#0=K?eW(OnR2g=bjQQ zIn-asXO?cs z)SMH$U?chPCXdg>-Tb~w&R$Ao8e#j*+_e^JYL?LLWo=b=t&?v2L|zFCo$H0}OeJB; zLS@q}7kjelGT7fdbn`8l6W2zagrdaF5In-!TIfTsq;xQ=H#s2&pFMU@o8aW4*vd|! zTFO3|4-j1HCY={}ty}v7!HMZY=fKF1S$3W3n1{~KjR1G{rND!^kQaO+!@qQDU}N&M zqmpSYn?=2cG&7&eDP{KH(qDPlgTUHP2eS~)B&(n$bGDmX-2JC)3TE#6VGWnr^ z*?fsw&u9rWCb(q}sGSo@bD1d52wkD3=kECoBSKhL z>~_uQzDikvc!}_dph;7;AQ=jdG{}vkPIE4j1U)fkmw>8lis1vS9j}?!G-x?NJ)67+*6Zg0FwP3c~HJpCo5Aeta%&RNdbF!P$XwGt4|Sb8ao zq}0KpC?QirB2FX`8A2U$R5jHqQ$rYKSKrir8Kp`3v>i*hCEnlmCUTUWab4cgx9xBT z0WDNlT`qi^fX5cR9f_RwHBsPACk4pK;L#%)3}F*!Sai1}Jokx_F86dwg$SUz-frUx zH~7^k*KjdlB^+`&ap@w8*eziNsS7KtlUH>?`(Fk+L-x#x!l zZ|Q`FKq8mFf?QLRqY3`K)(}2b+4hgt@P7*re|3ic5FR|F5>zmTWqP*T6msR)`^5wh zWGO<6b8Blf%>{-5)ID%6xnzxX^ko>^2Vl)8H8Oe75@Clv{IG8& zkH!h$m}B8Tx?OuMHTXyxMbIgykTZuS5o`Yjfa!67E8W>14_HQ%J%Gb55VdJ8b2U=r z-VH6vJ|gfY7}up%`e=NYIbS8xeF)z3$lj2~GkY&z-arM+v+BOu$zJTh8Ie92e?s#d z<9ibRxWFl8GsfSG(dqxC^Jtsx*ws)H4&o*3WzNX!r25fVTb0G72^nl5@2;tftYs~W zs4=-bbD1+;ep}aBvyOZ0KRGGe9*;%PI3v8Wu!It~urkd%J42D8jDt7S=~E8Loyn-1 zHZTccMYqf?2H!+p3jbv^`N^X{h)tDUQP*fMsh?ESIc5nq9MP=Ri zE(e7=t7@SBRSd+jxEE2uRo-9>M=;B~y&DJL?GGPm3k*qQvBSE1TU~b+0!Nh6pNfn~ z*?PhF&<-+Dj@8e%xI9PMQ7wmF7ZYU^*PT_bQ2EZcnhQ4!%t3;WK zCoX8Kmg!AInU=enpG4a_u6L2QXTO!K zl3dxP=83b`1qv|*7@q{8OOVhtIsj6I80b)&)+*Is)m<9JJH~}7E-Z>e!TJ{~l^9O; z!895g{c?3pJRcoyR!~S1XR3n1+)A<7TuoWM-r8i?$DZu!As#x)=L#*=exsiivb=Vc z4PC(`PvnUfv9IE9Ryg*tX9(;=;t~!bT@(JiQ>ce`Y5U3qUovJm2~0?qbP+DDnAQqA;Su{o z`g{R^W#k!oo-H_%fJa^f>VtB+#-!@k6!29ybEh0io%N%?K(ttwtL$F1whm!Bf-)`U z5O>vbDI?gFgjYFm6UY=(J-QOu;x)Sc5TkSZ?3L7~##)+w5yqTe0SfM=YsX&$Bux?(u>Lo8~f>#)!elfIT<9&7qQOIY;X zA4`{aj!%q8M+rP)I)i*49LKMF>-=CTBIZXwL8y?AymwiEt z2K3&U5(E*Qhfoka2VBAeMjAbWxI2ly$}ijg6>k-o4go10^S6BR`TeO&_m`<{LeHpI zX4WAa*>rqL%axMU_?FZ!q(XxZ;tOPih$+W8%)UVDzXy6z;(oJr-IiADr&i)Uxle>*g={fDTKXeFhf zf-`*Gb$JPWSS3plq_4xzPhU-dr5o@_G=IlvnPfCX=m%W1=Sw8j=VxJKMhRvv7Z~2! z)Tu}!)khE3oCdst(9PwQERwmRkfE;4NC};vZaaK^+rc)Uqas0?(IC?@DV(}Jw#~`#4+d3eIf?#c(UY}c>Yni^6{;(EDbp95eQzW696gHlnk+E*YORX}yebjd&ybl0|&3 zihyj_&2(CT=~hI!@26OMt{vj^-I$i9OGQF{3tOZb`f7Xs={N=6lZ7EZHm&in=>4yh zV(4>O6s`AxxgC$v30=PWGl%V@b>a|;;`13{lUs*dGg7r%WkJ`UFm&+zk2 z#Oh=lt779~R_6~3)`k_p#YLh0VdJaY@m+arx$c%hyBAPug%9q35iDl+=zb+@Ck<@2+lL{eJ%9z+#a@xil z(TP+J-X4+ahZH*_I!EmbS!NtGMYh4DazIK4^rXHgOEur3cQl-xdMi73^L2 z@@zJ!AiEnQ4D?(lGuA{YHy7=tO%TJhui*(i?XgsO3$c)LNMr1f)+Lwf4%N~@*pu4s z3!bX@UVn~zF)*X*t$1tY#{?IKAZDtZ=l{l0h$K$OzdR60?_pW&% zeI{X1)R1n2E0!w}!ujaaR~%}XWcP*&p?hIXV=rDE%6-8z8<23S)QQ%dGqRAMLreLi z{HgmP9roucR@R(ayXI%VnbeTf;b!6y)AMH27&8(30n}2OSb|y#8cce9^6eonf~Ru2 z`sFm!!{M$o-{F8Zhhf~{>s#;sqOu|Uf=1Y?Fi_M!@^zNqmW+;y{BYp1C*LoM31teh z7e-u)d9QK|upr#!1ChR-uVQ+1t0Z)jGoJ=T^eY z;!5;@g#uv(9RdknWIe<+J!cQy0s?5rPXy$^g@WR zgJ~wQi*|5)SAO)!Clt721>bBuPH^(2dQTS?gC=Bh%J3kJ+u4p<(4*$Pe!#_=C zhFwdXwSU0@{VzEDSLbE_l~MXnbcX+EYKT{pRZzhgnVISmwW|J>LNMj; z1r>xuVW5F>vhYQS8wwt;Rmze?dKv;v2`>5t(>mEY$Sd2MEY6RDvk?{v-^Yn-IKl|W z*(@?Lje>#a`jYcc1=)A^D_`c|U={%EpN249Kozo$)+{TTZ8Q{%jQ6jqSRM7iCeg<<`1K4XBikw(D&BJxdoCjE%8AFzv@FxAzH$ zub9C;9n`ED7ed~4-mY-13X(UXG(t>Q11-K~nT;DC^0%nSLikX(f~UBwtmJ$@vNtS} zsFjDCtO0MdVUMQ>t`-h zonaIpO8vl?&~jQ=H5Rc#p6Kt*ZY0Ssm)z7(22My^o{g*Sh-}lsYiBr`HKkOx8=6vD zQ-Y%j?r0N#eF?GO(me^TblLll$Ew8=^7QvN9|by?!9u^J3t8O={I?z^+v#3QULmtJ zYy`2yppmayUb+wpm$y-4$s>~qx3|lG%^&2Az`BHhY<7srj|=iTe+1X5wdErfLqN6; z;UcE1aI#x}X4&K%e|Txa1|TE5=AC{LO5tuw;xeuhF=53@AR)(|xpk;vIW*MImGGF6 zYjC%o1sE$(ZDR1$yyIrMKf)-7)aFTT%Q9&L@lUj33Q zB~{s2KW3Q+Z&}|>GP*JY7cSu0ZR3lYON}9oL;8fS9d;Nhn|^fJrqBjjL)9pYS3Ow= zS2prAG4i2MCsU;zcqHOlMsgxs?&T&kgqJ{0qB>x>@!mnDO$f9vtGrP}| zn|a!h48CqVarhWh`_z}##O~df5g_I{EjxgQ7I97vL-(CWr-SD`wIQz{08e0u`#Fb$^$0faUWFZ zOZ<^q=G6Ll7!SW&PqVP~ul;2h%=)AbQT4Lx0Yvb=hjvOu zL(#3=#*UtFemZluYDh%=4w!b->|cEUV|D-l{(*hu4+NdMmb$BmnR+dS$1F(Cby zU6~~R`4a!L=<S)iSlu`i^1pJ9~`=9MOrZp--2@Qx&5orJR`?)1Wi zG_seaa4kBJcrXv8yzd%7E`We;Bmn3^LWnLa{w@!$f0{XuZsn_dE1hcOEnWMGTE`5~ zuTjC3N#3F$F7a4NllMoX(f{Uf5!kuB;G&N$LZf9eh0-IsaB=%0;C zOXd15E&P7H2FHim_sYbgCE?Zi|=YPU*~-&?m!A+ljMu54!2rOgKa(z671+p&O*hP3nA zx%hNMQww`UvF2LmK`BIp4l$e;?z5qRy{Vg_R%@%%)aeiKx$aAz<_K*y1s5p!YJ9X! z*a+!6sTHRb9>`GI&&LWc8@*6^-B;YVi@ak~uRZNJd#VmKWcOdoo|X~PIkUA=I}g!M zB0*l$^i5?3@7kh&$m0^dj3*g>ra~Utk$-46z`f%Ci9ed3#vGT;?a#fq!&7Y{bIdoN zcd-PV;Dp?}Y-O#DnoNmRK6(WuL3P_tY!5Rj zsI{!*K_6S{tUdlz;G9UOm`s~T8Hb`{&!lqf(sDV>u)-t@=i(KC(y}RICCZrBb4c zthcgbxKr=gU-+fb)~FlZ%TEp+;Y>Ua;>NTS6n8Zxef`iRz@(H{p{x@}ML!zYgdzEX$u_DUVHo%WqknQ*jTsRvF5_WFZgCHat%O)LO6s60rm ze*8_?ap^6f|FNX!Q;SMW>JQy`z>sFZ6@zDFmUg43KQBLx*`Ha-{wiHXw6K`Bv%;%)(yNg=J{k`C$BMVLe)HGWKM^<#^^ zHI75R^3uU3axiH~eG+S?I6)iTidx?CqfqA!_x?5#9T4FMal@x7bq4I7QyA{f%VD}I zoAGmCSljQS3$uC%9Ez(Cze5W2>XrwyJYyyXwO&zkxbRE;&w4*;(rl#^LV74c7^*56 zX{eRPo`m2~rZOXD7mcfWP{NWdd)MS#Dz3cEOZR-8gt()k8nN#CzvKVFBZXHEubhP> zB5>^m4AtXY%?YXPY??nw`H>HQpoJ~1P>o_XoKaMe`Al#TeiuSSwMh03X1rHWT?F*u zLPAp|{&DPH!|>j)!_WO&6GW@KAHCT46JzZ(`iwXEh>wl!oE#-!mzK0(BGA!A5&B)M zu+HyyeO5;UX_B%Lh4yy4!EU3!g-H4b5X5-(R8@qBRi;mSFwm*|mxD;I4%vQb zP~$TtD#tBHpFii4Rk`3FFe77a%*KPwPIVz~MS`S2Sn5%2AK$RnI&LxysL1b3-Cu=(UBJcdPD1a?eJwa#mV1+5&BtpG3#@V=^@z zH=UZtmbJ9P$(^)QuYij6Zd=r)*7lr5vads?!zx=nAqUgiPBmnq1Iv# zHsO==L*@gP{N{DN+ubRl2TT|_V$Fo~9%5r#o>2zJ-pdRN_ghwCcxCC?$fEG`ds}Gm zeyMXr{*Q}h%GpO{+I2&nR0%5yyOJzxf753PCyRmBzzYxY(A5y;7IPpU7u|YIKZ$VB z-daM7(WfX(xyp)MmMM_X#4ciM7_&?l)bii(DlcS(TpZAthrun(s7AZ>qCPTvPiy9vcqRJRZI5-S6-H!ah7@^F5mCuXe*DK&% zQ-KhA>D8U)ttp7e`@0XeO3Puc_tu8`52%oAa$ZP3!L8iskkGH9ci%a%KOII_R~dOn z*QIT@_gKC^6Bt{JVUtVcOXcbXChg!z;PQ&l36+v)lJ2Xd#k)*V@>1KcHc4B#j=iF* z;xsWmqIqp8u9VCxA&q!%JV4mR2zKg}esR|iojvAB)i~3u5=SZZY+rnX zq1@NZt~?%1fUayH{lTyNOXC|T`@oUgy5{<1fX8c_p=IdZT4418TmDTC0v06>wn=(VUnKnf{I-2j|Be>Xdg8If%U=D5HQP0A$ zOppu|8}F18o>((Jf<{Z^RO?Wo$!-Z1Vb7{RAg*r5Hv##2S-HDbyzd>jYo2FM-u8 zFXVm~mc&!O-;vl!MmqPTGHb3+3%JsRy1~emhBaLFn-b0I|LsvUXipvIdHWh?8ujSd z5Bi8|{nx2$X@5g~z+UgIg84-B1=LgJER^l!ZqCJ^0adg|%Kxk3cjlm0n)qVBQ zda~du|2fJ)#W_i+`c-}#$vl0=b_`?cD8zAuTFkXW`>W(&DuH}zD?dysC{^Ngl(%ZX zEB-KO^->i9sr6E4HRdU4Mco5J*mD;0bq0MlBpxVex3fXQAXz|NV2}nJG>!1_X-QR! zXeYRNTUc9#0nhxPW+HA96wlEx;g${$mU&l@V$ls4IMD`LS+Zav_s%^1hGkd-m3~y* zwB3zMQ5Yi|lVPqu)?CGb4fQW3B2GYt0k8pu4`7-zbtXYa|uu{_x8M69U7x5+>2CQ((X%GyFztBa3QSKC# zqIw$hhQA<)lO`PJ#hxnE!5lv^Bc2KZDPTVBB~<{13mDGGkWD(JJr$0rX9Jf)=;Z#F ze*Z6gms#q|&D^ApT+-kr{i|-%jt)qSvIOh)i65R+*6&UdyHJf>dGc>X-ec;13glhta*EVaE_UM>b{xePrBsU|uEsz?5Y)B7 zeX8jmUk=bz{Xl##zW5^vdSy9?Tp*zXZmMRFS3$9Ua1pS~8Q)fWi@ij}3uf6ZUu#9- z`6%5m*nIl_h}a51Rxzp(t@S)cX_N3CrC$WnvPoW-tuDaCF6D+tLes&4SVC7hNP3+= zBC6AdJp6E00efSQAV;-TnPZd{G&zGqY9=PNK!{xh*xoX}UWjwIjmBMUns4xhW&Bq) zZQBNlqM3SMpl+t7uE1*&jSwWyM@s!V#$XSZs<0 zlO&U7QXj2E^B{h7Fk#o0{zO4kOkvN4{jC8Pc6Bb7`G3zV4VUJkW@`{*qRA2GEmMR1 z%_>|kbI(i*As>fX@lmvoVx6}n_FlAo-kfYjzL+W+z(@dr2(CP+jh+)9Q^&qesguF+ zD=_}bPL^SVWYg@gP3DcU7bX88I|t5mocGHwqdoWs7h*S%Pk(*Bag(fe&Y1t z<{Z>Otg5vk5Yi(t$u3DWv2YBD-B@pNSsK!h77a}HQ(YMiOyJ#pw5R4QpKyf<(oDpX zGlf*BOi67DgDa9_A?Mvf&UGqi7_OlWta@39D^oL^P&*7x>L^8heNF72f~Pn_!tZ(y z&7}GDHYr$}w z6yPE&?QU_|OTqo8V=?S~(NYf%XR*|Xw5C(mkb>~$4Gtnx*$;~G&)iZN8OjmF9v?I# zU;T)5PTHVY6wEtnmiJlUQF>=kdnHI{89gdA6QtvM%h*pKJ<*F0c z;UME#09)8u4^nKh+`@;})V$p6ls(CG8NzQg}Z80G&B^T7BYFc1IQxLIQI zw{r8J4>lqqEZi`>@S?S_us$S_$!u()hHM;*G4miogzQ!?Kn_$N21$R@j3n4eD?n|9 zm}BA++#k=xZP4&%d_yN535tm(IuEs&eWjCWQT0>TTi3soZJv24cEMuH1Hb{b0Dux; zn$aHZ2A`dALaCwN2d!M^@l4Is`*4(|62WUc!5HGW=8c@U+9!1XYsr`e&i7m86S?vY zqWg74Q^n*~t>CQe&!gG{D^|`C!wgoIjc~Ryj^`fzMr{f1mk$I#G~kT{4|qr6M-IkR zG&1|(G@E(O;+Nw#qgcozE3X@R2;ePLJ%J41Tyf*_H+{V$L z)Tfq@sl8%Xta>~%?Z1`w6;N?4S=SJp;O-DSXt2iJ-642zYuq8Y1_`b~0>KFuTmwNv zaCdiiNszyp_q}0;m*mYqQ@w8Y?Ov<*K6R?st-GrC*`-vupK?VEs`-g>R^kxC zhok-o-_L4uQadke_KO$ZT_4aLqf8bQ23{H(Y9^fb-<`|8t~IVY=qb1iP4nobI=O;0 zwjSy5AF^>FoNTr`_#oOPx7`U}96zvcDQ*$Dv$<_EKB}h?rJ|4LE|*h!gWxSGck4ma z_{I;R*`4F{mfDI4zsKEJVxoS|4|rMBk?-|beV6;KM^{Bx2eL}=vo&?cN$30P+`Qt% z9Q?yXSIRbe26xr`Z;x+|kz~DN7)P_{UZ&KYaN5me&C5#N_+pfKrQ#BH>AFiV0KZep zo87Tsdmh;?Nv9h+uZAIy>s{{m(chJ?cC$a9+g$`iUKx&XXqS_M&*0FZ<*MbzoBCIc zEHFAose15;NBfDF!o=oEUlW_LTLn|CK(*t0RWg3M>VE6TJ67oQfgD;cqIRA5voJDW zUTK>5ch#^2zLzCRVkc^z90z;hH-g<%O+YgsP5Bt$Em3&B{LzLk6An$Rs86%~^YECOt;%UF&=tGf5thn>z9 zpA~DnA8@rX4CoA)zTR&?Q5?&q$nhn=b&sz&=U#TxY8&WU2Nbl6(|pTQCVS2RKF}F5 zJ!h&1J6fX?NL6xe?R_|zxBOvZ*MIFZ&@#DUnE_RH65Fd2!b9S&i*rez;k)Wc8B1jt zHLal6Cbw`={X#?8(>t

m$l@@IuuYLF_7DepC969koga0nmn58SKZVv~St?Hl>su z(&qb8rdk0|>`mC*-fky9^59PJ z=r_}bJmYsKGE$D&y3W7&c9ZtKbRe?m^I9)Dl)MnA*0O0TB5o(LEa1fq7N4H@NO&b9 zi`OS+;WR8DUXbHx)Y{OLhl=w2ER@37J@a`L!;KSlMFWz?k20^dRQ8>hN2npak!tZ! z(xtq_IYn6h%`m#IjKebh@AYKw=m#^j#-y<6OJQ(L>>X=p!YQPh5at(5N2N=RvW8*A z1_j0QdiJQB@u4n&RlULyxdnA%UdYX1E<7_}Le;*~<_Fc7bO#ya1Wr@5W1sAd=Zr>4 zX-cz{d_QL{VrY5@6^EMg(*`>mQDtu3~Kf78InscE^=8)m1<|IqAGFOYSxJ%LYwIq0k zxCvOi=ry0IdQXzXH#qdhMfMijW3aR@!5vQF(138kY9RX7I_`i%d@XlW&_F&UMK_xY zvA}5J6p`M+Kxfkr`KhpzM^u+grem2v zkL8k??WH?8ZDDt7POR#dNp!VEe5tz--F~=9;Z@Ru}Sb6Chpw$#bEGdkhdlC&x zlF9a*B#wlOnN#^mv9$?}(g2|*(syRttLa(yjz+YE0Q#7fVwZu~&Z^kNv7YoewQVEn zLOOe@@KWaEx$OFlTrS$9_g)$N1Qc!6_2|>Hxe z_H0nM3<=;Wm*F}`e8fQ;ie+xIyZ=4L?U1wS*B~zQVLI}I!7g~UaX7_@ajxX@l$)NsyAiF(w#!}hK=Z<+ z<*nHZOR2!}KwA7BMUDibN?<51UeCdjPPB_3##Sve+qQbhUNO_evX^^ZJmZYZs^B=w zS+qA3lZY3sKZEh`bJsj%Ky2E0Y7;<+#^Xfd+6+y0f1+ax7|tlQo}3f30IPpDWIokGFbb~^Jp0Codd)V9ctD~x^rsNQl9EC~k_a8`u)Bu9 zlme{&i_-3`OZj*U{mkUsZ#m$ir?DMxWOp~W^H*!$SS7z zUOzs=qntsZt?qPqZO|Og=Su@Xo;1PcCd@+Ax2$~qNfH6Zyn_mj;k;bEyDhs*ZJpW3 zdTL8p{8jWuY9~WExZM|7S}7x& z;7EmP>t#|zhJAUkBgq^icI|2bNH%S7v}sNS(5EJel$@ZpB23v;YvnIgos(e-oW zr%=+&A?MK_a-)h;fLDf;e%J~wJ%ALfEh=C8y~xPpi;rtuk|=D-0Nw6++Q^Pxxt+F3$;`M=#~9n48Q%4NUFL z{3UO(_R4#rlpH2(b~7JXnpDMDbUb&QUF+koUpd@{I@CO_1c}wHmaA80Yaquq8s+eY zPfY;wP&5d$#sNUZ=f*Zs_@3Gs8bZ6Lusr~`sVOad05dx)&!Iwa*<^qo-y2`@no8xT z5zm|>^=4Prkf05U`c&Qt%XJhOG2-~CXlvs3i#awT z17)w-!d_UYG*@+5AxSYEzA?#!G5*1ZQH0izdMw(FN>n$~!TIr+*N@-7+z{t;xK9(n zc)EM_`zi+81?-CT)smdhwAtc!A#Dnp6t-0_Zr%u*Nbr37Loy5J%rN{E7{8>Lhz|HU zp-9@cb5hM& z#y!C6q?AyBgpNrxlJ;;F5k#N>xnjYr7%wYxnmffzAKlXsYB}ABm>OZFFmt1nOP+iv zDp?Fm!yW`+E~l>Hs}opTwE_2H?Fdzx3tC-lwoC{Y`f_{3#I4+EAr)Q*=gDm5cRozA9)iYgWaoKsr9qD-8l4j8Y# zY6ziarY}Z+#2hf2zkUX*V-WvzR-v_^2!{tF3I;-@hJMR05m)_dfps-BL|e*aB6tDB z@RjCKco0AKlgelY76o1YU6l?)9FOsKM2N3TnfvDYS@5$HZ`kDKeFuYOh9MdDY9!AX zoW;I*TEDbD_fvZpA zM(lDeg0*~Pffq>XOTMXRY>DzLMXi~4S@G&Xw^=aM_I8=H1q&Tm9eu&w4?nIjh?__) z6A~hN*?J{_Ug$RZ4&Ic9{xJ3QE?-wWI=RWxf;FT|V>}l?QLc2pdb@KXS^<}B`JMav z_-X*MEun*fkx~YrB1{H2?Fjv_JYHH+8mWzh3zQa6OB~{9Ay8^35VwDOy_sY`|BRtl z#f=S~F>-#$m3exO=fVYB@ougz^abj=VZZm+O|4v?%B!LZOrmSeANHTTXH8F=-36L& ztsM~@dvjsYik|X8x_NW!-?Xgob(4=*jrGfRefvzh`C5k{J$Qq1j|-sIJe#IiGa-7c zN$$#yk7@M%Okra*ah)>$Ips$yAz3}A2A23EN3RmrY0J zoM)vE_#rZ*iW8W-P_mUsj0VILy-tW{RGZR`_B59w zY4Vn}OjhWp;2gUXA|4;X?mo-eBczub~# z4$|dGVEsr9{*Gk1sO2Fd0fD;RDuCz8|CW27!NP6`dGb1lBgZHjO&wZc#);r5lM{)o zAvBS5$p@A5qPg4zs=<6p;yeWpbf~Mz%mT{7x3mr^9?Ql+PBB<~e(g`Jd%=?0IW_2< z?GzS^NnE&PT|+P5K@yh`bp}yz8A|@Z9Zz4dJF(Msx8_qt#$g@sh}OK4e_g)wEVQ40 zW11l0ImT#cbDEe2 ztDZgJb#n^%z4oK;yUAWXnP^VcCQXb1&yYhwP;e@n8iK!j^KZqV_p-OD@<#L=(9D4N zzs?Mx)#ty?3~n+b_vT^?2NUmxpK8Km05b(dyJ&jAjy6l7IG3Xe&GZqTVnD)#f78II za(Oz^Low>BfCQfAAj%RfE(0EE5%u0K2!~lqK(q#RF8W19l;Zuw&^WMh19G11i%lg! zf&~Mk0lCM#r&wly)9g5eZ&2=rH%tuB(y!S>F`2qaKw2;4v>fB-qfP$&0?by;xVmJS zx9C!w>{BEAC-QgE2#aeBUyT%! zlf@Ysn{{x+d_T|Ur7wrDWJN7|vgExmr*dNycNP8QQm$8Rmgg`D*I)H|Q-e`pBb zL4#7%opDY`If)_7*=;8kOVvwv8Wzr3?EB{1l*w1tKbgbPuQU-eyyfW^sL~=@x@um( zP!uY^Ai5T4G&?O%?aRqjYp9!vTXC#>6DTO^v>7yfEZ0&b5OPJC&Rh)yJBwW(NmQcMLL2W zK$~QQHi35uzJGYOVj(QtaPMlrmkLVelYHLr1g$i*2^u@NE9f_6NmE!N2oyze46g1p zg25WZ&zNuE>mhlN&8gjB_LPB#0yK2qs}zd%s5|Y^BaVn00^zteRWPrL!-hCUNJZQD zOz2|uMb2Yo8d&5^_n-^GpBjo(<~kejO!qf;_^M9PoEFMIsfmto^{|*{$rAZwAdw|S zE{I2C`yMH+>(Ni3F!8Vvwr39>BBfkJKP4qGg7o<1}Jm*c^dqIOY0-~MXprzfzA zto)3N7Mz7H+g+mL8L|Nv52|>!xhn}$a`)*t7dtwhlF)v_v+Tu>O2wfqPnh&Wio}BO zy&`5I!uYh^;CMhDEM{2V1#D07{`Kd|?%!b$+<)yK?H+>_boO%$M|*v9YsTLp z;r$=ZAJJI<66s!N{(u7uMh=?hObT(sNkHF_03Fnzdfn{YzX> zuYZ9F_Kytw3k(?5Ki}MsNzX4a{}_{h)9gR|SnkvMPaM$6L!kGd$RPio-=vlCK}7$M z)V~%Jko}Y39#i>mF#oCR|1-!x#(vjjlL8W??AHb<`|s_L{%qO4aeo8&5Tt>=qdvW{ zt1ZyR!Q96B|Fr(BoPYVp{h>2|aDR+dTf8qyHvS7YB>I1vpr3Nzn|#|F|Jx=IkGZ9J zHz;60+v@B04OmGXhT zTy{G&mIE!rv3W>{{OAe9;g1Xv(5wew!weT{d1q4$J~$o8y>j9%>R3C z(2)P6TK8YMe^ws+z)fiLA94R?z<*2s*BRr!{Opf*KV$GX{fhqnCgB$o+vAG1`*O4g zNblc6{!4Kh_%H9Pp8W~$ba)ZJD`^O>RTAe=NEF;cw>5`JD6ne$P4S=?ei0 z0AK(B5dq{Op(mg@Oqk`bD&j1YJdDipR}TikGhVKz@SoEUm}Q|y!nHu~h1jNIP;BvQ4QuLW#H%Xo!E_e=M4J_{*N4+!rqA zBnrWovWYB{ef7z5jAmqU{=Ip3iLu43OWGbDpjatqwAXywb%Px(8`2AZ?>w#QqEg4C z%#UBP;%jiy-7fQ;{o1E*rH{$@yC!xQ(jZt44_^~?*4D{cu= zw~tYthQO@As~sVVcb%!$M?+5{b4R}4=e2SC+3x5Vh(mGwEf-CP7i?K_bhxcPQt?Ng zIK@(Z;Gg@_BR{r=ZFu$>;uQFBYqhm24pp*+5{|D@0wo36v>|ZGX{*#gQp8!Xe9r&Q2h-*Bz-J1`cGCzpP=NZARa;#W2vWZrR-eVD*JtJ`!5X}|3CQPw!tCyqv#WpTtLw6Aq z-R}^FbNQY8^GMHR8Q1iFMNYHERaK!;?u^Mlt22CtxT&^jHLtyOUBF3a9?u`Qi>9+F z4x=SOV2Tu!!%)k~L)sAaXUVx_~GK1ch|xsc*07>V{$T^5VEvhhNMj;AXRb62eS&AY6I5Gy@VZwA+*YvnF>6+eknoxurtD9JD+x^8Fb zg=vy3O}~9~n-B50S%m9_)%#lzRFSUfg|1aHw5aYNAsAa5d$d>eF(2(%s(WT^J^KGoDa&eiza~#3LD=Vf7T@{@e3+?UslA?xCi)zPG#a!-Am>~Y~NHq z*0a0>SKaiNO9yF0<2+)|Iy#A2Om&?M(iaFQ11*Lit()9n4PX#O>u5=#-ju1${6V#u zsMQc~SJxtfq>p5>>?L5Kfsj&0>%F&`F4Tv@Y4Ow^;hIM0?Fd*kO6;BH*K2b{HC^`u zMVZ?gnHSh{HyYH-_F>o%aX)$$$Rf6oT-Uj<`#3WxG`ZGfnJ-sKD9UqO{6G~s#Udiv z-5Y5b)fYsz(j4{#y0R?6-ZF_4O(Ve6z)QAAVZq6=dhJ@ayycBw??}0KRs_4B{Vd2a z6+#t>Ikl2Fw2c|973snU0ZvlZ#n#)*QGNDOR)HTIDY)WG+F6E~uCE|L*H{Bx6>OP` zOV^I_rFFlsAfQH2aSZyi=8(X+a9#JnF4@qE@V^L*8JbI9Sngb280Y6 zZNGrLSvPo75wmf_W4D<7V z+8d(RO4CoW$dBzi25CW^-9gVyFIg`B(~o?EMYHMt;s<_R35UEKqQj=t$%bh0fOqRx zK+Rk|H=R3#ntVdFXU=3!z-z9)D_E^NO&vE*N z&5X9pm9;JQE3^37mIYR|e3hk!6#Ylf0~Eh5H7)C9I&#;X7{uoj zP+{405UpBrERVAYEx|}`;rl52*y1}MRC}$rQBBc<*dA{$Dc#H6yKJ_kJvQC==K8ueFLO@?z%SCxK_-n{;oX#L4)A5Y zSm{WllG2-ZF_tbZz|FBroWWeAnp6~{m|Q`-su4>~-TeVfN11gzt-aS;agsM8+uDrTZwUX%$mj$_iQDyLP zSB+SvZdjftvN{RYA9pwx=n~6ndNr&leODsF!bbb_6b9-b{W?Q7JDJWDxHchf^qU(y0O(Y8fK6MoTl&p{hAzc&GfSBo~&+urt)0I2ZXv z%Oyz#X+)YxA{d_T`}9H&M31juK#TZH(Z4L%9>-^$sS33vk9{reQ3!d_9O^gT=Ey)> z4nSHlkAxlKf|XoU)e#Uqv4-FiMRZl2Ws}?e34r9-qRaDMr zyHg}G&_yq;no=Ez)!m|M*8LIb<2{O%B38~^8d3WVzj7tZ6K~A48*Fql6keuiFewJT M+%-BR0m$bCKmY&$ literal 0 HcmV?d00001 diff --git a/tests/data/bright/dfc25_track2_trainval/train/post-event/bata-explosion_00000049_post_disaster.tif b/tests/data/bright/dfc25_track2_trainval/train/post-event/bata-explosion_00000049_post_disaster.tif new file mode 100644 index 0000000000000000000000000000000000000000..ce9f9398b2637b144517aca834b301c08272e4b6 GIT binary patch literal 1557 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 P!`~&@6Ar!@jRoP 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 Ee@MjswEzGB literal 0 HcmV?d00001 diff --git a/tests/data/bright/dfc25_track2_trainval/val/pre-event/val-disaster_00000001_pre_disaster.tif b/tests/data/bright/dfc25_track2_trainval/val/pre-event/val-disaster_00000001_pre_disaster.tif new file mode 100644 index 0000000000000000000000000000000000000000..4dea79477dc5de695567d4a3d57e36bdc1a9b058 GIT binary patch literal 4458 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 A5C8xG literal 0 HcmV?d00001 diff --git a/tests/data/bright/dfc25_track2_trainval/val_setlevel.txt b/tests/data/bright/dfc25_track2_trainval/val_setlevel.txt new file mode 100644 index 00000000000..dc0a4439bff --- /dev/null +++ b/tests/data/bright/dfc25_track2_trainval/val_setlevel.txt @@ -0,0 +1,2 @@ +val-disaster_00000001 +val-disaster_00000002 diff --git a/tests/datasets/test_bright.py b/tests/datasets/test_bright.py new file mode 100644 index 00000000000..b29f534722a --- /dev/null +++ b/tests/datasets/test_bright.py @@ -0,0 +1,89 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import os +import shutil +from pathlib import Path + +import matplotlib.pyplot as plt +import pytest +import torch +import torch.nn as nn +from _pytest.fixtures import SubRequest +from pytest import MonkeyPatch + +from torchgeo.datasets import BRIGHTDFC2025, DatasetNotFoundError + + +class TestBRIGHTDFC2025: + @pytest.fixture(params=['train', 'val', 'test']) + def dataset( + self, monkeypatch: MonkeyPatch, tmp_path: Path, request: SubRequest + ) -> BRIGHTDFC2025: + md5 = '7b0e24d45fb2d9a4f766196702586414' + monkeypatch.setattr(BRIGHTDFC2025, 'md5', md5) + url = os.path.join('tests', 'data', 'bright', 'dfc25_track2_trainval.zip') + monkeypatch.setattr(BRIGHTDFC2025, 'url', url) + root = tmp_path + split = request.param + transforms = nn.Identity() + return BRIGHTDFC2025(root, split, transforms, download=True, checksum=True) + + def test_getitem(self, dataset: BRIGHTDFC2025) -> None: + x = dataset[0] + assert isinstance(x, dict) + assert isinstance(x['image_pre'], torch.Tensor) + assert x['image_pre'].shape[0] == 3 + assert isinstance(x['image_post'], torch.Tensor) + assert x['image_post'].shape[0] == 3 + assert x['image_pre'].shape[-2:] == x['image_post'].shape[-2:] + if dataset.split != 'test': + assert isinstance(x['mask'], torch.Tensor) + assert x['image_pre'].shape[-2:] == x['mask'].shape[-2:] + + def test_len(self, dataset: BRIGHTDFC2025) -> None: + if dataset.split == 'train': + assert len(dataset) == 3 + elif dataset.split == 'val': + assert len(dataset) == 1 + else: + assert len(dataset) == 2 + + def test_already_downloaded(self, dataset: BRIGHTDFC2025) -> None: + BRIGHTDFC2025(root=dataset.root) + + def test_not_yet_extracted(self, tmp_path: Path) -> None: + filename = 'dfc25_track2_trainval.zip' + dir = os.path.join('tests', 'data', 'bright') + shutil.copyfile( + os.path.join(dir, filename), os.path.join(str(tmp_path), filename) + ) + BRIGHTDFC2025(root=str(tmp_path)) + + def test_invalid_split(self) -> None: + with pytest.raises(AssertionError): + BRIGHTDFC2025(split='foo') + + def test_not_downloaded(self, tmp_path: Path) -> None: + with pytest.raises(DatasetNotFoundError, match='Dataset not found'): + BRIGHTDFC2025(tmp_path) + + def test_corrupted(self, tmp_path: Path) -> None: + with open(os.path.join(tmp_path, 'dfc25_track2_trainval.zip'), 'w') as f: + f.write('bad') + with pytest.raises(RuntimeError, match='Dataset found, but corrupted.'): + BRIGHTDFC2025(root=tmp_path, checksum=True) + + def test_plot(self, dataset: BRIGHTDFC2025) -> None: + dataset.plot(dataset[0], suptitle='Test') + plt.close() + + if dataset.split != 'test': + sample = dataset[0] + sample['prediction'] = torch.clone(sample['mask']) + dataset.plot(sample, suptitle='Prediction') + plt.close() + + del sample['mask'] + dataset.plot(sample, suptitle='Only Prediction') + plt.close() diff --git a/torchgeo/datasets/__init__.py b/torchgeo/datasets/__init__.py index 8f238abd916..8177120c2a7 100644 --- a/torchgeo/datasets/__init__.py +++ b/torchgeo/datasets/__init__.py @@ -11,6 +11,7 @@ from .benin_cashews import BeninSmallHolderCashews from .bigearthnet import BigEarthNet from .biomassters import BioMassters +from .bright import BRIGHTDFC2025 from .cabuar import CaBuAr from .caffe import CaFFe from .cbf import CanadianBuildingFootprints @@ -152,6 +153,7 @@ __all__ = ( 'ADVANCE', + 'BRIGHTDFC2025', 'CDL', 'COWC', 'DFC2022', diff --git a/torchgeo/datasets/bright.py b/torchgeo/datasets/bright.py new file mode 100644 index 00000000000..bf98711a4ed --- /dev/null +++ b/torchgeo/datasets/bright.py @@ -0,0 +1,340 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +"""BRIGHT dataset.""" + +import os +import textwrap +from collections.abc import Callable +from typing import ClassVar + +import matplotlib.patches as mpatches +import matplotlib.pyplot as plt +import numpy as np +import rasterio +import torch +from einops import repeat +from matplotlib import colors +from matplotlib.figure import Figure +from torch import Tensor + +from .errors import DatasetNotFoundError +from .geo import NonGeoDataset +from .utils import Path, check_integrity, download_url, extract_archive + + +class BRIGHTDFC2025(NonGeoDataset): + """BRIGHT DFC2025 dataset. + + The `BRIGHT `__ dataset consists of bi-temporal + high-resolution multimodal images for + building damage assessment. The dataset is part of the 2025 IEEE GRSS Data Fusion Contest. + The pre-disaster images are optical images and the post-disaster images are SAR images, and + targets were manually annotated. The dataset is split into train, val, and test splits, but + the test split does not contain targets in this version. + + More information can be found at the `Challenge website `__. + + Dataset Features: + + * Pre-disaster optical images from MAXAR, NAIP, NOAA Digital Coast Raster Datasets, and the National Plan for Aerial Orthophotography Spain + * Post-disaster SAR images from Capella Space and Umbra + * high image resolution of 0.3-1m + + Dataset Format: + + * Images are in GeoTIFF format with pixel dimensions of 1024x1024 + * Pre-disaster are three channel images + * Post-disaster SAR images are single channel but repeated to have 3 channels + + If you use this dataset in your research, please cite the following paper: + + * https://arxiv.org/abs/2501.06019 + + .. versionadded:: 0.7 + """ + + classes = ('background', 'intact', 'damaged', 'destroyed') + + colormap = ( + 'white', # background + 'green', # intact + 'burlywood', # damaged + 'red', # destroyed + ) + + md5 = '2c435bb50345d425390eff59a92134ac' + + url = 'https://huggingface.co/datasets/torchgeo/bright/resolve/d19972f5e682ad684dcde35529a6afad4c719f1b/dfc25_track2_trainval_with_split.zip' + + data_dir = 'dfc25_track2_trainval' + + valid_split = ('train', 'val', 'test') + + # train_setlevels.txt are the training samples + # holdout_setlevels.txt are the validation samples + # val_setlevels.txt are the test samples + split_files: ClassVar[dict[str, str]] = { + 'train': 'train_setlevel.txt', + 'val': 'holdout_setlevel.txt', + 'test': 'val_setlevel.txt', + } + + px_class_values: ClassVar[dict[int, str]] = { + 0: 'background', + 1: 'intact', + 2: 'damaged', + 3: 'destroyed', + } + + def __init__( + self, + root: Path = 'data', + split: str = 'train', + transforms: Callable[[dict[str, Tensor]], dict[str, Tensor]] | None = None, + download: bool = False, + checksum: bool = False, + ) -> None: + """Initialize a new BRIGHT DFC2025 dataset instance. + + Args: + root: root directory where dataset can be found + split: train/val/test split to load + transforms: a function/transform that takes input sample and its target as + entry and returns a transformed version + download: if True, download dataset and store it in the root directory + checksum: if True, check the MD5 of the downloaded files (may be slow) + + Raises: + DatasetNotFoundError: If dataset is not found and *download* is False. + AssertionError: If *split* is not one of 'train', 'val', or 'test. + """ + assert split in self.valid_split, f'Split must be one of {self.valid_split}' + self.root = root + self.split = split + self.transforms = transforms + self.download = download + self.checksum = checksum + + self._verify() + + self.sample_paths = self._get_paths() + + def __getitem__(self, index: int) -> dict[str, Tensor]: + """Return an index within the dataset. + + Args: + index: index to return + + Returns: + data and target at that index, pre and post image + are returned under separate image keys + """ + idx_paths = self.sample_paths[index] + + image_pre = self._load_image(idx_paths['image_pre']).float() + image_post = self._load_image(idx_paths['image_post']).float() + # https://github.com/ChenHongruixuan/BRIGHT/blob/11b1ffafa4d30d2df2081189b56864b0de4e3ed7/dfc25_benchmark/dataset/make_data_loader.py#L101 + # post image is stacked to also have 3 channels + image_post = repeat(image_post, 'c h w -> (repeat c) h w', repeat=3) + + sample = {'image_pre': image_pre, 'image_post': image_post} + + if 'target' in idx_paths and self.split != 'test': + target = self._load_image(idx_paths['target']).long() + sample['mask'] = target + + if self.transforms is not None: + sample = self.transforms(sample) + + return sample + + def _get_paths(self) -> list[dict[str, str]]: + """Get paths to the dataset files based on specified splits. + + Returns: + a list of dictionaries containing paths to the pre, post, and target images + """ + split_file = self.split_files[self.split] + + file_path = os.path.join(self.root, self.data_dir, split_file) + with open(file_path) as f: + sample_ids = f.readlines() + + if self.split in ('train', 'val'): + dir_split_name = 'train' + else: + dir_split_name = 'val' + + sample_paths = [ + { + 'image_pre': os.path.join( + self.root, + self.data_dir, + dir_split_name, + 'pre-event', + f'{sample_id.strip()}_pre_disaster.tif', + ), + 'image_post': os.path.join( + self.root, + self.data_dir, + dir_split_name, + 'post-event', + f'{sample_id.strip()}_post_disaster.tif', + ), + } + for sample_id in sample_ids + ] + if self.split != 'test': + for sample, sample_id in zip(sample_paths, sample_ids): + sample['target'] = os.path.join( + self.root, + self.data_dir, + dir_split_name, + 'target', + f'{sample_id.strip()}_building_damage.tif', + ) + + return sample_paths + + def _verify(self) -> None: + """Verify the integrity of the dataset.""" + # check if the text split files exist + if all( + os.path.exists(os.path.join(self.root, self.data_dir, split_file)) + for split_file in self.split_files.values() + ): + # if split txt files exist check whether sample files exist + sample_paths = self._get_paths() + exists = [] + for sample in sample_paths: + exists.append( + all(os.path.exists(path) for name, path in sample.items()) + ) + if all(exists): + return + + # check if .zip files already exists (if so, then extract) + exists = [] + zip_file_path = os.path.join(self.root, self.data_dir + '.zip') + if os.path.exists(zip_file_path): + if self.checksum and not check_integrity(zip_file_path, self.md5): + raise RuntimeError('Dataset found, but corrupted.') + exists.append(True) + extract_archive(zip_file_path, self.root) + else: + exists.append(False) + + if all(exists): + return + + if not self.download: + raise DatasetNotFoundError(self) + + # download and extract the dataset + self._download() + extract_archive(zip_file_path, self.root) + + def _download(self) -> None: + """Download the dataset.""" + download_url( + self.url, + self.root, + self.data_dir + '.zip', + md5=self.md5 if self.checksum else None, + ) + + def __len__(self) -> int: + """Return the number of samples in the dataset. + + Returns: + number of samples in the dataset + """ + return len(self.sample_paths) + + def _load_image(self, path: Path) -> Tensor: + """Load a file from disk. + + Args: + path: path to the file to load + + Returns: + image tensor + """ + with rasterio.open(path) as src: + img = src.read() + tensor: Tensor = torch.from_numpy(img).float() + return tensor + + def plot( + self, + sample: dict[str, Tensor], + show_titles: bool = True, + suptitle: str | None = None, + ) -> Figure: + """Plot a sample from the dataset. + + Args: + sample: a sample returned by :meth:`__getitem__` + show_titles: flag indicating whether to show titles above each panel + suptitle: optional string to use as a suptitle + + Returns: + a matplotlib Figure with the rendered sample + """ + ncols = 2 + showing_mask = 'mask' in sample + showing_prediction = 'prediction' in sample + if showing_mask: + ncols += 1 + if showing_prediction: + ncols += 1 + + fig, axs = plt.subplots(nrows=1, ncols=ncols, figsize=(15, 5)) + + axs[0].imshow(sample['image_pre'].permute(1, 2, 0) / 255.0) + axs[0].axis('off') + + axs[1].imshow(sample['image_post'].permute(1, 2, 0) / 255.0) + axs[1].axis('off') + + cmap = colors.ListedColormap(self.colormap) + + if showing_mask: + axs[2].imshow(sample['mask'].squeeze(0), cmap=cmap, interpolation='none') + axs[2].axis('off') + unique_classes = np.unique(sample['mask'].numpy()) + handles = [ + mpatches.Patch( + color=cmap(ordinal), + label='\n'.join( + textwrap.wrap(self.px_class_values[px_class], width=10) + ), + ) + for ordinal, px_class in enumerate(self.px_class_values.keys()) + if ordinal in unique_classes + ] + axs[2].legend(handles=handles, loc='upper right', bbox_to_anchor=(1.4, 1)) + if showing_prediction: + axs[3].imshow( + sample['prediction'].squeeze(0), cmap=cmap, interpolation='none' + ) + axs[3].axis('off') + elif showing_prediction: + axs[2].imshow( + sample['prediction'].squeeze(0), cmap=cmap, interpolation='none' + ) + axs[2].axis('off') + + if show_titles: + axs[0].set_title('Pre-disaster image') + axs[1].set_title('Post-disaster image') + if showing_mask: + axs[2].set_title('Ground truth') + if showing_prediction: + axs[-1].set_title('Prediction') + + if suptitle is not None: + plt.suptitle(suptitle) + + return fig