Beispiel #1
0
        /// <summary>
        /// puts the given dots into a grid for easy lookup
        /// </summary>
        /// <param name="dots">detected calibration dots</param>
        /// <returns>grid object</returns>
        private static calibrationDot[,] CreateGrid(hypergraph dots)
        {
            int grid_tx = 9999, grid_ty = 9999;
            int grid_bx = -9999, grid_by = -9999;
            for (int i = 0; i < dots.Nodes.Count; i++)
            {
                calibrationDot dot = (calibrationDot)dots.Nodes[i];
                if (Math.Abs(dot.grid_x) < 50)
                {
                    if (dot.grid_x < grid_tx) grid_tx = dot.grid_x;
                    if (dot.grid_y < grid_ty) grid_ty = dot.grid_y;
                    if (dot.grid_x > grid_bx) grid_bx = dot.grid_x;
                    if (dot.grid_y > grid_by) grid_by = dot.grid_y;
                }
            }

            calibrationDot[,] grid = null;
            if (grid_bx > grid_tx + 1)
            {
                grid = new calibrationDot[grid_bx - grid_tx + 1, grid_by - grid_ty + 1];

                for (int i = 0; i < dots.Nodes.Count; i++)
                {
                    calibrationDot dot = (calibrationDot)dots.Nodes[i];
                    if ((!dot.centre) && (Math.Abs(dot.grid_x) < 50))
                    {
                        grid[dot.grid_x - grid_tx, dot.grid_y - grid_ty] = dot;
                    }
                }
            }

            return (grid);
        }
Beispiel #2
0
 /// <summary>
 /// returns the horizontal and vertical distance of the centre dot from the centre of the image
 /// </summary>
 /// <param name="image_width">
 /// width of the image <see cref="System.Int32"/>
 /// </param>
 /// <param name="image_height">
 /// height of the image <see cref="System.Int32"/>
 /// </param>
 /// <param name="dots">
 /// detected dots <see cref="hypergraph"/>
 /// </param>
 /// <param name="horizontal_distance">
 /// horizontal distance from the image centre in pixels <see cref="System.Single"/>
 /// </param>
 /// <param name="vertical_distance">
 /// vertical distance from the image centre in pixels <see cref="System.Single"/>
 /// </param>
 private static void GetCentreDotDisplacement(int image_width, int image_height,
                                              hypergraph dots,
                                              ref float horizontal_distance,
                                              ref float vertical_distance)
 {
     calibrationDot centre_dot = null;
     int i = 0;
     while ((i < dots.Nodes.Count) && (centre_dot == null))
     {
         calibrationDot dot = (calibrationDot)dots.Nodes[i];
         if (dot.centre)
             centre_dot = dot;
         else
             i++;
     }
     if (centre_dot != null)
     {
         horizontal_distance = (float)centre_dot.x - (image_width / 2.0f);
         vertical_distance = (float)centre_dot.y - (image_height / 2.0f);
     }
 }
Beispiel #3
0
 /// <summary>
 /// extracts lines from the given detected dots
 /// </summary>
 /// <param name="dots"></param>
 /// <returns></returns>
 private static List<List<double>> CreateLines(hypergraph dots,
                                               calibrationDot[,] grid)
 {
     List<List<double>> lines = new List<List<double>>();
     if (grid != null)
     {
         for (int grid_x = 0; grid_x < grid.GetLength(0); grid_x++)
         {
             List<double> line = new List<double>();
             for (int grid_y = 0; grid_y < grid.GetLength(1); grid_y++)
             {
                 if (grid[grid_x, grid_y] != null)
                 {
                     line.Add(grid[grid_x, grid_y].x);
                     line.Add(grid[grid_x, grid_y].y);
                 }
             }
             if (line.Count > 6)
             {
                 lines.Add(line);
             }
         }
         for (int grid_y = 0; grid_y < grid.GetLength(1); grid_y++)
         {
             List<double> line = new List<double>();
             for (int grid_x = 0; grid_x < grid.GetLength(0); grid_x++)
             {
                 if (grid[grid_x, grid_y] != null)
                 {
                     line.Add(grid[grid_x, grid_y].x);
                     line.Add(grid[grid_x, grid_y].y);
                 }
             }
             if (line.Count > 6)
             {
                 lines.Add(line);
             }
         }
     }
     return (lines);
 }
Beispiel #4
0
        /// <summary>
        /// detects a square around the centre dot on the calibration pattern
        /// </summary>
        /// <param name="dots">detected dots</param>
        /// <param name="centredots">returned dots belonging to the centre square</param>
        /// <returns>centre square</returns>
        private static polygon2D GetCentreSquare(hypergraph dots,
                                                 ref List<calibrationDot> centredots)
        {
            centredots = new List<calibrationDot>();

            // find the centre dot
            calibrationDot centre = null;
            int i = 0;
            while ((i < dots.Nodes.Count) && (centre == null))
            {
                calibrationDot dot = (calibrationDot)dots.Nodes[i];
                if (dot.centre) centre = dot;
                i++;
            }

            // look for the four surrounding dots
            List<calibrationDot> centre_dots = new List<calibrationDot>();
            List<double> distances = new List<double>();
            i = 0;
            for (i = 0; i < dots.Nodes.Count; i++)
            {
                calibrationDot dot = (calibrationDot)dots.Nodes[i];
                if (!dot.centre)
                {
                    double dx = dot.x - centre.x;
                    double dy = dot.y - centre.y;
                    double dist = Math.Sqrt(dx * dx + dy * dy);
                    if (distances.Count == 4)
                    {
                        int index = -1;
                        double max_dist = 0;
                        for (int j = 0; j < 4; j++)
                        {
                            if (distances[j] > max_dist)
                            {
                                index = j;
                                max_dist = distances[j];
                            }
                        }
                        if (dist < max_dist)
                        {
                            distances[index] = dist;
                            centre_dots[index] = dot;
                        }
                    }
                    else
                    {
                        distances.Add(dist);
                        centre_dots.Add(dot);
                    }
                }
            }

            polygon2D centre_square = null;
            if (centre_dots.Count == 4)
            {
                centre_square = new polygon2D();
                for (i = 0; i < 4; i++)
                    centre_square.Add(0, 0);

                double xx = centre.x;
                double yy = centre.y;
                int index = 0;
                for (i = 0; i < 4; i++)
                {
                    if ((centre_dots[i].x < xx) &&
                        (centre_dots[i].y < yy))
                    {
                        xx = centre_dots[i].x;
                        yy = centre_dots[i].y;
                        centre_square.x_points[0] = (float)xx;
                        centre_square.y_points[0] = (float)yy;
                        index = i;
                    }
                }
                centredots.Add(centre_dots[index]);

                xx = centre.x;
                yy = centre.y;
                for (i = 0; i < 4; i++)
                {
                    if ((centre_dots[i].x > xx) &&
                        (centre_dots[i].y < yy))
                    {
                        xx = centre_dots[i].x;
                        yy = centre_dots[i].y;
                        centre_square.x_points[1] = (float)xx;
                        centre_square.y_points[1] = (float)yy;
                        index = i;
                    }
                }
                centredots.Add(centre_dots[index]);

                xx = centre.x;
                yy = centre.y;
                for (i = 0; i < 4; i++)
                {
                    if ((centre_dots[i].x > xx) &&
                        (centre_dots[i].y > yy))
                    {
                        xx = centre_dots[i].x;
                        yy = centre_dots[i].y;
                        centre_square.x_points[2] = (float)xx;
                        centre_square.y_points[2] = (float)yy;
                        index = i;
                    }
                }
                centredots.Add(centre_dots[index]);

                xx = centre.x;
                yy = centre.y;
                for (i = 0; i < 4; i++)
                {
                    if ((centre_dots[i].x < xx) &&
                        (centre_dots[i].y > yy))
                    {
                        xx = centre_dots[i].x;
                        yy = centre_dots[i].y;
                        centre_square.x_points[3] = (float)xx;
                        centre_square.y_points[3] = (float)yy;
                        index = i;
                    }
                }
                centredots.Add(centre_dots[index]);
            }
            return (centre_square);
        }
Beispiel #5
0
        /// <summary>
        /// detects calibration dots within the given image
        /// </summary>
        /// <param name="filename">image to be examined</param>
        /// <param name="image_width">returned image width</param>
        /// <param name="image_height">returned image height</param>
        /// <returns>detected dots</returns>
        private static hypergraph DetectDots(string filename,
                                             ref int image_width, ref int image_height)
        {
            Console.WriteLine("Detecting dots in image " + filename);

            hypergraph dots = new hypergraph();

            int grouping_radius_percent = 2;
            int erosion_dilation = 0;
            float minimum_width = 0.0f;
            float maximum_width = 10;
            List<int> edges = null;

            List<calibrationDot> dot_shapes = shapes.DetectDots(filename, grouping_radius_percent, erosion_dilation,
                                                                minimum_width, maximum_width,
                                                                ref edges, "edges.jpg", "groups.jpg", "dots.jpg",
                                                                ref image_width, ref image_height);

            double tx = 99999;
            double ty = 99999;
            double bx = 0;
            double by = 0;
            for (int i = 0; i < dot_shapes.Count; i++)
            {
                if (dot_shapes[i].x < tx) tx = dot_shapes[i].x;
                if (dot_shapes[i].y < ty) ty = dot_shapes[i].y;
                if (dot_shapes[i].x > bx) bx = dot_shapes[i].x;
                if (dot_shapes[i].y > by) by = dot_shapes[i].y;
                dots.Add(dot_shapes[i]);
            }

            Console.WriteLine(dot_shapes.Count.ToString() + " dots discovered");

            return (dots);
        }
Beispiel #6
0
        /// <summary>
        /// links detected dots together
        /// </summary>
        /// <param name="dots">detected dots</param>
        /// <param name="current_dot">the current dot of interest</param>
        /// <param name="horizontal_dx">current expected horizontal displacement x coordinate</param>
        /// <param name="horizontal_dy">current expected horizontal displacement y coordinate</param>
        /// <param name="vertical_dx">current expected vertical displacement x coordinate</param>
        /// <param name="vertical_dy">current expected vertical displacement x coordinate</param>
        /// <param name="start_index">index number indicating which direction we will search in first</param>
        /// <param name="search_regions">returned list of search regions</param>
        private static void LinkDots(hypergraph dots,
                                     calibrationDot current_dot,
                                     double horizontal_dx, double horizontal_dy,
                                     double vertical_dx, double vertical_dy,
                                     int start_index,
                                     List<calibrationDot> search_regions)
        {
            if (!current_dot.centre)
            {
                int start_index2 = 0;
                double tollerance_divisor = 0.3f;
                double horizontal_tollerance = Math.Sqrt((horizontal_dx * horizontal_dx) + (horizontal_dy * horizontal_dy)) * tollerance_divisor;
                double vertical_tollerance = Math.Sqrt((vertical_dx * vertical_dx) + (vertical_dy * vertical_dy)) * tollerance_divisor;

                double x = 0, y = 0;
                List<int> indexes_found = new List<int>();
                List<bool> found_vertical = new List<bool>();

                // check each direction
                for (int i = 0; i < 4; i++)
                {
                    // starting direction offset
                    int ii = i + start_index;
                    if (ii >= 4) ii -= 4;

                    if (current_dot.Flags[ii] == false)
                    {
                        current_dot.Flags[ii] = true;
                        int opposite_flag = ii + 2;
                        if (opposite_flag >= 4) opposite_flag -= 4;

                        switch (ii)
                        {
                            case 0:
                                {
                                    // look above
                                    x = current_dot.x - vertical_dx;
                                    y = current_dot.y - vertical_dy;
                                    break;
                                }
                            case 1:
                                {
                                    // look right
                                    x = current_dot.x + horizontal_dx;
                                    y = current_dot.y + horizontal_dy;
                                    break;
                                }
                            case 2:
                                {
                                    // look below
                                    x = current_dot.x + vertical_dx;
                                    y = current_dot.y + vertical_dy;
                                    break;
                                }
                            case 3:
                                {
                                    // look left
                                    x = current_dot.x - horizontal_dx;
                                    y = current_dot.y - horizontal_dy;
                                    break;
                                }
                        }

                        calibrationDot search_region = new calibrationDot();
                        search_region.x = x;
                        search_region.y = y;
                        search_region.radius = (float)horizontal_tollerance;
                        search_regions.Add(search_region);

                        for (int j = 0; j < dots.Nodes.Count; j++)
                        {
                            if ((!((calibrationDot)dots.Nodes[j]).centre) &&
                                (dots.Nodes[j] != current_dot))
                            {
                                double dx = ((calibrationDot)dots.Nodes[j]).x - x;
                                double dy = ((calibrationDot)dots.Nodes[j]).y - y;
                                double dist_from_expected_position = Math.Sqrt(dx * dx + dy * dy);
                                bool dot_found = false;
                                if ((ii == 0) || (ii == 2))
                                {
                                    // vertical search
                                    if (dist_from_expected_position < vertical_tollerance)
                                    {
                                        dot_found = true;
                                        found_vertical.Add(true);
                                    }
                                }
                                else
                                {
                                    // horizontal search
                                    if (dist_from_expected_position < horizontal_tollerance)
                                    {
                                        dot_found = true;
                                        found_vertical.Add(false);
                                    }
                                }

                                if (dot_found)
                                {
                                    indexes_found.Add(j);
                                    j = dots.Nodes.Count;
                                }

                            }
                        }



                    }
                }

                for (int i = 0; i < indexes_found.Count; i++)
                {
                    start_index2 = start_index + 1;
                    if (start_index2 >= 4) start_index2 -= 4;

                    double found_dx = ((calibrationDot)dots.Nodes[indexes_found[i]]).x - current_dot.x;
                    double found_dy = ((calibrationDot)dots.Nodes[indexes_found[i]]).y - current_dot.y;

                    calibrationLink link = new calibrationLink();

                    if (found_vertical[i])
                    {
                        link.horizontal = false;

                        if (((vertical_dy > 0) && (found_dy < 0)) ||
                            ((vertical_dy < 0) && (found_dy > 0)))
                        {
                            found_dx = -found_dx;
                            found_dy = -found_dy;
                        }
                        LinkDots(dots, (calibrationDot)dots.Nodes[indexes_found[i]], horizontal_dx, horizontal_dy, found_dx, found_dy, start_index2, search_regions);
                    }
                    else
                    {
                        link.horizontal = true;

                        if (((horizontal_dx > 0) && (found_dx < 0)) ||
                            ((horizontal_dx < 0) && (found_dx > 0)))
                        {
                            found_dx = -found_dx;
                            found_dy = -found_dy;
                        }
                        LinkDots(dots, (calibrationDot)dots.Nodes[indexes_found[i]], found_dx, found_dy, vertical_dx, vertical_dy, start_index2, search_regions);
                    }

                    dots.LinkByReference((calibrationDot)dots.Nodes[indexes_found[i]], current_dot, link);

                }

            }
        }
Beispiel #7
0
        private static void ApplyGrid(hypergraph dots,
                                      List<calibrationDot> centre_dots)
        {
            const int UNASSIGNED = 9999;

            // mark all dots as unassigned
            for (int i = 0; i < dots.Nodes.Count; i++)
            {
                calibrationDot dot = (calibrationDot)dots.Nodes[i];
                dot.grid_x = UNASSIGNED;
                dot.grid_y = UNASSIGNED;
            }

            // assign grid positions to the four dots
            // surrounding the centre dot
            centre_dots[0].grid_x = -1;
            centre_dots[0].grid_y = 1;

            centre_dots[1].grid_x = 0;
            centre_dots[1].grid_y = 1;

            centre_dots[2].grid_x = 0;
            centre_dots[2].grid_y = 0;

            centre_dots[3].grid_x = -1;
            centre_dots[3].grid_y = 0;

            int dots_assigned = 4;

            // recursively assign grid positions to dots
            for (int i = 0; i < centre_dots.Count; i++)
                ApplyGrid(centre_dots[i], UNASSIGNED, ref dots_assigned);

            Console.WriteLine(dots_assigned.ToString() + " dots assigned grid coordinates");
        }
Beispiel #8
0
        /// <summary>
        /// shows the grid coordinates for each detected dot
        /// </summary>
        /// <param name="filename">raw image filename</param>
        /// <param name="dots">detected dots</param>
        /// <param name="output_filename">filename to save as</param>
        private static void ShowGridCoordinates(string filename,
                                                hypergraph dots,
                                                string output_filename)
        {
            Bitmap bmp = (Bitmap)Bitmap.FromFile(filename);
            byte[] img = new byte[bmp.Width * bmp.Height * 3];
            BitmapArrayConversions.updatebitmap(bmp, img);

            for (int i = 0; i < dots.Nodes.Count; i++)
            {
                calibrationDot dot = (calibrationDot)dots.Nodes[i];
                drawing.drawCircle(img, bmp.Width, bmp.Height, (float)dot.x, (float)dot.y, dot.radius, 0, 255, 0, 0);

                if (dot.grid_x != 9999)
                {
                    string coord = dot.grid_x.ToString() + "," + dot.grid_y.ToString();
                    drawing.AddText(img, bmp.Width, bmp.Height, coord, "Courier New", 8, 0, 0, 0, (int)dot.x - 0, (int)dot.y + 5);
                }
            }

            Bitmap output_bmp = new Bitmap(bmp.Width, bmp.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            BitmapArrayConversions.updatebitmap_unsafe(img, output_bmp);
            if (output_filename.ToLower().EndsWith("jpg"))
                output_bmp.Save(output_filename, System.Drawing.Imaging.ImageFormat.Jpeg);
            if (output_filename.ToLower().EndsWith("bmp"))
                output_bmp.Save(output_filename, System.Drawing.Imaging.ImageFormat.Bmp);
        }
Beispiel #9
0
        /// <summary>
        /// shows the grid fitted to the calibration pattern
        /// </summary>
        /// <param name="filename">raw image filename</param>
        /// <param name="dots">detected dots on the grid</param>
        /// <param name="search_regions">search regions around the dots</param>
        /// <param name="output_filename">filename to save as</param>
        private static void ShowGrid(string filename,
                                     hypergraph dots,
                                     List<calibrationDot> search_regions,
                                     string output_filename)
        {
            Bitmap bmp = (Bitmap)Bitmap.FromFile(filename);
            byte[] img = new byte[bmp.Width * bmp.Height * 3];
            BitmapArrayConversions.updatebitmap(bmp, img);

            if (search_regions != null)
            {
                for (int i = 0; i < search_regions.Count; i++)
                {
                    calibrationDot dot = (calibrationDot)search_regions[i];
                    drawing.drawCircle(img, bmp.Width, bmp.Height, (float)dot.x, (float)dot.y, (float)dot.radius, 255, 255, 0, 0);
                }
            }

            for (int i = 0; i < dots.Nodes.Count; i++)
            {
                calibrationDot dot = (calibrationDot)dots.Nodes[i];
                drawing.drawCircle(img, bmp.Width, bmp.Height, (float)dot.x, (float)dot.y, dot.radius, 0, 255, 0, 0);
            }

            for (int i = 0; i < dots.Links.Count; i++)
            {
                calibrationDot from_dot = (calibrationDot)dots.Links[i].From;
                calibrationDot to_dot = (calibrationDot)dots.Links[i].To;
                drawing.drawLine(img, bmp.Width, bmp.Height, (int)from_dot.x, (int)from_dot.y, (int)to_dot.x, (int)to_dot.y, 255, 0, 0, 0, false);
            }

            Bitmap output_bmp = new Bitmap(bmp.Width, bmp.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            BitmapArrayConversions.updatebitmap_unsafe(img, output_bmp);
            if (output_filename.ToLower().EndsWith("jpg"))
                output_bmp.Save(output_filename, System.Drawing.Imaging.ImageFormat.Jpeg);
            if (output_filename.ToLower().EndsWith("bmp"))
                output_bmp.Save(output_filename, System.Drawing.Imaging.ImageFormat.Bmp);
        }
Beispiel #10
0
        public static List<List<int>> GetGroups(List<int>edges,
                                                int img_width, int img_height,
                                                int image_border,
                                                int minimum_size_percent,
                                                bool squares_only,
                                                int max_search_depth,
                                                bool ignore_periphery,
                                                int grouping_radius_percent)
        {
#if SHOW_TIMINGS
                stopwatch timer_grouping = new stopwatch();
                timer_grouping.Start();
#endif
            const int compression = 4;
            
            int grouping_radius = grouping_radius_percent * img_width / (100 * compression);
            List<List<int>> groups = new List<List<int>>();            
                        
            // find line segments of significant length
            List<float> centres = null;
            List<float> bounding_boxes = null;
            List<List<int>> line_segments = 
                DetectLongestPerimeters(edges, 
                                        img_width, img_height, 
                                        image_border, 
                                        minimum_size_percent, 
                                        squares_only,
                                        max_search_depth,
                                        ignore_periphery,
                                        ref centres,
                                        ref bounding_boxes);

            bool[][] grouping_matrix = new bool[line_segments.Count][];
            for (int i = 0; i < line_segments.Count; i++)
                grouping_matrix[i] = new bool[line_segments.Count];
                        
            // map out the line segments
            const int step_size = 4;
            int ty = img_height-1;
            int by = 0;
            int tx = img_width-1;
            int bx = 0;
            int[,] line_segment_map = new int[(img_width/compression)+2, (img_height/compression)+2];
            for (int i = 0; i < line_segments.Count; i++)
            {
                List<int> line_segment = line_segments[i];
                for (int j = 0; j < line_segment.Count; j += step_size)
                {
                    int x = line_segment[j]/compression;
                    int y = line_segment[j+1]/compression;
                    if (line_segment_map[x, y] > 0)
                    {
                        if (line_segment_map[x, y] != i + 1)
                        {
                            int segment_index1 = line_segment_map[x, y]-1; 
                            int segment_index2 = i;
                            
                            // link the two segments
                            grouping_matrix[segment_index1][segment_index2] = true;
                            grouping_matrix[segment_index2][segment_index1] = true;
                        }
                    }
                    line_segment_map[x, y] = i + 1;
                    if (x < tx) tx = x;
                    if (x > bx) bx = x;
                    if (y < ty) ty = y;
                    if (y > by) by = y;
                }
            }
            
            // horizontal grouping
            for (int y = ty; y <= by; y++)
            {
                int prev_segment_index = -1;
                int prev_segment_x = -1;
                for (int x = tx; x <= bx; x++)
                {
                    int segment_index = line_segment_map[x, y];
                    if (segment_index > 0)
                    {
                        if (prev_segment_x > -1)
                        {
                            int dx = x - prev_segment_x;
                            if (dx < grouping_radius)
                            {
                                if (!grouping_matrix[segment_index-1][prev_segment_index])
                                {
                                    // get the line segment indexes
                                    segment_index--;                                    
                                    
                                    if (segment_index != prev_segment_index)
                                    {                                
                                        // link the two segments
                                        grouping_matrix[segment_index][prev_segment_index] = true;
                                        grouping_matrix[prev_segment_index][segment_index] = true;
                                    }
                                }
                            }
                        }
                        prev_segment_x = x;
                        prev_segment_index = line_segment_map[x, y]-1;
                    }
                }
            }

            // horizontal grouping
            for (int x = tx; x <= bx; x++)
            {
                int prev_segment_y = -1;
                int prev_segment_index = -1;
                for (int y = ty; y <= by; y++)
                {
                    int segment_index = line_segment_map[x, y];
                    if (segment_index > 0)
                    {
                        if (prev_segment_y > -1)
                        {
                            int dy = y - prev_segment_y;
                            if (dy < grouping_radius)
                            {
                                if (!grouping_matrix[segment_index-1][prev_segment_index])
                                {
                                    // get the line segment indexes
                                    segment_index--;
                                    
                                    if (segment_index != prev_segment_index)
                                    {                                
                                        // link the two segments
                                        grouping_matrix[segment_index][prev_segment_index] = true;
                                        grouping_matrix[prev_segment_index][segment_index] = true;
                                    }
                                }
                            }                            
                        }
                        prev_segment_y = y;
                        prev_segment_index = line_segment_map[x, y]-1;                        
                    }
                }
            }
                        
            // turn grouping matrix into a hypergraph
            hypergraph graph = new hypergraph(grouping_matrix.Length, 1);
            for (int i = 0; i < grouping_matrix.Length; i++)
            {
                for (int j = 0; j < grouping_matrix[i].Length; j++)
                {
                    if (grouping_matrix[i][j])
                        graph.LinkByIndex(j, i);
                }
            }
            
            // detect connected sets within the hypergraph
            for (int i = 0; i < graph.Nodes.Count; i++)
            {
                bool already_grouped = graph.GetFlagByIndex(i, 0);
                if (!already_grouped)
                {
                    List<hypergraph_node> connected_set = new List<hypergraph_node>();
                    graph.PropogateFlagFromIndex(i, 0, connected_set);
                    
                    List<hypergraph_node> members = new List<hypergraph_node>();
                    for (int j = 0; j < connected_set.Count; j++)
                    {
                        if (!members.Contains(connected_set[j])) members.Add(connected_set[j]);
                    }

                    List<int> group_members = new List<int>();
                    for (int j = 0; j < members.Count; j++)
                    {
                        int line_segment_index = members[j].ID;
                        List<int> perim = line_segments[line_segment_index];
                        for (int k = 0; k < perim.Count; k++)
                            group_members.Add(perim[k]);
                    }
                    groups.Add(group_members);
                    
                }
            }
            
#if SHOW_TIMINGS
            timer_grouping.Stop();
            //if (timer_grouping.time_elapsed_mS > 20)
                Console.WriteLine("GetGroups: " + timer_grouping.time_elapsed_mS.ToString() );
#endif                
            
            return(groups);
        }