private static Volume3D <byte> ToVolume3D(
            this ContoursPerSlice contours,
            double spacingX,
            double spacingY,
            double spacingZ,
            Point3D origin,
            Matrix3 direction,
            Region3D <int> roi)
        {
            ContoursPerSlice subContours = new ContoursPerSlice(
                contours.Where(x => x.Value != null).Select(
                    contour =>
                    new KeyValuePair <int, IReadOnlyList <ContourPolygon> >(
                        contour.Key - roi.MinimumZ,
                        contour.Value.Select(x =>
                                             new ContourPolygon(
                                                 x.ContourPoints.Select(
                                                     point => new PointF(point.X - roi.MinimumX, point.Y - roi.MinimumY)).ToArray(), 0))
                        .ToList())).ToDictionary(x => x.Key, y => y.Value));

            var result = new Volume3D <byte>(roi.MaximumX - roi.MinimumX + 1, roi.MaximumY - roi.MinimumY + 1, roi.MaximumZ - roi.MinimumZ + 1, spacingX, spacingY, spacingZ, origin, direction);

            result.Fill(subContours, ModelConstants.MaskForegroundIntensity);

            return(result);
        }
Esempio n. 2
0
        public MarchingCubesResults Generate(Region3D region, double step, double isoLevel)
        {
            var grid    = this.BuildGrid(region, step);
            var results = Generate(grid, isoLevel);

            return(results);
        }
        private static ContoursPerSlice GenerateContoursPerSlice(
            Volume3D <byte> volume,
            bool fillContours,
            byte foregroundId,
            SliceType sliceType,
            bool filterEmptyContours,
            Region3D <int> regionOfInterest,
            ContourSmoothingType axialSmoothingType)
        {
            var region = regionOfInterest ?? new Region3D <int>(0, 0, 0, volume.DimX - 1, volume.DimY - 1, volume.DimZ - 1);

            int startPoint;
            int endPoint;

            // Only smooth the output on the axial slices
            var smoothingType = axialSmoothingType;

            switch (sliceType)
            {
            case SliceType.Axial:
                startPoint = region.MinimumZ;
                endPoint   = region.MaximumZ;
                break;

            case SliceType.Coronal:
                startPoint    = region.MinimumY;
                endPoint      = region.MaximumY;
                smoothingType = ContourSmoothingType.None;
                break;

            case SliceType.Sagittal:
                startPoint    = region.MinimumX;
                endPoint      = region.MaximumX;
                smoothingType = ContourSmoothingType.None;
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(sliceType), sliceType, null);
            }

            var numberOfSlices  = endPoint - startPoint + 1;
            var arrayOfContours = new Tuple <int, IReadOnlyList <ContourPolygon> > [numberOfSlices];

            for (var i = 0; i < arrayOfContours.Length; i++)
            {
                var z        = startPoint + i;
                var volume2D = ExtractSlice.Slice(volume, sliceType, z);
                var contours = fillContours ?
                               ContoursFilled(volume2D, foregroundId, smoothingType):
                               ContoursWithHoles(volume2D, foregroundId, smoothingType);

                arrayOfContours[i] = Tuple.Create(z, contours);
            }

            return(new ContoursPerSlice(
                       arrayOfContours
                       .Where(x => !filterEmptyContours || x.Item2.Count > 0)
                       .ToDictionary(x => x.Item1, x => x.Item2)));
        }
Esempio n. 4
0
        public List <GridCube> Сheck(double from, double to, double step)
        {
            var edgesPerSide = Region3D.GetLenght(from, to) / step;
            var countOfCubes = Math.Pow(edgesPerSide, 3);

            var vertexPerSide      = edgesPerSide + 1;
            var uniqueEdgesPerSide = edgesPerSide * vertexPerSide;

            uniqueEdgesPerSide += uniqueEdgesPerSide;

            var edjesCount = vertexPerSide * uniqueEdgesPerSide + (vertexPerSide * vertexPerSide * edgesPerSide);


            var marchingCubes = new MarchingCubesAlgorithm(null);
            var region        = new CommonTypes.Region3D(from, to, from, to, from, to);
            var cubes         = marchingCubes.BuildGrid(region, step);

            Assert.AreEqual(cubes.Count, countOfCubes);

            var uniqueVertexes = new List <Point>();
            // 18 unique edges
            var lines = new List <GridLine>();

            foreach (var cube in cubes)
            {
                foreach (var edge in cube.Edges)
                {
                    if (!lines.Contains(edge))
                    {
                        lines.Add(edge);
                    }
                }

                foreach (var vert in cube.Vertex)
                {
                    // Check that objects are not created. Exclude overriden values.
                    //if (!uniqueVertexes.Any(p => object.ReferenceEquals(uniqueVertexes, vert)))
                    if (!uniqueVertexes.Contains(vert))
                    {
                        uniqueVertexes.Add(vert);
                    }
                }
            }

            var totalVertexes = vertexPerSide * vertexPerSide * vertexPerSide;

            Assert.AreEqual(uniqueVertexes.Count, totalVertexes);

            Assert.AreEqual(lines.Count, edjesCount);

            // Check last cube vertex
            var firstCube = cubes.FirstOrDefault();

            CheckVertexes(firstCube, from, from + step);
            var lastCube = cubes.LastOrDefault();

            CheckVertexes(lastCube, to - step, to);
            return(cubes);
        }
 /// <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 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 ContoursFilledPerSlice(
     Volume3D <byte> volume,
     byte foregroundId                       = ModelConstants.MaskForegroundIntensity,
     SliceType sliceType                     = SliceType.Axial,
     bool filterEmptyContours                = true,
     Region3D <int> regionOfInterest         = null,
     ContourSmoothingType axialSmoothingType = ContourSmoothingType.Small)
 {
     return(GenerateContoursPerSlice(volume, true, foregroundId, sliceType, filterEmptyContours, regionOfInterest, axialSmoothingType));
 }
Esempio n. 7
0
        public static IRegion2D ConvertTo2D(IRegion3D region3D, IMatrix44 lcs)
        {
            var regionCopy = new Region3D(region3D);

            GeomOperation.TransformToLCS(lcs, regionCopy);
            var outline2D = ConvertTo2D(regionCopy.Outline);
            var region2D  = new Region2D(outline2D);

            if (regionCopy.OpeningsCount > 0)
            {
                foreach (var opening3D in regionCopy.Openings)
                {
                    region2D.Openings.Add(ConvertTo2D(opening3D));
                }
            }

            return(region2D);
        }
        /// <summary>
        /// Gets whether the present object is inside of the given <paramref name="outer"/>
        /// region. If the present object is at the boundaries along any of the edges,
        /// this is still considered inside.
        /// </summary>
        /// <param name="region"></param>
        /// <param name="outer"></param>
        /// <returns></returns>
        public static bool InsideOf(this Region3D <int> region, Region3D <int> outer)
        {
            if (region.IsEmpty())
            {
                throw new ArgumentException("This operation can only be computed on non-empty regions.", nameof(region));
            }

            if (outer.IsEmpty())
            {
                throw new ArgumentException("This operation can only be computed on non-empty regions.", nameof(outer));
            }

            return(region.MinimumX >= outer.MinimumX &&
                   region.MaximumX <= outer.MaximumX &&
                   region.MinimumY >= outer.MinimumY &&
                   region.MaximumY <= outer.MaximumY &&
                   region.MinimumZ >= outer.MinimumZ &&
                   region.MaximumZ <= outer.MaximumZ);
        }
        public static Region3D <int> Dilate <T>(this Region3D <int> region, Volume3D <T> volume, double mmDilationX, double mmDilationY, double mmDilationZ)
        {
            if (region.IsEmpty())
            {
                return(region.Clone());
            }
            var dilatedMinimumX = region.MinimumX - (int)Math.Ceiling(mmDilationX / volume.SpacingX);
            var dilatedMaximumX = region.MaximumX + (int)Math.Ceiling(mmDilationX / volume.SpacingX);

            var dilatedMinimumY = region.MinimumY - (int)Math.Ceiling(mmDilationY / volume.SpacingY);
            var dilatedMaximumY = region.MaximumY + (int)Math.Ceiling(mmDilationY / volume.SpacingY);

            var dilatedMinimumZ = region.MinimumZ - (int)Math.Ceiling(mmDilationZ / volume.SpacingZ);
            var dilatedMaximumZ = region.MaximumZ + (int)Math.Ceiling(mmDilationZ / volume.SpacingZ);

            return(new Region3D <int>(
                       dilatedMinimumX < 0 ? 0 : dilatedMinimumX,
                       dilatedMinimumY < 0 ? 0 : dilatedMinimumY,
                       dilatedMinimumZ < 0 ? 0 : dilatedMinimumZ,
                       dilatedMaximumX >= volume.DimX ? volume.DimX - 1 : dilatedMaximumX,
                       dilatedMaximumY >= volume.DimY ? volume.DimY - 1 : dilatedMaximumY,
                       dilatedMaximumZ >= volume.DimZ ? volume.DimZ - 1 : dilatedMaximumZ));
        }
Esempio n. 10
0
        /// <summary>
        /// Create a grid to build a model based on.
        /// </summary>
        /// <returns>List of the created cubes.</returns>
        public List <GridCube> BuildGrid(Region3D region, double step)
        {
            if (step <= 0)
            {
                throw new ArgumentException(nameof(step) + " cannot be <=0");
            }

            var toReturn = new List <GridCube>();
            var totalX   = 0;
            var totalZ   = 0;

            var column     = 0;
            var prevColumn = 0;

            for (var y = region.MinY; y < region.MaxY;)
            {
                var isLastColumn = (y + step) > region.MaxY;
                var isFirstY     = column == 0;
                var nextYValue   = isLastColumn ? region.MaxY : y + step;

                var depth     = 0;
                var prevDepth = 0;
                var zIndex    = 0;
                for (var z = region.MinZ; z < region.MaxZ;)
                {
                    var isLastDepth = (z + step) > region.MaxZ;
                    var isFirstZ    = depth == 0;
                    var nextZValue  = isLastDepth ? region.MaxZ : z + step;
                    zIndex++;

                    var xIndex = 0;
                    var prevX  = region.MinX;
                    for (var x = region.MinX; x < region.MaxX;)
                    {
                        var isLastX    = (x + step) > region.MaxX;
                        var isFirstX   = xIndex == 0;
                        var nextXValue = isLastX ? region.MaxX : x + step;

                        if (!isLastColumn && !isLastX && !isLastDepth || (isFirstX && isLastX))
                        {
                            GridCube prevXCube = null;
                            if (!isFirstX)
                            {
                                prevXCube = toReturn[toReturn.Count - 1];
                            }

                            GridCube prevYCube = null;
                            if (!isFirstY)
                            {
                                var index = toReturn.Count - totalZ * totalX;
                                prevYCube = toReturn[index];
                            }

                            GridCube prevZCube = null;
                            if (!isFirstZ)
                            {
                                var index = toReturn.Count - totalX;
                                prevZCube = toReturn[index];
                            }

                            var newCube = new GridCube();

                            // Dont create object duplicates. Cubes edges are merged.
                            var edges = new List <GridLine>();
                            //0
                            var edge = GetMergeEdge(prevYCube, 4, prevZCube, 2);
                            edge = edge == null ? new GridLine(new Point(x, y, z) /*0*/, new Point(nextXValue, y, z) /*1*/, AxissConsts.X) : edge;
                            edges.Add(edge);
                            newCube.Vertex[0] = edge.Point1;
                            newCube.Vertex[1] = edge.Point2;

                            //1
                            edge = GetMergeEdge(prevYCube, 5);//, prevXCube, 3);
                            edge = edge ?? new GridLine(newCube.Vertex[1] /*1*/, new Point(nextXValue, y, nextZValue) /*2*/, AxissConsts.Z);
                            edges.Add(edge);

                            newCube.Vertex[2] = edge.Point2;

                            //2
                            edge = GetMergeEdge(prevYCube, 6);//, prevZCube, 0);
                            edge = edge ?? new GridLine(new Point(x, y, nextZValue) /*3*/, newCube.Vertex[2] /*2*/, AxissConsts.X);
                            edges.Add(edge);

                            newCube.Vertex[3] = edge.Point1;

                            //3
                            edge = GetMergeEdge(prevXCube, 1, prevYCube, 7);
                            edge = edge ?? new GridLine(newCube.Vertex[3] /*3*/, newCube.Vertex[0] /*0*/, AxissConsts.Z);
                            edges.Add(edge);

                            //4
                            edge = GetMergeEdge(prevZCube, 6);
                            edge = edge ?? new GridLine(new Point(x, nextYValue, z) /*4*/, new Point(nextXValue, nextYValue, z) /*5*/, AxissConsts.X);
                            edges.Add(edge);

                            newCube.Vertex[4] = edge.Point1;
                            newCube.Vertex[5] = edge.Point2;

                            //5
                            edge = new GridLine(newCube.Vertex[5] /*5*/, new Point(nextXValue, nextYValue, nextZValue) /*6*/, AxissConsts.Z);
                            edges.Add(edge);
                            newCube.Vertex[6] = edge.Point2;

                            //6
                            edge = new GridLine(new Point(x, nextYValue, nextZValue) /*7*/, newCube.Vertex[6] /*6*/, AxissConsts.X);
                            edges.Add(edge);


                            newCube.Vertex[7] = edge.Point1;

                            //7
                            edge = GetMergeEdge(prevXCube, 5);
                            edge = edge ?? new GridLine(newCube.Vertex[4] /*4*/, newCube.Vertex[7] /*7*/, AxissConsts.Y);
                            edges.Add(edge);

                            //8
                            edge = GetMergeEdge(prevXCube, 9, prevZCube, 11);
                            edge = edge == null ? new GridLine(newCube.Vertex[0] /*0*/, newCube.Vertex[4] /*4*/, AxissConsts.Y) : edge;
                            edges.Add(edge);

                            //9
                            edge = GetMergeEdge(prevZCube, 10);
                            edge = edge ?? new GridLine(newCube.Vertex[1] /*1*/, newCube.Vertex[5] /*5*/, AxissConsts.Y);
                            edges.Add(edge);

                            //10
                            edge = new GridLine(newCube.Vertex[2] /*2*/, newCube.Vertex[6] /*6*/, AxissConsts.Y);
                            edges.Add(edge);

                            //11
                            edge = GetMergeEdge(prevXCube, 10, null, 0);
                            edge = edge ?? new GridLine(newCube.Vertex[3] /*3*/, newCube.Vertex[7] /*7*/, AxissConsts.Y);
                            edges.Add(edge);


                            newCube.Edges = edges.ToArray();

                            toReturn.Add(newCube);
                        }

                        prevX = x;
                        xIndex++;
                        x = nextXValue;
                    }

                    totalX    = xIndex;
                    prevDepth = depth;
                    depth++;
                    z = nextZValue;
                }

                totalZ     = zIndex;
                prevColumn = column;
                column++;
                y = nextYValue;
            }

            return(toReturn);
        }
 /// <summary>
 /// Gets whether the region contains zero voxels. That is the case if, for any of the
 /// dimensions X, Y, Z, the minimum is larger than the maximum.
 /// </summary>
 /// <param name="region"></param>
 /// <returns></returns>
 public static bool IsEmpty(this Region3D <int> region)
 {
     return(region.MinimumX > region.MaximumX ||
            region.MinimumY > region.MaximumY ||
            region.MinimumZ > region.MaximumZ);
 }
        /// <summary>
        /// Gets the length of the region along the Z dimensions. The length
        /// of the region is the number of points (integers) that are included
        /// in the region. If the region has Minimum of 4, and Maximum of 5,
        /// the length is 2 (Minimum and Maximum are inclusive).
        /// </summary>
        /// <param name="region3D"></param>
        /// <returns></returns>
        public static int LengthZ(this Region3D <int> region3D)
        {
            var length = region3D.MaximumZ - region3D.MinimumZ + 1;

            return(length < 0 ? 0 : length);
        }
 /// <summary>
 /// Gets the number of points in the region, that is the product of the
 /// region length across the three dimensions.
 /// </summary>
 /// <param name="region"></param>
 /// <returns></returns>
 public static int Size(this Region3D <int> region)
 {
     return(region.LengthX() * region.LengthY() * region.LengthZ());
 }
 /// <summary>
 /// Gets whether a point with the given (x, y, z) coordinates is inside the region.
 /// </summary>
 /// <param name="region"></param>
 /// <param name="x"></param>
 /// <param name="y"></param>
 /// <param name="z"></param>
 /// <returns></returns>
 public static bool ContainsPoint(this Region3D <int> region, int x, int y, int z)
 {
     return(x >= region.MinimumX && x <= region.MaximumX &&
            y >= region.MinimumY && y <= region.MaximumY &&
            z >= region.MinimumZ && z <= region.MaximumZ);
 }
 /// <summary>
 /// Creates a volume that has the same spacing and coordinate system as the reference volume,
 /// and fills all points that fall inside of the contours in the present object with the
 /// default foreground value. The returned volume has its size determined by the given region of interest.
 /// Contour points are transformed using the region of interest.
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="contours">The contours to use for filling.</param>
 /// <param name="refVolume3D">The reference volume to copy spacing and coordinate system from.</param>
 /// <param name="regionOfInterest"></param>
 /// <returns></returns>
 public static Volume3D <byte> ToVolume3D <T>(this ContoursPerSlice contours, Volume3D <T> refVolume3D, Region3D <int> regionOfInterest)
 {
     return(contours.ToVolume3D(
                refVolume3D.SpacingX,
                refVolume3D.SpacingY,
                refVolume3D.SpacingZ,
                refVolume3D.Origin,
                refVolume3D.Direction,
                regionOfInterest));
 }