/// <summary> /// does this polygon overlap with the other, within the given screen dimensions /// </summary> /// <param name="other">other polygon object</param> /// <param name="image_width">image width</param> /// <param name="image_height">image height</param> /// <returns></returns> public bool overlaps(polygon2D other, int image_width, int image_height) { int i; bool retval = false; i = 0; while ((i < x_points.Count) && (retval == false)) { if (other.isInside((float)x_points[i] * 1000 / image_width, (float)y_points[i] * 1000 / image_height)) { retval = true; } i++; } i = 0; while ((i < other.x_points.Count) && (retval == false)) { if (isInside((float)other.x_points[i] * image_width / 1000, (float)other.y_points[i] * image_height / 1000)) { retval = true; } i++; } return(retval); }
/// <summary> /// return true if this polygon overlaps with another /// </summary> /// <param name="other"></param> /// <returns></returns> public bool overlaps(polygon2D other) { int i; bool retval = false; i = 0; while ((i < x_points.Count) && (retval == false)) { if (other.isInside((float)x_points[i], (float)y_points[i])) { retval = true; } i++; } i = 0; while ((i < other.x_points.Count) && (retval == false)) { if (isInside((float)other.x_points[i], (float)other.y_points[i])) { retval = true; } i++; } return(retval); }
/// <summary> /// return a scaled version of the polygon /// </summary> /// <param name="factor"></param> /// <returns></returns> public polygon2D Scale(float factor) { polygon2D rescaled = new polygon2D(); float centre_x = 0, centre_y = 0; getCentreOfGravity(ref centre_x, ref centre_y); for (int i = 0; i < x_points.Count; i++) { float dx = (float)x_points[i] - centre_x; float dy = (float)y_points[i] - centre_y; float x = (float)(centre_x + (dx * factor)); float y = (float)(centre_y + (dy * factor)); rescaled.Add(x, y); } return (rescaled); }
/// <summary> /// returns a copy of the polygon /// </summary> /// <returns></returns> public polygon2D Copy() { polygon2D new_poly = new polygon2D(); new_poly.name = name; new_poly.type = type; new_poly.occupied = occupied; if (x_points != null) { for (int i = 0; i < x_points.Count; i++) { float x = (float)x_points[i]; float y = (float)y_points[i]; new_poly.Add(x, y); } } return (new_poly); }
/// <summary> /// return a scaled version of the polygon /// </summary> /// <param name="factor"></param> /// <returns></returns> public polygon2D Scale(float factor) { polygon2D rescaled = new polygon2D(); float centre_x = 0, centre_y = 0; getCentreOfGravity(ref centre_x, ref centre_y); for (int i = 0; i < x_points.Count; i++) { float dx = (float)x_points[i] - centre_x; float dy = (float)y_points[i] - centre_y; float x = (float)(centre_x + (dx * factor)); float y = (float)(centre_y + (dy * factor)); rescaled.Add(x, y); } return(rescaled); }
/// <summary> /// returns a copy of the polygon /// </summary> /// <returns></returns> public polygon2D Copy() { polygon2D new_poly = new polygon2D(); new_poly.name = name; new_poly.type = type; new_poly.occupied = occupied; if (x_points != null) { for (int i = 0; i < x_points.Count; i++) { float x = (float)x_points[i]; float y = (float)y_points[i]; new_poly.Add(x, y); } } return(new_poly); }
/// <summary> /// applies a perimeter inside which spots should be contained /// any spots outside of the perimeter are removed /// </summary> /// <param name="perimeter">polygon shape of the perimeter</param> public void applySpotPerimeter(polygon2D perimeter) { if (spots != null) { for (int i = spots.Count - 1; i >= 0; i--) { blob spot = (blob)spots[i]; if (!perimeter.isInside((int)spot.x, (int)spot.y)) spots.RemoveAt(i); } } }
} /// <summary> /// after finding the horizontal and vertical axis of a region /// this removes any spots which are unlikely to lie inside the /// axis of a square or rectangular region /// </summary> /// <param name="spots">list of spot features</param> /// <param name="shear_angle_point">angle defining the primary axis of the region</param> private void removeSpots(ArrayList spots, float[,] shear_angle_point) { if (shear_angle_point != null) { polygon2D area_perimeter = new polygon2D(); float tx = shear_angle_point[0, 0]; float ty = shear_angle_point[0, 1]; float cx = shear_angle_point[1, 0]; float cy = shear_angle_point[1, 1]; float bx = shear_angle_point[2, 0]; float by = shear_angle_point[2, 1]; float dx1 = cx - tx; float dy1 = cy - ty; float dx2 = cx - bx; float dy2 = cy - by; float dx = dx1; if (Math.Abs(dx2) > Math.Abs(dx1)) dx = dx2; float dy = dy1; if (Math.Abs(dy2) > Math.Abs(dy1)) dy = dy2; // add a small border float x_offset = 4; float y_offset = 4; if (dx < 0) x_offset = -x_offset; if (dy < 0) y_offset = -y_offset; // create a polygon inside which the spot features are expected to lie area_perimeter.Add(tx + x_offset, ty + y_offset); area_perimeter.Add(cx + x_offset, cy + y_offset); area_perimeter.Add(bx + x_offset, by + y_offset); area_perimeter.Add(bx + (tx - cx) + x_offset, by + (ty - cy) + y_offset); // remove any spots outside of this perimeter for (int i = spots.Count - 1; i >= 0; i--) { blob spot = (blob)spots[i]; if (!area_perimeter.isInside(spot.interpolated_x, spot.interpolated_y)) spots.RemoveAt(i); }
public void Update(ArrayList polygons) { DateTime current_t = DateTime.Now; // forward prediction: update the state of all tracked polygons UpdatePositionOrientation(); // matching: do any of the tracked polygons match what we can currently see? for (int i = 0; i < polygons.Count; i++) { polygon2D poly = (polygon2D)polygons[i]; float poly_width = poly.right() - poly.left(); float poly_height = poly.bottom() - poly.top(); float av_radius = (poly_width + poly_height) / 4.0f; float[] orient = poly.getOrientations(); float[] grad = poly.getGradients(); if ((poly_width > minimum_dimension) && (poly_height > minimum_dimension)) { // find the centre of the polygon float centre_x = 0, centre_y = 0; poly.getCentreOfGravity(ref centre_x, ref centre_y); // are any existing centre points close to this? float min_displacement = 9999; int index = -1; for (int j = 0; j < tracked.Count; j++) { polygon2DTrackerData polytrack = (polygon2DTrackerData)tracked[j]; // exclusion zone float perimeter_radius = polytrack.av_radius * 1.4f / polytrack.persistence; float cx = polytrack.centre_x; float dx = Math.Abs(cx - centre_x); if (dx < perimeter_radius) { float cy = polytrack.centre_y; float dy = Math.Abs(cy - centre_y); if (dy < perimeter_radius) { float displacement = (float)Math.Sqrt((dx * dx) + (dy * dy)); if ((displacement < min_displacement) || (min_displacement == 9999)) { min_displacement = displacement; index = j; } } } } if (index > -1) { polygon2DTrackerData matched = (polygon2DTrackerData)tracked[index]; // update the position and velocities float dx = centre_x - matched.centre_x; float dy = centre_y - matched.centre_y; //float dorient = orient - matched.orientation; TimeSpan dt_seconds = current_t.Subtract(matched.last_seen); if (dt_seconds.TotalSeconds > 0) { // use running averages here to smooth out error float momentum = 0.99f; matched.vx = (matched.vx * momentum) + ((dx / (float)dt_seconds.TotalSeconds) * (1.0f - momentum)); matched.vy = (matched.vy * momentum) + ((dy / (float)dt_seconds.TotalSeconds) * (1.0f - momentum)); //matched.v_angular = ((matched.v_angular * momentum) + (dorient / (float)dt_seconds.TotalSeconds) * (1.0f - momentum)); } matched.centre_x = centre_x; matched.centre_y = centre_y; for (int k = 0; k < 4; k++) { matched.orientation[k] = orient[k]; } // find the closest orientation /* * float min_diff = 9999; * int closest_index = -1; * for (int k = 0; k < 4; k++) * { * float diff = Math.Abs(orient[k] - matched.orientation); * if (diff < min_diff) * { * min_diff = diff; * closest_index = k; * } * } * * // update the orientation * if (closest_index > -1) * { * matched.orientation = (orient[closest_index] * 0.01f) + (matched.orientation * 0.99f); * } */ matched.last_seen = current_t; matched.persistence++; matched.av_radius += (long)av_radius; // avoid overflows if (matched.persistence > 99999) { matched.persistence /= 2; matched.av_radius /= 2; } } else { /* * float min_orient = 9999; * for (int k = 0; k < 4; k++) * { * if (orient[k] < min_orient) * { * min_orient = orient[k]; * } * } */ // add a new tracked polygon to the list polygon2DTrackerData new_poly = new polygon2DTrackerData(); new_poly.centre_x = centre_x; new_poly.centre_y = centre_y; new_poly.orientation = orient; new_poly.colour[0] = (byte)(100 + rnd.Next(155)); new_poly.colour[1] = (byte)(100 + rnd.Next(155)); new_poly.colour[2] = (byte)(100 + rnd.Next(155)); new_poly.av_radius = (long)av_radius; tracked.Add(new_poly); } } } }
/// <summary> /// returns a grey scale histogram for the given image within the given perimeter region /// </summary> /// <param name="bmp">image data</param> /// <param name="wdth">width of the image</param> /// <param name="hght">height of the image</param> /// <param name="bytes_per_pixel">number of bytes per pixel</param> /// <param name="levels">histogram levels</param> /// <param name="perimeter">perimeter region inside which to calculate the histogram</param> /// <returns></returns> public static float[] GetGreyHistogram(byte[] bmp, int wdth, int hght, int bytes_per_pixel, int levels, polygon2D perimeter) { float[] hist = new float[levels]; int tx = (int)perimeter.left(); int ty = (int)perimeter.top(); int bx = (int)perimeter.right(); int by = (int)perimeter.bottom(); for (int y = ty; y <= by; y++) { if ((y > -1) && (y < hght)) { for (int x = tx; x <= bx; x++) { if ((x > -1) && (x < wdth)) { if (perimeter.isInside(x, y)) { int n = ((y * wdth) + x) * bytes_per_pixel; float intensity = 0; for (int col = 0; col < bytes_per_pixel; col++) intensity += bmp[n + col]; intensity /= bytes_per_pixel; int bucket = (int)Math.Round(intensity * levels / 255); if (bucket >= levels) bucket = levels - 1; hist[bucket]++; } } } } } // normalise the histogram float max = 1; for (int level = 0; level < levels; level++) if (hist[level] > max) max = hist[level]; for (int level = 0; level < levels; level++) hist[level] = hist[level] / max; return (hist); }
/// <summary> /// crops line features to the given perimeter shape /// </summary> /// <param name="lines">list of line features</param> /// <param name="perimeter">defines the shape within which lines should reside</param> public static ArrayList cropLines(ArrayList lines, polygon2D perimeter) { ArrayList cropped = new ArrayList(); for (int i = 0; i < lines.Count; i++) { linefeature line1 = (linefeature)lines[i]; if (perimeter.isInside((int)line1.x0, (int)line1.y0)) if (perimeter.isInside((int)line1.x1, (int)line1.y1)) cropped.Add(line1); } return(cropped); }
/// <summary> /// create a polygon from the corners /// </summary> private polygon2D createPolygon() { polygon2D new_polygon = new sluggish.utilities.polygon2D(); for (int i = 0; i < corners.Count; i += 2) { float x = (float)corners[i]; float y = (float)corners[i + 1]; new_polygon.Add(x, y); } return (new_polygon); }
/// <summary> /// show the region within the given image /// </summary> /// <param name="bmp">image to draw into</param> /// <param name="image_width">width of the image</param> /// <param name="image_height">height of the image</param> /// <param name="bytes_per_pixel">number of bytes per pixel</param> /// <param name="line_width">line width to use when drawing</param> public void Show(byte[] bmp, int image_width, int image_height, int bytes_per_pixel, int line_width, int style) { if (bytes_per_pixel == 3) { int[] colr = new int[3]; colr[0] = 255; colr[1] = 255; colr[2] = 255; // use different colours for different types of region if (classification == "interesting area") { colr[0] = 0; colr[1] = 255; colr[2] = 0; } if (classification == "datamatrix") { colr[0] = 0; colr[1] = 255; colr[2] = 0; } if (classification == "text") { colr[0] = 255; colr[1] = 255; colr[2] = 0; } /* if (geometry_type == "square") { colr[0] = 0; colr[1] = 255; colr[2] = 255; } if (geometry_type == "triangle") { colr[0] = 255; colr[1] = 0; colr[2] = 255; } */ switch (style) { case 0: // show boxes { float prev_x = 0, prev_y = 0; float initial_x = -1, initial_y = 0; float x = 0, y = 0; for (int i = 0; i < corners.Count; i += 2) { x = tx + (float)corners[i]; y = ty + (float)corners[i + 1]; if (i > 0) { sluggish.utilities.drawing.drawLine( bmp, image_width, image_height, (int)prev_x, (int)prev_y, (int)x, (int)y, colr[0], colr[1], colr[2], line_width, false); } else { initial_x = x; initial_y = y; } prev_x = x; prev_y = y; } if (initial_x > -1) { sluggish.utilities.drawing.drawLine( bmp, image_width, image_height, (int)initial_x, (int)initial_y, (int)x, (int)y, colr[0], colr[1], colr[2], line_width, false); } break; } case 1: // show colonisation { for (int x = tx; x < tx + width; x++) { for (int y = ty; y < ty + height; y++) { if (shape[x - tx, y - ty]) { int n = ((y * image_width) + x) * 3; for (int col = 0; col < 3; col++) bmp[n + col] = (byte)colr[col]; } } } break; } case 2: // show outline { float x = 0, y = 0; float prev_x = 0; float prev_y = 0; for (int i = 0; i < outline.Count; i += 2) { x = tx + (int)outline[i]; y = ty + (int)outline[i + 1]; if (i > 0) { sluggish.utilities.drawing.drawLine( bmp, image_width, image_height, (int)prev_x, (int)prev_y, (int)x, (int)y, colr[0], colr[1], colr[2], line_width, false); } prev_x = x; prev_y = y; } // show corners for (int i = 0; i < corners.Count; i += 2) { x = tx + (float)corners[i]; y = ty + (float)corners[i + 1]; if (i / 2 != highlight_corner) { sluggish.utilities.drawing.drawCircle( bmp, image_width, image_height, (int)x, (int)y, 5, colr[0], colr[1], colr[2], line_width); } else { sluggish.utilities.drawing.drawCircle( bmp, image_width, image_height, (int)x, (int)y, 5, 255, 0, 0, line_width + 1); } } break; } case 3: // show orientations { int centre_xx = tx + centre_x; int centre_yy = ty + centre_y; int dx = (int)((major_axis_length / 2) * Math.Cos(orientation)); int dy = (int)((major_axis_length / 2) * Math.Sin(orientation)); sluggish.utilities.drawing.drawLine( bmp, image_width, image_height, centre_xx - dx, centre_yy - dy, centre_xx + dx, centre_yy + dy, colr[0], colr[1], colr[2], line_width, false); dx = (int)((minor_axis_length / 2) * Math.Cos(orientation - (Math.PI / 2))); dy = (int)((minor_axis_length / 2) * Math.Sin(orientation - (Math.PI / 2))); sluggish.utilities.drawing.drawLine( bmp, image_width, image_height, centre_xx - dx, centre_yy - dy, centre_xx + dx, centre_yy + dy, colr[0], colr[1], colr[2], line_width, false); break; } case 4: // binary threshold { if (binary_image != null) { for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) //if (polygon.isInside(x, y)) { int xx = tx + x; int yy = ty + y; int n = ((yy * image_width) + xx) * 3; for (int col = 0; col < 3; col++) if (binary_image[x, y]) bmp[n + col] = (byte)255; else bmp[n + col] = (byte)0; } } break; } case 5: // background model low { if (binary_image != null) { for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) { int xx = tx + x; int yy = ty + y; int n = ((yy * image_width) + xx) * 3; for (int col = 0; col < 3; col++) bmp[n + col] = (byte)background_low[x, y]; } } break; } case 6: // background model high { if (binary_image != null) { for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) { int xx = tx + x; int yy = ty + y; int n = ((yy * image_width) + xx) * 3; for (int col = 0; col < 3; col++) bmp[n + col] = (byte)background_high[x, y]; } } break; } case 7: // polygon { if (polygon != null) { polygon.show(bmp, image_width, image_height, 255, 255, 0, 0, tx, ty); } break; } case 8: // spot responses { if (spot_map != null) { for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) { if (polygon.isInside(x, y)) { int xx = tx + x; int yy = ty + y; int n = ((yy * image_width) + xx) * 3; byte response_value = (byte)(spot_map[x, y] * 255); if (response_value > 30) { bmp[n] = 0; bmp[n + 1] = response_value; bmp[n + 2] = response_value; } } } } break; } case 9: // spot centres { if (spots != null) { polygon = createPolygon(); for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) { if (polygon.isInside(x, y)) { int xx = tx + x; int yy = ty + y; int n = ((yy * image_width) + xx) * 3; byte value = (byte)(spot_map[x, y] * 255); if (value > 5) { bmp[n] = value; bmp[n + 1] = 0; bmp[n + 2] = 0; } } } for (int i = 0; i < spots.Count; i++) { blob spot = (blob)spots[i]; int n = (((ty + (int)Math.Round(spot.interpolated_y)) * image_width) + (tx + (int)Math.Round(spot.interpolated_x))) * 3; bmp[n] = (byte)255; bmp[n + 1] = (byte)255; bmp[n + 2] = (byte)255; } } break; } case 10: // spots { if (spots != null) { for (int i = 0; i < spots.Count; i++) { blob spot = (blob)spots[i]; int x = tx + (int)Math.Round(spot.interpolated_x); int y = ty + (int)Math.Round(spot.interpolated_y); int radius = (int)Math.Round(spot.average_radius); int r = 0; int g = 255; int b = 0; if (spot.selected) { r = 255; } if (spot.touched) { r = 255; g = 0; b = 255; } sluggish.utilities.drawing.drawCircle(bmp, image_width, image_height, x, y, radius, r, g, b, 0); } } break; } case 11: // connected points { if (spots != null) { for (int i = 0; i < spots.Count; i++) { blob spot = (blob)spots[i]; int x1 = tx + (int)spot.x; int y1 = ty + (int)spot.y; for (int j = 0; j < spot.neighbours.Count; j++) { blob neighbour = (blob)spot.neighbours[j]; int x2 = tx + (int)neighbour.x; int y2 = ty + (int)neighbour.y; sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, x1, y1, x2, y2, 0, 255, 0, 0, false); } } } break; } case 12: // shear angle { if (shear_angle_point != null) { int x0 = tx + (int)(shear_angle_point[0, 0]); int y0 = ty + (int)(shear_angle_point[0, 1]); int x1 = tx + (int)(shear_angle_point[1, 0]); int y1 = ty + (int)(shear_angle_point[1, 1]); int x2 = tx + (int)(shear_angle_point[2, 0]); int y2 = ty + (int)(shear_angle_point[2, 1]); sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, x0, y0, x1, y1, 0, 255, 0, 0, false); sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, x1, y1, x2, y2, 0, 255, 0, 0, false); } break; } case 13: // square/rectangle detection { /* if (square_shape != null) { int prev_x = 0; int prev_y = 0; for (int i = 0; i < square_shape.x_points.Count+1; i++) { int index = i; if (index >= square_shape.x_points.Count) index -= square_shape.x_points.Count; int x = tx + (int)square_shape.x_points[index]; int y = ty + (int)square_shape.y_points[index]; if (i > 0) sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, prev_x, prev_y, x, y, 0,255,0, 0, false); prev_x = x; prev_y = y; } } */ break; } case 14: // edges { for (int i = 0; i < bmp.Length; i++) { int v = (int)(bmp[i] / 2.5f); bmp[i] = (byte)v; } if (spot_radius > 0) { int grid_padding = 2; float grid_fitting_pixels_per_index = 1.12f; int edge_tracing_search_depth = 2; float edge_tracing_threshold = 0.24f; float suppression_radius_factor = 1.23f; ArrayList horizontal_lines = null; ArrayList vertical_lines = null; float[] grid_spacing_horizontal = null; float[] grid_spacing_vertical = null; float dominant_orientation = 0; float secondary_orientation = 0; float shear_angle_radians = 0; ArrayList horizontal_maxima = null; ArrayList vertical_maxima = null; polygon2D grid = fitGrid(ref horizontal_lines, ref vertical_lines, grid_fitting_pixels_per_index, ref dominant_orientation, ref secondary_orientation, ref grid_spacing_horizontal, ref grid_spacing_vertical, ref horizontal_maxima, ref vertical_maxima, ref shear_angle_radians, ref shear_angle_point, grid_padding, suppression_radius_factor, edge_tracing_search_depth, edge_tracing_threshold); for (int i = 0; i < vertical_lines.Count; i++) { linefeature line = (linefeature)vertical_lines[i]; float x0 = tx + line.x0; float y0 = ty + line.y0; float x1 = tx + line.x1; float y1 = ty + line.y1; sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, (int)x0, (int)y0, (int)x1, (int)y1, 255, 255, 0, 0, false); } for (int i = 0; i < horizontal_lines.Count; i++) { linefeature line = (linefeature)horizontal_lines[i]; float x0 = tx + line.x0; float y0 = ty + line.y0; float x1 = tx + line.x1; float y1 = ty + line.y1; sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, (int)x0, (int)y0, (int)x1, (int)y1, 255, 255, 0, 0, false); } float line_length = image_height / 2; float dxx = line_length * (float)Math.Sin(grid_orientation); float dyy = line_length * (float)Math.Cos(grid_orientation); float dxx2 = line_length * (float)Math.Sin(grid_orientation + shear_angle + (Math.PI / 2)); float dyy2 = line_length * (float)Math.Cos(grid_orientation + shear_angle + (Math.PI / 2)); float cx = tx + centre_x; float cy = ty + centre_y; //sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, (int)(cx + dxx), (int)(cy + dyy), (int)(cx - dxx), (int)(cy - dyy), 255, 0, 0, 0, false); //sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, (int)(cx + dxx2), (int)(cy + dyy2), (int)(cx - dxx2), (int)(cy - dyy2), 255, 255, 255, 0, false); dxx = line_length * (float)Math.Sin(grid_orientation); dyy = line_length * (float)Math.Cos(grid_orientation); //dxx2 = line_length * (float)Math.Sin(grid_orientation + shear_angle + (Math.PI / 2)); //dyy2 = line_length * (float)Math.Cos(grid_orientation + shear_angle + (Math.PI / 2)); dxx2 = line_length * (float)Math.Sin(grid_secondary_orientation); dyy2 = line_length * (float)Math.Cos(grid_secondary_orientation); cx = tx + centre_x; cy = ty + centre_y; //sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, (int)(cx + dxx), (int)(cy + dyy), (int)(cx - dxx), (int)(cy - dyy), 255, 0, 0, 0, false); //sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, (int)(cx + dxx2), (int)(cy + dyy2), (int)(cx - dxx2), (int)(cy - dyy2), 255, 255, 255, 0, false); sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, (int)(cx + dxx), (int)(cy + dyy), (int)(cx), (int)(cy), 255, 0, 0, 0, false); sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, (int)(cx + dxx2), (int)(cy + dyy2), (int)(cx), (int)(cy), 255, 255, 255, 0, false); } break; } case 15: // spatial frequency histogram { if (spatial_frequency_histogram != null) { // clear the image for (int i = 0; i < bmp.Length; i++) bmp[i] = 0; // find the maximum non zero index, so that we can scale the graph over the width of the image int max_index = 1; for (int d = 0; d < spatial_frequency_histogram.Length; d++) if (spatial_frequency_histogram[d] > 0.05f) max_index = d; max_index += 2; if (max_index >= spatial_frequency_histogram.Length) max_index = spatial_frequency_histogram.Length - 1; // draw the histogram int prev_x = 0; int prev_y = image_height - 1; for (int d = 0; d < max_index; d++) { int x = d * (image_width - 1) / max_index; int y = image_height - 1 - (int)(spatial_frequency_histogram[d] * (image_height - 1)); sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, prev_x, prev_y, x, y, 0, 255, 0, 0, false); prev_x = x; prev_y = y; } } break; } case 16: // grid spacings { for (int i = 0; i < bmp.Length; i++) bmp[i] = 0; if ((spot_radius > 0) && (grid_graph_horizontal != null)) { for (int axis = 0; axis < 2; axis++) { int prev_x = 0, prev_y = 0; int start_index = 0; int end_index = 0; float[] grid_spacing = grid_graph_horizontal; if (axis == 1) grid_spacing = grid_graph_vertical; for (int i = 0; i < grid_spacing.Length; i++) { if (grid_spacing[i] > 0) { end_index = i; if (start_index == 0) start_index = i; } i++; } if (end_index > start_index) { for (int i = 0; i < grid_spacing.Length; i++) { int x = (i - start_index) * image_width / (end_index - start_index); int y = image_height - 1 - (int)(grid_spacing[i] * ((image_height - 1) / 2)) - (image_height * (1 - axis) / 2); if (i > 0) sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, prev_x, prev_y, x, y, 0, 255, 0, 0, false); prev_x = x; prev_y = y; } } } } break; } case 17: // grid lines { if ((spot_radius > 0) && (grid_horizontal_maxima != null)) { int line_length = width * 120 / 100; float secondary_orientation = grid_secondary_orientation; // + shear_angle + (float)(Math.PI / 2); for (int axis = 0; axis < 2; axis++) { ArrayList grid_maxima = grid_horizontal_maxima; float orient = grid_orientation; if (axis == 1) { grid_maxima = grid_vertical_maxima; orient = secondary_orientation; } for (int i = 0; i < grid_maxima.Count; i++) { float r = (float)grid_maxima[i]; int x0 = tx + centre_x + (int)(r * Math.Sin(orient)); int y0 = ty + centre_y + (int)(r * Math.Cos(orient)); int dx = (int)(line_length / 2 * Math.Sin(orient + (Math.PI / 2))); int dy = (int)(line_length / 2 * Math.Cos(orient + (Math.PI / 2))); sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, x0, y0, x0 + dx, y0 + dy, 0, 255, 0, 0, false); sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, x0, y0, x0 - dx, y0 - dy, 0, 255, 0, 0, false); } } /* polygon2D cell = getGridCellPerimeter(10, 5, horizontal_maxima, vertical_maxima, dominant_orientation); cell.show(bmp, image_width, image_height, 255, 0, 0, 0, tx, ty); */ } break; } case 18: // grid non-uniformity { if (polygon != null) { int line_length = width; float secondary_orientation = grid_secondary_orientation; // + shear_angle + (float)(Math.PI / 2); for (int axis = 0; axis < 2; axis++) { ArrayList grid_maxima = grid_horizontal_maxima; float orient = grid_orientation; if (axis == 1) { grid_maxima = grid_vertical_maxima; orient = secondary_orientation; } for (int i = 0; i < grid_maxima.Count; i++) { float r = (float)grid_maxima[i]; int x0 = tx + centre_x + (int)(r * Math.Sin(orient)); int y0 = ty + centre_y + (int)(r * Math.Cos(orient)); int dx = (int)(line_length / 2 * Math.Sin(orient + (Math.PI / 2))); int dy = (int)(line_length / 2 * Math.Cos(orient + (Math.PI / 2))); sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, x0, y0, x0 + dx, y0 + dy, 255, 0, 0, 0, false); sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, x0, y0, x0 - dx, y0 - dy, 255, 0, 0, 0, false); } } if (polygon.x_points.Count == 4) { float x0 = (float)polygon.x_points[0]; float y0 = (float)polygon.y_points[0]; float x1 = (float)polygon.x_points[1]; float y1 = (float)polygon.y_points[1]; float x2 = (float)polygon.x_points[2]; float y2 = (float)polygon.y_points[2]; float x3 = (float)polygon.x_points[3]; float y3 = (float)polygon.y_points[3]; float dx_top = x1 - x0; float dy_top = y1 - y0; float dx_bottom = x2 - x3; float dy_bottom = y2 - y3; float dx_left = x3 - x0; float dy_left = y3 - y0; float dx_right = x2 - x1; float dy_right = y2 - y1; for (int grid_x = 0; grid_x < grid_columns; grid_x++) { float x_top = x0 + (grid_x * dx_top / grid_columns); float x_bottom = x3 + (grid_x * dx_bottom / grid_columns); float y_top = y0 + (grid_x * dy_top / grid_columns); float y_bottom = y3 + (grid_x * dy_bottom / grid_columns); sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, tx + (int)x_top, ty + (int)y_top, tx + (int)x_bottom, ty + (int)y_bottom, 0, 255, 0, 0, false); } for (int grid_y = 0; grid_y < grid_rows; grid_y++) { float x_left = x0 + (grid_y * dx_left / grid_rows); float x_right = x1 + (grid_y * dx_right / grid_rows); float y_left = y0 + (grid_y * dy_left / grid_rows); float y_right = y1 + (grid_y * dy_right / grid_rows); sluggish.utilities.drawing.drawLine(bmp, image_width, image_height, tx + (int)x_left, ty + (int)y_left, tx + (int)x_right, ty + (int)y_right, 0, 255, 0, 0, false); } } } break; } case 19: // corner features { if (corner_features != null) { for (int i = 0; i < corner_features.Count; i += 2) { int x = (int)corner_features[i]; int y = (int)corner_features[i + 1]; int n = ((y * image_width) + x) * 3; bmp[n] = 0; bmp[n + 1] = (byte)255; bmp[n + 2] = 0; } } break; } case 20: // show segmentation responses { if (segmented != null) { for (int x = tx; x < tx + width; x++) { for (int y = ty; y < ty + height; y++) { byte v = (byte)segmented[x - tx, y - ty]; int n = ((y * image_width) + x) * 3; for (int col = 0; col < 3; col++) bmp[n + col] = v; } } } break; } case 21: // horizontal maxima { int local_radius = 2; int inhibitory_radius = image_width / 50; int min_intensity = 500; int max_intensity = 2500; int image_threshold = 5; int localAverageRadius = 500; int difference_threshold = 35; int step_size = 2; int max_features_per_row = 9; float average_magnitude_horizontal = 0; ArrayList[] hor_maxima = image.horizontal_maxima(bmp, image_width, image_height, 3, max_features_per_row, local_radius, inhibitory_radius, min_intensity, max_intensity, image_threshold, localAverageRadius, difference_threshold, step_size, ref average_magnitude_horizontal); if (hor_maxima != null) { for (int y = 0; y < image_height; y += step_size) { int no_of_features = hor_maxima[y].Count; for (int i = 0; i < no_of_features; i += 2) { float x = (float)hor_maxima[y][i]; float magnitude = (float)hor_maxima[y][i + 1]; if (magnitude > average_magnitude_horizontal * 0.3f) { int radius = 2; int r = 0; int g = 255; int b = 0; sluggish.utilities.drawing.drawCircle(bmp, image_width, image_height, (int)x, y, radius, r, g, b, 0); } } } } float average_magnitude_vertical = 0; ArrayList[] ver_maxima = image.vertical_maxima(bmp, image_width, image_height, 3, max_features_per_row, local_radius, inhibitory_radius, min_intensity, max_intensity, image_threshold, localAverageRadius, difference_threshold, step_size, ref average_magnitude_vertical); if (ver_maxima != null) { for (int x = 0; x < image_width; x += step_size) { int no_of_features = ver_maxima[x].Count; for (int i = 0; i < no_of_features; i += 2) { float y = (float)ver_maxima[x][i]; float magnitude = (float)ver_maxima[x][i + 1]; if (magnitude > average_magnitude_vertical * 0.3f) { int radius = 2; int r = 0; int g = 255; int b = 0; sluggish.utilities.drawing.drawCircle(bmp, image_width, image_height, x, (int)y, radius, r, g, b, 0); } } } } break; } } } else sluggish.utilities.logging.EventLog.AddEvent("Can't display regions in a mono image"); }
/// <summary> /// return true if this polygon overlaps with another /// </summary> /// <param name="other"></param> /// <returns></returns> public bool overlaps(polygon2D other) { int i; bool retval = false; i = 0; while ((i < x_points.Count) && (retval == false)) { if (other.isInside((float)x_points[i],(float)y_points[i])) retval=true; i++; } i = 0; while ((i < other.x_points.Count) && (retval == false)) { if (isInside((float)other.x_points[i], (float)other.y_points[i])) retval = true; i++; } return (retval); }
private void Snake(bool[,] binary_image, bool BlackOnWhite, int max_itterations, Random rnd) { bool SnakeComplete = false; //set the initial parameters for the snake prevSnakeStationaryPoints = 0; snakeStationary = 0; // itterate until the snake can get no smaller int i = 0; while ((!SnakeComplete) && (i < max_itterations)) { SnakeComplete = Update(binary_image, BlackOnWhite, elasticity, gravity, rnd); i++; } // create a new polygon shape shape = new polygon2D(); for (i = 0; i < no_of_points; i++) shape.Add((int)SnakePoint[i, SNAKE_X], (int)SnakePoint[i, SNAKE_Y]); }
/// <summary> /// returns a polygon describing the perimeter of a grid cell /// at the given coordinate /// </summary> /// <param name="grid_x">x grid coordinate</param> /// <param name="grid_y">y grid coordinate</param> /// <param name="horizontal_maxima">horizontal grid line positions</param> /// <param name="vertical_maxima">vertical grid line positions</param> /// <param name="dominant_orientation">oprientation of the grid pattern</param> /// <param name="shear angle_radians">deviation from perfectly perpendicular axes</param> /// <returns>polygon object for the grid cell</returns> private polygon2D getGridCellPerimeter(int grid_x, int grid_y, ArrayList horizontal_maxima, ArrayList vertical_maxima, float dominant_orientation, float secondary_orientation, float shear_angle_radians) { polygon2D perimeter = new polygon2D(); float r0 = (float)horizontal_maxima[grid_y]; float x0 = centre_x + (float)(r0 * Math.Sin(dominant_orientation)); float y0 = centre_y + (float)(r0 * Math.Cos(dominant_orientation)); float r1 = (float)horizontal_maxima[grid_y + 1]; float x1 = centre_x + (float)(r1 * Math.Sin(dominant_orientation)); float y1 = centre_y + (float)(r1 * Math.Cos(dominant_orientation)); float r2 = (float)vertical_maxima[grid_x]; //float secondary_orientation = dominant_orientation + // shear_angle_radians + // (float)(Math.PI / 2); float x2 = (float)(r2 * Math.Sin(secondary_orientation)); float y2 = (float)(r2 * Math.Cos(secondary_orientation)); float r3 = (float)vertical_maxima[grid_x + 1]; float x3 = (float)(r3 * Math.Sin(secondary_orientation)); float y3 = (float)(r3 * Math.Cos(secondary_orientation)); perimeter.Add(x0 + x2, y0 + y2); perimeter.Add(x0 + x3, y0 + y3); perimeter.Add(x1 + x3, y1 + y3); perimeter.Add(x1 + x2, y1 + y2); return (perimeter); }
/// <summary> /// set the position of corners from the given polygon /// </summary> /// <param name="p"></param> public void SetCorners(polygon2D p) { corners = new ArrayList(); for (int i = 0; i < p.x_points.Count; i++) { corners.Add((float)(p.x_points[i])); corners.Add((float)(p.y_points[i])); } }
public region(int tx, int ty, int width, int height, bool[,] region_image, int[,] segmented, int centre_x, int centre_y, ArrayList corner_features, byte[] bmp_mono, int bmp_width, int bmp_height, float vertex_inflation, float vertex_angular_offset, int downsampling_factor) { this.tx = tx; this.ty = ty; this.width = width; this.height = height; this.centre_x = centre_x - tx; this.centre_y = centre_y - ty; shape = new bool[width, height]; mono_image = new byte[width * height]; if (segmented != null) this.segmented = new byte[width, height]; for (int y = ty; y < ty + height; y++) { int yy = y - ty; int w1 = yy * width; int w2 = y * bmp_width; for (int x = tx; x < tx + width; x++) { int xx = x - tx; int xx2 = x / downsampling_factor; int yy2 = y / downsampling_factor; if ((xx2 < bmp_width / downsampling_factor) && (yy2 < bmp_height / downsampling_factor)) { // downsampled position int downsampled_x = x / downsampling_factor; int downsampled_y = y / downsampling_factor; // get the shape as a binary image shape[xx, yy] = region_image[downsampled_x, downsampled_y]; // copy the segmentation response // this is mainly just for debugging purposes if (segmented != null) this.segmented[xx, yy] = (byte)segmented[downsampled_x, downsampled_y]; int n1 = w1 + xx; int n2 = w2 + x; mono_image[n1] = bmp_mono[n2]; } } } // record corner features which are inside the bounding box int corners_border = 10; // a small extra border around the bounding box this.corner_features = new ArrayList(); for (int i = 0; i < corner_features.Count; i += 2) { int x = (int)corner_features[i]; int y = (int)corner_features[i + 1]; if ((x >= tx - corners_border) && (x <= tx + width + corners_border)) { if ((y >= ty - corners_border) && (y <= ty + height + corners_border)) { this.corner_features.Add(x); this.corner_features.Add(y); } } } //this.corner_features = corner_features; // trace around the outline of the shape int trace_downsampling = 4; if (width < 200) trace_downsampling = 2; if (width < 100) trace_downsampling = 1; traceOutline(trace_downsampling); // find corners int min_corner_separation = 15; int angular_step_size_degrees = 10; locateCorners(min_corner_separation, 2, angular_step_size_degrees); // detect the angle of each corner detectAngles(); if (aspect_ratio > 1.3f) // for elongated shapes just calculate a bounding box // based upon minor and major axis lengths createCornersFromAxes(); else // find corners fitCorners(this.corner_features, vertex_inflation, vertex_angular_offset); // create a polygon from the corners polygon = createPolygon(); // classify the shape depending upon number of corners // and angle properties classifyShape(); }
/// <summary> /// returns a polygon shape based upon the corner points located /// </summary> /// <returns></returns> public polygon2D GetPolygon() { polygon2D poly = new polygon2D(); for (int i = 0; i < corners.Count; i += 2) { float x = tx + (float)corners[i]; float y = ty + (float)corners[i + 1]; poly.Add(x, y); } return (poly); }
/// <summary> /// fits a grid to a region containing square or checkerboard pattern /// </summary> /// <param name="horizontal_lines">horizontal line features detected</param> /// <param name="vertical_lines">vertical line features detected</param> /// <param name="grid_fitting_pixels_per_index">number of pixels to be represented by each index of the spacings frequency array</param> /// <param name="dominant_orientation">the main orientation of the region</param> /// <param name="secondary_orientation">orientation of the axis perpendicular (or nearly so) to the main region orientation</param> /// <param name="grid_spacing_horizontal">graph showing horizontal grid spacing responses</param> /// <param name="grid_spacing_vertical">graph showing vertical grid spacing responses</param> /// <param name="horizontal_maxima">horizontal grid maxima distances from the centre of the region</param> /// <param name="vertical_maxima">vertical grid maxima distances from the centre of the region</param> /// <param name="shear_angle_radians">shear angle in radians</param> /// <param name="shear_angle_point">points used to display the shear angle</param> /// <param name="grid_padding">padding cells around the perimeter of the grid</param> /// <param name="suppression_radius_factor">scaling factor used for non maximal suppression when finding grid spacings, typically in the range 1.0-3.0</param> /// <param name="edge_tracing_search_depth">when tracing along edges to build line features this defines the depth of search to use</param> /// <param name="edge_tracing_threshold">a threshold applied to the spot map to produce edge features</param> public polygon2D fitGrid(ref ArrayList horizontal_lines, ref ArrayList vertical_lines, float grid_fitting_pixels_per_index, ref float dominant_orientation, ref float secondary_orientation, ref float[] grid_spacing_horizontal, ref float[] grid_spacing_vertical, ref ArrayList horizontal_maxima, ref ArrayList vertical_maxima, ref float shear_angle_radians, ref float[,] shear_angle_point, int grid_padding, float suppression_radius_factor, int edge_tracing_search_depth, float edge_tracing_threshold) { shear_angle_radians = 0; polygon2D grid = new polygon2D(); // a threshold applied to the spot map float edge_threshold = edge_tracing_threshold; // pixels per index defines the number of pixels which will be // represented by every index of the grid spacing array // This value should be proportional to the estimated spot radius // as previously derrived from a frequency analysis of the binary image //int pixels_per_index = (int)(spot_radius/2); //if (pixels_per_index < 1) pixels_per_index = 1; // pixels per index defines the number of pixels which will be // represented by every index of the grid spacing array // This value should be proportional to the estimated spot radius // as previously derrived from a frequency analysis of the binary image float pixels_per_index = (spot_radius * grid_fitting_pixels_per_index); if (pixels_per_index < 0.1f) pixels_per_index = 0.1f; // whether to use the spot map or the binary image to find edges // if the spot radius is too small then the spot map just looks like // a blur and grid spacings are hard to distinguish clearly float square_pattern_min_spot_radius = 3.0f; float spot_radius_percent = spot_radius * 100 / width; bool use_edges_from_binary_image = false; if (spot_radius < square_pattern_min_spot_radius) use_edges_from_binary_image = true; //Console.WriteLine("Spot radius = " + spot_radius_percent.ToString()); // when tracing along edges to build line features this // defines the depth of search to use // if the spot radius is only very small (a couple of pixels) // we don't want to search too far, otherwise lines are inappropriately joined if (use_edges_from_binary_image) edge_tracing_search_depth = 1; // looking for smaller features // detect vertical and horizontal edge features // this is done by applying a threshold to the spot map // detect vertical lines by tracing along edges if (vertical_lines == null) { ArrayList[] vertical_edges = null; if (use_edges_from_binary_image) vertical_edges = sluggish.utilities.image.detectVerticalEdges(binary_image); else vertical_edges = sluggish.utilities.image.detectVerticalEdges(spot_map, edge_threshold); vertical_lines = sluggish.utilities.image.traceVerticalLines(vertical_edges, width, 2, edge_tracing_search_depth); vertical_lines = sluggish.utilities.image.cropLines(vertical_lines, polygon); } // detect horizontal lines by tracing along edges if (horizontal_lines == null) { ArrayList[] horizontal_edges = null; if (use_edges_from_binary_image) horizontal_edges = sluggish.utilities.image.detectHorizontalEdges(binary_image); else horizontal_edges = sluggish.utilities.image.detectHorizontalEdges(spot_map, edge_threshold); horizontal_lines = sluggish.utilities.image.traceHorizontalLines(horizontal_edges, width, 2, edge_tracing_search_depth); horizontal_lines = sluggish.utilities.image.cropLines(horizontal_lines, polygon); } // find the dominant orientation // best vertical orientation float score_vertical = 0; float orientation_vertical = sluggish.utilities.image.getDominantOrientation(vertical_lines, 1, ref score_vertical); // best horizontal orientation float score_horizontal = 0; float orientation_horizontal = sluggish.utilities.image.getDominantOrientation(horizontal_lines, 2, ref score_horizontal); // choose the orientation with the strongest response dominant_orientation = orientation_vertical; secondary_orientation = orientation_horizontal; if (score_horizontal > score_vertical) { dominant_orientation = orientation_horizontal - (float)(Math.PI / 2); secondary_orientation = orientation_horizontal; //dominant_orientation = orientation_horizontal; //secondary_orientation = orientation_vertical; } // keep the orientation within the range -PI/2 - PI/2 // so that it's always pointing "up" if (dominant_orientation > Math.PI / 2) dominant_orientation -= (float)Math.PI; if (dominant_orientation < -Math.PI / 2) dominant_orientation += (float)Math.PI; // calculate shear angle shear_angle_radians = orientation_vertical - orientation_horizontal; if (shear_angle_radians > 0) shear_angle_radians -= (float)(Math.PI / 2); else shear_angle_radians += (float)(Math.PI / 2); // create an array to store interceptions with the dominant axis grid_spacing_horizontal = new float[(int)Math.Round(width * 2 / pixels_per_index)]; grid_spacing_vertical = new float[(int)Math.Round(width * 2 / pixels_per_index)]; // find interception points between lines and the dominant axis int x0, y0, x1, y1; float dxx, dyy; // vector in the dominant orientation float dxx2, dyy2; // vector perpendicular to the dominant orientation float line_length = 1000; // some arbitrary length - really all that we're interested in is the orientation float[] grid_spacing = null; ArrayList lines = null; for (int axis = 0; axis < 2; axis++) { if (axis == 0) { lines = horizontal_lines; grid_spacing = grid_spacing_horizontal; // vector in the dominant orientation dxx = line_length * (float)Math.Sin(dominant_orientation); dyy = line_length * (float)Math.Cos(dominant_orientation); // vector perpendicular to the dominant orientation dxx2 = line_length * (float)Math.Sin(dominant_orientation + (Math.PI / 2)); dyy2 = line_length * (float)Math.Cos(dominant_orientation + (Math.PI / 2)); } else { lines = vertical_lines; grid_spacing = grid_spacing_vertical; // vector in the dominant orientation dxx = line_length * (float)Math.Sin(dominant_orientation + (Math.PI / 2)); dyy = line_length * (float)Math.Cos(dominant_orientation + (Math.PI / 2)); // vector perpendicular to the dominant orientation dxx2 = line_length * (float)Math.Sin(dominant_orientation); dyy2 = line_length * (float)Math.Cos(dominant_orientation); } // coordinates for a line along the axis x0 = (int)(centre_x + dxx); y0 = (int)(centre_y + dyy); x1 = (int)(centre_x - dxx); y1 = (int)(centre_y - dyy); float histogram_max = 0; for (int i = 0; i < lines.Count; i++) { linefeature line = (linefeature)lines[i]; for (int j = 0; j < 5; j++) { // use the start and end points of the line float px = line.x0; float py = line.y0; switch (j) { case 1: { px = line.x1; py = line.y1; break; } case 2: { px = line.x0 + ((line.x1 - line.x0) / 2); py = line.y0 + ((line.y1 - line.y0) / 2); break; } case 3: { px = line.x0 + ((line.x1 - line.x0) / 4); py = line.y0 + ((line.y1 - line.y0) / 4); break; } case 4: { px = line.x0 + ((line.x1 - line.x0) * 3 / 4); py = line.y0 + ((line.y1 - line.y0) * 3 / 4); break; } } // locate intersection float ix = 0, iy = 0; // intersection coordinate sluggish.utilities.geometry.intersection(x0, y0, x1, y1, px, py, px + dxx2, py + dyy2, ref ix, ref iy); if (ix != 9999) { // measure the distance of the intersection point from // the centre of the region float dx = ix - centre_x; float dy = iy - centre_y; float dist = (float)Math.Sqrt((dx * dx) + (dy * dy)); if (dist < width) { if (((axis == 0) && (dy < 0)) || ((axis == 1) && (dx < 0))) dist = -dist; int index = (int)Math.Round((dist / (float)pixels_per_index) + (grid_spacing.Length / 2.0f)); if (index >= grid_spacing.Length) index = grid_spacing.Length - 1; grid_spacing[index]++; if (grid_spacing[index] > histogram_max) histogram_max = grid_spacing[index]; } } } } if (histogram_max > 0) { for (int j = 0; j < grid_spacing.Length; j++) grid_spacing[j] /= histogram_max; } } // locate maxima within the horizontal and vertical graphs float min_grid_threshold = 0.2f; int equalisation_steps = 3; equaliseGridAxes(ref horizontal_maxima, ref vertical_maxima, grid_spacing_horizontal, grid_spacing_vertical, pixels_per_index, grid_padding, suppression_radius_factor, equalisation_steps, min_grid_threshold); return (grid); }
/// <summary> /// after finding the horizontal and vertical axis of a region /// this removes any spots which are unlikely to lie inside the /// axis of a square or rectangular region /// </summary> /// <param name="spots">list of spot features</param> /// <param name="shear_angle_point">angle defining the primary axis of the region</param> /// <param name="spot_culling_threshold">the ratio of possible out of bounds spots to the total number of spots must be below this threshold in order for out of bounds cases to be removed</param> private void removeSpots(ArrayList spots, float[,] shear_angle_point, float spot_culling_threshold) { if (shear_angle_point != null) { polygon2D area_perimeter = new polygon2D(); float tx = shear_angle_point[0, 0]; float ty = shear_angle_point[0, 1]; float cx = shear_angle_point[1, 0]; float cy = shear_angle_point[1, 1]; float bx = shear_angle_point[2, 0]; float by = shear_angle_point[2, 1]; float dx1 = cx - tx; float dy1 = cy - ty; float dx2 = cx - bx; float dy2 = cy - by; float dx = dx1; if (Math.Abs(dx2) > Math.Abs(dx1)) dx = dx2; float dy = dy1; if (Math.Abs(dy2) > Math.Abs(dy1)) dy = dy2; // add a small border float x_offset = 4; float y_offset = 4; if (dx < 0) x_offset = -x_offset; if (dy < 0) y_offset = -y_offset; // create a polygon inside which the spot features are expected to lie area_perimeter.Add(tx + x_offset, ty + y_offset); area_perimeter.Add(cx + x_offset, cy + y_offset); area_perimeter.Add(bx + x_offset, by + y_offset); area_perimeter.Add(bx + (tx - cx) + x_offset, by + (ty - cy) + y_offset); // remove any spots outside of this perimeter ArrayList potential_victims = new ArrayList(); for (int i = spots.Count - 1; i >= 0; i--) { blob spot = (blob)spots[i]; if (!area_perimeter.isInside(spot.interpolated_x, spot.interpolated_y)) { // add the index of this spot to the list of potential victims <evil laughter> potential_victims.Add(i); } } if (potential_victims.Count > 0) { // what fraction of the spots are potential victims? // if this ratio is too large then perhaps we have made a dreadful mistake! float victims_ratio = potential_victims.Count / (float)spots.Count; if (victims_ratio < spot_culling_threshold) { // let the slaughter commence for (int i = 0; i < potential_victims.Count; i++) { int victim_index = (int)potential_victims[i]; spots.RemoveAt(victim_index); } } } } }
/// <summary> /// fits a grid to a region containing spot features /// </summary> /// <param name="spots">list of detected spot features</param> /// <param name="max_distance">the maximum perpendicular distance below which the spot is considered to touch the line, in the range, typically in the range 0.0-1.0 as a fraction of the spot radius</param> /// <param name="connection_radius">radius within which spots are considered to be neighbours, typically in the range 2.0-3.0</param> /// <param name="grid_fitting_pixels_per_index">number of pixels to be represented by each index of the spacings frequency array</param> /// <param name="dominant_orientation">the main orientation of the region</param> /// <param name="secondary_orientation">orientation perpendicular (or nearly so) to the dominant orientation</param> /// <param name="grid_spacing_horizontal">graph showing horizontal grid spacing responses</param> /// <param name="grid_spacing_vertical">graph showing vertical grid spacing responses</param> /// <param name="horizontal_maxima">horizontal grid maxima distances from the centre of the region</param> /// <param name="vertical_maxima">vertical grid maxima distances from the centre of the region</param> /// <param name="shear_angle_radians">difference from perfectly perpendicular axes</param> /// <param name="shear_angle_point">points used to display the shear angle</param> /// <param name="grid_padding">padding cells around the perimeter of the grid</param> /// <param name="supression_radius_factor">scaling factor used to adjust non-maximal suppression radius when finding grid spacings, typically in the range 1.0-3.0</param> /// <param name="orientation_tollerance">maximum deviation from the preferred orientation when finding the main axis</param> /// <param name="spot_culling_threshold">threshold used to remove possible out of bounds spots, in the range 0.0 - 1.0</param> public polygon2D fitGrid(ArrayList spots, float max_distance, float connection_radius, float grid_fitting_pixels_per_index, ref float dominant_orientation, ref float secondary_orientation, ref float[] grid_spacing_horizontal, ref float[] grid_spacing_vertical, ref ArrayList horizontal_maxima, ref ArrayList vertical_maxima, ref float shear_angle_radians, ref float[,] shear_angle_point, int grid_padding, float supression_radius_factor, float orientation_tollerance, float spot_culling_threshold) { polygon2D grid = new polygon2D(); // connect neighbouring spots if (spots != null) { for (int i = 0; i < spots.Count - 1; i++) { blob spot1 = (blob)spots[i]; float neighbour_radius = spot1.average_radius * connection_radius; for (int j = i + 1; j < spots.Count; j++) { blob spot2 = (blob)spots[j]; if (spot1.AddNeighbour(spot2, neighbour_radius)) { spot2.AddNeighbour(spot1, neighbour_radius); } } } } // find the maximum number of aligned spots ArrayList spots_aligned = null; float preferred_orientation = -1; float line_centre_x0 = 0; float line_centre_y0 = 0; polynomial best_fit_line = fitLineToSpots(spots, false, ref spots_aligned, preferred_orientation, orientation_tollerance, max_distance, ref line_centre_x0, ref line_centre_y0); if (best_fit_line != null) { // find the orientation of the best fit line float sample_x = 100; float sample_y = best_fit_line.RegVal(sample_x); float hyp = (float)Math.Sqrt((sample_x * sample_x) + (sample_y * sample_y)); dominant_orientation = (float)Math.Asin(sample_x / hyp); if (sample_y < 0) dominant_orientation = (float)(Math.PI * 2) - dominant_orientation; float orient = dominant_orientation; // if the main orientation discovered is horizontally // oriented then rotate it into a vertical orientation //bool dominant_orientation_horizontal = false; if (sample_x > Math.Abs(sample_y)) { //dominant_orientation_horizontal = true; dominant_orientation -= (float)(Math.PI / 2); } // always point downwards if (hyp * Math.Cos(dominant_orientation) < 0) dominant_orientation -= (float)Math.PI; orientation = dominant_orientation; // orientation perpendicular (or nearly perpendicular) to the // dominant orientation secondary_orientation = dominant_orientation + (float)(Math.PI / 2); // find the second maximum number of aligned spots ArrayList second_spots_aligned = null; preferred_orientation = orient + (float)(Math.PI / 2); if (preferred_orientation > (float)(Math.PI * 2)) preferred_orientation -= (float)(Math.PI * 2); float line_centre_x1 = 0; float line_centre_y1 = 0; polynomial second_best_fit_line = fitLineToSpots(spots, true, ref second_spots_aligned, preferred_orientation, orientation_tollerance, max_distance, ref line_centre_x1, ref line_centre_y1); if (second_best_fit_line != null) { // find a couple of points on the second best fit line float sample_x2 = 100; float sample_y2 = second_best_fit_line.RegVal(sample_x2); //float hyp2 = (float)Math.Sqrt((sample_x2 * sample_x2) + (sample_y2 * sample_y2)); //float orient2 = (float)Math.Asin(sample_x2 / hyp2); //if (sample_y2 < 0) orient2 = (float)(Math.PI * 2) - orient2; // get the shear angle shear_angle_radians = getShearAngle( line_centre_x0, line_centre_y0, line_centre_x0 + sample_x, line_centre_y0 + sample_y, line_centre_x1, line_centre_y1, line_centre_x1 + sample_x2, line_centre_y1 + sample_y2, ref shear_angle_point); // remove spots which are unlikely to be useful removeSpots(spots, shear_angle_point, spot_culling_threshold); } // pixels per index defines the number of pixels which will be // represented by every index of the grid spacing array // This value should be proportional to the estimated spot radius // as previously derrived from a frequency analysis of the binary image float pixels_per_index = (spot_radius * grid_fitting_pixels_per_index); if (pixels_per_index < 0.1f) pixels_per_index = 0.1f; // create an array to store interceptions with the dominant axis grid_spacing_horizontal = new float[(int)(width * 2 / pixels_per_index) + 1]; grid_spacing_vertical = new float[(int)(width * 2 / pixels_per_index) + 1]; // find interception points between lines and the dominant axis int x0, y0, x1, y1; float dxx, dyy; // vector in the dominant orientation float dxx2, dyy2; // vector perpendicular to the dominant orientation float line_length = 1000; // some arbitrary length - really all that we're interested in is the orientation float[] grid_spacing = null; for (int axis = 0; axis < 2; axis++) { if (axis == 0) { grid_spacing = grid_spacing_horizontal; // vector in the dominant orientation dxx = line_length * (float)Math.Sin(dominant_orientation); dyy = line_length * (float)Math.Cos(dominant_orientation); // vector perpendicular to the dominant orientation dxx2 = line_length * (float)Math.Sin(secondary_orientation); dyy2 = line_length * (float)Math.Cos(secondary_orientation); } else { grid_spacing = grid_spacing_vertical; // vector in the dominant orientation dxx = line_length * (float)Math.Sin(secondary_orientation); dyy = line_length * (float)Math.Cos(secondary_orientation); // vector perpendicular to the dominant orientation dxx2 = line_length * (float)Math.Sin(dominant_orientation); dyy2 = line_length * (float)Math.Cos(dominant_orientation); } // coordinates for a line along the axis x0 = (int)(centre_x + dxx); y0 = (int)(centre_y + dyy); x1 = (int)(centre_x - dxx); y1 = (int)(centre_y - dyy); float histogram_max = 0; if (spots != null) { for (int i = 0; i < spots.Count; i++) { blob spot = (blob)spots[i]; float px = spot.interpolated_x; float py = spot.interpolated_y; // locate intersection float ix = 0, iy = 0; // intersection coordinate sluggish.utilities.geometry.intersection(x0, y0, x1, y1, px, py, px + dxx2, py + dyy2, ref ix, ref iy); if (ix != 9999) { // measure the distance of the intersection point from // the centre of the region float dx = ix - centre_x; float dy = iy - centre_y; float dist = (float)Math.Sqrt((dx * dx) + (dy * dy)); if (dist < width) { if (((axis == 0) && (dy < 0)) || ((axis == 1) && (dx < 0))) dist = -dist; int index = (int)Math.Round((dist / pixels_per_index) + (grid_spacing.Length / 2.0f)); if (index >= grid_spacing.Length) index = grid_spacing.Length - 1; grid_spacing[index]++; if (grid_spacing[index] > histogram_max) histogram_max = grid_spacing[index]; } } } } if (histogram_max > 0) { for (int j = 0; j < grid_spacing.Length; j++) grid_spacing[j] /= histogram_max; } } // locate maxima within the horizontal and vertical graphs float min_grid_threshold = 0.1f; int equalisation_steps = 1; equaliseGridAxes(ref horizontal_maxima, ref vertical_maxima, grid_spacing_horizontal, grid_spacing_vertical, pixels_per_index, grid_padding, supression_radius_factor, equalisation_steps, min_grid_threshold); // alternate the maxima points horizontal_maxima = alternateMaxima(horizontal_maxima); vertical_maxima = alternateMaxima(vertical_maxima); } return (grid); }
/// <summary> /// does this polygon overlap with the other, within the given screen dimensions /// </summary> /// <param name="other">other polygon object</param> /// <param name="image_width">image width</param> /// <param name="image_height">image height</param> /// <returns></returns> public bool overlaps(polygon2D other, int image_width, int image_height) { int i; bool retval = false; i = 0; while ((i < x_points.Count) && (retval == false)) { if (other.isInside((float)x_points[i] * 1000 / image_width, (float)y_points[i] * 1000 / image_height)) retval = true; i++; } i = 0; while ((i < other.x_points.Count) && (retval == false)) { if (isInside((float)other.x_points[i] * image_width / 1000, (float)other.y_points[i] * image_height / 1000)) retval = true; i++; } return (retval); }
/// <summary> /// initialise the snake based upon a polygon shape /// </summary> /// <param name="binary_image"></param> /// <param name="BlackOnWhite"></param> /// <param name="initial_points"></param> /// <param name="max_itterations"></param> /// <param name="rnd"></param> public void Snake(bool[,] binary_image, bool BlackOnWhite, polygon2D initial_points, int max_itterations, Random rnd) { if (initial_points.x_points.Count > 1) { // get the perimeter length of the initial shape float perimeter_length = initial_points.getPerimeterLength(); // distribute points evenly along the perimeter int side_index = 0; float side_length_total = 0; for (int i = 0; i < no_of_points; i++) { // position of this point along the perimeter float perimeter_position = i * perimeter_length / no_of_points; float total = side_length_total; while (total < perimeter_position) { side_length_total = total; total += initial_points.getSideLength(side_index); if (total < perimeter_position) side_index++; } float side_length = initial_points.getSideLength(side_index); if (side_length > 0) { float perimeter_diff = perimeter_position - side_length_total; float fraction = perimeter_diff / side_length; float tx = 0, ty = 0, bx = 0, by = 0; initial_points.getSidePositions(side_index, ref tx, ref ty, ref bx, ref by); float dx = bx - tx; float dy = by - ty; SnakePoint[i, SNAKE_X] = tx + (dx * fraction); SnakePoint[i, SNAKE_Y] = ty + (dy * fraction); } } Snake(binary_image, BlackOnWhite, max_itterations, rnd); } }