/// <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);
 }
示例#2
0
        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);
        }
示例#5
0
        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);