diff --git a/README.md b/README.md index f0ec2ba..85a873f 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ pip install 'git+https://github.com/isaaccorley/torchrs.git#egg=torch-rs[train]' * [GID-15 - Semantic Segmentation](https://github.com/isaaccorley/torchrs#gid-15) * [TiSeLaC - Time-Series Land Cover Classification](https://github.com/isaaccorley/torchrs#tiselac) * [UC Merced - Land Use Classification](https://github.com/isaaccorley/torchrs#uc-merced-ucm) +* [PatternNet - Image Retrieval / Scene Classification](https://github.com/isaaccorley/torchrs#patternnet) ### PROBA-V Super Resolution @@ -886,6 +887,42 @@ dataset.classes """ ``` +### PatternNet + + + +The [PatternNet](https://sites.google.com/view/zhouwx/dataset) dataset, proposed in ["PatternNet: A Benchmark Dataset for Performance Evaluation of Remote Sensing Image Retrieval", Yang et al.](https://arxiv.org/abs/1706.03424) is a image retrieval and scene classification dataset of 30,400 256x256 high resolution (.06-5m) RGB images extracted using [Google Earth](https://earth.google.com/web/) and [Google Maps](https://developers.google.com/maps) with 38 scene classes (800 images per class). This dataset was originally proposed as a remote sensing image retrieval (RSIR) dataset with classes selected for high intra-class diversity and inter-class similarity such that image retrieval requires learning fine-grained details between multiple classes. Additionally, this dataset has some unique classes not found in other scene classification datasets, e.g. oil well, nursing home, solar panel, etc. + +The dataset can be downloaded (1.4GB) using `scripts/download_patternnet.sh` and instantiated below: + +```python +import torchvision.transforms as T +from torchrs.datasets import PatternNet + +transform = T.Compose([T.ToTensor()]) + +dataset = PatternNet( + root="path/to/dataset/", + transform=transform +) + +x, y = dataset[0] +""" +x: (3, 256, 256) +y: int +""" + +dataset.classes +""" +['airplane', 'baseball_field', 'basketball_court', 'beach', 'bridge', 'cemetery', 'chaparral', +'christmas_tree_farm', 'closed_road', 'coastal_mansion', 'crosswalk', 'dense_residential', +'ferry_terminal', 'football_field', 'forest', 'freeway', 'golf_course', 'harbor', 'intersection', +'mobile_home_park', 'nursing_home', 'oil_gas_field', 'oil_well', 'overpass', 'parking_lot', 'parking_space', +'railway', 'river', 'runway', 'runway_marking', 'shipping_yard', 'solar_panel','sparse_residential', +'storage_tank', 'swimming_pool', 'tennis_court', 'transformer_station', 'wastewater_treatment_plant'] +""" +``` + ## Models * [Multi-Image Super Resolution - RAMS](https://github.com/isaaccorley/torchrs#multi-image-super-resolution---rams) diff --git a/assets/patternnet.png b/assets/patternnet.png new file mode 100644 index 0000000..eb80b38 Binary files /dev/null and b/assets/patternnet.png differ diff --git a/scripts/download_patternnet.sh b/scripts/download_patternnet.sh new file mode 100644 index 0000000..67be569 --- /dev/null +++ b/scripts/download_patternnet.sh @@ -0,0 +1,5 @@ +pip install gdown +mkdir -p .data/ +gdown --id 127lxXYqzO6Bd0yZhvEbgIfz95HaEnr9K +unzip PatternNet.zip -d .data/ +rm PatternNet.zip diff --git a/torchrs/datasets/__init__.py b/torchrs/datasets/__init__.py index f122cd2..908c37d 100644 --- a/torchrs/datasets/__init__.py +++ b/torchrs/datasets/__init__.py @@ -23,6 +23,7 @@ from .dubai_segmentation import DubaiSegmentation from .hkh_glacier import HKHGlacierMapping from .ucm import UCM +from .patternnet import PatternNet __all__ = [ @@ -30,5 +31,5 @@ "RESISC45", "RSICD", "OSCD", "S2Looking", "LEVIRCDPlus", "FAIR1M", "SydneyCaptions", "UCMCaptions", "S2MTCP", "ADVANCE", "SAT4", "SAT6", "HRSCD", "InriaAIL", "Tiselac", "GID15", "ZueriCrop", "AID", "DubaiSegmentation", - "HKHGlacierMapping", "UCM" + "HKHGlacierMapping", "UCM", "PatternNet" ] diff --git a/torchrs/datasets/patternnet.py b/torchrs/datasets/patternnet.py new file mode 100644 index 0000000..3c23e8d --- /dev/null +++ b/torchrs/datasets/patternnet.py @@ -0,0 +1,21 @@ +import os + +import torchvision.transforms as T +from torchvision.datasets import ImageFolder + + +class PatternNet(ImageFolder): + """ PatternNet dataset from 'PatternNet: A benchmark dataset for performance + evaluation of remote sensing image retrieval', Zhou at al. (2018) + https://arxiv.org/abs/1706.03424 + + """ + def __init__( + self, + root: str = ".data/PatternNet", + transform: T.Compose = T.Compose([T.ToTensor()]) + ): + super().__init__( + root=os.path.join(root, "images"), + transform=transform + ) diff --git a/torchrs/train/datamodules/__init__.py b/torchrs/train/datamodules/__init__.py index 11fa7bd..b98d2b6 100644 --- a/torchrs/train/datamodules/__init__.py +++ b/torchrs/train/datamodules/__init__.py @@ -23,6 +23,7 @@ from .dubai_segmentation import DubaiSegmentationDataModule from .hkh_glacier import HKHGlacierMappingDataModule from .ucm import UCMDataModule +from .patternnet import PatternNetDataModule __all__ = [ @@ -32,5 +33,5 @@ "FAIR1MDataModule", "SydneyCaptionsDataModule", "UCMCaptionsDataModule", "S2MTCPDataModule", "ADVANCEDataModule", "SAT4DataModule", "SAT6DataModule", "HRSCDDataModule", "InriaAILDataModule", "TiselacDataModule", "GID15DataModule", "ZueriCropDataModule", "AIDDataModule", - "DubaiSegmentationDataModule", "HKHGlacierMappingDataModule", "UCMDataModule" + "DubaiSegmentationDataModule", "HKHGlacierMappingDataModule", "UCMDataModule", "PatternNetDataModule" ] diff --git a/torchrs/train/datamodules/patternnet.py b/torchrs/train/datamodules/patternnet.py new file mode 100644 index 0000000..b5ca8e8 --- /dev/null +++ b/torchrs/train/datamodules/patternnet.py @@ -0,0 +1,26 @@ +from typing import Optional + +import torchvision.transforms as T + +from torchrs.datasets.utils import dataset_split +from torchrs.train.datamodules import BaseDataModule +from torchrs.datasets import PatternNet + + +class PatternNetDataModule(BaseDataModule): + + def __init__( + self, + root: str = ".data/PatternNet", + transform: T.Compose = T.Compose([T.ToTensor()]), + *args, **kwargs + ): + super().__init__(*args, **kwargs) + self.root = root + self.transform = transform + + def setup(self, stage: Optional[str] = None): + dataset = PatternNet(root=self.root, transform=self.transform) + self.train_dataset, self.val_dataset, self.test_dataset = dataset_split( + dataset, val_pct=self.val_split, test_pct=self.test_split + )