標籤:webpacs dcmtk orthanc deconstructed pacs
背景:
上篇博文為引子,介紹了一款神奇的開源PACS系統——Orthanc。本篇開始解讀官方Cookbook中的相關內容,對於簡單的瀏覽、訪問和上傳請閱讀前篇博文。在常規的PACS系統中還未出現對於DCM映像的修改和匿名化操作,因此此次重點介紹Orthanc利用REST API實現對DCM醫學映像的修改(modification)和匿名化(anonymization)。對於官方Cookbook中的執行個體進行示範和調試,通過Orthanc源碼分析確保樣本在本機良好運行。注意:官方Cookbook中的樣本在Windows下會有錯誤,詳情見博文。
Orthanc介紹:
取名為Orthanc源自於J.R.R. Tolkien’s(托爾金)的小說。Orthanc是艾辛格(Isengard)要塞中的黑塔,初建於第二世紀,用於儲存收納南方王國的真知晶石——palantíri,一種圓形且能夠看見遠方的石頭,透過palantíri可以跟遠方使用palantíri的人進行交流。Orthanc Server正是取palantíri的此層含義,設計出一種可在整個醫院DICOM拓撲網路中便捷、透明以及可程式化訪問醫學映像的系統(可參照wiki百科的介紹:http://en.wikipedia.org/wiki/Isengard)。
另外,Orthanc中同時包含了“RTH“,即Radiotherapy。其實Orthanc本身源自於法國de Liège大學中心醫院(Centre Hospitalier Universitaire)對於放射治療服務的研究。
Orthanc之Modification & AnonymizationAnonymization:
Orthanc從0.5.0版本之後引入了對DICOM資源的匿名化操作,可對患者(patients)、檢查(studies)、序列(series)和映像(instances)多個層級進行匿名化處理。為了方便示範,此處以instances層級為例進行介紹:
1)按照上篇博文上傳兩幅測試映像到Orthanc Server,如所示:
2)利用curl命令列查看一下上述兩個instances,擷取ID號,結果如下:
curl http://localhost:8042/instances
此處擷取的instance ID號為:c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c
3)按照Orthanc官方Cookbook的說明,進行匿名化操作
輸入指令:curl http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/anonymize -X POST -d ‘{}‘ > c:\orthanc-anonymize.dcm
但是並未獲得如期結果,開啟c盤發現orthanc-anonymization.dcm檔案大小為0KB。開啟curl的verbose模式,
curl –v http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/anonymize -X POST -d ‘{}‘ > c:\orthanc-anonymize.dcm
查看輸出資訊如下:
發現Orthanc Server中HTTP 服務返回值為404。
通過自己查看官方Cookbook給出的指令,除了對應instance的UID不同外,並未找到問題,暫且跳過,嘗試一下Modification。
Modification:
1)上傳DCM檔案到Orthanc Server,同Anonymization中相同;
2)利用curl http://localhost:8042/instances擷取指定instance的ID號;
3)參照官方Cookbook進行Modification處理
輸入如下指令,此次為了方便,直接使用curl的verbose模式:
curl –v http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/modify –X POST –d ‘{"Replace”:{"PatientName”:"hello”,"PatientID”:"world”}}‘ >c:\orthanc-modify.dcm
輸出結果如下:
目前為止,參照Orthanc官方的Cookbook說明,給出的兩個樣本都在本機無法順利完成。因此猜測官網中給出的代碼有可能是Linux系統的,在Windows系統下應該做出適當的調整,但是該怎樣調整呢?該調整哪一部分呢?請繼續往下看……
調試Orthanc官方Cookbook樣本:0)Orthanc REST API資料查詢:
在調試之前,我們先要搞清楚出現問題的大致原因,排除一些常見的錯誤,比如軟體版本錯誤、指令拼字錯誤等等。上面介紹的Anonymization和Modification都是利用了Orthanc的REST API功能,那麼Orthanc各版本對於REST API的支援程度如何呢?我們查看一下官方的說明,如所示:
從可以看出,Orthanc從0.5.0版本之後就支援多層級的修改和匿名化操作,如上一篇博文所述,我本機安裝的是最新的0.8.5版本。因此可以確保軟體版本無誤,另外在上述樣本模擬過程中我們也對比排查了指令拼字錯誤。
接下來使出我們的殺手鐧吧:“啟動C:\Orthanc-0.8.5\Orthanc.sln工程,進入偵錯模式,查看Orthanc的源碼”
開啟Orthanc.sln解決方案,右鍵Orthanc工程開啟偵錯模式,在關於REST API的幾個核心類中插入斷點,初次嘗試插入的斷點如下:
1)Anonymization源碼調試:
從新按照上一節中的步驟,首先利用Orthanc Explorer向偵錯模式下的Orthanc Server添加DICOM檔案,此時直接按F5跳過調試,因為映像載入我們並未遇到問題。(注:此步操作必須重新添加,因為偵錯模式下資料的儲存目錄是C:\Orthanc-0.8.5\OrthancStorage,與二進位安裝包預設的C:OrthancStorage不同,上一節中我們添加的映像在調試狀態下是看不到的)
輸入指令查看ID:
curl http://localhost:8042/instances
開始輸入上一節的Anonymization指令:
curl -v http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/anonymize -X POST -d ‘{}‘ > c:\orthanc-anonymize.dcm
Orthanc工程首次停在了斷點RestApi.cpp中的Visit函數內,如所示:
利用F11單步調試,隨後進入到解析Anonymization請求操作的函數ParseAnonymizationRequest內部,可以看到在該函數內給出了一個樣本與我們輸入的格式相同
繼續單步調試,最後發現在解析Json格式的AnonymizationRequest指令,即我們輸入的‘{}’,json_reader.cpp中的readToken返回值為tokenError。
至此想必我們找到了Anonymization指令出錯的原因的,應該是我們輸入的Json格式不正確,雖然我們是按照Orthanc官方的Cookbook來進行的。為了確定我們的Json格式是否有誤,在線上Json格式檢查網站(http://www.bejson.com/)測試一下,結果為:
按照提示,去掉指令中Json部分的單引號,輸入:
curl -v http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/anonymize -X POST -d {} > c:\orthanc-anonymize.dcm
curl調試輸出的結果為:
利用DICOM看圖軟體,開啟可以發現orthanc-anonymize.dcm檔案中的患者姓名已經被隱藏掉了。
【總結】:正確的指令應該是外邊不添加單引號,如下所示:
curl -v http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/anonymize -X POST -d {} > c:\orthanc-anonymize.dcm
2)Modification源碼調試:
既然已經找到了Anonymization中代碼出錯的問題,讓我們去掉Modification中的單引號,嘗試一下:
輸入指令:
curl -v http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/modify -X POST -d {"Replace":{"PatientName":"hello","PatientID":"world"}} >c:\orthanc-modify.dcm
令我們失望的是依然看到了HTTP Server返回的404錯誤。
由於Modification指令中單引號內部還存在著更多的雙引號,所以死讓我們繼續調試,根據上次的經驗,有可能還是Json格式輸入輸入錯誤,然我們只保留json_reader.cpp中的readToken斷點,直接查看一下解析結果是否是tokenError?。
重新輸入指令,進入到readToken內部,發現函數依然返回tokenError,結果如所示:
利用VS內建的查看工具,我們可以看到readToken函數解析的字串current_,其真實內容是:{Replace:{PatientName:hello,PatientID:world}},其中並未看到我們輸入的雙引號,將{Replace:{PatientName:hello,PatientID:world}}輸入到http://www.bejson.com/中,也得到了錯誤的結果提示:
這說明我們在命令列中輸入的Json指令中的雙引號,在被VS讀入過程中被忽略了。
那麼我們利用逸出字元將雙引號傳遞進入,輸入指令:
curl -v http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/modify -X POST -d {\"Replace\":{\"PatientName\":\"hello\",\"PatientID\":\"world\"}} >c:\orthanc-modify.dcm
查看結果:
最終運行輸出為,
在DCM看圖軟體中開啟orthanc-modify.dcm,可以看到PatientID和PatientName已經修改。
至此,關於Orthanc Modification和Anonymization的調試已經順利完成,可以鬆一口氣啦,還是調試原始碼給力啊^_^。
知識點補充:
Orthanc作為開源項目,擷取其源碼自然是輕而易舉。雖然號稱輕型Server系統,但是代碼量還是足以嚇退大多數人的。初次面對海量的代碼還是很難進行調試的。下面以上述調試Orthanc官方Cookbook中的Anonymization和Modification樣本過程為例,講解一下如何開始調試開源項目:
1)如何調試Orthanc中的Http Server
之所以上一節最開始將斷點設定在了RestXXX.cpp檔案中,是因為本博文中的介紹的Modification和Anonymization使用的是Orthanc提供的REST API服務,因此首先推測錯誤應該出現在REST API的相關實現函數中。這種方法可以順利的解決我們上面遇到的問題,並且如你所見我們已經實現了問題的排查。
但是既然調試進入了Orthanc的源碼內,就順道兒搞清楚Orthanc中Http Server的整體流程,也順便找找究竟是在哪一個地方將curl指令中的雙引號忽略的。
再一次輸入指令,進入調試狀態。斷點第一次停在RestApi.cpp中的Visit函數處,在前一節中我們利用“單步調試”順利的逐步進入到了JSON的解析函數readToken內,“單步調試”是向前追溯的最佳手段,也是VS提供給我們的最直觀最有利的錯誤排查工具。但是為了要瞭解整個流程,我們此處需要“回溯”,那麼VS提供給我們逆流而上的工具是什麼呢?那就是“尋找所有引用”,如所示:
我們可以由RestApi.cpp 轉到上一級,即RestApiHierarchy.cpp中的LookupResource函數,如此迭代下去相信可以回溯到整個流程的最頂端。這種方法雖然笨拙了一些,但是比較實用。
想必在Windows編程環境下大家還是更喜歡“可視化”的直觀操作,即所見即所得 (WYSIWYG)。那麼單擊功能表列中的“調試”,選擇“視窗”中的“呼叫堆疊”,可以直觀的看到在RestApi.cpp中的Visit函數之前的各種函數調用順序,如所示:
至此我們可以看到整個Http Server服務的開始端點是mongoose.cpp中的worker_thread函數(在“呼叫堆疊”中相關函數右鍵選擇“轉到源碼”可以查看函數的具體位置)。由中“呼叫堆疊”的順序我們已經可以清晰的看到Orthanc Http Server的整個調用流程。那麼我們在worker_thread函數中插入斷點,重新輸入curl指令,查看一下在整個流程的起始狀態下,Orthanc Http Server接收到的資料體是什麼格式?如所示:
在process_new_connection函數內部讀取curl發出的POST請求指令時,串連緩衝buf的內容中已經不存在雙引號了,因此可以確定curl的-d參數在發送JSON格式資料到Orthanc的Http Server時忽略了雙引號。後續可以研究一下curl在windows環境下的使用,尤其是-d參數的設定。
2)如何調試Orthanc中的DICOM Server
由於DICOM Server比較熟悉,先驗知識比較多,因此調試起來比HTTP Server容易。在Orthanc的main函數中我們可以看到httpServer.Start()和dicomServer.Start(),dicomServer.Start()就是DICOM Server的進入點,由於Orthanc是基於DCMTK開發的,因此後續的流程應該是交由DCMTK來完成的,更詳細的細節可參考本專欄前面的DCMTK網路系列文章。另外預告一下下一篇博文會詳細介紹利用fo-dicom開源庫打造一個簡單的DICOM Server服務端,那時再詳細的對比分析一下Orthanc中的DICOM Server,敬請期待。
參考資料:
https://code.google.com/p/orthanc/wiki/OrthancCookbook,Cookbook官網連結
https://code.google.com/p/orthanc/wiki/Anonymization,Orthanc的Modification和Anonymization介紹
https://docs.google.com/spreadsheet/pub?key=0Ao5aRMxCX2hldEJadzVUaWFmNW5QTWhrYTI3UHMzdXc&single=true&gid=8&output=html,REST API支援圖譜
https://docs.google.com/spreadsheet/pub?key=0Ao5aRMxCX2hldEJadzVUaWFmNW5QTWhrYTI3UHMzdXc&single=true&gid=0&output=html,REST API支援圖譜
後續專欄博文預告:
fo-dicom搭建簡單的DICOM Server
[email protected]
時間:2014-11-29
DICOM醫學影像處理:Deconstructed PACS之Orthanc,Modification & Anonymization