Cookbook 64: Offline Pulmonary CT Emphysema & COPD Quantitative Classifier

This cookbook details how to deploy a localized pulmonology CT analysis engine to segment lung parenchyma, calculate low-attenuation area percentages (LAA% <-950 HU), and stage Chronic Obstructive Pulmonary Disease (COPD) severity directly from volumetric chest CT scans without cloud API dependencies.

1. Overview & Use Case

Quantifying functional lung destruction in COPD and emphysema requires reproducible low-attenuation volumetric measurements across thin-slice chest CT series. In clinical trials, pulmonary rehabilitation centers, and occupational health clinics, localized CT densitometry accelerates disease staging.

This engine enables:

       [ Volumetric Thin-Slice Chest CT / DICOM Series ]
                              │
                              ▼
                    [ COPDPress-CT-v1 ] 
                    ├── 3D Lung Parenchyma Segmentation
                    └── Airway & Vessel Suppression
                              │
                              ▼
                  [ Emphysema-Quant-CLI ]
                  ├── LAA% Densitometry (<-950 HU)
                  └── GOLD Severity Classification (1-4)
                              │
                              ▼
         [ Structured Pulmonology Diagnostic Summary JSON ]

2. Installation & Prerequisites

Ensure Python 3.10+, pydicom, SimpleITK, torch, and numpy are installed:

pip install pydicom SimpleITK torch numpy

Clone the offline model weights:

git clone https://github.com/OpenPHRorg/copdpress-ct-local.git
cd copdpress-ct-local

3. Volumetric CT Preprocessing & HU Calibration

Resample chest CT volume to isotropic voxel size (1.0mm³) and convert raw pixel data to Hounsfield Units (HU):

import SimpleITK as sitk
import numpy as np

def load_and_calibrate_ct(dicom_dir):
    reader = sitk.ImageSeriesReader()
    dicom_names = reader.GetGDCMSeriesFileNames(dicom_dir)
    reader.SetFileNames(dicom_names)
    image = reader.Execute()

    # Resample to 1.0mm x 1.0mm x 1.0mm isotropic voxels
    original_spacing = image.GetSpacing()
    new_spacing = [1.0, 1.0, 1.0]
    original_size = image.GetSize()
    
    new_size = [
        int(round(original_size[i] * original_spacing[i] / new_spacing[i]))
        for i in range(3)
    ]

    resample = sitk.ResampleImageFilter()
    resample.SetInterpolator(sitk.sitkLinear)
    resample.SetOutputSpacing(new_spacing)
    resample.SetSize(new_size)
    resample.SetOutputDirection(image.GetDirection())
    resample.SetOutputOrigin(image.GetOrigin())
    
    resampled_image = resample.Execute(image)
    hu_array = sitk.GetArrayFromImage(resampled_image)
    
    return hu_array

# Example loading
ct_volume_hu = load_and_calibrate_ct("./sample_chest_ct_dicom")
print(f"Calibrated CT Volume Shape (HU): {ct_volume_hu.shape}")

4. Inference & Densitometric Emphysema Index

Execute lung segmentation and calculate Low-Attenuation Area (LAA% <-950 HU):

import torch

def quantize_emphysema(hu_volume):
    model = torch.hub.load('OpenPHRorg/copdpress-ct-local', 'copd_seg_v1', pretrained=True)
    model.eval()

    tensor_input = torch.from_numpy(hu_volume).unsqueeze(0).unsqueeze(0).float()
    
    with torch.no_grad():
        lung_mask = model(tensor_input)
        lung_mask_binary = (torch.sigmoid(lung_mask) > 0.5).numpy()[0, 0]

    # Mask lung parenchyma
    lung_hu = hu_volume[lung_mask_binary]
    
    # Calculate Emphysema Index (LAA% <-950 HU)
    laa_950 = np.sum(lung_hu < -950) / float(len(lung_hu)) * 100.0
    laa_910 = np.sum(lung_hu < -910) / float(len(lung_hu)) * 100.0

    gold_stage = "GOLD 4 (Very Severe)" if laa_950 > 25.0 else "GOLD 3 (Severe)" if laa_950 > 15.0 else "GOLD 2 (Moderate)" if laa_950 > 5.0 else "GOLD 1 (Mild / Normal)"

    return {
        "emphysema_index_laa950_pct": round(laa_950, 2),
        "laa910_pct": round(laa_910, 2),
        "copd_gold_stage": gold_stage,
        "total_lung_volume_liters": round(len(lung_hu) / 1e6, 2)
    }

# Run quantitation assessment
result = quantize_emphysema(ct_volume_hu)
print(f"Pulmonology Diagnostic Summary: {result}")

5. Verification & Summary

This engine delivers instant, offline pulmonary densitometry conforming to GOLD COPD guidelines.