Lompati ke konten utama
Browser ini sudah tidak didukung.
Mutakhirkan ke Microsoft Edge untuk memanfaatkan fitur, pembaruan keamanan, dan dukungan teknis terkini.
Menggunakan API Standar DICOMWeb™ dengan Python
- Artikel
- 09/27/2022
- 11 menit untuk membaca
Dalam
artikel ini
Tutorial ini menggunakan Python untuk menunjukkan bekerja dengan Layanan DICOM.
Dalam tutorial, kita akan menggunakan contoh file .dcm DICOM berikut.
- blue-circle.dcm
- dicom-metadata.csv
- green-square.dcm
- red-triangle.dcm
Nama file, studyUID, seriesUID, dan instanceUID dari sampel file DICOM adalah sebagai
berikut:
File | 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
|
Catatan
Masing-masing file ini mewakili satu instans dan merupakan bagian dari studi yang sama. Selain itu, segitiga hijau dan merah adalah bagian dari seri yang sama, sementara lingkaran biru berada dalam seri terpisah.
Prasyarat
Untuk menggunakan DICOMWeb™ Standard API, Anda harus memiliki instans layanan DICOM yang disebarkan. Jika Anda belum menyebarkan layanan DICOM, lihat
Menyebarkan layanan DICOM menggunakan portal Microsoft Azure.
Setelah Anda menyebarkan instans layanan DICOM, ambil URL untuk layanan Aplikasi Anda:
- Masuk ke Portal Microsoft Azure.
- Cari Sumber daya terbaru dan pilih instans
layanan DICOM Anda.
- Salin URL Layanan layanan DICOM Anda.
- Jika Anda belum mendapatkan token, lihat Mendapatkan token akses untuk layanan DICOM menggunakan Azure CLI.
Untuk kode ini, kami akan mengakses layanan Pratinjau Umum Azure. Penting bahwa Anda tidak mengunggah informasi kesehatan privat
(PHI).
DICOMweb™ Standard banyak menggunakan multipart/related
permintaan HTTP yang dikombinasikan dengan header terima khusus DICOM. Pengembang yang akrab dengan API berbasis REST lainnya sering menemukan bekerja dengan standar DICOMweb™ canggung. Namun, setelah Anda siap dan berjalan, mudah digunakan. Hanya perlu sedikit keakraban untuk memulai.
Mengimpor pustaka Python yang sesuai
Pertama, impor pustaka Python yang diperlukan.
Kami
telah memilih untuk menerapkan contoh ini menggunakan pustaka sinkron requests
. Untuk dukungan asinkron, pertimbangkan untuk menggunakan httpx
atau pustaka asinkron lainnya. Selain itu, kami mengimpor dua fungsi pendukung dari urllib3
untuk mendukung bekerja dengan multipart/related
permintaan.
Selain itu, kami mengimpor DefaultAzureCredential
untuk masuk ke Azure dan mendapatkan token.
import requests
import pydicom
from pathlib import Path
from urllib3.filepost import encode_multipart_formdata, choose_boundary
from azure.identity import DefaultAzureCredential
Mengonfigurasi variabel yang ditentukan pengguna
untuk digunakan di seluruh
Ganti semua nilai variabel yang dibungkus dalam { } dengan nilai Anda sendiri. Selain itu, validasi bahwa variabel yang dibangun sudah benar. Misalnya, base_url
dibangun menggunakan URL Layanan lalu ditambahkan dengan versi REST API yang digunakan. URL Layanan layanan DICOM Anda adalah: https://.dicom.azurehealthcareapis.com
. Anda dapat menggunakan portal Microsoft Azure untuk menavigasi ke layanan DICOM dan mendapatkan URL Layanan Anda. Anda juga dapat mengunjungi
Dokumentasi layanan PENERAPAN Versi API untuk DICOM untuk informasi selengkapnya tentang penerapan versi. Jika Anda menggunakan URL kustom, Anda harus mengambil alih nilai tersebut dengan url Anda sendiri.
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
Mengautentikasi ke Azure dan mendapatkan token
DefaultAzureCredential
memungkinkan kami untuk
mendapatkan berbagai cara untuk mendapatkan token untuk masuk ke layanan. Kami akan menggunakan AzureCliCredential
untuk mendapatkan token untuk masuk ke layanan. Ada penyedia kredensial lain seperti ManagedIdentityCredential
dan EnvironmentCredential
yang juga dimungkinkan untuk digunakan. Untuk menggunakan AzureCliCredential, Anda harus masuk ke Azure dari CLI sebelum menjalankan kode ini. (Untuk informasi selengkapnya, lihat
Mendapatkan token akses untuk layanan DICOM menggunakan Azure CLI.) Atau, Anda cukup menyalin dan menempelkan token yang diambil saat masuk dari CLI.
Catatan
DefaultAzureCredential
mengembalikan beberapa objek Kredensial yang berbeda. Kami mereferensikan AzureCliCredential
sebagai item ke-5 dalam koleksi yang dikembalikan. Ini mungkin tidak
konsisten. Jika demikian, batalkan print(credential.credential)
komentar pada baris. Ini akan mencantumkan semua item. Temukan indeks yang benar, ingat bahwa Python menggunakan pengindeksan berbasis nol.
Catatan
Jika Anda belum masuk ke Azure menggunakan CLI, ini akan gagal. Anda harus masuk ke Azure dari CLI agar ini berfungsi.
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}'
Requests
Pustaka (dan sebagian
besar pustaka Python) tidak berfungsi dengan multipart\related
cara yang mendukung DICOMweb™. Karena pustaka ini, kita harus menambahkan beberapa metode untuk mendukung bekerja dengan file DICOM.
encode_multipart_related
mengambil sekumpulan bidang (dalam kasus DICOM, pustaka ini umumnya adalah file bendungan Bagian 10) dan batas opsional yang ditentukan pengguna. Ini mengembalikan kedua tubuh penuh, bersama dengan content_type, yang dapat digunakan.
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
Membuat requests
sesi
Membuat sesi, yang requests
disebut client
, yang akan digunakan untuk berkomunikasi dengan layanan DICOM.
client = requests.session()
Pastikan autentikasi dikonfigurasi dengan benar
Panggil titik akhir API changefeed, yang akan mengembalikan 200 jika autentikasi berhasil.
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!')
Contoh berikut menyoroti file DICOM yang bertahan.
Contoh
ini menunjukkan cara mengunggah satu file DICOM, dan menggunakan sedikit Python untuk memuat file DICOM (sebagai byte) ke dalam memori. Dengan meneruskan array file ke parameter encode_multipart_related
bidang , beberapa file dapat diunggah dalam satu POST. Terkadang digunakan untuk mengunggah beberapa instans di dalam rangkaian atau studi lengkap.
Rincian:
Jalan:.. /studies
Metode: POST
Header:
- Terima: application/dicom+json
- Jenis
Konten: multipihak/terkait; type="application/dicom"
- Otorisasi: Pembawa $token"
Isi:
- Jenis Konten: application/dicom untuk setiap file yang diunggah, dipisahkan oleh nilai batas
Beberapa bahasa dan alat pemrograman ber perilaku berbeda. Misalnya, beberapa di antaranya mengharuskan Anda untuk menentukan batas Anda sendiri. Untuk itu, Anda mungkin perlu menggunakan header Content-Type yang sedikit dimodifikasi. Berikut ini telah
berhasil digunakan.
- Jenis Konten: multipihak/terkait; type="application/dicom"; boundary=ABCD1234
- Jenis Konten: multipihak/terkait; boundary=ABCD1234
- Jenis Konten: multipihak/terkait
#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)
Menyimpan instans untuk studi tertentu
Contoh ini menunjukkan cara mengunggah beberapa file DICOM ke dalam studi yang ditentukan. Ini menggunakan sedikit Python untuk memuat terlebih dahulu file DICOM (sebagai byte) ke
dalam memori.
Dengan meneruskan array file ke parameter encode_multipart_related
bidang , beberapa file dapat diunggah dalam satu POST. Terkadang digunakan untuk mengunggah rangkaian atau studi lengkap.
Rincian:
- Jalan:.. /studies/{study}
- Metode: POST
- Header:
- Terima: application/dicom+json
- Jenis Konten: multipihak/terkait; type="application/dicom"
- Otorisasi: Pembawa $token"
- Isi:
- Jenis Konten: application/dicom untuk
setiap file yang diunggah, dipisahkan oleh nilai batas
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)
Menyimpan instans tunggal (non-standar)
Contoh kode berikut menunjukkan cara mengunggah satu file DICOM. Ini adalah titik akhir API non-standar yang menyederhanakan pengunggahan satu file sebagai byte biner yang dikirim dalam isi permintaan
Rincian:
- Jalan:.. /studies
- Metode: POST
- Header:
- Terima:
application/dicom+json
- Jenis Konten: aplikasi/dicom
- Otorisasi: Pembawa $token"
- Isi:
- Berisi satu file DICOM sebagai byte biner.
#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
Contoh berikut menyoroti pengambilan instans DICOM.
Mengambil semua instans dalam studi
Contoh ini mengambil semua instans dalam satu studi.
Rincian:
- Jalan:.. /studies/{study}
- Metode: GET
- Header:
- Terima:
multipihak/terkait; type="application/dicom"; transfer-sintaks=*
- Otorisasi: Pembawa $token"
Ketiga file dcm yang kami unggah sebelumnya adalah bagian dari studi yang sama sehingga respons harus mengembalikan ketiga instans. Validasi bahwa respons memiliki kode status OK dan ketiga instans dikembalikan.
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)
Menggunakan instans yang diambil
Instans diambil sebagai byte biner. Anda dapat mengulangi item yang
dikembalikan dan mengonversi byte menjadi seperti file yang dapat dibaca oleh pydicom
.
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)
Permintaan ini mengambil metadata untuk semua instans dalam satu studi.
Rincian:
- Jalan:.. /studies/{study}/metadata
- Metode: GET
- Header:
- Terima: application/dicom+json
- Otorisasi: Pembawa $token"
Ketiga .dcm
file
yang kami unggah sebelumnya adalah bagian dari studi yang sama sehingga respons harus mengembalikan metadata untuk ketiga instans. Validasi bahwa respons memiliki kode status OK dan bahwa semua metadata dikembalikan.
url = f'{base_url}/studies/{study_uid}/metadata'
headers = {'Accept':'application/dicom+json', "Authorization":bearer_token}
response = client.get(url, headers=headers) #, verify=False)
Mengambil semua instans dalam seri
Permintaan ini mengambil semua instans dalam satu seri.
Rincian:
- Jalan:.. /studies/{study}/series/{series}
- Metode: GET
- Header:
- Terima:
multipihak/terkait; type="application/dicom"; transfer-sintaks=*
- Otorisasi: Pembawa $token"
Seri ini memiliki dua instans (persegi hijau dan segitiga merah), sehingga respons harus mengembalikan kedua instans. Validasi bahwa respons memiliki kode status OK dan kedua instans dikembalikan.
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)
Permintaan ini mengambil metadata untuk semua instans dalam satu
seri.
Rincian:
- Jalan:.. /studies/{study}/series/{series}/metadata
- Metode: GET
- Header:
- Terima: application/dicom+json
- Otorisasi: Pembawa $token"
Seri ini memiliki dua instans (persegi hijau dan segitiga merah), sehingga respons harus kembali untuk kedua instans. Validasi bahwa respons memiliki kode status OK dan kedua metadata instans dikembalikan.
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)
Mengambil satu instans dalam serangkaian studi
Permintaan ini mengambil satu instans.
Rincian:
- Jalan:.. /studies/{study}/series{series}/instances/{instance}
- Metode: GET
- Header:
- Terima: aplikasi/dicom; transfer-sintaks=*
- Otorisasi: Pembawa $token"
Contoh kode ini hanya boleh mengembalikan instans segitiga merah. Validasi bahwa respons memiliki kode
status OK dan instans dikembalikan.
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)
Permintaan ini mengambil metadata untuk satu instans dalam satu studi dan seri.
Rincian:
- Jalan:.. /studies/{study}/series/{series}/instances/{instance}/metadata
- Metode: GET
- Header:
- Terima: application/dicom+json
- Otorisasi: Pembawa $token"
Contoh kode ini hanya boleh mengembalikan metadata untuk instans segitiga merah. Validasi bahwa respons memiliki kode status OK dan metadata dikembalikan.
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)
Mengambil satu atau beberapa bingkai dari satu instans
Permintaan ini mengambil satu atau beberapa bingkai dari satu instans.
Rincian:
- Jalan:.. /studies/{study}/series{series}/instances/{instance}/frames/1,2,3
- Metode: GET
- Header:
- Otorisasi: Pembawa $token"
Accept: multipart/related; type="application/octet-stream"; transfer-syntax=1.2.840.10008.1.2.1
(Default) atauAccept: multipart/related; type="application/octet-stream"; transfer-syntax=*
atauAccept: multipart/related; type="application/octet-stream";
Contoh kode ini harus mengembalikan satu-satunya bingkai dari segitiga merah. Validasi bahwa respons memiliki kode status OK dan bingkai dikembalikan.
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)
Dalam contoh berikut, kami mencari item menggunakan pengidentifikasi uniknya. Anda juga dapat mencari atribut lain, seperti PatientName.
Lihat dokumen
Pernyataan Kesuaian DICOM untuk atribut DICOM yang didukung.
Mencari studi
Permintaan ini mencari satu atau beberapa studi oleh atribut DICOM.
Rincian:
- Jalan:.. Belajar? StudyInstanceUID={study}
- Metode: GET
- Header:
- Terima:
application/dicom+json
- Otorisasi: Pembawa $token"
Validasi bahwa respons mencakup satu studi dan bahwa kode respons OK.
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)
Cari seri
Permintaan ini mencari satu atau beberapa seri berdasarkan atribut DICOM.
Rincian:
- Jalan:.. Seri? SeriesInstanceUID={series}
- Metode: GET
- Header:
- Terima: application/dicom+json
- Otorisasi: Pembawa $token"
Validasi bahwa
respons mencakup satu seri dan bahwa kode respons OK.
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)
Mencari seri dalam studi
Permintaan ini mencari satu atau beberapa seri dalam satu studi oleh atribut DICOM.
Rincian:
- Jalan:.. /studies/{study}/series? SeriesInstanceUID={series}
- Metode: GET
- Header:
- Terima: application/dicom+json
- Otorisasi: Pembawa $token"
Validasi bahwa respons mencakup satu seri dan bahwa
kode respons OK.
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)
Mencari instans
Permintaan ini mencari satu atau beberapa instans berdasarkan atribut DICOM.
Rincian:
- Jalan:.. /instans? SOPInstanceUID={instance}
- Metode: GET
- Header:
- Terima: application/dicom+json
- Otorisasi: Pembawa $token"
Validasi bahwa respons mencakup satu instans dan bahwa kode respons OK.
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)
Mencari
instans dalam studi
Permintaan ini mencari satu atau beberapa instans dalam satu studi oleh atribut DICOM.
Rincian:
- Jalan:.. /studies/{study}/instances? SOPInstanceUID={instance}
- Metode: GET
- Header:
- Terima: application/dicom+json
- Otorisasi: Pembawa $token"
Validasi bahwa respons mencakup satu instans dan bahwa kode respons OK.
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)
Mencari instans
dalam studi dan seri
Permintaan ini mencari satu atau beberapa instans dalam satu studi dan satu seri oleh atribut DICOM.
Rincian:
- Jalan:.. /studies/{study}/series/{series}/instances? SOPInstanceUID={instance}
- Metode: GET
- Header:
- Terima: application/dicom+json
- Otorisasi: Pembawa $token"
Validasi bahwa respons mencakup satu instans dan bahwa kode respons OK.
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)
Menghapus DICOM
Catatan
Penghapusan bukan bagian dari standar DICOM, tetapi telah ditambahkan untuk kenyamanan.
Kode respons 204 dikembalikan saat penghapusan berhasil. Kode respons 404 dikembalikan jika item belum pernah ada atau sudah dihapus.
Menghapus instans tertentu dalam studi dan seri
Permintaan ini menghapus satu instans dalam satu studi dan satu seri.
Rincian:
- Jalan:..
/studies/{study}/series/{series}/instances/{instance}
- Metode: DELETE
- Header:
- Otorisasi: $token Pembawa
Permintaan ini menghapus instans segitiga merah dari server. Jika berhasil, kode status respons tidak berisi konten.
headers = {"Authorization":bearer_token}
url = f'{base_url}/studies/{study_uid}/series/{series_uid}/instances/{instance_uid}'
response = client.delete(url, headers=headers)
Menghapus seri tertentu dalam studi
Permintaan ini menghapus satu seri (dan semua instans anak) dalam satu studi.
Rincian:
- Jalan:..
/studies/{study}/series/{series}
- Metode: DELETE
- Header:
- Otorisasi: $token Pembawa
Contoh kode ini menghapus instans persegi hijau (ini adalah satu-satunya elemen yang tersisa dalam seri) dari server. Jika berhasil, kode status respons tidak akan menghapus konten.
headers = {"Authorization":bearer_token}
url = f'{base_url}/studies/{study_uid}/series/{series_uid}'
response = client.delete(url, headers=headers)
Menghapus studi tertentu
Permintaan ini menghapus satu studi (dan semua seri dan instans anak).
Rincian:
- Jalan:..
/studies/{study}
- Metode: DELETE
- Header:
- Otorisasi: $token Pembawa
headers = {"Authorization":bearer_token}
url = f'{base_url}/studies/{study_uid}'
response = client.delete(url, headers=headers)
Langkah berikutnya
Untuk informasi tentang layanan DICOM, lihat