TorchXRayVision

repository·main·Indexed 22 days ago

https://github.com/mlmed/torchxrayvision

An open-source library providing a common interface for chest X-ray datasets and deep learning models. It includes pre-trained DenseNet and ResNet models for pathology prediction, specialized models for race and age, anatomical segmentation models, and pre-trained autoencoders. The library supports standardized preprocessing and a uniform interface for multiple public datasets including NIH, CheXpert, PadChest, and MIMIC-CXR.

Tokens
15.4K
Snippets
53
Records
62
Agent score
74%

What's inside torchxrayvision

  1. Enable pathology masks in datasets

    main

    Many datasets (like NIH_Dataset and CheXpert_Dataset) support pixel-level segmentation masks. When initializing the dataset, set pathology_masks=True.

    Each sample returned by the dataset will then include a "pathology_masks" key, which maps pathology names to binary pixel arrays of the same spatial size as the image ("img").

    import torchxrayvision as xrv
    
    ds = xrv.datasets.NIH_Dataset(
        imgpath="/data/NIH",
        pathology_masks=True,
    )
    sample = ds[0]
    print(sample.keys())          # dict_keys(['img', 'lab', 'pathology_masks', ...])
    print(sample["pathology_masks"].keys())  # e.g. {'Atelectasis': array(...)}
  2. Interpret model predictions and handle ~0.5 values

    main

    If a model prediction for a specific pathology is consistently around 0.5, it likely indicates that the specific label was not included in the training set for that weight configuration (an untrained head).

    Only interpret labels that are explicitly present in model.pathologies. You can inspect the raw logits for all pathologies using the following pattern:

    import torchxrayvision as xrv
    import torch
    
    model = xrv.models.DenseNet(weights="densenet121-res224-all")
    model.eval()
    
    img = xrv.utils.load_image("path/to/xray.jpg")
    img = xrv.datasets.XRayCenterCrop()(img)
    img = xrv.datasets.XRayResizer(224)(img)
    img_tensor = torch.from_numpy(img).unsqueeze(0)  # [1, 1, 224, 224]
    
    with torch.no_grad():
        output = model(img_tensor)[0].cpu().numpy()  # shape: (num_pathologies,)
    
    for pathology, score in zip(model.pathologies, output):
        print(f"{pathology}: {score:.4f}")
  3. How TorchXRayVision datasets work

    main

    TorchXRayVision provides a unified interface for various chest X-ray datasets through a common base class xrv.datasets.Dataset. Every dataset class exposes three core attributes that allow you to access labels and metadata consistently:

    • pathologies: An ordered list of label names (strings).
    • labels: A 2-D NumPy array of shape (samples, pathologies) containing values 1, 0, or NaN.
    • csv: A Pandas DataFrame containing per-image metadata.

    To load a dataset, you typically only need to provide the path to the image directory.

    import torchxrayvision as xrv
    
    d = xrv.datasets.NIH_Dataset(imgpath="/path/to/images")
  4. Use XRay image transforms with torchvision

    main

    TorchXRayVision provides specialized transform objects designed for X-ray images. These transforms operate on (1, H, W) float32 NumPy arrays (the standard output format for xrv.datasets.Dataset.__getitem__ calls) and are compatible with torchvision.transforms.Compose.

    from torchvision import transforms
    import torchxrayvision as xrv
    
    # Example of composing X-ray specific transforms
    transform = transforms.Compose([
        xrv.datasets.XRayResizer(224),
        xrv.datasets.XRayCenterCrop(224),
    ])
  5. Use the TorchXRayVision Model interface

    main

    All models in torchxrayvision follow a standard interface defined in xrv.models.Model. The primary method for inference is forward(x), where x is the input tensor. Models are typically initialized with specific weights and optional thresholding parameters.

    # Conceptual usage of the Model interface
    import torch
    from xrv.models import Model
    
    # Assuming a model instance 'model' is initialized
    # x should be a tensor of shape (batch_size, channels, height, width)
    x = torch.randn(1, 1, 224, 224)
    output = model.forward(x)
  6. Understand dataset fields and metadata

    main

    Each dataset object provides access to pathologies, labels, and metadata via a pandas DataFrame. When using xrv.datasets.Subset_Dataset or xrv.datasets.Merge_Dataset, these fields are preserved.

    Core Fields

    • .pathologies: A list of pathology names contained in the dataset.
    • .labels: A tensor/array containing 1, 0, or NaN for each label in .pathologies.
    • .csv: A pandas DataFrame containing the metadata.

    Common CSV Metadata Fields

    • csv.patientid: Unique identifier for samples.
    • csv.offset_day_int: Integer time offset in days.
    • csv.age_years: Patient age in years.
    • csv.sex_male: Boolean/indicator if patient is male.
    • csv.sex_female: Boolean/indicator if patient is female.
  7. Quickstart: Process a chest X-ray image with a pre-trained model

    main

    This guide demonstrates the standard workflow for using TorchXRayVision: preparing an image using the library's normalization and transformation utilities, loading a pre-trained DenseNet model, and extracting pathology probabilities.

    Note: This software is for research and software development only. It is NOT FOR MEDICAL USE and should not be used for diagnostic or clinical decision making.

    import torchxrayvision as xrv
    import skimage, torch, torchvision
    
    # 1. Prepare the image:
    img = skimage.io.imread("16747_3_1.jpg")
    # Convert 8-bit image to [-1024, 1024] range
    img = xrv.datasets.normalize(img, 255) 
    # Make single color channel
    img = img.mean(2)[None, ...]
    
    transform = torchvision.transforms.Compose([
        xrv.datasets.XRayCenterCrop(),
        xrv.datasets.XRayResizer(224),
    ])
    
    img = transform(img)
    img = torch.from_numpy(img)
    
    # 2. Load model and process image
    model = xrv.models.DenseNet(weights="densenet121-res224-all")
    # Pass image with batch dimension: [1, C, H, W]
    outputs = model(img[None,...]) 
    
    # 3. Print results as a dictionary mapping pathology names to probabilities
    results = dict(zip(model.pathologies, outputs[0].detach().numpy()))
    print(results)
  8. Quickstart: Load a model and process a chest X-ray image

    main

    To use TorchXRayVision, you need to prepare the image by normalizing it, converting it to a single channel, and applying specific transformations (centering and resizing). You can then load a pre-trained DenseNet model and pass the processed image through it to get pathology predictions.

    import torchxrayvision as xrv
    import skimage, torch, torchvision
    
    # Prepare the image:
    img = skimage.io.imread("16747_3_1.jpg")
    img = xrv.datasets.normalize(img, 255) # convert 8-bit image to [-1024, 1024] range
    img = img.mean(2)[None, ...] # Make single color channel
    
    transform = torchvision.transforms.Compose([xrv.datasets.XRayCenterCrop(),xrv.datasets.XRayResizer(224)])
    
    img = transform(img)
    img = torch.from_numpy(img)
    
    # Load model and process image
    model = xrv.models.DenseNet(weights="densenet121-res224-all")
    outputs = model(img[None,...]) # or model.features(img[None,...]) 
    
    # Print results
    print(dict(zip(model.pathologies,outputs[0].detach().numpy())))
  9. Normalize image intensity to the expected range

    main

    All TorchXRayVision models require pixel values to be in the range [-1024, 1024]. Providing images in [0, 255] (uint8) or [0, 1] (float) will result in incorrect predictions.

    Recommended Approach: Use xrv.utils.load_image, which automatically handles PNG, JPG, and DICOM files and returns a correctly normalized [1, H, W] float32 array.

    Manual Normalization: If loading images via other libraries (e.g., skimage, PIL, cv2), use xrv.utils.normalize. You must specify the maxval corresponding to the original bit depth:

    • For 8-bit images: maxval=255
    • For 12-bit images (common in DICOM): maxval=4095

    Note: The warn_normalization utility will issue a warning during the first forward pass if it detects an incorrect input range.

    import skimage.io
    import torchxrayvision as xrv
    
    # For 8-bit images
    img = skimage.io.imread("path/to/xray.jpg")
    img = xrv.utils.normalize(img, maxval=255, reshape=True)
    
    # For 12-bit images (e.g. raw DICOM pixels)
    img = xrv.utils.normalize(raw_pixels, maxval=4095)
  10. Ensure reproducibility with random seeds

    main

    To ensure deterministic behavior when creating datasets and models, set the seeds for random, numpy, and torch (including CUDA if applicable) before initialization.

    import torch, numpy as np, random
    seed = 0
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    if torch.cuda.is_available():
        torch.cuda.manual_seed_all(seed)
  11. Pre-fetch model weights to avoid slow first imports

    main
    The first time a specific pretrained weight (e.g., densenet121-res224-all) is used, it must be downloaded, which can cause a delay. To avoid this in production or deployment, you can pre-fetch weights in a setup script by calling torchxrayvision.models.get_model(weights=...) or by instantiating the model directly.