DICOM医学图像处理:基于DCMTK工具包学习和分析worklist
背景:
? ? ? ? DICOM3.0協議中有介紹關于worklist的部分。簡而言之,worklist可以看做是放射科設備從醫院RIS系統中自動讀取患者信息的一種“通信協議”,可以指存儲在RIS系統中的患者數據庫,主要包括患者的基本信息(如年齡、性別、身高、體重、出生年月等),這與DCM文件信息頭MetaInfo中的多數字段重合。因此從RIS系統中自動獲取worklist是醫院信息化的必要組成部分。下面簡單的給出幾個圖像,形象的描述一下worklist的作用。
worklist的實例學習:
? ? ? ? 在簡單的了解了worklist的作用后,下面我們利用DCMTK提供的工具包(wlmscpfs.exe和findscu.exe)來真實模擬一下該場景,從而更深刻的學習worklist的功能。
worklist簡單的看做一種“通訊”,那么自然就存在著通訊的兩端,暫且稱作“服務端”和“客戶端”。這里我們用wlmscpfs.exe來作為worklist通訊的服務端,即等待外部訪問的終端;用findscu.exe來作為服務端,用來發起worklist訪問。
首先簡單的介紹一下工具包的指令及使用方式。wlmscpfs.exe是類似于DOS時代的命令,通過設定參數可以實現不同的目的。利用Win+R鍵開啟操作系統的運行窗口,輸入cmd后進入到命令提示行窗口。然后進入到DCMTK編譯后的bin文件夾(我本機地址是C:\Program Files (x86)\DCMTK\bin)目錄,此時直接輸入wlmscpfs.exe就可以看到關于該命令工具的各種說明。
>cd C:\Program Files (x86)\DCMTK\bin
>wlmscpfs.exe
(注,此處如果為了省事,可以將bin文件夾路徑添加到windows的環境變量中,如是就可以在任何目錄下使用bin下的各種工具了)
? ? ? ? 由上圖看到,wlmscpfs.exe工具至少需要給出port一個參數,即開啟worklist服務的本機端口號。另外還需要輸入worklist數據庫文件的地址,用來供客戶端查詢、訪問使用。下面我們正式開啟worklist的服務端程序,至于開啟全過程,可以跟大家推薦CSDN一位博主的精品文章(http://blog.csdn.net/pachleng/article/details/5800513),博文中給出了具體的操作步驟,這個實例是對DCMTK論壇中的補充和更新。大家可以動手試一下。
第一步:建立各級目錄
? ? ? ? 以我的電腦為例,我在D盤創建了DCMWorklist文件夾,然后建立了兩個子文件夾wlistdb和wlistqry。
第二步:準備worklist數據庫文件,開啟worklist服務端服務。
? ? ? ? 然后將worklist的數據庫文件拷貝到wlistdb目錄下,此處參見冷哥博文,記得建立OFFIS子目錄。至于worklist數據庫文件通常在dcmtk庫的源碼中已經給出了,默認目錄是dcmtk-3.6.0\dcmtk-3.6.0\dcmwlm\datawlistdb(我用的是3.6.0版本)。但是源碼中的文件通常是.dump擴展名的文件,也就是我們常見的文本文件(用記事本或者Notepad++等工具雙擊即可打開)。通過利用dcmtk工具包中的dump2dcm.exe可以將.dump文件轉換成.wl文件,轉換后的.wl文件就是worklist數據庫文件。
>dcmp2dcm.exe .\dcmtk-3.6.0\dcmtk-3.6.0\dcmwlm\data\wklistdb\wlist1.dump d:\DCMWorklist\wlistdb\OFFIS\wlist.wl
>……
? ? ? ? 有了worklist數據庫文件后,我們就可以開啟worklist服務了,利用的工具就是前文提到的wlmscpfs.exe(從工具名稱中的SCP就可以看出這應該是服務端開啟服務的)。
>wlmscpfs.exe –d –dfr –dfp d:\DCMWorklist\wlistdb 104???? (注:其中的-d是為了方便我們觀察工具運行過程而開啟的調試開關)
>……
第三步,準備查詢文件,開啟worklist查詢。
? ? ? ? 服務端已經準備就緒,下面就是該發起worklist查詢服務啦。利用的工具是findscu.exe(從工具名稱中的SCU同樣可以猜測出這是客戶端)。dcmtk源碼包中同樣給我們提供了查詢worklist的查詢文件,默認目錄是dcmtk-3.6.0\dcmtk-3.6.0\dcmwlm\data\wlistqry,打開后可以看到里面以.dump格式存在的文件,再次利用dump2dcm.exe將.dump轉換為.wl文件。
>dcmp2dcm.exe .\dcmtk-3.6.0\dcmtk-3.6.0\dcmwlm\data\wklistqrt\wlistqry1.dump d:\DCMWorklist\wlistqry\wlistqry.wl
? ? ? ? 然后我們利用findscu發起查詢請求,
>findscu.exe –d 127.0.0.1 104 d:\DCMWorklist\wlistqry\wlistqry.wl –aec OFFIS
? ? ? ? 其中aec代表的是被請求或者說被呼叫的應用端的名稱,即我們wlistdb文件夾內的OFFIS子文件夾。
通過以上三步,我們就簡單的利用dcmtk提供的wlmscpf.exe和findscu.exe工具包以及相應的wklistdb數據庫文件和wklistqry數據庫查詢文件模擬了worklist服務開啟及查詢的整個流程。是不是很簡單,很容易上手。
實際結果分析:
? ? ? ? 上一部分中提到了在使用wlmscpfs.exe和findscu.exe工具包的時候開啟了-d調試模式,目的就是為了方便我們跟蹤整個通訊的流程。另外為了方便查看,我們利用重定向技術,將wlmscpfs.exe和findscu.exe工具包的調試信息輸出到txt文件,方便我們事后進行再次對比查看。下面將兩個工具包的調試信息用Notopad++打開,對比分析一下,見下圖:
? ? ? ? 從調試信息可以清晰的看到wlmscpfs.exe與findscu.exe之間的通信流程,該流程在DICOM3.0標準的第四部分(Service Class Specifications )和第八部分(Network Communication Support for Message Exchange)都有詳細的介紹,上述的重定向生成的文本文檔就是學習DICOM3.0第四、八部分最好的實例。因為dcmtk是開源的,所以這方便我們分析wlmscpfs.exe和findscu.exe兩個工具包的源碼。具體分析見下一節。
wlmscpfs.exe和findscu.exe工具包源碼分析:
| ? | wlmscpfs.exe | findscu.exe |
| C/S | worklist服務端 | worklist客戶端 |
| 源碼文件 | wlmscpfs.cc wlcefs.cc wlmactmg.cc | findscu.cc |
| 內部函數 | 1)ConnectToDataSource();//開啟連接 2)WlmActivityManager(); //函數內部利用WSAStartup()啟動了Windows套接字服務 3)StartProvidingService(); //啟動worklist管理服務,位于wlmactmg.cc文件。函數內部調用(a)(b)兩個函數。 (a)ASC_initializeNetwork()函數 ASC_initializeNetwork函數調用了DICOM協議封裝的TCP協議函數DUL_InitializeNetwork(該函數內部就會出現我們在套接字編程中常見的socket、setsockopt、bind和listen函數) (b)WaitForAssociation();函數 WaitForAssociation函數調用了DICOM協議封裝的TCP/IP協議函數receiveTransportConnectionTCP(該函數內部利用的是select端口模式,會出現套接字編程中常見的select、accept函數) 4)disconnectfromDataSource(); //斷開連接的函數。 | 1)WSAStartup();//初始化套接字服務 2)DcmFindSCU::initializeNetWork(); //函數內部調用的也是ASC_initializeNetwork函數。 3)DcmFindSCU::performQuery(); //同樣該函數內部封裝了很多以DUL開頭的協議操作函數。DUL是DICOM? Upper Layer 的縮寫。 4)WSACleanup(); |
? ? ? ? 從上述分析中我們基本可以看出,worklist的通訊是建立在TCP/IP這一現有協議之上的,可以說是對協議的二次封裝。與我們平時進行套接字編程的基本流程相似,搞清楚了這一點,對于分析工具包、學習工具包的使用都會有很大的幫助。
worklist數據庫文件或查詢文件(*.wl)的生成:
1)問題提出:
? ? ? ? 參照冷哥博文(http://blog.csdn.net/pachleng/article/details/5800513、http://blog.csdn.net/pachleng/article/details/5827232)中的說明,我們可以很容易的利用DCMTK的工具包學習worklist操作。但是在實際應用模仿過程中,有的人可能會好奇為什么要把wklist.wl和wklistqry.wl文件分別放在不同目錄?為什么同為以.wl為擴展名的文件,一個就可以作為worklist的數據庫文件放在服務端,而另一個就是客戶單的查詢文件?如果想發起自己的查詢,即C-FIND請求,我們怎么手動生成wklistqry.wl文件?
? ? ? ? 針對上述問題,在dcmtk的論壇中也曾經有人提到過,參見(http://forum.dcmtk.org/viewtopic.php?f=1&t=1475&hilit=wlmscpfs.exe%23p5016)。利用UltraEdit工具將wklist.wl和wklistqry.wl文件同時打開,對比如下:
? ? ? ? 從上圖中可以看出,作為worklist客戶端數據庫文件的wklist.wl中是將患者真實信息以符合DICOM3.0標準字段的形式存儲,而客戶端發起查詢請求的wklistqry.wl文件內部只是簡單的需要查詢的空字段,即各個字段的值域都為空。參考博文中給出了利用dump2dcm.exe工具包將.dump文件轉換成wklistqry.wl文件(dump可以認為是普通的文本文件,可以利用記事本等工具進行直接編輯)。如下圖所示:
? ? ? ? ?例如作為測試用的wlist-2.dump文件中的患者ID為123456,生成后的wlist-2.wl文件中的(0010,0020)字段也是123456。
2)實際測試:
? ? ? ? 從dump2dcm.exe工具包的說明我們就可以知道,.wl文件其實就是dcm文件,只是該類文件中并不存在真實的像素信息。通常只包含信息頭部分,主要指的是患者的各項信息。因此想利用dcmtk的庫函數直接獲取.wl文件,其實就是手動構造dcm文件的過程。參見http://support.dcmtk.org/docs/mod_dcmdata.html#Examples中的第二個例子,我們可以手動生成以“.wl”為后綴的dcm文件。具體的代碼如下:
?
#include <stdio.h> #include <tchar.h> #include "dcmtk/config/osconfig.h" #include "dcmtk/dcmdata/dctk.h" #include "dcmtk/dcmdata/dcpxitem.h" #include "dcmtk/dcmjpeg/djdecode.h" #include "dcmtk/dcmjpeg/djencode.h" #include "dcmtk/dcmjpeg/djcodece.h" #include "dcmtk/dcmjpeg/djrplol.h" using namespace std;int main() {char uid[100];DcmFileFormat fileformat;DcmDataset *dataset = fileformat.getDataset();/************************************************利用下列語句可以生成worklist的數據庫文件,即*不含有影像信息的dcm文件*************************************************/dataset->putAndInsertString(DCM_SOPClassUID, UID_SecondaryCaptureImageStorage);dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));dataset->putAndInsertString(DCM_PatientName, "Doe^John");OFCondition status = fileformat.saveFile("D:\\DcmWorklist\\worklist\\test.wl", EXS_LittleEndianExplicit);if (status.bad())cerr << "Error: cannot write DICOM file (" << status.text() << ")" << endl;return 0; }? ? ? ? 既然服務端需要的worklist數據庫文件和客戶端需要的查詢文件都是.wl文件,唯一的區別就是一個有值域,一個沒有。因此利用上述代碼我們既可以生成worklist數據庫文件,也可以生成worklist查詢文件。
3)測試結果:
? ? ? ? 查看findscu.exe工具包,我們可以找到-k選項,也就是在提供了查詢文件wklistqry.wl的同時也可以指定限定的查詢字段,例如
>findscu 127.0.0.1 104 -v -k 0010,0020="123456" -aec OFFIS wlistqry.wl
? ? ? ? 如果不添加-k 0010,0020=“123456”限定選項,查詢結果如前文中重定向的結果相同,而添加了限定字段后,我們能夠查詢到的就只有數據庫端中滿足PatientID字段為123456的患者數據。具體結果如下:
? ? ? ? 從中我們可以看到服務端給我們的反饋是PatientID為123456的患者信息,所返回的信息都是wlistqry.wl文件中要求的字段,其中通過-k 0010,0020=“123456”限定項來限定了查詢的結果,在服務端的反饋是兩個患者中表明有一個匹配的worklist數據庫文件,如上圖中黃色區域所示。
? ? ?猜想:既然我們可以利用dcmtk自由生成客戶端的.wl查詢文件,而-k 0010,0020=”123456”就是對該查詢文件的補充,那么是不是如果我們直接把123456寫入到wlistqry.wl中的(0010,0020)字段的值域,而直接利用修改后的查詢文件也會得到相同的結果呢?此處利用自己的代碼將0010,0020字段的值域填充為123456
#include <stdio.h> #include <tchar.h> #include "dcmtk/config/osconfig.h" #include "dcmtk/dcmdata/dctk.h" #include "dcmtk/dcmdata/dcpxitem.h" #include "dcmtk/dcmjpeg/djdecode.h" #include "dcmtk/dcmjpeg/djencode.h" #include "dcmtk/dcmjpeg/djcodece.h" #include "dcmtk/dcmjpeg/djrplol.h" using namespace std;int main() {char uid[100];DcmFileFormat fileformat;DcmDataset *dataset = fileformat.getDataset();/***********************************************【猜測一】:*利用下列語句可以生成worklist的查詢文件*即,* 各個字段數據都為空的dcm文件************************************************/dataset->putAndInsertString(DCM_SOPClassUID, UID_SecondaryCaptureImageStorage);dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));dataset->putAndInsertString(DCM_ImplementationVersionName,"OFFIS_DCMTK_361");dataset->putAndInsertString(DCM_SpecificCharacterSet,"");dataset->putAndInsertString(DCM_PatientName, "");dataset->putAndInsertString(DCM_PatientID,"123456");dataset->putAndInsertString(DCM_PatientBirthDate,"");dataset->putAndInsertString(DCM_PatientSex,"");OFCondition status = fileformat.saveFile("D:\\DcmWorklist\\worklist\\testqry.wl", EXS_LittleEndianExplicit);if (status.bad())cerr << "Error: cannot write DICOM file (" << status.text() << ")" << endl;return 0; }? ? ? ? 然后利用
>findscu 127.0.0.1 104 -v -aec OFFIS testqry.wl 指令直接發起查詢。
? ? ? ? 查詢結果反饋如下圖所示:
? ? ? ? 上圖證明了我們的猜想,通過寫入wlistqry.wl的相關字段的值域,就等同于在findscu.exe指令中添加-k XXXX,XXXX限定選項。
? ? ? ? 至此我們詳細的介紹了如何模擬worklist的雙端服務,如何開啟服務端服務、發起客戶端查詢,關鍵是對如何利用dcmtk的庫函數來生成自定義的查詢端.wl文件進行了補充設實例測試。
(完)
?
作者:zssure@163.com
時間:2014-08-23
總結
以上是生活随笔為你收集整理的DICOM医学图像处理:基于DCMTK工具包学习和分析worklist的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: matlab拟合四次函数表达式,用mat
- 下一篇: matlab光顺拐点,基于MATLAB的