Imaging Bridge
Intent
Connect FHIR-based clinical systems with DICOM imaging systems through standardized web APIs, enabling seamless integration between clinical metadata and imaging data.
Forces
- Metadata vs Payload (Imaging): Clinical data (FHIR) and imaging data (DICOM) live in separate systems with different access patterns.
- Interactive Viewing & Context Sync: Clinical workflows require real-time coordination between multiple applications.
Structure
The Imaging Bridge pattern provides a unified interface that maps between FHIR ImagingStudy resources and DICOMweb endpoints for actual image data retrieval.
Key Components
ImagingBridge
Main coordination component for FHIR-DICOM integration
FHIRClient
Retrieves ImagingStudy resources and clinical metadata
DICOMwebClient
Interacts with PACS/VNA via WADO-RS, QIDO-RS, STOW-RS
AuthMapper
Maps authentication between FHIR and DICOM contexts
ImageCache
Caches frequently accessed images and thumbnails
Behavior
EMR Image Display Workflow
The following sequence shows how an EMR retrieves and displays images through the bridge:
Key Integration Points
- Study Discovery
- Endpoint Resolution
- Authentication Mapping
- Image Retrieval
- Unified Presentation
Implementation Considerations
ImagingStudy Resource Structure
Example FHIR ImagingStudy resource showing the relationship between FHIR metadata and DICOMweb endpoints for image retrieval.
{
"resourceType": "ImagingStudy",
"id": "example-ct-chest",
"identifier": [{
"type": {
"coding": [{
"system": "http://dicom.nema.org/resources/ontology/DCM",
"code": "110180",
"display": "Study Instance UID"
}]
},
"value": "1.2.840.113619.2.5.1762583153.215519.978957063.78"
}],
"status": "available",
"subject": {
"reference": "Patient/example"
},
"started": "2023-12-01T09:00:00Z",
"endpoint": [{
"reference": "Endpoint/dicomweb-pacs-1"
}],
"numberOfSeries": 1,
"numberOfInstances": 64,
"series": [{
"uid": "1.2.840.113619.2.5.1762583153.215519.978957063.78.1",
"number": 1,
"modality": {
"system": "http://dicom.nema.org/resources/ontology/DCM",
"code": "CT"
},
"description": "Chest CT with contrast",
"numberOfInstances": 64,
"endpoint": [{
"reference": "Endpoint/dicomweb-pacs-1"
}],
"bodySite": {
"system": "http://snomed.info/sct",
"code": "51185008",
"display": "Thoracic structure"
}
}]
}
Endpoint Configuration
Configuration for DICOMweb endpoints (WADO-RS, QIDO-RS, STOW-RS) including authentication settings and connection parameters.
{
"resourceType": "Endpoint",
"id": "dicomweb-pacs-1",
"status": "active",
"connectionType": {
"system": "http://terminology.hl7.org/CodeSystem/endpoint-connection-type",
"code": "dicom-wado-rs"
},
"name": "Hospital PACS DICOMweb Service",
"managingOrganization": {
"reference": "Organization/hospital"
},
"payloadType": [{
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/endpoint-payload-type",
"code": "DICOM"
}]
}],
"payloadMimeType": [
"application/dicom",
"image/jpeg",
"image/png"
],
"address": "https://pacs.hospital.org/dicomweb",
"header": [
"Authorization: Bearer {token}"
]
}
Bridge Implementation
Core bridge implementation that coordinates between FHIR servers and DICOMweb endpoints with study/series/instance resolution.
class ImagingBridge:
def __init__(self, fhir_client, dicomweb_client, auth_mapper):
self.fhir_client = fhir_client
self.dicomweb_client = dicomweb_client
self.auth_mapper = auth_mapper
async def get_patient_studies(self, patient_id, auth_context):
"""Retrieve imaging studies for patient with unified metadata"""
# Query FHIR for ImagingStudy resources
studies = await self.fhir_client.search(
'ImagingStudy',
{'patient': patient_id, 'status': 'available'}
)
# Enrich with DICOMweb metadata if requested
enriched_studies = []
for study in studies:
enriched_study = await self._enrich_study_metadata(study, auth_context)
enriched_studies.append(enriched_study)
return enriched_studies
async def _enrich_study_metadata(self, imaging_study, auth_context):
"""Enrich FHIR study with additional DICOM metadata"""
# Get DICOMweb endpoint
endpoint = await self._resolve_endpoint(imaging_study.endpoint[0])
# Convert FHIR auth to DICOM auth
dicom_auth = self.auth_mapper.map_auth_context(auth_context, endpoint)
# Query DICOM metadata via QIDO-RS
study_uid = self._extract_study_uid(imaging_study)
dicom_metadata = await self.dicomweb_client.qido_search_studies(
endpoint.address, study_uid, dicom_auth
)
# Merge metadata
return self._merge_fhir_dicom_metadata(imaging_study, dicom_metadata)
async def get_image_data(self, study_uid, series_uid, instance_uid,
accept_type="image/jpeg", auth_context=None):
"""Retrieve image data via WADO-RS"""
# Find appropriate endpoint
endpoint = await self._find_endpoint_for_study(study_uid)
# Map authentication
dicom_auth = self.auth_mapper.map_auth_context(auth_context, endpoint)
# Retrieve image via WADO-RS
image_data = await self.dicomweb_client.wado_retrieve_instance(
endpoint.address, study_uid, series_uid, instance_uid,
accept_type, dicom_auth
)
return image_data
Authentication Mapping
Maps authentication contexts between FHIR (OAuth2/SMART) and DICOM systems (certificates, tokens, session cookies).
class AuthenticationMapper:
def __init__(self):
self.mapping_rules = {
'smart-on-fhir': self._map_smart_to_dicom,
'basic-auth': self._map_basic_auth,
'mutual-tls': self._map_mtls
}
def map_auth_context(self, fhir_auth_context, dicom_endpoint):
"""Map FHIR auth context to DICOMweb authentication"""
auth_type = self._detect_auth_type(dicom_endpoint)
mapper = self.mapping_rules.get(auth_type)
if mapper:
return mapper(fhir_auth_context, dicom_endpoint)
else:
raise ValueError(f"Unsupported auth type: {auth_type}")
def _map_smart_to_dicom(self, fhir_context, endpoint):
"""Map SMART on FHIR token to DICOM bearer token"""
# Extract access token from FHIR context
access_token = fhir_context.access_token
# Map to DICOMweb authorization header
return {
'Authorization': f'Bearer {access_token}',
'Accept': 'application/dicom+json'
}
def _map_basic_auth(self, fhir_context, endpoint):
"""Map to basic authentication"""
# Extract credentials from endpoint configuration
username = endpoint.extension.get('dicom-username')
password = endpoint.extension.get('dicom-password')
if username and password:
credentials = base64.b64encode(f"{username}:{password}".encode()).decode()
return {
'Authorization': f'Basic {credentials}'
}
Image Caching Strategy
Implements caching for DICOM images and metadata with LRU eviction, prefetching, and memory management for performance optimization.
class ImageCache:
def __init__(self, cache_backend, ttl_seconds=3600):
self.cache = cache_backend
self.ttl = ttl_seconds
async def get_cached_image(self, study_uid, series_uid, instance_uid, accept_type):
"""Retrieve image from cache if available"""
cache_key = self._generate_cache_key(study_uid, series_uid, instance_uid, accept_type)
cached_data = await self.cache.get(cache_key)
if cached_data:
return cached_data
return None
async def cache_image(self, study_uid, series_uid, instance_uid, accept_type, image_data):
"""Store image in cache"""
cache_key = self._generate_cache_key(study_uid, series_uid, instance_uid, accept_type)
await self.cache.set(cache_key, image_data, ttl=self.ttl)
def _generate_cache_key(self, study_uid, series_uid, instance_uid, accept_type):
"""Generate unique cache key for image"""
return f"img:{study_uid}:{series_uid}:{instance_uid}:{accept_type}"
EMR Integration
JavaScript client for EMR integration with the imaging bridge, handling viewer launching and image display coordination.
// Example EMR-side integration
class EMRImagingComponent {
constructor(imagingBridgeUrl) {
this.bridgeUrl = imagingBridgeUrl;
}
async loadPatientImages(patientId, authToken) {
// Get available studies
const studies = await this.fetchStudies(patientId, authToken);
// Render study list
this.renderStudyList(studies);
// Load thumbnails for first study
if (studies.length > 0) {
await this.loadStudyThumbnails(studies[0], authToken);
}
}
async fetchStudies(patientId, authToken) {
const response = await fetch(`${this.bridgeUrl}/patients/${patientId}/studies`, {
headers: {
'Authorization': `Bearer ${authToken}`,
'Accept': 'application/fhir+json'
}
});
return await response.json();
}
async loadImage(studyUid, seriesUid, instanceUid, authToken) {
const response = await fetch(
`${this.bridgeUrl}/studies/${studyUid}/series/${seriesUid}/instances/${instanceUid}`,
{
headers: {
'Authorization': `Bearer ${authToken}`,
'Accept': 'image/jpeg'
}
}
);
const blob = await response.blob();
return URL.createObjectURL(blob);
}
}
Related Patterns
- IID Facade: IID Facade provides simple viewer launching using Imaging Bridge metadata
- Event Observer: Event Observer synchronizes imaging context across clinical applications
- Security Strategy: Security Strategy maps FHIR authentication to DICOM access control
- Audit & Provenance Chain: Image access and viewing events are logged for compliance
Benefits
- Unified Interface: Single API for both FHIR metadata and DICOM images
- Standards Compliance: Uses established FHIR and DICOMweb standards
- Security Integration: Maps authentication between FHIR and DICOM contexts
- Performance Optimization: Caching and efficient image retrieval
- Workflow Integration: Seamless EMR-PACS integration
Trade-offs
- Complexity: Additional layer between clinical systems and imaging
- Performance: Network overhead for metadata enrichment
- Security Surface: More complex authentication mapping
- Dependency: Requires both FHIR and DICOMweb infrastructure
References
- FHIR ImagingStudy - FHIR imaging study resource
- DICOMweb - DICOM web services overview
- WADO-RS - Web Access to DICOM Objects
- QIDO-RS - Query for DICOM Objects
- STOW-RS - Store Over the Web
Thumbnail Generation
Consider generating and caching thumbnail images for faster study browsing. Use WADO-RS with appropriate accept headers for optimized image formats.
Large Image Handling
Implement appropriate timeouts and streaming for large image datasets. Consider progressive loading strategies for multi-frame and high-resolution studies.