Skip to content

OpenDXA/opendxa-python

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

opendxa-python

A Python library for parsing DXA (DEXA) bone density and body composition scan reports from various vendors.

Overview

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

Supported Scanners

Manufacturer Models Status
GE Lunar Prodigy, Prodigy Primo, iDXA Supported

Installation

pip install opendxa

Or install from source:

git clone https://github.com/opendxa/opendxa-python.git
cd opendxa-python
pip install -e .

Dependencies

  • Python 3.9+
  • pdfplumber
  • pydantic

Quick Start

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))

Output Schema

OpenDXA outputs data conforming to the OpenDXA Core Schema, which includes:

Patient

  • Patient ID, date of birth, sex, age at exam

Exam

  • Exam date, facility, indication

Device

  • Manufacturer, model, software version

Bone Density

  • Sites (lumbar spine, hip, femoral neck, whole body, etc.)
  • BMD (g/cm²), T-scores, Z-scores
  • WHO classification (normal, osteopenia, osteoporosis)
  • Reference population

Body Composition

  • 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

Example Output

{
  "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
    }
  }
}

Adding a Custom Parser

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())

Unit Conversions

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³

Schema Documentation

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.

Contributing

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

License

MIT License - see LICENSE for details.

Disclaimer

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.

Releases

No releases published

Packages

No packages published

Languages