/// <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); }
/// <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); }