/// <summary> /// A valid line segmentation may contain 0 or 0xffffff as the /// background, and otherwise numbers components starting at 1. /// The segmentation consists of segmented background pixels /// (0x80xxxx) and segmented foreground pixels (0x00xxxx). The /// segmented foreground pixels should constitute a usable /// binarization of the original image. /// </summary> public static void check_line_segmentation(Intarray cseg) { if (cseg.Length1d() == 0) { return; } if (cseg.Rank() != 2) { throw new Exception("check_line_segmentation: rank must be 2"); } for (int i = 0; i < cseg.Length1d(); i++) { int value = cseg.At1d(i); if (value == 0) { continue; } if (value == 0xffffff) { continue; } if ((value & 0x800000) > 0) { if ((value & ~0x800000) > 100000) { throw new Exception("check_line_segmentation: (value & ~0x800000) > 100000"); } } else if (value > 100000) { throw new Exception("check_line_segmentation: value > 100000"); } } }
public static void check_approximately_sorted(Intarray labels) { for (int i = 0; i < labels.Length1d(); i++) { if (labels.At1d(i) > 100000) { throw new Exception("labels out of range"); } } Narray <Rect> rboxes = new Narray <Rect>(); ImgLabels.bounding_boxes(ref rboxes, labels); #if false // TODO/tmb disabling check until the overseg issue is fixed --tmb for (int i = 1; i < rboxes.Length(); i++) { if (rboxes[i].Right < rboxes[i - 1].Left) { /*errors_log("bad segmentation", labels); * errors_log.recolor("bad segmentation (recolored)", labels); * throw_fmt("boxes aren't approximately sorted: " * "box %d is to the left from box %d", i, i-1);*/ } } #endif }
public static void rseg_to_cseg(Intarray cseg, Intarray rseg, Intarray ids) { Intarray map = new Intarray(NarrayUtil.Max(rseg) + 1); map.Fill(0); int color = 0; for (int i = 0; i < ids.Length(); i++) { if (ids[i] == 0) { continue; } color++; int start = ids[i] >> 16; int end = ids[i] & 0xFFFF; if (start > end) { throw new Exception("segmentation encoded in IDs looks seriously broken!"); } if (start >= map.Length() || end >= map.Length()) { throw new Exception("segmentation encoded in IDs doesn't fit!"); } for (int j = start; j <= end; j++) { map[j] = color; } } cseg.MakeLike(rseg); for (int i = 0; i < cseg.Length1d(); i++) { cseg.Put1d(i, map[rseg.At1d(i)]); } }
/// <summary> /// Merge segments from start to end. /// </summary> /// <param name="cseg">Output</param> /// <param name="rseg">Input</param> /// <param name="start">start merge position</param> /// <param name="end">end merge position</param> public static void rseg_to_cseg(Intarray cseg, Intarray rseg, int start, int end) { int maxSegNum = NarrayUtil.Max(rseg); if (start > end) { throw new Exception("segmentation encoded in IDs looks seriously broken!"); } if (start > maxSegNum || end > maxSegNum) { throw new Exception("segmentation encoded in IDs doesn't fit!"); } Intarray map = new Intarray(maxSegNum + 1); map.Fill(0); int color = 1; for (int i = 1; i <= maxSegNum; i++) { map[i] = color; if (i < start || i >= end) { color++; } } cseg.MakeLike(rseg); for (int i = 0; i < cseg.Length1d(); i++) { cseg.Put1d(i, map[rseg.At1d(i)]); } }
public override void Charseg(ref Intarray outimage, Bytearray inimage) { int swidth = PGeti("swidth"); int sheight = PGeti("sheight"); Bytearray image = new Bytearray(); image.Copy(inimage); OcrRoutine.binarize_simple(image); OcrRoutine.Invert(image); outimage.Copy(image); if (swidth > 0 || sheight > 0) { Morph.binary_close_rect(image, swidth, sheight); } Intarray labels = new Intarray(); labels.Copy(image); ImgLabels.label_components(ref labels); for (int i = 0; i < outimage.Length1d(); i++) { if (outimage.At1d(i) > 0) { outimage.Put1d(i, SegmRoutine.cseg_pixel(labels.At1d(i))); } } SegmRoutine.make_line_segmentation_white(outimage); SegmRoutine.check_line_segmentation(outimage); }
public static void make_line_segmentation_black(Intarray a) { check_line_segmentation(a); ImgMisc.replace_values(a, 0xFFFFFF, 0); for (int i = 0; i < a.Length1d(); i++) { a.Put1d(i, a.At1d(i) & 0xFFF); } }
public static void combine_segmentations(ref Intarray dst, Intarray src) { dst.SameDims(src); int n = NarrayUtil.Max(dst) + 1; for (int i = 0; i < dst.Length1d(); i++) { dst.Put1d(i, (dst.At1d(i) + src.At1d(i) * n)); } ImgLabels.renumber_labels(dst, 1); }
public static void check_page_segmentation(Intarray pseg) { bool allow_zero = true; Narray <bool> used = new Narray <bool>(5000); used.Fill(false); int nused = 0; int mused = 0; for (int i = 0; i < pseg.Length1d(); i++) { uint pixel = (uint)pseg.At1d(i); if (!(allow_zero || pixel != 0)) { throw new Exception("CHECK_ARG: (allow_zero || pixel != 0)"); } if (pixel == 0 || pixel == 0xffffff) { continue; } int column = (int)(0xff & (pixel >> 16)); int paragraph = (int)(0xff & (pixel >> 8)); int line = (int)(0xff & pixel); if (!((column > 0 && column < 32) || column == 254 || column == 255)) { throw new Exception("CHECK_ARG: ((column > 0 && column < 32) || column == 254 || column == 255)"); } if (!((paragraph >= 0 && paragraph < 64) || (paragraph >= 250 && paragraph <= 255))) { throw new Exception("CHECK_ARG: ((paragraph >= 0 && paragraph < 64) || (paragraph >= 250 && paragraph <= 255))"); } if (column < 32) { if (!used[line]) { nused++; } used[line] = true; if (line > mused) { mused = line; } } } // character segments need to be numbered sequentially (no gaps) // (gaps usually happen when someone passes a binary image instead of a segmentation) if (!(nused == mused || nused == mused + 1)) { Global.Debugf("warn", "check_page_segmentation found non-sequentially numbered segments"); } }
public static void remove_small_components <T>(Narray <T> bimage, int mw, int mh) { Intarray image = new Intarray(); image.Copy(bimage); ImgLabels.label_components(ref image); Narray <Rect> rects = new Narray <Rect>(); ImgLabels.bounding_boxes(ref rects, image); Bytearray good = new Bytearray(rects.Length()); for (int i = 0; i < good.Length(); i++) { good[i] = 1; } for (int i = 0; i < rects.Length(); i++) { if (rects[i].Width() < mw && rects[i].Height() < mh) { // printf("*** %d %d %d\n",i,rects[i].width(),rects[i].height()); good[i] = 0; } } for (int i = 0; i < image.Length1d(); i++) { if (good[image.At1d(i)] == 0) { image.Put1d(i, 0); } } for (int i = 0; i < image.Length1d(); i++) { if (image.At1d(i) == 0) { bimage.Put1d(i, default(T)); // default(T) - 0 } } }
/// <summary> /// Renumber the non-zero pixels in an image to start with pixel value start. /// The numerical order of pixels is preserved. /// </summary> public static int renumber_labels(Intarray image, int start = 1) { //SortedList<int, int> translation = new SortedList<int, int>(256); Dictionary <int, int> translation = new Dictionary <int, int>(256); int n = start; for (int i = 0; i < image.Length1d(); i++) { int pixel = image.At1d(i); if (pixel == 0 || pixel == 0xffffff) { continue; } if (!translation.ContainsKey(pixel)) { translation.Add(pixel, n); n++; } } n = start; int[] keys = translation.Keys.ToArray(); foreach (int key in keys) { translation[key] = n++; } for (int i = 0; i < image.Length1d(); i++) { int pixel = image.At1d(i); if (pixel == 0 || pixel == 0xffffff) { continue; } image.Put1d(i, translation[pixel]); } return(n); }
/// <summary> /// Remove segments from start to end. /// </summary> /// <param name="cseg">Output</param> /// <param name="rseg">Input</param> /// <param name="start">start remove position</param> /// <param name="end">end remove position</param> public static void rseg_to_cseg_remove(Intarray cseg, Intarray rseg, Bytearray outimg, Bytearray img, int start, int end) { int maxSegNum = NarrayUtil.Max(rseg); if (start > end) { throw new Exception("segmentation encoded in IDs looks seriously broken!"); } if (start > maxSegNum || end > maxSegNum) { throw new Exception("segmentation encoded in IDs doesn't fit!"); } if (rseg.Length1d() != img.Length1d()) { throw new Exception("rseg and img must have same a dimension!"); } Intarray map = new Intarray(maxSegNum + 1); map.Fill(0); int color = 1; for (int i = 1; i <= maxSegNum; i++) { map[i] = color; if (i < start || i > end) { color++; } else { map[i] = 0; } } cseg.MakeLike(rseg); outimg.Copy(img); for (int i = 0; i < cseg.Length1d(); i++) { int val = rseg.At1d(i); cseg.Put1d(i, map[val]); if (val > 0 && map[val] == 0) { outimg.Put1d(i, 255); } } }
public static void simple_recolor(Intarray image) { /*for (int i = 0; i < image.Length1d(); i++) * { * if (image.At1d(i) == 0) continue; * image.At1d(i) = enumerator[image.at1d(i)]; * }*/ for (int i = 0; i < image.Length1d(); i++) { int value = image.At1d(i); if (value == 0 || value == 0xffffff) { continue; } image.Put1d(i, interesting_colors(1 + value % 19)); } }
public static void line_segmentation_sort_x(Intarray segmentation) { if (NarrayUtil.Max(segmentation) > 100000) { throw new Exception("line_segmentation_merge_small_components: to many segments"); } Narray <Rect> bboxes = new Narray <Rect>(); ImgLabels.bounding_boxes(ref bboxes, segmentation); Floatarray x0s = new Floatarray(); unchecked { x0s.Push((float)-999999); } for (int i = 1; i < bboxes.Length(); i++) { if (bboxes[i].Empty()) { x0s.Push(999999); } else { x0s.Push(bboxes[i].x0); } } // dprint(x0s,1000); printf("\n"); Narray <int> permutation = new Intarray(); Narray <int> rpermutation = new Intarray(); NarrayUtil.Quicksort(permutation, x0s); rpermutation.Resize(permutation.Length()); for (int i = 0; i < permutation.Length(); i++) { rpermutation[permutation[i]] = i; } // dprint(rpermutation,1000); printf("\n"); for (int i = 0; i < segmentation.Length1d(); i++) { if (segmentation.At1d(i) == 0) { continue; } segmentation.Put1d(i, rpermutation[segmentation.At1d(i)]); } }
public static void check_approximately_sorted(Intarray labels) { for (int i = 0; i < labels.Length1d(); i++) if (labels.At1d(i) > 100000) throw new Exception("labels out of range"); Narray<Rect> rboxes = new Narray<Rect>(); ImgLabels.bounding_boxes(ref rboxes, labels); #if false // TODO/tmb disabling check until the overseg issue is fixed --tmb for(int i=1;i<rboxes.Length();i++) { if(rboxes[i].Right<rboxes[i-1].Left) { /*errors_log("bad segmentation", labels); errors_log.recolor("bad segmentation (recolored)", labels); throw_fmt("boxes aren't approximately sorted: " "box %d is to the left from box %d", i, i-1);*/ } } #endif }
public override void Charseg(ref Intarray outimage, Bytearray inimage) { int swidth = PGeti("swidth"); int sheight = PGeti("sheight"); Bytearray image = new Bytearray(); image.Copy(inimage); OcrRoutine.binarize_simple(image); OcrRoutine.Invert(image); outimage.Copy(image); if (swidth > 0 || sheight > 0) Morph.binary_close_rect(image, swidth, sheight); Intarray labels = new Intarray(); labels.Copy(image); ImgLabels.label_components(ref labels); for(int i=0; i<outimage.Length1d(); i++) if (outimage.At1d(i) > 0) outimage.Put1d(i, SegmRoutine.cseg_pixel(labels.At1d(i))); SegmRoutine.make_line_segmentation_white(outimage); SegmRoutine.check_line_segmentation(outimage); }
public override void Charseg(ref Intarray segmentation, Bytearray inraw) { setParams(); //Logger.Default.Image("segmenting", inraw); int PADDING = 3; OcrRoutine.optional_check_background_is_lighter(inraw); Bytearray image = new Bytearray(); image.Copy(inraw); OcrRoutine.binarize_simple(image); OcrRoutine.Invert(image); SetImage(image); FindAllCuts(); FindBestCuts(); Intarray seg = new Intarray(); seg.MakeLike(image); seg.Fill(255); for (int r = 0; r < bestcuts.Length(); r++) { int w = seg.Dim(0); int c = bestcuts[r]; Narray <Point> cut = cuts[c]; for (int y = 0; y < image.Dim(1); y++) { for (int i = -1; i <= 1; i++) { int x = cut[y].X; if (x < 1 || x >= w - 1) { continue; } seg[x + i, y] = 0; } } } ImgLabels.label_components(ref seg); // dshowr(seg,"YY"); dwait(); segmentation.Copy(image); for (int i = 0; i < seg.Length1d(); i++) { if (segmentation.At1d(i) == 0) { seg.Put1d(i, 0); } } ImgLabels.propagate_labels_to(ref segmentation, seg); if (PGeti("component_segmentation") > 0) { Intarray ccseg = new Intarray(); ccseg.Copy(image); ImgLabels.label_components(ref ccseg); SegmRoutine.combine_segmentations(ref segmentation, ccseg); if (PGeti("fix_diacritics") > 0) { SegmRoutine.fix_diacritics(segmentation); } } #if false SegmRoutine.line_segmentation_merge_small_components(ref segmentation, small_merge_threshold); SegmRoutine.line_segmentation_sort_x(segmentation); #endif SegmRoutine.make_line_segmentation_white(segmentation); // set_line_number(segmentation, 1); //Logger.Default.Image("resulting segmentation", segmentation); }
public override void Charseg(ref Intarray segmentation, Bytearray inraw) { setParams(); //Logger.Default.Image("segmenting", inraw); int PADDING = 3; OcrRoutine.optional_check_background_is_lighter(inraw); Bytearray image = new Bytearray(); image.Copy(inraw); OcrRoutine.binarize_simple(image); OcrRoutine.Invert(image); SetImage(image); FindAllCuts(); FindBestCuts(); Intarray seg = new Intarray(); seg.MakeLike(image); seg.Fill(255); for (int r = 0; r < bestcuts.Length(); r++) { int w = seg.Dim(0); int c = bestcuts[r]; Narray<Point> cut = cuts[c]; for (int y = 0; y < image.Dim(1); y++) { for (int i = -1; i <= 1; i++) { int x = cut[y].X; if (x < 1 || x >= w - 1) continue; seg[x + i, y] = 0; } } } ImgLabels.label_components(ref seg); // dshowr(seg,"YY"); dwait(); segmentation.Copy(image); for (int i = 0; i < seg.Length1d(); i++) if (segmentation.At1d(i) == 0) seg.Put1d(i, 0); ImgLabels.propagate_labels_to(ref segmentation, seg); if (PGeti("component_segmentation") > 0) { Intarray ccseg = new Intarray(); ccseg.Copy(image); ImgLabels.label_components(ref ccseg); SegmRoutine.combine_segmentations(ref segmentation, ccseg); if (PGeti("fix_diacritics") > 0) { SegmRoutine.fix_diacritics(segmentation); } } #if false SegmRoutine.line_segmentation_merge_small_components(ref segmentation, small_merge_threshold); SegmRoutine.line_segmentation_sort_x(segmentation); #endif SegmRoutine.make_line_segmentation_white(segmentation); // set_line_number(segmentation, 1); //Logger.Default.Image("resulting segmentation", segmentation); }
/// <summary> /// Label the connected components of an image. /// </summary> public static int label_components(ref Intarray image, bool four_connected = false) { int w = image.Dim(0), h = image.Dim(1); // We slice the image into columns and call make_set() // for every continuous segment within each column. // Maximal number of segments per column is (h + 1) / 2. // We do it `w' times, so it's w * (h + 1) / 2. // We also need to add 1 because index 0 is not used, but counted. UnionFind uf = new UnionFind(w * (h + 1) / 2 + 1); uf.make_set(0); int top = 1; for(int i=0; i<image.Length1d(); i++) image.Put1d(i, (image.At1d(i) > 0 ? 1 : 0)); //for(int i=0;i<w;i++) {image(i,0) = 0; image(i,h-1) = 0;} //for(int j=0;j<h;j++) {image(0,j) = 0; image(w-1,j) = 0;} for(int i=0; i<w; i++) { int current_label = 0; for(int j=0; j<h; j++) { int pixel = image[i,j]; int range = four_connected ? 0 : 1; for(int delta=-range; delta<=range; delta++) { int adj_label = NarrayUtil.Bat(image, i-1, j+delta, 0); if(pixel == 0) { current_label = 0; continue; } if(current_label == 0) { current_label = top; uf.make_set(top); top++; } if(adj_label > 0) { current_label = uf.find_set(current_label); adj_label = uf.find_set(adj_label); if(current_label != adj_label) { uf.make_union(current_label, adj_label); current_label = uf.find_set(current_label); adj_label = uf.find_set(adj_label); } } image[i,j] = current_label; } } } for(int i=0;i<image.Length1d();i++) { if(image.At1d(i) == 0) continue; image.Put1d(i, uf.find_set(image.At1d(i))); } return renumber_labels(image, 1); }
public static void simple_recolor(Intarray image) { /*for (int i = 0; i < image.Length1d(); i++) { if (image.At1d(i) == 0) continue; image.At1d(i) = enumerator[image.at1d(i)]; }*/ for (int i = 0; i < image.Length1d(); i++) { int value = image.At1d(i); if (value == 0 || value == 0xffffff) continue; image.Put1d(i, interesting_colors(1 + value % 19)); } }
/// <summary> /// Renumber the non-zero pixels in an image to start with pixel value start. /// The numerical order of pixels is preserved. /// </summary> public static int renumber_labels(Intarray image, int start=1) { //SortedList<int, int> translation = new SortedList<int, int>(256); Dictionary<int, int> translation = new Dictionary<int, int>(256); int n = start; for(int i=0; i<image.Length1d(); i++) { int pixel = image.At1d(i); if(pixel==0 || pixel==0xffffff) continue; if (!translation.ContainsKey(pixel)) { translation.Add(pixel, n); n++; } } n = start; int[] keys = translation.Keys.ToArray(); foreach (int key in keys) translation[key] = n++; for(int i=0;i<image.Length1d();i++) { int pixel = image.At1d(i); if(pixel==0 || pixel==0xffffff) continue; image.Put1d(i, translation[pixel]); } return n; }
/// <summary> /// Label the connected components of an image. /// </summary> public static int label_components(ref Intarray image, bool four_connected = false) { int w = image.Dim(0), h = image.Dim(1); // We slice the image into columns and call make_set() // for every continuous segment within each column. // Maximal number of segments per column is (h + 1) / 2. // We do it `w' times, so it's w * (h + 1) / 2. // We also need to add 1 because index 0 is not used, but counted. UnionFind uf = new UnionFind(w * (h + 1) / 2 + 1); uf.make_set(0); int top = 1; for (int i = 0; i < image.Length1d(); i++) { image.Put1d(i, (image.At1d(i) > 0 ? 1 : 0)); } //for(int i=0;i<w;i++) {image(i,0) = 0; image(i,h-1) = 0;} //for(int j=0;j<h;j++) {image(0,j) = 0; image(w-1,j) = 0;} for (int i = 0; i < w; i++) { int current_label = 0; for (int j = 0; j < h; j++) { int pixel = image[i, j]; int range = four_connected ? 0 : 1; for (int delta = -range; delta <= range; delta++) { int adj_label = NarrayUtil.Bat(image, i - 1, j + delta, 0); if (pixel == 0) { current_label = 0; continue; } if (current_label == 0) { current_label = top; uf.make_set(top); top++; } if (adj_label > 0) { current_label = uf.find_set(current_label); adj_label = uf.find_set(adj_label); if (current_label != adj_label) { uf.make_union(current_label, adj_label); current_label = uf.find_set(current_label); adj_label = uf.find_set(adj_label); } } image[i, j] = current_label; } } } for (int i = 0; i < image.Length1d(); i++) { if (image.At1d(i) == 0) { continue; } image.Put1d(i, uf.find_set(image.At1d(i))); } return(renumber_labels(image, 1)); }