|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
r"""Implements RSVQA-HR dataset in TFDS. |
|
|
|
Remote sensing visual question answering task, using high-resolution airborne |
|
image data at 15cm resolution per pixel. |
|
|
|
It's small dataset at source (14G), so simple to run locally. |
|
First, download and unzip the dataset from https://zenodo.org/records/6344367 |
|
and place it in /tmp/data/rsvqa_hr. |
|
|
|
Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): |
|
|
|
cd third_party/py/big_vision/datasets |
|
env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=rsvqa_hr |
|
|
|
Example to load: |
|
|
|
import tensorflow_datasets as tfds |
|
dataset = tfds.load('rsvqa_hr', split='train', data_dir='/tmp/tfds') |
|
|
|
Dataset splits (all): |
|
train: 625,340 examples/questions |
|
val: 102,843 examples/questions |
|
test: 222,684 examples/questions |
|
test_2: 105,647 examples/questions (other area, unknown instrument) |
|
Non-numeric data splits (nonum): |
|
train: 371,834 examples/questions |
|
val: 60,405 examples/questions |
|
test: 131,468 examples/questions |
|
test_2: 62,554 examples/questions |
|
|
|
Note: due to image duplication with each question, the dataset size is |
|
significatnly increased by the number of questions per image. |
|
|
|
Recommended training splits: |
|
train: train |
|
minitrain: train[:5%] |
|
eval: val |
|
full_train: train+val |
|
test: test |
|
|
|
Image sizes: 512x512 |
|
Number of answers per question: 1 |
|
Question types distribution in train split: |
|
- Area (area): 14.6% (integers, binned into {0m2, 1-10m2, 11-100m2, 101-1000m2, >1000m2}) |
|
- Comparison(comp): 33.5% |
|
- Count (count): 26.0% (integers, not binned, maximum number of objects is 89) |
|
- Presence (presence): 26.0% |
|
""" |
|
import json |
|
import os |
|
|
|
import numpy as np |
|
import tensorflow_datasets as tfds |
|
|
|
|
|
_DESCRIPTION = """RSVQA-HR dataset.""" |
|
|
|
|
|
_CITATION = """ |
|
@article{Lobry_2020, |
|
title={RSVQA: Visual Question Answering for Remote Sensing Data}, |
|
volume={58}, |
|
ISSN={1558-0644}, |
|
url={http://dx.doi.org/10.1109/TGRS.2020.2988782}, |
|
DOI={10.1109/tgrs.2020.2988782}, |
|
number={12}, |
|
journal={IEEE Transactions on Geoscience and Remote Sensing}, |
|
publisher={Institute of Electrical and Electronics Engineers (IEEE)}, |
|
author={Lobry, Sylvain and Marcos, Diego and Murray, Jesse and Tuia, Devis}, |
|
year={2020}, |
|
month=dec, pages={8555-8566} } |
|
""" |
|
|
|
|
|
|
|
PATH = '/tmp/data/rsvqa_hr/' |
|
|
|
|
|
class RsvqaHrConfig(tfds.core.BuilderConfig): |
|
"""Config to specify each variant.""" |
|
|
|
def __init__(self, nonum, **kwargs): |
|
name = 'nonum' if nonum else 'all' |
|
super(RsvqaHrConfig, self).__init__(name=name, **kwargs) |
|
self.nonum = nonum |
|
|
|
|
|
class RsvqaHr(tfds.core.GeneratorBasedBuilder): |
|
"""DatasetBuilder for RSVQA-HR dataset.""" |
|
|
|
VERSION = tfds.core.Version('1.0.2') |
|
RELEASE_NOTES = { |
|
'1.0.0': 'First release.', |
|
'1.0.1': 'Rename binned values.', |
|
'1.0.2': 'Removed explicit png image encoding.', |
|
} |
|
|
|
BUILDER_CONFIGS = [ |
|
RsvqaHrConfig(nonum=False), |
|
RsvqaHrConfig(nonum=True), |
|
] |
|
|
|
def _info(self): |
|
"""Returns the metadata.""" |
|
|
|
return tfds.core.DatasetInfo( |
|
builder=self, |
|
description=_DESCRIPTION, |
|
features=tfds.features.FeaturesDict({ |
|
'question_id': tfds.features.Scalar(np.int32), |
|
'filename': tfds.features.Text(), |
|
'image': tfds.features.Image(), |
|
'question': tfds.features.Text(), |
|
'question_type': tfds.features.Text(), |
|
'answers': tfds.features.Sequence(tfds.features.Text()), |
|
'raw_answers': tfds.features.Sequence(tfds.features.Text()), |
|
}), |
|
supervised_keys=None, |
|
homepage='https://rsvqa.sylvainlobry.com/', |
|
citation=_CITATION, |
|
) |
|
|
|
def _split_generators(self, dl_manager: tfds.download.DownloadManager): |
|
"""Returns SplitGenerators.""" |
|
return { |
|
split: self._generate_examples(split) |
|
for split in ('train', 'val', 'test', 'test_2') |
|
} |
|
|
|
def _generate_examples(self, split): |
|
"""Yields (key, example) tuples.""" |
|
if split == 'test_2': |
|
split = 'test_phili' |
|
questions_path = os.path.join(PATH + f'USGS_split_{split}_questions.json') |
|
answers_path = os.path.join(PATH + f'USGS_split_{split}_answers.json') |
|
images_path = os.path.join(PATH + 'Data') |
|
|
|
with open(questions_path, 'r') as f: |
|
questions = json.loads(f.read())['questions'] |
|
with open(answers_path, 'r') as f: |
|
answers = json.loads(f.read())['answers'] |
|
|
|
for q, a in zip(questions, answers): |
|
assert q['active'] == a['active'] |
|
if not q['active']: |
|
continue |
|
if self.builder_config.nonum and q['type'] in ('area', 'count'): |
|
continue |
|
assert q['answers_ids'][0] == a['id'] |
|
assert q['id'] == a['question_id'] |
|
|
|
filename = f'{q["img_id"]}.png' |
|
yield q['id'], { |
|
'question_id': q['id'], |
|
'filename': filename, |
|
'image': os.path.join(images_path, filename), |
|
'question': q['question'], |
|
'question_type': q['type'], |
|
'answers': [bin_answer(a['answer'], q['type'])], |
|
'raw_answers': [a['answer']], |
|
} |
|
|
|
|
|
def bin_answer(answer, question_type): |
|
"""Bins answers into expected ranges.""" |
|
if question_type == 'area': |
|
area = int(answer[:-2]) |
|
if area == 0: |
|
return '0 m2' |
|
elif area <= 10: |
|
return 'between 1 m2 and 10 m2' |
|
elif area <= 100: |
|
return 'between 11 m2 and 100 m2' |
|
elif area <= 1000: |
|
return 'between 101 m2 and 1000 m2' |
|
else: |
|
return 'more than 1000 m2' |
|
return answer |
|
|