import requests
import pydicom
from pathlib import Path
from urllib3.filepost import encode_multipart_formdata, choose_boundary
from azure.identity import DefaultAzureCredentialAccessing the DICOM service from the TRE
1 Accessing the DICOM service from the TRE
Note: DICOM data accessed through this service has been anonymised to some degree and will not mirror data from the original source.
- PII has been removed
- Dates have been moved
1.0.1 Set api URL and version
service_url="https://hdspixldflowehrprod-dicom-pixld-flowehr-prod.dicom.azurehealthcareapis.com"
version="v1"
base_url = f"{service_url}/{version}"
print(service_url)1.0.2 Authenticate to Azure
Enter the provided code in a browser outside of the TRE VM
!az login --use-device-codeEnsure the correct subscription is set as the ‘default’ subscription. Please select the subscription name you would like to use for futher authentication against the DICOM service from the list of subscriptions returned by the previous cell.
Replace your-subscription-name with the actual subscription name in the below cell and run the cell.
!az account set --subscription "your-subscription-name"1.0.3 Generate bearer token via DefaultAzureCredential
from azure.identity import DefaultAzureCredential, AzureCliCredential
credential = DefaultAzureCredential()
token = credential.credentials[3].get_token('https://dicom.healthcareapis.azure.com')
bearer_token = f'Bearer {token.token}'1.0.4 Optional - Alternative token generation with AzureCliCredential
Generates an equivalent token to the above cell, may be used if problems with DefaultAzureCredential are encountered.
credential = AzureCliCredential()
bearer_token = f"Bearer {credential.get_token('https://dicom.healthcareapis.azure.com').token}"1.1 Create a requests session
client = requests.session()1.2 Verify authentication has performed correctly
headers = {"Authorization":bearer_token}
url= f'{base_url}/changefeed'
response = client.get(url,headers=headers)
if (response.status_code != 200):
print('Error! Likely not authenticated!')
print(response.status_code)1.3 Querying the DICOM Service
1.3.1 Search for studies
url = f"{base_url}/studies"
headers = {'Accept':'application/dicom+json', "Authorization":bearer_token}
response_query = client.get(url, headers=headers)
print(f"{response_query.status_code=}, {response_query.content=}")Extract study IDs from response content - returned as bytes
StudyInstanceUID corresponds to 0020000D - see the DICOM documentation for details
import json
r = json.loads(response_query.content.decode())
study_uids = [study["0020000D"]["Value"][0] for study in r]1.3.2 Search by study UID
study_uid = study_uids[0] # as an example, use the previous query
url = f"{base_url}/studies"
headers = {'Accept':'application/dicom+json', "Authorization":bearer_token}
params = {'StudyInstanceUID':study_uid}
response_query = client.get(url, headers=headers, params=params)
print(f"{response_query.status_code=}, {response_query.content=}")Return series UIDs within a single study
url = f'{base_url}/studies/{study_uids[0]}/series'
headers = {'Accept':'application/dicom+json', "Authorization":bearer_token}
response = client.get(url, headers=headers)
print(f"{response.status_code=}, {response.content=}")Extract series IDs from response content - returned as bytes
SeriesInstanceUID corresponds to 0020000E - see the DICOM documentation for details
r = json.loads(response.content.decode())
series_uids = [study["0020000E"]["Value"][0] for study in r]Search within study by series UID
series_uid = series_uids[0]
url = f'{base_url}/studies/{study_uid}/series'
headers = {'Accept':'application/dicom+json', "Authorization":bearer_token}
params = {'SeriesInstanceUID':series_uid}
response = client.get(url, headers=headers, params=params) #, verify=False)
print(f"{response.status_code=}, {response.content=}")Search all studies by series UID
url = f'{base_url}/series'
headers = {'Accept': 'application/dicom+json', "Authorization":bearer_token}
params = {'SeriesInstanceUID': series_uid}
response = client.get(url, headers=headers, params=params)
print(f"{response.status_code=}, {response.content=}")1.4 Retrieve all instances within a study
1.4.1 For a single study UID
url = f'{base_url}/studies/{study_uid}'
headers = {'Accept':'multipart/related; type="application/dicom"; transfer-syntax=*', "Authorization":bearer_token}
response = client.get(url, headers=headers)Instances are retrieved as bytes - to return useful output, we’ll loop through returned items and convert to files that can be read by pydicom
import requests_toolbelt as tb
from io import BytesIO
mpd = tb.MultipartDecoder.from_response(response)
retrieved_dcm_files = []
for part in mpd.parts:
# headers returned as binary
print(part.headers[b'content-type'])
dcm = pydicom.dcmread(BytesIO(part.content))
print(dcm.PatientName)
print(dcm.SOPInstanceUID)
retrieved_dcm_files.append(dcm)print(retrieved_dcm_files[0].file_meta)1.4.2 For multiple study UIDs
response_array = []
for study_uid in study_uids:
url = f'{base_url}/studies/{study_uid}'
headers = {'Accept':'multipart/related; type="application/dicom"; transfer-syntax=*', "Authorization":bearer_token}
response = client.get(url, headers=headers)
response_array.append(response)Parse returned items and output a list of lists, with a list of instances per study
import requests_toolbelt as tb
from io import BytesIO
retrieved_dcm_files_multistudy = []
for r in response_array:
mpd = tb.MultipartDecoder.from_response(r)
retrieved_study_dcm_files = []
for part in mpd.parts:
dcm = pydicom.dcmread(BytesIO(part.content))
retrieved_study_dcm_files.append(dcm)
retrieved_dcm_files_multistudy.append(retrieved_study_dcm_files)print(retrieved_dcm_files_multistudy[0][0].file_meta)1.5 View an image with matplotlib
Instance images can be viewed by plotting the pixel array with matplotlib (or a similar library)
import matplotlib.pyplot as plt
plt.imshow(retrieved_dcm_files[0].pixel_array, cmap=plt.cm.bone)1.6 Retrieve a single instance within a study
Extract instance IDs from response content - returned as bytes
SOPInstanceUID corresponds to 00080018 - see the DICOM documentation for details
study_uid, series_uid = study_uids[0], series_uids[0]
url = f'{base_url}/studies/{study_uid}/series/{series_uid}/instances'
headers = {'Accept': 'application/dicom+json', "Authorization":bearer_token}
response = client.get(url, headers=headers)
r = json.loads(response.content.decode())
instance_uids = [series["00080018"]["Value"][0] for series in r]instance_uid = instance_uids[0]
url = f'{base_url}/studies/{study_uid}/series/{series_uid}/instances/{instance_uid}'
headers = {'Accept':'application/dicom; transfer-syntax=*', "Authorization":bearer_token}
response = client.get(url, headers=headers)Again, the single instance is returned as bytes, which we can pass to pydicom with
dicom_file = pydicom.dcmread(BytesIO(response.content))
print(dicom_file.PatientName)
print(dicom_file.SOPInstanceUID)
print(dicom_file.file_meta)plt.imshow(dicom_file.pixel_array, cmap=plt.cm.bone)