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