/// <summary> /// Converts a multi-label map to a set of binary volumes. If a voxel has value v in the /// input image, v >= 1, then the (v-1)th result volume will have set that voxel to 1. /// Put another way: The i.th result volume will have voxels non-zero wherever the input /// volume had value (i+1). /// </summary> /// <param name="image">A multi-label input volume.</param> /// <param name="numOutputMasks">The number of result volumes that will be generated. /// This value must be at least equal to the maximum voxel value in the input volume.</param> /// <returns></returns> public static Volume3D <byte>[] MultiLabelMapping(Volume3D <byte> image, int numOutputMasks) { var result = new Volume3D <byte> [numOutputMasks]; for (var i = 0; i < numOutputMasks; i++) { result[i] = image.CreateSameSize <byte>(); } for (var i = 0; i < image.Length; ++i) { if (image[i] != 0 && image[i] <= numOutputMasks) { result[image[i] - 1][i] = 1; } } return(result); }
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"); }