Leptonica 1.68
C Image Processing Library
|
00001 /*====================================================================* 00002 - Copyright (C) 2001 Leptonica. All rights reserved. 00003 - This software is distributed in the hope that it will be 00004 - useful, but with NO WARRANTY OF ANY KIND. 00005 - No author or distributor accepts responsibility to anyone for the 00006 - consequences of using this software, or for whether it serves any 00007 - particular purpose or works at all, unless he or she says so in 00008 - writing. Everyone is granted permission to copy, modify and 00009 - redistribute this source code, for commercial or non-commercial 00010 - purposes, with the following restrictions: (1) the origin of this 00011 - source code must not be misrepresented; (2) modified versions must 00012 - be plainly marked as such; and (3) this notice may not be removed 00013 - or altered from any source or modified source distribution. 00014 *====================================================================*/ 00015 00016 00017 /* 00018 * jbclass.c 00019 * 00020 * These are functions for unsupervised classification of 00021 * collections of connected components -- either characters or 00022 * words -- in binary images. They can be used as image 00023 * processing steps in jbig2 compression. 00024 * 00025 * Initialization 00026 * 00027 * JBCLASSER *jbRankHausInit() [rank hausdorff encoder] 00028 * JBCLASSER *jbCorrelationInit() [correlation encoder] 00029 * JBCLASSER *jbCorrelationInitWithoutComponents() [ditto] 00030 * static JBCLASSER *jbCorrelationInitInternal() 00031 * 00032 * Classify the pages 00033 * 00034 * l_int32 jbAddPages() 00035 * l_int32 jbAddPage() 00036 * l_int32 jbAddPageComponents() 00037 * 00038 * Rank hausdorff classifier 00039 * 00040 * l_int32 jbClassifyRankHaus() 00041 * l_int32 pixHaustest() 00042 * l_int32 pixRankHaustest() 00043 * 00044 * Binary correlation classifier 00045 * 00046 * l_int32 jbClassifyCorrelation() 00047 * 00048 * Determine the image components we start with 00049 * 00050 * l_int32 jbGetComponents() 00051 * PIX *pixWordMaskByDilation() 00052 * 00053 * Build grayscale composites (templates) 00054 * 00055 * PIXA *jbAccumulateComposites 00056 * PIXA *jbTemplatesFromComposites 00057 * 00058 * Utility functions for Classer 00059 * 00060 * JBCLASSER *jbClasserCreate() 00061 * void jbClasserDestroy() 00062 * 00063 * Utility functions for Data 00064 * 00065 * JBDATA *jbDataSave() 00066 * void jbDataDestroy() 00067 * l_int32 jbDataWrite() 00068 * JBDATA *jbDataRead() 00069 * PIXA *jbDataRender() 00070 * l_int32 jbGetULCorners() 00071 * l_int32 jbGetLLCorners() 00072 * 00073 * Static helpers 00074 * 00075 * static JBFINDCTX *findSimilarSizedTemplatesInit() 00076 * static l_int32 findSimilarSizedTemplatesNext() 00077 * static void findSimilarSizedTemplatesDestroy() 00078 * static l_int32 finalPositioningForAlignment() 00079 * 00080 * Note: this is NOT an implementation of the JPEG jbig2 00081 * proposed standard encoder, the specifications for which 00082 * can be found at http://www.jpeg.org/jbigpt2.html. 00083 * (See below for a full implementation.) 00084 * It is an implementation of the lower-level part of an encoder that: 00085 * 00086 * (1) identifies connected components that are going to be used 00087 * (2) puts them in similarity classes (this is an unsupervised 00088 * classifier), and 00089 * (3) stores the result in a simple file format (2 files, 00090 * one for templates and one for page/coordinate/template-index 00091 * quartets). 00092 * 00093 * An actual implementation of the official jbig2 encoder could 00094 * start with parts (1) and (2), and would then compress the quartets 00095 * according to the standards requirements (e.g., Huffman or 00096 * arithmetic coding of coordinate differences and image templates). 00097 * 00098 * The low-level part of the encoder provided here has the 00099 * following useful features: 00100 * 00101 * - It is accurate in the identification of templates 00102 * and classes because it uses a windowed hausdorff 00103 * distance metric. 00104 * - It is accurate in the placement of the connected 00105 * components, doing a two step process of first aligning 00106 * the the centroids of the template with those of each instance, 00107 * and then making a further correction of up to +- 1 pixel 00108 * in each direction to best align the templates. 00109 * - It is fast because it uses a morphologically based 00110 * matching algorithm to implement the hausdorff criterion, 00111 * and it selects the patterns that are possible matches 00112 * based on their size. 00113 * 00114 * We provide two different matching functions, one using Hausdorff 00115 * distance and one using a simple image correlation. 00116 * The Hausdorff method sometimes produces better results for the 00117 * same number of classes, because it gives a relatively small 00118 * effective weight to foreground pixels near the boundary, 00119 * and a relatively large weight to foreground pixels that are 00120 * not near the boundary. By effectively ignoring these boundary 00121 * pixels, Hausdorff weighting corresponds better to the expected 00122 * probabilities of the pixel values in a scanned image, where the 00123 * variations in instances of the same printed character are much 00124 * more likely to be in pixels near the boundary. By contrast, 00125 * the correlation method gives equal weight to all foreground pixels. 00126 * 00127 * For best results, use the correlation method. Correlation takes 00128 * the number of fg pixels in the AND of instance and template, 00129 * divided by the product of the number of fg pixels in instance 00130 * and template. It compares this with a threshold that, in 00131 * general, depends on the fractional coverage of the template. 00132 * For heavy text, the threshold is raised above that for light 00133 * text, By using both these parameters (basic threshold and 00134 * adjustment factor for text weight), one has more flexibility 00135 * and can arrive at the fewest substitution errors, although 00136 * this comes at the price of more templates. 00137 * 00138 * The strict Hausdorff scoring is not a rank weighting, because a 00139 * single pixel beyond the given distance will cause a match 00140 * failure. A rank Hausdorff is more robust to non-boundary noise, 00141 * but it is also more susceptible to confusing components that 00142 * should be in different classes. For implementing a jbig2 00143 * application for visually lossless binary image compression, 00144 * you have two choices: 00145 * 00146 * (1) use a 3x3 structuring element (size = 3) and a strict 00147 * Hausdorff comparison (rank = 1.0 in the rank Hausdorff 00148 * function). This will result in a minimal number of classes, 00149 * but confusion of small characters, such as italic and 00150 * non-italic lower-case 'o', can still occur. 00151 * (2) use the correlation method with a threshold of 0.85 00152 * and a weighting factor of about 0.7. This will result in 00153 * a larger number of classes, but should not be confused 00154 * either by similar small characters or by extremely 00155 * thick sans serif characters, such as in prog/cootoots.png. 00156 * 00157 * As mentioned above, if visual substitution errors must be 00158 * avoided, you should use the correlation method. 00159 * 00160 * We provide executables that show how to do the encoding: 00161 * prog/jbrankhaus.c 00162 * prog/jbcorrelation.c 00163 * 00164 * The basic flow for correlation classification goes as follows, 00165 * where specific choices have been made for parameters (Hausdorff 00166 * is the same except for initialization): 00167 * 00168 * // Initialize and save data in the classer 00169 * JBCLASSER *classer = 00170 * jbCorrelationInit(JB_CONN_COMPS, 0, 0, 0.8, 0.7); 00171 * SARRAY *safiles = getSortedPathnamesInDirectory(directory, 00172 * NULL, 0, 0); 00173 * jbAddPages(classer, safiles); 00174 * 00175 * // Save the data in a data structure for serialization, 00176 * // and write it into two files. 00177 * JBDATA *data = jbDataSave(classer); 00178 * jbDataWrite(rootname, data); 00179 * 00180 * // Reconstruct (render) the pages from the encoded data. 00181 * PIXA *pixa = jbDataRender(data, FALSE); 00182 * 00183 * Adam Langley has recently built a jbig2 standards-compliant 00184 * encoder, the first one to appear in open source. You can get 00185 * this encoder at: 00186 * 00187 * http://www.imperialviolet.org/jbig2.html 00188 * 00189 * It uses arithmetic encoding throughout. It encodes binary images 00190 * losslessly with a single arithmetic coding over the full image. 00191 * It also does both lossy and lossless encoding from connected 00192 * components, using leptonica to generate the templates representing 00193 * each cluster. 00194 */ 00195 00196 #include <string.h> 00197 #include <math.h> 00198 #include "allheaders.h" 00199 00200 /* MSVC can't handle arrays dimensioned by static const integers */ 00201 #define L_BUF_SIZE 512 00202 00203 /* For jbClassifyRankHaus(): size of border added around 00204 * pix of each c.c., to allow further processing. This 00205 * should be at least the sum of the MAX_DIFF_HEIGHT 00206 * (or MAX_DIFF_WIDTH) and one-half the size of the Sel */ 00207 static const l_int32 JB_ADDED_PIXELS = 6; 00208 00209 /* For pixHaustest(), pixRankHaustest() and pixCorrelationScore(): 00210 * choose these to be 2 or greater */ 00211 static const l_int32 MAX_DIFF_WIDTH = 2; /* use at least 2 */ 00212 static const l_int32 MAX_DIFF_HEIGHT = 2; /* use at least 2 */ 00213 00214 /* In initialization, you have the option to discard components 00215 * (cc, characters or words) that have either width or height larger 00216 * than a given size. This is convenient for jbDataSave(), because 00217 * the components are placed onto a regular lattice with cell 00218 * dimension equal to the maximum component size. The default 00219 * values are given here. If you want to save all components, 00220 * use a sufficiently large set of dimensions. */ 00221 static const l_int32 MAX_CONN_COMP_WIDTH = 350; /* default max cc width */ 00222 static const l_int32 MAX_CHAR_COMP_WIDTH = 350; /* default max char width */ 00223 static const l_int32 MAX_WORD_COMP_WIDTH = 1000; /* default max word width */ 00224 static const l_int32 MAX_COMP_HEIGHT = 120; /* default max component height */ 00225 00226 /* Max allowed dilation to merge characters into words */ 00227 #define MAX_ALLOWED_DILATION 14 00228 00229 /* This stores the state of a state machine which fetches 00230 * similar sized templates */ 00231 struct JbFindTemplatesState 00232 { 00233 JBCLASSER *classer; /* classer */ 00234 l_int32 w; /* desired width */ 00235 l_int32 h; /* desired height */ 00236 l_int32 i; /* index into two_by_two step array */ 00237 NUMA *numa; /* current number array */ 00238 l_int32 n; /* current element of numa */ 00239 }; 00240 typedef struct JbFindTemplatesState JBFINDCTX; 00241 00242 00243 /* Static initialization function */ 00244 static JBCLASSER * jbCorrelationInitInternal(l_int32 components, 00245 l_int32 maxwidth, l_int32 maxheight, l_float32 thresh, 00246 l_float32 weightfactor, l_int32 keep_components); 00247 00248 /* Static helper functions */ 00249 static JBFINDCTX * findSimilarSizedTemplatesInit(JBCLASSER *classer, PIX *pixs); 00250 static l_int32 findSimilarSizedTemplatesNext(JBFINDCTX *context); 00251 static void findSimilarSizedTemplatesDestroy(JBFINDCTX **pcontext); 00252 static l_int32 finalPositioningForAlignment(PIX *pixs, l_int32 x, l_int32 y, 00253 l_int32 idelx, l_int32 idely, PIX *pixt, 00254 l_int32 *sumtab, l_int32 *pdx, l_int32 *pdy); 00255 00256 #ifndef NO_CONSOLE_IO 00257 #define DEBUG_PLOT_CC 0 00258 #define DEBUG_CORRELATION_SCORE 0 00259 #endif /* ~NO_CONSOLE_IO */ 00260 00261 00262 /*----------------------------------------------------------------------* 00263 * Initialization * 00264 *----------------------------------------------------------------------*/ 00265 /*! 00266 * jbRankHausInit() 00267 * 00268 * Input: components (JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS) 00269 * maxwidth (of component; use 0 for default) 00270 * maxheight (of component; use 0 for default) 00271 * size (of square structuring element; 2, representing 00272 * 2x2 sel, is necessary for reasonable accuracy of 00273 * small components; combine this with rank ~ 0.97 00274 * to avoid undue class expansion) 00275 * rank (rank val of match, each way; in [0.5 - 1.0]; 00276 * when using size = 2, 0.97 is a reasonable value) 00277 * Return: jbclasser if OK; NULL on error 00278 */ 00279 JBCLASSER * 00280 jbRankHausInit(l_int32 components, 00281 l_int32 maxwidth, 00282 l_int32 maxheight, 00283 l_int32 size, 00284 l_float32 rank) 00285 { 00286 JBCLASSER *classer; 00287 00288 PROCNAME("jbRankHausInit"); 00289 00290 if (components != JB_CONN_COMPS && components != JB_CHARACTERS && 00291 components != JB_WORDS) 00292 return (JBCLASSER *)ERROR_PTR("invalid components", procName, NULL); 00293 if (size < 1 || size > 10) 00294 return (JBCLASSER *)ERROR_PTR("size not reasonable", procName, NULL); 00295 if (rank < 0.5 || rank > 1.0) 00296 return (JBCLASSER *)ERROR_PTR("rank not in [0.5-1.0]", procName, NULL); 00297 if (maxwidth == 0) { 00298 if (components == JB_CONN_COMPS) 00299 maxwidth = MAX_CONN_COMP_WIDTH; 00300 else if (components == JB_CHARACTERS) 00301 maxwidth = MAX_CHAR_COMP_WIDTH; 00302 else /* JB_WORDS */ 00303 maxwidth = MAX_WORD_COMP_WIDTH; 00304 } 00305 if (maxheight == 0) 00306 maxheight = MAX_COMP_HEIGHT; 00307 00308 if ((classer = jbClasserCreate(JB_RANKHAUS, components)) == NULL) 00309 return (JBCLASSER *)ERROR_PTR("classer not made", procName, NULL); 00310 classer->maxwidth = maxwidth; 00311 classer->maxheight = maxheight; 00312 classer->sizehaus = size; 00313 classer->rankhaus = rank; 00314 classer->nahash = numaHashCreate(5507, 4); /* 5507 is prime */ 00315 return classer; 00316 } 00317 00318 00319 /*! 00320 * jbCorrelationInit() 00321 * 00322 * Input: components (JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS) 00323 * maxwidth (of component; use 0 for default) 00324 * maxheight (of component; use 0 for default) 00325 * thresh (value for correlation score: in [0.4 - 0.98]) 00326 * weightfactor (corrects thresh for thick characters [0.0 - 1.0]) 00327 * Return: jbclasser if OK; NULL on error 00328 * 00329 * Notes: 00330 * (1) For scanned text, suggested input values are: 00331 * thresh ~ [0.8 - 0.85] 00332 * weightfactor ~ [0.5 - 0.6] 00333 * (2) For electronically generated fonts (e.g., rasterized pdf), 00334 * a very high thresh (e.g., 0.95) will not cause a significant 00335 * increase in the number of classes. 00336 */ 00337 JBCLASSER * 00338 jbCorrelationInit(l_int32 components, 00339 l_int32 maxwidth, 00340 l_int32 maxheight, 00341 l_float32 thresh, 00342 l_float32 weightfactor) 00343 { 00344 return jbCorrelationInitInternal(components, maxwidth, maxheight, thresh, 00345 weightfactor, 1); 00346 } 00347 00348 /*! 00349 * jbCorrelationInitWithoutComponents() 00350 * 00351 * Input: same as jbCorrelationInit 00352 * Output: same as jbCorrelationInit 00353 * 00354 * Note: acts the same as jbCorrelationInit(), but the resulting 00355 * object doesn't keep a list of all the components. 00356 */ 00357 JBCLASSER * 00358 jbCorrelationInitWithoutComponents(l_int32 components, 00359 l_int32 maxwidth, 00360 l_int32 maxheight, 00361 l_float32 thresh, 00362 l_float32 weightfactor) 00363 { 00364 return jbCorrelationInitInternal(components, maxwidth, maxheight, thresh, 00365 weightfactor, 0); 00366 } 00367 00368 00369 static JBCLASSER * 00370 jbCorrelationInitInternal(l_int32 components, 00371 l_int32 maxwidth, 00372 l_int32 maxheight, 00373 l_float32 thresh, 00374 l_float32 weightfactor, 00375 l_int32 keep_components) 00376 { 00377 JBCLASSER *classer; 00378 00379 PROCNAME("jbCorrelationInitInternal"); 00380 00381 if (components != JB_CONN_COMPS && components != JB_CHARACTERS && 00382 components != JB_WORDS) 00383 return (JBCLASSER *)ERROR_PTR("invalid components", procName, NULL); 00384 if (thresh < 0.4 || thresh > 0.98) 00385 return (JBCLASSER *)ERROR_PTR("thresh not in range [0.4 - 0.98]", 00386 procName, NULL); 00387 if (weightfactor < 0.0 || weightfactor > 1.0) 00388 return (JBCLASSER *)ERROR_PTR("weightfactor not in range [0.0 - 1.0]", 00389 procName, NULL); 00390 if (maxwidth == 0) { 00391 if (components == JB_CONN_COMPS) 00392 maxwidth = MAX_CONN_COMP_WIDTH; 00393 else if (components == JB_CHARACTERS) 00394 maxwidth = MAX_CHAR_COMP_WIDTH; 00395 else /* JB_WORDS */ 00396 maxwidth = MAX_WORD_COMP_WIDTH; 00397 } 00398 if (maxheight == 0) 00399 maxheight = MAX_COMP_HEIGHT; 00400 00401 00402 if ((classer = jbClasserCreate(JB_CORRELATION, components)) == NULL) 00403 return (JBCLASSER *)ERROR_PTR("classer not made", procName, NULL); 00404 classer->maxwidth = maxwidth; 00405 classer->maxheight = maxheight; 00406 classer->thresh = thresh; 00407 classer->weightfactor = weightfactor; 00408 classer->nahash = numaHashCreate(5507, 4); /* 5507 is prime */ 00409 classer->keep_pixaa = keep_components; 00410 return classer; 00411 } 00412 00413 00414 /*----------------------------------------------------------------------* 00415 * Classify the pages * 00416 *----------------------------------------------------------------------*/ 00417 /*! 00418 * jbAddPages() 00419 * 00420 * Input: jbclasser 00421 * safiles (of page image file names) 00422 * Return: 0 if OK; 1 on error 00423 * 00424 * Note: 00425 * (1) jbclasser makes a copy of the array of file names. 00426 * (2) The caller is still responsible for destroying the input array. 00427 */ 00428 l_int32 00429 jbAddPages(JBCLASSER *classer, 00430 SARRAY *safiles) 00431 { 00432 l_int32 i, nfiles; 00433 char *fname; 00434 PIX *pix; 00435 00436 PROCNAME("jbAddPages"); 00437 00438 if (!classer) 00439 return ERROR_INT("classer not defined", procName, 1); 00440 if (!safiles) 00441 return ERROR_INT("safiles not defined", procName, 1); 00442 00443 classer->safiles = sarrayCopy(safiles); 00444 nfiles = sarrayGetCount(safiles); 00445 for (i = 0; i < nfiles; i++) { 00446 fname = sarrayGetString(safiles, i, 0); 00447 if ((pix = pixRead(fname)) == NULL) { 00448 L_WARNING_INT("image file %d not read", procName, i); 00449 continue; 00450 } 00451 if (pixGetDepth(pix) != 1) { 00452 L_WARNING_INT("image file %d not 1 bpp", procName, i); 00453 continue; 00454 } 00455 jbAddPage(classer, pix); 00456 pixDestroy(&pix); 00457 } 00458 00459 return 0; 00460 } 00461 00462 00463 /*! 00464 * jbAddPage() 00465 * 00466 * Input: jbclasser 00467 * pixs (of input page) 00468 * Return: 0 if OK; 1 on error 00469 */ 00470 l_int32 00471 jbAddPage(JBCLASSER *classer, 00472 PIX *pixs) 00473 { 00474 BOXA *boxas; 00475 PIXA *pixas; 00476 00477 PROCNAME("jbAddPage"); 00478 00479 if (!classer) 00480 return ERROR_INT("classer not defined", procName, 1); 00481 if (!pixs || pixGetDepth(pixs) != 1) 00482 return ERROR_INT("pixs not defined or not 1 bpp", procName, 1); 00483 00484 classer->w = pixGetWidth(pixs); 00485 classer->h = pixGetHeight(pixs); 00486 00487 /* Get the appropriate components and their bounding boxes */ 00488 if (jbGetComponents(pixs, classer->components, classer->maxwidth, 00489 classer->maxheight, &boxas, &pixas)) { 00490 return ERROR_INT("components not made", procName, 1); 00491 } 00492 00493 jbAddPageComponents(classer, pixs, boxas, pixas); 00494 boxaDestroy(&boxas); 00495 pixaDestroy(&pixas); 00496 return 0; 00497 } 00498 00499 00500 /*! 00501 * jbAddPageComponents() 00502 * 00503 * Input: jbclasser 00504 * pixs (of input page) 00505 * boxas (b.b. of components for this page) 00506 * pixas (components for this page) 00507 * Return: 0 if OK; 1 on error 00508 * 00509 * Notes: 00510 * (1) If there are no components on the page, we don't require input 00511 * of empty boxas or pixas, although that's the typical situation. 00512 */ 00513 l_int32 00514 jbAddPageComponents(JBCLASSER *classer, 00515 PIX *pixs, 00516 BOXA *boxas, 00517 PIXA *pixas) 00518 { 00519 l_int32 n; 00520 00521 PROCNAME("jbAddPageComponents"); 00522 00523 if (!classer) 00524 return ERROR_INT("classer not defined", procName, 1); 00525 if (!pixs) 00526 return ERROR_INT("pix not defined", procName, 1); 00527 00528 /* Test for no components on the current page. Always update the 00529 * number of pages processed, even if nothing is on it. */ 00530 if (!boxas || !pixas || (boxaGetCount(boxas) == 0)) { 00531 classer->npages++; 00532 return 0; 00533 } 00534 00535 /* Get classes. For hausdorff, it uses a specified size of 00536 * structuring element and specified rank. For correlation, 00537 * it uses a specified threshold. */ 00538 if (classer->method == JB_RANKHAUS) { 00539 if (jbClassifyRankHaus(classer, boxas, pixas)) 00540 return ERROR_INT("rankhaus classification failed", procName, 1); 00541 } 00542 else { /* classer->method == JB_CORRELATION */ 00543 if (jbClassifyCorrelation(classer, boxas, pixas)) 00544 return ERROR_INT("correlation classification failed", procName, 1); 00545 } 00546 00547 /* Find the global UL corners, adjusted for each instance so 00548 * that the class template and instance will have their 00549 * centroids in the same place. Then the template can be 00550 * used to replace the instance. */ 00551 if (jbGetULCorners(classer, pixs, boxas)) 00552 return ERROR_INT("UL corners not found", procName, 1); 00553 00554 /* Update total component counts and number of pages processed. */ 00555 n = boxaGetCount(boxas); 00556 classer->baseindex += n; 00557 numaAddNumber(classer->nacomps, n); 00558 classer->npages++; 00559 00560 return 0; 00561 } 00562 00563 00564 /*----------------------------------------------------------------------* 00565 * Classification using windowed rank hausdorff metric * 00566 *----------------------------------------------------------------------*/ 00567 /*! 00568 * jbClassifyRankHaus() 00569 * 00570 * Input: jbclasser 00571 * boxa (of new components for classification) 00572 * pixas (of new components for classification) 00573 * Return: 0 if OK; 1 on error 00574 */ 00575 l_int32 00576 jbClassifyRankHaus(JBCLASSER *classer, 00577 BOXA *boxa, 00578 PIXA *pixas) 00579 { 00580 l_int32 n, nt, i, wt, ht, iclass, size, found, testval; 00581 l_int32 *sumtab; 00582 l_int32 npages, area1, area3; 00583 l_int32 *tab8; 00584 l_float32 rank, x1, y1, x2, y2; 00585 BOX *box; 00586 NUMA *naclass, *napage; 00587 NUMA *nafg; /* fg area of all instances */ 00588 NUMA *nafgt; /* fg area of all templates */ 00589 JBFINDCTX *findcontext; 00590 NUMAHASH *nahash; 00591 PIX *pix, *pix1, *pix2, *pix3, *pix4; 00592 PIXA *pixa, *pixa1, *pixa2, *pixat, *pixatd; 00593 PIXAA *pixaa; 00594 PTA *pta, *ptac, *ptact; 00595 SEL *sel; 00596 00597 PROCNAME("jbClassifyRankHaus"); 00598 00599 if (!classer) 00600 return ERROR_INT("classer not found", procName, 1); 00601 if (!boxa) 00602 return ERROR_INT("boxa not found", procName, 1); 00603 if (!pixas) 00604 return ERROR_INT("pixas not found", procName, 1); 00605 00606 npages = classer->npages; 00607 size = classer->sizehaus; 00608 sel = selCreateBrick(size, size, size / 2, size / 2, SEL_HIT); 00609 00610 /* Generate the bordered pixa, with and without dilation. 00611 * pixa1 and pixa2 contain all the input components. */ 00612 n = pixaGetCount(pixas); 00613 pixa1 = pixaCreate(n); 00614 pixa2 = pixaCreate(n); 00615 for (i = 0; i < n; i++) { 00616 pix = pixaGetPix(pixas, i, L_CLONE); 00617 pix1 = pixAddBorderGeneral(pix, JB_ADDED_PIXELS, JB_ADDED_PIXELS, 00618 JB_ADDED_PIXELS, JB_ADDED_PIXELS, 0); 00619 pix2 = pixDilate(NULL, pix1, sel); 00620 pixaAddPix(pixa1, pix1, L_INSERT); /* un-dilated */ 00621 pixaAddPix(pixa2, pix2, L_INSERT); /* dilated */ 00622 pixDestroy(&pix); 00623 } 00624 00625 /* Get the centroids of all the bordered images. 00626 * These are relative to the UL corner of each (bordered) pix. */ 00627 pta = pixaCentroids(pixa1); /* centroids for this page; use here */ 00628 ptac = classer->ptac; /* holds centroids of components up to this page */ 00629 ptaJoin(ptac, pta, 0, 0); /* save centroids of all components */ 00630 ptact = classer->ptact; /* holds centroids of templates */ 00631 00632 /* Use these to save the class and page of each component. */ 00633 naclass = classer->naclass; 00634 napage = classer->napage; 00635 sumtab = makePixelSumTab8(); 00636 00637 /* Store the unbordered pix in a pixaa, in a hierarchical 00638 * set of arrays. There is one pixa for each class, 00639 * and the pix in each pixa are all the instances found 00640 * of that class. This is actually more than one would need 00641 * for a jbig2 encoder, but there are two reasons to keep 00642 * them around: (1) the set of instances for each class 00643 * can be used to make an improved binary (or, better, 00644 * a grayscale) template, rather than simply using the first 00645 * one in the set; (2) we can investigate the failures 00646 * of the classifier. This pixaa grows as we process 00647 * successive pages. */ 00648 pixaa = classer->pixaa; 00649 00650 /* arrays to store class exemplars (templates) */ 00651 pixat = classer->pixat; /* un-dilated */ 00652 pixatd = classer->pixatd; /* dilated */ 00653 00654 /* Fill up the pixaa tree with the template exemplars as 00655 * the first pix in each pixa. As we add each pix, 00656 * we also add the associated box to the pixa. 00657 * We also keep track of the centroid of each pix, 00658 * and use the difference between centroids (of the 00659 * pix with the exemplar we are checking it with) 00660 * to align the two when checking that the Hausdorff 00661 * distance does not exceed a threshold. 00662 * The threshold is set by the Sel used for dilating. 00663 * For example, a 3x3 brick, sel_3, corresponds to a 00664 * Hausdorff distance of 1. In general, for an NxN brick, 00665 * with N odd, corresponds to a Hausdorff distance of (N - 1)/2. 00666 * It turns out that we actually need to use a sel of size 2x2 00667 * to avoid small bad components when there is a halftone image 00668 * from which components can be chosen. 00669 * The larger the Sel you use, the fewer the number of classes, 00670 * and the greater the likelihood of putting semantically 00671 * different objects in the same class. For simplicity, 00672 * we do this separately for the case of rank == 1.0 (exact 00673 * match within the Hausdorff distance) and rank < 1.0. */ 00674 rank = classer->rankhaus; 00675 nahash = classer->nahash; 00676 if (rank == 1.0) { 00677 for (i = 0; i < n; i++) { 00678 pix1 = pixaGetPix(pixa1, i, L_CLONE); 00679 pix2 = pixaGetPix(pixa2, i, L_CLONE); 00680 ptaGetPt(pta, i, &x1, &y1); 00681 nt = pixaGetCount(pixat); /* number of templates */ 00682 found = FALSE; 00683 findcontext = findSimilarSizedTemplatesInit(classer, pix1); 00684 while ((iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) { 00685 /* Find score for this template */ 00686 pix3 = pixaGetPix(pixat, iclass, L_CLONE); 00687 pix4 = pixaGetPix(pixatd, iclass, L_CLONE); 00688 ptaGetPt(ptact, iclass, &x2, &y2); 00689 testval = pixHaustest(pix1, pix2, pix3, pix4, x1 - x2, y1 - y2, 00690 MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT); 00691 pixDestroy(&pix3); 00692 pixDestroy(&pix4); 00693 if (testval == 1) { 00694 found = TRUE; 00695 numaAddNumber(naclass, iclass); 00696 numaAddNumber(napage, npages); 00697 if (classer->keep_pixaa) { 00698 pixa = pixaaGetPixa(pixaa, iclass, L_CLONE); 00699 pix = pixaGetPix(pixas, i, L_CLONE); 00700 pixaAddPix(pixa, pix, L_INSERT); 00701 box = boxaGetBox(boxa, i, L_CLONE); 00702 pixaAddBox(pixa, box, L_INSERT); 00703 pixaDestroy(&pixa); 00704 } 00705 break; 00706 } 00707 } 00708 findSimilarSizedTemplatesDestroy(&findcontext); 00709 if (found == FALSE) { /* new class */ 00710 numaAddNumber(naclass, nt); 00711 numaAddNumber(napage, npages); 00712 pixa = pixaCreate(0); 00713 pix = pixaGetPix(pixas, i, L_CLONE); /* unbordered instance */ 00714 pixaAddPix(pixa, pix, L_INSERT); 00715 wt = pixGetWidth(pix); 00716 ht = pixGetHeight(pix); 00717 numaHashAdd(nahash, ht * wt, nt); 00718 box = boxaGetBox(boxa, i, L_CLONE); 00719 pixaAddBox(pixa, box, L_INSERT); 00720 pixaaAddPixa(pixaa, pixa, L_INSERT); /* unbordered instance */ 00721 ptaAddPt(ptact, x1, y1); 00722 pixaAddPix(pixat, pix1, L_INSERT); /* bordered template */ 00723 pixaAddPix(pixatd, pix2, L_INSERT); /* bordered dil template */ 00724 } 00725 else { /* don't save them */ 00726 pixDestroy(&pix1); 00727 pixDestroy(&pix2); 00728 } 00729 } 00730 } 00731 else { /* rank < 1.0 */ 00732 if ((nafg = pixaCountPixels(pixas)) == NULL) /* areas for this page */ 00733 return ERROR_INT("nafg not made", procName, 1); 00734 nafgt = classer->nafgt; 00735 tab8 = makePixelSumTab8(); 00736 for (i = 0; i < n; i++) { /* all instances on this page */ 00737 pix1 = pixaGetPix(pixa1, i, L_CLONE); 00738 numaGetIValue(nafg, i, &area1); 00739 pix2 = pixaGetPix(pixa2, i, L_CLONE); 00740 ptaGetPt(pta, i, &x1, &y1); /* use pta for this page */ 00741 nt = pixaGetCount(pixat); /* number of templates */ 00742 found = FALSE; 00743 findcontext = findSimilarSizedTemplatesInit(classer, pix1); 00744 while ((iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) { 00745 /* Find score for this template */ 00746 pix3 = pixaGetPix(pixat, iclass, L_CLONE); 00747 numaGetIValue(nafgt, iclass, &area3); 00748 pix4 = pixaGetPix(pixatd, iclass, L_CLONE); 00749 ptaGetPt(ptact, iclass, &x2, &y2); 00750 testval = pixRankHaustest(pix1, pix2, pix3, pix4, 00751 x1 - x2, y1 - y2, 00752 MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT, 00753 area1, area3, rank, tab8); 00754 pixDestroy(&pix3); 00755 pixDestroy(&pix4); 00756 if (testval == 1) { /* greedy match; take the first */ 00757 found = TRUE; 00758 numaAddNumber(naclass, iclass); 00759 numaAddNumber(napage, npages); 00760 if (classer->keep_pixaa) { 00761 pixa = pixaaGetPixa(pixaa, iclass, L_CLONE); 00762 pix = pixaGetPix(pixas, i, L_CLONE); 00763 pixaAddPix(pixa, pix, L_INSERT); 00764 box = boxaGetBox(boxa, i, L_CLONE); 00765 pixaAddBox(pixa, box, L_INSERT); 00766 pixaDestroy(&pixa); 00767 } 00768 break; 00769 } 00770 } 00771 findSimilarSizedTemplatesDestroy(&findcontext); 00772 if (found == FALSE) { /* new class */ 00773 numaAddNumber(naclass, nt); 00774 numaAddNumber(napage, npages); 00775 pixa = pixaCreate(0); 00776 pix = pixaGetPix(pixas, i, L_CLONE); /* unbordered instance */ 00777 pixaAddPix(pixa, pix, L_INSERT); 00778 wt = pixGetWidth(pix); 00779 ht = pixGetHeight(pix); 00780 numaHashAdd(nahash, ht * wt, nt); 00781 box = boxaGetBox(boxa, i, L_CLONE); 00782 pixaAddBox(pixa, box, L_INSERT); 00783 pixaaAddPixa(pixaa, pixa, L_INSERT); /* unbordered instance */ 00784 ptaAddPt(ptact, x1, y1); 00785 pixaAddPix(pixat, pix1, L_INSERT); /* bordered template */ 00786 pixaAddPix(pixatd, pix2, L_INSERT); /* ditto */ 00787 numaAddNumber(nafgt, area1); 00788 } 00789 else { /* don't save them */ 00790 pixDestroy(&pix1); 00791 pixDestroy(&pix2); 00792 } 00793 } 00794 FREE(tab8); 00795 numaDestroy(&nafg); 00796 } 00797 classer->nclass = pixaGetCount(pixat); 00798 00799 FREE(sumtab); 00800 ptaDestroy(&pta); 00801 pixaDestroy(&pixa1); 00802 pixaDestroy(&pixa2); 00803 selDestroy(&sel); 00804 return 0; 00805 } 00806 00807 00808 /*! 00809 * pixHaustest() 00810 * 00811 * Input: pix1 (new pix, not dilated) 00812 * pix2 (new pix, dilated) 00813 * pix3 (exemplar pix, not dilated) 00814 * pix4 (exemplar pix, dilated) 00815 * delx (x comp of centroid difference) 00816 * dely (y comp of centroid difference) 00817 * maxdiffw (max width difference of pix1 and pix2) 00818 * maxdiffh (max height difference of pix1 and pix2) 00819 * Return: 0 (FALSE) if no match, 1 (TRUE) if the new 00820 * pix is in the same class as the exemplar. 00821 * 00822 * Note: we check first that the two pix are roughly 00823 * the same size. Only if they meet that criterion do 00824 * we compare the bitmaps. The Hausdorff is a 2-way 00825 * check. The centroid difference is used to align the two 00826 * images to the nearest integer for each of the checks. 00827 * These check that the dilated image of one contains 00828 * ALL the pixels of the undilated image of the other. 00829 * Checks are done in both direction. A single pixel not 00830 * contained in either direction results in failure of the test. 00831 */ 00832 l_int32 00833 pixHaustest(PIX *pix1, 00834 PIX *pix2, 00835 PIX *pix3, 00836 PIX *pix4, 00837 l_float32 delx, /* x(1) - x(3) */ 00838 l_float32 dely, /* y(1) - y(3) */ 00839 l_int32 maxdiffw, 00840 l_int32 maxdiffh) 00841 { 00842 l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch; 00843 PIX *pixt; 00844 00845 /* Eliminate possible matches based on size difference */ 00846 wi = pixGetWidth(pix1); 00847 hi = pixGetHeight(pix1); 00848 wt = pixGetWidth(pix3); 00849 ht = pixGetHeight(pix3); 00850 delw = L_ABS(wi - wt); 00851 if (delw > maxdiffw) 00852 return FALSE; 00853 delh = L_ABS(hi - ht); 00854 if (delh > maxdiffh) 00855 return FALSE; 00856 00857 /* Round difference in centroid location to nearest integer; 00858 * use this as a shift when doing the matching. */ 00859 if (delx >= 0) 00860 idelx = (l_int32)(delx + 0.5); 00861 else 00862 idelx = (l_int32)(delx - 0.5); 00863 if (dely >= 0) 00864 idely = (l_int32)(dely + 0.5); 00865 else 00866 idely = (l_int32)(dely - 0.5); 00867 00868 /* Do 1-direction hausdorff, checking that every pixel in pix1 00869 * is within a dilation distance of some pixel in pix3. Namely, 00870 * that pix4 entirely covers pix1: 00871 * pixt = pixSubtract(NULL, pix1, pix4), including shift 00872 * where pixt has no ON pixels. */ 00873 pixt = pixCreateTemplate(pix1); 00874 pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC, pix1, 0, 0); 00875 pixRasterop(pixt, idelx, idely, wi, hi, PIX_DST & PIX_NOT(PIX_SRC), 00876 pix4, 0, 0); 00877 pixZero(pixt, &boolmatch); 00878 if (boolmatch == 0) { 00879 pixDestroy(&pixt); 00880 return FALSE; 00881 } 00882 00883 /* Do 1-direction hausdorff, checking that every pixel in pix3 00884 * is within a dilation distance of some pixel in pix1. Namely, 00885 * that pix2 entirely covers pix3: 00886 * pixSubtract(pixt, pix3, pix2), including shift 00887 * where pixt has no ON pixels. */ 00888 pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix3, 0, 0); 00889 pixRasterop(pixt, 0, 0, wt, ht, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0); 00890 pixZero(pixt, &boolmatch); 00891 pixDestroy(&pixt); 00892 return boolmatch; 00893 } 00894 00895 00896 /*! 00897 * pixRankHaustest() 00898 * 00899 * Input: pix1 (new pix, not dilated) 00900 * pix2 (new pix, dilated) 00901 * pix3 (exemplar pix, not dilated) 00902 * pix4 (exemplar pix, dilated) 00903 * delx (x comp of centroid difference) 00904 * dely (y comp of centroid difference) 00905 * maxdiffw (max width difference of pix1 and pix2) 00906 * maxdiffh (max height difference of pix1 and pix2) 00907 * area1 (fg pixels in pix1) 00908 * area3 (fg pixels in pix3) 00909 * rank (rank value of test, each way) 00910 * tab8 (table of pixel sums for byte) 00911 * Return: 0 (FALSE) if no match, 1 (TRUE) if the new 00912 * pix is in the same class as the exemplar. 00913 * 00914 * Note: we check first that the two pix are roughly 00915 * the same size. Only if they meet that criterion do 00916 * we compare the bitmaps. We convert the rank value to 00917 * a number of pixels by multiplying the rank fraction by the number 00918 * of pixels in the undilated image. The Hausdorff is a 2-way 00919 * check. The centroid difference is used to align the two 00920 * images to the nearest integer for each of the checks. 00921 * The rank hausdorff checks that the dilated image of one 00922 * contains the rank fraction of the pixels of the undilated 00923 * image of the other. Checks are done in both direction. 00924 * Failure of the test in either direction results in failure 00925 * of the test. 00926 */ 00927 l_int32 00928 pixRankHaustest(PIX *pix1, 00929 PIX *pix2, 00930 PIX *pix3, 00931 PIX *pix4, 00932 l_float32 delx, /* x(1) - x(3) */ 00933 l_float32 dely, /* y(1) - y(3) */ 00934 l_int32 maxdiffw, 00935 l_int32 maxdiffh, 00936 l_int32 area1, 00937 l_int32 area3, 00938 l_float32 rank, 00939 l_int32 *tab8) 00940 { 00941 l_int32 wi, hi, wt, ht, delw, delh, idelx, idely, boolmatch; 00942 l_int32 thresh1, thresh3; 00943 PIX *pixt; 00944 00945 /* Eliminate possible matches based on size difference */ 00946 wi = pixGetWidth(pix1); 00947 hi = pixGetHeight(pix1); 00948 wt = pixGetWidth(pix3); 00949 ht = pixGetHeight(pix3); 00950 delw = L_ABS(wi - wt); 00951 if (delw > maxdiffw) 00952 return FALSE; 00953 delh = L_ABS(hi - ht); 00954 if (delh > maxdiffh) 00955 return FALSE; 00956 00957 /* Upper bounds in remaining pixels for allowable match */ 00958 thresh1 = (l_int32)(area1 * (1. - rank) + 0.5); 00959 thresh3 = (l_int32)(area3 * (1. - rank) + 0.5); 00960 00961 /* Round difference in centroid location to nearest integer; 00962 * use this as a shift when doing the matching. */ 00963 if (delx >= 0) 00964 idelx = (l_int32)(delx + 0.5); 00965 else 00966 idelx = (l_int32)(delx - 0.5); 00967 if (dely >= 0) 00968 idely = (l_int32)(dely + 0.5); 00969 else 00970 idely = (l_int32)(dely - 0.5); 00971 00972 /* Do 1-direction hausdorff, checking that every pixel in pix1 00973 * is within a dilation distance of some pixel in pix3. Namely, 00974 * that pix4 entirely covers pix1: 00975 * pixt = pixSubtract(NULL, pix1, pix4), including shift 00976 * where pixt has no ON pixels. */ 00977 pixt = pixCreateTemplate(pix1); 00978 pixRasterop(pixt, 0, 0, wi, hi, PIX_SRC, pix1, 0, 0); 00979 pixRasterop(pixt, idelx, idely, wi, hi, PIX_DST & PIX_NOT(PIX_SRC), 00980 pix4, 0, 0); 00981 pixThresholdPixelSum(pixt, thresh1, &boolmatch, tab8); 00982 if (boolmatch == 1) { /* above thresh1 */ 00983 pixDestroy(&pixt); 00984 return FALSE; 00985 } 00986 00987 /* Do 1-direction hausdorff, checking that every pixel in pix3 00988 * is within a dilation distance of some pixel in pix1. Namely, 00989 * that pix2 entirely covers pix3: 00990 * pixSubtract(pixt, pix3, pix2), including shift 00991 * where pixt has no ON pixels. */ 00992 pixRasterop(pixt, idelx, idely, wt, ht, PIX_SRC, pix3, 0, 0); 00993 pixRasterop(pixt, 0, 0, wt, ht, PIX_DST & PIX_NOT(PIX_SRC), pix2, 0, 0); 00994 pixThresholdPixelSum(pixt, thresh3, &boolmatch, tab8); 00995 pixDestroy(&pixt); 00996 if (boolmatch == 1) /* above thresh3 */ 00997 return FALSE; 00998 else 00999 return TRUE; 01000 } 01001 01002 01003 /*----------------------------------------------------------------------* 01004 * Classification using windowed correlation score * 01005 *----------------------------------------------------------------------*/ 01006 /*! 01007 * jbClassifyCorrelation() 01008 * 01009 * Input: jbclasser 01010 * boxa (of new components for classification) 01011 * pixas (of new components for classification) 01012 * Return: 0 if OK; 1 on error 01013 */ 01014 l_int32 01015 jbClassifyCorrelation(JBCLASSER *classer, 01016 BOXA *boxa, 01017 PIXA *pixas) 01018 { 01019 l_int32 n, nt, i, iclass, wt, ht, found, area, area1, area2, npages, 01020 overthreshold; 01021 l_int32 *sumtab, *centtab; 01022 l_uint32 *row, word; 01023 l_float32 x1, y1, x2, y2, xsum, ysum; 01024 l_float32 thresh, weight, threshold; 01025 BOX *box; 01026 NUMA *naclass, *napage; 01027 NUMA *nafgt; /* fg area of all templates */ 01028 NUMA *naarea; /* w * h area of all templates */ 01029 JBFINDCTX *findcontext; 01030 NUMAHASH *nahash; 01031 PIX *pix, *pix1, *pix2; 01032 PIXA *pixa, *pixa1, *pixat; 01033 PIXAA *pixaa; 01034 PTA *pta, *ptac, *ptact; 01035 l_int32 *pixcts; /* pixel counts of each pixa */ 01036 l_int32 **pixrowcts; /* row-by-row pixel counts of each pixa */ 01037 l_int32 x, y, rowcount, downcount, wpl; 01038 l_uint8 byte; 01039 01040 PROCNAME("jbClassifyCorrelation"); 01041 01042 if (!classer) 01043 return ERROR_INT("classer not found", procName, 1); 01044 if (!boxa) 01045 return ERROR_INT("boxa not found", procName, 1); 01046 if (!pixas) 01047 return ERROR_INT("pixas not found", procName, 1); 01048 01049 npages = classer->npages; 01050 01051 /* Generate the bordered pixa, which contains all the the 01052 * input components. This will not be saved. */ 01053 n = pixaGetCount(pixas); 01054 pixa1 = pixaCreate(n); 01055 for (i = 0; i < n; i++) { 01056 pix = pixaGetPix(pixas, i, L_CLONE); 01057 pix1 = pixAddBorderGeneral(pix, JB_ADDED_PIXELS, JB_ADDED_PIXELS, 01058 JB_ADDED_PIXELS, JB_ADDED_PIXELS, 0); 01059 pixaAddPix(pixa1, pix1, L_INSERT); 01060 pixDestroy(&pix); 01061 } 01062 01063 /* Use these to save the class and page of each component. */ 01064 naclass = classer->naclass; 01065 napage = classer->napage; 01066 01067 /* Get the number of fg pixels in each component. */ 01068 nafgt = classer->nafgt; /* holds fg areas of the templates */ 01069 sumtab = makePixelSumTab8(); 01070 01071 pixcts = (l_int32 *)CALLOC(n, sizeof(*pixcts)); 01072 pixrowcts = (l_int32 **)CALLOC(n, sizeof(*pixrowcts)); 01073 centtab = makePixelCentroidTab8(); 01074 if (!pixcts || !pixrowcts || !centtab) 01075 return ERROR_INT("calloc fail in pix*cts or centtab", procName, 1); 01076 01077 /* Count the "1" pixels in each row of the pix in pixa1; this 01078 * allows pixCorrelationScoreThresholded to abort early if a match 01079 * is impossible. This loop merges three calculations: the total 01080 * number of "1" pixels, the number of "1" pixels in each row, and 01081 * the centroid. The centroids are relative to the UL corner of 01082 * each (bordered) pix. The pixrowcts[i][y] are the total number 01083 * of fg pixels in pixa[i] below row y. */ 01084 pta = ptaCreate(n); 01085 for (i = 0; i < n; i++) { 01086 pix = pixaGetPix(pixa1, i, L_CLONE); 01087 pixrowcts[i] = (l_int32 *)CALLOC(pixGetHeight(pix), 01088 sizeof(**pixrowcts)); 01089 xsum = 0; 01090 ysum = 0; 01091 wpl = pixGetWpl(pix); 01092 row = pixGetData(pix) + (pixGetHeight(pix) - 1) * wpl; 01093 downcount = 0; 01094 for (y = pixGetHeight(pix) - 1; y >= 0; y--, row -= wpl) { 01095 pixrowcts[i][y] = downcount; 01096 rowcount = 0; 01097 for (x = 0; x < wpl; x++) { 01098 word = row[x]; 01099 byte = word & 0xff; 01100 rowcount += sumtab[byte]; 01101 xsum += centtab[byte] + (x * 32 + 24) * sumtab[byte]; 01102 byte = (word >> 8) & 0xff; 01103 rowcount += sumtab[byte]; 01104 xsum += centtab[byte] + (x * 32 + 16) * sumtab[byte]; 01105 byte = (word >> 16) & 0xff; 01106 rowcount += sumtab[byte]; 01107 xsum += centtab[byte] + (x * 32 + 8) * sumtab[byte]; 01108 byte = (word >> 24) & 0xff; 01109 rowcount += sumtab[byte]; 01110 xsum += centtab[byte] + x * 32 * sumtab[byte]; 01111 } 01112 downcount += rowcount; 01113 ysum += rowcount * y; 01114 } 01115 pixcts[i] = downcount; 01116 ptaAddPt(pta, 01117 xsum / (l_float32)downcount, ysum / (l_float32)downcount); 01118 pixDestroy(&pix); 01119 } 01120 01121 ptac = classer->ptac; /* holds centroids of components up to this page */ 01122 ptaJoin(ptac, pta, 0, 0); /* save centroids of all components */ 01123 ptact = classer->ptact; /* holds centroids of templates */ 01124 01125 /* Store the unbordered pix in a pixaa, in a hierarchical 01126 * set of arrays. There is one pixa for each class, 01127 * and the pix in each pixa are all the instances found 01128 * of that class. This is actually more than one would need 01129 * for a jbig2 encoder, but there are two reasons to keep 01130 * them around: (1) the set of instances for each class 01131 * can be used to make an improved binary (or, better, 01132 * a grayscale) template, rather than simply using the first 01133 * one in the set; (2) we can investigate the failures 01134 * of the classifier. This pixaa grows as we process 01135 * successive pages. */ 01136 pixaa = classer->pixaa; 01137 01138 /* Array to store class exemplars */ 01139 pixat = classer->pixat; 01140 01141 /* Fill up the pixaa tree with the template exemplars as 01142 * the first pix in each pixa. As we add each pix, 01143 * we also add the associated box to the pixa. 01144 * We also keep track of the centroid of each pix, 01145 * and use the difference between centroids (of the 01146 * pix with the exemplar we are checking it with) 01147 * to align the two when checking that the correlation 01148 * score exceeds a threshold. The correlation score 01149 * is given by the square of the area of the AND 01150 * between aligned instance and template, divided by 01151 * the product of areas of each image. For identical 01152 * template and instance, the score is 1.0. 01153 * If the threshold is too small, non-equivalent instances 01154 * will be placed in the same class; if too large, there will 01155 * be an unnecessary division of classes representing the 01156 * same character. The weightfactor adds in some of the 01157 * difference (1.0 - thresh), depending on the heaviness 01158 * of the template (measured as the fraction of fg pixels). */ 01159 thresh = classer->thresh; 01160 weight = classer->weightfactor; 01161 naarea = classer->naarea; 01162 nahash = classer->nahash; 01163 for (i = 0; i < n; i++) { 01164 pix1 = pixaGetPix(pixa1, i, L_CLONE); 01165 area1 = pixcts[i]; 01166 ptaGetPt(pta, i, &x1, &y1); /* centroid for this instance */ 01167 nt = pixaGetCount(pixat); 01168 found = FALSE; 01169 findcontext = findSimilarSizedTemplatesInit(classer, pix1); 01170 while ( (iclass = findSimilarSizedTemplatesNext(findcontext)) > -1) { 01171 /* Get the template */ 01172 pix2 = pixaGetPix(pixat, iclass, L_CLONE); 01173 numaGetIValue(nafgt, iclass, &area2); 01174 ptaGetPt(ptact, iclass, &x2, &y2); /* template centroid */ 01175 01176 /* Find threshold for this template */ 01177 if (weight > 0.0) { 01178 numaGetIValue(naarea, iclass, &area); 01179 threshold = thresh + (1. - thresh) * weight * area2 / area; 01180 } 01181 else 01182 threshold = thresh; 01183 01184 /* Find score for this template */ 01185 overthreshold = pixCorrelationScoreThresholded(pix1, pix2, 01186 area1, area2, 01187 x1 - x2, y1 - y2, 01188 MAX_DIFF_WIDTH, 01189 MAX_DIFF_HEIGHT, 01190 sumtab, 01191 pixrowcts[i], 01192 threshold); 01193 #if DEBUG_CORRELATION_SCORE 01194 { 01195 l_float32 score, testscore; 01196 l_int32 count, testcount; 01197 score = pixCorrelationScore(pix1, pix2, area1, area2, 01198 x1 - x2, y1 - y2, 01199 MAX_DIFF_WIDTH, MAX_DIFF_HEIGHT, 01200 sumtab); 01201 01202 testscore = pixCorrelationScoreSimple(pix1, pix2, area1, area2, 01203 x1 - x2, y1 - y2, MAX_DIFF_WIDTH, 01204 MAX_DIFF_HEIGHT, sumtab); 01205 count = (l_int32)rint(sqrt(score * area1 * area2)); 01206 testcount = (l_int32)rint(sqrt(testscore * area1 * area2)); 01207 if ((score >= threshold) != (testscore >= threshold)) { 01208 fprintf(stderr, "Correlation score mismatch: %d(%g,%d) vs %d(%g,%d) (%g)\n", 01209 count, score, score >= threshold, 01210 testcount, testscore, testscore >= threshold, 01211 score - testscore); 01212 } 01213 01214 if ((score >= threshold) != overthreshold) { 01215 fprintf(stderr, "Mismatch between correlation/threshold comparison: %g(%g,%d) >= %g(%g) vs %s\n", 01216 score, score*area1*area2, count, threshold, threshold*area1*area2, (overthreshold ? "true" : "false")); 01217 } 01218 } 01219 #endif /* DEBUG_CORRELATION_SCORE */ 01220 pixDestroy(&pix2); 01221 01222 if (overthreshold) { /* greedy match */ 01223 found = TRUE; 01224 numaAddNumber(naclass, iclass); 01225 numaAddNumber(napage, npages); 01226 if (classer->keep_pixaa) { 01227 /* We are keeping a record of all components */ 01228 pixa = pixaaGetPixa(pixaa, iclass, L_CLONE); 01229 pix = pixaGetPix(pixas, i, L_CLONE); 01230 pixaAddPix(pixa, pix, L_INSERT); 01231 box = boxaGetBox(boxa, i, L_CLONE); 01232 pixaAddBox(pixa, box, L_INSERT); 01233 pixaDestroy(&pixa); 01234 } 01235 break; 01236 } 01237 } 01238 findSimilarSizedTemplatesDestroy(&findcontext); 01239 if (found == FALSE) { /* new class */ 01240 numaAddNumber(naclass, nt); 01241 numaAddNumber(napage, npages); 01242 pixa = pixaCreate(0); 01243 pix = pixaGetPix(pixas, i, L_CLONE); /* unbordered instance */ 01244 pixaAddPix(pixa, pix, L_INSERT); 01245 wt = pixGetWidth(pix); 01246 ht = pixGetHeight(pix); 01247 numaHashAdd(nahash, ht * wt, nt); 01248 box = boxaGetBox(boxa, i, L_CLONE); 01249 pixaAddBox(pixa, box, L_INSERT); 01250 pixaaAddPixa(pixaa, pixa, L_INSERT); /* unbordered instance */ 01251 ptaAddPt(ptact, x1, y1); 01252 numaAddNumber(nafgt, area1); 01253 pixaAddPix(pixat, pix1, L_INSERT); /* bordered template */ 01254 area = (pixGetWidth(pix1) - 2 * JB_ADDED_PIXELS) * 01255 (pixGetHeight(pix1) - 2 * JB_ADDED_PIXELS); 01256 numaAddNumber(naarea, area); 01257 } 01258 else { /* don't save it */ 01259 pixDestroy(&pix1); 01260 } 01261 } 01262 classer->nclass = pixaGetCount(pixat); 01263 01264 FREE(pixcts); 01265 FREE(centtab); 01266 for (i = 0; i < n; i++) { 01267 FREE(pixrowcts[i]); 01268 } 01269 FREE(pixrowcts); 01270 01271 FREE(sumtab); 01272 ptaDestroy(&pta); 01273 pixaDestroy(&pixa1); 01274 return 0; 01275 } 01276 01277 01278 /*----------------------------------------------------------------------* 01279 * Determine the image components we start with * 01280 *----------------------------------------------------------------------*/ 01281 /*! 01282 * jbGetComponents() 01283 * 01284 * Input: pixs (1 bpp) 01285 * components (JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS) 01286 * maxwidth, maxheight (of saved components; larger are discarded) 01287 * &pboxa (<return> b.b. of component items) 01288 * &ppixa (<return> component items) 01289 * Return: 0 if OK, 1 on error 01290 */ 01291 l_int32 01292 jbGetComponents(PIX *pixs, 01293 l_int32 components, 01294 l_int32 maxwidth, 01295 l_int32 maxheight, 01296 BOXA **pboxad, 01297 PIXA **ppixad) 01298 { 01299 l_int32 empty, res, redfactor; 01300 BOXA *boxa; 01301 PIX *pixt1, *pixt2, *pixt3; 01302 PIXA *pixa, *pixat; 01303 01304 PROCNAME("jbGetComponents"); 01305 01306 if (!pboxad) 01307 return ERROR_INT("&boxad not defined", procName, 1); 01308 *pboxad = NULL; 01309 if (!ppixad) 01310 return ERROR_INT("&pixad not defined", procName, 1); 01311 *ppixad = NULL; 01312 if (!pixs) 01313 return ERROR_INT("pixs not defined", procName, 1); 01314 if (components != JB_CONN_COMPS && components != JB_CHARACTERS && 01315 components != JB_WORDS) 01316 return ERROR_INT("invalid components", procName, 1); 01317 01318 pixZero(pixs, &empty); 01319 if (empty) { 01320 *pboxad = boxaCreate(0); 01321 *ppixad = pixaCreate(0); 01322 return 0; 01323 } 01324 01325 /* If required, preprocess input pixs. The method for both 01326 * characters and words is to generate a connected component 01327 * mask over the units that we want to aggregrate, which are, 01328 * in general, sets of related connected components in pixs. 01329 * For characters, we want to include the dots with 01330 * 'i', 'j' and '!', so we do a small vertical closing to 01331 * generate the mask. For words, we make a mask over all 01332 * characters in each word. This is a bit more tricky, because 01333 * the spacing between words is difficult to predict a priori, 01334 * and words can be typeset with variable spacing that can 01335 * in some cases be barely larger than the space between 01336 * characters. The first step is to generate the mask and 01337 * identify each of its connected components. */ 01338 if (components == JB_CONN_COMPS) { /* no preprocessing */ 01339 boxa = pixConnComp(pixs, &pixa, 8); 01340 } 01341 else if (components == JB_CHARACTERS) { 01342 pixt1 = pixMorphSequence(pixs, "c1.6", 0); 01343 boxa = pixConnComp(pixt1, &pixat, 8); 01344 pixa = pixaClipToPix(pixat, pixs); 01345 pixDestroy(&pixt1); 01346 pixaDestroy(&pixat); 01347 } 01348 else { /* components == JB_WORDS */ 01349 01350 /* Do the operations at about 150 ppi resolution. 01351 * It is much faster at 75 ppi, but the results are 01352 * more accurate at 150 ppi. This will segment the 01353 * words in body text. It can be expected that relatively 01354 * infrequent words in a larger font will be split. */ 01355 res = pixGetXRes(pixs); 01356 if (res <= 200) { 01357 redfactor = 1; 01358 pixt1 = pixClone(pixs); 01359 } 01360 else if (res <= 400) { 01361 redfactor = 2; 01362 pixt1 = pixReduceRankBinaryCascade(pixs, 1, 0, 0, 0); 01363 } 01364 else { 01365 redfactor = 4; 01366 pixt1 = pixReduceRankBinaryCascade(pixs, 1, 1, 0, 0); 01367 } 01368 01369 pixt2 = pixWordMaskByDilation(pixt1, 0, NULL); 01370 01371 /* Expand the optimally dilated word mask to full res. */ 01372 pixt3 = pixExpandReplicate(pixt2, redfactor); 01373 01374 /* Pull out the pixels in pixs corresponding to the mask 01375 * components in pixt3. Note that above we used threshold 01376 * levels in the reduction of 1 to insure that the resulting 01377 * mask fully covers the input pixs. The downside of using 01378 * a threshold of 1 is that very close characters from adjacent 01379 * lines can be joined. But with a level of 2 or greater, 01380 * it is necessary to use a seedfill, followed by a pixOr(): 01381 * pixt4 = pixSeedfillBinary(NULL, pixt3, pixs, 8); 01382 * pixOr(pixt3, pixt3, pixt4); 01383 * to insure that the mask coverage is complete over pixs. */ 01384 boxa = pixConnComp(pixt3, &pixat, 4); 01385 pixa = pixaClipToPix(pixat, pixs); 01386 pixaDestroy(&pixat); 01387 pixDestroy(&pixt1); 01388 pixDestroy(&pixt2); 01389 pixDestroy(&pixt3); 01390 } 01391 01392 /* Remove large components, and save the results. */ 01393 *ppixad = pixaSelectBySize(pixa, maxwidth, maxheight, L_SELECT_IF_BOTH, 01394 L_SELECT_IF_LTE, NULL); 01395 *pboxad = boxaSelectBySize(boxa, maxwidth, maxheight, L_SELECT_IF_BOTH, 01396 L_SELECT_IF_LTE, NULL); 01397 pixaDestroy(&pixa); 01398 boxaDestroy(&boxa); 01399 01400 return 0; 01401 } 01402 01403 01404 /*! 01405 * pixWordMaskByDilation() 01406 * 01407 * Input: pixs (1 bpp; typ. at 75 to 150 ppi) 01408 * maxsize (use 0 for default; not to exceed 14) 01409 * &size (<optional return> size of optimal horiz Sel) 01410 * Return: pixd (dilated word mask), or null on error 01411 * 01412 * Notes: 01413 * (1) For 75 to 150 ppi, the optimal dilation should not exceed 7. 01414 * This is the default size chosen if maxsize <= 0. 01415 * (2) To run this on images at resolution between 200 and 300, it 01416 * is advisable to use a larger maxsize, say between 10 and 14. 01417 * (3) The best size for dilating to get word masks is optionally returned. 01418 */ 01419 PIX * 01420 pixWordMaskByDilation(PIX *pixs, 01421 l_int32 maxsize, 01422 l_int32 *psize) 01423 { 01424 l_int32 i, diffmin, ndiff, imin; 01425 l_int32 ncc[MAX_ALLOWED_DILATION + 1]; 01426 BOXA *boxa; 01427 NUMA *nacc; 01428 PIX *pixt1, *pixt2, *pixt3; 01429 PIXA *pixa; 01430 SEL *sel; 01431 01432 PROCNAME("pixWordMaskbyDilation"); 01433 01434 if (!pixs) 01435 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01436 01437 /* Find the optimal dilation to create the word mask. 01438 * Look for successively increasing dilations where the 01439 * number of connected components doesn't decrease. 01440 * This is the situation where the components in the 01441 * word mask should properly cover each word. Use of 01442 * 4 cc slightly reduces the likelihood that words from 01443 * different lines are joined. */ 01444 diffmin = 1000000; 01445 pixa = pixaCreate(8); 01446 pixt1 = pixCopy(NULL, pixs); 01447 pixaAddPix(pixa, pixt1, L_COPY); 01448 01449 if (maxsize <= 0) 01450 maxsize = 7; /* default */ 01451 if (maxsize > MAX_ALLOWED_DILATION) 01452 maxsize = MAX_ALLOWED_DILATION; 01453 nacc = numaCreate(maxsize); 01454 for (i = 0; i <= maxsize; i++) { 01455 if (i == 0) /* first one not dilated */ 01456 pixt2 = pixCopy(NULL, pixt1); 01457 else /* successive dilation by sel_2h */ 01458 pixt2 = pixMorphSequence(pixt1, "d2.1", 0); 01459 boxa = pixConnCompBB(pixt2, 4); 01460 ncc[i] = boxaGetCount(boxa); 01461 numaAddNumber(nacc, ncc[i]); 01462 if (i > 0) { 01463 ndiff = ncc[i - 1] - ncc[i]; 01464 #if DEBUG_PLOT_CC 01465 fprintf(stderr, "ndiff[%d] = %d\n", i - 1, ndiff); 01466 #endif /* DEBUG_PLOT_CC */ 01467 if (ndiff < diffmin) { 01468 imin = i; 01469 diffmin = ndiff; 01470 } 01471 } 01472 pixaAddPix(pixa, pixt2, L_COPY); 01473 pixDestroy(&pixt1); 01474 pixt1 = pixt2; 01475 boxaDestroy(&boxa); 01476 } 01477 pixDestroy(&pixt1); 01478 01479 #if DEBUG_PLOT_CC 01480 {GPLOT *gplot; 01481 NUMA *naseq; 01482 naseq = numaMakeSequence(1, 1, numaGetCount(nacc)); 01483 gplot = gplotCreate("/tmp/cc_output", GPLOT_PNG, 01484 "Number of cc vs. horizontal dilation", 01485 "Sel horiz", "Number of cc"); 01486 gplotAddPlot(gplot, naseq, nacc, GPLOT_LINES, ""); 01487 gplotMakeOutput(gplot); 01488 gplotDestroy(&gplot); 01489 numaDestroy(&naseq); 01490 } 01491 #endif /* DEBUG_PLOT_CC */ 01492 01493 /* Save the result of the optimal dilation */ 01494 pixt2 = pixaGetPix(pixa, imin, L_CLONE); 01495 sel = selCreateBrick(1, imin, 0, imin - 1, SEL_HIT); 01496 pixt3 = pixErode(NULL, pixt2, sel); /* remove effect of dilation */ 01497 selDestroy(&sel); 01498 pixDestroy(&pixt2); 01499 pixaDestroy(&pixa); 01500 if (psize) 01501 *psize = imin + 1; 01502 01503 numaDestroy(&nacc); 01504 return pixt3; 01505 } 01506 01507 01508 /*----------------------------------------------------------------------* 01509 * Build grayscale composites (templates) * 01510 *----------------------------------------------------------------------*/ 01511 /*! 01512 * jbAccumulateComposites() 01513 * 01514 * Input: pixaa (one pixa for each class) 01515 * &pna (<return> number of samples used to build each composite) 01516 * &ptat (<return> centroids of bordered composites) 01517 * Return: pixad (accumulated sum of samples in each class), 01518 * or null on error 01519 * 01520 */ 01521 PIXA * 01522 jbAccumulateComposites(PIXAA *pixaa, 01523 NUMA **pna, 01524 PTA **pptat) 01525 { 01526 l_int32 n, nt, i, j, d, minw, maxw, minh, maxh, xdiff, ydiff; 01527 l_float32 x, y, xave, yave; 01528 NUMA *na; 01529 PIX *pix, *pixt1, *pixt2, *pixsum; 01530 PIXA *pixa, *pixad; 01531 PTA *ptat, *pta; 01532 01533 PROCNAME("jbAccumulateComposites"); 01534 01535 if (!pptat) 01536 return (PIXA *)ERROR_PTR("&ptat not defined", procName, NULL); 01537 *pptat = NULL; 01538 if (!pna) 01539 return (PIXA *)ERROR_PTR("&na not defined", procName, NULL); 01540 *pna = NULL; 01541 if (!pixaa) 01542 return (PIXA *)ERROR_PTR("pixaa not defined", procName, NULL); 01543 01544 n = pixaaGetCount(pixaa); 01545 if ((ptat = ptaCreate(n)) == NULL) 01546 return (PIXA *)ERROR_PTR("ptat not made", procName, NULL); 01547 *pptat = ptat; 01548 pixad = pixaCreate(n); 01549 na = numaCreate(n); 01550 *pna = na; 01551 01552 for (i = 0; i < n; i++) { 01553 pixa = pixaaGetPixa(pixaa, i, L_CLONE); 01554 nt = pixaGetCount(pixa); 01555 numaAddNumber(na, nt); 01556 if (nt == 0) { 01557 L_WARNING("empty pixa found!", procName); 01558 pixaDestroy(&pixa); 01559 continue; 01560 } 01561 pixaSizeRange(pixa, &minw, &minh, &maxw, &maxh); 01562 pix = pixaGetPix(pixa, 0, L_CLONE); 01563 d = pixGetDepth(pix); 01564 pixDestroy(&pix); 01565 pixt1 = pixCreate(maxw, maxh, d); 01566 pixsum = pixInitAccumulate(maxw, maxh, 0); 01567 pta = pixaCentroids(pixa); 01568 01569 /* Find the average value of the centroids ... */ 01570 xave = yave = 0; 01571 for (j = 0; j < nt; j++) { 01572 ptaGetPt(pta, j, &x, &y); 01573 xave += x; 01574 yave += y; 01575 } 01576 xave = xave / (l_float32)nt; 01577 yave = yave / (l_float32)nt; 01578 01579 /* and place all centroids at their average value */ 01580 for (j = 0; j < nt; j++) { 01581 pixt2 = pixaGetPix(pixa, j, L_CLONE); 01582 ptaGetPt(pta, j, &x, &y); 01583 xdiff = (l_int32)(x - xave); 01584 ydiff = (l_int32)(y - yave); 01585 pixClearAll(pixt1); 01586 pixRasterop(pixt1, xdiff, ydiff, maxw, maxh, PIX_SRC, 01587 pixt2, 0, 0); 01588 pixAccumulate(pixsum, pixt1, L_ARITH_ADD); 01589 pixDestroy(&pixt2); 01590 } 01591 pixaAddPix(pixad, pixsum, L_INSERT); 01592 ptaAddPt(ptat, xave, yave); 01593 01594 pixaDestroy(&pixa); 01595 pixDestroy(&pixt1); 01596 ptaDestroy(&pta); 01597 } 01598 01599 return pixad; 01600 } 01601 01602 01603 /*! 01604 * jbTemplatesFromComposites() 01605 * 01606 * Input: pixac (one pix of composites for each class) 01607 * na (number of samples used for each class composite) 01608 * Return: pixad (8 bpp templates for each class), or null on error 01609 * 01610 */ 01611 PIXA * 01612 jbTemplatesFromComposites(PIXA *pixac, 01613 NUMA *na) 01614 { 01615 l_int32 n, i; 01616 l_float32 nt; /* number of samples in the composite; always an integer */ 01617 l_float32 factor; 01618 PIX *pixsum; /* accumulated composite */ 01619 PIX *pixd; 01620 PIXA *pixad; 01621 01622 PROCNAME("jbTemplatesFromComposites"); 01623 01624 if (!pixac) 01625 return (PIXA *)ERROR_PTR("pixac not defined", procName, NULL); 01626 if (!na) 01627 return (PIXA *)ERROR_PTR("na not defined", procName, NULL); 01628 01629 n = pixaGetCount(pixac); 01630 pixad = pixaCreate(n); 01631 for (i = 0; i < n; i++) { 01632 pixsum = pixaGetPix(pixac, i, L_COPY); /* changed internally */ 01633 numaGetFValue(na, i, &nt); 01634 factor = 255. / nt; 01635 pixMultConstAccumulate(pixsum, factor, 0); /* changes pixsum */ 01636 pixd = pixFinalAccumulate(pixsum, 0, 8); 01637 pixaAddPix(pixad, pixd, L_INSERT); 01638 pixDestroy(&pixsum); 01639 } 01640 01641 return pixad; 01642 } 01643 01644 01645 01646 /*----------------------------------------------------------------------* 01647 * jbig2 utility routines * 01648 *----------------------------------------------------------------------*/ 01649 /*! 01650 * jbClasserCreate() 01651 * 01652 * Input: method (JB_RANKHAUS, JB_CORRELATION) 01653 * components (JB_CONN_COMPS, JB_CHARACTERS, JB_WORDS) 01654 * Return: jbclasser, or null on error 01655 */ 01656 JBCLASSER * 01657 jbClasserCreate(l_int32 method, 01658 l_int32 components) 01659 { 01660 JBCLASSER *classer; 01661 01662 PROCNAME("jbClasserCreate"); 01663 01664 if ((classer = (JBCLASSER *)CALLOC(1, sizeof(JBCLASSER))) == NULL) 01665 return (JBCLASSER *)ERROR_PTR("classer not made", procName, NULL); 01666 if (method != JB_RANKHAUS && method != JB_CORRELATION) 01667 return (JBCLASSER *)ERROR_PTR("invalid type", procName, NULL); 01668 if (components != JB_CONN_COMPS && components != JB_CHARACTERS && 01669 components != JB_WORDS) 01670 return (JBCLASSER *)ERROR_PTR("invalid type", procName, NULL); 01671 01672 classer->method = method; 01673 classer->components = components; 01674 classer->nacomps = numaCreate(0); 01675 classer->pixaa = pixaaCreate(0); 01676 classer->pixat = pixaCreate(0); 01677 classer->pixatd = pixaCreate(0); 01678 classer->nafgt = numaCreate(0); 01679 classer->naarea = numaCreate(0); 01680 classer->ptac = ptaCreate(0); 01681 classer->ptact = ptaCreate(0); 01682 classer->naclass = numaCreate(0); 01683 classer->napage = numaCreate(0); 01684 classer->ptaul = ptaCreate(0); 01685 01686 return classer; 01687 } 01688 01689 01690 /* 01691 * jbClasserDestroy() 01692 * 01693 * Input: &classer (<to be nulled>) 01694 * Return: void 01695 */ 01696 void 01697 jbClasserDestroy(JBCLASSER **pclasser) 01698 { 01699 JBCLASSER *classer; 01700 01701 if (!pclasser) 01702 return; 01703 if ((classer = *pclasser) == NULL) 01704 return; 01705 01706 sarrayDestroy(&classer->safiles); 01707 numaDestroy(&classer->nacomps); 01708 pixaaDestroy(&classer->pixaa); 01709 pixaDestroy(&classer->pixat); 01710 pixaDestroy(&classer->pixatd); 01711 numaHashDestroy(&classer->nahash); 01712 numaDestroy(&classer->nafgt); 01713 numaDestroy(&classer->naarea); 01714 ptaDestroy(&classer->ptac); 01715 ptaDestroy(&classer->ptact); 01716 numaDestroy(&classer->naclass); 01717 numaDestroy(&classer->napage); 01718 ptaDestroy(&classer->ptaul); 01719 ptaDestroy(&classer->ptall); 01720 FREE(classer); 01721 *pclasser = NULL; 01722 return; 01723 } 01724 01725 01726 /*! 01727 * jbDataSave() 01728 * 01729 * Input: jbclasser 01730 * latticew, latticeh (cell size used to store each 01731 * connected component in the composite) 01732 * Return: jbdata, or null on error 01733 * 01734 * Notes: 01735 * (1) This routine stores the jbig2-type data required for 01736 * generating a lossy jbig2 version of the image. 01737 * It can be losslessly written to (and read from) two files. 01738 * (2) It generates and stores the mosaic of templates. 01739 * (3) It clones the Numa and Pta arrays, so these must all 01740 * be destroyed by the caller. 01741 * (4) Input 0 to use the default values for latticew and/or latticeh, 01742 */ 01743 JBDATA * 01744 jbDataSave(JBCLASSER *classer) 01745 { 01746 l_int32 maxw, maxh; 01747 JBDATA *data; 01748 PIX *pix; 01749 01750 PROCNAME("jbDataSave"); 01751 01752 if (!classer) 01753 return (JBDATA *)ERROR_PTR("classer not defined", procName, NULL); 01754 01755 /* Write the templates into an array. */ 01756 pixaSizeRange(classer->pixat, NULL, NULL, &maxw, &maxh); 01757 if ((pix = pixaDisplayOnLattice(classer->pixat, maxw + 1, maxh + 1)) 01758 == NULL) 01759 return (JBDATA *)ERROR_PTR("data not made", procName, NULL); 01760 01761 if ((data = (JBDATA *)CALLOC(1, sizeof(JBDATA))) == NULL) 01762 return (JBDATA *)ERROR_PTR("data not made", procName, NULL); 01763 data->pix = pix; 01764 data->npages = classer->npages; 01765 data->w = classer->w; 01766 data->h = classer->h; 01767 data->nclass = classer->nclass; 01768 data->latticew = maxw + 1; 01769 data->latticeh = maxh + 1; 01770 data->naclass = numaClone(classer->naclass); 01771 data->napage = numaClone(classer->napage); 01772 data->ptaul = ptaClone(classer->ptaul); 01773 01774 return data; 01775 } 01776 01777 01778 /* 01779 * jbDataDestroy() 01780 * 01781 * Input: &data (<to be nulled>) 01782 * Return: void 01783 */ 01784 void 01785 jbDataDestroy(JBDATA **pdata) 01786 { 01787 JBDATA *data; 01788 01789 if (!pdata) 01790 return; 01791 if ((data = *pdata) == NULL) 01792 return; 01793 01794 pixDestroy(&data->pix); 01795 numaDestroy(&data->naclass); 01796 numaDestroy(&data->napage); 01797 ptaDestroy(&data->ptaul); 01798 FREE(data); 01799 *pdata = NULL; 01800 return; 01801 } 01802 01803 01804 /*! 01805 * jbDataWrite() 01806 * 01807 * Input: rootname (for output files; everything but the extension) 01808 * jbdata 01809 * Return: 0 if OK, 1 on error 01810 * 01811 * Notes: 01812 * (1) Serialization function that writes data in jbdata to file. 01813 */ 01814 l_int32 01815 jbDataWrite(const char *rootout, 01816 JBDATA *jbdata) 01817 { 01818 char buf[L_BUF_SIZE]; 01819 l_int32 w, h, nclass, npages, cellw, cellh, ncomp, i, x, y, iclass, ipage; 01820 NUMA *naclass, *napage; 01821 PTA *ptaul; 01822 PIX *pixt; 01823 FILE *fp; 01824 01825 PROCNAME("jbDataWrite"); 01826 01827 if (!rootout) 01828 return ERROR_INT("no rootout", procName, 1); 01829 if (!jbdata) 01830 return ERROR_INT("no jbdata", procName, 1); 01831 01832 npages = jbdata->npages; 01833 w = jbdata->w; 01834 h = jbdata->h; 01835 pixt = jbdata->pix; 01836 nclass = jbdata->nclass; 01837 cellw = jbdata->latticew; 01838 cellh = jbdata->latticeh; 01839 naclass = jbdata->naclass; 01840 napage = jbdata->napage; 01841 ptaul = jbdata->ptaul; 01842 01843 snprintf(buf, L_BUF_SIZE, "%s%s", rootout, JB_TEMPLATE_EXT); 01844 pixWrite(buf, pixt, IFF_PNG); 01845 01846 snprintf(buf, L_BUF_SIZE, "%s%s", rootout, JB_DATA_EXT); 01847 if ((fp = fopenWriteStream(buf, "wb")) == NULL) 01848 return ERROR_INT("stream not opened", procName, 1); 01849 ncomp = ptaGetCount(ptaul); 01850 fprintf(fp, "jb data file\n"); 01851 fprintf(fp, "num pages = %d\n", npages); 01852 fprintf(fp, "page size: w = %d, h = %d\n", w, h); 01853 fprintf(fp, "num components = %d\n", ncomp); 01854 fprintf(fp, "num classes = %d\n", nclass); 01855 fprintf(fp, "template lattice size: w = %d, h = %d\n", cellw, cellh); 01856 for (i = 0; i < ncomp; i++) { 01857 numaGetIValue(napage, i, &ipage); 01858 numaGetIValue(naclass, i, &iclass); 01859 ptaGetIPt(ptaul, i, &x, &y); 01860 fprintf(fp, "%d %d %d %d\n", ipage, iclass, x, y); 01861 } 01862 fclose(fp); 01863 01864 return 0; 01865 } 01866 01867 01868 /*! 01869 * jbDataRead() 01870 * 01871 * Input: rootname (for template and data files) 01872 * Return: jbdata, or NULL on error 01873 */ 01874 JBDATA * 01875 jbDataRead(const char *rootname) 01876 { 01877 char fname[L_BUF_SIZE]; 01878 char *linestr; 01879 l_uint8 *data; 01880 l_int32 nsa, i, w, h, cellw, cellh, x, y, iclass, ipage; 01881 l_int32 npages, nclass, ncomp; 01882 size_t size; 01883 JBDATA *jbdata; 01884 NUMA *naclass, *napage; 01885 PIX *pixs; 01886 PTA *ptaul; 01887 SARRAY *sa; 01888 01889 PROCNAME("jbDataRead"); 01890 01891 if (!rootname) 01892 return (JBDATA *)ERROR_PTR("rootname not defined", procName, NULL); 01893 01894 snprintf(fname, L_BUF_SIZE, "%s%s", rootname, JB_TEMPLATE_EXT); 01895 if ((pixs = pixRead(fname)) == NULL) 01896 return (JBDATA *)ERROR_PTR("pix not read", procName, NULL); 01897 01898 snprintf(fname, L_BUF_SIZE, "%s%s", rootname, JB_DATA_EXT); 01899 if ((data = l_binaryRead(fname, &size)) == NULL) 01900 return (JBDATA *)ERROR_PTR("data not read", procName, NULL); 01901 01902 if ((sa = sarrayCreateLinesFromString((char *)data, 0)) == NULL) 01903 return (JBDATA *)ERROR_PTR("sa not made", procName, NULL); 01904 nsa = sarrayGetCount(sa); /* number of cc + 6 */ 01905 linestr = sarrayGetString(sa, 0, 0); 01906 if (strcmp(linestr, "jb data file")) 01907 return (JBDATA *)ERROR_PTR("invalid jb data file", procName, NULL); 01908 linestr = sarrayGetString(sa, 1, 0); 01909 sscanf(linestr, "num pages = %d", &npages); 01910 linestr = sarrayGetString(sa, 2, 0); 01911 sscanf(linestr, "page size: w = %d, h = %d", &w, &h); 01912 linestr = sarrayGetString(sa, 3, 0); 01913 sscanf(linestr, "num components = %d", &ncomp); 01914 linestr = sarrayGetString(sa, 4, 0); 01915 sscanf(linestr, "num classes = %d\n", &nclass); 01916 linestr = sarrayGetString(sa, 5, 0); 01917 sscanf(linestr, "template lattice size: w = %d, h = %d\n", &cellw, &cellh); 01918 01919 #if 1 01920 fprintf(stderr, "num pages = %d\n", npages); 01921 fprintf(stderr, "page size: w = %d, h = %d\n", w, h); 01922 fprintf(stderr, "num components = %d\n", ncomp); 01923 fprintf(stderr, "num classes = %d\n", nclass); 01924 fprintf(stderr, "template lattice size: w = %d, h = %d\n", cellw, cellh); 01925 #endif 01926 01927 if ((naclass = numaCreate(ncomp)) == NULL) 01928 return (JBDATA *)ERROR_PTR("naclass not made", procName, NULL); 01929 if ((napage = numaCreate(ncomp)) == NULL) 01930 return (JBDATA *)ERROR_PTR("napage not made", procName, NULL); 01931 if ((ptaul = ptaCreate(ncomp)) == NULL) 01932 return (JBDATA *)ERROR_PTR("pta not made", procName, NULL); 01933 for (i = 6; i < nsa; i++) { 01934 linestr = sarrayGetString(sa, i, 0); 01935 sscanf(linestr, "%d %d %d %d\n", &ipage, &iclass, &x, &y); 01936 numaAddNumber(napage, ipage); 01937 numaAddNumber(naclass, iclass); 01938 ptaAddPt(ptaul, x, y); 01939 } 01940 01941 if ((jbdata = (JBDATA *)CALLOC(1, sizeof(JBDATA))) == NULL) 01942 return (JBDATA *)ERROR_PTR("data not made", procName, NULL); 01943 jbdata->pix = pixs; 01944 jbdata->npages = npages; 01945 jbdata->w = w; 01946 jbdata->h = h; 01947 jbdata->nclass = nclass; 01948 jbdata->latticew = cellw; 01949 jbdata->latticeh = cellh; 01950 jbdata->naclass = naclass; 01951 jbdata->napage = napage; 01952 jbdata->ptaul = ptaul; 01953 01954 FREE(data); 01955 sarrayDestroy(&sa); 01956 return jbdata; 01957 } 01958 01959 01960 /*! 01961 * jbDataRender() 01962 * 01963 * Input: jbdata 01964 * debugflag (if TRUE, writes into 2 bpp pix and adds 01965 * component outlines in color) 01966 * Return: pixa (reconstruction of original images, using templates) or 01967 * null on error 01968 */ 01969 PIXA * 01970 jbDataRender(JBDATA *data, 01971 l_int32 debugflag) 01972 { 01973 l_int32 i, w, h, cellw, cellh, x, y, iclass, ipage; 01974 l_int32 npages, nclass, ncomp, wp, hp; 01975 BOX *box; 01976 NUMA *naclass, *napage; 01977 PIX *pixt, *pixt2, *pix, *pixd; 01978 PIXA *pixat; /* pixa of templates */ 01979 PIXA *pixad; /* pixa of output images */ 01980 PIXCMAP *cmap; 01981 PTA *ptaul; 01982 01983 PROCNAME("jbDataRender"); 01984 01985 if (!data) 01986 return (PIXA *)ERROR_PTR("data not defined", procName, NULL); 01987 01988 npages = data->npages; 01989 w = data->w; 01990 h = data->h; 01991 pixt = data->pix; 01992 nclass = data->nclass; 01993 cellw = data->latticew; 01994 cellh = data->latticeh; 01995 naclass = data->naclass; 01996 napage = data->napage; 01997 ptaul = data->ptaul; 01998 ncomp = numaGetCount(naclass); 01999 02000 /* Reconstruct the original set of images from the templates 02001 * and the data associated with each component. First, 02002 * generate the output pixa as a set of empty pix. */ 02003 if ((pixad = pixaCreate(npages)) == NULL) 02004 return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); 02005 for (i = 0; i < npages; i++) { 02006 if (debugflag == FALSE) 02007 pix = pixCreate(w, h, 1); 02008 else { 02009 pix = pixCreate(w, h, 2); 02010 cmap = pixcmapCreate(2); 02011 pixcmapAddColor(cmap, 255, 255, 255); 02012 pixcmapAddColor(cmap, 0, 0, 0); 02013 pixcmapAddColor(cmap, 255, 0, 0); /* for box outlines */ 02014 pixSetColormap(pix, cmap); 02015 } 02016 pixaAddPix(pixad, pix, L_INSERT); 02017 } 02018 02019 /* Put the class templates into a pixa. */ 02020 if ((pixat = pixaCreateFromPix(pixt, nclass, cellw, cellh)) == NULL) 02021 return (PIXA *)ERROR_PTR("pixat not made", procName, NULL); 02022 02023 /* Place each component in the right location on its page. */ 02024 for (i = 0; i < ncomp; i++) { 02025 numaGetIValue(napage, i, &ipage); 02026 numaGetIValue(naclass, i, &iclass); 02027 pix = pixaGetPix(pixat, iclass, L_CLONE); /* the template */ 02028 wp = pixGetWidth(pix); 02029 hp = pixGetHeight(pix); 02030 ptaGetIPt(ptaul, i, &x, &y); 02031 pixd = pixaGetPix(pixad, ipage, L_CLONE); /* the output page */ 02032 if (debugflag == FALSE) 02033 pixRasterop(pixd, x, y, wp, hp, PIX_SRC | PIX_DST, pix, 0, 0); 02034 else { 02035 pixt2 = pixConvert1To2Cmap(pix); 02036 pixRasterop(pixd, x, y, wp, hp, PIX_SRC | PIX_DST, pixt2, 0, 0); 02037 box = boxCreate(x, y, wp, hp); 02038 pixRenderBoxArb(pixd, box, 1, 255, 0, 0); 02039 pixDestroy(&pixt2); 02040 boxDestroy(&box); 02041 } 02042 pixDestroy(&pix); /* the clone only */ 02043 pixDestroy(&pixd); /* the clone only */ 02044 } 02045 02046 pixaDestroy(&pixat); 02047 return pixad; 02048 } 02049 02050 02051 /*! 02052 * jbGetULCorners() 02053 * 02054 * Input: jbclasser 02055 * pixs (full res image) 02056 * boxa (of c.c. bounding rectangles for this page) 02057 * Return: 0 if OK, 1 on error 02058 * 02059 * Notes: 02060 * (1) This computes the ptaul field, which has the global UL corners, 02061 * adjusted for each specific component, so that each component 02062 * can be replaced by the template for its class and have the 02063 * centroid in the template in the same position as the 02064 * centroid of the original connected component. It is important 02065 * that this be done properly to avoid a wavy baseline in the 02066 * result. 02067 * (2) The array fields ptac and ptact give the centroids of 02068 * those components relative to the UL corner of each component. 02069 * Here, we compute the difference in each component, round to 02070 * nearest integer, and correct the box->x and box->y by 02071 * the appropriate integral difference. 02072 * (3) The templates and stored instances are all bordered. 02073 */ 02074 l_int32 02075 jbGetULCorners(JBCLASSER *classer, 02076 PIX *pixs, 02077 BOXA *boxa) 02078 { 02079 l_int32 i, baseindex, index, n, iclass, idelx, idely, x, y, dx, dy; 02080 l_int32 *sumtab; 02081 l_float32 x1, x2, y1, y2, delx, dely; 02082 BOX *box; 02083 NUMA *naclass; 02084 PIX *pixt; 02085 PTA *ptac, *ptact, *ptaul; 02086 02087 PROCNAME("jbGetULCorners"); 02088 02089 if (!classer) 02090 return ERROR_INT("classer not defined", procName, 1); 02091 if (!pixs) 02092 return ERROR_INT("pixs not defined", procName, 1); 02093 if (!boxa) 02094 return ERROR_INT("boxa not defined", procName, 1); 02095 02096 n = boxaGetCount(boxa); 02097 ptaul = classer->ptaul; 02098 naclass = classer->naclass; 02099 ptac = classer->ptac; 02100 ptact = classer->ptact; 02101 baseindex = classer->baseindex; /* num components before this page */ 02102 sumtab = makePixelSumTab8(); 02103 for (i = 0; i < n; i++) { 02104 index = baseindex + i; 02105 ptaGetPt(ptac, index, &x1, &y1); 02106 numaGetIValue(naclass, index, &iclass); 02107 ptaGetPt(ptact, iclass, &x2, &y2); 02108 delx = x2 - x1; 02109 dely = y2 - y1; 02110 if (delx >= 0) 02111 idelx = (l_int32)(delx + 0.5); 02112 else 02113 idelx = (l_int32)(delx - 0.5); 02114 if (dely >= 0) 02115 idely = (l_int32)(dely + 0.5); 02116 else 02117 idely = (l_int32)(dely - 0.5); 02118 if ((box = boxaGetBox(boxa, i, L_CLONE)) == NULL) 02119 return ERROR_INT("box not found", procName, 1); 02120 boxGetGeometry(box, &x, &y, NULL, NULL); 02121 02122 /* Get final increments dx and dy for best alignment */ 02123 pixt = pixaGetPix(classer->pixat, iclass, L_CLONE); 02124 finalPositioningForAlignment(pixs, x, y, idelx, idely, 02125 pixt, sumtab, &dx, &dy); 02126 /* if (i % 20 == 0) 02127 fprintf(stderr, "dx = %d, dy = %d\n", dx, dy); */ 02128 ptaAddPt(ptaul, x - idelx + dx, y - idely + dy); 02129 boxDestroy(&box); 02130 pixDestroy(&pixt); 02131 } 02132 02133 FREE(sumtab); 02134 return 0; 02135 } 02136 02137 02138 /*! 02139 * jbGetLLCorners() 02140 * 02141 * Input: jbclasser 02142 * Return: 0 if OK, 1 on error 02143 * 02144 * Notes: 02145 * (1) This computes the ptall field, which has the global LL corners, 02146 * adjusted for each specific component, so that each component 02147 * can be replaced by the template for its class and have the 02148 * centroid in the template in the same position as the 02149 * centroid of the original connected component. It is important 02150 * that this be done properly to avoid a wavy baseline in the result. 02151 * (2) It is computed here from the corresponding UL corners, where 02152 * the input templates and stored instances are all bordered. 02153 * This should be done after all pages have been processed. 02154 * (3) For proper substitution, the templates whose LL corners are 02155 * placed in these locations must be UN-bordered. 02156 * This is available for a realistic jbig2 encoder, which would 02157 * (1) encode each template without a border, and (2) encode 02158 * the position using the LL corner (rather than the UL 02159 * corner) because the difference between y-values 02160 * of successive instances is typically close to zero. 02161 */ 02162 l_int32 02163 jbGetLLCorners(JBCLASSER *classer) 02164 { 02165 l_int32 i, iclass, n, x1, y1, h; 02166 NUMA *naclass; 02167 PIX *pix; 02168 PIXA *pixat; 02169 PTA *ptaul, *ptall; 02170 02171 PROCNAME("jbGetLLCorners"); 02172 02173 if (!classer) 02174 return ERROR_INT("classer not defined", procName, 1); 02175 02176 ptaul = classer->ptaul; 02177 naclass = classer->naclass; 02178 pixat = classer->pixat; 02179 02180 ptaDestroy(&classer->ptall); 02181 n = ptaGetCount(ptaul); 02182 ptall = ptaCreate(n); 02183 classer->ptall = ptall; 02184 02185 /* If the templates were bordered, we would add h - 1 to the UL 02186 * corner y-value. However, because the templates to be used 02187 * here have their borders removed, and the borders are 02188 * JB_ADDED_PIXELS on each side, we add h - 1 - 2 * JB_ADDED_PIXELS 02189 * to the UL corner y-value. */ 02190 for (i = 0; i < n; i++) { 02191 ptaGetIPt(ptaul, i, &x1, &y1); 02192 numaGetIValue(naclass, i, &iclass); 02193 pix = pixaGetPix(pixat, iclass, L_CLONE); 02194 h = pixGetHeight(pix); 02195 ptaAddPt(ptall, x1, y1 + h - 1 - 2 * JB_ADDED_PIXELS); 02196 pixDestroy(&pix); 02197 } 02198 02199 return 0; 02200 } 02201 02202 02203 /*----------------------------------------------------------------------* 02204 * Static helpers * 02205 *----------------------------------------------------------------------*/ 02206 /* When looking for similar matches we check templates whose size is +/- 2 in 02207 * each direction. This involves 25 possible sizes. This array contains the 02208 * offsets for each of those positions in a spiral pattern. There are 25 pairs 02209 * of numbers in this array: even positions are x values. */ 02210 static int two_by_two_walk[50] = { 02211 0, 0, 02212 0, 1, 02213 -1, 0, 02214 0, -1, 02215 1, 0, 02216 -1, 1, 02217 1, 1, 02218 -1, -1, 02219 1, -1, 02220 0, -2, 02221 2, 0, 02222 0, 2, 02223 -2, 0, 02224 -1, -2, 02225 1, -2, 02226 2, -1, 02227 2, 1, 02228 1, 2, 02229 -1, 2, 02230 -2, 1, 02231 -2, -1, 02232 -2, -2, 02233 2, -2, 02234 2, 2, 02235 -2, 2}; 02236 02237 02238 /*! 02239 * findSimilarSizedTemplatesInit() 02240 * 02241 * Input: classer 02242 * pixs (instance to be matched) 02243 * Return: Allocated context to be used with findSimilar* 02244 */ 02245 static JBFINDCTX * 02246 findSimilarSizedTemplatesInit(JBCLASSER *classer, 02247 PIX *pixs) 02248 { 02249 JBFINDCTX *state; 02250 02251 state = (JBFINDCTX *)CALLOC(1, sizeof(JBFINDCTX)); 02252 state->w = pixGetWidth(pixs) - 2 * JB_ADDED_PIXELS; 02253 state->h = pixGetHeight(pixs) - 2 * JB_ADDED_PIXELS; 02254 state->classer = classer; 02255 02256 return state; 02257 } 02258 02259 02260 static void 02261 findSimilarSizedTemplatesDestroy(JBFINDCTX **pstate) 02262 { 02263 JBFINDCTX *state; 02264 02265 PROCNAME("findSimilarSizedTemplatesDestroy"); 02266 02267 if (pstate == NULL) { 02268 L_WARNING("ptr address is null", procName); 02269 return; 02270 } 02271 if ((state = *pstate) == NULL) 02272 return; 02273 02274 numaDestroy(&state->numa); 02275 FREE(state); 02276 *pstate = NULL; 02277 return; 02278 } 02279 02280 02281 /*! 02282 * findSimilarSizedTemplatesNext() 02283 * 02284 * Input: state (from findSimilarSizedTemplatesInit) 02285 * Return: Next template number, or -1 when finished 02286 * 02287 * We have a hash table mapping template area to a list of template 02288 * numbers with that area. We wish to find similar sized templates, 02289 * so we first look for templates with the same width and height, and 02290 * then with width + 1, etc. This walk is guided by the 02291 * two_by_two_walk array, above. 02292 * 02293 * We don't want to have to collect the whole list of templates first because 02294 * (we hope) to find it quickly. So we keep the context for this walk in an 02295 * explictit state structure and this function acts like a generator. 02296 */ 02297 static l_int32 02298 findSimilarSizedTemplatesNext(JBFINDCTX *state) 02299 { 02300 l_int32 desiredh, desiredw, size, templ; 02301 PIX *pixt; 02302 02303 while(1) { /* Continue the walk over step 'i' */ 02304 if (state->i >= 25) { /* all done */ 02305 return -1; 02306 } 02307 02308 desiredw = state->w + two_by_two_walk[2 * state->i]; 02309 desiredh = state->h + two_by_two_walk[2 * state->i + 1]; 02310 if (desiredh < 1 || desiredw < 1) { /* invalid size */ 02311 state->i++; 02312 continue; 02313 } 02314 02315 if (!state->numa) { 02316 /* We have yet to start walking the array for the step 'i' */ 02317 state->numa = numaHashGetNuma(state->classer->nahash, 02318 desiredh * desiredw); 02319 if (!state->numa) { /* nothing there */ 02320 state->i++; 02321 continue; 02322 } 02323 02324 state->n = 0; /* OK, we got a numa. */ 02325 } 02326 02327 /* Continue working on this numa */ 02328 size = numaGetCount(state->numa); 02329 for ( ; state->n < size; ) { 02330 templ = (l_int32)(state->numa->array[state->n++] + 0.5); 02331 pixt = pixaGetPix(state->classer->pixat, templ, L_CLONE); 02332 if (pixGetWidth(pixt) - 2 * JB_ADDED_PIXELS == desiredw && 02333 pixGetHeight(pixt) - 2 * JB_ADDED_PIXELS == desiredh) { 02334 pixDestroy(&pixt); 02335 return templ; 02336 } 02337 pixDestroy(&pixt); 02338 } 02339 02340 /* Exhausted the numa; take another step and try again */ 02341 state->i++; 02342 numaDestroy(&state->numa); 02343 continue; 02344 } 02345 } 02346 02347 02348 /*! 02349 * finalPositioningForAlignment() 02350 * 02351 * Input: pixs (input page image) 02352 * x, y (location of UL corner of bb of component in pixs) 02353 * idelx, idely (compensation to match centroids of component 02354 * and template) 02355 * pixt (template, with JB_ADDED_PIXELS of padding on all sides) 02356 * sumtab (for summing fg pixels in an image) 02357 * &dx, &dy (return delta on position for best match; each 02358 * one is in the set {-1, 0, 1}) 02359 * Return: 0 if OK, 1 on error 02360 * 02361 */ 02362 static l_int32 02363 finalPositioningForAlignment(PIX *pixs, 02364 l_int32 x, 02365 l_int32 y, 02366 l_int32 idelx, 02367 l_int32 idely, 02368 PIX *pixt, 02369 l_int32 *sumtab, 02370 l_int32 *pdx, 02371 l_int32 *pdy) 02372 { 02373 l_int32 w, h, i, j, minx, miny, count, mincount; 02374 PIX *pixi; /* clipped from source pixs */ 02375 PIX *pixr; /* temporary storage */ 02376 BOX *box; 02377 02378 PROCNAME("finalPositioningForAlignment"); 02379 02380 if (!pixs) 02381 return ERROR_INT("pixs not defined", procName, 1); 02382 if (!pixt) 02383 return ERROR_INT("pixt not defined", procName, 1); 02384 if (!pdx || !pdy) 02385 return ERROR_INT("&dx and &dy not both defined", procName, 1); 02386 if (!sumtab) 02387 return ERROR_INT("sumtab not defined", procName, 1); 02388 *pdx = *pdy = 0; 02389 02390 /* Use JB_ADDED_PIXELS pixels padding on each side */ 02391 w = pixGetWidth(pixt); 02392 h = pixGetHeight(pixt); 02393 box = boxCreate(x - idelx - JB_ADDED_PIXELS, 02394 y - idely - JB_ADDED_PIXELS, w, h); 02395 pixi = pixClipRectangle(pixs, box, NULL); 02396 boxDestroy(&box); 02397 if (!pixi) 02398 return ERROR_INT("pixi not made", procName, 1); 02399 02400 pixr = pixCreate(pixGetWidth(pixi), pixGetHeight(pixi), 1); 02401 mincount = 0x7fffffff; 02402 for (i = -1; i <= 1; i++) { 02403 for (j = -1; j <= 1; j++) { 02404 pixCopy(pixr, pixi); 02405 pixRasterop(pixr, j, i, w, h, PIX_SRC ^ PIX_DST, pixt, 0, 0); 02406 pixCountPixels(pixr, &count, sumtab); 02407 if (count < mincount) { 02408 minx = j; 02409 miny = i; 02410 mincount = count; 02411 } 02412 } 02413 } 02414 pixDestroy(&pixi); 02415 pixDestroy(&pixr); 02416 02417 *pdx = minx; 02418 *pdy = miny; 02419 return 0; 02420 }