大家好
最近遇到一個問題
需要在原本的應用程式中添加查詢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 添加
給大家參考囉


留言板
歡迎留下建議與分享!希望一起交流!感恩!