/// <summary> /// Save all images and the rt struct to the given folder. /// </summary> /// <param name="folderPath"></param> /// <param name="medicalVolume"></param> /// <returns></returns> public static async Task SaveMedicalVolumeAsync( string folderPath, MedicalVolume medicalVolume) { await Task.WhenAll( SaveDicomImageAsync(folderPath, medicalVolume), SaveRtStructAsync(Path.Combine(folderPath, "rtstruct.dcm"), medicalVolume.Struct)); }
public VolumeLoaderResult(string seriesId, MedicalVolume volume, Exception error, IReadOnlyList <string> warnings) { SeriesUid = seriesId; Volume = volume; Error = error; Warnings = warnings; }
/// <summary> /// Converts a medical volume, scan files and a set of binary masks, into a Dicom representation. /// The returned set of Dicom files will have files for all slices of the scan, and an RtStruct /// file containing the contours that were derived from the masks. The RtStruct file will be the first /// entry in the returned list of Dicom files. /// Use with extreme care - many Dicom elements have to be halluzinated here, and there's no /// guarantee that the resulting Dicom will be usable beyond what is needed in InnerEye. /// </summary> /// <param name="medicalVolume">The medical scan.</param> /// <param name="scanFiles">The image modality through which the scan was acquired.</param> /// <param name="contours">A list of contours for individual anatomical structures, alongside /// <returns></returns> public static List <DicomFileAndPath> ScanAndContoursToDicom( MedicalVolume medicalVolume, IReadOnlyList <DicomFileAndPath> scanFiles, IReadOnlyList <ContourRenderingInformation> contours) { var rtFile = ContoursToDicomRtFile(contours, medicalVolume.Identifiers, medicalVolume.Volume.Transform); var dicomFiles = new List <DicomFileAndPath> { rtFile }; dicomFiles.AddRange(scanFiles); return(dicomFiles); }
/// <summary> /// Returns a task that saves a copy of the original DICOM files forming a medical volume. /// </summary> /// <param name="folderPath"></param> /// <param name="medicalVolume"></param> /// <returns></returns> private static async Task SaveDicomImageAsync(string folderPath, MedicalVolume medicalVolume) { await Task.Run(() => Parallel.ForEach( medicalVolume.FilePaths, file => { if (!File.Exists(file)) { throw new FileNotFoundException( $"The original dicom directory was modified while using this image. File {file} is missing"); } // ReSharper disable once AssignNullToNotNullAttribute var destFile = Path.Combine(folderPath, Path.GetFileName(file)); File.Copy(file, destFile); })); }
/// <summary> /// Attempt to load a volume from the given SeriesUID for the given DicomFolderContents /// </summary> /// <param name="dfc">A pre-built description of DICOM contents within a particular folder</param> /// <param name="seriesUID">The DICOM seriesUID you wish to construct a volume for</param> /// <param name="acceptanceTests">An implementation of IVolumeGeometricAcceptanceTest defining the geometric constraints of your application</param> /// <param name="loadStructuresIfExists">True if rt-structures identified in the folder and referencing seriesUID should be loaded</param> /// <param name="supportLossyCodecs">If you wish to accept lossy encodings of image pixel data</param> /// <returns></returns> private static VolumeLoaderResult LoadDicomSeries( DicomFolderContents dfc, DicomUID seriesUID, IVolumeGeometricAcceptanceTest acceptanceTests, bool loadStructuresIfExists, bool supportLossyCodecs) { try { var dicomSeriesContent = dfc.Series.FirstOrDefault((s) => s.SeriesUID == seriesUID); var warnings = new List <string>(); RadiotherapyStruct rtStruct = null; if (dicomSeriesContent != null) { var volumeData = DicomSeriesReader.BuildVolume(dicomSeriesContent.Content.Select(x => x.File.Dataset), acceptanceTests, supportLossyCodecs); if (volumeData != null && loadStructuresIfExists) { var rtStructData = dfc.RTStructs.FirstOrDefault(rt => rt.SeriesUID == seriesUID); if (rtStructData != null) { if (rtStructData.Content.Count == 1) { var rtStructAndWarnings = RtStructReader.LoadContours( rtStructData.Content.First().File.Dataset, volumeData.Transform.DicomToData, seriesUID.UID, null, false); rtStruct = rtStructAndWarnings.Item1; var warning = rtStructAndWarnings.Item2; if (!string.IsNullOrEmpty(warning)) { warnings.Add(warning); } } else if (rtStructData.Content.Count > 1) { warnings.Add("There is more than 1 RT STRUCT referencing this series - skipping structure set load"); } } } var dicomIdentifiers = dicomSeriesContent.Content.Select((v) => DicomIdentifiers.ReadDicomIdentifiers(v.File.Dataset)).ToArray(); if (rtStruct == null) { rtStruct = RadiotherapyStruct.CreateDefault(dicomIdentifiers); } var result = new MedicalVolume( volumeData, dicomIdentifiers, dicomSeriesContent.Content.Select((d) => d.Path).ToArray(), rtStruct); return(new VolumeLoaderResult(seriesUID.UID, result, null, warnings)); } throw new Exception("Could not find that series"); } catch (Exception oops) { return(new VolumeLoaderResult(seriesUID.UID, null, oops, new List <string>())); } }