Použití standardních rozhraní API DICOMweb s Pythonem
Tento článek ukazuje, jak pracovat se službou DICOMweb pomocí Pythonu a ukázkové .dcm souborů DICOM®.
Použijte tyto ukázkové soubory:
- blue-circle.dcm
- dicom-metadata.csv
- green-square.dcm
- red-triangle.dcm
Název souboru, studyUID, seriesUID a instanceUID ukázkových souborů DICOM jsou:
Soubor | StudyUID | SeriesUID | INSTANCEUID |
---|---|---|---|
green-square.dcm | 1.2.826.0.1.3680043.8.498.13230779778012324449356534479549187420 | 1.2.826.0.1.3680043.8.498.45787841905473114233124723359129632652 | 1.2.826.0.1.3680043.8.498.12714725698140337137334606354172323212 |
red-triangle.dcm | 1.2.826.0.1.3680043.8.498.13230779778012324449356534479549187420 | 1.2.826.0.1.3680043.8.498.45787841905473114233124723359129632652 | 1.2.826.0.1.3680043.8.498.47359123102728459884412887463296905395 |
blue-circle.dcm | 1.2.826.0.1.3680043.8.498.13230779778012324449356534479549187420 | 1.2.826.0.1.3680043.8.498.77033797676425927098669402985243398207 | 1.2.826.0.1.3680043.8.498.13273713909719068980354078852867170114 |
Poznámka:
Každý z těchto souborů představuje jednu instanci a je součástí stejné studie. Také zelený čtverec a červený trojúhelník jsou součástí stejné řady, zatímco modrý kruh je v samostatné řadě.
Požadavky
Pokud chcete používat rozhraní API DICOMweb Standard, musíte mít nasazenou instanci služby DICOM. Další informace najdete v tématu Nasazení služby DICOM pomocí webu Azure Portal.
Po nasazení instance služby DICOM načtěte adresu URL vaší služby App Service:
- Přihlaste se k portálu Azure.
- Vyhledejte nedávné prostředky a vyberte instanci služby DICOM.
- Zkopírujte adresu URL služby služby DICOM.
- Pokud token nemáte, přečtěte si téma Získání přístupového tokenu pro službu DICOM pomocí Azure CLI.
Pro tento kód získáte přístup ke službě Azure Ve verzi Public Preview. Je důležité, abyste nenahráli žádné soukromé zdravotní údaje (PHI).
Práce se službou DICOM
DiCOMweb Standard velmi využívá multipart/related
požadavky HTTP v kombinaci s hlavičkami přijetí specifické pro DICOM. Vývojáři obeznámení s dalšími rozhraními API založenými na REST často hledají práci se standardem DICOMweb awkward. Po zprovoznění je ale snadné ho používat. Stačí trochu povědomosti, abyste mohli začít.
Import knihoven Pythonu
Nejprve naimportujte potřebné knihovny Pythonu.
Tento příklad implementujeme pomocí synchronní requests
knihovny. Pro asynchronní podporu zvažte použití httpx
nebo jinou asynchronní knihovnu. Kromě toho importujeme dvě podpůrné funkce z urllib3
podpory práce s multipart/related
požadavky.
Kromě toho importujeme DefaultAzureCredential
, abychom se přihlásili k Azure a získali token.
import requests
import pydicom
from pathlib import Path
from urllib3.filepost import encode_multipart_formdata, choose_boundary
from azure.identity import DefaultAzureCredential
Konfigurace uživatelem definovaných proměnných
Nahraďte všechny hodnoty proměnných zabalené v { } vlastními hodnotami. Kromě toho ověřte správnost všech vytvořených proměnných. Například base_url
se sestaví pomocí adresy URL služby a pak se připojí k používané verzi rozhraní REST API. Adresa URL služby vaší služby DICOM je: https://<workspacename-dicomservicename>.dicom.azurehealthcareapis.com
. Pomocí webu Azure Portal můžete přejít do služby DICOM a získat adresu URL služby. Další informace o správě verzí najdete také v dokumentaci k rozhraní API pro službu DICOM. Pokud používáte vlastní adresu URL, musíte tuto hodnotu přepsat vlastními.
dicom_service_name = "{server-name}"
path_to_dicoms_dir = "{path to the folder that includes green-square.dcm and other dcm files}"
base_url = f"{Service URL}/v{version}"
study_uid = "1.2.826.0.1.3680043.8.498.13230779778012324449356534479549187420"; #StudyInstanceUID for all 3 examples
series_uid = "1.2.826.0.1.3680043.8.498.45787841905473114233124723359129632652"; #SeriesInstanceUID for green-square and red-triangle
instance_uid = "1.2.826.0.1.3680043.8.498.47359123102728459884412887463296905395"; #SOPInstanceUID for red-triangle
Ověření v Azure a získání tokenu
DefaultAzureCredential
umožňuje nám používat různé způsoby, jak získat tokeny pro přihlášení ke službě. V tomto příkladu AzureCliCredential
použijte k získání tokenu pro přihlášení ke službě. Existují další poskytovatelé přihlašovacích údajů, jako ManagedIdentityCredential
je a EnvironmentCredential
které můžete použít. Pokud chcete použít AzureCliCredential, musíte se před spuštěním tohoto kódu přihlásit k Azure z rozhraní příkazového řádku. Další informace najdete v tématu Získání přístupového tokenu pro službu DICOM pomocí Azure CLI. Případně zkopírujte a vložte token načtený při přihlašování z rozhraní příkazového řádku.
Poznámka:
DefaultAzureCredential
vrátí několik různých objektů Credential. Odkazujeme na AzureCliCredential
5. položku ve vrácené kolekci. Nemusí to být vždy případ. Pokud ne, odkomentujte čáru print(credential.credential)
. Zobrazí se seznam všech položek. Vyhledejte správný index a připomínáme, že Python používá indexování založené na nule.
Poznámka:
Pokud jste se k Azure nepřihlásili pomocí rozhraní příkazového řádku, dojde k selhání. Aby to fungovalo, musíte být přihlášeni k Azure z rozhraní příkazového řádku.
from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()
#print(credential.credentials) # this can be used to find the index of the AzureCliCredential
token = credential.credentials[4].get_token('https://dicom.healthcareapis.azure.com')
bearer_token = f'Bearer {token.token}'
Vytvoření podpůrných metod pro podporu multipart\related
Knihovny Requests
(a většina knihoven Pythonu) nefungují multipart\related
způsobem, který podporuje DICOMweb. Kvůli těmto knihovnám musíme přidat několik metod pro podporu práce se soubory DICOM.
encode_multipart_related
přebírá sadu polí (v případě DICOM jsou tyto knihovny obecně součástí 10 souborů dam) a volitelnou uživatelsky definovanou hranici. Vrátí celé tělo spolu s content_type, které lze použít.
def encode_multipart_related(fields, boundary=None):
if boundary is None:
boundary = choose_boundary()
body, _ = encode_multipart_formdata(fields, boundary)
content_type = str('multipart/related; boundary=%s' % boundary)
return body, content_type
Vytvoření requests
relace
requests
Vytvoří relaci s názvemclient
, která se používá ke komunikaci se službou DICOM.
client = requests.session()
Ověřte, že je správně nakonfigurované ověřování.
Volání koncového bodu rozhraní API kanálu změn, který vrátí hodnotu 200, pokud je ověřování úspěšné.
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!')
Nahrání instancí DICOM (STOW)
Následující příklady zvýrazňují zachování souborů DICOM.
Ukládání instancí pomocí multipart/related
Tento příklad ukazuje, jak nahrát jeden soubor DICOM a používá Python k předběžnému načtení souboru DICOM do paměti jako bajty. Pokud je pole souborů předáno parametru encode_multipart_related
pole, lze do jednoho POST nahrát více souborů. Někdy se používá k nahrání několika instancí do celé řady nebo studie.
Podrobnosti:
Cesta:.. /studie
Metoda: POST
Hlavičky:
- Přijmout: application/dicom+json
- Typ obsahu: vícedílné/související; type="application/dicom"
- Autorizace: nosný $token"
Text:
- Content-Type: application/dicom for each file upload, separated by a boundary value
Některé programovací jazyky a nástroje se chovají odlišně. Například některé vyžadují, abyste definovali vlastní hranici. U těchto jazyků a nástrojů možná budete muset použít mírně upravenou hlavičku Content-Type. Tyto jazyky a nástroje lze úspěšně použít.
- Typ obsahu: vícedílné/související; type="application/dicom"; boundary=ABCD1234
- Typ obsahu: vícedílné/související; boundary=ABCD1234
- Typ obsahu: vícedílné/související
#upload blue-circle.dcm
filepath = Path(path_to_dicoms_dir).joinpath('blue-circle.dcm')
# Read through file and load bytes into memory
with open(filepath,'rb') as reader:
rawfile = reader.read()
files = {'file': ('dicomfile', rawfile, 'application/dicom')}
#encode as multipart_related
body, content_type = encode_multipart_related(fields = files)
headers = {'Accept':'application/dicom+json', "Content-Type":content_type, "Authorization":bearer_token}
url = f'{base_url}/studies'
response = client.post(url, body, headers=headers, verify=False)
Ukládání instancí pro konkrétní studii
Tento příklad ukazuje, jak nahrát více souborů DICOM do zadané studie. Používá Python k předběžnému načtení souboru DICOM do paměti jako bajtů.
Pokud je pole souborů předáno parametru encode_multipart_related
pole, lze do jednoho POST nahrát více souborů. Někdy se používá k nahrání kompletní řady nebo studie.
Podrobnosti:
- Cesta:.. /studies/{study}
- Metoda: POST
- Hlavičky:
- Přijmout: application/dicom+json
- Typ obsahu: vícedílné/související; type="application/dicom"
- Autorizace: nosný $token"
- Tělo:
- Content-Type: application/dicom for each file upload, separated by a boundary value
filepath_red = Path(path_to_dicoms_dir).joinpath('red-triangle.dcm')
filepath_green = Path(path_to_dicoms_dir).joinpath('green-square.dcm')
# Open up and read through file and load bytes into memory
with open(filepath_red,'rb') as reader:
rawfile_red = reader.read()
with open(filepath_green,'rb') as reader:
rawfile_green = reader.read()
files = {'file_red': ('dicomfile', rawfile_red, 'application/dicom'),
'file_green': ('dicomfile', rawfile_green, 'application/dicom')}
#encode as multipart_related
body, content_type = encode_multipart_related(fields = files)
headers = {'Accept':'application/dicom+json', "Content-Type":content_type, "Authorization":bearer_token}
url = f'{base_url}/studies'
response = client.post(url, body, headers=headers, verify=False)
Ukládání jedné instance (nestandardní)
Následující příklad kódu ukazuje, jak nahrát jeden soubor DICOM. Jedná se o nestandardní koncový bod rozhraní API, který zjednodušuje nahrání jednoho souboru jako binární bajty odeslané v těle požadavku.
Podrobnosti:
- Cesta:.. /studie
- Metoda: POST
- Hlavičky:
- Přijmout: application/dicom+json
- Typ obsahu: application/dicom
- Autorizace: nosný $token"
- Tělo:
- Obsahuje jeden soubor DICOM jako binární bajty.
#upload blue-circle.dcm
filepath = Path(path_to_dicoms_dir).joinpath('blue-circle.dcm')
# Open up and read through file and load bytes into memory
with open(filepath,'rb') as reader:
body = reader.read()
headers = {'Accept':'application/dicom+json', 'Content-Type':'application/dicom', "Authorization":bearer_token}
url = f'{base_url}/studies'
response = client.post(url, body, headers=headers, verify=False)
response # response should be a 409 Conflict if the file was already uploaded in the above request
Načtení instancí DICOM (WADO)
Následující příklady zvýrazňují načítání instancí DICOM.
Načtení všech instancí ve studii
Tento příklad načte všechny instance v rámci jedné studie.
Podrobnosti:
- Cesta:.. /studies/{study}
- Metoda: GET
- Hlavičky:
- Přijmout: vícedílné/související; type="application/dicom"; transfer-syntax=*
- Autorizace: nosný $token"
Všechny tři soubory DCM nahrané dříve jsou součástí stejné studie, takže odpověď by měla vrátit všechny tři instance. Ověřte, že odpověď obsahuje stavový kód OK a že se vrátí všechny tři instance.
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) #, verify=False)
Použití načtených instancí
Instance se načtou jako binární bajty. Vrácené položky můžete procházet a převést bajty na soubor, který pydicom
se dá číst následujícím způsobem.
import requests_toolbelt as tb
from io import BytesIO
mpd = tb.MultipartDecoder.from_response(response)
for part in mpd.parts:
# Note that the headers are returned as binary!
print(part.headers[b'content-type'])
# You can convert the binary body (of each part) into a pydicom DataSet
# And get direct access to the various underlying fields
dcm = pydicom.dcmread(BytesIO(part.content))
print(dcm.PatientName)
print(dcm.SOPInstanceUID)
Načtení metadat všech instancí ve studii
Tento požadavek načte metadata pro všechny instance v rámci jedné studie.
Podrobnosti:
- Cesta:.. /studies/{study}/metadata
- Metoda: GET
- Hlavičky:
- Přijmout: application/dicom+json
- Autorizace: nosný $token"
Všechny tři .dcm
soubory, které jsme nahráli dříve, jsou součástí stejné studie, takže odpověď by měla vrátit metadata pro všechny tři instance. Ověřte, že odpověď obsahuje stavový kód OK a že se vrátí všechna metadata.
url = f'{base_url}/studies/{study_uid}/metadata'
headers = {'Accept':'application/dicom+json', "Authorization":bearer_token}
response = client.get(url, headers=headers) #, verify=False)
Načtení všech instancí v rámci řady
Tento požadavek načte všechny instance v rámci jedné řady.
Podrobnosti:
- Cesta:.. /studies/{study}/series/{series}
- Metoda: GET
- Hlavičky:
- Přijmout: vícedílné/související; type="application/dicom"; transfer-syntax=*
- Autorizace: nosný $token"
Tato řada má dvě instance (zelený čtverec a červený trojúhelník), takže odpověď by měla vrátit obě instance. Ověřte, že odpověď obsahuje stavový kód OK a že se vrátí obě instance.
url = f'{base_url}/studies/{study_uid}/series/{series_uid}'
headers = {'Accept':'multipart/related; type="application/dicom"; transfer-syntax=*', "Authorization":bearer_token}
response = client.get(url, headers=headers) #, verify=False)
Načtení metadat všech instancí v řadě
Tento požadavek načte metadata pro všechny instance v rámci jedné řady.
Podrobnosti:
- Cesta:.. /studies/{study}/series/{series}/metadata
- Metoda: GET
- Hlavičky:
- Přijmout: application/dicom+json
- Autorizace: nosný $token"
Tato řada má dvě instance (zelený čtverec a červený trojúhelník), takže odpověď by se měla vrátit pro obě instance. Ověřte, že odpověď obsahuje stavový kód OK a že se vrátí metadata obou instancí.
url = f'{base_url}/studies/{study_uid}/series/{series_uid}/metadata'
headers = {'Accept':'application/dicom+json', "Authorization":bearer_token}
response = client.get(url, headers=headers) #, verify=False)
Načtení jedné instance v rámci řady studie
Tento požadavek načte jednu instanci.
Podrobnosti:
- Cesta:.. /studies/{study}/series{series}/instance/{instance}
- Metoda: GET
- Hlavičky:
- Přijmout: application/dicom; transfer-syntax=*
- Autorizace: nosný $token"
Tento příklad kódu by měl vrátit pouze červený trojúhelník instance. Ověřte, že odpověď obsahuje stavový kód OK a že je vrácena instance.
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) #, verify=False)
Načtení metadat jedné instance v rámci řady studií
Tento požadavek načte metadata pro jednu instanci v rámci jedné studie a řady.
Podrobnosti:
- Cesta:.. /studies/{study}/series/{series}/instance/{instance}/metadata
- Metoda: GET
- Hlavičky:
- Přijmout: application/dicom+json
- Autorizace: nosný $token"
Tento příklad kódu by měl vrátit pouze metadata pro instanci červený trojúhelník. Ověřte, že odpověď obsahuje stavový kód OK a že se vrátí metadata.
url = f'{base_url}/studies/{study_uid}/series/{series_uid}/instances/{instance_uid}/metadata'
headers = {'Accept':'application/dicom+json', "Authorization":bearer_token}
response = client.get(url, headers=headers) #, verify=False)
Načtení jednoho nebo více rámců z jedné instance
Tento požadavek načte jeden nebo více rámců z jedné instance.
Podrobnosti:
- Cesta:.. /studies/{study}/series{series}/instance/{instance}/frames/1,2,3
- Metoda: GET
- Hlavičky:
- Autorizace: nosný $token"
Accept: multipart/related; type="application/octet-stream"; transfer-syntax=1.2.840.10008.1.2.1
(Výchozí) neboAccept: multipart/related; type="application/octet-stream"; transfer-syntax=*
neboAccept: multipart/related; type="application/octet-stream";
Tento příklad kódu by měl vrátit jediný rámec z červeného trojúhelníku. Ověřte, že odpověď obsahuje stavový kód OK a že se vrátí rámec.
url = f'{base_url}/studies/{study_uid}/series/{series_uid}/instances/{instance_uid}/frames/1'
headers = {'Accept':'multipart/related; type="application/octet-stream"; transfer-syntax=*', "Authorization":bearer_token}
response = client.get(url, headers=headers) #, verify=False)
Dotaz DICOM (QIDO)
V následujících příkladech vyhledáme položky pomocí jejich jedinečných identifikátorů. Můžete také vyhledat další atributy, například PatientName.
Informace o podporovanýchatributch
Hledání studií
Tento požadavek vyhledá jednu nebo více studií podle atributů DICOM.
Podrobnosti:
- Cesta:.. /studie? StudyInstanceUID={study}
- Metoda: GET
- Hlavičky:
- Přijmout: application/dicom+json
- Autorizace: nosný $token"
Ověřte, že odpověď obsahuje jednu studii a že kód odpovědi je v pořádku.
url = f'{base_url}/studies'
headers = {'Accept':'application/dicom+json', "Authorization":bearer_token}
params = {'StudyInstanceUID':study_uid}
response = client.get(url, headers=headers, params=params) #, verify=False)
Hledání řad
Tento požadavek vyhledá jednu nebo více řad podle atributů DICOM.
Podrobnosti:
- Cesta:.. /řada? SeriesInstanceUID={series}
- Metoda: GET
- Hlavičky:
- Přijmout: application/dicom+json
- Autorizace: nosný $token"
Ověřte, že odpověď obsahuje jednu řadu a že kód odpovědi je v pořádku.
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) #, verify=False)
Hledání řad v rámci studie
Tento požadavek vyhledá jednu nebo více řad v rámci jedné studie podle atributů DICOM.
Podrobnosti:
- Cesta:.. /studies/{study}/series? SeriesInstanceUID={series}
- Metoda: GET
- Hlavičky:
- Přijmout: application/dicom+json
- Autorizace: nosný $token"
Ověřte, že odpověď obsahuje jednu řadu a že kód odpovědi je v pořádku.
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)
Hledání instancí
Tento požadavek vyhledá jednu nebo více instancí podle atributů DICOM.
Podrobnosti:
- Cesta:.. /instance? SOPInstanceUID={instance}
- Metoda: GET
- Hlavičky:
- Přijmout: application/dicom+json
- Autorizace: nosný $token"
Ověřte, že odpověď obsahuje jednu instanci a že kód odpovědi je v pořádku.
url = f'{base_url}/instances'
headers = {'Accept':'application/dicom+json', "Authorization":bearer_token}
params = {'SOPInstanceUID':instance_uid}
response = client.get(url, headers=headers, params=params) #, verify=False)
Hledání instancí ve studii
Tento požadavek vyhledá jednu nebo více instancí v rámci jedné studie podle atributů DICOM.
Podrobnosti:
- Cesta:.. /studies/{study}/instance? SOPInstanceUID={instance}
- Metoda: GET
- Hlavičky:
- Přijmout: application/dicom+json
- Autorizace: nosný $token"
Ověřte, že odpověď obsahuje jednu instanci a že kód odpovědi je v pořádku.
url = f'{base_url}/studies/{study_uid}/instances'
headers = {'Accept':'application/dicom+json', "Authorization":bearer_token}
params = {'SOPInstanceUID':instance_uid}
response = client.get(url, headers=headers, params=params) #, verify=False)
Hledání instancí ve studii a řadě
Tento požadavek vyhledá jednu nebo více instancí v rámci jedné studie a jedné řady podle atributů DICOM.
Podrobnosti:
- Cesta:.. /studies/{study}/series/{series}/instance? SOPInstanceUID={instance}
- Metoda: GET
- Hlavičky:
- Přijmout: application/dicom+json
- Autorizace: nosný $token"
Ověřte, že odpověď obsahuje jednu instanci a že kód odpovědi je v pořádku.
url = f'{base_url}/studies/{study_uid}/series/{series_uid}/instances'
headers = {'Accept':'application/dicom+json'}
params = {'SOPInstanceUID':instance_uid}
response = client.get(url, headers=headers, params=params) #, verify=False)
Odstranění DICOM
Poznámka:
Odstranění není součástí standardu DICOM, ale bylo přidáno pro usnadnění.
Po úspěšném odstranění se vrátí kód odpovědi 204. Kód odpovědi 404 se vrátí, pokud položky nikdy neexistovaly nebo jsou již odstraněny.
Odstranění konkrétní instance v rámci studie a řady
Tento požadavek odstraní jednu instanci v rámci jedné studie a jedné řady.
Podrobnosti:
- Cesta:.. /studies/{study}/series/{series}/instance/{instance}
- Metoda: DELETE
- Hlavičky:
- Autorizace: Nosný $token
Tento požadavek odstraní instanci červeného trojúhelníku ze serveru. V případě úspěchu neobsahuje stavový kód odpovědi žádný obsah.
headers = {"Authorization":bearer_token}
url = f'{base_url}/studies/{study_uid}/series/{series_uid}/instances/{instance_uid}'
response = client.delete(url, headers=headers)
Odstranění konkrétní řady v rámci studie
Tento požadavek odstraní jednu řadu (a všechny podřízené instance) v rámci jedné studie.
Podrobnosti:
- Cesta:.. /studies/{study}/series/{series}
- Metoda: DELETE
- Hlavičky:
- Autorizace: Nosný $token
Tento příklad kódu odstraní ze serveru instanci zeleného čtverce (je to jediný prvek, který zbývá v řadě). V případě úspěchu stavový kód odpovědi neodstraní obsah.
headers = {"Authorization":bearer_token}
url = f'{base_url}/studies/{study_uid}/series/{series_uid}'
response = client.delete(url, headers=headers)
Odstranění konkrétní studie
Tento požadavek odstraní jednu studii (a všechny podřízené řady a instance).
Podrobnosti:
- Cesta:.. /studies/{study}
- Metoda: DELETE
- Hlavičky:
- Autorizace: Nosný $token
headers = {"Authorization":bearer_token}
url = f'{base_url}/studies/{study_uid}'
response = client.delete(url, headers=headers)
Poznámka:
DICOM® je registrovaná ochranná známka asociace National Electrical Manufacturers Association pro publikace standardů týkající se digitální komunikace s lékařskými informacemi.