標籤:webpacs orthanc dicom restful api wado
背景:
Orthanc是博主發現的一個很完美的DICOM和HTTP服務端開源軟體,前幾篇分別介紹了Orthanc的基本使用。Orthanc從0.8.0版本之後給出了Plugin SDK,通過該SDK可以利用Orthanc內建的REST API實現WADO服務,下面就參照官網給出的說明介紹一下如何使用SDK實現WADO服務,並且對官網的執行個體進行更新,採用最新的方式直接實現WADO服務。
官方說明中文翻譯:1)簡介
DICOM標準定義了檔案格式以及醫學影像網路傳輸協議。WADO,即Web Access to DICOM Persistent Objects,是DICOM3.0標準中制定的一種基於網路web服務訪問醫學映像的協議(具體在DICOM3.0第18部分)。通過WADO協議,專業的醫生可以使用常見的瀏覽器(目前Orthanc貌似不支援IE)預覽和下載醫學映像。
本博文所附代碼給出了一個實現簡單WADO服務的樣本。該樣本可以返回原始的DICOM映像,或者JPEG格式映像;並可以作為Orthanc的外掛程式來運行。藉助於Orthanc的架構,可以通過簡單的幾行代碼實現WADO服務。
2)背景:
Orthanc是一款開源的、輕型的、獨立的,並支援指令碼化的DICOM服務端。Orthanc主要用來精簡臨床就醫流程和簡化醫學映像的管理。另外通過相容常見的JSON、PNG格式和RESTful API,使得DICOM標準在電腦映像領域得到更廣泛的應用。Orthanc隱藏了DICOM檔案格式和DICOM協議的複雜性,因此醫院普通的網路管理員以及專業的醫學映像自動分析軟體開發人員都可以使用。Orthanc可以作為一個健壯的醫學影像處理中心,為各個醫院提供服務。
從0.8.0(2014 7月份發布)開始,Orthanc給外部開發人員 提供了外掛程式開發SDK。利用SDK開發的動態庫形式的外掛程式可以被匯入到Orthanc服務中,外掛程式通過註冊回呼函數來響應瀏覽器的HTTP請求。回呼函數反過來可以訪問Orthanc資料庫提取目標DICOM檔案的資訊。Orthanc Plugin SDK以C語言標頭檔形式給出,連結如下:https://code.google.com/p/orthanc/source/browse/Plugins/OrthancCPlugin/OrthancCPlugin.h?name=Orthanc-0.8.0,說明文檔:http://www.codeproject.com/KB/webservices/797118/OrthancPluginDocumentation.zip
3)DICOM 和 WADO
本小節只概括介紹WADO協議,詳細介紹參見DICOM3.0標準的第18部分。
DICOM協議裡規定了如下標準:一個患者(Patient)可以做多次檢查(Studies)。每一個檢查(Study)包含一系列醫學映像,即序列(Series)。舉個例子:標準的PET-CT檢查(Study)會包含兩組序列,CT 序列和PET 序列。序列中通常對應人體的二維/三維/四維影像。每種影像會被分割成多個檔案儲存體,即Instance(也就是我們看到的包含單幅映像的單個尾碼為DCM的檔案)。
通常,一個DICOM執行個體(Instance)可以看做是二維映像與儲存了患者元資訊(人口統計學資訊,如姓名、年齡、身高、體重等等)結構的組合。患者元資訊通常是包含了DICOM標籤對應值的數組。每個標籤由兩個十六進位數表示。非常重要的是,每一級的檢查(Study)、序列(Series)和映像(Instance)要求全域唯一。
例如(0x0020,0x000d)代表的是Study Instance UID,定義檢查(Study)的唯一性;
…………
一個WADO請求就是一個簡單的HTTP GET請求,請求中包含了Study、Series和Instance標識符。例如:
http://localhost/wado? studyUID=1.2.840.113845.11.1000000001951524609.20121203131451.1457891& seriesUID=1.2.840.113619.2.278.3.262930758.589.1354512768.115& objectUID=1.2.840.113619.2.278.3.262930758.589.1354512768.116.1& requestType=WADO
該WADO請求的響應會是與studyUID/seriesUID/objectUID對應的DICOM映像的JPEG格式。如果希望直接擷取DICOM格式檔案,應在WADO請求中添加contentType=application%2Fdicom,如下所示:
http://localhost/wado? studyUID=1.2.840.113845.11.1000000001951524609.20121203131451.1457891& seriesUID=1.2.840.113619.2.278.3.262930758.589.1354512768.115& objectUID=1.2.840.113619.2.278.3.262930758.589.1354512768.116.1& contentType=application%2Fdicom& requestType=WADO
官方說明就簡單的翻譯到這裡,下面採用結合具體案例的方式來進行。
Orthanc WADO Plugin的編輯及使用:
該範例程式碼依賴於下面四部分:Orthanc Plugin SDK(0.8.0版本之後);CImg Library(用於將DICOM映像轉換成PNG格式);JsonCpp庫,用於解析Orthanc服務返回的Json格式的檔案;CMake,用於編譯源碼。
1)Orthanc WADO Plugin的編譯:
下載官方說明中的源碼(http://www.codeproject.com/KB/webservices/797118/WadoPluginSources.zip),解壓後按照README.txt中的說明編譯安裝Orthanc WADO Plugin:
第一步:進入cmd命令列模式,建立編譯目錄,輸入指令:mkdir Build
第二步:進入Build目錄
第三步:啟動Cmake,開始編譯,輸入cmake ..\WadoPluginSources(註:這裡..意思是返回WadoPluginSources源碼中CmakeList.txt所在的目錄,README.txt中的寫法是錯誤的)
第四步:開啟Build目錄下的WadoPlugin.sln工程,利用VS進行編譯,會在Build\Debug目錄下看到WadoPlugin.dll,說明WADO外掛程式產生成功。
2)Orthanc WADO Plugin的安裝:
源碼包中README.txt給出的安裝說明有誤,應該將WadoPlugin.dll全路徑名添加到Configuration.json檔案中Plugin對應的欄位內,如所示:
注意:在Windows系統中輸入的WadoPlugin.dll的路徑應該使用中的【/】,或者輸入"c:\\WadoPluginSources\\Build\\Debug\\WadoPlugin.dll”。否則會出現錯誤。
3)Orthanc WADO Plugin啟動:
修改完Configuration.json檔案後,准到Orthanc.exe所在目錄,例如我本機為:C:\Orthanc-0.8.5\Debug>Orthanc.exe ../../Orthanc/Configuration.json。【注意:後面跟的是添加了WadoPlugin.dll的Configuration.json的路徑,如果輸入Orthanc.exe --config=Configuration.json,是產生預設Configuration.json的結果,而並不會啟動WadoPlugin服務)。
4)執行個體測試:
按照前幾篇博文方式,上傳兩幅測試映像,結果如下:
其中已知test1的各級UID為:
StudyInstanceUID=1.3.6.1.4.1.30071.6.176694098609799.4240639413125000;
SeriesInstanceUID=1.3.6.1.4.1.30071.6.176694098609799.4240639413125000.1;
SOPInstanceUID=2.16.840.114421.81623.9430067258.9493139258;
構造WADO請求,查詢test1映像,請求串連為:http://localhost:8042/wado?studyUID=1.3.6.1.4.1.30071.6.176694098609799.4240639413125000&seriesUID=1.3.6.1.4.1.30071.6.176694098609799.4240639413125000.1&objectUID=2.16.840.114421.81623.9430067258.9493139258&requestType=WADO
瀏覽器結果如下所示,與test1.dcm原檔案相同。
新版Orthanc WADO Plugin:1)官方說明:
官方說明中有這樣一段:(http://www.codeproject.com/Articles/797118/Implementing-a-WADO-Server-using-Orthanc)
當從WADO HTTP請求中解析出study/series/instance標識符後,需要在Orthance資料庫中進行查詢。為了實現查詢定位,官方博文中給出的代碼執行個體直接借用了Orthanc內建的RESTful API服務(而不是直接響應WADO HTTP 要求)。
首先需要定位study級,代碼如下:
static bool LocateStudy(Json::Value& study, const std::string& studyUID){ // Retrieve the list of the studies that are stored in Orthanc Json::Value listOfStudies; if (!OrthancContext::GetInstance().RestApiDoGet(listOfStudies, "/studies")) { return false; } // Retrieve information about each of these studies for (Json::Value::ArrayIndex i = 0; i < listOfStudies.size(); i++) { std::string studyUri = "/studies/" + listOfStudies[i].asString(); if (OrthancContext::GetInstance().RestApiDoGet(study, studyUri)) { // If the "StudyInstanceUID" of this study matches, we are done if (study["MainDicomTags"]["StudyInstanceUID"].asString() == studyUID) { return true; } } } return false;}
LocateStudy函數內部先構造出/studies形式的RESTful API的uri請求,查詢出Orthanc中的所有study的UUID,然後再迴圈遍曆每一個獲得的studyUUID,構造出/studies/{id}形式的RESTful API請求,逐個對比返回JSON結果中的StudyInstanceUID標籤,實現study定位;
其次定位sereis級,代碼如下:
static bool LocateSeries(Json::Value& series, const Json::Value& parentStudy, const std::string& seriesUID){ // Loop over the child series of the located study const Json::Value& listOfSeries = parentStudy["Series"]; for (Json::Value::ArrayIndex j = 0; j < listOfSeries.size(); j++) { std::string seriesUri = "/series/" + listOfSeries[j].asString(); // If the "SeriesInstanceUID" of this series matches, we are done if (OrthancContext::GetInstance().RestApiDoGet(series, seriesUri) && series["MainDicomTags"]["SeriesInstanceUID"].asString() == seriesUID) { return true; } } return false;}
與study級類同;
最後是Instance級,代碼如下:
static bool LocateInstance(Json::Value& instance, const Json::Value& parentSeries, const std::string& objectUID){ // Loop over the child instances of the located series const Json::Value& listOfInstances = parentSeries["Instances"]; for (Json::Value::ArrayIndex k = 0; k < listOfInstances.size(); k++) { std::string instanceUri = "/instances/" + listOfInstances[k].asString(); // If the "SOPInstanceUID" of this series matches "objectUID", we are done if (OrthancContext::GetInstance().RestApiDoGet(instance, instanceUri) && instance["MainDicomTags"]["SOPInstanceUID"].asString() == objectUID) { return true; } } return false;}
上述定位流程複雜,從0.8.0版本之後Orthanc提供了直接存取資料庫中DICOM索引的函數,OrthancPluginLookupPatient(),OrthancPluginLookupStudy(), OrthancPluginLookupStudyWithAccessionNumber(), OrthancPluginLookupSeries()and OrthancPluginLookupInstance()。利用該類函數就需不要先定位study、再定位series、最後定位instance如此繁瑣了,修改後的代碼如下:
//2014-12-07:zssure//利用新的Orthanc外掛程式的介面,直接定位Instance//http://www.codeproject.com/Articles/797118/Implementing-a-WADO-Server-using-Orthancstatic bool LocateInstance(Json::Value& instance, const std::string& objectUID){char* instanceId = OrthancPluginLookupInstance(OrthancContext::GetInstance().GetContext(), objectUID.c_str());if (instanceId == NULL){return false;}std::string instanceUri = "/instances/" + std::string(instanceId);OrthancPluginFreeString(OrthancContext::GetInstance().GetContext(), instanceId);return (OrthancContext::GetInstance().RestApiDoGet(instance, instanceUri) &&instance["MainDicomTags"]["SOPInstanceUID"].asString() == objectUID);}//zssure:end
2)新版Wado Plugin修改:
按照官方的說明Orthanc Plugin SDK是以C標頭檔格式給出,因此直接利用Orthanc-0.8.5中的OrthancCPlugin.h檔案替換掉WadoPluginSources中的OrthancCPlugin.h後,發現WadoPlugin.cpp中我們新添加的LocateInstance函數中的GetContext()無法識別:
開啟OrthancContext.h檔案發現,檔案中並不存在GetContext()函數,因此需要手動添加公有函數:
public:OrthancPluginContext* GetContext(){return context_;}
修改完成後可以識別GetContext()函數了,但是編譯後出現如下錯誤:
將#include "../../Resources/ThirdParty/VisualStudio/stdint.h"代碼修改為#include "stdint.h"後即可消除上述錯誤。重建後可獲得新版WadoPlugin.dll外掛程式。重新輸入WADO Request,得到測試結果如下:
至此Orthanc WADO Plugin的開發就講解完成了。
後續博文介紹:
fo-dicom搭建簡單的DICOM Server
[email protected]
時間:2014-12-10
DICOM醫學影像處理:Orthanc Plugin SDK實現WADO服務