/// <summary> /// detect regions within the given image /// </summary> /// <param name="bmp">image data</param> /// <param name="image_width">width of the image</param> /// <param name="image_height">height of the image</param> /// <param name="min_region_volume_percent">ignore regions which are smaller than this threshold</param> /// <param name="min_aspect_ratio">minimum aspect ratio</param> /// <param name="max_aspect_ratio">maxiumum aspect ratio</param> /// <param name="vertex_inflation"></param> /// <param name="vertex_angular_offset"></param> /// <param name="segmentation_threshold">thresholds to use for segmentation</param> /// <param name="segmented">segmentation map</param> /// <param name="corner_features">returns detected corner features as a list of integers, suitable for use by other functions</param> /// <param name="downsampling_factor">factor by which to reduce the original image size</param> /// <param name="border_percent">defines a border within which the centres of detected regions must be</param> /// <param name="rectangle_classification">the classification to be applied to shapes having a rectangular aspect ratio</param> /// <param name="square_classification">the classification to be applied to shapes having a square aspect ratio</param> /// <returns>list of regions detected</returns> public static regionCollection DetectRegions( byte[] bmp, int image_width, int image_height, int min_region_volume_percent, float min_aspect_ratio, float max_aspect_ratio, float vertex_inflation, float vertex_angular_offset, int[] segmentation_threshold, int[,] segmented, ArrayList corner_features, int downsampling_factor, int border_percent, byte[] bmp_mono, String rectangle_classification, String square_classification) { // dimensions of the downsampled image int small_image_width = image_width / downsampling_factor; int small_image_height = image_height / downsampling_factor; // the centre of detected regions must be inside this region // in order to be considered as candidates int min_x = small_image_width * border_percent / 100; int max_x = small_image_width - min_x; int min_y = small_image_height * border_percent / 100; int max_y = small_image_height - min_y; // stack used by the flood fill algorithm // allocating space here just saves the time needed to create the // array for each flood fill routine call int[] flood_fill_stack = new int[small_image_width * small_image_height * 5]; // this object stores the detected regions regionCollection regions = new regionCollection(); if (bmp != null) { // min region volume in pixels int min_region_volume = (image_width / downsampling_factor) * (image_height / downsampling_factor) * min_region_volume_percent / 1000; // create arrays which will be used for flood fill operations bool[,] filled = new bool[small_image_width, small_image_height]; bool[,] region_image = new bool[small_image_width, small_image_height]; for (int threshold_level = 0; threshold_level < segmentation_threshold.Length; threshold_level++) { // clear the filled array if (threshold_level > 0) for (int clear_x = 0; clear_x < small_image_width; clear_x++) for (int clear_y = 0; clear_y < small_image_height; clear_y++) filled[clear_x, clear_y] = false; int tx, ty, bx, by; for (int y = 0; y < small_image_height; y++) { for (int x = 0; x < small_image_width; x++) { if (filled[x, y] == false) if (segmented[x, y] > segmentation_threshold[threshold_level]) { tx = x; ty = y; bx = x; by = y; long pixels = 0; long av_intensity = 0; long av_x = 0, av_y = 0; // clear the region_image array for (int clear_x = 0; clear_x < small_image_width; clear_x++) for (int clear_y = 0; clear_y < small_image_height; clear_y++) region_image[clear_x, clear_y] = false; // using a linear flood fill avoids stack overflow exceptions sluggish.utilities.drawing.floodFillLinear( x, y, segmentation_threshold[threshold_level], flood_fill_stack, ref tx, ref ty, ref bx, ref by, ref pixels, ref av_intensity, segmented, filled, region_image, ref av_x, ref av_y, small_image_width, small_image_height); if (by > ty) { if (pixels > min_region_volume) { av_x = av_x / pixels; av_y = av_y / pixels; // is the centre of the region inside the border? if ((av_x > min_x) && (av_x < max_x) && (av_y > min_y) && (av_y < max_y)) { // convert values from region growing back into // the original image resolution if (downsampling_factor > 1) { av_x *= downsampling_factor; av_y *= downsampling_factor; tx *= downsampling_factor; ty *= downsampling_factor; bx *= downsampling_factor; by *= downsampling_factor; } // add an extra border int border = (bx - tx) / 20; int tx2 = tx - border; if (tx2 < 0) tx2 = 0; int ty2 = ty - border; if (ty2 < 0) ty2 = 0; int bx2 = bx + border; if (bx2 >= image_width) bx2 = image_width - 1; int by2 = by + border; if (by2 >= image_height) by2 = image_height - 1; // create a region object int wdth = bx2 - tx2; int hght = by2 - ty2; sluggish.imageprocessing.region new_region = new sluggish.imageprocessing.region(tx2, ty2, wdth, hght, region_image, segmented, (int)av_x, (int)av_y, corner_features, bmp_mono, image_width, image_height, vertex_inflation, vertex_angular_offset, downsampling_factor); if (new_region.aspect_ratio > max_aspect_ratio) new_region.classification = "text"; // add this region to the collection regions.Add(new_region); new_region.classification = rectangle_classification; if ((new_region.aspect_ratio >= min_aspect_ratio) && (new_region.aspect_ratio <= max_aspect_ratio)) { new_region.classification = square_classification; } } } } } } } } } return (regions); }
/// <summary> /// detect regions within the given image /// </summary> /// <param name="bmp">image data</param> /// <param name="image_width">width of the image</param> /// <param name="image_height">height of the image</param> /// <param name="min_region_volume_percent">ignore regions which are smaller than this threshold</param> /// <param name="min_aspect_ratio">minimum aspect ratio</param> /// <param name="max_aspect_ratio">maxiumum aspect ratio</param> /// <param name="vertex_inflation"></param> /// <param name="vertex_angular_offset"></param> /// <param name="segmentation_threshold">thresholds to use for segmentation</param> /// <param name="segmented">segmentation map</param> /// <param name="corner_features">returns detected corner features as a list of integers, suitable for use by other functions</param> /// <param name="downsampling_factor">factor by which to reduce the original image size</param> /// <param name="border_percent">defines a border within which the centres of detected regions must be</param> /// <param name="rectangle_classification">the classification to be applied to shapes having a rectangular aspect ratio</param> /// <param name="square_classification">the classification to be applied to shapes having a square aspect ratio</param> /// <returns>list of regions detected</returns> public static regionCollection DetectRegions( byte[] bmp, int image_width, int image_height, int min_region_volume_percent, float min_aspect_ratio, float max_aspect_ratio, float vertex_inflation, float vertex_angular_offset, int[] segmentation_threshold, int[,] segmented, ArrayList corner_features, int downsampling_factor, int border_percent, byte[] bmp_mono, String rectangle_classification, String square_classification) { // dimensions of the downsampled image int small_image_width = image_width / downsampling_factor; int small_image_height = image_height / downsampling_factor; // the centre of detected regions must be inside this region // in order to be considered as candidates int min_x = small_image_width * border_percent / 100; int max_x = small_image_width - min_x; int min_y = small_image_height * border_percent / 100; int max_y = small_image_height - min_y; // stack used by the flood fill algorithm // allocating space here just saves the time needed to create the // array for each flood fill routine call int[] flood_fill_stack = new int[small_image_width * small_image_height * 5]; // this object stores the detected regions regionCollection regions = new regionCollection(); if (bmp != null) { // min region volume in pixels int min_region_volume = (image_width / downsampling_factor) * (image_height / downsampling_factor) * min_region_volume_percent / 1000; // create arrays which will be used for flood fill operations bool[,] filled = new bool[small_image_width, small_image_height]; bool[,] region_image = new bool[small_image_width, small_image_height]; for (int threshold_level = 0; threshold_level < segmentation_threshold.Length; threshold_level++) { // clear the filled array if (threshold_level > 0) { for (int clear_x = 0; clear_x < small_image_width; clear_x++) { for (int clear_y = 0; clear_y < small_image_height; clear_y++) { filled[clear_x, clear_y] = false; } } } int tx, ty, bx, by; for (int y = 0; y < small_image_height; y++) { for (int x = 0; x < small_image_width; x++) { if (filled[x, y] == false) { if (segmented[x, y] > segmentation_threshold[threshold_level]) { tx = x; ty = y; bx = x; by = y; long pixels = 0; long av_intensity = 0; long av_x = 0, av_y = 0; // clear the region_image array for (int clear_x = 0; clear_x < small_image_width; clear_x++) { for (int clear_y = 0; clear_y < small_image_height; clear_y++) { region_image[clear_x, clear_y] = false; } } // using a linear flood fill avoids stack overflow exceptions sluggish.utilities.drawing.floodFillLinear( x, y, segmentation_threshold[threshold_level], flood_fill_stack, ref tx, ref ty, ref bx, ref by, ref pixels, ref av_intensity, segmented, filled, region_image, ref av_x, ref av_y, small_image_width, small_image_height); if (by > ty) { if (pixels > min_region_volume) { av_x = av_x / pixels; av_y = av_y / pixels; // is the centre of the region inside the border? if ((av_x > min_x) && (av_x < max_x) && (av_y > min_y) && (av_y < max_y)) { // convert values from region growing back into // the original image resolution if (downsampling_factor > 1) { av_x *= downsampling_factor; av_y *= downsampling_factor; tx *= downsampling_factor; ty *= downsampling_factor; bx *= downsampling_factor; by *= downsampling_factor; } // add an extra border int border = (bx - tx) / 20; int tx2 = tx - border; if (tx2 < 0) { tx2 = 0; } int ty2 = ty - border; if (ty2 < 0) { ty2 = 0; } int bx2 = bx + border; if (bx2 >= image_width) { bx2 = image_width - 1; } int by2 = by + border; if (by2 >= image_height) { by2 = image_height - 1; } // create a region object int wdth = bx2 - tx2; int hght = by2 - ty2; sluggish.imageprocessing.region new_region = new sluggish.imageprocessing.region(tx2, ty2, wdth, hght, region_image, segmented, (int)av_x, (int)av_y, corner_features, bmp_mono, image_width, image_height, vertex_inflation, vertex_angular_offset, downsampling_factor); if (new_region.aspect_ratio > max_aspect_ratio) { new_region.classification = "text"; } // add this region to the collection regions.Add(new_region); new_region.classification = rectangle_classification; if ((new_region.aspect_ratio >= min_aspect_ratio) && (new_region.aspect_ratio <= max_aspect_ratio)) { new_region.classification = square_classification; } } } } } } } } } } return(regions); }