public void LoadMedicalVolumeTest()
        {
            var dir             = TestData.GetFullImagesPath(@"sample_dicom");
            var acceptanceTests = new ModerateGeometricAcceptanceTest(string.Empty, string.Empty);
            var medicalVolumes  = MedIO.LoadAllDicomSeriesInFolderAsync(dir, acceptanceTests).Result;

            Assert.AreEqual(1, medicalVolumes.Count);

            var medicalVolume = medicalVolumes.First().Volume;

            Assert.IsTrue(medicalVolume.Struct.Contours.Any(x => x.StructureSetRoi.RoiName == "Bladder"));
            Assert.IsTrue(medicalVolume.Struct.Contours.Any(x => x.StructureSetRoi.RoiName == "Femur_L"));

            RtStructWriter.SaveRtStruct(_tempfile, medicalVolume.Struct);

            DicomFile file = DicomFile.Open(_tempfile);

            var identifiers = medicalVolume.Identifiers.First();

            var reloaded = RtStructReader.LoadContours(
                file.Dataset,
                medicalVolume.Volume.Transform.DicomToData,
                identifiers.Series.SeriesInstanceUid,
                identifiers.Study.StudyInstanceUid).Item1;

            Assert.IsTrue(reloaded.Contours.Any(x => x.StructureSetRoi.RoiName == "Bladder"));
            Assert.IsTrue(reloaded.Contours.Any(x => x.StructureSetRoi.RoiName == "Femur_L"));
        }
Пример #2
0
 public static Tuple <RadiotherapyStruct, string> LoadStruct(string rtfile, Transform3 dicomToData, string studyUId, string seriesUId)
 {
     try
     {
         var file = DicomFile.Open(rtfile);
         return(RtStructReader.LoadContours(file.Dataset, dicomToData, seriesUId, studyUId, true));
     }
     catch (Exception ex)
     {
         throw new ArgumentException($"RT file {rtfile} cannot be loaded - {ex.Message}");
     }
 }
Пример #3
0
        /// <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>()));
            }
        }
        public void VolumeToDicomAndBack()
        {
            var outputFolder = Path.Combine(TestContext.CurrentContext.TestDirectory, "DicomOutput");

            if (Directory.Exists(outputFolder))
            {
                Directory.Delete(outputFolder, recursive: true);
                Thread.Sleep(1000);
            }
            Directory.CreateDirectory(outputFolder);
            var scan = new Volume3D <short>(5, 5, 5);

            foreach (var index in scan.Array.Indices())
            {
                scan.Array[index] = (short)index;
            }
            // Create 3 structures, each with a different color
            var masks  = new List <ContourRenderingInformation>();
            var colors = new[]
            {
                new RGBColor(255, 0, 0),
                new RGBColor(0, 255, 0),
                new RGBColor(0, 0, 255),
            };

            foreach (var index in Enumerable.Range(0, 3))
            {
                var mask = scan.CreateSameSize <byte>();
                mask[index + 1, index + 1, index + 1] = 1;
                masks.Add(new ContourRenderingInformation($"structure_{index}", colors[index], mask));
            }
            var seriesDescription = "description";
            var patientId         = DicomUID.Generate().UID;
            var studyId           = DicomUID.Generate().UID;
            var dicomFiles        = NiiToDicomHelpers.ScanAndContoursToDicom(scan, ImageModality.CT, masks,
                                                                             seriesDescription, patientId, studyId);
            // Write to disk, so that we can load it into the App as well
            var dicomFilesOnDisk = new List <string>();

            foreach (var dicomFile in dicomFiles)
            {
                dicomFilesOnDisk.Add(dicomFile.SaveToFolder(outputFolder));
            }
            // Test if the first returned Dicom file is really the RTStruct
            var rtStructFromFile = RtStructReader.LoadContours(dicomFilesOnDisk[0], scan.Transform.DicomToData);

            Assert.IsNotNull(rtStructFromFile);
            Assert.AreEqual(masks.Count, rtStructFromFile.Item1.Contours.Count);
            var fromDisk = NiiToDicomHelpers.MedicalVolumeFromDicomFolder(outputFolder);

            VolumeAssert.AssertVolumesMatch(scan, fromDisk.Volume, "Loaded scan does not match");
            Assert.AreEqual(seriesDescription, fromDisk.Identifiers.First().Series.SeriesDescription);
            Assert.AreEqual(patientId, fromDisk.Identifiers.First().Patient.Id);
            Assert.AreEqual(studyId, fromDisk.Identifiers.First().Study.StudyInstanceUid);
            foreach (var index in Enumerable.Range(0, fromDisk.Struct.Contours.Count))
            {
                var loadedMask = fromDisk.Struct.Contours[index].Contours.ToVolume3D(scan);
                VolumeAssert.AssertVolumesMatch(masks[index].Contour.ToVolume3D(scan), loadedMask, $"Loaded mask {index}");
                Assert.AreEqual(masks[index].Name, fromDisk.Struct.Contours[index].StructureSetRoi.RoiName, $"Loaded mask name {index}");
            }

            // Now test if we can ZIP up all the Dicom files, and read them back in.
            var zippedDicom   = ZippedDicom.DicomFilesToZipArchive(dicomFiles);
            var dicomFromZip  = ZippedDicom.DicomFilesFromZipArchive(zippedDicom);
            var volumeFromZip = NiiToDicomHelpers.MedicalVolumeFromDicom(dicomFromZip.ToList());

            VolumeAssert.AssertVolumesMatch(scan, volumeFromZip.Volume, "Scan from Zip archive does not match");
            Assert.AreEqual(masks.Count, volumeFromZip.Struct.Contours.Count, "RtStructs from Zip do not match");
        }
Пример #5
0
        /// <summary>
        /// Test Nifti file to DICOM-RT translation.
        /// </summary>
        /// <param name="niftiFilename">Source Nifti file.</param>
        /// <param name="referenceSeries">Reference DICOM series folder.</param>
        /// <param name="structureNames">List of structure names for DICOM-RT.</param>
        /// <param name="structureColors">List of structure colours for DICOM-RT.</param>
        /// <param name="fillHoles">List of fill hole flags for DICOM-RT.</param>
        /// <param name="roiInterpretedType">List of roiInterpretedType for DICOM-RT.</param>
        /// <param name="debugFolderName">If present, create a full set of debug images.</param>
        /// <param name="testVolumesMatch">If true, check the volumes match.</param>
        /// <param name="expectedNiftiFilename">Expect volume to match Nifti file.</param>
        public static void DoTestNiftiToDicom(
            string niftiFilename,
            string referenceSeries,
            string[] structureNames,
            RGBColorOption?[] structureColors,
            bool?[] fillHoles,
            ROIInterpretedType[] roiInterpretedTypes,
            string debugFolderName,
            bool testVolumesMatch,
            string expectedNiftiFilename)
        {
            var outputFileName = Path.GetRandomFileName() + ".dcm";

            RTConverters.ConvertNiftiToDicom(
                niftiFilename,
                referenceSeries,
                structureNames,
                structureColors,
                fillHoles,
                roiInterpretedTypes,
                outputFileName,
                "modelX:1",
                "manufacturer",
                "interpreter");

            // Open the newly created DICOM-RT file
            var dicomRTFile = DicomFile.Open(outputFileName);

            // Get the medical volume from the reference
            var acceptanceTests         = new ModerateGeometricAcceptanceTest(string.Empty, string.Empty);
            var referenceMedicalVolumes = MedIO.LoadAllDicomSeriesInFolderAsync(referenceSeries, acceptanceTests).Result;

            Assert.AreEqual(1, referenceMedicalVolumes.Count);

            var referenceMedicalVolume = referenceMedicalVolumes.First().Volume;

            var referenceIdentifiers = referenceMedicalVolume.Identifiers.First();

            // Extract the RTStruct from the DICOM-RT file
            var reloaded = RtStructReader.LoadContours(
                dicomRTFile.Dataset,
                referenceMedicalVolume.Volume.Transform.DicomToData,
                referenceIdentifiers.Series.SeriesInstanceUid,
                referenceIdentifiers.Study.StudyInstanceUid);

            Assert.IsNotNull(reloaded);

            var reloadedRTStruct = reloaded.Item1;

            Assert.AreEqual(referenceIdentifiers.Patient.Id, reloadedRTStruct.Patient.Id);
            Assert.AreEqual(referenceIdentifiers.Study.StudyInstanceUid, reloadedRTStruct.Study.StudyInstanceUid);
            Assert.AreEqual(DicomRTSeries.RtModality, reloadedRTStruct.RTSeries.Modality);

            // Load the nifti file
            var sourceVolume = MedIO.LoadNiftiAsByte(expectedNiftiFilename);

            // Split this volume from segment id to a set of mask volumes
            var labels = VolumeMetadataMapper.MultiLabelMapping(sourceVolume, structureNames.Length);

            // Make tuples of mask volume, color, names, fill holes.
            var volumesWithMetadata = VolumeMetadataMapper.MapVolumeMetadata(labels, structureNames, structureColors, fillHoles, roiInterpretedTypes).ToArray();

            Assert.AreEqual(structureNames.Length, reloadedRTStruct.Contours.Count);

            for (int i = 0; i < reloadedRTStruct.Contours.Count; i++)
            {
                var contourVolumes = new Volume3D <byte>(
                    referenceMedicalVolume.Volume.DimX,
                    referenceMedicalVolume.Volume.DimY,
                    referenceMedicalVolume.Volume.DimZ,
                    referenceMedicalVolume.Volume.SpacingX,
                    referenceMedicalVolume.Volume.SpacingY,
                    referenceMedicalVolume.Volume.SpacingZ,
                    referenceMedicalVolume.Volume.Origin,
                    referenceMedicalVolume.Volume.Direction);

                contourVolumes.Fill(reloadedRTStruct.Contours[i].Contours, (byte)1);

                var v = volumesWithMetadata[i];

                if (!string.IsNullOrWhiteSpace(debugFolderName))
                {
                    for (var z = 0; z < referenceMedicalVolume.Volume.DimZ; z++)
                    {
                        var actualFileName = Path.Combine(debugFolderName, "actual", $"slice_{v.name}_{z}.png");
                        var actualSlice    = contourVolumes.Slice(SliceType.Axial, z);
                        actualSlice.SaveBinaryMaskToPng(actualFileName);

                        var expectedFileName = Path.Combine(debugFolderName, "expected", $"slice_{v.name}_{z}.png");
                        var expectedSlice    = v.volume.Slice(SliceType.Axial, z);
                        expectedSlice.SaveBinaryMaskToPng(expectedFileName);
                    }
                }

                if (testVolumesMatch)
                {
                    VolumeAssert.AssertVolumesMatch(v.volume, contourVolumes, $"Loaded mask {i}");
                }

                Assert.AreEqual(v.name, reloadedRTStruct.Contours[i].StructureSetRoi.RoiName, $"Loaded mask name {i}");

                var expectedColor = Tuple.Create(v.color.R, v.color.G, v.color.B);
                Assert.AreEqual(expectedColor, reloadedRTStruct.Contours[i].DicomRtContour.RGBColor, $"Loaded color {i}");
            }
        }