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 * selgen.c 00018 * 00019 * This file contains functions that generate hit-miss Sels 00020 * for doing a loose match to a small bitmap. The hit-miss 00021 * Sel is made from a given bitmap. Several "knobs" 00022 * are available to control the looseness of the match. 00023 * In general, a tight match will have fewer false positives 00024 * (bad matches) but more false negatives (missed patterns). 00025 * The values to be used depend on the quality and variation 00026 * of the image in which the pattern is to be searched, 00027 * and the relative penalties of false positives and 00028 * false negatives. Default values for the three knobs -- 00029 * minimum distance to boundary pixels, number of extra pixels 00030 * added to selected sides, and minimum acceptable runlength 00031 * in eroded version -- are provided. 00032 * 00033 * The generated hit-miss Sels can always be used in the 00034 * rasterop implementation of binary morphology (in morph.h). 00035 * If they are small enough (not more than 31 pixels extending 00036 * in any direction from the Sel origin), they can also be used 00037 * to auto-generate dwa code (fmorphauto.c). 00038 * 00039 * 00040 * Generate a subsampled structuring element 00041 * SEL *pixGenerateSelWithRuns() 00042 * SEL *pixGenerateSelRandom() 00043 * SEL *pixGenerateSelBoundary() 00044 * 00045 * Accumulate data on runs along lines 00046 * NUMA *pixGetRunCentersOnLine() 00047 * NUMA *pixGetRunsOnLine() 00048 * 00049 * Subsample boundary pixels in relatively ordered way 00050 * PTA *pixSubsampleBoundaryPixels() 00051 * PTA *adjacentOnPixelInRaster() 00052 * 00053 * Display generated sel with originating image 00054 * PIX *pixDisplayHitMissSel() 00055 */ 00056 00057 #include <stdio.h> 00058 #include <stdlib.h> 00059 #include "allheaders.h" 00060 00061 00062 /* default minimum distance of a hit-miss pixel element to 00063 * a boundary pixel of its color. */ 00064 static const l_int32 DEFAULT_DISTANCE_TO_BOUNDARY = 1; 00065 static const l_int32 MAX_DISTANCE_TO_BOUNDARY = 4; 00066 00067 /* default min runlength to accept a hit or miss element located 00068 * at its center */ 00069 static const l_int32 DEFAULT_MIN_RUNLENGTH = 3; 00070 00071 00072 /* default scalefactor for displaying image and hit-miss sel 00073 * that is derived from it */ 00074 static const l_int32 DEFAULT_SEL_SCALEFACTOR = 7; 00075 static const l_int32 MAX_SEL_SCALEFACTOR = 31; /* should be big enough */ 00076 00077 #ifndef NO_CONSOLE_IO 00078 #define DEBUG_DISPLAY_HM_SEL 0 00079 #endif /* ~NO_CONSOLE_IO */ 00080 00081 00082 /*-----------------------------------------------------------------* 00083 * Generate a subsampled structuring element * 00084 *-----------------------------------------------------------------*/ 00085 /*! 00086 * pixGenerateSelWithRuns() 00087 * 00088 * Input: pix (1 bpp, typically small, to be used as a pattern) 00089 * nhlines (number of hor lines along which elements are found) 00090 * nvlines (number of vert lines along which elements are found) 00091 * distance (min distance from boundary pixel; use 0 for default) 00092 * minlength (min runlength to set hit or miss; use 0 for default) 00093 * toppix (number of extra pixels of bg added above) 00094 * botpix (number of extra pixels of bg added below) 00095 * leftpix (number of extra pixels of bg added to left) 00096 * rightpix (number of extra pixels of bg added to right) 00097 * &pixe (<optional return> input pix expanded by extra pixels) 00098 * Return: sel (hit-miss for input pattern), or null on error 00099 * 00100 * Notes: 00101 * (1) The horizontal and vertical lines along which elements are 00102 * selected are roughly equally spaced. The actual locations of 00103 * the hits and misses are the centers of respective run-lengths. 00104 * (2) No elements are selected that are less than 'distance' pixels away 00105 * from a boundary pixel of the same color. This makes the 00106 * match much more robust to edge noise. Valid inputs of 00107 * 'distance' are 0, 1, 2, 3 and 4. If distance is either 0 or 00108 * greater than 4, we reset it to the default value. 00109 * (3) The 4 numbers for adding rectangles of pixels outside the fg 00110 * can be use if the pattern is expected to be surrounded by bg 00111 * (white) pixels. On the other hand, if the pattern may be near 00112 * other fg (black) components on some sides, use 0 for those sides. 00113 * (4) The pixels added to a side allow you to have miss elements there. 00114 * There is a constraint between distance, minlength, and 00115 * the added pixels for this to work. We illustrate using the 00116 * default values. If you add 5 pixels to the top, and use a 00117 * distance of 1, then you end up with a vertical run of at least 00118 * 4 bg pixels along the top edge of the image. If you use a 00119 * minimum runlength of 3, each vertical line will always find 00120 * a miss near the center of its run. However, if you use a 00121 * minimum runlength of 5, you will not get a miss on every vertical 00122 * line. As another example, if you have 7 added pixels and a 00123 * distance of 2, you can use a runlength up to 5 to guarantee 00124 * that the miss element is recorded. We give a warning if the 00125 * contraint does not guarantee a miss element outside the 00126 * image proper. 00127 * (5) The input pix, as extended by the extra pixels on selected sides, 00128 * can optionally be returned. For debugging, call 00129 * pixDisplayHitMissSel() to visualize the hit-miss sel superimposed 00130 * on the generating bitmap. 00131 */ 00132 SEL * 00133 pixGenerateSelWithRuns(PIX *pixs, 00134 l_int32 nhlines, 00135 l_int32 nvlines, 00136 l_int32 distance, 00137 l_int32 minlength, 00138 l_int32 toppix, 00139 l_int32 botpix, 00140 l_int32 leftpix, 00141 l_int32 rightpix, 00142 PIX **ppixe) 00143 { 00144 l_int32 ws, hs, w, h, x, y, xval, yval, i, j, nh, nm; 00145 l_float32 delh, delw; 00146 NUMA *nah, *nam; 00147 PIX *pixt1, *pixt2, *pixfg, *pixbg; 00148 PTA *ptah, *ptam; 00149 SEL *seld, *sel; 00150 00151 PROCNAME("pixGenerateSelWithRuns"); 00152 00153 if (ppixe) *ppixe = NULL; 00154 if (!pixs) 00155 return (SEL *)ERROR_PTR("pixs not defined", procName, NULL); 00156 if (pixGetDepth(pixs) != 1) 00157 return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL); 00158 if (nhlines < 1 && nvlines < 1) 00159 return (SEL *)ERROR_PTR("nvlines and nhlines both < 1", procName, NULL); 00160 00161 if (distance <= 0) 00162 distance = DEFAULT_DISTANCE_TO_BOUNDARY; 00163 if (minlength <= 0) 00164 minlength = DEFAULT_MIN_RUNLENGTH; 00165 if (distance > MAX_DISTANCE_TO_BOUNDARY) { 00166 L_WARNING("distance too large; setting to max value", procName); 00167 distance = MAX_DISTANCE_TO_BOUNDARY; 00168 } 00169 00170 /* Locate the foreground */ 00171 pixClipToForeground(pixs, &pixt1, NULL); 00172 if (!pixt1) 00173 return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL); 00174 ws = pixGetWidth(pixt1); 00175 hs = pixGetHeight(pixt1); 00176 w = ws; 00177 h = hs; 00178 00179 /* Crop out a region including the foreground, and add pixels 00180 * on sides depending on the side flags */ 00181 if (toppix || botpix || leftpix || rightpix) { 00182 x = y = 0; 00183 if (toppix) { 00184 h += toppix; 00185 y = toppix; 00186 if (toppix < distance + minlength) 00187 L_WARNING("no miss elements in added top pixels", procName); 00188 } 00189 if (botpix) { 00190 h += botpix; 00191 if (botpix < distance + minlength) 00192 L_WARNING("no miss elements in added bot pixels", procName); 00193 } 00194 if (leftpix) { 00195 w += leftpix; 00196 x = leftpix; 00197 if (leftpix < distance + minlength) 00198 L_WARNING("no miss elements in added left pixels", procName); 00199 } 00200 if (rightpix) { 00201 w += rightpix; 00202 if (rightpix < distance + minlength) 00203 L_WARNING("no miss elements in added right pixels", procName); 00204 } 00205 pixt2 = pixCreate(w, h, 1); 00206 pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0); 00207 } 00208 else 00209 pixt2 = pixClone(pixt1); 00210 if (ppixe) 00211 *ppixe = pixClone(pixt2); 00212 pixDestroy(&pixt1); 00213 00214 /* Identify fg and bg pixels that are at least 'distance' pixels 00215 * away from the boundary pixels in their set */ 00216 seld = selCreateBrick(2 * distance + 1, 2 * distance + 1, 00217 distance, distance, SEL_HIT); 00218 pixfg = pixErode(NULL, pixt2, seld); 00219 pixbg = pixDilate(NULL, pixt2, seld); 00220 pixInvert(pixbg, pixbg); 00221 selDestroy(&seld); 00222 pixDestroy(&pixt2); 00223 00224 /* Accumulate hit and miss points */ 00225 ptah = ptaCreate(0); 00226 ptam = ptaCreate(0); 00227 if (nhlines >= 1) { 00228 delh = (l_float32)h / (l_float32)(nhlines + 1); 00229 for (i = 0, y = 0; i < nhlines; i++) { 00230 y += (l_int32)(delh + 0.5); 00231 nah = pixGetRunCentersOnLine(pixfg, -1, y, minlength); 00232 nam = pixGetRunCentersOnLine(pixbg, -1, y, minlength); 00233 nh = numaGetCount(nah); 00234 nm = numaGetCount(nam); 00235 for (j = 0; j < nh; j++) { 00236 numaGetIValue(nah, j, &xval); 00237 ptaAddPt(ptah, xval, y); 00238 } 00239 for (j = 0; j < nm; j++) { 00240 numaGetIValue(nam, j, &xval); 00241 ptaAddPt(ptam, xval, y); 00242 } 00243 numaDestroy(&nah); 00244 numaDestroy(&nam); 00245 } 00246 } 00247 if (nvlines >= 1) { 00248 delw = (l_float32)w / (l_float32)(nvlines + 1); 00249 for (i = 0, x = 0; i < nvlines; i++) { 00250 x += (l_int32)(delw + 0.5); 00251 nah = pixGetRunCentersOnLine(pixfg, x, -1, minlength); 00252 nam = pixGetRunCentersOnLine(pixbg, x, -1, minlength); 00253 nh = numaGetCount(nah); 00254 nm = numaGetCount(nam); 00255 for (j = 0; j < nh; j++) { 00256 numaGetIValue(nah, j, &yval); 00257 ptaAddPt(ptah, x, yval); 00258 } 00259 for (j = 0; j < nm; j++) { 00260 numaGetIValue(nam, j, &yval); 00261 ptaAddPt(ptam, x, yval); 00262 } 00263 numaDestroy(&nah); 00264 numaDestroy(&nam); 00265 } 00266 } 00267 00268 /* Make the Sel with those points */ 00269 sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE); 00270 nh = ptaGetCount(ptah); 00271 for (i = 0; i < nh; i++) { 00272 ptaGetIPt(ptah, i, &x, &y); 00273 selSetElement(sel, y, x, SEL_HIT); 00274 } 00275 nm = ptaGetCount(ptam); 00276 for (i = 0; i < nm; i++) { 00277 ptaGetIPt(ptam, i, &x, &y); 00278 selSetElement(sel, y, x, SEL_MISS); 00279 } 00280 00281 pixDestroy(&pixfg); 00282 pixDestroy(&pixbg); 00283 ptaDestroy(&ptah); 00284 ptaDestroy(&ptam); 00285 return sel; 00286 } 00287 00288 00289 /*! 00290 * pixGenerateSelRandom() 00291 * 00292 * Input: pix (1 bpp, typically small, to be used as a pattern) 00293 * hitfract (fraction of allowable fg pixels that are hits) 00294 * missfract (fraction of allowable bg pixels that are misses) 00295 * distance (min distance from boundary pixel; use 0 for default) 00296 * toppix (number of extra pixels of bg added above) 00297 * botpix (number of extra pixels of bg added below) 00298 * leftpix (number of extra pixels of bg added to left) 00299 * rightpix (number of extra pixels of bg added to right) 00300 * &pixe (<optional return> input pix expanded by extra pixels) 00301 * Return: sel (hit-miss for input pattern), or null on error 00302 * 00303 * Notes: 00304 * (1) Either of hitfract and missfract can be zero. If both are zero, 00305 * the sel would be empty, and NULL is returned. 00306 * (2) No elements are selected that are less than 'distance' pixels away 00307 * from a boundary pixel of the same color. This makes the 00308 * match much more robust to edge noise. Valid inputs of 00309 * 'distance' are 0, 1, 2, 3 and 4. If distance is either 0 or 00310 * greater than 4, we reset it to the default value. 00311 * (3) The 4 numbers for adding rectangles of pixels outside the fg 00312 * can be use if the pattern is expected to be surrounded by bg 00313 * (white) pixels. On the other hand, if the pattern may be near 00314 * other fg (black) components on some sides, use 0 for those sides. 00315 * (4) The input pix, as extended by the extra pixels on selected sides, 00316 * can optionally be returned. For debugging, call 00317 * pixDisplayHitMissSel() to visualize the hit-miss sel superimposed 00318 * on the generating bitmap. 00319 */ 00320 SEL * 00321 pixGenerateSelRandom(PIX *pixs, 00322 l_float32 hitfract, 00323 l_float32 missfract, 00324 l_int32 distance, 00325 l_int32 toppix, 00326 l_int32 botpix, 00327 l_int32 leftpix, 00328 l_int32 rightpix, 00329 PIX **ppixe) 00330 { 00331 l_int32 ws, hs, w, h, x, y, i, j, thresh; 00332 l_uint32 val; 00333 PIX *pixt1, *pixt2, *pixfg, *pixbg; 00334 SEL *seld, *sel; 00335 00336 PROCNAME("pixGenerateSelRandom"); 00337 00338 if (ppixe) *ppixe = NULL; 00339 if (!pixs) 00340 return (SEL *)ERROR_PTR("pixs not defined", procName, NULL); 00341 if (pixGetDepth(pixs) != 1) 00342 return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL); 00343 if (hitfract <= 0.0 && missfract <= 0.0) 00344 return (SEL *)ERROR_PTR("no hits or misses", procName, NULL); 00345 if (hitfract > 1.0 || missfract > 1.0) 00346 return (SEL *)ERROR_PTR("fraction can't be > 1.0", procName, NULL); 00347 00348 if (distance <= 0) 00349 distance = DEFAULT_DISTANCE_TO_BOUNDARY; 00350 if (distance > MAX_DISTANCE_TO_BOUNDARY) { 00351 L_WARNING("distance too large; setting to max value", procName); 00352 distance = MAX_DISTANCE_TO_BOUNDARY; 00353 } 00354 00355 /* Locate the foreground */ 00356 pixClipToForeground(pixs, &pixt1, NULL); 00357 if (!pixt1) 00358 return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL); 00359 ws = pixGetWidth(pixt1); 00360 hs = pixGetHeight(pixt1); 00361 w = ws; 00362 h = hs; 00363 00364 /* Crop out a region including the foreground, and add pixels 00365 * on sides depending on the side flags */ 00366 if (toppix || botpix || leftpix || rightpix) { 00367 x = y = 0; 00368 if (toppix) { 00369 h += toppix; 00370 y = toppix; 00371 } 00372 if (botpix) 00373 h += botpix; 00374 if (leftpix) { 00375 w += leftpix; 00376 x = leftpix; 00377 } 00378 if (rightpix) 00379 w += rightpix; 00380 pixt2 = pixCreate(w, h, 1); 00381 pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0); 00382 } 00383 else 00384 pixt2 = pixClone(pixt1); 00385 if (ppixe) 00386 *ppixe = pixClone(pixt2); 00387 pixDestroy(&pixt1); 00388 00389 /* Identify fg and bg pixels that are at least 'distance' pixels 00390 * away from the boundary pixels in their set */ 00391 seld = selCreateBrick(2 * distance + 1, 2 * distance + 1, 00392 distance, distance, SEL_HIT); 00393 pixfg = pixErode(NULL, pixt2, seld); 00394 pixbg = pixDilate(NULL, pixt2, seld); 00395 pixInvert(pixbg, pixbg); 00396 selDestroy(&seld); 00397 pixDestroy(&pixt2); 00398 00399 /* Generate the sel from a random selection of these points */ 00400 sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE); 00401 if (hitfract > 0.0) { 00402 thresh = (l_int32)(hitfract * (l_float64)RAND_MAX); 00403 for (i = 0; i < h; i++) { 00404 for (j = 0; j < w; j++) { 00405 pixGetPixel(pixfg, j, i, &val); 00406 if (val) { 00407 if (rand() < thresh) 00408 selSetElement(sel, i, j, SEL_HIT); 00409 } 00410 } 00411 } 00412 } 00413 if (missfract > 0.0) { 00414 thresh = (l_int32)(missfract * (l_float64)RAND_MAX); 00415 for (i = 0; i < h; i++) { 00416 for (j = 0; j < w; j++) { 00417 pixGetPixel(pixbg, j, i, &val); 00418 if (val) { 00419 if (rand() < thresh) 00420 selSetElement(sel, i, j, SEL_MISS); 00421 } 00422 } 00423 } 00424 } 00425 00426 pixDestroy(&pixfg); 00427 pixDestroy(&pixbg); 00428 return sel; 00429 } 00430 00431 00432 /*! 00433 * pixGenerateSelBoundary() 00434 * 00435 * Input: pix (1 bpp, typically small, to be used as a pattern) 00436 * hitdist (min distance from fg boundary pixel) 00437 * missdist (min distance from bg boundary pixel) 00438 * hitskip (number of boundary pixels skipped between hits) 00439 * missskip (number of boundary pixels skipped between misses) 00440 * topflag (flag for extra pixels of bg added above) 00441 * botflag (flag for extra pixels of bg added below) 00442 * leftflag (flag for extra pixels of bg added to left) 00443 * rightflag (flag for extra pixels of bg added to right) 00444 * &pixe (<optional return> input pix expanded by extra pixels) 00445 * Return: sel (hit-miss for input pattern), or null on error 00446 * 00447 * Notes: 00448 * (1) All fg elements selected are exactly hitdist pixels away from 00449 * the nearest fg boundary pixel, and ditto for bg elements. 00450 * Valid inputs of hitdist and missdist are 0, 1, 2, 3 and 4. 00451 * For example, a hitdist of 0 puts the hits at the fg boundary. 00452 * Usually, the distances should be > 0 avoid the effect of 00453 * noise at the boundary. 00454 * (2) Set hitskip < 0 if no hits are to be used. Ditto for missskip. 00455 * If both hitskip and missskip are < 0, the sel would be empty, 00456 * and NULL is returned. 00457 * (3) The 4 flags determine whether the sel is increased on that side 00458 * to allow bg misses to be placed all along that boundary. 00459 * The increase in sel size on that side is the minimum necessary 00460 * to allow the misses to be placed at mindist. For text characters, 00461 * the topflag and botflag are typically set to 1, and the leftflag 00462 * and rightflag to 0. 00463 * (4) The input pix, as extended by the extra pixels on selected sides, 00464 * can optionally be returned. For debugging, call 00465 * pixDisplayHitMissSel() to visualize the hit-miss sel superimposed 00466 * on the generating bitmap. 00467 * (5) This is probably the best of the three sel generators, in the 00468 * sense that you have the most flexibility with the smallest number 00469 * of hits and misses. 00470 */ 00471 SEL * 00472 pixGenerateSelBoundary(PIX *pixs, 00473 l_int32 hitdist, 00474 l_int32 missdist, 00475 l_int32 hitskip, 00476 l_int32 missskip, 00477 l_int32 topflag, 00478 l_int32 botflag, 00479 l_int32 leftflag, 00480 l_int32 rightflag, 00481 PIX **ppixe) 00482 { 00483 l_int32 ws, hs, w, h, x, y, ix, iy, i, npt; 00484 PIX *pixt1, *pixt2, *pixt3, *pixfg, *pixbg; 00485 SEL *selh, *selm, *sel_3, *sel; 00486 PTA *ptah, *ptam; 00487 00488 PROCNAME("pixGenerateSelBoundary"); 00489 00490 if (ppixe) *ppixe = NULL; 00491 if (!pixs) 00492 return (SEL *)ERROR_PTR("pixs not defined", procName, NULL); 00493 if (pixGetDepth(pixs) != 1) 00494 return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL); 00495 if (hitdist < 0 || hitdist > 4 || missdist < 0 || missdist > 4) 00496 return (SEL *)ERROR_PTR("dist not in {0 .. 4}", procName, NULL); 00497 if (hitskip < 0 && missskip < 0) 00498 return (SEL *)ERROR_PTR("no hits or misses", procName, NULL); 00499 00500 /* Locate the foreground */ 00501 pixClipToForeground(pixs, &pixt1, NULL); 00502 if (!pixt1) 00503 return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL); 00504 ws = pixGetWidth(pixt1); 00505 hs = pixGetHeight(pixt1); 00506 w = ws; 00507 h = hs; 00508 00509 /* Crop out a region including the foreground, and add pixels 00510 * on sides depending on the side flags */ 00511 if (topflag || botflag || leftflag || rightflag) { 00512 x = y = 0; 00513 if (topflag) { 00514 h += missdist + 1; 00515 y = missdist + 1; 00516 } 00517 if (botflag) 00518 h += missdist + 1; 00519 if (leftflag) { 00520 w += missdist + 1; 00521 x = missdist + 1; 00522 } 00523 if (rightflag) 00524 w += missdist + 1; 00525 pixt2 = pixCreate(w, h, 1); 00526 pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0); 00527 } 00528 else { 00529 pixt2 = pixClone(pixt1); 00530 } 00531 if (ppixe) 00532 *ppixe = pixClone(pixt2); 00533 pixDestroy(&pixt1); 00534 00535 /* Identify fg and bg pixels that are exactly hitdist and 00536 * missdist (rsp) away from the boundary pixels in their set. 00537 * Then get a subsampled set of these points. */ 00538 sel_3 = selCreateBrick(3, 3, 1, 1, SEL_HIT); 00539 if (hitskip >= 0) { 00540 selh = selCreateBrick(2 * hitdist + 1, 2 * hitdist + 1, 00541 hitdist, hitdist, SEL_HIT); 00542 pixt3 = pixErode(NULL, pixt2, selh); 00543 pixfg = pixErode(NULL, pixt3, sel_3); 00544 pixXor(pixfg, pixfg, pixt3); 00545 ptah = pixSubsampleBoundaryPixels(pixfg, hitskip); 00546 pixDestroy(&pixt3); 00547 pixDestroy(&pixfg); 00548 selDestroy(&selh); 00549 } 00550 if (missskip >= 0) { 00551 selm = selCreateBrick(2 * missdist + 1, 2 * missdist + 1, 00552 missdist, missdist, SEL_HIT); 00553 pixt3 = pixDilate(NULL, pixt2, selm); 00554 pixbg = pixDilate(NULL, pixt3, sel_3); 00555 pixXor(pixbg, pixbg, pixt3); 00556 ptam = pixSubsampleBoundaryPixels(pixbg, missskip); 00557 pixDestroy(&pixt3); 00558 pixDestroy(&pixbg); 00559 selDestroy(&selm); 00560 } 00561 selDestroy(&sel_3); 00562 pixDestroy(&pixt2); 00563 00564 /* Generate the hit-miss sel from these point */ 00565 sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE); 00566 if (hitskip >= 0) { 00567 npt = ptaGetCount(ptah); 00568 for (i = 0; i < npt; i++) { 00569 ptaGetIPt(ptah, i, &ix, &iy); 00570 selSetElement(sel, iy, ix, SEL_HIT); 00571 } 00572 } 00573 if (missskip >= 0) { 00574 npt = ptaGetCount(ptam); 00575 for (i = 0; i < npt; i++) { 00576 ptaGetIPt(ptam, i, &ix, &iy); 00577 selSetElement(sel, iy, ix, SEL_MISS); 00578 } 00579 } 00580 00581 ptaDestroy(&ptah); 00582 ptaDestroy(&ptam); 00583 return sel; 00584 } 00585 00586 00587 /*-----------------------------------------------------------------* 00588 * Accumulate data on runs along lines * 00589 *-----------------------------------------------------------------*/ 00590 /*! 00591 * pixGetRunCentersOnLine() 00592 * 00593 * Input: pixs (1 bpp) 00594 * x, y (set one of these to -1; see notes) 00595 * minlength (minimum length of acceptable run) 00596 * Return: numa of fg runs, or null on error 00597 * 00598 * Notes: 00599 * (1) Action: this function computes the fg (black) and bg (white) 00600 * pixel runlengths along the specified horizontal or vertical line, 00601 * and returns a Numa of the "center" pixels of each fg run 00602 * whose length equals or exceeds the minimum length. 00603 * (2) This only works on horizontal and vertical lines. 00604 * (3) For horizontal runs, set x = -1 and y to the value 00605 * for all points along the raster line. For vertical runs, 00606 * set y = -1 and x to the value for all points along the 00607 * pixel column. 00608 * (4) For horizontal runs, the points in the Numa are the x 00609 * values in the center of fg runs that are of length at 00610 * least 'minlength'. For vertical runs, the points in the 00611 * Numa are the y values in the center of fg runs, again 00612 * of length 'minlength' or greater. 00613 * (5) If there are no fg runs along the line that satisfy the 00614 * minlength constraint, the returned Numa is empty. This 00615 * is not an error. 00616 */ 00617 NUMA * 00618 pixGetRunCentersOnLine(PIX *pixs, 00619 l_int32 x, 00620 l_int32 y, 00621 l_int32 minlength) 00622 { 00623 l_int32 w, h, i, r, nruns, len; 00624 NUMA *naruns, *nad; 00625 00626 PROCNAME("pixGetRunCentersOnLine"); 00627 00628 if (!pixs) 00629 return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); 00630 if (pixGetDepth(pixs) != 1) 00631 return (NUMA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); 00632 if (x != -1 && y != -1) 00633 return (NUMA *)ERROR_PTR("x or y must be -1", procName, NULL); 00634 if (x == -1 && y == -1) 00635 return (NUMA *)ERROR_PTR("x or y cannot both be -1", procName, NULL); 00636 00637 if ((nad = numaCreate(0)) == NULL) 00638 return (NUMA *)ERROR_PTR("nad not made", procName, NULL); 00639 w = pixGetWidth(pixs); 00640 h = pixGetHeight(pixs); 00641 if (x == -1) { /* horizontal run */ 00642 if (y < 0 || y >= h) 00643 return nad; 00644 naruns = pixGetRunsOnLine(pixs, 0, y, w - 1, y); 00645 } 00646 else { /* vertical run */ 00647 if (x < 0 || x >= w) 00648 return nad; 00649 naruns = pixGetRunsOnLine(pixs, x, 0, x, h - 1); 00650 } 00651 nruns = numaGetCount(naruns); 00652 00653 /* extract run center values; the first run is always bg */ 00654 r = 0; /* cumulative distance along line */ 00655 for (i = 0; i < nruns; i++) { 00656 if (i % 2 == 0) { /* bg run */ 00657 numaGetIValue(naruns, i, &len); 00658 r += len; 00659 continue; 00660 } 00661 else { 00662 numaGetIValue(naruns, i, &len); 00663 if (len >= minlength) 00664 numaAddNumber(nad, r + len / 2); 00665 r += len; 00666 } 00667 } 00668 00669 numaDestroy(&naruns); 00670 return nad; 00671 } 00672 00673 00674 /*! 00675 * pixGetRunsOnLine() 00676 * 00677 * Input: pixs (1 bpp) 00678 * x1, y1, x2, y2 00679 * Return: numa, or null on error 00680 * 00681 * Notes: 00682 * (1) Action: this function uses the bresenham algorithm to compute 00683 * the pixels along the specified line. It returns a Numa of the 00684 * runlengths of the fg (black) and bg (white) runs, always 00685 * starting with a white run. 00686 * (2) If the first pixel on the line is black, the length of the 00687 * first returned run (which is white) is 0. 00688 */ 00689 NUMA * 00690 pixGetRunsOnLine(PIX *pixs, 00691 l_int32 x1, 00692 l_int32 y1, 00693 l_int32 x2, 00694 l_int32 y2) 00695 { 00696 l_int32 w, h, x, y, npts; 00697 l_int32 i, runlen, preval; 00698 l_uint32 val; 00699 NUMA *numa; 00700 PTA *pta; 00701 00702 PROCNAME("pixGetRunsOnLine"); 00703 00704 if (!pixs) 00705 return (NUMA *)ERROR_PTR("pixs not defined", procName, NULL); 00706 if (pixGetDepth(pixs) != 1) 00707 return (NUMA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); 00708 00709 w = pixGetWidth(pixs); 00710 h = pixGetHeight(pixs); 00711 if (x1 < 0 || x1 >= w) 00712 return (NUMA *)ERROR_PTR("x1 not valid", procName, NULL); 00713 if (x2 < 0 || x2 >= w) 00714 return (NUMA *)ERROR_PTR("x2 not valid", procName, NULL); 00715 if (y1 < 0 || y1 >= h) 00716 return (NUMA *)ERROR_PTR("y1 not valid", procName, NULL); 00717 if (y2 < 0 || y2 >= h) 00718 return (NUMA *)ERROR_PTR("y2 not valid", procName, NULL); 00719 00720 if ((pta = generatePtaLine(x1, y1, x2, y2)) == NULL) 00721 return (NUMA *)ERROR_PTR("pta not made", procName, NULL); 00722 if ((npts = ptaGetCount(pta)) == 0) 00723 return (NUMA *)ERROR_PTR("pta has no pts", procName, NULL); 00724 00725 if ((numa = numaCreate(0)) == NULL) 00726 return (NUMA *)ERROR_PTR("numa not made", procName, NULL); 00727 00728 for (i = 0; i < npts; i++) { 00729 ptaGetIPt(pta, i, &x, &y); 00730 pixGetPixel(pixs, x, y, &val); 00731 if (i == 0) { 00732 if (val == 1) { /* black pixel; append white run of size 0 */ 00733 numaAddNumber(numa, 0); 00734 } 00735 preval = val; 00736 runlen = 1; 00737 continue; 00738 } 00739 if (val == preval) { /* extend current run */ 00740 preval = val; 00741 runlen++; 00742 } 00743 else { /* end previous run */ 00744 numaAddNumber(numa, runlen); 00745 preval = val; 00746 runlen = 1; 00747 } 00748 } 00749 numaAddNumber(numa, runlen); /* append last run */ 00750 00751 ptaDestroy(&pta); 00752 return numa; 00753 } 00754 00755 00756 /*-----------------------------------------------------------------* 00757 * Subsample boundary pixels in relatively ordered way * 00758 *-----------------------------------------------------------------*/ 00759 /*! 00760 * pixSubsampleBoundaryPixels() 00761 * 00762 * Input: pixs (1 bpp, with only boundary pixels in fg) 00763 * skip (number to skip between samples as you traverse boundary) 00764 * Return: pta, or null on error 00765 * 00766 * Notes: 00767 * (1) If skip = 0, we take all the fg pixels. 00768 * (2) We try to traverse the boundaries in a regular way. 00769 * Some pixels may be missed, and these are then subsampled 00770 * randomly with a fraction determined by 'skip'. 00771 * (3) The most natural approach is to use a depth first (stack-based) 00772 * method to find the fg pixels. However, the pixel runs are 00773 * 4-connected and there are relatively few branches. So 00774 * instead of doing a proper depth-first search, we get nearly 00775 * the same result using two nested while loops: the outer 00776 * one continues a raster-based search for the next fg pixel, 00777 * and the inner one does a reasonable job running along 00778 * each 4-connected coutour. 00779 */ 00780 PTA * 00781 pixSubsampleBoundaryPixels(PIX *pixs, 00782 l_int32 skip) 00783 { 00784 l_int32 x, y, xn, yn, xs, ys, xa, ya, count; 00785 PIX *pixt; 00786 PTA *pta; 00787 00788 PROCNAME("pixSubsampleBoundaryPixels"); 00789 00790 if (!pixs) 00791 return (PTA *)ERROR_PTR("pixs not defined", procName, NULL); 00792 if (pixGetDepth(pixs) != 1) 00793 return (PTA *)ERROR_PTR("pixs not 1 bpp", procName, NULL); 00794 if (skip < 0) 00795 return (PTA *)ERROR_PTR("skip < 0", procName, NULL); 00796 00797 if (skip == 0) 00798 return ptaGetPixelsFromPix(pixs, NULL); 00799 00800 pta = ptaCreate(0); 00801 pixt = pixCopy(NULL, pixs); 00802 xs = ys = 0; 00803 while (nextOnPixelInRaster(pixt, xs, ys, &xn, &yn)) { /* new series */ 00804 xs = xn; 00805 ys = yn; 00806 00807 /* Add first point in this series */ 00808 ptaAddPt(pta, xs, ys); 00809 00810 /* Trace out boundary, erasing all and saving every (skip + 1)th */ 00811 x = xs; 00812 y = ys; 00813 pixSetPixel(pixt, x, y, 0); 00814 count = 0; 00815 while (adjacentOnPixelInRaster(pixt, x, y, &xa, &ya)) { 00816 x = xa; 00817 y = ya; 00818 pixSetPixel(pixt, x, y, 0); 00819 if (count == skip) { 00820 ptaAddPt(pta, x, y); 00821 count = 0; 00822 } 00823 else { 00824 count++; 00825 } 00826 } 00827 } 00828 00829 pixDestroy(&pixt); 00830 return pta; 00831 } 00832 00833 00834 /*! 00835 * adjacentOnPixelInRaster() 00836 * 00837 * Input: pixs (1 bpp) 00838 * x, y (current pixel) 00839 * xa, ya (adjacent ON pixel, found by simple CCW search) 00840 * Return: 1 if a pixel is found; 0 otherwise or on error 00841 * 00842 * Notes: 00843 * (1) Search is in 4-connected directions first; then on diagonals. 00844 * This allows traversal along a 4-connected boundary. 00845 */ 00846 l_int32 00847 adjacentOnPixelInRaster(PIX *pixs, 00848 l_int32 x, 00849 l_int32 y, 00850 l_int32 *pxa, 00851 l_int32 *pya) 00852 { 00853 l_int32 w, h, i, xa, ya, found; 00854 l_int32 xdel[] = {-1, 0, 1, 0, -1, 1, 1, -1}; 00855 l_int32 ydel[] = {0, 1, 0, -1, 1, 1, -1, -1}; 00856 l_uint32 val; 00857 00858 PROCNAME("adjacentOnPixelInRaster"); 00859 00860 if (!pixs) 00861 return ERROR_INT("pixs not defined", procName, 0); 00862 if (pixGetDepth(pixs) != 1) 00863 return ERROR_INT("pixs not 1 bpp", procName, 0); 00864 w = pixGetWidth(pixs); 00865 h = pixGetHeight(pixs); 00866 found = 0; 00867 for (i = 0; i < 8; i++) { 00868 xa = x + xdel[i]; 00869 ya = y + ydel[i]; 00870 if (xa < 0 || xa >= w || ya < 0 || ya >= h) 00871 continue; 00872 pixGetPixel(pixs, xa, ya, &val); 00873 if (val == 1) { 00874 found = 1; 00875 *pxa = xa; 00876 *pya = ya; 00877 break; 00878 } 00879 } 00880 return found; 00881 } 00882 00883 00884 00885 /*-----------------------------------------------------------------* 00886 * Display generated sel with originating image * 00887 *-----------------------------------------------------------------*/ 00888 /*! 00889 * pixDisplayHitMissSel() 00890 * 00891 * Input: pixs (1 bpp) 00892 * sel (hit-miss in general) 00893 * scalefactor (an integer >= 1; use 0 for default) 00894 * hitcolor (RGB0 color for center of hit pixels) 00895 * misscolor (RGB0 color for center of miss pixels) 00896 * Return: pixd (RGB showing both pixs and sel), or null on error 00897 * Notes: 00898 * (1) We don't allow scalefactor to be larger than MAX_SEL_SCALEFACTOR 00899 * (2) The colors are conveniently given as 4 bytes in hex format, 00900 * such as 0xff008800. The least significant byte is ignored. 00901 */ 00902 PIX * 00903 pixDisplayHitMissSel(PIX *pixs, 00904 SEL *sel, 00905 l_int32 scalefactor, 00906 l_uint32 hitcolor, 00907 l_uint32 misscolor) 00908 { 00909 l_int32 i, j, type; 00910 l_float32 fscale; 00911 PIX *pixt, *pixd; 00912 PIXCMAP *cmap; 00913 00914 PROCNAME("pixDisplayHitMissSel"); 00915 00916 if (!pixs) 00917 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00918 if (pixGetDepth(pixs) != 1) 00919 return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL); 00920 if (!sel) 00921 return (PIX *)ERROR_PTR("sel not defined", procName, NULL); 00922 00923 if (scalefactor <= 0) 00924 scalefactor = DEFAULT_SEL_SCALEFACTOR; 00925 if (scalefactor > MAX_SEL_SCALEFACTOR) { 00926 L_WARNING("scalefactor too large; using max value", procName); 00927 scalefactor = MAX_SEL_SCALEFACTOR; 00928 } 00929 00930 /* Generate a version of pixs with a colormap */ 00931 pixt = pixConvert1To8(NULL, pixs, 0, 1); 00932 cmap = pixcmapCreate(8); 00933 pixcmapAddColor(cmap, 255, 255, 255); 00934 pixcmapAddColor(cmap, 0, 0, 0); 00935 pixcmapAddColor(cmap, hitcolor >> 24, (hitcolor >> 16) & 0xff, 00936 (hitcolor >> 8) & 0xff); 00937 pixcmapAddColor(cmap, misscolor >> 24, (misscolor >> 16) & 0xff, 00938 (misscolor >> 8) & 0xff); 00939 pixSetColormap(pixt, cmap); 00940 00941 /* Color the hits and misses */ 00942 for (i = 0; i < sel->sy; i++) { 00943 for (j = 0; j < sel->sx; j++) { 00944 selGetElement(sel, i, j, &type); 00945 if (type == SEL_DONT_CARE) 00946 continue; 00947 if (type == SEL_HIT) 00948 pixSetPixel(pixt, j, i, 2); 00949 else /* type == SEL_MISS */ 00950 pixSetPixel(pixt, j, i, 3); 00951 } 00952 } 00953 00954 /* Scale it up */ 00955 fscale = (l_float32)scalefactor; 00956 pixd = pixScaleBySampling(pixt, fscale, fscale); 00957 00958 pixDestroy(&pixt); 00959 return pixd; 00960 } 00961