From 900e8a1e74f5c2aa87837f878851abcc2d37c270 Mon Sep 17 00:00:00 2001 From: "Adam J. Stewart" Date: Sat, 27 Jul 2024 09:24:56 +0200 Subject: [PATCH] NASA Marine Debris: radiant mlhub -> source coop (#2183) --- tests/data/nasa_marine_debris/data.py | 58 +++++++ .../20160928_153233_0e16_16816-29821-16.npy | Bin 0 -> 288 bytes .../20160928_153233_0e16_16816-29824-16.npy | Bin 0 -> 208 bytes .../20160928_153233_0e16_16816-29825-16.npy | Bin 0 -> 208 bytes .../20160928_153233_0e16_16816-29828-16.npy | Bin 0 -> 128 bytes .../20160928_153233_0e16_16816-29829-16.npy | Bin 0 -> 208 bytes .../nasa_marine_debris_labels.tar.gz | Bin 483 -> 0 bytes .../pixel_bounds.npy | Bin 208 -> 0 bytes .../pixel_bounds.npy | Bin 208 -> 0 bytes .../pixel_bounds.npy | Bin 168 -> 0 bytes .../pixel_bounds.npy | Bin 208 -> 0 bytes .../nasa_marine_debris_source.tar.gz | Bin 1240 -> 0 bytes .../image_geotiff.tif | Bin 1728 -> 0 bytes .../image_geotiff.tif | Bin 1728 -> 0 bytes .../image_geotiff.tif | Bin 1728 -> 0 bytes .../image_geotiff.tif | Bin 1728 -> 0 bytes .../20160928_153233_0e16_16816-29821-16.tif | Bin 0 -> 3450 bytes .../20160928_153233_0e16_16816-29824-16.tif | Bin 0 -> 3450 bytes .../20160928_153233_0e16_16816-29825-16.tif | Bin 0 -> 3450 bytes .../20160928_153233_0e16_16816-29828-16.tif | Bin 0 -> 3450 bytes .../20160928_153233_0e16_16816-29829-16.tif | Bin 0 -> 3450 bytes tests/datasets/test_nasa_marine_debris.py | 69 ++------ torchgeo/datasets/nasa_marine_debris.py | 148 ++++-------------- 23 files changed, 102 insertions(+), 173 deletions(-) create mode 100755 tests/data/nasa_marine_debris/data.py create mode 100644 tests/data/nasa_marine_debris/labels/20160928_153233_0e16_16816-29821-16.npy create mode 100644 tests/data/nasa_marine_debris/labels/20160928_153233_0e16_16816-29824-16.npy create mode 100644 tests/data/nasa_marine_debris/labels/20160928_153233_0e16_16816-29825-16.npy create mode 100644 tests/data/nasa_marine_debris/labels/20160928_153233_0e16_16816-29828-16.npy create mode 100644 tests/data/nasa_marine_debris/labels/20160928_153233_0e16_16816-29829-16.npy delete mode 100644 tests/data/nasa_marine_debris/nasa_marine_debris_labels.tar.gz delete mode 100755 tests/data/nasa_marine_debris/nasa_marine_debris_labels/nasa_marine_debris_labels_20160928_153233_0e16_16816-29821-16/pixel_bounds.npy delete mode 100755 tests/data/nasa_marine_debris/nasa_marine_debris_labels/nasa_marine_debris_labels_20160928_153233_0e16_16816-29824-16/pixel_bounds.npy delete mode 100755 tests/data/nasa_marine_debris/nasa_marine_debris_labels/nasa_marine_debris_labels_20160928_153233_0e16_16816-29825-16/pixel_bounds.npy delete mode 100755 tests/data/nasa_marine_debris/nasa_marine_debris_labels/nasa_marine_debris_labels_20160928_153233_0e16_16816-29828-16/pixel_bounds.npy delete mode 100644 tests/data/nasa_marine_debris/nasa_marine_debris_source.tar.gz delete mode 100644 tests/data/nasa_marine_debris/nasa_marine_debris_source/nasa_marine_debris_source_20160928_153233_0e16_16816-29821-16/image_geotiff.tif delete mode 100644 tests/data/nasa_marine_debris/nasa_marine_debris_source/nasa_marine_debris_source_20160928_153233_0e16_16816-29824-16/image_geotiff.tif delete mode 100644 tests/data/nasa_marine_debris/nasa_marine_debris_source/nasa_marine_debris_source_20160928_153233_0e16_16816-29825-16/image_geotiff.tif delete mode 100644 tests/data/nasa_marine_debris/nasa_marine_debris_source/nasa_marine_debris_source_20160928_153233_0e16_16816-29828-16/image_geotiff.tif create mode 100644 tests/data/nasa_marine_debris/source/20160928_153233_0e16_16816-29821-16.tif create mode 100644 tests/data/nasa_marine_debris/source/20160928_153233_0e16_16816-29824-16.tif create mode 100644 tests/data/nasa_marine_debris/source/20160928_153233_0e16_16816-29825-16.tif create mode 100644 tests/data/nasa_marine_debris/source/20160928_153233_0e16_16816-29828-16.tif create mode 100644 tests/data/nasa_marine_debris/source/20160928_153233_0e16_16816-29829-16.tif diff --git a/tests/data/nasa_marine_debris/data.py b/tests/data/nasa_marine_debris/data.py new file mode 100755 index 00000000000..a782dea3d27 --- /dev/null +++ b/tests/data/nasa_marine_debris/data.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import os + +import numpy as np +import rasterio as rio +from rasterio import Affine +from rasterio.crs import CRS + +SIZE = 32 +DTYPE = np.uint8 + +np.random.seed(0) + +profile = { + 'driver': 'GTiff', + 'dtype': DTYPE, + 'width': SIZE, + 'height': SIZE, + 'count': 3, + 'crs': CRS.from_epsg(4326), + 'transform': Affine( + 2.1457672119140625e-05, + 0.0, + -87.626953125, + 0.0, + -2.0629065249348766e-05, + 15.977172621632805, + ), +} + +os.makedirs('source', exist_ok=True) +os.makedirs('labels', exist_ok=True) + +files = [ + '20160928_153233_0e16_16816-29821-16', + '20160928_153233_0e16_16816-29824-16', + '20160928_153233_0e16_16816-29825-16', + '20160928_153233_0e16_16816-29828-16', + '20160928_153233_0e16_16816-29829-16', +] +for file in files: + with rio.open(os.path.join('source', f'{file}.tif'), 'w', **profile) as f: + for i in range(1, 4): + Z = np.random.randint(np.iinfo(DTYPE).max, size=(SIZE, SIZE), dtype=DTYPE) + f.write(Z, i) + + count = np.random.randint(5) + x = np.random.randint(SIZE, size=count) + y = np.random.randint(SIZE, size=count) + dx = np.random.randint(5, size=count) + dy = np.random.randint(5, size=count) + label = np.ones(count) + Z = np.stack([x, y, x + dx, y + dy, label], axis=-1) + np.save(os.path.join('labels', f'{file}.npy'), Z) diff --git a/tests/data/nasa_marine_debris/labels/20160928_153233_0e16_16816-29821-16.npy b/tests/data/nasa_marine_debris/labels/20160928_153233_0e16_16816-29821-16.npy new file mode 100644 index 0000000000000000000000000000000000000000..104f61ed70134bdfaa397033dd8f262c4f35888b GIT binary patch literal 288 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I#yItr$mItsN4WCJb+Fwk*;&;n4}2#x>29wILS6}N}dGEmw9O2gDyL;1E) W+6IjeQ)dPhhuLQZ<(ok15B31U+b1Ld literal 0 HcmV?d00001 diff --git a/tests/data/nasa_marine_debris/labels/20160928_153233_0e16_16816-29824-16.npy b/tests/data/nasa_marine_debris/labels/20160928_153233_0e16_16816-29824-16.npy new file mode 100644 index 0000000000000000000000000000000000000000..4de2e99e4ffaf51c9bc165c82ebe9598d9b99b21 GIT binary patch literal 208 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I$7Itr$mItsN4WCJb+Ffet1&?->c5=yH<=@0f0c^GLA73YA`4p91oJpiD1 BAkP2* literal 0 HcmV?d00001 diff --git a/tests/data/nasa_marine_debris/labels/20160928_153233_0e16_16816-29825-16.npy b/tests/data/nasa_marine_debris/labels/20160928_153233_0e16_16816-29825-16.npy new file mode 100644 index 0000000000000000000000000000000000000000..e90c5b706e637b2e89d16f1ae6ae396070768617 GIT binary patch literal 208 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I$7Itr$mItsN4WCJb+FpzP8&>~P;1xhPG=@0f0c|#~)0!pLvKiC5Rn@%9s literal 0 HcmV?d00001 diff --git a/tests/data/nasa_marine_debris/labels/20160928_153233_0e16_16816-29828-16.npy b/tests/data/nasa_marine_debris/labels/20160928_153233_0e16_16816-29828-16.npy new file mode 100644 index 0000000000000000000000000000000000000000..99aa9231262bfe8272aeceae8b8eef31ef33570a GIT binary patch literal 128 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= XXCxM+0{I#SItr$mItsN4WCJb$SPvU; literal 0 HcmV?d00001 diff --git a/tests/data/nasa_marine_debris/labels/20160928_153233_0e16_16816-29829-16.npy b/tests/data/nasa_marine_debris/labels/20160928_153233_0e16_16816-29829-16.npy new file mode 100644 index 0000000000000000000000000000000000000000..577edba2c816b7348ff6c35c4606341249b2d0cd GIT binary patch literal 208 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I$7Itr$mItsN4WCJb+Ffez3&=yeI3XT849wM&>6*qy>T2R^yN`J5i0JtC_ AYXATM literal 0 HcmV?d00001 diff --git a/tests/data/nasa_marine_debris/nasa_marine_debris_labels.tar.gz b/tests/data/nasa_marine_debris/nasa_marine_debris_labels.tar.gz deleted file mode 100644 index 3a4c8edbeeb0a847160e7d3f666d7cf796dda64c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 483 zcmV<90UZ7xiwFP!000001MS&OPr@)52k?3IQ}i}XccxnbzU7Q=~ z&}fXG!Y^z&V1x)nTx%Boze}N8LgDGt{4>g|&eUUO@=~jb9+|vWMK;n!-47Z=twhTC z$P!7vj|%~dm=uBwX^T3miw$ zqTZ{cZFjb`7bvs)%hrGBXgy07^*2>D+jQTy>n-J~|EKwjF4U-cDkpWXoWB0|_k^GM zsQ5P94o?__6(tLr&fGwymHZ04Z<(?;@Ng8b(L@*nb_ zi~dLcYX=|o|9$>rlt##ZJ|O??HwXPM8_9nW@}CdLf5?9>`XBkP9emV(+xgGs5BX0Z z|M~Ez{Kx(;|3Nbx^ncVy{=@(OULgM=|GDUY0^hpc*g$0&`PS z5N&8~YJg0`0E3~4p{cR4k+GSPA%lUTsga2(gMumboPZmY7MCOzDJU>xCg&IB;890N zeu#oRobiuSCf>-v(9FQn$RZvXWk$xv@dl}eX7Pq*7KUcJMwS*vCc1`ZD5-~p_%|^% z2gbjVnTeUnX#CRy=;`a}JNV@1rTb*&Wz)zGPz0Nqn84y2od#uhl(b-A2uxpw3<^Fp zat{iC#eY$LehEHxgye@PpjH0Q%uP&BjZaU_FUd?x(*qI%?a@(y70@I8k@7#b^557P zz5F*eG%zw^P%xlJ0F3bR-_uizfdfbjGcYhRGcW>KOhC+t#AX7rnSt~tAZCV&vjQ<2 zk{VGc8>CJKN!%9724N>C8$>uGv0WHh!1`E#Y*#4z2M`BC?G*ru^ELB;*5gK0F4)7 z1v!Wj;xYz?hHrKZ2R7_xTJ+V9n2=O>5qhB9*C*KcjlKg+3}O~L&~qR*69Xd)REz^e zKNDc&*x1ev3QI7*PnnZrLpw;G8%#4WbmW#ylU-5-GS6XINQ7g!d$59qNex2-&^itk zF<$l-r^J9ADQ2?#9WF_M7E8i(#e0BCEzXqL%1`h}39?j~=Bqx%D>Z0m%C=bj89r&j zmP@YXn$Ph|58nCYTdn!wS*uPvo4zF~H*9z6w!8T|V)DYRmtK2Yz9%j}eD~9Df9nq<6hzpliSxD}Nh*xk zlV&d4et>?({Qh#gTiSmD|q0kWmt4t2W(t`IW5FsJ&_1W7prvDT}sUc0G6d zoxJksz0bba?tf5F5o4z=-h2E>QDw}&bo06AUzAkE+AR-Xd;LvWb?m<9<$LdcsHlmv zSD$|N`IoBNxc%wd@4o+0Qx|W){QBGPf9mSv_doys=l=l#9#+N!6Lff)4+{8*vL2j} zBg=kBphuPS(1bO*+=m6unDQQ;@Wz(^h#-%v;E{sYz?rYM&N5vsL%> zq&Iu@&j|AzH9Rv}=dAHrVV|p}XD8>}dALAfG`2=#Yc#fK6I(=9bE5zUph5f3kShIu z!_odfJ-}%Hf3*Kki~K*@{~sEF9`R33|KHGjwEs^JFxvme+yDP@a^j?g#P|OlUwi!U z|1#SDAMK-z_9#bVYc#e-`~QO-(4hTiL5=>uxxr}vpB`Ye|3BLQr$zoB?f(xAK#%w* zYyRJCwEs^JFxvme+y4)+SDZn@{J-wfm!?S%NBjSyeU#B2RB!U=)mkQ7{Sy0{{SQ!yD@WPyhfM Cn`49k diff --git a/tests/data/nasa_marine_debris/nasa_marine_debris_source/nasa_marine_debris_source_20160928_153233_0e16_16816-29821-16/image_geotiff.tif b/tests/data/nasa_marine_debris/nasa_marine_debris_source/nasa_marine_debris_source_20160928_153233_0e16_16816-29821-16/image_geotiff.tif deleted file mode 100644 index 471c657e5f60f3f5d521e0a633ec208c28818570..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1728 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%0D`#QEKB)3!Y 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 literal 0 HcmV?d00001 diff --git a/tests/data/nasa_marine_debris/source/20160928_153233_0e16_16816-29828-16.tif b/tests/data/nasa_marine_debris/source/20160928_153233_0e16_16816-29828-16.tif new file mode 100644 index 0000000000000000000000000000000000000000..f8d75a064ad7a7ad349db6310901c7d05def133e GIT binary patch literal 3450 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 None: - glob_path = os.path.join('tests', 'data', 'nasa_marine_debris', '*.tar.gz') - for tarball in glob.iglob(glob_path): - shutil.copy(tarball, output_dir) - - -def fetch(collection_id: str, **kwargs: str) -> Collection: - return Collection() - - -class Collection_corrupted: - def download(self, output_dir: str, **kwargs: str) -> None: - filenames = NASAMarineDebris.filenames - for filename in filenames: - with open(os.path.join(output_dir, filename), 'w') as f: - f.write('bad') - - -def fetch_corrupted(collection_id: str, **kwargs: str) -> Collection_corrupted: - return Collection_corrupted() +from torchgeo.datasets.utils import Executable class TestNASAMarineDebris: - @pytest.fixture() - def dataset(self, monkeypatch: MonkeyPatch, tmp_path: Path) -> NASAMarineDebris: - radiant_mlhub = pytest.importorskip('radiant_mlhub', minversion='0.3') - monkeypatch.setattr(radiant_mlhub.Collection, 'fetch', fetch) - md5s = ['6f4f0d2313323950e45bf3fc0c09b5de', '540cf1cf4fd2c13b609d0355abe955d7'] - monkeypatch.setattr(NASAMarineDebris, 'md5s', md5s) - root = tmp_path + @pytest.fixture + def dataset( + self, azcopy: Executable, monkeypatch: MonkeyPatch, tmp_path: Path + ) -> NASAMarineDebris: + url = os.path.join('tests', 'data', 'nasa_marine_debris') + monkeypatch.setattr(NASAMarineDebris, 'url', url) transforms = nn.Identity() - return NASAMarineDebris(root, transforms, download=True, checksum=True) + return NASAMarineDebris(tmp_path, transforms, download=True) def test_getitem(self, dataset: NASAMarineDebris) -> None: x = dataset[0] @@ -58,36 +33,12 @@ def test_getitem(self, dataset: NASAMarineDebris) -> None: assert x['boxes'].shape[-1] == 4 def test_len(self, dataset: NASAMarineDebris) -> None: - assert len(dataset) == 4 + assert len(dataset) == 5 def test_already_downloaded( self, dataset: NASAMarineDebris, tmp_path: Path ) -> None: - NASAMarineDebris(root=tmp_path, download=True) - - def test_already_downloaded_not_extracted( - self, dataset: NASAMarineDebris, tmp_path: Path - ) -> None: - shutil.rmtree(dataset.root) - os.makedirs(tmp_path, exist_ok=True) - Collection().download(output_dir=str(tmp_path)) - NASAMarineDebris(root=tmp_path, download=False) - - def test_corrupted_previously_downloaded(self, tmp_path: Path) -> None: - filenames = NASAMarineDebris.filenames - for filename in filenames: - with open(os.path.join(tmp_path, filename), 'w') as f: - f.write('bad') - with pytest.raises(RuntimeError, match='Dataset checksum mismatch.'): - NASAMarineDebris(root=tmp_path, download=False, checksum=True) - - def test_corrupted_new_download( - self, tmp_path: Path, monkeypatch: MonkeyPatch - ) -> None: - with pytest.raises(RuntimeError, match='Dataset checksum mismatch.'): - radiant_mlhub = pytest.importorskip('radiant_mlhub', minversion='0.3') - monkeypatch.setattr(radiant_mlhub.Collection, 'fetch', fetch_corrupted) - NASAMarineDebris(root=tmp_path, download=True, checksum=True) + NASAMarineDebris(tmp_path, download=True) def test_not_downloaded(self, tmp_path: Path) -> None: with pytest.raises(DatasetNotFoundError, match='Dataset not found'): diff --git a/torchgeo/datasets/nasa_marine_debris.py b/torchgeo/datasets/nasa_marine_debris.py index cf018150f2d..9c57e290406 100644 --- a/torchgeo/datasets/nasa_marine_debris.py +++ b/torchgeo/datasets/nasa_marine_debris.py @@ -3,6 +3,7 @@ """NASA Marine Debris dataset.""" +import glob import os from collections.abc import Callable @@ -16,18 +17,13 @@ from .errors import DatasetNotFoundError from .geo import NonGeoDataset -from .utils import ( - Path, - check_integrity, - download_radiant_mlhub_collection, - extract_archive, -) +from .utils import Path, which class NASAMarineDebris(NonGeoDataset): """NASA Marine Debris dataset. - The `NASA Marine Debris `__ + The `NASA Marine Debris `__ dataset is a dataset for detection of floating marine debris in satellite imagery. Dataset features: @@ -52,26 +48,19 @@ class NASAMarineDebris(NonGeoDataset): This dataset requires the following additional library to be installed: - * `radiant-mlhub `_ to download the - imagery and labels from the Radiant Earth MLHub + * `azcopy `_: to download the + dataset from Source Cooperative. .. versionadded:: 0.2 """ - collection_ids = ['nasa_marine_debris_source', 'nasa_marine_debris_labels'] - directories = ['nasa_marine_debris_source', 'nasa_marine_debris_labels'] - filenames = ['nasa_marine_debris_source.tar.gz', 'nasa_marine_debris_labels.tar.gz'] - md5s = ['fe8698d1e68b3f24f0b86b04419a797d', 'd8084f5a72778349e07ac90ec1e1d990'] - class_label = 'marine_debris' + url = 'https://radiantearth.blob.core.windows.net/mlhub/nasa-marine-debris' def __init__( self, root: Path = 'data', transforms: Callable[[dict[str, Tensor]], dict[str, Tensor]] | None = None, download: bool = False, - api_key: str | None = None, - checksum: bool = False, - verbose: bool = False, ) -> None: """Initialize a new NASA Marine Debris Dataset instance. @@ -80,9 +69,6 @@ def __init__( 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 - api_key: a RadiantEarth MLHub API key to use for downloading the dataset - checksum: if True, check the MD5 of the downloaded files (may be slow) - verbose: if True, print messages when new tiles are loaded Raises: DatasetNotFoundError: If dataset is not found and *download* is False. @@ -90,11 +76,11 @@ def __init__( self.root = root self.transforms = transforms self.download = download - self.api_key = api_key - self.checksum = checksum - self.verbose = verbose + self._verify() - self.files = self._load_files() + + self.source = sorted(glob.glob(os.path.join(self.root, 'source', '*.tif'))) + self.labels = sorted(glob.glob(os.path.join(self.root, 'labels', '*.npy'))) def __getitem__(self, index: int) -> dict[str, Tensor]: """Return an index within the dataset. @@ -105,15 +91,21 @@ def __getitem__(self, index: int) -> dict[str, Tensor]: Returns: data and labels at that index """ - image = self._load_image(self.files[index]['image']) - boxes = self._load_target(self.files[index]['target']) - sample = {'image': image, 'boxes': boxes} + with rasterio.open(self.source[index]) as source: + image = torch.from_numpy(source.read()).float() + + labels = np.load(self.labels[index]) + + # Boxes contain unnecessary value of 1 after xyxy coords + boxes = torch.from_numpy(labels[:, :4]) # Filter invalid boxes - w_check = (sample['boxes'][:, 2] - sample['boxes'][:, 0]) > 0 - h_check = (sample['boxes'][:, 3] - sample['boxes'][:, 1]) > 0 + w_check = (boxes[:, 2] - boxes[:, 0]) > 0 + h_check = (boxes[:, 3] - boxes[:, 1]) > 0 indices = w_check & h_check - sample['boxes'] = sample['boxes'][indices] + boxes = boxes[indices] + + sample = {'image': image, 'boxes': boxes} if self.transforms is not None: sample = self.transforms(sample) @@ -126,85 +118,13 @@ def __len__(self) -> int: Returns: length of the dataset """ - return len(self.files) - - def _load_image(self, path: Path) -> Tensor: - """Load a single image. - - Args: - path: path to the image - - Returns: - the image - """ - with rasterio.open(path) as f: - array = f.read() - tensor = torch.from_numpy(array).float() - return tensor - - def _load_target(self, path: Path) -> Tensor: - """Load the target bounding boxes for a single image. - - Args: - path: path to the labels - - Returns: - the target boxes - """ - array = np.load(path) - # boxes contain unecessary value of 1 after xyxy coords - array = array[:, :4] - tensor = torch.from_numpy(array) - return tensor - - def _load_files(self) -> list[dict[str, str]]: - """Load a image and label files. - - Returns: - list of dicts containing image and label files - """ - image_root = os.path.join(self.root, self.directories[0]) - target_root = os.path.join(self.root, self.directories[1]) - image_folders = sorted( - f for f in os.listdir(image_root) if not f.endswith('json') - ) - - files = [] - for folder in image_folders: - files.append( - { - 'image': os.path.join(image_root, folder, 'image_geotiff.tif'), - 'target': os.path.join( - target_root, - folder.replace('source', 'labels'), - 'pixel_bounds.npy', - ), - } - ) - return files + return len(self.source) def _verify(self) -> None: """Verify the integrity of the dataset.""" - # Check if the files already exist - exists = [ - os.path.exists(os.path.join(self.root, directory)) - for directory in self.directories - ] - if all(exists): - return - - # Check if zip file already exists (if so then extract) - exists = [] - for filename, md5 in zip(self.filenames, self.md5s): - filepath = os.path.join(self.root, filename) - if os.path.exists(filepath): - if self.checksum and not check_integrity(filepath, md5): - raise RuntimeError('Dataset checksum mismatch.') - exists.append(True) - extract_archive(filepath) - else: - exists.append(False) - + # Check if the directories already exist + dirs = ['source', 'labels'] + exists = [os.path.exists(os.path.join(self.root, d)) for d in dirs] if all(exists): return @@ -212,14 +132,14 @@ def _verify(self) -> None: if not self.download: raise DatasetNotFoundError(self) - # Download and extract the dataset - for collection_id in self.collection_ids: - download_radiant_mlhub_collection(collection_id, self.root, self.api_key) - for filename, md5 in zip(self.filenames, self.md5s): - filepath = os.path.join(self.root, filename) - if self.checksum and not check_integrity(filepath, md5): - raise RuntimeError('Dataset checksum mismatch.') - extract_archive(filepath) + # Download the dataset + self._download() + + def _download(self) -> None: + """Download the dataset.""" + os.makedirs(self.root, exist_ok=True) + azcopy = which('azcopy') + azcopy('sync', self.url, self.root, '--recursive=true') def plot( self,