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}"); } }
/// <summary> /// Computes an instance of contour statistics, <see cref="ContourStatistics"/>, from those voxels /// of the <paramref name="originalVolume"/> where the mask volume attains the foreground value. /// </summary> /// <param name="image"></param> /// <param name="mask"></param> /// <param name="foreground"></param> /// <returns></returns> public static ContourStatistics FromVolumeAndMask(ReadOnlyVolume3D <short> image, Volume3D <byte> mask, byte foreground = 1) { image = image ?? throw new ArgumentNullException(nameof(image)); mask = mask ?? throw new ArgumentNullException(nameof(mask)); if (mask.Length != image.Length) { throw new ArgumentException("Image and mask must have the same length", nameof(image)); } double mean = 0; double variance = 0; double sum = 0; ulong count = 0; // StreamingStatistics.MeanVariance for (int i = 0; i < image.Length; i++) { if (mask[i] == foreground) { count++; double xi = image[i]; sum += xi; var diff = (count * xi) - sum; if (count > 1) { variance += (diff * diff) / (count * (count - 1)); } mean += (xi - mean) / count; } } var volumeSizeInCC = count * image.VoxelVolume / 1000d; var standardDeviation = count == 0 ? 0 : Math.Sqrt(variance / count); return(new ContourStatistics(volumeSizeInCC, mean, standardDeviation)); }