A Python library for parsing DXA (DEXA) bone density and body composition scan reports from various vendors.
This library extracts structured data from DXA (DEXA) scan PDF reports and normalizes it into a consistent JSON schema, regardless of the scanner manufacturer. This enables:
- Data portability - Convert proprietary PDF reports to an open format
- Longitudinal tracking - Compare scans across different facilities and equipment
- Research & analytics - Aggregate data from multiple sources
- Integration - Feed normalized data into health apps and research databases
| Manufacturer | Models | Status |
|---|---|---|
| GE Lunar | Prodigy, Prodigy Primo, iDXA | Supported |
pip install opendxaOr install from source:
git clone https://github.com/opendxa/opendxa-python.git
cd opendxa-python
pip install -e .- Python 3.9+
- pdfplumber
- pydantic
from opendxa import parse_dxa_pdf
# Parse a DXA scan PDF
report = parse_dxa_pdf("path/to/scan.pdf")
# Access patient information
print(f"Patient: {report.patient.patient_id}")
print(f"Age: {report.patient.age_at_exam_years}")
# Access bone density results
print(f"Total Body BMD: {report.bone_density.sites[-1].bmd_g_cm2} g/cm²")
print(f"T-Score: {report.bone_density.sites[-1].t_score}")
print(f"Diagnosis: {report.bone_density.overall.diagnosis}")
# Access body composition
if report.body_composition and report.body_composition.has_body_comp:
print(f"Body Fat: {report.body_composition.totals.percent_body_fat}%")
print(f"Lean Mass: {report.body_composition.totals.lean_mass_kg} kg")
# Export to JSON
print(report.model_dump_json(indent=2))OpenDXA outputs data conforming to the OpenDXA Core Schema, which includes:
- Patient ID, date of birth, sex, age at exam
- Exam date, facility, indication
- Manufacturer, model, software version
- Sites (lumbar spine, hip, femoral neck, whole body, etc.)
- BMD (g/cm²), T-scores, Z-scores
- WHO classification (normal, osteopenia, osteoporosis)
- Reference population
- Total body: mass, fat, lean, BMC, body fat %
- Regional: arms, legs, trunk, android, gynoid
- Visceral adipose tissue (VAT)
- Derived metrics: A/G ratio, appendicular lean mass
{
"schema_version": "0.1",
"patient": {
"patient_id": "Doe, John",
"sex_at_birth": "male",
"date_of_birth": "1980-11-13",
"age_at_exam_years": 38.7
},
"device": {
"manufacturer": "GE Lunar",
"model": "Lunar Prodigy Primo"
},
"bone_density": {
"sites": [
{
"site_id": "ge_lunar_total",
"site_type": "whole_body",
"bmd_g_cm2": 1.406,
"t_score": 2.0,
"who_classification": "normal"
}
],
"overall": {
"lowest_t_score": 2.0,
"diagnosis": "normal"
}
},
"body_composition": {
"has_body_comp": true,
"totals": {
"body_mass_kg": 98.43,
"percent_body_fat": 11.8,
"lean_mass_kg": 83.46
}
}
}from opendxa.parsers.base import DXAParser, PDFContent
from opendxa.models.schema import Manufacturer, OpenDXAReport
class CustomVendorParser(DXAParser):
VENDOR = Manufacturer.OTHER
PARSER_VERSION = "1.0.0"
def can_parse(self, content: PDFContent) -> bool:
return "Custom Vendor" in content.full_text
def parse(self, content: PDFContent) -> OpenDXAReport:
# Implement your parsing logic
...
# Register the parser
factory = DXAParserFactory()
factory.register_parser(CustomVendorParser())OpenDXA normalizes all values to metric units. Conversion utilities are available:
from opendxa.utils import lbs_to_kg, inches_to_cm, cubic_inches_to_cm3
weight_kg = lbs_to_kg(217.0) # 98.43 kg
height_cm = inches_to_cm(73.0) # 185.42 cm
vat_cm3 = cubic_inches_to_cm3(46.81) # 767.08 cm³The OpenDXA schema is designed to be:
- Vendor-agnostic - Works with any DXA manufacturer
- Comprehensive - Captures bone density, body composition, and metadata
- Extensible - Additional fields can be added without breaking compatibility
- Validated - Pydantic models enforce type safety and constraints
See schema/ for the full JSON Schema definitions.
Contributions are welcome! See CONTRIBUTING.md for guidelines.
Priority areas:
- Adding parsers for Hologic and Norland scanners
- Improving extraction accuracy
- Adding more sample reports for testing
MIT License - see LICENSE for details.
This software is for informational purposes only and is not intended to provide medical advice. DXA scan interpretation should be performed by qualified healthcare professionals. The authors are not responsible for any clinical decisions made based on data extracted by this tool.