Spaces:
Running on Zero
Running on Zero
Download build_samples.py from devYoda/scanlens-models: direct link, hf CLI and curl.
- Browser
- Download file 6.39 kB
-
https://huggingface.co/spaces/devYoda/scanlens-models/resolve/main/build_samples.py
- Command line
-
hf download hf://spaces/devYoda/scanlens-models/build_samples.py
-
curl -L -o build_samples.py https://huggingface.co/spaces/devYoda/scanlens-models/resolve/main/build_samples.py
6.39 kB
| """Builds public/samples/: a few labelled, anonymised scans from public datasets, shown as | |
| "Try a sample scan" on the upload page. Each keeps its dataset label as the known answer. | |
| .venv/bin/pip install pyarrow # only needed for this script | |
| .venv/bin/python build_samples.py | |
| Images already in public/samples/ are kept; delete one to download it again. | |
| """ | |
| import io | |
| import json | |
| import time | |
| import urllib.error | |
| import urllib.parse | |
| import urllib.request | |
| from pathlib import Path | |
| from PIL import Image | |
| HERE = Path(__file__).parent | |
| OUT = HERE.parent / "public" / "samples" | |
| HF = "https://huggingface.co/datasets" | |
| SOURCES = { | |
| "chest-xray": dict(repo="hf-vision/chest-xray-pneumonia", source="Chest X-ray pneumonia dataset (Kermany et al.)", licence="CC BY 4.0"), | |
| "brain-mri": dict(repo="BJyotibrat/Masoud-Nickparvar-Brain-Tumor-MRI-Dataset", source="Brain tumour MRI dataset (Masoud Nickparvar)", licence="CC BY 4.0"), | |
| "chest-ct": dict(repo="Mahadih534/Chest_CT-Scan_images-Dataset", source="Chest CT-scan images (Mohamed Hany)", licence="CC"), | |
| "chest-xray-tb": dict(repo="realsudarshan/chest-xray-tb-pneumonia", source="Chest X-ray TB, pneumonia and normal (compiled from public sets)", licence="CC BY 4.0"), | |
| "retina-photo": dict(repo="bumbledeep/eyepacs", source="EyePACS diabetic retinopathy photos (Kaggle 2015)", licence="MIT (as listed on the Hugging Face copy)"), | |
| "skin-photo": dict(repo="marmal88/skin_cancer", source="HAM10000 dermatoscopic images (Tschandl et al.)", licence="CC BY-NC 4.0"), | |
| "knee-xray": dict(repo="SilpaCS/kneeosteoarthritis", source="Knee osteoarthritis severity grading dataset (Chen, from the OAI)", licence="CC BY 4.0"), | |
| } | |
| # Row numbers of the parquet-only samples below (see from_rows). | |
| ROWS = {"chest-xray-tuberculosis": 1700, "retina-photo-moderate-dr": 12663, "retina-photo-no-dr": 12600, | |
| "skin-photo-melanoma": 1201, "skin-photo-nevus": 800, "knee-xray-kl3": 8000, "knee-xray-kl0": 500} | |
| # (sample id, scan type, dataset label, folder in the dataset repo). Chest X-rays come from a | |
| # parquet file instead of folders, so their folder is the class index. | |
| WANTED = [ | |
| ("chest-xray-normal", "chest-xray", "Normal", 0), | |
| ("chest-xray-pneumonia", "chest-xray", "Pneumonia", 1), | |
| ("brain-mri-glioma", "brain-mri", "Glioma", "Testing/glioma"), | |
| ("brain-mri-meningioma", "brain-mri", "Meningioma", "Testing/meningioma"), | |
| ("brain-mri-notumor", "brain-mri", "No tumour", "Testing/notumor"), | |
| ("chest-ct-adenocarcinoma", "chest-ct", "Adenocarcinoma", "test/adenocarcinoma"), | |
| ("chest-ct-normal", "chest-ct", "Normal", "test/normal"), | |
| # These datasets are only parquet files, so samples are picked by row through the datasets server: | |
| # (split, row number). A fifth item names the SOURCES entry when it isn't the scan type. | |
| ("chest-xray-tuberculosis", "chest-xray", "Tuberculosis", ("test", ROWS["chest-xray-tuberculosis"]), "chest-xray-tb"), | |
| ("retina-photo-moderate-dr", "retina-photo", "Moderate diabetic retinopathy", ("train", ROWS["retina-photo-moderate-dr"])), | |
| ("retina-photo-no-dr", "retina-photo", "No diabetic retinopathy", ("train", ROWS["retina-photo-no-dr"])), | |
| ("skin-photo-melanoma", "skin-photo", "Melanoma", ("test", ROWS["skin-photo-melanoma"])), | |
| ("skin-photo-nevus", "skin-photo", "Mole (melanocytic nevus)", ("test", ROWS["skin-photo-nevus"])), | |
| ("knee-xray-kl3", "knee-xray", "Kellgren-Lawrence grade 3", ("train", ROWS["knee-xray-kl3"])), | |
| ("knee-xray-kl0", "knee-xray", "Kellgren-Lawrence grade 0", ("train", ROWS["knee-xray-kl0"])), | |
| ] | |
| # Colour photographs stay in colour; scans are saved in grayscale. | |
| COLOUR = {"retina-photo", "skin-photo"} | |
| def get(url, tries=3): | |
| for attempt in range(tries): | |
| try: | |
| with urllib.request.urlopen(url, timeout=60) as r: | |
| return r.read() | |
| except urllib.error.URLError: # the datasets server sometimes answers 502 or 429 | |
| if attempt == tries - 1: | |
| raise | |
| time.sleep(10) | |
| def from_folder(repo, folder): | |
| """A file a third of the way through the folder, so reruns pick the same image.""" | |
| tree = json.loads(get(f"https://huggingface.co/api/datasets/{repo}/tree/main/{urllib.parse.quote(folder)}")) | |
| files = sorted(f["path"] for f in tree if f["type"] == "file") | |
| return get(f"{HF}/{repo}/resolve/main/{urllib.parse.quote(files[len(files) // 3])}") | |
| def from_parquet(repo, label): | |
| import pyarrow.parquet as pq | |
| data = get(f"{HF}/{repo}/resolve/main/data/validation-00000-of-00001.parquet") | |
| rows = pq.read_table(io.BytesIO(data)).to_pylist() | |
| return next(r for r in rows if r["label"] == label)["image"]["bytes"] | |
| def from_rows(repo, split, row): | |
| q = urllib.parse.quote(repo) | |
| config = json.loads(get(f"https://datasets-server.huggingface.co/splits?dataset={q}"))["splits"][0]["config"] | |
| rows = json.loads(get(f"https://datasets-server.huggingface.co/rows?dataset={q}&config={config}" | |
| f"&split={split}&offset={row}&length=1"))["rows"] | |
| image = next(v for v in rows[0]["row"].values() if isinstance(v, dict) and "src" in v) | |
| return get(image["src"]) | |
| def main(): | |
| OUT.mkdir(parents=True, exist_ok=True) | |
| manifest = [] | |
| for sid, scan, truth, where, *source in WANTED: | |
| src = SOURCES[source[0] if source else scan] | |
| path = OUT / f"{sid}.jpg" | |
| if not path.exists(): | |
| if isinstance(where, tuple): | |
| raw = from_rows(src["repo"], *where) | |
| elif isinstance(where, int): | |
| raw = from_parquet(src["repo"], where) | |
| else: | |
| raw = from_folder(src["repo"], where) | |
| img = Image.open(io.BytesIO(raw)).convert("RGB" if scan in COLOUR else "L") | |
| # Colour photos compress less well; smaller keeps each sample under about 150 KB. | |
| img.thumbnail((768, 768) if scan in COLOUR else (1024, 1024)) | |
| img.save(path, "JPEG", quality=85 if scan in COLOUR else 90) | |
| print("downloaded", path.name) | |
| manifest.append(dict(id=sid, file=path.name, scan=scan, truth=truth, source=src["source"], | |
| url=f"{HF}/{src['repo']}", licence=src["licence"])) | |
| (OUT / "samples.json").write_text(json.dumps(manifest, indent=2) + "\n") | |
| print(f"wrote {len(manifest)} samples") | |
| if __name__ == "__main__": | |
| main() | |