private void LoadGrid(String filename) { if (File.Exists(filename)) { stopwatch grid_timer = new stopwatch(); // read the data into a byte array grid_timer.Start(); Byte[] grid_data = File.ReadAllBytes(filename); grid_timer.Stop(); lstBenchmarks.Items.Add("Disk read time " + Convert.ToString(grid_timer.time_elapsed_mS) + " mS"); // decompress the data grid_timer.Start(); Byte[] decompressed_grid_data = AcedInflator.Instance.Decompress(grid_data, 0, 0, 0); grid_timer.Stop(); lstBenchmarks.Items.Add("Decompression time " + Convert.ToString(grid_timer.time_elapsed_mS) + " mS"); grid_timer.Start(); sim.rob.LoadGrid(decompressed_grid_data); grid_timer.Stop(); lstBenchmarks.Items.Add("Creation time " + Convert.ToString(grid_timer.time_elapsed_mS) + " mS"); } }
/// <summary> /// update the robot's map /// </summary> /// <param name="state">robot state</param> private static void Update(ThreadMappingState state) { // object used for taking benchmark timings stopwatch clock = new stopwatch(); clock.Start(); // update all current poses with the observed rays state.motion.LocalGrid = state.grid; state.motion.AddObservation(state.stereo_rays, false); clock.Stop(); state.benchmark_observation_update = clock.time_elapsed_mS; // what's the relative position of the robot inside the grid ? pos3D relative_position = new pos3D(state.pose.x - state.grid.x, state.pose.y - state.grid.y, 0); relative_position.pan = state.pose.pan - state.grid.pan; clock.Start(); // garbage collect dead occupancy hypotheses state.grid.GarbageCollect(); clock.Stop(); state.benchmark_garbage_collection = clock.time_elapsed_mS; }
/// <summary> /// save the occupancy grid to file and show some benchmarks /// </summary> /// <param name="filename"></param> private void SaveGrid(String filename) { FileStream fp = new FileStream(filename, FileMode.Create); BinaryWriter binfile = new BinaryWriter(fp); lstBenchmarks.Items.Clear(); stopwatch grid_timer = new stopwatch(); grid_timer.Start(); Byte[] grid_data = sim.rob.SaveGrid(); grid_timer.Stop(); lstBenchmarks.Items.Add("Distillation time " + Convert.ToString(grid_timer.time_elapsed_mS) + " mS"); grid_timer.Start(); Byte[] compressed_grid_data = AcedDeflator.Instance.Compress(grid_data, 0, grid_data.Length, AcedCompressionLevel.Fast, 0, 0); grid_timer.Stop(); lstBenchmarks.Items.Add("Compression ratio " + Convert.ToString(100 - (int)(compressed_grid_data.Length * 100 / grid_data.Length)) + " %"); lstBenchmarks.Items.Add("Compression time " + Convert.ToString(grid_timer.time_elapsed_mS) + " mS"); grid_timer.Start(); binfile.Write(compressed_grid_data); grid_timer.Stop(); lstBenchmarks.Items.Add("Disk write time " + Convert.ToString(grid_timer.time_elapsed_mS) + " mS"); binfile.Close(); fp.Close(); }
private void update() { // load calibration data stereointerface.loadCalibration(calibration_filename); // get image data from bitmaps int bytes_per_pixel = 3; // load images into the correspondence object stereointerface.loadImage(fullres_left, image_width, image_height, true, bytes_per_pixel); stereointerface.loadImage(fullres_right, image_width, image_height, false, bytes_per_pixel); // set the quality of the disparity map stereointerface.setDisparityMapCompression(horizontal_compression, vertical_compression); clock.Start(); // perform stereo matching stereointerface.stereoMatchRun(0, 8, correspondence_algorithm_type); long correspondence_time_mS = clock.Stop(); txtStereoCorrespondenceTime.Buffer.Text = correspondence_time_mS.ToString(); // make a bitmap byte[] depthmap = new byte[image_width * image_height * 3]; stereointerface.getDisparityMap(depthmap, image_width, image_height, 0); picDepthMap.Pixbuf = GtkBitmap.createPixbuf(image_width, image_height); GtkBitmap.setBitmap(depthmap, picDepthMap); }
private void update() { // load calibration data stereointerface.loadCalibration(calibration_filename); // get image data from bitmaps int bytes_per_pixel = 3; int image_width = picLeftImage.Image.Width; int image_height = picLeftImage.Image.Height; byte[] fullres_left = new byte[image_width * image_height * bytes_per_pixel]; byte[] fullres_right = new byte[fullres_left.Length]; BitmapArrayConversions.updatebitmap((Bitmap)picLeftImage.Image, fullres_left); BitmapArrayConversions.updatebitmap((Bitmap)picRightImage.Image, fullres_right); // load images into the correspondence object stereointerface.loadImage(fullres_left, image_width, image_height, true, bytes_per_pixel); stereointerface.loadImage(fullres_right, image_width, image_height, false, bytes_per_pixel); // set the quality of the disparity map stereointerface.setDisparityMapCompression(horizontal_compression, vertical_compression); clock.Start(); // perform stereo matching stereointerface.stereoMatchRun(0, 8, correspondence_algorithm_type); long correspondence_time_mS = clock.Stop(); txtStereoCorrespondenceTime.Text = correspondence_time_mS.ToString(); // make a bitmap Bitmap depthmap_bmp = new Bitmap(image_width, image_height, System.Drawing.Imaging.PixelFormat.Format24bppRgb); picDepthMap.Image = depthmap_bmp; byte[] depthmap = new byte[image_width * image_height * 3]; stereointerface.getDisparityMap(depthmap, image_width, image_height, 0); BitmapArrayConversions.updatebitmap_unsafe(depthmap, depthmap_bmp); picDepthMap.Refresh(); }
private void timUpdate_Tick(object sender, EventArgs e) { int processingTime; bool readyToUpdate = false; if (((left_imageloaded) && (right_imageloaded)) || ((left_camera_running) && (right_camera_running))) { // get images from the two cameras captureCameraImages(); if (!((left_camera_running) && (right_camera_running))) { //update from loaded images picLeftImage.Image = new Bitmap(left_image_filename); picRightImage.Image = new Bitmap(right_image_filename); global_variables.standard_width = picLeftImage.Image.Width; global_variables.standard_height = picLeftImage.Image.Height; // initialise arrays if necessary if (global_variables.left_bmp == null) { global_variables.left_bmp = new Byte[global_variables.standard_width * global_variables.standard_height * 3]; global_variables.right_bmp = new Byte[global_variables.standard_width * global_variables.standard_height * 3]; } left_image = (Bitmap)picLeftImage.Image; right_image = (Bitmap)picRightImage.Image; // copy bitmap data into byte arrays BitmapArrayConversions.updatebitmap(left_image, global_variables.left_bmp); BitmapArrayConversions.updatebitmap(right_image, global_variables.right_bmp); readyToUpdate = true; } else { //update from live cameras if ((captureState[0] == 2) && (captureState[1] == 2)) { // reset the capture state, so the system knows it can begin // grabbing frames once again captureState[0] = 0; captureState[1] = 0; readyToUpdate = true; } } // we have the images. Now it's time to do something with them... if (readyToUpdate) { // start timing the stereo processing clock.Start(); // find stereo correspondences stereo.stereoMatch(global_variables.left_bmp, global_variables.right_bmp, global_variables.standard_width, global_variables.standard_height, true); // initialise some bitmaps to show the output of stereo processing if (!outputInitialised) { picOutput.Image = new Bitmap(global_variables.standard_width, global_variables.standard_height, PixelFormat.Format24bppRgb); disp_bmp_data = new Byte[global_variables.standard_width * global_variables.standard_height * 3]; outputInitialised = true; } // how long did the processing take ? processingTime = (int)clock.Stop(); // show stereo disparities as circles switch(display_type) { case 0: { showDisparities(); break; } case 1: { break; } case 2: { showDisparityMap(); break; } case 3: { break; } case 4: { break; } case 5: { showDisparityMap(); break; } } } } }
/// <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); }
/// <summary> /// returns a polygon representing the periphery of a square region /// </summary> /// <param name="edges"> /// edges within the square region <see cref="List`1"/> /// </param> /// <param name="erode_dilate"> /// erosion or dilation level <see cref="System.Int32"/> /// </param> /// <param name="perim"> /// returned periphery <see cref="polygon2D"/> /// </param> /// <returns> /// set of edges around the periphery <see cref="List`1"/> /// </returns> private static List<List<int>> GetSquarePeriphery(List<int> edges, int erode_dilate, ref polygon2D perim) { List<List<int>> result = new List<List<int>>(); perim = new polygon2D(); // find the bounding box for all edges int tx = 99999; int ty = 99999; int bx = -99999; int by = -99999; for (int i = edges.Count-2; i >= 0; i -= 2) { int x = edges[i]; int y = edges[i+1]; if (x < tx) tx = x; if (y < ty) ty = y; if (x > bx) bx = x; if (y > by) by = y; } int w = bx - tx; int h = by - ty; if ((w > 0) && (h > 0)) { int[] left = new int[h+1]; int[] right = new int[h+1]; int[] top = new int[w+1]; int[] bottom = new int[w+1]; for (int i = edges.Count - 2; i >= 0; i -= 2) { int x = edges[i]; int x2 = x - tx; int y = edges[i+1]; int y2 = y - ty; // left side if ((left[y2] == 0) || (x < left[y2])) left[y2] = x; // right side if ((right[y2] == 0) || (x > right[y2])) right[y2] = x; // top if ((top[x2] == 0) || (y < top[x2])) top[x2] = y; // bottom if ((bottom[x2] == 0) || (y > bottom[x2])) bottom[x2] = y; } #if SHOW_TIMINGS stopwatch timer_best_fit = new stopwatch(); timer_best_fit.Start(); #endif // find a best fit line for the left side int best_start = 0; int best_end = 0; int hits = BestFitLine(left, ref best_start, ref best_end); float left_x0 = left[best_start]; float left_y0 = ty + best_start; float left_x1 = left[best_end]; float left_y1 = ty + best_end; /* BestFitLineAverage(left, ref left_x0, ref left_y0, ref left_x1, ref left_y1); left_y0 += ty; left_y1 += ty; */ // find a best fit line for the right side best_start = 0; best_end = 0; hits = BestFitLine(right, ref best_start, ref best_end); float right_x0 = right[best_start]; float right_y0 = ty + best_start; float right_x1 = right[best_end]; float right_y1 = ty + best_end; /* BestFitLineAverage(right, ref right_x0, ref right_y0, ref right_x1, ref right_y1); right_y0 += ty; right_y1 += ty; */ // find a best fit line for the top side best_start = 0; best_end = 0; hits = BestFitLine(top, ref best_start, ref best_end); float top_x0 = tx + best_start; float top_y0 = top[best_start]; float top_x1 = tx + best_end; float top_y1 = top[best_end]; /* BestFitLineAverage(top, ref top_x0, ref top_y0, ref top_x1, ref top_y1); top_x0 += tx; top_x1 += tx; */ // find a best fit line for the bottom side best_start = 0; best_end = 0; hits = BestFitLine(bottom, ref best_start, ref best_end); float bottom_x0 = tx + best_start; float bottom_y0 = bottom[best_start]; float bottom_x1 = tx + best_end; float bottom_y1 = bottom[best_end]; /* BestFitLineAverage(bottom, ref bottom_x0, ref bottom_y0, ref bottom_x1, ref bottom_y1); bottom_x0 += tx; bottom_x1 += tx; */ #if SHOW_TIMINGS timer_best_fit.Stop(); if (timer_best_fit.time_elapsed_mS > 20) Console.WriteLine("GetSquarePeriphery: best fit " + timer_best_fit.time_elapsed_mS.ToString() ); #endif // find the intersection between the left side and the top side float ix=0; float iy = 0; geometry.intersection(left_x1, left_y1, left_x0, left_y0, top_x1, top_y1, top_x0, top_y0, ref ix, ref iy); perim.Add(ix, iy); // find the intersection between the right side and the top side ix = 0; iy = 0; geometry.intersection(right_x1, right_y1, right_x0, right_y0, top_x0, top_y0, top_x1, top_y1, ref ix, ref iy); perim.Add(ix, iy); // find the intersection between the right side and the bottom side ix = 0; iy = 0; geometry.intersection(right_x1, right_y1, right_x0, right_y0, bottom_x0, bottom_y0, bottom_x1, bottom_y1, ref ix, ref iy); perim.Add(ix, iy); // find the intersection between the left side and the bottom side ix = 0; iy = 0; geometry.intersection(left_x1, left_y1, left_x0, left_y0, bottom_x0, bottom_y0, bottom_x1, bottom_y1, ref ix, ref iy); perim.Add(ix, iy); // left and right List<int> left_edges = new List<int>(); List<int> right_edges = new List<int>(); for (int y = h; y >= 0; y--) { if (left[y] != 0) { left_edges.Add(left[y]); left_edges.Add(ty + y); } if (right[y] != 0) { right_edges.Add(right[y]); right_edges.Add(ty + y); } } // top and bottom List<int> top_edges = new List<int>(); List<int> bottom_edges = new List<int>(); for (int x = w; x >= 0; x--) { if (top[x] != 0) { top_edges.Add(tx + x); top_edges.Add(top[x]); } if (bottom[x] != 0) { bottom_edges.Add(tx + x); bottom_edges.Add(bottom[x]); } } float aspect_check = perim.getShortestSide() / perim.getLongestSide(); if (aspect_check > 0.2f) { result.Add(left_edges); result.Add(right_edges); result.Add(top_edges); result.Add(bottom_edges); } else perim = null; } // shrink the perimeter according to the erosion/dilation value if ((perim != null) && (erode_dilate != 0)) { if (perim.x_points != null) { float shrink_percent = (erode_dilate*2) / (perim.getPerimeterLength()/4.0f); perim = perim.Scale(1.0f - shrink_percent); } else perim = null; } return(result); }
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); }