VQA-RAD¶
Modality: CXR / CT / MRI (mixed) | Format: JPEG | Dim: 2D | Task: Visual Question Answering
Overview¶
VQA-RAD (Visual Question Answering in Radiology) is a benchmark of clinically generated question-answer pairs over 315 radiology images sampled from MedPix (chest X-ray, CT, and MRI cases across HEAD/CHEST/ABD). It contains 2,248 questions authored or verified by clinicians, split into closed-ended (yes/no, choice) and open-ended (free-text) answer types. Published in Lau et al., Scientific Data (2018), released under CC0 1.0.
VQA-RAD has no patient/study concept — every image is its own independent case, so patient_id and study_id are both derived from the image filename.
This dataset uses BaseVQADataset rather than BaseRadiologicalDataset. The data dict contains mixed types (Tensor + strings), so the standard output_cls / output_bbox mechanism does not apply.
Download¶
Available from OSF: Visual Question Answering in Radiology (VQA-RAD) — the dataset's official, public home. No registration required; CC0 1.0 license.
The OSF project exposes 5 items at the top level: 3 equivalent metadata files (JSON/XLSX/XML — the harmonizer reads the JSON), a Readme.docx, and an image folder. The image folder can be fetched as a zip via OSF's folder-zip endpoint:
# metadata + readme
curl -sL -o "VQA_RAD Dataset Public.json" "https://osf.io/download/6qdas/"
curl -sL -o "Readme.docx" "https://osf.io/download/bd96f/"
# images (315 JPEGs, ~15MB zip)
curl -sL -o "VQA_RAD Image Folder.zip" \
"https://files.osf.io/v1/resources/89kps/providers/osfstorage/5b21453986d8510011c277bc/?zip="
unzip -q "VQA_RAD Image Folder.zip" -d "VQA_RAD Image Folder"
Expected layout¶
VQA-RAD/
VQA_RAD Dataset Public.json # 2,248 QA records (harmonizer reads this)
VQA_RAD Dataset Public.xlsx # same data, spreadsheet form (unused)
VQA_RAD Dataset Public.xml # same data, XML form (unused)
Readme.docx
VQA_RAD Image Folder/ # flat, 315 JPEGs, no subdirs
synpic100132.jpg
synpic100176.jpg
...
Constructor arguments¶
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
base_image_dir |
str |
Yes* | None |
VQA_RAD Image Folder/ directory (flat, direct parent of the 315 JPEGs) |
json_path |
str |
No | auto | Path to VQA_RAD Dataset Public.json; auto-discovered in the parent of base_image_dir when omitted |
Shared arguments (inherited from BaseVQADataset)¶
| Argument | Type | Required | Default | Description |
|---|---|---|---|---|
output_struct |
bool |
No | False |
Include "answer_struct" in data dict — unset for VQA-RAD, whose answers are plain strings, not structured (no answer_struct column is populated) |
output_question_type |
bool |
No | False |
Include "question_type" string (e.g. PRES, ABN, MODALITY, …) in data dict |
transform |
MONAI Compose | No | 2D VQA pipeline | Custom MONAI transform (must not use SelectItemsD, or question/answer get dropped) |
cache_dir |
str |
No | "./cache" |
MONAI PersistentDataset cache; None disables caching |
dtype |
torch.dtype |
No | torch.bfloat16 |
Output tensor dtype for "img"; use torch.float32 on CPU |
harmonized_df |
pd.DataFrame |
No | None |
Pre-built harmonized DataFrame |
harmonizer |
harmonizer | No | None |
Pre-instantiated harmonizer |
harmonizer_path |
str |
No | None |
Path to saved harmonized CSV |
*base_image_dir can be omitted when harmonizer_path, harmonizer, or harmonized_df is provided.
Dataset constructor¶
from radharmony.dataset import VQARadDataset
ds = VQARadDataset(
base_image_dir="/data/VQA-RAD/VQA_RAD Image Folder",
output_question_type=True,
)
dataset = ds.get_datasets()
sample = dataset[0]
# sample["img"] -> Tensor(3, 224, 224)
# sample["question"] -> "Are regions of the brain infarcted?"
# sample["answer"] -> "Yes"
# sample["question_type"] -> "PRES"
Splitting by the dataset's own official train/test split (rather than a random patient-level split) requires filtering the harmonized DataFrame on split before constructing the dataset:
from radharmony.harmonizer import VQARadHarmonizer
h = VQARadHarmonizer(
json_path="/data/VQA-RAD/VQA_RAD Dataset Public.json",
base_image_dir="/data/VQA-RAD/VQA_RAD Image Folder",
)
df = h.harmonize()
train_ds = VQARadDataset(
base_image_dir="/data/VQA-RAD/VQA_RAD Image Folder",
harmonized_df=df[df["split"] == "train"],
)
test_ds = VQARadDataset(
base_image_dir="/data/VQA-RAD/VQA_RAD Image Folder",
harmonized_df=df[df["split"] == "test"],
)
Harmonizer¶
from radharmony.harmonizer import VQARadHarmonizer
h = VQARadHarmonizer(
json_path="/data/VQA-RAD/VQA_RAD Dataset Public.json",
base_image_dir="/data/VQA-RAD/VQA_RAD Image Folder",
)
df = h.harmonize()
print(df.columns.tolist())
# ['patient_id', 'study_id', 'question_id', 'image_path', 'question',
# 'answer', 'question_type', 'split', 'answer_type', 'image_organ']
df.to_csv("vqa_rad_harmonized.csv", index=False)
Load from saved harmonized CSV¶
import pandas as pd
from radharmony.dataset import VQARadDataset
ds = VQARadDataset(
base_image_dir="/data/VQA-RAD/VQA_RAD Image Folder",
harmonized_df=pd.read_csv("vqa_rad_harmonized.csv"),
)
Harmonizer notes¶
- Reads
VQA_RAD Dataset Public.json(a flat list of 2,248 records) directly — no separate label/annotation CSV - No
Patient IDor study-id field exists in the source;patient_idandstudy_idare both the image filename stem (e.g.synpic54610.jpg->synpic54610) since every image is an independent case qidis globally unique across all 2,248 questions and is used directly asquestion_idphrase_typein the source (freeform/para/test_freeform/test_para) marks the dataset's official train/test split — thetest_prefixed variants are the paper's held-out 451-question test set — carried through as thesplitcolumn (1,797 train / 451 test)answer_type(OPEN/CLOSED) andimage_organ(HEAD/CHEST/ABD) are carried through viaEXTRA_OUTPUT_COLSas bonus metadata, not part of the core VQA schema- A handful of source rows have integer answers (e.g. "How many gallstones are identified?" ->
4) and a couple ofanswer_typevalues have a trailing space ("CLOSED ") — both are normalized (cast tostr,.strip()'d) during harmonization answer_structis not populated — VQA-RAD's answers are plain strings, not structured/nested (unlike e.g. RAD-ReStruct)
VQA data dict¶
Each sample contains:
| Key | Type | Notes |
|---|---|---|
"img" |
Tensor (3, 224, 224) |
Loaded and normalised image (RGB — MedPix JPEGs are not all single-channel) |
"question" |
str |
Question text |
"answer" |
str |
Answer text |
"patient_id" |
str |
Image filename stem (no real patient concept) |
"study_id" |
str |
Same as patient_id (no real study concept) |
"question_id" |
str |
qid from the source JSON |
"question_type" |
str |
Question category (only when output_question_type=True) |
Notes on BaseVQADataset¶
VQA-RAD inherits BaseVQADataset instead of BaseRadiologicalDataset for the same reasons as MIMIC-Ext-CXR-QBA (see mimic_ext_cxr_qba.md): the standard 2-D transform's SelectItemsD would drop question/answer/question_id, and the output_cls/LABEL_COLS concept doesn't apply to free-text QA.
radharmony.integrity.check_dataset() (used by scripts/check_datasets.py and the integrity notebook) works unmodified against VQARadDataset — it already validates image shape/dtype/range/NaN generically and collates arbitrary sample keys, so no VQA-specific stats were needed for a meaningful pass/fail check. It does not currently render question/answer text in the Gradio app's viewer — app.py has no VQA-shaped UI (no question/answer display path); that remains unimplemented for this and every other VQA dataset.