// -*- C++ -*- // Copyright 2006-2007 Deutsches Forschungszentrum fuer Kuenstliche Intelligenz // or its licensors, as applicable. // Copyright 1995-2005 by Thomas M. Breuel // // You may not use this file except under the terms of the accompanying license. // // Licensed under the Apache License, Version 2.0 (the "License"); you // may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Project: // File: // Purpose: // Responsible: tmb // Reviewer: // Primary Repository: // Web Sites: // FIXME this should really work "word"-wise, centered on each word, // otherwise it does the wrong thing for non-deskewed lines // (it worked "word"-wise in the original version) #include #include #include #include #include "colib.h" #include "imgio.h" #include "imglib.h" #include "segmentation.h" #include "queue.h" #include "ocr-utils.h" #include "ocr-segmentations.h" #include "logger.h" #include "iulib/dgraphics.h" using namespace ocropus; using namespace iulib; using namespace colib; namespace { Logger log_main("lineseg.seg-cuts"); } namespace hacked_labels { // FIXME get rid of this --tmb struct UnionFind { narray p,rank; UnionFind(int max=10000) { p.resize(max); fill(p,-1); rank.resize(max); fill(rank,-1); } void make_set(int x) { if(x<0) throw "range error (UnionFind::make_set)"; p[x] = x; rank[x] = 0; } void make_union(int x,int y) { if(x==y) return; link(find_set(x),find_set(y)); } void link(int x,int y) { if(rank[x]>rank[y]) { p[y] = x; } else { p[x] = y; if(rank[x]==rank[y]) rank[y]++; } } int find_set(int x) { if(x<0) throw "range error (UnionFind::find_set)"; if(p[x]<0) throw "trying to find a set that hasn't been created yet"; if(x!=p[x]) p[x] = find_set(p[x]); return p[x]; } }; /// Label the connected components of an image. static int label_components_internal(intarray &image, intarray &guidance, bool four_connected) { 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(w * (h + 1) / 2 + 1); uf.make_set(0); int top = 1; for(int i=0;i= h) continue; int adj_label = image.at(i-1,j+delta); int adj_guide = guidance.at(i-1,j+delta); if(adj_label && guide == adj_guide) { 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=unsigned(n)) continue; if(data(k)>=lmin) continue; lmin = data(k); } result(i) = lmin; } } static void local_minima(intarray &result,floatarray &data,int r,float threshold) { int n = data.length(); result.clear(); floatarray lmin; local_min(lmin,data,r); for(int i=1;i > cuts; floatarray cutcosts; CurvedCutSegmenterImpl() { //params_for_chars(); params_for_lines(); //params_from_hwrec_c(); } void params_for_lines() { down_cost = 0; outside_diagonal_cost = 4; inside_diagonal_cost = 4; boundary_diagonal_cost = 0; outside_weight = 0; boundary_weight = -1; inside_weight = 4; min_range = 3; //min_thresh = -2.0; min_thresh = 10.0; } // this function calculates the actual costs void step(int x0,int x1,int y) { int w = wimage.dim(0),h = wimage.dim(1); Queue queue(w*h); for(int i=x0;incost) { costs(i,j+direction) = ncost; sources(i,j+direction) = i; if(j+direction!=limit) queue.enqueue(point(i,j+direction)); } if(i>low) { if(wimage(i,j)==0) ncost = cost+wimage(i,j)+outside_diagonal_cost; else if(wimage(i,j)>0) ncost = cost+wimage(i,j)+inside_diagonal_cost; else if(wimage(i,j)<0) ncost = cost+wimage(i,j)+boundary_diagonal_cost; if(costs(i-1,j+direction)>ncost) { costs(i-1,j+direction) = ncost; sources(i-1,j+direction) = i; if(j+direction!=limit) queue.enqueue(point(i-1,j+direction)); } } if(i0) ncost = cost+wimage(i,j)+inside_diagonal_cost; else if(wimage(i,j)<0) ncost = cost+wimage(i,j)+boundary_diagonal_cost; if(costs(i+1,j+direction)>ncost) { costs(i+1,j+direction) = ncost; sources(i+1,j+direction) = i; if(j+direction!=limit) queue.enqueue(point(i+1,j+direction)); } } } } void find_allcuts() { int w = wimage.dim(0), h = wimage.dim(1); // initialize dimensions of cuts, costs etc cuts.resize(w); cutcosts.resize(w); costs.resize(w,h); sources.resize(w,h); fill(costs, 1000000000); for(int i=0;i bottom; int i = x, j = where; while(j>=0) { bottom.push(point(i,j)); i = sources(i,j); j--; } //cuts(x).resize(h); for(i=bottom.length()-1;i>=0;i--) cuts(x).push(bottom(i)); } fill(costs, 1000000000); for(int i=0;i top; int i = x, j = where; while(jwhere) top.push(point(i,j)); i = sources(i,j); j++; } for(i=0;i &cut = cuts(bestcuts(i)); for(int j=0;j bboxes; bounding_boxes(bboxes,segmentation); floatarray x0s; x0s.push(-999999); for(int i=1;ilength1d();i++) { if(segmentation->at1d(i)==0) continue; segmentation->at1d(i) = rpermutation(segmentation->at1d(i)); } } void remove_small_components(lineseg segmentation,int r=5) { CHECK(max(*segmentation)<100000); narray bboxes; bounding_boxes(bboxes,segmentation); for(int i=1;i bboxes; bounding_boxes(bboxes,segmentation); bboxes(0) = rectangle(); bool changed; do { changed = false; for(int i=1;i=r || b.height()>=r) continue; #if 1 floatarray dists; for(int j=0;j segmenter; int small_merge_threshold; CurvedCutSegmenterToISegmentLineAdapter() { small_merge_threshold = 1; } virtual const char *description() { return "curved cut segmenter"; } virtual void set(const char *key,const char *value) { log_main.format("set parameter %s to sf", key, value); if(!strcmp(key,"debug")) segmenter->debug = value; else throw "unknown key"; } virtual void set(const char *key,double value) { log_main.format("set parameter %s to %f", key, value); if(!strcmp(key,"down_cost")) segmenter->down_cost = (int)value; else if(!strcmp(key,"small_merge_threshold")) small_merge_threshold = (int)value; else if(!strcmp(key,"outside_diagonal_cost")) segmenter->outside_diagonal_cost = (int)value; else if(!strcmp(key,"inside_diagonal_cost")) segmenter->inside_diagonal_cost = (int)value; else if(!strcmp(key,"boundary_diagonal_cost")) segmenter->boundary_diagonal_cost = (int)value; else if(!strcmp(key,"outside_weight")) segmenter->outside_weight = (int)value; else if(!strcmp(key,"boundary_weight")) segmenter->boundary_weight = (int)value; else if(!strcmp(key,"inside_weight")) segmenter->inside_weight = (int)value; else if(!strcmp(key,"min_range")) segmenter->min_range = (int)value; else if(!strcmp(key,"min_thresh")) segmenter->min_thresh = value; else throw "unknown key"; } virtual void charseg(intarray &result_segmentation,bytearray &orig_image) { log_main("segmenting", orig_image); enum {PADDING = 3}; bytearray image; copy(image, orig_image); optional_check_background_is_lighter(image); binarize_simple(image); invert(image); pad_by(image, PADDING, PADDING); intarray segmentation; // pass image to segmenter segmenter->set_image(image); // find all cuts in the image segmenter->find_allcuts(); // choose the best of all cuts segmenter->find_bestcuts(); segmentation.resize(image.dim(0),image.dim(1)); for(int i=0;ibestcuts.length();r++) { int c = segmenter->bestcuts(r); narray &cut = segmenter->cuts(c); for(int y=0;y