public static IEnumerable <(string ChannelId, DicomFolderContents Content)> DecompressSegmentationData(Stream inputStream) { var files = DicomCompressionHelpers.DecompressPayload(inputStream); var dictionaryChannelToFiles = new Dictionary <string, List <byte[]> >(); foreach ((string filename, byte[] data) in files) { var channelId = filename.Split(DicomCompressionHelpers.ChannelIdAndDicomSeriesSeparator).First(); if (!dictionaryChannelToFiles.ContainsKey(channelId)) { dictionaryChannelToFiles.Add(channelId, new List <byte[]>()); } dictionaryChannelToFiles[channelId].Add(data); } var result = new List <(string ChannelId, DicomFolderContents content)>(); foreach (var item in dictionaryChannelToFiles) { var fileAndPaths = item.Value .Select(x => DicomFileAndPath.SafeCreate(new MemoryStream(x), string.Empty)) .ToList(); result.Add((item.Key, DicomFolderContents.Build(fileAndPaths))); } return(result); }
/// <summary> /// Convert from Nifti format to DICOM-RT format. /// </summary> /// <param name="niftiFilename">Nifti input filename.</param> /// <param name="referenceSeries">Path to folder of reference DICOM files.</param> /// <param name="structureNames">Names for each structure.</param> /// <param name="structureColors">Colors for each structure, defaults to red if this array smaller than list of structures.</param> /// <param name="fillHoles">Flags to enable fill holes for each structure, defaults to false this array smaller than list of structures..</param> /// <param name="dcmFilename">Target output file.</param> public static void ConvertNiftiToDicom( string niftiFilename, string referenceSeries, string[] structureNames, RGBColorOption?[] structureColors, bool?[] fillHoles, ROIInterpretedType[] roiInterpretedTypes, string dcmFilename, string modelNameAndVersion, string manufacturer, string interpreter) { var sourceVolume = MedIO.LoadNiftiAsByte(niftiFilename); Trace.TraceInformation($"Loaded NIFTI from {niftiFilename}"); var labels = VolumeMetadataMapper.MultiLabelMapping(sourceVolume, structureNames.Length); var volumesWithMetadata = VolumeMetadataMapper.MapVolumeMetadata(labels, structureNames, structureColors, fillHoles, roiInterpretedTypes); var referenceVolume = DicomSeriesHelpers.LoadVolume( DicomFolderContents.Build( Directory.EnumerateFiles(referenceSeries).Select(x => DicomFileAndPath.SafeCreate(x)).ToList() )); var outputEncoder = new DicomRTStructOutputEncoder(); var outputStructureBytes = outputEncoder.EncodeStructures( volumesWithMetadata, new Dictionary <string, MedicalVolume>() { { "", referenceVolume } }, modelNameAndVersion, manufacturer, interpreter); File.WriteAllBytes(dcmFilename, outputStructureBytes.Array); }
/// <summary> /// Loads a medical volume from a set of Dicom files, including the RT structures if present. /// The Dicom files are expected to contain a single Dicom Series. /// </summary> /// <param name="dicomFileAndPath">The Dicom files to load from</param> /// <param name="maxPixelSizeRatioMR">The maximum allowed aspect ratio for pixels, if the volume is an MR scan.</param> /// <returns></returns> /// <exception cref="InvalidDataException">If the volume cannot be loaded from the Dicom files</exception> public static MedicalVolume MedicalVolumeFromDicom(IReadOnlyList <DicomFileAndPath> dicomFileAndPath, double maxPixelSizeRatioMR = ModerateGeometricAcceptanceTest.DefaultMaxPixelSizeRatioMR) { var acceptanceTest = new ModerateGeometricAcceptanceTest("Non Square pixels", "Unsupported Orientation", maxPixelSizeRatioMR); var volumeLoaderResult = MedIO.LoadAllDicomSeries( DicomFolderContents.Build(dicomFileAndPath), acceptanceTest, loadStructuresIfExists: true, supportLossyCodecs: false); if (volumeLoaderResult.Count != 1) { throw new InvalidDataException($"Unable to load the scan from the Dicom files: There should be exactly 1 series, but got {volumeLoaderResult.Count}."); } var result = volumeLoaderResult[0]; if (result.Volume == null) { throw new InvalidDataException("An exception was thrown trying to load the scan from the Dicom files.", result.Error); } return(result.Volume); }
public void RtStructOutputEncoder_SuccessWithValidInputs() { var labels = VolumeMetadataMapper.MultiLabelMapping(sourceVolume, NumValidLabels); var volumesWithMetadata = VolumeMetadataMapper.MapVolumeMetadata(labels, StructureNames, StructureColors, FillHoles, ROIInterpretedTypes); var referenceVolume = DicomSeriesHelpers.LoadVolume(DicomFolderContents.Build( Directory.EnumerateFiles(TestDicomVolumeLocation).Select(x => DicomFileAndPath.SafeCreate(x)).ToList() )); var outputEncoder = new DicomRTStructOutputEncoder(); var outputStructureBytes = outputEncoder.EncodeStructures( volumesWithMetadata, new Dictionary <string, MedicalVolume>() { { "", referenceVolume } }, "modelX:1", "manufacturer", "interpreter"); var dcm = DicomFile.Open(new MemoryStream(outputStructureBytes.Array)); // Check the output format (should be RT struct) Assert.AreEqual(DicomUID.RTStructureSetStorage, dcm.FileMetaInfo.MediaStorageSOPClassUID); // Check stuff in StructureSet ROI sequence var structSetRois = dcm.Dataset.GetSequence(DicomTag.StructureSetROISequence).Items; var iter = StructureNames.GetEnumerator(); iter.MoveNext(); var origReferencedFrameOfReference = referenceVolume.Identifiers.First().FrameOfReference.FrameOfReferenceUid; foreach (var roi in structSetRois) { // Verify that names in the generated DICOM Rt structure are the ones we've supplied Assert.AreEqual(iter.Current, roi.GetString(DicomTag.ROIName)); iter.MoveNext(); // Verify that this roi references the same frame of reference as the original image Assert.AreEqual(roi.GetString(DicomTag.ReferencedFrameOfReferenceUID), origReferencedFrameOfReference); } // Check stuff in ROI Contour sequence var roiContours = dcm.Dataset.GetSequence(DicomTag.ROIContourSequence).Items; var iterColors = 0; var sopInstanceUIDs = referenceVolume.Identifiers.Select(x => x.Image.SopCommon.SopInstanceUid); foreach (var contourSequence in roiContours) { // Verify that colors in the generated contour sequence are the ones we've supplied var currentColor = StructureColors[iterColors].Value; var currentColorString = string.Format("{0}\\{1}\\{2}", currentColor.R, currentColor.G, currentColor.B); Assert.AreEqual(contourSequence.GetString(DicomTag.ROIDisplayColor), currentColorString); iterColors++; // Verify that all contour types are closed planar Assert.IsTrue(contourSequence.GetSequence(DicomTag.ContourSequence).Items.All( x => x.GetString(DicomTag.ContourGeometricType) == "CLOSED_PLANAR")); // Verify that for all contours there exists a SOP Instance UID in the original series Assert.IsTrue(contourSequence.GetSequence(DicomTag.ContourSequence).Items.All( x => sopInstanceUIDs.Contains(x.GetSequence(DicomTag.ContourImageSequence).Items[0].GetString(DicomTag.ReferencedSOPInstanceUID)))); } }