大家好
最近遇到一個問題
需要在原本的應用程式中添加查詢PACS服務上的dicom 影像功能
到處爬文找到很多文章:
### DICOM医学图像处理:fo-dicom网络传输之C-FIND and C-MOVE
### C-MOVE SCU端需要实现C-STORE SCP服务
## fo-dicom 套件
測試許久之後
發現查詢相對要簡單
但是要下載就是需要啟動一個臨時的 DicomService 來接收 dicom 影像
首先要在NuGet安裝 fo-dicom 這套件
有 github,但是我是在NuGet安裝的
至於版本我沒有測試出差異
## 查詢使用 C-FIND
這邊是新增查詢query
查詢條件預設是 Modality 與 studyDate
其他條件應該是用 cFind.Dataset.Add 方式加入
DicomCFindRequest cFind = DicomCFindRequest.CreateStudyQuery( modalitiesInStudy:"NM", studyDateTime: new DicomDateRange(dateTimePicker2.Value.AddDays(-1), dateTimePicker2.Value.AddDays(1)) ); cFind.Dataset.Add(DicomTag.AccessionNumber, "N0*"); cFind.Dataset.Add(DicomTag.StudyTime, ""); // erases the tag 'StudyTime' cFind.Dataset.Add(DicomTag.StudyInstanceUID, ""); // erases the tag 'Study Instance UID' cFind.Dataset.Add(DicomTag.SeriesDescription, ""); cFind.Dataset.Add(DicomTag.StationName, ""); //新增取得返回資料事件 cFind.OnResponseReceived += (DicomCFindRequest request, DicomCFindResponse response) => { try { if (response.Dataset == null) { return; } string StudyInstanceUIDStr = response.Dataset.Get(DicomTag.StudyInstanceUID, ""); cfindSUIDs.Add(StudyInstanceUIDStr); } catch (Exception ex) { MessageBox.Show("錯誤:" + ex.Message); } };
## C-MOVE 下載
然後如果要用 C-MOVE 功能則需要啟用 DicomService (會用到網路-需要注意該程式能夠通過防火牆)
啟動 DicomService 可以參考以下程式:
//新增自定義的 DicomService 類別 用於儲存 C MOVE檔案 public delegate DicomCStoreResponse OnCStoreRequestCallback(DicomCStoreRequest request); class CStoreSCP : DicomService, IDicomServiceProvider, IDicomCStoreProvider, IDicomCEchoProvider { private static DicomTransferSyntax[] AcceptedTransferSyntaxes = new DicomTransferSyntax[] { DicomTransferSyntax.ExplicitVRLittleEndian, DicomTransferSyntax.ExplicitVRBigEndian, DicomTransferSyntax.ImplicitVRLittleEndian }; private static DicomTransferSyntax[] AcceptedImageTransferSyntaxes = new DicomTransferSyntax[] { // Lossless DicomTransferSyntax.JPEGLSLossless, DicomTransferSyntax.JPEG2000Lossless, DicomTransferSyntax.JPEGProcess14SV1, DicomTransferSyntax.JPEGProcess14, DicomTransferSyntax.RLELossless, // Lossy DicomTransferSyntax.JPEGLSNearLossless, DicomTransferSyntax.JPEG2000Lossy, DicomTransferSyntax.JPEGProcess1, DicomTransferSyntax.JPEGProcess2_4, // Uncompressed DicomTransferSyntax.ExplicitVRLittleEndian, DicomTransferSyntax.ExplicitVRBigEndian, DicomTransferSyntax.ImplicitVRLittleEndian }; public CStoreSCP(Stream stream, Logger log) : base(stream, log) { } public void OnReceiveAssociationRequest(DicomAssociation association) { foreach (var pc in association.PresentationContexts) { if (pc.AbstractSyntax == DicomUID.Verification) pc.AcceptTransferSyntaxes(AcceptedTransferSyntaxes); else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None) pc.AcceptTransferSyntaxes(AcceptedImageTransferSyntaxes); } SendAssociationAccept(association); } public void OnReceiveAssociationReleaseRequest() { SendAssociationReleaseResponse(); } public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason) { } public void OnConnectionClosed(int errorCode) { } public static OnCStoreRequestCallback OnCStoreRequestCallBack; public DicomCStoreResponse OnCStoreRequest(DicomCStoreRequest request) { //自定義儲存方案 if (OnCStoreRequestCallBack != null) { return OnCStoreRequestCallBack(request); } return new DicomCStoreResponse(request, DicomStatus.NoSuchActionType); } public void OnCStoreRequestException(string tempFileName, Exception e) { } public DicomCEchoResponse OnCEchoRequest(DicomCEchoRequest request) { return new DicomCEchoResponse(request, DicomStatus.Success); } public void OnConnectionClosed(Exception exception) { } } // 設定 C-STORE SCP服務,接收C-MOVE SCP資料 StudyInstanceUID 算是用於辨識的唯一值 CStoreSCP.OnCStoreRequestCallBack = (request) => { try { var studyUid = request.Dataset.Get(DicomTag.StudyInstanceUID); path = Path.Combine(path, studyUid); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } //******************** 儲存檔案 ******************** path = Path.Combine(path, instUid) + ".dcm"; request.File.Save(path); } catch (Exception ex) { MessageBox.Show("C-MOVE 錯誤:" + ex.Message); } return new DicomCStoreResponse(request, DicomStatus.Success); }; var cstoreServer = new DicomServer(CmovePortNum);//新增儲存資料用SERVER服務 var cmoveClient = new DicomClient();//新增發出命令的Client cmoveClient.NegotiateAsyncOps();//非同步執行 //這邊的 GoMoveDatas 裡面放的是 StudyInstanceUID foreach (KeyValuePair<string, GoMoves> kvp in GoMoveDatas) { //這邊的 kvp.Key 是 StudyInstanceUID 就是新增要下載的StudyInstanceUID的query DicomCMoveRequest CMove = new DicomCMoveRequest(LocalAE, kvp.Key); CMove.OnResponseReceived += (Dicom.Network.DicomCMoveRequest rq1, Dicom.Network.DicomCMoveResponse rs) => { if (rs.Status == Dicom.Network.DicomStatus.Success) { return; } return; }; cmoveClient.AddRequest(CMove); } //發送需求 cmoveClient.Send(RemoteIP, RemotePortNum, false, LocalAE, RemoteAE); //接收完檔案後 停止 儲存資料用SERVER服務 cstoreServer.Stop();
個人認為要成功從 C-MOVE 下載影像主要是需要 CStoreSCP 類別 與 cstoreServer 啟用 以及 OnCStoreRequestCallBack 添加
給大家參考囉
留言板
歡迎留下建議與分享!希望一起交流!感恩!