sandy45's picture
Deploy trained ChestViT Space
37fbec9
Raw
History Blame Contribute Delete
8.4 kB
"""
data/download.py
----------------
Downloads the NIH ChestX-ray14 dataset from Kaggle using the Kaggle API.
SETUP (one-time):
1. Create a Kaggle account at https://www.kaggle.com
2. Go to: Account β†’ Settings β†’ Create New API Token β†’ downloads kaggle.json
3. Place kaggle.json at:
Windows : C:\\Users\\<YourName>\\.kaggle\\kaggle.json
Linux : ~/.kaggle/kaggle.json
4. Run: python data/download.py
Dataset: nih-chest-xrays/data
- 112,120 frontal chest X-ray images (PNG, 1024Γ—1024)
- Data_Entry_2017.csv β€” image metadata + labels
- train_val_list.txt β€” official train+val split
- test_list.txt β€” official test split
Total size: ~42 GB (compressed ~11 GB β€” the script downloads zip chunks)
"""
import os
import sys
import zipfile
import shutil
from pathlib import Path
from typing import Optional
# ── Rich for pretty console output ───────────────────────────────────────────
try:
from rich.console import Console
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn
from rich.panel import Panel
console = Console()
except ImportError:
import builtins
class _FallbackConsole:
def print(self, *args, **kwargs):
builtins.print(*args)
def rule(self, *args, **kwargs):
builtins.print("─" * 60)
console = _FallbackConsole()
# ── Constants ─────────────────────────────────────────────────────────────────
KAGGLE_DATASET = "nih-chest-xrays/data"
DEFAULT_RAW_DIR = Path("./data/raw")
REQUIRED_FILES = [
"Data_Entry_2017.csv",
"train_val_list.txt",
"test_list.txt",
]
def _check_kaggle_credentials() -> bool:
"""Verify kaggle.json exists and is readable."""
kaggle_dir = Path.home() / ".kaggle"
kaggle_json = kaggle_dir / "kaggle.json"
if not kaggle_json.exists():
console.print(
"[bold red]βœ— kaggle.json not found![/bold red]\n"
f" Expected location: {kaggle_json}\n\n"
" Steps to fix:\n"
" 1. Visit https://www.kaggle.com β†’ Account β†’ Settings\n"
" 2. Click 'Create New API Token' β†’ downloads kaggle.json\n"
f" 3. Move it to: {kaggle_dir}\n"
" 4. Re-run this script.",
style="red",
)
return False
# Ensure correct permissions (important on Linux/macOS)
if os.name != "nt":
kaggle_json.chmod(0o600)
return True
def _verify_images_dir(raw_dir: Path) -> bool:
"""
Check if images are already extracted to avoid re-downloading.
We consider extraction complete if >1000 .png files exist.
"""
images_dir = raw_dir / "images"
if not images_dir.exists():
return False
png_count = sum(1 for _ in images_dir.glob("*.png"))
console.print(f" Found {png_count:,} existing PNG files in {images_dir}")
return png_count > 1000
def download_dataset(
raw_dir: Optional[Path] = None,
force: bool = False,
) -> Path:
"""
Download and extract NIH ChestX-ray14 from Kaggle.
Args:
raw_dir: Directory to extract data into.
force: Re-download even if files already exist.
Returns:
Path to the extracted raw data directory.
"""
raw_dir = Path(raw_dir or DEFAULT_RAW_DIR)
raw_dir.mkdir(parents=True, exist_ok=True)
# ── 1. Check credentials ──────────────────────────────────────────────────
console.print(Panel("[bold cyan]NIH ChestX-ray14 β€” Dataset Download[/bold cyan]",
subtitle="Kaggle API"))
if not _check_kaggle_credentials():
sys.exit(1)
# ── 2. Check if already downloaded ───────────────────────────────────────
required_present = all((raw_dir / f).exists() for f in REQUIRED_FILES)
images_present = _verify_images_dir(raw_dir)
if required_present and images_present and not force:
console.print("[bold green]βœ“ Dataset already downloaded and extracted.[/bold green]")
console.print(f" Data root: {raw_dir.resolve()}")
return raw_dir
# ── 3. Import kaggle (after credentials check) ────────────────────────────
try:
import kaggle # noqa: F401 β€” triggers credential load
from kaggle.api.kaggle_api_extended import KaggleApiExtended
except ImportError:
console.print("[red]βœ— kaggle package not installed. Run: pip install kaggle[/red]")
sys.exit(1)
api = KaggleApiExtended()
api.authenticate()
# ── 4. Download dataset zip(s) ────────────────────────────────────────────
console.print(f"\n[yellow]⬇ Downloading '{KAGGLE_DATASET}' β†’ {raw_dir.resolve()}[/yellow]")
console.print(" ⚠ This dataset is ~42 GB. Download time depends on your connection.")
console.print(" ⚠ The Kaggle API downloads each image zip file sequentially.\n")
api.dataset_download_files(
KAGGLE_DATASET,
path=str(raw_dir),
unzip=False, # We handle extraction ourselves for better control
quiet=False,
)
console.print("[green]βœ“ Download complete.[/green]")
# ── 5. Extract all zip files ──────────────────────────────────────────────
images_dir = raw_dir / "images"
images_dir.mkdir(exist_ok=True)
zip_files = sorted(raw_dir.glob("*.zip")) + sorted(raw_dir.glob("images_*.tar.gz"))
console.print(f"\n[yellow]πŸ“¦ Extracting {len(zip_files)} archive(s)...[/yellow]")
for zip_path in zip_files:
console.print(f" Extracting: {zip_path.name}")
if zip_path.suffix == ".zip":
with zipfile.ZipFile(zip_path, "r") as zf:
# Extract images to images_dir, metadata to raw_dir
for member in zf.namelist():
if member.endswith(".png"):
# Extract flat into images_dir
target = images_dir / Path(member).name
if not target.exists():
with zf.open(member) as src, open(target, "wb") as dst:
shutil.copyfileobj(src, dst)
elif not Path(member).is_dir():
zf.extract(member, raw_dir)
console.print(f" βœ“ {zip_path.name} extracted.")
# ── 6. Verify essential files ─────────────────────────────────────────────
console.rule("Verification")
all_ok = True
for fname in REQUIRED_FILES:
fpath = raw_dir / fname
status = "[green]βœ“[/green]" if fpath.exists() else "[red]βœ— MISSING[/red]"
console.print(f" {status} {fname}")
if not fpath.exists():
all_ok = False
png_count = sum(1 for _ in images_dir.glob("*.png"))
console.print(f" [cyan]PNG images found: {png_count:,}[/cyan]")
if all_ok and png_count > 1000:
console.print("\n[bold green]πŸŽ‰ Dataset ready![/bold green]")
console.print(f" Data root : {raw_dir.resolve()}")
console.print(f" Images : {images_dir.resolve()}")
else:
console.print("\n[bold red]⚠ Some files may be missing. Check the output above.[/bold red]")
return raw_dir
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Download NIH ChestX-ray14 from Kaggle")
parser.add_argument(
"--output-dir", type=str, default=str(DEFAULT_RAW_DIR),
help="Directory to save the dataset (default: ./data/raw)",
)
parser.add_argument(
"--force", action="store_true",
help="Re-download even if dataset already exists",
)
args = parser.parse_args()
download_dataset(raw_dir=Path(args.output_dir), force=args.force)