Source code for stable_datasets.images.eurosat

import zipfile

from PIL import Image as PILImage

from stable_datasets.schema import ClassLabel, DatasetInfo, DatasetSource, DownloadInfo, Features, Version
from stable_datasets.schema import Image as ImageFeature
from stable_datasets.utils import BaseDatasetBuilder


[docs] class EuroSAT(BaseDatasetBuilder): """ EuroSAT Dataset Abstract A land use / land cover classification dataset built from Sentinel-2 satellite imagery, covering 10 classes with 27,000 labeled 64x64 RGB images in total. There is no official train/val/test split shipped with the archive (images are organized one folder per class), so this builder exposes a single "train" split with every image (the same pattern as AWA2). """ VERSION = Version("1.0.0") SOURCE = DatasetSource( homepage="https://github.com/phelber/EuroSAT", citation=r"""@article{helber2019eurosat, title={EuroSAT: A Novel Dataset and Deep Learning Benchmark for Land Use and Land Cover Classification}, author={Helber, Patrick and Bischke, Benjamin and Dengel, Andreas and Borth, Damian}, journal={IEEE Journal of Selected Topics in Applied Earth Observations and Remote Sensing}, year={2019}}""", assets={ # RGB-only version (there is also a 13-band multispectral release). # The original host (madm.dfki.de) was unreachable (connection # timeout) as of 2026-10-12, so this mirror is primary with the # original kept as a fallback. "train": DownloadInfo( url="https://huggingface.co/datasets/torchgeo/eurosat/resolve/main/EuroSAT.zip", fallbacks=["https://madm.dfki.de/files/sentinel/EuroSAT.zip"], ), }, # Imagery also falls under the Copernicus Sentinel Data Terms and Conditions. license="MIT License", ) def _info(self): return DatasetInfo( description="EuroSAT dataset with 10 land use / land cover classes.", features=Features( { "image": ImageFeature(encode_format="JPEG"), "label": ClassLabel(names=self._labels()), } ), supervised_keys=("image", "label"), homepage=self.SOURCE["homepage"], license=self.SOURCE["license"], citation=self.SOURCE["citation"], ) def _generate_examples(self, data_path, split): class_names = self.info.features["label"].names label_mapping = {name: idx for idx, name in enumerate(class_names)} with zipfile.ZipFile(data_path, "r") as z: for path in z.namelist(): if not path.lower().endswith((".jpg", ".jpeg")): continue # Images live at "<archive_root>/<ClassName>/<ClassName>_<idx>.jpg". # We key off the immediate parent folder name rather than a # hardcoded root (e.g. "2750/") so a differently-named release # of the archive still resolves correctly. parts = path.split("/") if len(parts) < 2: continue class_name = parts[-2] if class_name not in label_mapping: continue with z.open(path) as image_file: image = PILImage.open(image_file).convert("RGB") yield path, {"image": image, "label": label_mapping[class_name]} @staticmethod def _labels(): """10 land use / land cover class names, matching the dataset's folder names.""" return [ "AnnualCrop", "Forest", "HerbaceousVegetation", "Highway", "Industrial", "Pasture", "PermanentCrop", "Residential", "River", "SeaLake", ]