// -*- 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: // TODO: add garbage collection of unreachable components #include #include #include #include #include #include "colib.h" #include "imgio.h" #include "imglib.h" #include "grid.h" using namespace ocropus; using namespace colib; #define MAXFLOAT 3.40282347e+38F struct Rect { int x0,y0,x1,y1; Rect():x0(1),y0(1),x1(0),y1(0) {} Rect(int x0,int y0,int x1,int y1):x0(x0),y0(y0),x1(x1),y1(y1) {} void include(int x,int y) { if(x0>x1) { x0 = x; x1 = x+1; y0 = y; y1 = y+1; } else { x0 = min(x0,x); y0 = min(y0,y); x1 = max(x1,x+1); y1 = max(y1,y+1); } } void include(const Rect &other) { include(other.x0,other.y0); include(other.x1-1,other.y1-1); //otherwise include(include()) doesnt work } int width() { return x1-x0; } int height() { return y1-y0; } }; struct Mean2 { float x,y,n,minx; Mean2() { x = 0; y = 0; n = 0; minx=MAXFLOAT; } void add(float nx,float ny) { if (nxvj) return 1; else return 0; } void index(intarray &index,floatarray &values) { int n = values.length(); index.resize(n); for(int i=0;i means(nlabels); for(int i=0;i bboxes(nlabels); for(int i=0;i 0.f) basepoint += maxShift; //DBG(500) << "basepoint: " << basepoint << std::endl; // basepoint and descender must not be at the same pixel! if (basepoint == 0) basepoint = 1; // lineInfo is just one column in the height of the line image bytearray lineInfoImage(1, image.dim(1) + maxShift + 1); // initialize "all white" fill(lineInfoImage, 255); lineInfoImage(0, basepoint) = 0; // -- mark descender -- int descender = basepoint - (int) desc; // descender must not be below bounding box! if (descender < 0 || descender == basepoint) descender = 0; lineInfoImage(0, descender) = 0; // -- mark x-height -- // xHeight and basepoint must not be at the same pixel! if (xHeight == 0) xHeight = 1; // limit x-height to not exceed bounding box if (basepoint + xHeight >= lineInfoImage.dim(1)) { xHeight = lineInfoImage.dim(1) - 2 - basepoint; } // std::cout << VAR(basepoint) << std::endl; // std::cout << VAR(xHeight) << std::endl; // std::cout << VAR(lineInfoImage.dim(1)) << std::endl; lineInfoImage(0, basepoint + xHeight) = 0; // -- mark ascender (for now top) -- lineInfoImage(0, lineInfoImage.dim(1)-1) = 0; // add the info as first element to the grid result.add(lineInfoImage); } // extract ranges of connected components //DBG(500) << "start combination of bboxes..." << ::std::endl; // Note: the older skript "classify_simple.sh" relied on a slightly different version of the following code // if you intend to use a classifier based on connected components, please check out revision 180 int offset=1; for(int i=1;ii && (bboxes(k).x0-bboxes(k-1).x1) > maxgap) { if(k>i && (((bboxes(k).x0-r.x1) > maxgap) || //since bboxes(k-1).x1 might be smaller than r.x1 (mergeabove && (r.x1>=r.x0) && (bboxes(k).y0r.y0)))) { // only merge if one cc is above other // not sure if (r.x1>=r.x0) is necessary here, because we already know that k>i ... if (maxonly==1) offset=(k)-i; skip = true; break; } if (smallsize) { // don't add small connected "sub"-components, e.g. single pixel... if(bboxes(k).width()be) continue; } //DBG(40) << "Bef: " << VAR(r.y0) << VAR(r.y1) << VAR(r.height()) << VAR(r.width()) << ::std::endl; //r.include(r.x0,r.y0-10 < 0 ? 0 : r.y0-10); //DBG(40) << "Aft: " << VAR(r.y0) << VAR(r.y1) << VAR(r.height()) << VAR(r.width()) << ::std::endl; // the merged component must be within size range if(size) { if(r.width()w1||r.height()h1) continue; } // reset bottom of bounding box to zero, to keep y position of // the character (sub image) // backup variables for the original bounding box of the merged // components to write in the group info file int bottomBak = 0, topBak = 0; if (lineInfo) { bottomBak = r.y0; topBak = r.y1; r.y0 = 0; r.y1 = image.dim(1); } printf("r=(%d %d %d %d)\n", r.x0, r.y0, r.x1, r.y1); ///////////////////////////////////////////////////// // extract pixels belonging to the merged component //DBG(600) << "Extract pixels belonging to the merged component..." << ::std::endl; bytearray sub; sub.resize(r.width(), r.height() + maxShift + 1); // 'erase' sub => paint all white fill(sub, 255); int numpixels=0; int shift = 0; for(int x=r.x0;x=i&&pixel<=j); int pX, pY; pX = x-r.x0; pY = y-r.y0; // shift the sub image according to slope // (use x center of bbox for correction) if (lineInfo && x==r.x0) { shift = 0; if (slope > 0.f) shift += maxShift; //shift -= (int) (slope * ((float) (r.x0+r.x1))/2.f); shift -= (int) (slope * (float)x); //std::cout << "\tshift: " << shift; } // draw baseline (for debugging) //if (pY + shift == basepoint) sub(pX, pY + shift) = 200; if (pixel) { // paint data in black sub(pX, pY + shift) = 0; numpixels++; } } // restore original bounding box of merged components if (lineInfo) { r.y0 = bottomBak; r.y1 = topBak; } printf("numpixels: %d\n",numpixels); // only add merged components that have a minimum number of pixels if (numpixels<4) continue; printf("survived the first continue\n"); // only add merged components that have a minimum "blackness" float blackness=((float)numpixels)/(float)(r.width()*r.height()); if (blackness<0.1) continue; printf("survived the second continue\n"); // scaled is not used... // bytearray scaled(16,16); // rescale(scaled,sub); //DBG(600) << VAR(j) << VAR(i) << VAR(offset) << ::std::endl; if(!result.add(sub)) break; //DBG(600) << "Wrote: " << VAR(j) << VAR(i) << VAR(offset) << ::std::endl; fprintf(info,"%d:%d %d,%d,%d,%d\n",i,j+offset-1+((maxonly==1&&i==nlabels-1)?1:0),r.x0,r.y0,r.x1,r.y1); } } // write the result result.save(argv[1]); } catch(const char *msg) { fprintf(stderr,"exception: %s\n",msg); exit(1); } }