Пример #1
0
        /// <summary>
        /// detects square shapes within the given mono image
        /// </summary>
        /// <param name="img_mono">
        /// mono image data <see cref="System.Byte"/>
        /// </param>
        /// <param name="img_width">
        /// image width <see cref="System.Int32"/>
        /// </param>
        /// <param name="img_height">
        /// image height <see cref="System.Int32"/>
        /// </param>
        /// <param name="ignore_periphery">
        /// whether to ignore edge features which stray into the border of the image <see cref="System.Boolean"/>
        /// </param>
        /// <param name="image_border_percent">
        /// percentage of the image considred to be the surrounding border <see cref="System.Int32"/>
        /// </param>
        /// <param name="grouping_radius_percent">
        /// radius used to group adjacent edge features <see cref="System.Int32"/>
        /// </param>
        /// <param name="erosion_dilation">
        /// erosion dilation level <see cref="System.Int32"/>
        /// </param>
        /// <param name="black_on_white">
        /// whether this image contains dark markings on a lighter background <see cref="System.Boolean"/>
        /// </param>
        /// <param name="minimum_volume_percent">
        /// minimum volume of the square as a percentage of the image volume (prevents very small stuff being detected) <see cref="System.Int32"/>
        /// </param>
        /// <param name="use_original_image">
        /// whether to allow img_mono to be altered
        /// </param>
        /// <param name="minimum_aspect_ratio">
        /// minimum aspect ratio when searching for valid regions
        /// </param>
        /// <param name="maximum_aspect_ratio">
        /// maximum aspect ratio when searching for valid regions
        /// </param>
        /// <returns>
        /// list of polygons representing the square regions <see cref="List`1"/>
        /// </returns>
    	public static List<polygon2D> DetectSquaresMono(byte[] mono_img, 
                                                        int img_width, int img_height,
                                                        bool ignore_periphery,
                                                        int image_border_percent,
                                                        int[] grouping_radius_percent,
                                                        int[] erosion_dilation,
                                                        bool black_on_white,
                                                        int minimum_volume_percent,
                                                        bool use_original_image,
                                                        float minimum_aspect_ratio,
                                                        float maximum_aspect_ratio,
                                                        bool squares_only,
                                                        bool debug,
                                                        int circular_ROI_radius,
                                                        ref List<int> edges)
        {
#if SHOW_TIMINGS
            stopwatch timer_square_detection = new stopwatch();
            timer_square_detection.Start();
#endif
            const int minimum_size_percent = 5;
            const int connect_edges_radius = 5;
            
            // maximum recursion depth when joining edges
            const int max_search_depth = 8000;
            
            // create a list to store the results
            List<polygon2D> square_shapes = new List<polygon2D>();
            
            // create edge detection object
            CannyEdgeDetector edge_detector = new CannyEdgeDetector();
            edge_detector.automatic_thresholds = true;  
            edge_detector.enable_edge_orientations = false;

#if SHOW_TIMINGS
            stopwatch timer_copy_invert = new stopwatch();
            timer_copy_invert.Start();
#endif
            
            byte[] img_mono = mono_img;
            if (!use_original_image)
            {
                // make a copy of the original image (we don't want to alter it)
                img_mono = (byte[])mono_img.Clone();
            }

            // if the image contains light markings on a darker background invert the pixels            
            if (!black_on_white)
            {
                for (int i = img_mono.Length-1; i >= 0; i--)
                {
                    img_mono[i] = (byte)(255 - img_mono[i]);
                }
            }

#if SHOW_TIMINGS
            timer_copy_invert.Stop();
            if (timer_copy_invert.time_elapsed_mS > 20)
                Console.WriteLine("DetectSquaresMono: copy/invert  " + timer_copy_invert.time_elapsed_mS.ToString() );
#endif
                            
            int image_border = img_width * image_border_percent / 100;

            byte[] img_mono2 = null;                        
            byte[] eroded = null;
            byte[] dilated = null;
            bool previous_eroded = false;
            bool previous_dilated = false;
            
            // try each erosion/dilation level
            for (int erosion_dilation_level = 0; erosion_dilation_level < erosion_dilation.Length; erosion_dilation_level++)
            {

#if SHOW_TIMINGS
                stopwatch timer_erode_dilate = new stopwatch();
                timer_erode_dilate.Start();
#endif
                
                // erode
                if (erosion_dilation[erosion_dilation_level] > 0)
                {                    
                    if (previous_eroded)
                        eroded = image.Erode(eroded, img_width, img_height, 
                                             erosion_dilation[erosion_dilation_level] - erosion_dilation[erosion_dilation_level-1], 
                                             eroded);
                    else
                        eroded = image.Erode(img_mono, img_width, img_height, 
                                             erosion_dilation[erosion_dilation_level], 
                                             null);
                    img_mono2 = eroded;
                    previous_eroded = true;
                }
                
                // dilate
                if (erosion_dilation[erosion_dilation_level] < 0)
                {
                    if (previous_dilated)
                        dilated = image.Dilate(dilated, img_width, img_height, 
                                               -erosion_dilation[erosion_dilation_level] + erosion_dilation[erosion_dilation_level-1], 
                                               dilated);
                    else
                        dilated = image.Dilate(img_mono, img_width, img_height, 
                                               -erosion_dilation[erosion_dilation_level], 
                                               null);
                    img_mono2 = dilated;
                    previous_dilated = true;
                }
                
                // just copy the original image
                if (erosion_dilation[erosion_dilation_level] == 0)
                {
                    if (erosion_dilation_level > 0)
                        img_mono2 = (byte[])img_mono.Clone();
                    else
                        img_mono2 = img_mono;
                    previous_eroded = false;
                    previous_dilated = false;
                }

#if SHOW_TIMINGS
                timer_erode_dilate.Stop();
                if (timer_erode_dilate.time_elapsed_mS > 20)
                    Console.WriteLine("DetectSquaresMono: erode/dilate  " + timer_erode_dilate.time_elapsed_mS.ToString() );

                stopwatch timer_edge_detection = new stopwatch();
                timer_edge_detection.Start();
#endif                
                // detect edges using canny algorithm
                edge_detector.Update(img_mono2, img_width, img_height);

#if SHOW_TIMINGS
                timer_edge_detection.Stop();
                if (timer_edge_detection.time_elapsed_mS > 20)
                    Console.WriteLine("DetectSquaresMono: edge detect  " + timer_edge_detection.time_elapsed_mS.ToString() );
#endif
                
                if (debug)
                {
                    string filename = "canny_magnitudes_" + erosion_dilation_level.ToString() + ".bmp";
                    byte[] magnitudes = new byte[img_width * img_height * 3];
                    edge_detector.ShowMagnitudes(magnitudes, img_width, img_height);
                    Bitmap magnitudes_bmp = new Bitmap(img_width, img_height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                    BitmapArrayConversions.updatebitmap_unsafe(magnitudes, magnitudes_bmp);
                    magnitudes_bmp.Save(filename, System.Drawing.Imaging.ImageFormat.Bmp);
                }

#if SHOW_TIMINGS
                stopwatch timer_edge_joining = new stopwatch();
                timer_edge_joining.Start();
#endif

                // connect edges which are a short distance apart
                edge_detector.ConnectBrokenEdges(connect_edges_radius);
                
#if SHOW_TIMINGS
                timer_edge_joining.Stop();
                if (timer_edge_joining.time_elapsed_mS > 20)
                    Console.WriteLine("DetectSquaresMono: edge joining  " + timer_edge_joining.time_elapsed_mS.ToString() );
#endif
                
                // remove edges which are outside a circular region of interest
                if (circular_ROI_radius > 0)
                {
                    int circular_ROI_radius_sqr = circular_ROI_radius * circular_ROI_radius;                    
                    int half_width = img_width / 2;
                    int half_height = img_height / 2;
                    for (int i = edge_detector.edges.Count - 2; i >= 0; i -= 2)
                    {
                        int x = edge_detector.edges[i];
                        int y = edge_detector.edges[i+1];
                        int dx = x - half_width;
                        int dy = y - half_height;
                        int dist_sqr = (dx*dx)+(dy*dy);
                        if (dist_sqr > circular_ROI_radius_sqr)
                        {
                            edge_detector.edges.RemoveAt(i+1);
                            edge_detector.edges.RemoveAt(i);
                        }
                    }
                }
                edges = edge_detector.edges;

                // try different groupings
                for (int group_radius_index = 0; group_radius_index < grouping_radius_percent.Length; group_radius_index++)
                {               
#if SHOW_TIMINGS
                    stopwatch timer_grouping = new stopwatch();
                    timer_grouping.Start();
                    stopwatch timer_perim2 = new stopwatch();
                    timer_perim2.Start();
#endif
                    // group edges together into objects
                    List<List<int>> groups = 
                        GetGroups(edge_detector.edges, 
                                  img_width, img_height, image_border, 
                                  minimum_size_percent,
                                  false, max_search_depth, ignore_periphery, 
                                  grouping_radius_percent[group_radius_index]);
                    
#if SHOW_TIMINGS
                    timer_grouping.Stop();
                    if (timer_grouping.time_elapsed_mS > 20)
                        Console.WriteLine("DetectSquaresMono: edge grouping  " + timer_grouping.time_elapsed_mS.ToString() );
#endif
                    
                    if (groups != null)
                    {
#if SHOW_TIMINGS
                        stopwatch timer_aspect = new stopwatch();
                        timer_aspect.Start();
#endif

                        //List<List<int>> squares = groups;
                        
                        // get the set of edges with aspect ratio closest to square

                        List<List<int>> squares = GetValidGroups(groups,
                                                                 img_width, img_height,
                                                                 //minimum_aspect_ratio, maximum_aspect_ratio,
                                                                 minimum_size_percent);

#if SHOW_TIMINGS
                        timer_grouping.Stop();
                        if (timer_aspect.time_elapsed_mS > 20)
                            Console.WriteLine("DetectSquaresMono: aspect checking  " + timer_aspect.time_elapsed_mS.ToString() );
#endif
                        if (squares != null)                 
                        {
                            
                            for (int i = 0; i < squares.Count; i++)
                            {
#if SHOW_TIMINGS
                                stopwatch timer_periphery = new stopwatch();
                                timer_periphery.Start();
#endif

                                List<int> square = squares[i];
                                polygon2D perim = null;
                                List<List<int>> periphery = GetSquarePeriphery(square, erosion_dilation[erosion_dilation_level], ref perim);
                                
#if SHOW_TIMINGS
                                timer_periphery.Stop();
                                //if (timer_periphery.time_elapsed_mS > 20)
                                    Console.WriteLine("DetectSquaresMono: periphery detect  " + timer_periphery.time_elapsed_mS.ToString() );
#endif
                                
                                if (perim != null)
                                {
                                    float longest_side = perim.getLongestSide();
                                    float shortest_side = perim.getShortestSide();
                                    
                                    // not too small
                                    if (shortest_side * 100 / img_width > minimum_volume_percent)
                                    {
                                        float aspect = shortest_side / longest_side;
                                        
                                        if ((aspect > minimum_aspect_ratio) &&
                                            (aspect < maximum_aspect_ratio))
                                        {
                                            // check the angles
                                            bool angle_out_of_range = false;
                                            int vertex = 0;
                                            while ((vertex < perim.x_points.Count) &&
                                                   (!angle_out_of_range) && (vertex < 4))
                                            {
                                                float angle = perim.GetInteriorAngle(vertex);
                                                angle = angle / (float)Math.PI * 180;
                                                if ((angle < 70) || (angle > 110)) angle_out_of_range = true;
                                                vertex++;
                                            }
                                            
                                            if (!angle_out_of_range)
                                            {
                                                float aspect1 = perim.getSideLength(0) / perim.getSideLength(2);
                                                if ((aspect1 > 0.9f) && (aspect1 < 1.1f))
                                                {                                        
                                                    float aspect2 = perim.getSideLength(1) / perim.getSideLength(3);
                                                    if ((aspect2 > 0.9f) && (aspect2 < 1.1f))
                                                        square_shapes.Add(perim);
                                                }
                                                
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
#if SHOW_TIMINGS
                    timer_perim2.Stop();
                    if (timer_perim2.time_elapsed_mS > 20)
                        Console.WriteLine("DetectSquaresMono: grouping and periphery " + timer_perim2.time_elapsed_mS.ToString());
#endif
                }
            }

            // remove any overlapping squares
            square_shapes = SelectSquares(square_shapes, 0);
            
#if SHOW_TIMINGS
            timer_square_detection.Stop();
            if (timer_square_detection.time_elapsed_mS > 20)
                Console.WriteLine("DetectSquaresMono: " + timer_square_detection.time_elapsed_mS.ToString() );
#endif
            return(square_shapes);
        }
Пример #2
0
    	public static void ShowPeriphery(string filename, 
                                         string periphery_filename, 
                                         int grouping_radius_percent,
                                         int erosion_dilation,
                                         int minimum_size_percent,
                                         bool black_on_white,
                                         bool squares_only) 
        {
            const int max_search_depth = 8000;
            const int connect_edges_radius = 5;
            
            if (File.Exists(filename))
            {
                Bitmap bmp = (Bitmap)Bitmap.FromFile(filename);
                byte[] img = new byte[bmp.Width * bmp.Height * 3];
                byte[] squares_img = null; //new byte[bmp.Width * bmp.Height * 3];
                BitmapArrayConversions.updatebitmap(bmp, img);
                                
                // convert to mono
                byte[] img_mono = image.monoImage(img, bmp.Width, bmp.Height);
                
                if (!black_on_white)
                {
                    for (int i = 0; i < img_mono.Length; i++)
                        img_mono[i] = (byte)(255 - img_mono[i]);
                }                
                
                if (erosion_dilation > 0)
                {
                    byte[] eroded = image.Erode(img_mono, bmp.Width, bmp.Height, erosion_dilation, null);
                    img_mono = eroded;
                }
                
                if (erosion_dilation < 0)
                {
                    byte[] dilated = image.Dilate(img_mono, bmp.Width, bmp.Height, -erosion_dilation, null);
                    img_mono = dilated;
                }

                CannyEdgeDetector edge_detector = new CannyEdgeDetector();
                edge_detector.automatic_thresholds = true;
                edge_detector.Update(img_mono, bmp.Width, bmp.Height);
                edge_detector.ConnectBrokenEdges(connect_edges_radius);

                // group edges together into objects
                List<List<int>> groups = 
                        GetGroups(edge_detector.edges, 
                                  bmp.Width, bmp.Height, 0, 
                                  minimum_size_percent,
                                  false, max_search_depth, false, 
                                  grouping_radius_percent);

                if (groups != null)
                {
                    float minimum_aspect_ratio = 0.7f;
                    float maximum_aspect_ratio = 1.3f;
                    if (!squares_only)
                    {
                        minimum_aspect_ratio = 0.3f;
                        maximum_aspect_ratio = 20.0f;
                    }
                    
                    // get the set of edges with aspect ratio closest to square
                    List<List<int>> squares = GetAspectRange(groups,
                                                             bmp.Width, bmp.Height,
                                                             minimum_aspect_ratio, maximum_aspect_ratio,
                                                             minimum_size_percent, squares_only);
                    if (squares != null)                 
                    {
                        for (int i = 0; i < squares.Count; i++)
                        {
                            List<int> square = squares[i];
                            polygon2D perim = null;
                            List<List<int>> periphery = GetSquarePeriphery(square, erosion_dilation, ref perim);
                            
                            if (perim != null)
                            {
                                squares_img = ShowEdges(ref squares_img, periphery, bmp.Width, bmp.Height);
                                if (squares_img != null)
                                    perim.show(squares_img, bmp.Width, bmp.Height, 150, 150, 150, 0);
                            }
                        }
                    }
                }
                    
                Bitmap periphery_bmp = new Bitmap(bmp.Width, bmp.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                BitmapArrayConversions.updatebitmap_unsafe(squares_img, periphery_bmp);
                periphery_bmp.Save(periphery_filename, System.Drawing.Imaging.ImageFormat.Bmp);                
            }
        }
Пример #3
0
    	public static void ShowRectangles(string filename, 
                                          string square_filename,
                                          bool ignore_periphery,
                                          int image_border_percent,
                                          int grouping_radius_percent,
                                          int minimum_size_percent,
                                          bool show_centres,
                                          int erosion_dilation,
                                          bool black_on_white,
                                          int minimum_volume_percent) 
        {
            const int max_search_depth = 8000;
            const int connect_edges_radius = 5;
            
            if (File.Exists(filename))
            {
                Bitmap bmp = (Bitmap)Bitmap.FromFile(filename);
                byte[] img = new byte[bmp.Width * bmp.Height * 3];
                BitmapArrayConversions.updatebitmap(bmp, img);
                
                if (!black_on_white)
                {
                    for (int i = 0; i < img.Length; i++)
                    {
                        img[i] = (byte)(255 - img[i]);
                    }
                }
                                
                int image_border = bmp.Width * image_border_percent / 100;
                
                // convert to mono
                byte[] img_mono = image.monoImage(img, bmp.Width, bmp.Height);
                
                if (erosion_dilation > 0)
                {
                    byte[] eroded = image.Erode(img_mono, bmp.Width, bmp.Height, erosion_dilation, null);
                    img_mono = eroded;
                }
                
                if (erosion_dilation < 0)
                {
                    byte[] dilated = image.Dilate(img_mono, bmp.Width, bmp.Height, -erosion_dilation, null);
                    img_mono = dilated;
                }
                
                CannyEdgeDetector edge_detector = new CannyEdgeDetector();
                edge_detector.automatic_thresholds = true;
                edge_detector.Update(img_mono, bmp.Width, bmp.Height);
                edge_detector.ConnectBrokenEdges(connect_edges_radius);
                
                List<List<int>> groups = 
                    GetGroups(edge_detector.edges, 
                              bmp.Width, bmp.Height, image_border, 
                              minimum_size_percent,
                              false, max_search_depth, ignore_periphery, 
                              grouping_radius_percent);

                if (groups != null)
                {
                    // get the set of edges with aspect ratio closest to square
                    List<List<int>> squares = GetAspectRange(groups,
                                                            bmp.Width, bmp.Height,
                                                            0.3f, 20.0f,
                                                            minimum_size_percent, false);
                    
                    //Console.WriteLine("squares: " + squares.Count.ToString());
                    
                    if (squares != null)                    
                    {
                        byte[] squareImage = null;
                        squareImage = ShowEdges(ref squareImage, squares, bmp.Width, bmp.Height);
                        
                        Bitmap square_bmp = new Bitmap(bmp.Width, bmp.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                        BitmapArrayConversions.updatebitmap_unsafe(squareImage, square_bmp);
                        square_bmp.Save(square_filename, System.Drawing.Imaging.ImageFormat.Bmp);
                    }
                }
            }
        }
Пример #4
0
    	public static void ShowPerimeters(string filename, 
                                          string edges_filename, 
                                          string magnitudes_filename,
                                          string perimeters_filename,
                                          bool ignore_periphery,
                                          int grouping_radius_percent,
                                          int minimum_size_percent,
                                          bool show_centres,
                                          int erosion_dilation,
                                          bool black_on_white) 
        {
            const int max_search_depth = 8000;
            const int connect_edges_radius = 5;
            
            if (File.Exists(filename))
            {
                Bitmap bmp = (Bitmap)Bitmap.FromFile(filename);
                byte[] img = new byte[bmp.Width * bmp.Height * 3];
                BitmapArrayConversions.updatebitmap(bmp, img);
                
                if (!black_on_white)
                {
                    for (int i = 0; i < img.Length; i++)
                    {
                        img[i] = (byte)(255 - img[i]);
                    }
                }
                
                // convert to mono
                byte[] img_mono = image.monoImage(img, bmp.Width, bmp.Height);
                
                if (erosion_dilation > 0)
                {
                    byte[] eroded = image.Erode(img_mono, bmp.Width, bmp.Height, erosion_dilation, null);
                    img_mono = eroded;
                }
                
                if (erosion_dilation < 0)
                {
                    byte[] dilated = image.Dilate(img_mono, bmp.Width, bmp.Height, -erosion_dilation, null);
                    img_mono = dilated;
                }

                CannyEdgeDetector edge_detector = new CannyEdgeDetector();
                edge_detector.automatic_thresholds = true;
                edge_detector.Update(img_mono, bmp.Width, bmp.Height);
                edge_detector.ConnectBrokenEdges(connect_edges_radius);

                byte[] magnitudes = new byte[bmp.Width * bmp.Height * 3];
                edge_detector.ShowMagnitudes(magnitudes, bmp.Width, bmp.Height);
                Bitmap magnitudes_bmp = new Bitmap(bmp.Width, bmp.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                BitmapArrayConversions.updatebitmap_unsafe(magnitudes, magnitudes_bmp);
                magnitudes_bmp.Save(magnitudes_filename, System.Drawing.Imaging.ImageFormat.Bmp);
                
                
                /*
                byte[] perimetersImage = 
                    ShowLongestPerimeters(edge_detector.edges, 
                                          bmp.Width, bmp.Height, 
                                          0, minimum_size_percent, false,
                                          max_search_depth,
                                          ignore_periphery,
                                          show_centres);
                */
                
                List<List<int>> groups = null;
                byte[] perimetersImage = ShowGroups(edge_detector.edges,
                                                    bmp.Width, bmp.Height,
                                                    0, minimum_size_percent,
                                                    false,
                                                    max_search_depth,
                                                    ignore_periphery,
                                                    grouping_radius_percent,
                                                    ref groups);
                    
                Bitmap edges_bmp = new Bitmap(bmp.Width, bmp.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                BitmapArrayConversions.updatebitmap_unsafe(edge_detector.getEdgesImage(), edges_bmp);
                edges_bmp.Save(edges_filename, System.Drawing.Imaging.ImageFormat.Bmp);
                
                Bitmap perimeters_bmp = new Bitmap(bmp.Width, bmp.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                BitmapArrayConversions.updatebitmap_unsafe(perimetersImage, perimeters_bmp);
                perimeters_bmp.Save(perimeters_filename, System.Drawing.Imaging.ImageFormat.Bmp);
            }
        }
Пример #5
0
    	public static List<calibrationDot> DetectDots(byte[] mono_img, byte[] img_colour, 
                                                      int img_width, int img_height,
                                                      int grouping_radius_percent,
                                                      int erosion_dilation,
                                                      float minimum_width,
                                                      float maximum_width,
                                                      ref List<int> edges,
                                                      ref List<List<int>> groups)
        {
            // create a list to store the results
            List<calibrationDot> dots = null;
            const int connect_edges_radius = 5;
            int minimum_width_pixels = (int)(minimum_width * img_width / 100);
            int maximum_width_pixels = (int)(maximum_width * img_width / 100);
            int minimum_size_percent = (int)((minimum_width_pixels*minimum_width_pixels) * 100 / (img_width*img_height));
            
            // maximum recursion depth when joining edges
            const int max_search_depth = 8000;
            
            // create edge detection object
            CannyEdgeDetector edge_detector = new CannyEdgeDetector();
            edge_detector.automatic_thresholds = true;  
            edge_detector.enable_edge_orientations = false;

            byte[] img_mono = (byte[])mono_img.Clone();

            // erode
            if (erosion_dilation > 0)
            {       
                byte[] eroded = image.Erode(img_mono, img_width, img_height, 
                                            erosion_dilation, 
                                            null);
                img_mono = eroded;
            }
            
            // dilate
            if (erosion_dilation < 0)
            {
                byte[] dilated = image.Dilate(img_mono, img_width, img_height, 
                                             -erosion_dilation, 
                                              null);
                img_mono = dilated;
            }
                
            // detect edges using canny algorithm
            edge_detector.Update(img_mono, img_width, img_height);

            // connect edges which are a short distance apart
            edge_detector.ConnectBrokenEdges(connect_edges_radius);
                
            edges = edge_detector.edges;

            // group edges together into objects
            groups = GetGroups(edge_detector.edges, 
                               img_width, img_height, 0, 
                               minimum_size_percent,
                               false, max_search_depth, false, 
                               grouping_radius_percent);
                                        
            if (groups != null)
            {
                dots = GetDots(groups, img_colour,
                               img_width, img_height,
                               0.7f, 1.3f,
                               minimum_width_pixels, maximum_width_pixels);
            }

            // remove any overlapping squares
            //dot_shapes = SelectSquares(dot_shapes, 0);
            
            return(dots);
        }