在介紹如何生成nrrd前,了解一下為什么醫學影像上一般使用nrrd的體素模型?
為什么醫學影像上一般使用nrrd的體素模型?
在醫學影像領域,?NRRD(Nearly Raw Raster Data)格式?被廣泛用于存儲體素模型(如CT、MRI數據),主要基于以下技術優勢:
?1. 靈活的數據存儲方式?
- ?支持原始數據無損存儲?
NRRD可直接存儲未經壓縮的體素數據(如16位整型、32位浮點),避免DICOM等格式的二次編碼損失,適合科研和精準醫療。 - ?多維數據兼容性?
可處理3D體素(CT/MRI)、4D時序數據(如fMRI、心臟動態掃描)甚至更高維數據(如DTI纖維追蹤)。
?2. 強大的元數據支持?
- ?關鍵醫學參數嵌入?
通過文本頭文件(Header)記錄:- 體素間距(
spacings
:如?0.5mm×0.5mm×1.0mm
) - 空間方向矩陣(
space directions
,用于對齊DICOM坐標系) - 患者體位標識(如LPS/RAS坐標系)
- 體素間距(
- ?可擴展性?
支持自定義字段(如掃描協議、廠商參數),比DICOM更輕量但足夠專業。
?3. 跨平臺與開源生態?
- ?無專利限制?
相比DICOM(需授權),NRRD為開源格式,兼容ITK、VTK、3D Slicer等主流醫學軟件。 - ?簡化預處理流程?
直接存儲已重建的體素矩陣,省去DICOM多層文件合并的步驟,加速AI模型訓練(如nnUNet直接讀取NRRD)。
?4. 與其他格式的對比?
特性 | NRRD | DICOM | NIfTI |
---|---|---|---|
?數據維度? | 支持任意維度 | 單層2D為主 | 3D/4D |
?元數據靈活性? | 高(自定義字段) | 高(但結構復雜) | 中(固定字段) |
?坐標系支持? | 明確空間方向 | 需計算 | 固定(RAS/LPS) |
?適用場景? | 科研/算法開發 | 臨床歸檔 | 神經影像(fMRI) |
?典型應用場景?
- ?放射治療規劃?:NRRD存儲的CT體素數據可直接用于劑量計算(如RayStation)。
- ?深度學習?:MONAI等框架默認支持NRRD,便于加載標注數據(如
image.nrrd
+label.nrrd
)。 - ?三維重建?:通過VTK將NRRD轉換為STL模型用于3D打印。
?注意事項?
-
- ?臨床兼容性?:醫院PACS系統通常仍以DICOM為主,NRRD多用于科研后端。
- ?壓縮選項?:NRRD支持GZIP壓縮(如
*.nhdr
+*.raw.gz
),但會犧牲部分讀寫速度。
以上是愚通過ds了解到的一些情況,僅供參考。
代碼實現
以下為調用SimpleITK實現將Dicom文件轉化為Nrrd體素模型的示例:
public class DicomToNRRDHelper{/// <summary>/// DICOM序列轉化為NRRD文件/// </summary>/// <param name="dicomDirectory">dicom文件路徑</param>/// <param name="outPutFilename">包含路徑和后綴名,且后綴名必須為.NRRD(因為需要以體素渲染)</param>/// <returns>失敗為false;成功為true</returns>[MethodImpl(MethodImplOptions.Synchronized)]public static bool DicomToNRRD(string dicomDirectory, string outPutFilename){// 輸入驗證if (!Directory.Exists(dicomDirectory)){NlogHelper.Logger.Error($"Directory not found: {dicomDirectory}");return false;}if (!outPutFilename.EndsWith(".nrrd", StringComparison.OrdinalIgnoreCase)){NlogHelper.Logger.Error("Output file must have .nrrd extension");return false;}Image image3D = null;try{var seriesIDs = ImageSeriesReader.GetGDCMSeriesIDs(dicomDirectory);if (seriesIDs == null || seriesIDs.Length == 0){NlogHelper.Logger.Error($"No DICOM series found in: {dicomDirectory}");return false;}var seriesFileNames = ImageSeriesReader.GetGDCMSeriesFileNames(dicomDirectory, seriesIDs[0]);NlogHelper.Logger.Debug($"Processing {seriesFileNames.Count} DICOM files");if (seriesFileNames.Count > 1){using var reader = new ImageSeriesReader();reader.SetFileNames(seriesFileNames);image3D = reader.Execute();}else if (seriesFileNames.Count == 1){using var reader = new ImageFileReader();reader.SetFileName(seriesFileNames[0]);image3D = reader.Execute();}else{return false;}// 保留原始像素類型using var filter = new CastImageFilter();// 保留原始像素類型選項filter.SetOutputPixelType(image3D.GetPixelID());using var convertedImage = filter.Execute(image3D);using (var writer = new ImageFileWriter()){writer.SetFileName(outPutFilename);writer.Execute(convertedImage);}NlogHelper.Logger.Info($"Successfully created: {outPutFilename}");return true;}catch (Exception ex){NlogHelper.Logger.Error(ex, $"Failed to convert DICOM to NRRD");return false;}finally{image3D?.Dispose();}}}
注意事項
- SimpleITK中涉及到image、reader、writer最好都顯示釋放一下或 使用using語句,以達到使用完及時回收這些非托管資源。盡量不要依賴于它們在終結器中通過調用dispose的相關方法來實現資源回收,這個在實際項目中有過慘痛教訓(資源不能及時回收,導致非托管對象無限增長,最終導致程序崩潰)。
- 對于非托管對象的釋放,特別是在try catch語句中,若異常時容易忽略,盡量在finally中處理一下。
- 日志要盡量完善,如文件的長度檢測;文件路徑的檢測;文件名的規范性檢測等待
以上需要在實際開發中引起注意。