public void LoadingInvalidFile(string file) { var ex = Assert.Catch(() => MedIO.LoadNiftiAsFloat(file), "Loading a non-existent file should fail"); Assert.IsTrue(ex.Message.Contains(file)); Assert.IsTrue(ex.Message.Contains("Could not find file")); }
public static MedicalVolume LoadVolume(DicomFolderContents dicomFolder) { dicomFolder = dicomFolder ?? throw new ArgumentNullException(nameof(dicomFolder)); var volumeLoaderResult = MedIO.LoadAllDicomSeries( dicomFolder, new ModerateGeometricAcceptanceTest("Non Square pixels", "Unsupported Orientation"), loadStructuresIfExists: false, supportLossyCodecs: false); if (volumeLoaderResult.Count > 1) { throw new Exception($"More than 1 volume loaded for path {dicomFolder.FolderPath}"); } var volumeLoaderFirstResult = volumeLoaderResult.First(); if (volumeLoaderFirstResult.Error != null) { throw volumeLoaderFirstResult.Error; } if (volumeLoaderFirstResult.Warnings != null && volumeLoaderFirstResult.Warnings.Count > 0) { throw new Exception(string.Join(",", volumeLoaderFirstResult.Warnings)); } return(volumeLoaderFirstResult.Volume); }
public void LoadImageAndContours() { var acceptanceTests = new ModerateGeometricAcceptanceTest(string.Empty, string.Empty); var medicalVolume = MedIO.LoadAllDicomSeriesInFolderAsync(TestData.GetFullImagesPath(@"sample_dicom"), acceptanceTests).Result.First().Volume; var volumes = new Dictionary <string, ContourStatistics>() { { "Bladder", new ContourStatistics(95.87, -1000.0, 0) }, { "Femur_L", new ContourStatistics(205.04, -1000.0, 0) }, }; var readOnlyVolume = new ReadOnlyVolume3D <short>(medicalVolume.Volume); for (int i = 0; i < medicalVolume.Struct.Contours.Count; i++) { var contour = medicalVolume.Struct.Contours[i]; var name = contour.StructureSetRoi.RoiName; var contourVolume = contour.Contours.ToVolume3D(medicalVolume.Volume); Console.WriteLine($"Starting checks for contour {name}"); var contourStatistics = ContourStatistics.FromVolumeAndMask(readOnlyVolume, contourVolume); Assert.AreEqual(volumes[contour.StructureSetRoi.RoiName].SizeInCubicCentimeters, contourStatistics.SizeInCubicCentimeters, 1e-1, $"Size for contour {name}"); Assert.AreEqual(volumes[contour.StructureSetRoi.RoiName].VoxelValueMean, contourStatistics.VoxelValueMean, 1e-1, $"Mean for contour {name}"); Assert.AreEqual(volumes[contour.StructureSetRoi.RoiName].VoxelValueStandardDeviation, contourStatistics.VoxelValueStandardDeviation, 1e-1, $"Std for contour {name}"); } }
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")); }
public void WritingInvalidExtension() { var file = @"C:\temp\nonsense.ni"; var ex = Assert.Catch(() => MedIO.SaveNifti(image, file)); Assert.IsTrue(ex.Message.Contains("filenames must end with")); Assert.IsTrue(ex.Message.Contains(file)); }
public void WritingInvalidFile() { var file = "XYZ:/somegarbage.nii.gz"; var ex = Assert.Catch(() => MedIO.SaveNifti(image, file), "Writing to a nonsensical path should fail"); Assert.IsTrue(ex.Message.Contains(file)); Assert.IsTrue(ex.Message.Contains("writing to file")); }
public void NiftiFileNamesValid(string fileName, NiftiCompression expected) { Assert.AreEqual(expected, MedIO.GetNiftiCompressionOrFail(fileName)); Assert.IsTrue(MedIO.IsNiftiFile(fileName)); var extension = MedIO.GetNiftiExtension(expected); Assert.IsTrue(fileName.EndsWith(extension)); }
public void Setup() { sourceVolume = MedIO.LoadNiftiAsByte(TestNiftiSegmentationLocation); Trace.TraceInformation($"Loaded NIFTI from {TestNiftiSegmentationLocation}"); Assert.AreEqual(NumValidLabels, FillHoles.Length); Assert.AreEqual(NumValidLabels, StructureColors.Length); Assert.AreEqual(NumValidLabels, StructureNames.Length); }
public void NiftiSaveShort(NiftiCompression niftiCompression) { var volume = TestHelpers.SingleSlice(Enumerable.Range(0, 9).Select(b => (short)b).ToArray()); var file = TestHelpers.CreateTempNiftiName(niftiCompression); MedIO.SaveNifti(volume, file); var loadedVolume = MedIO.LoadNiftiAsShort(file); VolumeAssert.AssertVolumesMatch(volume, loadedVolume); }
public void WritingToFileAndReading(string extension) { var file = Path.GetTempFileName() + extension; MedIO.SaveNifti(image, file); var image2 = MedIO.LoadNiftiAsByte(file); Assert.AreEqual(image.DimX, image2.DimX); Assert.AreEqual(image.DimY, image2.DimY); Assert.AreEqual(image.DimZ, image2.DimZ); File.Delete(file); }
public void LoadNiftiInUInt16Format() { var filename = TestData.GetFullImagesPath("vol_uint16.nii.gz"); var niftiInUShort = MedIO.LoadNiftiInUShortFormat(filename); var distinctValues = niftiInUShort.Array.Distinct().ToArray(); var expected = new ushort[] { 0, 1, 2 }; Assert.AreEqual(expected, distinctValues, "The Nifti file contains 2 contours, should hence have 3 distinct values"); var niftiCastToByte = MedIO.LoadNiftiAsByte(filename); var niftiCastToShort = MedIO.LoadNiftiAsShort(filename); Assert.AreEqual(niftiCastToByte.Length, niftiCastToShort.Length); Assert.AreEqual(niftiCastToByte.Length, niftiInUShort.Length); Assert.AreEqual(new ushort[] { 0, 1, 2 }, niftiCastToShort.Array.Distinct().ToArray()); Assert.AreEqual(new byte[] { 0, 1, 2 }, niftiCastToByte.Array.Distinct().ToArray()); }
public void ReadniftiAndSave() { var tempRandomNiftyFilePath = GetTempNiftiFileName(); var medimage3D = MedIO.LoadNiftiAsFloat(TestData.GetFullImagesPath(@"vol_int16.nii.gz")); Assert.IsNotNull(medimage3D); Assert.AreEqual(3, medimage3D.Dimensions); MedIO.SaveNifti(medimage3D, tempRandomNiftyFilePath); var savedMedimage3D = MedIO.LoadNiftiAsFloat(tempRandomNiftyFilePath); Assert.AreEqual(medimage3D.Origin, savedMedimage3D.Origin); Assert.AreEqual(medimage3D.Array, savedMedimage3D.Array); }
public void LoadingInvalidExtensions() { const string validExtension = ".nii"; const string invalidExtension = ".ni"; var basePath = Path.GetTempFileName(); var validPath = basePath + validExtension; var invalidPath = basePath + invalidExtension; // Can't save with invalid extension, so save with valid extension and rename. MedIO.SaveNifti(image, validPath); File.Move(validPath, invalidPath); var ex = Assert.Catch(() => MedIO.LoadNiftiAsFloat(invalidPath)); Assert.IsTrue(ex.Message.Contains("filenames must end with")); Assert.IsTrue(ex.Message.Contains(invalidPath)); }
/// <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> /// Creates a unique file name for a compressed Nifti file in the user's temp folder. /// </summary> /// <param name="fileNamePrefix">If provided, the file name itself will start with this prefix, /// then followed by a random part./param> /// <returns></returns> public static string CreateTempNiftiName(NiftiCompression niftiCompression, string fileNamePrefix = null) { var prefix = RandomFileNameInTempFolder(fileNamePrefix); return(prefix + MedIO.GetNiftiExtension(niftiCompression)); }
public void NiftiFileNamesInvalid(string fileName) { Assert.Throws <ArgumentException>(() => MedIO.GetNiftiCompressionOrFail(fileName)); Assert.IsFalse(MedIO.IsNiftiFile(fileName)); }
public void NiftiFileExtension(NiftiCompression compression) { var extension = MedIO.GetNiftiExtension(compression); Assert.AreEqual(compression, MedIO.GetNiftiCompression(extension)); }
/// <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}"); } }
public void LoadAndSaveMedicalVolumeTest() { var directory = TestData.GetFullImagesPath("sample_dicom"); var acceptanceTests = new StrictGeometricAcceptanceTest(string.Empty, string.Empty); var medicalVolume = MedIO.LoadAllDicomSeriesInFolderAsync(directory, acceptanceTests).Result.First().Volume; Directory.CreateDirectory(_tempFolder); Console.WriteLine($"Directory created {_tempFolder}"); Volume3D <byte>[] contourVolumes = new Volume3D <byte> [medicalVolume.Struct.Contours.Count]; for (int i = 0; i < medicalVolume.Struct.Contours.Count; i++) { contourVolumes[i] = new Volume3D <byte>( medicalVolume.Volume.DimX, medicalVolume.Volume.DimY, medicalVolume.Volume.DimZ, medicalVolume.Volume.SpacingX, medicalVolume.Volume.SpacingY, medicalVolume.Volume.SpacingZ, medicalVolume.Volume.Origin, medicalVolume.Volume.Direction); contourVolumes[i].Fill(medicalVolume.Struct.Contours[i].Contours, (byte)1); } // Calculate contours based on masks var rtContours = new List <RadiotherapyContour>(); for (int i = 0; i < medicalVolume.Struct.Contours.Count; i++) { var contour = medicalVolume.Struct.Contours[i]; var contourForAllSlices = GetContoursForAllSlices(contourVolumes[i]); var rtcontour = new DicomRTContour( contour.DicomRtContour.ReferencedRoiNumber, contour.DicomRtContour.RGBColor, contourForAllSlices); DicomRTStructureSetROI rtROIstructure = new DicomRTStructureSetROI( contour.StructureSetRoi.RoiNumber, contour.StructureSetRoi.RoiName, string.Empty, ERoiGenerationAlgorithm.Semiautomatic); DicomRTObservation observation = new DicomRTObservation( contour.DicomRtObservation.ReferencedRoiNumber, new DicomPersonNameConverter("Left^Richard^^Dr"), ROIInterpretedType.EXTERNAL); rtContours.Add(new RadiotherapyContour(rtcontour, rtROIstructure, observation)); } var rtStructureSet = new RadiotherapyStruct( medicalVolume.Struct.StructureSet, medicalVolume.Struct.Patient, medicalVolume.Struct.Equipment, medicalVolume.Struct.Study, medicalVolume.Struct.RTSeries, rtContours); MedIO.SaveMedicalVolumeAsync(_tempFolder, new MedicalVolume( medicalVolume.Volume, medicalVolume.Identifiers, medicalVolume.FilePaths, rtStructureSet)).Wait(); var medicalVolume2 = MedIO.LoadAllDicomSeriesInFolderAsync(_tempFolder, acceptanceTests).Result.First().Volume; foreach (var radiotherapyContour in medicalVolume2.Struct.Contours.Where(x => x.DicomRtContour.DicomRtContourItems.First().GeometricType == "CLOSED_PLANAR")) { var savedContour = medicalVolume2.Struct.Contours.First(x => x.StructureSetRoi.RoiName == radiotherapyContour.StructureSetRoi.RoiName); foreach (var contour in radiotherapyContour.Contours) { Assert.AreEqual(radiotherapyContour.DicomRtObservation.ROIInterpretedType, ROIInterpretedType.EXTERNAL); for (int i = 0; i < contour.Value.Count; i++) { if (!contour.Value[i].Equals(savedContour.Contours.ContoursForSlice(contour.Key)[i])) { Console.WriteLine(radiotherapyContour.StructureSetRoi.RoiName); Assert.Fail(); } } } } }
/// <summary> /// Common code for planar Nifti to DICOM-RT tests. /// </summary> /// <param name="sourceMaskFilename">Source mask filename.</param> /// <param name="expectedMaskFilename">Optional expected mask filename if not the same as source.</param> /// <param name="fillHoles">Fill holes flag.</param> /// <param name="debugFolderName">Optional folder name for debug images.</param> public static void CommonTestNiftiToDicomFillHoles(string sourceMaskFilename, string expectedMaskFilename, bool?fillHoles, string debugFolderName) { // Create the Nifti file as a NxMx1 volume var sourceMaskVolume2D = ExtractContourTests.LoadMask(sourceMaskFilename); Assert.IsTrue(sourceMaskVolume2D.Array.Any(x => x == 0)); Assert.IsTrue(sourceMaskVolume2D.Array.Any(x => x == 1)); var dimZ = 2; var sourceVolume3D = sourceMaskVolume2D.Extrude(dimZ, 1.0); var niftiFile = TestHelpers.CreateTempNiftiName(NiftiCompression.GZip); MedIO.SaveNifti(sourceVolume3D, niftiFile); // Create the reference DICOM files. var referenceDicomFolder = Path.Combine(TestContext.CurrentContext.TestDirectory, "ReferenceDicom"); if (Directory.Exists(referenceDicomFolder)) { Directory.Delete(referenceDicomFolder, recursive: true); Thread.Sleep(1000); } Directory.CreateDirectory(referenceDicomFolder); var scan = new Volume3D <short>(sourceVolume3D.DimX, sourceVolume3D.DimY, sourceVolume3D.DimZ); foreach (var index in scan.Array.Indices()) { scan.Array[index] = (short)index; } var seriesDescription = "description"; var patientID = DicomUID.Generate().UID; var studyInstanceID = DicomUID.Generate().UID; var dicomFiles = NiiToDicomHelpers.ScanToDicomInMemory(scan, ImageModality.CT, seriesDescription, patientID, studyInstanceID, null); var dicomFilesOnDisk = new List <string>(); foreach (var dicomFile in dicomFiles) { dicomFilesOnDisk.Add(dicomFile.SaveToFolder(referenceDicomFolder)); } // Turn the expected mask into a volume. var expectedNiftiFile = string.Empty; if (!string.IsNullOrEmpty(expectedMaskFilename)) { var expectedMask = ExtractContourTests.LoadMask(expectedMaskFilename); var expectedVolume = expectedMask.Extrude(dimZ, 1.0); expectedNiftiFile = TestHelpers.CreateTempNiftiName(NiftiCompression.GZip); MedIO.SaveNifti(expectedVolume, expectedNiftiFile); } else { // Expected is the same as the source. expectedNiftiFile = niftiFile; } DoTestNiftiToDicom( niftiFile, referenceDicomFolder, new[] { "background", "foreground" }, new RGBColorOption?[] { new RGBColorOption(0xFF, 0x00, 0x00), new RGBColorOption(0x00, 0xFF, 0x00) }, new[] { fillHoles, fillHoles }, new[] { ROIInterpretedType.ORGAN, ROIInterpretedType.CTV }, debugFolderName, true, expectedNiftiFile); }