/// <summary> /// Creates a new instance of the class, from a binary mask. The contour is extracted from the mask /// using the default settings: Background 0, foreground 1, axial slices. /// </summary> /// <param name="name">The name of the anatomical structure that is represented by the contour.</param> /// <param name="color">The color that should be used to render the contour.</param> /// <param name="mask">The binary mask that represents the anatomical structure.</param> /// <exception cref="ArgumentNullException">The contour name or mask was null.</exception> public ContourRenderingInformation(string name, RGBColor color, Volume3D <byte> mask) { Name = name ?? throw new ArgumentNullException(nameof(name)); Color = color; mask = mask ?? throw new ArgumentNullException(nameof(mask)); Contour = ExtractContours.ContoursWithHolesPerSlice(mask); }
public void FillPolygonTests7() { var dimX = 5; var dimY = 5; var expectedArray = new byte[] { 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, }; var volume2D = new Volume2D <byte>(expectedArray, 5, 5, 1, 1, new Point2D(), new Matrix2()); var extractContour = ExtractContours.PolygonsFilled(volume2D); var output1 = new byte[dimX * dimY]; var polygon = extractContour.First().Points.Select(x => CreatePoint(x.X + 0.00002, x.Y - 0.00001)).ToArray(); FillPolygon.Fill(polygon, output1, dimX, dimY, 0, 0, (byte)1); for (var i = 0; i < output1.Length; i++) { Assert.AreEqual(expectedArray[i], output1[i]); } }
/// <summary> /// Extracts contours from all slices of the given volume, searching for the given foreground value. /// Contour extraction will take holes into account. /// </summary> /// <param name="volume"></param> /// <param name="foregroundId">The voxel value that should be used in the contour search as foreground.</param> /// <param name="axialSmoothingType">The smoothing that should be applied when going from a point polygon to /// contours. This will only affect axial slice, for other slice types no smoothing will be applied. /// <param name="sliceType">The type of slice that should be used for contour extraction.</param> /// <param name="filterEmptyContours">If true, contours with no points are not extracted.</param> /// <param name="regionOfInterest"></param> /// <returns></returns> public static ContoursPerSlice ContoursWithHolesPerSlice( this Volume3D <byte> volume, byte foregroundId = ModelConstants.MaskForegroundIntensity, SliceType sliceType = SliceType.Axial, bool filterEmptyContours = true, Region3D <int> regionOfInterest = null, ContourSmoothingType axialSmoothingType = ContourSmoothingType.Small) => ExtractContours.ContoursWithHolesPerSlice(volume, foregroundId, sliceType, filterEmptyContours, regionOfInterest, axialSmoothingType);
/// <summary> /// Extracts axial contours for the foreground values in the given volume. After extracting the contours, /// a check is conducted if the contours truthfully represent the actual volume. This will throw exceptions /// for example if the incoming volume has "doughnut shape" structures. /// </summary> /// <param name="volume">The mask volume to extract contours from.</param> /// <param name="maxAbsoluteDifference">The maximum allowed difference in foreground voxels when going /// from mask to contours to mask.</param> /// <param name="maxRelativeDifference">The maximum allowed relative in foreground voxels (true - rendered)/true /// when going from mask to contours to mask.</param> /// <returns></returns> public static ContoursPerSlice ExtractContoursAndCheck(this Volume3D <byte> volume, int?maxAbsoluteDifference = 10, double?maxRelativeDifference = 0.15 ) { var contours = ExtractContours.ContoursWithHolesPerSlice( volume, foregroundId: ModelConstants.MaskForegroundIntensity, sliceType: SliceType.Axial, filterEmptyContours: true, regionOfInterest: null, axialSmoothingType: ContourSmoothingType.Small); var slice = new byte[volume.DimX * volume.DimY]; void ClearSlice() { for (var index = 0; index < slice.Length; index++) { slice[index] = ModelConstants.MaskBackgroundIntensity; } } foreach (var contourPerSlice in contours) { var indexZ = contourPerSlice.Key; var offsetZ = indexZ * volume.DimXY; ClearSlice(); foreach (var contour in contourPerSlice.Value) { FillPolygon.Fill(contour.ContourPoints, slice, volume.DimX, volume.DimY, 1, 0, ModelConstants.MaskForegroundIntensity); } var true1 = 0; var rendered1 = 0; for (var index = 0; index < slice.Length; index++) { if (volume[offsetZ + index] == ModelConstants.MaskForegroundIntensity) { true1++; } if (slice[index] == ModelConstants.MaskForegroundIntensity) { rendered1++; } } CheckContourRendering(true1, rendered1, maxAbsoluteDifference, maxRelativeDifference, $"Slice z={indexZ}"); } ; return(contours); }
public void FillPolygonTestsRandomCheckTermination() { var dimX = 2000; int seed = Guid.NewGuid().GetHashCode(); Console.WriteLine($"Seed {seed}"); var random = new Random(seed); var byteArray = Enumerable.Range(0, dimX * dimX).Select(_ => (byte)random.Next(0, 2)).ToArray(); var volume2D = new Volume2D <byte>(byteArray, dimX, dimX, 1, 1, new Point2D(), new Matrix2()); var extractContour = ExtractContours.PolygonsFilled(volume2D); Console.WriteLine($"Contours count {extractContour.Count}"); Assert.IsTrue(extractContour.Count > 0); }
/// <summary> /// Extracts the contours around all voxel values in the volume that have the given foreground value. /// All other voxel values (zero and anything that is not the foreground value) is treated as background. /// Contour extraction will not take account of holes, and hence only return the outermost /// contour around a region of interest. /// </summary> /// <param name="volume"></param> /// <param name="foregroundId">The voxel value that should be used as foreground in the contour search.</param> /// <param name="smoothingType">The smoothing that should be applied when going from a point polygon to /// a contour.</param> /// <returns></returns> public static IReadOnlyList <ContourPolygon> ContoursFilled( this Volume2D <byte> volume, byte foregroundId = 1, ContourSmoothingType smoothingType = ContourSmoothingType.Small) => ExtractContours.ContoursFilled(volume, foregroundId, smoothingType);