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 /* 00019 * sel1.c 00020 * 00021 * Basic ops on Sels and Selas 00022 * 00023 * Create/destroy/copy: 00024 * SELA *selaCreate() 00025 * void selaDestroy() 00026 * SEL *selCreate() 00027 * void selDestroy() 00028 * SEL *selCopy() 00029 * SEL *selCreateBrick() 00030 * SEL *selCreateComb() 00031 * 00032 * Helper proc: 00033 * l_int32 **create2dIntArray() 00034 * 00035 * Extension of sela: 00036 * SELA *selaAddSel() 00037 * l_int32 selaExtendArray() 00038 * 00039 * Accessors: 00040 * l_int32 selaGetCount() 00041 * SEL *selaGetSel() 00042 * char *selGetName() 00043 * l_int32 selSetName() 00044 * l_int32 selaFindSelByName() 00045 * l_int32 selGetElement() 00046 * l_int32 selSetElement() 00047 * l_int32 selGetParameters() 00048 * l_int32 selSetOrigin() 00049 * l_int32 selGetTypeAtOrigin() 00050 * char *selaGetBrickName() 00051 * char *selaGetCombName() 00052 * static char *selaComputeCompositeParameters() 00053 * l_int32 getCompositeParameters() 00054 * SARRAY *selaGetSelnames() 00055 * 00056 * Max translations for erosion and hmt 00057 * l_int32 selFindMaxTranslations() 00058 * 00059 * Rotation by multiples of 90 degrees 00060 * SEL *selRotateOrth() 00061 * 00062 * Sela and Sel serialized I/O 00063 * SELA *selaRead() 00064 * SELA *selaReadStream() 00065 * SEL *selRead() 00066 * SEL *selReadStream() 00067 * l_int32 selaWrite() 00068 * l_int32 selaWriteStream() 00069 * l_int32 selWrite() 00070 * l_int32 selWriteStream() 00071 * 00072 * Building custom hit-miss sels from compiled strings 00073 * SEL *selCreateFromString() 00074 * char *selPrintToString() [for debugging] 00075 * 00076 * Building custom hit-miss sels from a simple file format 00077 * SELA *selaCreateFromFile() 00078 * static SEL *selCreateFromSArray() 00079 * 00080 * Making hit-only sels from Pta and Pix 00081 * SEL *selCreateFromPta() 00082 * SEL *selCreateFromPix() 00083 * 00084 * Making hit-miss sels from Pix and image files 00085 * SEL *selReadFromColorImage() 00086 * SEL *selCreateFromColorPix() 00087 * 00088 * Printable display of sel 00089 * PIX *selDisplayInPix() 00090 * PIX *selaDisplayInPix() 00091 * 00092 * Usage notes: 00093 * In this file we have seven functions that make sels: 00094 * (1) selCreate(), with input (h, w, [name]) 00095 * The generic function. Roll your own, using selSetElement(). 00096 * (2) selCreateBrick(), with input (h, w, cy, cx, val) 00097 * The most popular function. Makes a rectangular sel of 00098 * all hits, misses or don't-cares. We have many morphology 00099 * operations that create a sel of all hits, use it, and 00100 * destroy it. 00101 * (3) selCreateFromString() with input (text, h, w, [name]) 00102 * Adam Langley's clever function, allows you to make a hit-miss 00103 * sel from a string in code that is geometrically laid out 00104 * just like the actual sel. 00105 * (4) selaCreateFromFile() with input (filename) 00106 * This parses a simple file format to create an array of 00107 * hit-miss sels. The sel data uses the same encoding 00108 * as in (3), with geometrical layout enforced. 00109 * (5) selCreateFromPta() with input (pta, cy, cx, [name]) 00110 * Another way to make a sel with only hits. 00111 * (6) selCreateFromPix() with input (pix, cy, cx, [name]) 00112 * Yet another way to make a sel from hits. 00113 * (7) selCreateFromColorPix() with input (pix, name). 00114 * Another way to make a general hit-miss sel, starting with 00115 * an image editor. 00116 * In addition, there are three functions in selgen.c that 00117 * automatically generate a hit-miss sel from a pix and 00118 * a number of parameters. This is useful for problems like 00119 * "find all patterns that look like this one." 00120 * 00121 * Consistency, being the hobgoblin of small minds, 00122 * is adhered to here in the dimensioning and accessing of sels. 00123 * Everything is done in standard matrix (row, column) order. 00124 * When we set specific elements in a sel, we likewise use 00125 * (row, col) ordering: 00126 * selSetElement(), with input (row, col, type) 00127 */ 00128 00129 #include <string.h> 00130 #include "allheaders.h" 00131 00132 /* MS VC++ can't handle array initialization with static consts ! */ 00133 #define L_BUF_SIZE 256 00134 00135 static const l_int32 INITIAL_PTR_ARRAYSIZE = 50; /* n'import quoi */ 00136 static const l_int32 MANY_SELS = 1000; 00137 00138 static SEL *selCreateFromSArray(SARRAY *sa, l_int32 first, l_int32 last); 00139 00140 struct CompParameterMap 00141 { 00142 l_int32 size; 00143 l_int32 size1; 00144 l_int32 size2; 00145 char selnameh1[20]; 00146 char selnameh2[20]; 00147 char selnamev1[20]; 00148 char selnamev2[20]; 00149 }; 00150 00151 static const struct CompParameterMap comp_parameter_map[] = 00152 { { 2, 2, 1, "sel_2h", "", "sel_2v", "" }, 00153 { 3, 3, 1, "sel_3h", "", "sel_3v", "" }, 00154 { 4, 2, 2, "sel_2h", "sel_comb_4h", "sel_2v", "sel_comb_4v" }, 00155 { 5, 5, 1, "sel_5h", "", "sel_5v", "" }, 00156 { 6, 3, 2, "sel_3h", "sel_comb_6h", "sel_3v", "sel_comb_6v" }, 00157 { 7, 7, 1, "sel_7h", "", "sel_7v", "" }, 00158 { 8, 4, 2, "sel_4h", "sel_comb_8h", "sel_4v", "sel_comb_8v" }, 00159 { 9, 3, 3, "sel_3h", "sel_comb_9h", "sel_3v", "sel_comb_9v" }, 00160 { 10, 5, 2, "sel_5h", "sel_comb_10h", "sel_5v", "sel_comb_10v" }, 00161 { 11, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" }, 00162 { 12, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" }, 00163 { 13, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" }, 00164 { 14, 7, 2, "sel_7h", "sel_comb_14h", "sel_7v", "sel_comb_14v" }, 00165 { 15, 5, 3, "sel_5h", "sel_comb_15h", "sel_5v", "sel_comb_15v" }, 00166 { 16, 4, 4, "sel_4h", "sel_comb_16h", "sel_4v", "sel_comb_16v" }, 00167 { 17, 4, 4, "sel_4h", "sel_comb_16h", "sel_4v", "sel_comb_16v" }, 00168 { 18, 6, 3, "sel_6h", "sel_comb_18h", "sel_6v", "sel_comb_18v" }, 00169 { 19, 5, 4, "sel_5h", "sel_comb_20h", "sel_5v", "sel_comb_20v" }, 00170 { 20, 5, 4, "sel_5h", "sel_comb_20h", "sel_5v", "sel_comb_20v" }, 00171 { 21, 7, 3, "sel_7h", "sel_comb_21h", "sel_7v", "sel_comb_21v" }, 00172 { 22, 11, 2, "sel_11h", "sel_comb_22h", "sel_11v", "sel_comb_22v" }, 00173 { 23, 6, 4, "sel_6h", "sel_comb_24h", "sel_6v", "sel_comb_24v" }, 00174 { 24, 6, 4, "sel_6h", "sel_comb_24h", "sel_6v", "sel_comb_24v" }, 00175 { 25, 5, 5, "sel_5h", "sel_comb_25h", "sel_5v", "sel_comb_25v" }, 00176 { 26, 5, 5, "sel_5h", "sel_comb_25h", "sel_5v", "sel_comb_25v" }, 00177 { 27, 9, 3, "sel_9h", "sel_comb_27h", "sel_9v", "sel_comb_27v" }, 00178 { 28, 7, 4, "sel_7h", "sel_comb_28h", "sel_7v", "sel_comb_28v" }, 00179 { 29, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" }, 00180 { 30, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" }, 00181 { 31, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" }, 00182 { 32, 8, 4, "sel_8h", "sel_comb_32h", "sel_8v", "sel_comb_32v" }, 00183 { 33, 11, 3, "sel_11h", "sel_comb_33h", "sel_11v", "sel_comb_33v" }, 00184 { 34, 7, 5, "sel_7h", "sel_comb_35h", "sel_7v", "sel_comb_35v" }, 00185 { 35, 7, 5, "sel_7h", "sel_comb_35h", "sel_7v", "sel_comb_35v" }, 00186 { 36, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" }, 00187 { 37, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" }, 00188 { 38, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" }, 00189 { 39, 13, 3, "sel_13h", "sel_comb_39h", "sel_13v", "sel_comb_39v" }, 00190 { 40, 8, 5, "sel_8h", "sel_comb_40h", "sel_8v", "sel_comb_40v" }, 00191 { 41, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" }, 00192 { 42, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" }, 00193 { 43, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" }, 00194 { 44, 11, 4, "sel_11h", "sel_comb_44h", "sel_11v", "sel_comb_44v" }, 00195 { 45, 9, 5, "sel_9h", "sel_comb_45h", "sel_9v", "sel_comb_45v" }, 00196 { 46, 9, 5, "sel_9h", "sel_comb_45h", "sel_9v", "sel_comb_45v" }, 00197 { 47, 8, 6, "sel_8h", "sel_comb_48h", "sel_8v", "sel_comb_48v" }, 00198 { 48, 8, 6, "sel_8h", "sel_comb_48h", "sel_8v", "sel_comb_48v" }, 00199 { 49, 7, 7, "sel_7h", "sel_comb_49h", "sel_7v", "sel_comb_49v" }, 00200 { 50, 10, 5, "sel_10h", "sel_comb_50h", "sel_10v", "sel_comb_50v" }, 00201 { 51, 10, 5, "sel_10h", "sel_comb_50h", "sel_10v", "sel_comb_50v" }, 00202 { 52, 13, 4, "sel_13h", "sel_comb_52h", "sel_13v", "sel_comb_52v" }, 00203 { 53, 9, 6, "sel_9h", "sel_comb_54h", "sel_9v", "sel_comb_54v" }, 00204 { 54, 9, 6, "sel_9h", "sel_comb_54h", "sel_9v", "sel_comb_54v" }, 00205 { 55, 11, 5, "sel_11h", "sel_comb_55h", "sel_11v", "sel_comb_55v" }, 00206 { 56, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" }, 00207 { 57, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" }, 00208 { 58, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" }, 00209 { 59, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" }, 00210 { 60, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" }, 00211 { 61, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" }, 00212 { 62, 9, 7, "sel_9h", "sel_comb_63h", "sel_9v", "sel_comb_63v" }, 00213 { 63, 9, 7, "sel_9h", "sel_comb_63h", "sel_9v", "sel_comb_63v" } }; 00214 00215 00216 00217 /*------------------------------------------------------------------------* 00218 * Create / Destroy / Copy * 00219 *------------------------------------------------------------------------*/ 00220 /*! 00221 * selaCreate() 00222 * 00223 * Input: n (initial number of sel ptrs; use 0 for default) 00224 * Return: sela, or null on error 00225 */ 00226 SELA * 00227 selaCreate(l_int32 n) 00228 { 00229 SELA *sela; 00230 00231 PROCNAME("selaCreate"); 00232 00233 if (n <= 0) 00234 n = INITIAL_PTR_ARRAYSIZE; 00235 if (n > MANY_SELS) 00236 L_WARNING_INT("%d sels", procName, n); 00237 00238 if ((sela = (SELA *)CALLOC(1, sizeof(SELA))) == NULL) 00239 return (SELA *)ERROR_PTR("sela not made", procName, NULL); 00240 00241 sela->nalloc = n; 00242 sela->n = 0; 00243 00244 /* make array of se ptrs */ 00245 if ((sela->sel = (SEL **)CALLOC(n, sizeof(SEL *))) == NULL) 00246 return (SELA *)ERROR_PTR("sel ptrs not made", procName, NULL); 00247 00248 return sela; 00249 } 00250 00251 00252 /*! 00253 * selaDestroy() 00254 * 00255 * Input: &sela (<to be nulled>) 00256 * Return: void 00257 */ 00258 void 00259 selaDestroy(SELA **psela) 00260 { 00261 SELA *sela; 00262 l_int32 i; 00263 00264 if (!psela) return; 00265 if ((sela = *psela) == NULL) 00266 return; 00267 00268 for (i = 0; i < sela->n; i++) 00269 selDestroy(&sela->sel[i]); 00270 FREE(sela->sel); 00271 FREE(sela); 00272 *psela = NULL; 00273 return; 00274 } 00275 00276 00277 /*! 00278 * selCreate() 00279 * 00280 * Input: height, width 00281 * name (<optional> sel name; can be null) 00282 * Return: sel, or null on error 00283 * 00284 * Notes: 00285 * (1) selCreate() initializes all values to 0. 00286 * (2) After this call, (cy,cx) and nonzero data values must be 00287 * assigned. If a text name is not assigned here, it will 00288 * be needed later when the sel is put into a sela. 00289 */ 00290 SEL * 00291 selCreate(l_int32 height, 00292 l_int32 width, 00293 const char *name) 00294 { 00295 SEL *sel; 00296 00297 PROCNAME("selCreate"); 00298 00299 if ((sel = (SEL *)CALLOC(1, sizeof(SEL))) == NULL) 00300 return (SEL *)ERROR_PTR("sel not made", procName, NULL); 00301 if (name) 00302 sel->name = stringNew(name); 00303 sel->sy = height; 00304 sel->sx = width; 00305 if ((sel->data = create2dIntArray(height, width)) == NULL) 00306 return (SEL *)ERROR_PTR("data not allocated", procName, NULL); 00307 00308 return sel; 00309 } 00310 00311 00312 /*! 00313 * selDestroy() 00314 * 00315 * Input: &sel (<to be nulled>) 00316 * Return: void 00317 */ 00318 void 00319 selDestroy(SEL **psel) 00320 { 00321 l_int32 i; 00322 SEL *sel; 00323 00324 PROCNAME("selDestroy"); 00325 00326 if (psel == NULL) { 00327 L_WARNING("ptr address is NULL!", procName); 00328 return; 00329 } 00330 if ((sel = *psel) == NULL) 00331 return; 00332 00333 for (i = 0; i < sel->sy; i++) 00334 FREE(sel->data[i]); 00335 FREE(sel->data); 00336 if (sel->name) 00337 FREE(sel->name); 00338 FREE(sel); 00339 00340 *psel = NULL; 00341 return; 00342 } 00343 00344 00345 /*! 00346 * selCopy() 00347 * 00348 * Input: sel 00349 * Return: a copy of the sel, or null on error 00350 */ 00351 SEL * 00352 selCopy(SEL *sel) 00353 { 00354 l_int32 sx, sy, cx, cy, i, j; 00355 SEL *csel; 00356 00357 PROCNAME("selCopy"); 00358 00359 if (!sel) 00360 return (SEL *)ERROR_PTR("sel not defined", procName, NULL); 00361 00362 if ((csel = (SEL *)CALLOC(1, sizeof(SEL))) == NULL) 00363 return (SEL *)ERROR_PTR("csel not made", procName, NULL); 00364 selGetParameters(sel, &sy, &sx, &cy, &cx); 00365 csel->sy = sy; 00366 csel->sx = sx; 00367 csel->cy = cy; 00368 csel->cx = cx; 00369 00370 if ((csel->data = create2dIntArray(sy, sx)) == NULL) 00371 return (SEL *)ERROR_PTR("sel data not made", procName, NULL); 00372 00373 for (i = 0; i < sy; i++) 00374 for (j = 0; j < sx; j++) 00375 csel->data[i][j] = sel->data[i][j]; 00376 00377 if (sel->name) 00378 csel->name = stringNew(sel->name); 00379 00380 return csel; 00381 } 00382 00383 00384 /*! 00385 * selCreateBrick() 00386 * 00387 * Input: height, width 00388 * cy, cx (origin, relative to UL corner at 0,0) 00389 * type (SEL_HIT, SEL_MISS, or SEL_DONT_CARE) 00390 * Return: sel, or null on error 00391 * 00392 * Notes: 00393 * (1) This is a rectangular sel of all hits, misses or don't cares. 00394 */ 00395 SEL * 00396 selCreateBrick(l_int32 h, 00397 l_int32 w, 00398 l_int32 cy, 00399 l_int32 cx, 00400 l_int32 type) 00401 { 00402 l_int32 i, j; 00403 SEL *sel; 00404 00405 PROCNAME("selCreateBrick"); 00406 00407 if (h <= 0 || w <= 0) 00408 return (SEL *)ERROR_PTR("h and w must both be > 0", procName, NULL); 00409 if (type != SEL_HIT && type != SEL_MISS && type != SEL_DONT_CARE) 00410 return (SEL *)ERROR_PTR("invalid sel element type", procName, NULL); 00411 00412 if ((sel = selCreate(h, w, NULL)) == NULL) 00413 return (SEL *)ERROR_PTR("sel not made", procName, NULL); 00414 selSetOrigin(sel, cy, cx); 00415 for (i = 0; i < h; i++) 00416 for (j = 0; j < w; j++) 00417 sel->data[i][j] = type; 00418 00419 return sel; 00420 } 00421 00422 00423 /*! 00424 * selCreateComb() 00425 * 00426 * Input: factor1 (contiguous space between comb tines) 00427 * factor2 (number of comb tines) 00428 * direction (L_HORIZ, L_VERT) 00429 * Return: sel, or null on error 00430 * 00431 * Notes: 00432 * (1) This generates a comb Sel of hits with the origin as 00433 * near the center as possible. 00434 */ 00435 SEL * 00436 selCreateComb(l_int32 factor1, 00437 l_int32 factor2, 00438 l_int32 direction) 00439 { 00440 l_int32 i, size, z; 00441 SEL *sel; 00442 00443 PROCNAME("selCreateComb"); 00444 00445 if (factor1 < 1 || factor2 < 1) 00446 return (SEL *)ERROR_PTR("factors must be >= 1", procName, NULL); 00447 if (direction != L_HORIZ && direction != L_VERT) 00448 return (SEL *)ERROR_PTR("invalid direction", procName, NULL); 00449 00450 size = factor1 * factor2; 00451 if (direction == L_HORIZ) { 00452 sel = selCreate(1, size, NULL); 00453 selSetOrigin(sel, 0, size / 2); 00454 } 00455 else { 00456 sel = selCreate(size, 1, NULL); 00457 selSetOrigin(sel, size / 2, 0); 00458 } 00459 00460 for (i = 0; i < factor2; i++) { 00461 if (factor2 & 1) /* odd */ 00462 z = factor1 / 2 + i * factor1; 00463 else 00464 z = factor1 / 2 + i * factor1; 00465 /* fprintf(stderr, "i = %d, factor1 = %d, factor2 = %d, z = %d\n", 00466 i, factor1, factor2, z); */ 00467 if (direction == L_HORIZ) 00468 selSetElement(sel, 0, z, SEL_HIT); 00469 else 00470 selSetElement(sel, z, 0, SEL_HIT); 00471 } 00472 00473 return sel; 00474 } 00475 00476 00477 /*! 00478 * create2dIntArray() 00479 * 00480 * Input: sy (rows == height) 00481 * sx (columns == width) 00482 * Return: doubly indexed array (i.e., an array of sy row pointers, 00483 * each of which points to an array of sx ints) 00484 * 00485 * Notes: 00486 * (1) The array[sy][sx] is indexed in standard "matrix notation", 00487 * with the row index first. 00488 */ 00489 l_int32 ** 00490 create2dIntArray(l_int32 sy, 00491 l_int32 sx) 00492 { 00493 l_int32 i; 00494 l_int32 **array; 00495 00496 PROCNAME("create2dIntArray"); 00497 00498 if ((array = (l_int32 **)CALLOC(sy, sizeof(l_int32 *))) == NULL) 00499 return (l_int32 **)ERROR_PTR("ptr array not made", procName, NULL); 00500 00501 for (i = 0; i < sy; i++) { 00502 if ((array[i] = (l_int32 *)CALLOC(sx, sizeof(l_int32))) == NULL) 00503 return (l_int32 **)ERROR_PTR("array not made", procName, NULL); 00504 } 00505 00506 return array; 00507 } 00508 00509 00510 00511 /*------------------------------------------------------------------------* 00512 * Extension of sela * 00513 *------------------------------------------------------------------------*/ 00514 /*! 00515 * selaAddSel() 00516 * 00517 * Input: sela 00518 * sel to be added 00519 * selname (ignored if already defined in sel; 00520 * req'd in sel when added to a sela) 00521 * copyflag (for sel: 0 inserts, 1 copies) 00522 * Return: 0 if OK; 1 on error 00523 * 00524 * Notes: 00525 * (1) This adds a sel, either inserting or making a copy. 00526 * (2) Because every sel in a sela must have a name, it copies 00527 * the input name if necessary. You can input NULL for 00528 * selname if the sel already has a name. 00529 */ 00530 l_int32 00531 selaAddSel(SELA *sela, 00532 SEL *sel, 00533 const char *selname, 00534 l_int32 copyflag) 00535 { 00536 l_int32 n; 00537 SEL *csel; 00538 00539 PROCNAME("selaAddSel"); 00540 00541 if (!sela) 00542 return ERROR_INT("sela not defined", procName, 1); 00543 if (!sel) 00544 return ERROR_INT("sel not defined", procName, 1); 00545 if (!sel->name && !selname) 00546 return ERROR_INT("added sel must have name", procName, 1); 00547 00548 if (copyflag == TRUE) { 00549 if ((csel = selCopy(sel)) == NULL) 00550 return ERROR_INT("csel not made", procName, 1); 00551 } 00552 else /* copyflag is false; insert directly */ 00553 csel = sel; 00554 if (!csel->name) 00555 csel->name = stringNew(selname); 00556 00557 n = selaGetCount(sela); 00558 if (n >= sela->nalloc) 00559 selaExtendArray(sela); 00560 sela->sel[n] = csel; 00561 sela->n++; 00562 00563 return 0; 00564 } 00565 00566 00567 /*! 00568 * selaExtendArray() 00569 * 00570 * Input: sela 00571 * Return: 0 if OK; 1 on error 00572 */ 00573 l_int32 00574 selaExtendArray(SELA *sela) 00575 { 00576 PROCNAME("selaExtendArray"); 00577 00578 if (!sela) 00579 return ERROR_INT("sela not defined", procName, 1); 00580 00581 if ((sela->sel = (SEL **)reallocNew((void **)&sela->sel, 00582 sizeof(SEL *) * sela->nalloc, 00583 2 * sizeof(SEL *) * sela->nalloc)) == NULL) 00584 return ERROR_INT("new ptr array not returned", procName, 1); 00585 00586 sela->nalloc = 2 * sela->nalloc; 00587 return 0; 00588 } 00589 00590 00591 00592 /*----------------------------------------------------------------------* 00593 * Accessors * 00594 *----------------------------------------------------------------------*/ 00595 /*! 00596 * selaGetCount() 00597 * 00598 * Input: sela 00599 * Return: count, or 0 on error 00600 */ 00601 l_int32 00602 selaGetCount(SELA *sela) 00603 { 00604 PROCNAME("selaGetCount"); 00605 00606 if (!sela) 00607 return ERROR_INT("sela not defined", procName, 0); 00608 00609 return sela->n; 00610 } 00611 00612 00613 /*! 00614 * selaGetSel() 00615 * 00616 * Input: sela 00617 * index of sel to be retrieved (not copied) 00618 * Return: sel, or null on error 00619 * 00620 * Notes: 00621 * (1) This returns a ptr to the sel, not a copy, so the caller 00622 * must not destroy it! 00623 */ 00624 SEL * 00625 selaGetSel(SELA *sela, 00626 l_int32 i) 00627 { 00628 PROCNAME("selaGetSel"); 00629 00630 if (!sela) 00631 return (SEL *)ERROR_PTR("sela not defined", procName, NULL); 00632 00633 if (i < 0 || i >= sela->n) 00634 return (SEL *)ERROR_PTR("invalid index", procName, NULL); 00635 return sela->sel[i]; 00636 } 00637 00638 00639 /*! 00640 * selGetName() 00641 * 00642 * Input: sel 00643 * Return: sel name (not copied), or null if no name or on error 00644 */ 00645 char * 00646 selGetName(SEL *sel) 00647 { 00648 PROCNAME("selGetName"); 00649 00650 if (!sel) 00651 return (char *)ERROR_PTR("sel not defined", procName, NULL); 00652 00653 return sel->name; 00654 } 00655 00656 00657 /*! 00658 * selSetName() 00659 * 00660 * Input: sel 00661 * name (<optional>; can be null) 00662 * Return: 0 if OK, 1 on error 00663 * 00664 * Notes: 00665 * (1) Always frees the existing sel name, if defined. 00666 * (2) If name is not defined, just clears any existing sel name. 00667 */ 00668 l_int32 00669 selSetName(SEL *sel, 00670 const char *name) 00671 { 00672 PROCNAME("selSetName"); 00673 00674 if (!sel) 00675 return ERROR_INT("sel not defined", procName, 1); 00676 00677 return stringReplace(&sel->name, name); 00678 } 00679 00680 00681 /*! 00682 * selaFindSelByName() 00683 * 00684 * Input: sela 00685 * sel name 00686 * &index (<optional, return>) 00687 * &sel (<optional, return> sel (not a copy)) 00688 * Return: 0 if OK; 1 on error 00689 */ 00690 l_int32 00691 selaFindSelByName(SELA *sela, 00692 const char *name, 00693 l_int32 *pindex, 00694 SEL **psel) 00695 { 00696 l_int32 i, n; 00697 char *sname; 00698 SEL *sel; 00699 00700 PROCNAME("selaFindSelByName"); 00701 00702 if (pindex) *pindex = -1; 00703 if (psel) *psel = NULL; 00704 00705 if (!sela) 00706 return ERROR_INT("sela not defined", procName, 1); 00707 00708 n = selaGetCount(sela); 00709 for (i = 0; i < n; i++) 00710 { 00711 if ((sel = selaGetSel(sela, i)) == NULL) { 00712 L_WARNING("missing sel", procName); 00713 continue; 00714 } 00715 00716 sname = selGetName(sel); 00717 if (sname && (!strcmp(name, sname))) { 00718 if (pindex) 00719 *pindex = i; 00720 if (psel) 00721 *psel = sel; 00722 return 0; 00723 } 00724 } 00725 00726 return 1; 00727 } 00728 00729 00730 /*! 00731 * selGetElement() 00732 * 00733 * Input: sel 00734 * row 00735 * col 00736 * &type (<return> SEL_HIT, SEL_MISS, SEL_DONT_CARE) 00737 * Return: 0 if OK; 1 on error 00738 */ 00739 l_int32 00740 selGetElement(SEL *sel, 00741 l_int32 row, 00742 l_int32 col, 00743 l_int32 *ptype) 00744 { 00745 PROCNAME("selGetElement"); 00746 00747 if (!ptype) 00748 return ERROR_INT("&type not defined", procName, 1); 00749 *ptype = SEL_DONT_CARE; 00750 if (!sel) 00751 return ERROR_INT("sel not defined", procName, 1); 00752 if (row < 0 || row >= sel->sy) 00753 return ERROR_INT("sel row out of bounds", procName, 1); 00754 if (col < 0 || col >= sel->sx) 00755 return ERROR_INT("sel col out of bounds", procName, 1); 00756 00757 *ptype = sel->data[row][col]; 00758 return 0; 00759 } 00760 00761 00762 /*! 00763 * selSetElement() 00764 * 00765 * Input: sel 00766 * row 00767 * col 00768 * type (SEL_HIT, SEL_MISS, SEL_DONT_CARE) 00769 * Return: 0 if OK; 1 on error 00770 * 00771 * Notes: 00772 * (1) Because we use row and column to index into an array, 00773 * they are always non-negative. The location of the origin 00774 * (and the type of operation) determine the actual 00775 * direction of the rasterop. 00776 */ 00777 l_int32 00778 selSetElement(SEL *sel, 00779 l_int32 row, 00780 l_int32 col, 00781 l_int32 type) 00782 { 00783 PROCNAME("selSetElement"); 00784 00785 if (!sel) 00786 return ERROR_INT("sel not defined", procName, 1); 00787 if (type != SEL_HIT && type != SEL_MISS && type != SEL_DONT_CARE) 00788 return ERROR_INT("invalid sel element type", procName, 1); 00789 if (row < 0 || row >= sel->sy) 00790 return ERROR_INT("sel row out of bounds", procName, 1); 00791 if (col < 0 || col >= sel->sx) 00792 return ERROR_INT("sel col out of bounds", procName, 1); 00793 00794 sel->data[row][col] = type; 00795 return 0; 00796 } 00797 00798 00799 /*! 00800 * selGetParameters() 00801 * 00802 * Input: sel 00803 * &sy, &sx, &cy, &cx (<optional return>; each can be null) 00804 * Return: 0 if OK, 1 on error 00805 */ 00806 l_int32 00807 selGetParameters(SEL *sel, 00808 l_int32 *psy, 00809 l_int32 *psx, 00810 l_int32 *pcy, 00811 l_int32 *pcx) 00812 { 00813 PROCNAME("selGetParameters"); 00814 00815 if (psy) *psy = 0; 00816 if (psx) *psx = 0; 00817 if (pcy) *pcy = 0; 00818 if (pcx) *pcx = 0; 00819 if (!sel) 00820 return ERROR_INT("sel not defined", procName, 1); 00821 if (psy) *psy = sel->sy; 00822 if (psx) *psx = sel->sx; 00823 if (pcy) *pcy = sel->cy; 00824 if (pcx) *pcx = sel->cx; 00825 return 0; 00826 } 00827 00828 00829 /*! 00830 * selSetOrigin() 00831 * 00832 * Input: sel 00833 * cy, cx 00834 * Return: 0 if OK; 1 on error 00835 */ 00836 l_int32 00837 selSetOrigin(SEL *sel, 00838 l_int32 cy, 00839 l_int32 cx) 00840 { 00841 PROCNAME("selSetOrigin"); 00842 00843 if (!sel) 00844 return ERROR_INT("sel not defined", procName, 1); 00845 sel->cy = cy; 00846 sel->cx = cx; 00847 return 0; 00848 } 00849 00850 00851 /*! 00852 * selGetTypeAtOrigin() 00853 * 00854 * Input: sel 00855 * &type (<return> SEL_HIT, SEL_MISS, SEL_DONT_CARE) 00856 * Return: 0 if OK; 1 on error or if origin is not found 00857 */ 00858 l_int32 00859 selGetTypeAtOrigin(SEL *sel, 00860 l_int32 *ptype) 00861 { 00862 l_int32 sx, sy, cx, cy, i, j; 00863 00864 PROCNAME("selGetTypeAtOrigin"); 00865 00866 if (!ptype) 00867 return ERROR_INT("&type not defined", procName, 1); 00868 *ptype = SEL_DONT_CARE; /* init */ 00869 if (!sel) 00870 return ERROR_INT("sel not defined", procName, 1); 00871 00872 selGetParameters(sel, &sy, &sx, &cy, &cx); 00873 for (i = 0; i < sy; i++) { 00874 for (j = 0; j < sx; j++) { 00875 if (i == cy && j == cx) { 00876 selGetElement(sel, i, j, ptype); 00877 return 0; 00878 } 00879 } 00880 } 00881 00882 return ERROR_INT("sel origin not found", procName, 1); 00883 } 00884 00885 00886 /*! 00887 * selaGetBrickName() 00888 * 00889 * Input: sela 00890 * hsize, vsize (of brick sel) 00891 * Return: sel name (new string), or null if no name or on error 00892 */ 00893 char * 00894 selaGetBrickName(SELA *sela, 00895 l_int32 hsize, 00896 l_int32 vsize) 00897 { 00898 l_int32 i, nsels, sx, sy; 00899 SEL *sel; 00900 00901 PROCNAME("selaGetBrickName"); 00902 00903 if (!sela) 00904 return (char *)ERROR_PTR("sela not defined", procName, NULL); 00905 00906 nsels = selaGetCount(sela); 00907 for (i = 0; i < nsels; i++) { 00908 sel = selaGetSel(sela, i); 00909 selGetParameters(sel, &sy, &sx, NULL, NULL); 00910 if (hsize == sx && vsize == sy) 00911 return stringNew(selGetName(sel)); 00912 } 00913 00914 return (char *)ERROR_PTR("sel not found", procName, NULL); 00915 } 00916 00917 00918 /*! 00919 * selaGetCombName() 00920 * 00921 * Input: sela 00922 * size (the product of sizes of the brick and comb parts) 00923 * direction (L_HORIZ, L_VERT) 00924 * Return: sel name (new string), or null if name not found or on error 00925 * 00926 * Notes: 00927 * (1) Combs are by definition 1-dimensional, either horiz or vert. 00928 * (2) Use this with comb Sels; e.g., from selaAddDwaCombs(). 00929 */ 00930 char * 00931 selaGetCombName(SELA *sela, 00932 l_int32 size, 00933 l_int32 direction) 00934 { 00935 char *selname; 00936 char combname[L_BUF_SIZE]; 00937 l_int32 i, nsels, sx, sy, found; 00938 SEL *sel; 00939 00940 PROCNAME("selaGetCombName"); 00941 00942 if (!sela) 00943 return (char *)ERROR_PTR("sela not defined", procName, NULL); 00944 if (direction != L_HORIZ && direction != L_VERT) 00945 return (char *)ERROR_PTR("invalid direction", procName, NULL); 00946 00947 /* Derive the comb name we're looking for */ 00948 if (direction == L_HORIZ) 00949 snprintf(combname, L_BUF_SIZE, "sel_comb_%dh", size); 00950 else /* direction == L_VERT */ 00951 snprintf(combname, L_BUF_SIZE, "sel_comb_%dv", size); 00952 00953 found = FALSE; 00954 nsels = selaGetCount(sela); 00955 for (i = 0; i < nsels; i++) { 00956 sel = selaGetSel(sela, i); 00957 selGetParameters(sel, &sy, &sx, NULL, NULL); 00958 if (sy != 1 && sx != 1) /* 2-D; not a comb */ 00959 continue; 00960 selname = selGetName(sel); 00961 if (!strcmp(selname, combname)) { 00962 found = TRUE; 00963 break; 00964 } 00965 } 00966 00967 if (found) 00968 return stringNew(selname); 00969 else 00970 return (char *)ERROR_PTR("sel not found", procName, NULL); 00971 } 00972 00973 00974 /* --------- Function used to generate code in this file ---------- */ 00975 #if 0 00976 static void selaComputeCompositeParameters(const char *fileout); 00977 00978 /*! 00979 * selaComputeCompParameters() 00980 * 00981 * Input: output filename 00982 * Return: void 00983 * 00984 * Notes: 00985 * (1) This static function was used to construct the comp_parameter_map[] 00986 * array at the top of this file. It is static because it does 00987 * not need to be called again. It remains here to show how 00988 * the composite parameter map was computed. 00989 * (2) The output file was pasted directly into comp_parameter_map[]. 00990 * The composite parameter map is used to quickly determine 00991 * the linear decomposition parameters and sel names. 00992 */ 00993 static void 00994 selaComputeCompositeParameters(const char *fileout) 00995 { 00996 char *str, *nameh1, *nameh2, *namev1, *namev2; 00997 char buf[L_BUF_SIZE]; 00998 l_int32 size, size1, size2, len; 00999 SARRAY *sa; 01000 SELA *selabasic, *selacomb; 01001 01002 selabasic = selaAddBasic(NULL); 01003 selacomb = selaAddDwaCombs(NULL); 01004 sa = sarrayCreate(64); 01005 for (size = 2; size < 64; size++) { 01006 selectComposableSizes(size, &size1, &size2); 01007 nameh1 = selaGetBrickName(selabasic, size1, 1); 01008 namev1 = selaGetBrickName(selabasic, 1, size1); 01009 if (size2 > 1) { 01010 nameh2 = selaGetCombName(selacomb, size1 * size2, L_HORIZ); 01011 namev2 = selaGetCombName(selacomb, size1 * size2, L_VERT); 01012 } 01013 else { 01014 nameh2 = stringNew(""); 01015 namev2 = stringNew(""); 01016 } 01017 snprintf(buf, L_BUF_SIZE, 01018 " { %d, %d, %d, \"%s\", \"%s\", \"%s\", \"%s\" },", 01019 size, size1, size2, nameh1, nameh2, namev1, namev2); 01020 sarrayAddString(sa, buf, L_COPY); 01021 FREE(nameh1); 01022 FREE(nameh2); 01023 FREE(namev1); 01024 FREE(namev2); 01025 } 01026 str = sarrayToString(sa, 1); 01027 len = strlen(str); 01028 l_binaryWrite(fileout, "w", str, len + 1); 01029 FREE(str); 01030 sarrayDestroy(&sa); 01031 selaDestroy(&selabasic); 01032 selaDestroy(&selacomb); 01033 return; 01034 } 01035 #endif 01036 /* -------------------------------------------------------------------- */ 01037 01038 01039 /*! 01040 * getCompositeParameters() 01041 * 01042 * Input: size 01043 * &size1 (<optional return> brick factor size) 01044 * &size2 (<optional return> comb factor size) 01045 * &nameh1 (<optional return> name of horiz brick) 01046 * &nameh2 (<optional return> name of horiz comb) 01047 * &namev1 (<optional return> name of vert brick) 01048 * &namev2 (<optional return> name of vert comb) 01049 * Return: 0 if OK, 1 on error 01050 * 01051 * Notes: 01052 * (1) This uses the big lookup table at the top of this file. 01053 * (2) All returned strings are copies that must be freed. 01054 */ 01055 l_int32 01056 getCompositeParameters(l_int32 size, 01057 l_int32 *psize1, 01058 l_int32 *psize2, 01059 char **pnameh1, 01060 char **pnameh2, 01061 char **pnamev1, 01062 char **pnamev2) 01063 { 01064 l_int32 index; 01065 01066 PROCNAME("selaGetSelnames"); 01067 01068 if (psize1) *psize1 = 0; 01069 if (psize2) *psize2 = 0; 01070 if (pnameh1) *pnameh1 = NULL; 01071 if (pnameh2) *pnameh2 = NULL; 01072 if (pnamev1) *pnamev1 = NULL; 01073 if (pnamev2) *pnamev2 = NULL; 01074 if (size < 2 || size > 63) 01075 return ERROR_INT("valid size range is {2 ... 63}", procName, 1); 01076 index = size - 2; 01077 if (psize1) 01078 *psize1 = comp_parameter_map[index].size1; 01079 if (psize2) 01080 *psize2 = comp_parameter_map[index].size2; 01081 if (pnameh1) 01082 *pnameh1 = stringNew(comp_parameter_map[index].selnameh1); 01083 if (pnameh2) 01084 *pnameh2 = stringNew(comp_parameter_map[index].selnameh2); 01085 if (pnamev1) 01086 *pnamev1 = stringNew(comp_parameter_map[index].selnamev1); 01087 if (pnamev2) 01088 *pnamev2 = stringNew(comp_parameter_map[index].selnamev2); 01089 return 0; 01090 } 01091 01092 01093 /*! 01094 * selaGetSelnames() 01095 * 01096 * Input: sela 01097 * Return: sa (of all sel names), or null on error 01098 */ 01099 SARRAY * 01100 selaGetSelnames(SELA *sela) 01101 { 01102 char *selname; 01103 l_int32 i, n; 01104 SEL *sel; 01105 SARRAY *sa; 01106 01107 PROCNAME("selaGetSelnames"); 01108 01109 if (!sela) 01110 return (SARRAY *)ERROR_PTR("sela not defined", procName, NULL); 01111 if ((n = selaGetCount(sela)) == 0) 01112 return (SARRAY *)ERROR_PTR("no sels in sela", procName, NULL); 01113 01114 if ((sa = sarrayCreate(n)) == NULL) 01115 return (SARRAY *)ERROR_PTR("sa not made", procName, NULL); 01116 for (i = 0; i < n; i++) { 01117 sel = selaGetSel(sela, i); 01118 selname = selGetName(sel); 01119 sarrayAddString(sa, selname, 1); 01120 } 01121 01122 return sa; 01123 } 01124 01125 01126 01127 /*----------------------------------------------------------------------* 01128 * Max translations for erosion and hmt * 01129 *----------------------------------------------------------------------*/ 01130 /*! 01131 * selFindMaxTranslations() 01132 * 01133 * Input: sel 01134 * &xp, &yp, &xn, &yn (<return> max shifts) 01135 * Return: 0 if OK; 1 on error 01136 * 01137 * Note: these are the maximum shifts for the erosion operation. 01138 * For example, when j < cx, the shift of the image 01139 * is +x to the cx. This is a positive xp shift. 01140 */ 01141 l_int32 01142 selFindMaxTranslations(SEL *sel, 01143 l_int32 *pxp, 01144 l_int32 *pyp, 01145 l_int32 *pxn, 01146 l_int32 *pyn) 01147 { 01148 l_int32 sx, sy, cx, cy, i, j; 01149 l_int32 maxxp, maxyp, maxxn, maxyn; 01150 01151 PROCNAME("selaFindMaxTranslations"); 01152 01153 if (!pxp || !pyp || !pxn || !pyn) 01154 return ERROR_INT("&xp (etc) defined", procName, 1); 01155 *pxp = *pyp = *pxn = *pyn = 0; 01156 if (!sel) 01157 return ERROR_INT("sel not defined", procName, 1); 01158 selGetParameters(sel, &sy, &sx, &cy, &cx); 01159 01160 maxxp = maxyp = maxxn = maxyn = 0; 01161 for (i = 0; i < sy; i++) { 01162 for (j = 0; j < sx; j++) { 01163 if (sel->data[i][j] == 1) { 01164 maxxp = L_MAX(maxxp, cx - j); 01165 maxyp = L_MAX(maxyp, cy - i); 01166 maxxn = L_MAX(maxxn, j - cx); 01167 maxyn = L_MAX(maxyn, i - cy); 01168 } 01169 } 01170 } 01171 01172 *pxp = maxxp; 01173 *pyp = maxyp; 01174 *pxn = maxxn; 01175 *pyn = maxyn; 01176 01177 return 0; 01178 } 01179 01180 01181 /*----------------------------------------------------------------------* 01182 * Rotation by multiples of 90 degrees * 01183 *----------------------------------------------------------------------*/ 01184 /*! 01185 * selRotateOrth() 01186 * 01187 * Input: sel 01188 * quads (0 - 4; number of 90 degree cw rotations) 01189 * Return: seld, or null on error 01190 */ 01191 SEL * 01192 selRotateOrth(SEL *sel, 01193 l_int32 quads) 01194 { 01195 l_int32 i, j, ni, nj, sx, sy, cx, cy, nsx, nsy, ncx, ncy, type; 01196 SEL *seld; 01197 01198 PROCNAME("selRotateOrth"); 01199 01200 if (!sel) 01201 return (SEL *)ERROR_PTR("sel not defined", procName, NULL); 01202 if (quads < 0 || quads > 4) 01203 return (SEL *)ERROR_PTR("quads not in {0,1,2,3,4}", procName, NULL); 01204 if (quads == 0 || quads == 4) 01205 return selCopy(sel); 01206 01207 selGetParameters(sel, &sy, &sx, &cy, &cx); 01208 if (quads == 1) { /* 90 degrees cw */ 01209 nsx = sy; 01210 nsy = sx; 01211 ncx = sy - cy - 1; 01212 ncy = cx; 01213 } else if (quads == 2) { /* 180 degrees cw */ 01214 nsx = sx; 01215 nsy = sy; 01216 ncx = sx - cx - 1; 01217 ncy = sy - cy - 1; 01218 } else { /* 270 degrees cw */ 01219 nsx = sy; 01220 nsy = sx; 01221 ncx = cy; 01222 ncy = sx - cx - 1; 01223 } 01224 seld = selCreateBrick(nsy, nsx, ncy, ncx, SEL_DONT_CARE); 01225 if (sel->name) 01226 seld->name = stringNew(sel->name); 01227 01228 for (i = 0; i < sy; i++) { 01229 for (j = 0; j < sx; j++) { 01230 selGetElement(sel, i, j, &type); 01231 if (quads == 1) { 01232 ni = j; 01233 nj = sy - i - 1; 01234 } else if (quads == 2) { 01235 ni = sy - i - 1; 01236 nj = sx - j - 1; 01237 } else { /* quads == 3 */ 01238 ni = sx - j - 1; 01239 nj = i; 01240 } 01241 selSetElement(seld, ni, nj, type); 01242 } 01243 } 01244 01245 return seld; 01246 } 01247 01248 01249 /*----------------------------------------------------------------------* 01250 * Sela and Sel serialized I/O * 01251 *----------------------------------------------------------------------*/ 01252 /*! 01253 * selaRead() 01254 * 01255 * Input: filename 01256 * Return: sela, or null on error 01257 */ 01258 SELA * 01259 selaRead(const char *fname) 01260 { 01261 FILE *fp; 01262 SELA *sela; 01263 01264 PROCNAME("selaRead"); 01265 01266 if (!fname) 01267 return (SELA *)ERROR_PTR("fname not defined", procName, NULL); 01268 01269 if ((fp = fopenReadStream(fname)) == NULL) 01270 return (SELA *)ERROR_PTR("stream not opened", procName, NULL); 01271 if ((sela = selaReadStream(fp)) == NULL) 01272 return (SELA *)ERROR_PTR("sela not returned", procName, NULL); 01273 fclose(fp); 01274 01275 return sela; 01276 } 01277 01278 01279 /*! 01280 * selaReadStream() 01281 * 01282 * Input: stream 01283 * Return: sela, or null on error 01284 */ 01285 SELA * 01286 selaReadStream(FILE *fp) 01287 { 01288 l_int32 i, n, version; 01289 SEL *sel; 01290 SELA *sela; 01291 01292 PROCNAME("selaReadStream"); 01293 01294 if (!fp) 01295 return (SELA *)ERROR_PTR("stream not defined", procName, NULL); 01296 01297 if (fscanf(fp, "\nSela Version %d\n", &version) != 1) 01298 return (SELA *)ERROR_PTR("not a sela file", procName, NULL); 01299 if (version != SEL_VERSION_NUMBER) 01300 return (SELA *)ERROR_PTR("invalid sel version", procName, NULL); 01301 if (fscanf(fp, "Number of Sels = %d\n\n", &n) != 1) 01302 return (SELA *)ERROR_PTR("not a sela file", procName, NULL); 01303 01304 if ((sela = selaCreate(n)) == NULL) 01305 return (SELA *)ERROR_PTR("sela not made", procName, NULL); 01306 sela->nalloc = n; 01307 01308 for (i = 0; i < n; i++) 01309 { 01310 if ((sel = selReadStream(fp)) == NULL) 01311 return (SELA *)ERROR_PTR("sel not made", procName, NULL); 01312 selaAddSel(sela, sel, NULL, 0); 01313 } 01314 01315 return sela; 01316 } 01317 01318 01319 /*! 01320 * selRead() 01321 * 01322 * Input: filename 01323 * Return: sel, or null on error 01324 */ 01325 SEL * 01326 selRead(const char *fname) 01327 { 01328 FILE *fp; 01329 SEL *sel; 01330 01331 PROCNAME("selRead"); 01332 01333 if (!fname) 01334 return (SEL *)ERROR_PTR("fname not defined", procName, NULL); 01335 01336 if ((fp = fopenReadStream(fname)) == NULL) 01337 return (SEL *)ERROR_PTR("stream not opened", procName, NULL); 01338 if ((sel = selReadStream(fp)) == NULL) 01339 return (SEL *)ERROR_PTR("sela not returned", procName, NULL); 01340 fclose(fp); 01341 01342 return sel; 01343 } 01344 01345 01346 /*! 01347 * selReadStream() 01348 * 01349 * Input: stream 01350 * Return: sel, or null on error 01351 */ 01352 SEL * 01353 selReadStream(FILE *fp) 01354 { 01355 char *selname; 01356 char linebuf[L_BUF_SIZE]; 01357 l_int32 sy, sx, cy, cx, i, j, version, ignore; 01358 SEL *sel; 01359 01360 PROCNAME("selReadStream"); 01361 01362 if (!fp) 01363 return (SEL *)ERROR_PTR("stream not defined", procName, NULL); 01364 01365 if (fscanf(fp, " Sel Version %d\n", &version) != 1) 01366 return (SEL *)ERROR_PTR("not a sel file", procName, NULL); 01367 if (version != SEL_VERSION_NUMBER) 01368 return (SEL *)ERROR_PTR("invalid sel version", procName, NULL); 01369 01370 if (fgets(linebuf, L_BUF_SIZE, fp) == NULL) 01371 return (SEL *)ERROR_PTR("error reading into linebuf", procName, NULL); 01372 selname = stringNew(linebuf); 01373 sscanf(linebuf, " ------ %s ------", selname); 01374 01375 if (fscanf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", 01376 &sy, &sx, &cy, &cx) != 4) 01377 return (SEL *)ERROR_PTR("dimensions not read", procName, NULL); 01378 01379 if ((sel = selCreate(sy, sx, selname)) == NULL) 01380 return (SEL *)ERROR_PTR("sel not made", procName, NULL); 01381 selSetOrigin(sel, cy, cx); 01382 01383 for (i = 0; i < sy; i++) { 01384 ignore = fscanf(fp, " "); 01385 for (j = 0; j < sx; j++) 01386 ignore = fscanf(fp, "%1d", &sel->data[i][j]); 01387 ignore = fscanf(fp, "\n"); 01388 } 01389 ignore = fscanf(fp, "\n"); 01390 01391 FREE(selname); 01392 return sel; 01393 } 01394 01395 01396 /*! 01397 * selaWrite() 01398 * 01399 * Input: filename 01400 * sela 01401 * Return: 0 if OK, 1 on error 01402 */ 01403 l_int32 01404 selaWrite(const char *fname, 01405 SELA *sela) 01406 { 01407 FILE *fp; 01408 01409 PROCNAME("selaWrite"); 01410 01411 if (!fname) 01412 return ERROR_INT("fname not defined", procName, 1); 01413 if (!sela) 01414 return ERROR_INT("sela not defined", procName, 1); 01415 01416 if ((fp = fopenWriteStream(fname, "wb")) == NULL) 01417 return ERROR_INT("stream not opened", procName, 1); 01418 selaWriteStream(fp, sela); 01419 fclose(fp); 01420 01421 return 0; 01422 } 01423 01424 01425 /*! 01426 * selaWriteStream() 01427 * 01428 * Input: stream 01429 * sela 01430 * Return: 0 if OK, 1 on error 01431 */ 01432 l_int32 01433 selaWriteStream(FILE *fp, 01434 SELA *sela) 01435 { 01436 l_int32 i, n; 01437 SEL *sel; 01438 01439 PROCNAME("selaWriteStream"); 01440 01441 if (!fp) 01442 return ERROR_INT("stream not defined", procName, 1); 01443 if (!sela) 01444 return ERROR_INT("sela not defined", procName, 1); 01445 01446 n = selaGetCount(sela); 01447 fprintf(fp, "\nSela Version %d\n", SEL_VERSION_NUMBER); 01448 fprintf(fp, "Number of Sels = %d\n\n", n); 01449 for (i = 0; i < n; i++) { 01450 if ((sel = selaGetSel(sela, i)) == NULL) 01451 continue; 01452 selWriteStream(fp, sel); 01453 } 01454 return 0; 01455 } 01456 01457 01458 /*! 01459 * selWrite() 01460 * 01461 * Input: filename 01462 * sel 01463 * Return: 0 if OK, 1 on error 01464 */ 01465 l_int32 01466 selWrite(const char *fname, 01467 SEL *sel) 01468 { 01469 FILE *fp; 01470 01471 PROCNAME("selWrite"); 01472 01473 if (!fname) 01474 return ERROR_INT("fname not defined", procName, 1); 01475 if (!sel) 01476 return ERROR_INT("sel not defined", procName, 1); 01477 01478 if ((fp = fopenWriteStream(fname, "wb")) == NULL) 01479 return ERROR_INT("stream not opened", procName, 1); 01480 selWriteStream(fp, sel); 01481 fclose(fp); 01482 01483 return 0; 01484 } 01485 01486 01487 /*! 01488 * selWriteStream() 01489 * 01490 * Input: stream 01491 * sel 01492 * Return: 0 if OK, 1 on error 01493 */ 01494 l_int32 01495 selWriteStream(FILE *fp, 01496 SEL *sel) 01497 { 01498 l_int32 sx, sy, cx, cy, i, j; 01499 01500 PROCNAME("selWriteStream"); 01501 01502 if (!fp) 01503 return ERROR_INT("stream not defined", procName, 1); 01504 if (!sel) 01505 return ERROR_INT("sel not defined", procName, 1); 01506 selGetParameters(sel, &sy, &sx, &cy, &cx); 01507 01508 fprintf(fp, " Sel Version %d\n", SEL_VERSION_NUMBER); 01509 fprintf(fp, " ------ %s ------\n", selGetName(sel)); 01510 fprintf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", sy, sx, cy, cx); 01511 for (i = 0; i < sy; i++) { 01512 fprintf(fp, " "); 01513 for (j = 0; j < sx; j++) 01514 fprintf(fp, "%d", sel->data[i][j]); 01515 fprintf(fp, "\n"); 01516 } 01517 fprintf(fp, "\n"); 01518 01519 return 0; 01520 } 01521 01522 01523 /*----------------------------------------------------------------------* 01524 * Building custom hit-miss sels from compiled strings * 01525 *----------------------------------------------------------------------*/ 01526 /*! 01527 * selCreateFromString() 01528 * 01529 * Input: text 01530 * height, width 01531 * name (<optional> sel name; can be null) 01532 * Return: sel of the given size, or null on error 01533 * 01534 * Notes: 01535 * (1) The text is an array of chars (in row-major order) where 01536 * each char can be one of the following: 01537 * 'x': hit 01538 * 'o': miss 01539 * ' ': don't-care 01540 * (2) Use an upper case char to indicate the origin of the Sel. 01541 * When the origin falls on a don't-care, use 'C' as the uppecase 01542 * for ' '. 01543 * (3) The text can be input in a format that shows the 2D layout; e.g., 01544 * static const char *seltext = "x " 01545 * "x Oo " 01546 * "x " 01547 * "xxxxx"; 01548 */ 01549 SEL * 01550 selCreateFromString(const char *text, 01551 l_int32 h, 01552 l_int32 w, 01553 const char *name) 01554 { 01555 SEL *sel; 01556 l_int32 y, x; 01557 char ch; 01558 01559 PROCNAME("selCreateFromString"); 01560 01561 if (h < 1) 01562 return (SEL *)ERROR_PTR("height must be > 0", procName, NULL); 01563 if (w < 1) 01564 return (SEL *)ERROR_PTR("width must be > 0", procName, NULL); 01565 01566 sel = selCreate(h, w, name); 01567 01568 for (y = 0; y < h; ++y) { 01569 for (x = 0; x < w; ++x) { 01570 ch = *(text++); 01571 switch (ch) 01572 { 01573 case 'X': 01574 selSetOrigin(sel, y, x); 01575 case 'x': 01576 selSetElement(sel, y, x, SEL_HIT); 01577 break; 01578 01579 case 'O': 01580 selSetOrigin(sel, y, x); 01581 case 'o': 01582 selSetElement(sel, y, x, SEL_MISS); 01583 break; 01584 01585 case 'C': 01586 selSetOrigin(sel, y, x); 01587 case ' ': 01588 selSetElement(sel, y, x, SEL_DONT_CARE); 01589 break; 01590 01591 case '\n': 01592 /* ignored */ 01593 continue; 01594 01595 default: 01596 selDestroy(&sel); 01597 return (SEL *)ERROR_PTR("unknown char", procName, NULL); 01598 } 01599 } 01600 } 01601 01602 return sel; 01603 } 01604 01605 01606 /*! 01607 * selPrintToString() 01608 * 01609 * Input: sel 01610 * Return: str (string; caller must free) 01611 * 01612 * Notes: 01613 * (1) This is an inverse function of selCreateFromString. 01614 * It prints a textual representation of the SEL to a malloc'd 01615 * string. The format is the same as selCreateFromString 01616 * except that newlines are inserted into the output 01617 * between rows. 01618 * (2) This is useful for debugging. However, if you want to 01619 * save some Sels in a file, put them in a Sela and write 01620 * them out with selaWrite(). They can then be read in 01621 * with selaRead(). 01622 */ 01623 char * 01624 selPrintToString(SEL *sel) 01625 { 01626 char is_center; 01627 char *str, *strptr; 01628 l_int32 type; 01629 l_int32 sx, sy, cx, cy, x, y; 01630 01631 PROCNAME("selPrintToString"); 01632 01633 if (!sel) 01634 return (char *)ERROR_PTR("sel not defined", procName, NULL); 01635 01636 selGetParameters(sel, &sy, &sx, &cy, &cx); 01637 if ((str = (char *)CALLOC(1, sy * (sx + 1) + 1)) == NULL) 01638 return (char *)ERROR_PTR("calloc fail for str", procName, NULL); 01639 strptr = str; 01640 01641 for (y = 0; y < sy; ++y) { 01642 for (x = 0; x < sx; ++x) { 01643 selGetElement(sel, y, x, &type); 01644 is_center = (x == cx && y == cy); 01645 switch (type) { 01646 case SEL_HIT: 01647 *(strptr++) = is_center ? 'X' : 'x'; 01648 break; 01649 case SEL_MISS: 01650 *(strptr++) = is_center ? 'O' : 'o'; 01651 break; 01652 case SEL_DONT_CARE: 01653 *(strptr++) = is_center ? 'C' : ' '; 01654 break; 01655 } 01656 } 01657 *(strptr++) = '\n'; 01658 } 01659 01660 return str; 01661 } 01662 01663 01664 /*----------------------------------------------------------------------* 01665 * Building custom hit-miss sels from a simple file format * 01666 *----------------------------------------------------------------------*/ 01667 /*! 01668 * selaCreateFromFile() 01669 * 01670 * Input: filename 01671 * Return: sela, or null on error 01672 * 01673 * Notes: 01674 * (1) The file contains a sequence of Sel descriptions. 01675 * (2) Each Sel is formatted as follows: 01676 * - Any number of comment lines starting with '#' are ignored 01677 * - The next line contains the selname 01678 * - The next lines contain the Sel data. They must be 01679 * formatted similarly to the string format in 01680 * selCreateFromString(), with each line beginning and 01681 * ending with a double-quote, and showing the 2D layout. 01682 * - Each Sel ends when a blank line, a comment line, or 01683 * the end of file is reached. 01684 * (3) See selCreateFromString() for a description of the string 01685 * format for the Sel data. As an example, here are the lines 01686 * of is a valid file for a single Sel. In the file, all lines 01687 * are left-justified: 01688 * # diagonal sel 01689 * sel_5diag 01690 * "x " 01691 * " x " 01692 * " X " 01693 * " x " 01694 * " x" 01695 */ 01696 SELA * 01697 selaCreateFromFile(const char *filename) 01698 { 01699 char *filestr, *line; 01700 l_int32 i, n, first, last, nsel, insel; 01701 size_t nbytes; 01702 NUMA *nafirst, *nalast; 01703 SARRAY *sa; 01704 SEL *sel; 01705 SELA *sela; 01706 01707 PROCNAME("selaCreateFromFile"); 01708 01709 if (!filename) 01710 return (SELA *)ERROR_PTR("filename not defined", procName, NULL); 01711 01712 filestr = (char *)l_binaryRead(filename, &nbytes); 01713 sa = sarrayCreateLinesFromString(filestr, 1); 01714 FREE(filestr); 01715 n = sarrayGetCount(sa); 01716 sela = selaCreate(0); 01717 01718 /* Find the start and end lines for each Sel. 01719 * We allow the "blank" lines to be null strings or 01720 * to have standard whitespace (' ','\t',\'n') or be '#'. */ 01721 nafirst = numaCreate(0); 01722 nalast = numaCreate(0); 01723 insel = FALSE; 01724 for (i = 0; i < n; i++) { 01725 line = sarrayGetString(sa, i, L_NOCOPY); 01726 if (!insel && 01727 (line[0] != '\0' && line[0] != ' ' && 01728 line[0] != '\t' && line[0] != '\n' && line[0] != '#')) { 01729 numaAddNumber(nafirst, i); 01730 insel = TRUE; 01731 continue; 01732 } 01733 if (insel && 01734 (line[0] == '\0' || line[0] == ' ' || 01735 line[0] == '\t' || line[0] == '\n' || line[0] == '#')) { 01736 numaAddNumber(nalast, i - 1); 01737 insel = FALSE; 01738 continue; 01739 } 01740 } 01741 if (insel) /* fell off the end of the file */ 01742 numaAddNumber(nalast, n - 1); 01743 01744 /* Extract sels */ 01745 nsel = numaGetCount(nafirst); 01746 for (i = 0; i < nsel; i++) { 01747 numaGetIValue(nafirst, i, &first); 01748 numaGetIValue(nalast, i, &last); 01749 if ((sel = selCreateFromSArray(sa, first, last)) == NULL) { 01750 fprintf(stderr, "Error reading sel from %d to %d\n", first, last); 01751 selaDestroy(&sela); 01752 sarrayDestroy(&sa); 01753 numaDestroy(&nafirst); 01754 numaDestroy(&nalast); 01755 return (SELA *)ERROR_PTR("bad sela file", procName, NULL); 01756 } 01757 selaAddSel(sela, sel, NULL, 0); 01758 } 01759 01760 numaDestroy(&nafirst); 01761 numaDestroy(&nalast); 01762 sarrayDestroy(&sa); 01763 return sela; 01764 } 01765 01766 01767 /*! 01768 * selCreateFromSArray() 01769 * 01770 * Input: sa 01771 * first (line of sarray where Sel begins) 01772 * last (line of sarray where Sel ends) 01773 * Return: sela, or null on error 01774 * 01775 * Notes: 01776 * (1) The Sel contains the following lines: 01777 * - The first line is the selname 01778 * - The remaining lines contain the Sel data. They must 01779 * be formatted similarly to the string format in 01780 * selCreateFromString(), with each line beginning and 01781 * ending with a double-quote, and showing the 2D layout. 01782 * - 'last' gives the last line in the Sel data. 01783 * (2) See selCreateFromString() for a description of the string 01784 * format for the Sel data. As an example, here are the lines 01785 * of is a valid file for a single Sel. In the file, all lines 01786 * are left-justified: 01787 * # diagonal sel 01788 * sel_5diag 01789 * "x " 01790 * " x " 01791 * " X " 01792 * " x " 01793 * " x" 01794 */ 01795 static SEL * 01796 selCreateFromSArray(SARRAY *sa, 01797 l_int32 first, 01798 l_int32 last) 01799 { 01800 char ch; 01801 char *name, *line; 01802 l_int32 n, len, i, w, h, y, x; 01803 SEL *sel; 01804 01805 PROCNAME("selCreateFromSArray"); 01806 01807 if (!sa) 01808 return (SEL *)ERROR_PTR("sa not defined", procName, NULL); 01809 n = sarrayGetCount(sa); 01810 if (first < 0 || first >= n || last <= first || last >= n) 01811 return (SEL *)ERROR_PTR("invalid range", procName, NULL); 01812 01813 name = sarrayGetString(sa, first, L_NOCOPY); 01814 h = last - first; 01815 line = sarrayGetString(sa, first + 1, L_NOCOPY); 01816 len = strlen(line); 01817 if (line[0] != '"' || line[len - 1] != '"') 01818 return (SEL *)ERROR_PTR("invalid format", procName, NULL); 01819 w = len - 2; 01820 if ((sel = selCreate(h, w, name)) == NULL) 01821 return (SEL *)ERROR_PTR("sel not made", procName, NULL); 01822 for (i = first + 1; i <= last; i++) { 01823 line = sarrayGetString(sa, i, L_NOCOPY); 01824 y = i - first - 1; 01825 for (x = 0; x < w; ++x) { 01826 ch = line[x + 1]; /* skip the leading double-quote */ 01827 switch (ch) 01828 { 01829 case 'X': 01830 selSetOrigin(sel, y, x); 01831 case 'x': 01832 selSetElement(sel, y, x, SEL_HIT); 01833 break; 01834 01835 case 'O': 01836 selSetOrigin(sel, y, x); 01837 case 'o': 01838 selSetElement(sel, y, x, SEL_MISS); 01839 break; 01840 01841 case 'C': 01842 selSetOrigin(sel, y, x); 01843 case ' ': 01844 selSetElement(sel, y, x, SEL_DONT_CARE); 01845 break; 01846 default: 01847 selDestroy(&sel); 01848 return (SEL *)ERROR_PTR("unknown char", procName, NULL); 01849 } 01850 } 01851 } 01852 01853 return sel; 01854 } 01855 01856 01857 /*----------------------------------------------------------------------* 01858 * Making hit-only SELs from Pta and Pix * 01859 *----------------------------------------------------------------------*/ 01860 /*! 01861 * selCreateFromPta() 01862 * 01863 * Input: pta 01864 * cy, cx (origin of sel) 01865 * name (<optional> sel name; can be null) 01866 * Return: sel (of minimum required size), or null on error 01867 * 01868 * Notes: 01869 * (1) The origin and all points in the pta must be positive. 01870 */ 01871 SEL * 01872 selCreateFromPta(PTA *pta, 01873 l_int32 cy, 01874 l_int32 cx, 01875 const char *name) 01876 { 01877 l_int32 i, n, x, y, w, h; 01878 BOX *box; 01879 SEL *sel; 01880 01881 PROCNAME("selCreateFromPta"); 01882 01883 if (!pta) 01884 return (SEL *)ERROR_PTR("pta not defined", procName, NULL); 01885 if (cy < 0 || cx < 0) 01886 return (SEL *)ERROR_PTR("(cy, cx) not both >= 0", procName, NULL); 01887 n = ptaGetCount(pta); 01888 if (n == 0) 01889 return (SEL *)ERROR_PTR("no pts in pta", procName, NULL); 01890 01891 box = ptaGetBoundingRegion(pta); 01892 boxGetGeometry(box, &x, &y, &w, &h); 01893 boxDestroy(&box); 01894 if (x < 0 || y < 0) 01895 return (SEL *)ERROR_PTR("not all x and y >= 0", procName, NULL); 01896 01897 sel = selCreate(y + h, x + w, name); 01898 selSetOrigin(sel, cy, cx); 01899 for (i = 0; i < n; i++) { 01900 ptaGetIPt(pta, i, &x, &y); 01901 selSetElement(sel, y, x, SEL_HIT); 01902 } 01903 01904 return sel; 01905 } 01906 01907 01908 /*! 01909 * selCreateFromPix() 01910 * 01911 * Input: pix 01912 * cy, cx (origin of sel) 01913 * name (<optional> sel name; can be null) 01914 * Return: sel, or null on error 01915 * 01916 * Notes: 01917 * (1) The origin must be positive. 01918 */ 01919 SEL * 01920 selCreateFromPix(PIX *pix, 01921 l_int32 cy, 01922 l_int32 cx, 01923 const char *name) 01924 { 01925 SEL *sel; 01926 l_int32 i, j, w, h, d; 01927 l_uint32 val; 01928 01929 PROCNAME("selCreateFromPix"); 01930 01931 if (!pix) 01932 return (SEL *)ERROR_PTR("pix not defined", procName, NULL); 01933 if (cy < 0 || cx < 0) 01934 return (SEL *)ERROR_PTR("(cy, cx) not both >= 0", procName, NULL); 01935 pixGetDimensions(pix, &w, &h, &d); 01936 if (d != 1) 01937 return (SEL *)ERROR_PTR("pix not 1 bpp", procName, NULL); 01938 01939 sel = selCreate(h, w, name); 01940 selSetOrigin(sel, cy, cx); 01941 for (i = 0; i < h; i++) { 01942 for (j = 0; j < w; j++) { 01943 pixGetPixel(pix, j, i, &val); 01944 if (val) 01945 selSetElement(sel, i, j, SEL_HIT); 01946 } 01947 } 01948 01949 return sel; 01950 } 01951 01952 01953 /*----------------------------------------------------------------------* 01954 * Making hit-miss sels from color Pix and image files * 01955 *----------------------------------------------------------------------*/ 01956 /*! 01957 * 01958 * selReadFromColorImage() 01959 * 01960 * Input: pathname 01961 * Return: sel if OK; null on error 01962 * 01963 * Notes: 01964 * (1) Loads an image from a file and creates a (hit-miss) sel. 01965 * (2) The sel name is taken from the pathname without the directory 01966 * and extension. 01967 */ 01968 SEL * 01969 selReadFromColorImage(const char *pathname) 01970 { 01971 PIX *pix; 01972 SEL *sel; 01973 char *basename, *selname; 01974 01975 PROCNAME("selReadFromColorImage"); 01976 01977 splitPathAtExtension (pathname, &basename, NULL); 01978 splitPathAtDirectory (basename, NULL, &selname); 01979 FREE(basename); 01980 01981 if ((pix = pixRead(pathname)) == NULL) 01982 return (SEL *)ERROR_PTR("pix not returned", procName, NULL); 01983 if ((sel = selCreateFromColorPix(pix, selname)) == NULL) 01984 return (SEL *)ERROR_PTR("sel not made", procName, NULL); 01985 FREE(selname); 01986 pixDestroy(&pix); 01987 01988 return sel; 01989 } 01990 01991 01992 /*! 01993 * 01994 * selCreateFromColorPix() 01995 * 01996 * Input: pixs (cmapped or rgb) 01997 * selname (<optional> sel name; can be null) 01998 * Return: sel if OK, null on error 01999 * 02000 * Notes: 02001 * (1) The sel size is given by the size of pixs. 02002 * (2) In pixs, hits are represented by green pixels, misses by red 02003 * pixels, and don't-cares by white pixels. 02004 * (3) In pixs, there may be no misses, but there must be at least 1 hit. 02005 * (4) At most there can be only one origin pixel, which is optionally 02006 * specified by using a lower-intensity pixel: 02007 * if a hit: dark green 02008 * if a miss: dark red 02009 * if a don't care: gray 02010 * If there is no such pixel, the origin defaults to the approximate 02011 * center of the sel. 02012 */ 02013 SEL * 02014 selCreateFromColorPix(PIX *pixs, 02015 char *selname) 02016 { 02017 PIXCMAP *cmap; 02018 SEL *sel; 02019 l_int32 hascolor, hasorigin, nohits; 02020 l_int32 w, h, d, i, j, red, green, blue; 02021 l_uint32 pixval; 02022 02023 PROCNAME("selCreateFromColorPix"); 02024 02025 if (!pixs) 02026 return (SEL *)ERROR_PTR("pixs not defined", procName, NULL); 02027 02028 hascolor = FALSE; 02029 cmap = pixGetColormap(pixs); 02030 if (cmap) 02031 pixcmapHasColor(cmap, &hascolor); 02032 pixGetDimensions(pixs, &w, &h, &d); 02033 if (hascolor == FALSE && d != 32) 02034 return (SEL *)ERROR_PTR("pixs has no color", procName, NULL); 02035 02036 if ((sel = selCreate (h, w, NULL)) == NULL) 02037 return (SEL *)ERROR_PTR ("sel not made", procName, NULL); 02038 selSetOrigin (sel, h / 2, w / 2); 02039 selSetName(sel, selname); 02040 02041 hasorigin = FALSE; 02042 nohits = TRUE; 02043 for (i = 0; i < h; i++) { 02044 for (j = 0; j < w; j++) { 02045 pixGetPixel (pixs, j, i, &pixval); 02046 02047 if (cmap) 02048 pixcmapGetColor (cmap, pixval, &red, &green, &blue); 02049 else { 02050 red = GET_DATA_BYTE (&pixval, COLOR_RED); 02051 green = GET_DATA_BYTE (&pixval, COLOR_GREEN); 02052 blue = GET_DATA_BYTE (&pixval, COLOR_BLUE); 02053 } 02054 02055 if (red < 255 && green < 255 && blue < 255) { 02056 if (hasorigin) 02057 L_WARNING("multiple origins in sel image", procName); 02058 selSetOrigin (sel, i, j); 02059 hasorigin = TRUE; 02060 } 02061 if (!red && green && !blue) { 02062 nohits = FALSE; 02063 selSetElement (sel, i, j, SEL_HIT); 02064 } 02065 else if (red && !green && !blue) 02066 selSetElement (sel, i, j, SEL_MISS); 02067 else if (red && green && blue) 02068 selSetElement (sel, i, j, SEL_DONT_CARE); 02069 else { 02070 selDestroy(&sel); 02071 return (SEL *)ERROR_PTR("invalid color", procName, NULL); 02072 } 02073 } 02074 } 02075 02076 if (nohits) { 02077 selDestroy(&sel); 02078 return (SEL *)ERROR_PTR("no hits in sel", procName, NULL); 02079 } 02080 return sel; 02081 } 02082 02083 02084 /*----------------------------------------------------------------------* 02085 * Printable display of sel * 02086 *----------------------------------------------------------------------*/ 02087 /*! 02088 * selDisplayInPix() 02089 * 02090 * Input: sel 02091 * size (of grid interiors; odd; minimum size of 13 is enforced) 02092 * gthick (grid thickness; minimum size of 2 is enforced) 02093 * Return: pix (display of sel), or null on error 02094 * 02095 * Notes: 02096 * (1) This gives a visual representation of a general (hit-miss) sel. 02097 * (2) The empty sel is represented by a grid of intersecting lines. 02098 * (3) Three different patterns are generated for the sel elements: 02099 * - hit (solid black circle) 02100 * - miss (black ring; inner radius is radius2) 02101 * - origin (cross, XORed with whatever is there) 02102 */ 02103 PIX * 02104 selDisplayInPix(SEL *sel, 02105 l_int32 size, 02106 l_int32 gthick) 02107 { 02108 l_int32 i, j, w, h, sx, sy, cx, cy, type, width; 02109 l_int32 radius1, radius2, shift1, shift2, x0, y0; 02110 PIX *pixd, *pix2, *pixh, *pixm, *pixorig; 02111 PTA *pta1, *pta2, *pta1t, *pta2t; 02112 02113 PROCNAME("selDisplayInPix"); 02114 02115 if (!sel) 02116 return (PIX *)ERROR_PTR("sel not defined", procName, NULL); 02117 if (size < 13) { 02118 L_WARNING("size < 13; setting to 13", procName); 02119 size = 13; 02120 } 02121 if (size % 2 == 0) 02122 size++; 02123 if (gthick < 2) { 02124 L_WARNING("grid thickness < 2; setting to 2", procName); 02125 gthick = 2; 02126 } 02127 selGetParameters(sel, &sy, &sx, &cy, &cx); 02128 w = size * sx + gthick * (sx + 1); 02129 h = size * sy + gthick * (sy + 1); 02130 pixd = pixCreate(w, h, 1); 02131 02132 /* Generate grid lines */ 02133 for (i = 0; i <= sy; i++) 02134 pixRenderLine(pixd, 0, gthick / 2 + i * (size + gthick), 02135 w - 1, gthick / 2 + i * (size + gthick), 02136 gthick, L_SET_PIXELS); 02137 for (j = 0; j <= sx; j++) 02138 pixRenderLine(pixd, gthick / 2 + j * (size + gthick), 0, 02139 gthick / 2 + j * (size + gthick), h - 1, 02140 gthick, L_SET_PIXELS); 02141 02142 /* Generate hit and miss patterns */ 02143 radius1 = (l_int32)(0.85 * ((size - 1) / 2) + 0.5); /* of hit */ 02144 radius2 = (l_int32)(0.65 * ((size - 1) / 2) + 0.5); /* inner miss radius */ 02145 pta1 = generatePtaFilledCircle(radius1); 02146 pta2 = generatePtaFilledCircle(radius2); 02147 shift1 = (size - 1) / 2 - radius1; /* center circle in square */ 02148 shift2 = (size - 1) / 2 - radius2; 02149 pta1t = ptaTransform(pta1, shift1, shift1, 1.0, 1.0); 02150 pta2t = ptaTransform(pta2, shift2, shift2, 1.0, 1.0); 02151 pixh = pixGenerateFromPta(pta1t, size, size); /* hits */ 02152 pix2 = pixGenerateFromPta(pta2t, size, size); 02153 pixm = pixSubtract(NULL, pixh, pix2); 02154 02155 /* Generate crossed lines for origin pattern */ 02156 pixorig = pixCreate(size, size, 1); 02157 width = size / 8; 02158 pixRenderLine(pixorig, size / 2, (l_int32)(0.12 * size), 02159 size / 2, (l_int32)(0.88 * size), 02160 width, L_SET_PIXELS); 02161 pixRenderLine(pixorig, (l_int32)(0.15 * size), size / 2, 02162 (l_int32)(0.85 * size), size / 2, 02163 width, L_FLIP_PIXELS); 02164 pixRasterop(pixorig, size / 2 - width, size / 2 - width, 02165 2 * width, 2 * width, PIX_NOT(PIX_DST), NULL, 0, 0); 02166 02167 /* Specialize origin pattern for this sel */ 02168 selGetTypeAtOrigin(sel, &type); 02169 if (type == SEL_HIT) 02170 pixXor(pixorig, pixorig, pixh); 02171 else if (type == SEL_MISS) 02172 pixXor(pixorig, pixorig, pixm); 02173 02174 /* Paste the patterns in */ 02175 y0 = gthick; 02176 for (i = 0; i < sy; i++) { 02177 x0 = gthick; 02178 for (j = 0; j < sx; j++) { 02179 selGetElement(sel, i, j, &type); 02180 if (i == cy && j == cx) /* origin */ 02181 pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixorig, 0, 0); 02182 else if (type == SEL_HIT) 02183 pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixh, 0, 0); 02184 else if (type == SEL_MISS) 02185 pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixm, 0, 0); 02186 x0 += size + gthick; 02187 } 02188 y0 += size + gthick; 02189 } 02190 02191 pixDestroy(&pix2); 02192 pixDestroy(&pixh); 02193 pixDestroy(&pixm); 02194 pixDestroy(&pixorig); 02195 ptaDestroy(&pta1); 02196 ptaDestroy(&pta1t); 02197 ptaDestroy(&pta2); 02198 ptaDestroy(&pta2t); 02199 return pixd; 02200 } 02201 02202 02203 /*! 02204 * selaDisplayInPix() 02205 * 02206 * Input: sela 02207 * size (of grid interiors; odd; minimum size of 13 is enforced) 02208 * gthick (grid thickness; minimum size of 2 is enforced) 02209 * spacing (between sels, both horizontally and vertically) 02210 * ncols (number of sels per "line") 02211 * Return: pix (display of all sels in sela), or null on error 02212 * 02213 * Notes: 02214 * (1) This gives a visual representation of all the sels in a sela. 02215 * (2) See notes in selDisplayInPix() for display params of each sel. 02216 * (3) This gives the nicest results when all sels in the sela 02217 * are the same size. 02218 */ 02219 PIX * 02220 selaDisplayInPix(SELA *sela, 02221 l_int32 size, 02222 l_int32 gthick, 02223 l_int32 spacing, 02224 l_int32 ncols) 02225 { 02226 l_int32 nsels, i, w, width; 02227 PIX *pixt, *pixd; 02228 PIXA *pixa; 02229 SEL *sel; 02230 02231 PROCNAME("selaDisplayInPix"); 02232 02233 if (!sela) 02234 return (PIX *)ERROR_PTR("sela not defined", procName, NULL); 02235 if (size < 13) { 02236 L_WARNING("size < 13; setting to 13", procName); 02237 size = 13; 02238 } 02239 if (size % 2 == 0) 02240 size++; 02241 if (gthick < 2) { 02242 L_WARNING("grid thickness < 2; setting to 2", procName); 02243 gthick = 2; 02244 } 02245 if (spacing < 5) { 02246 L_WARNING("spacing < 5; setting to 5", procName); 02247 spacing = 5; 02248 } 02249 02250 /* Accumulate the pix of each sel */ 02251 nsels = selaGetCount(sela); 02252 pixa = pixaCreate(nsels); 02253 for (i = 0; i < nsels; i++) { 02254 sel = selaGetSel(sela, i); 02255 pixt = selDisplayInPix(sel, size, gthick); 02256 pixaAddPix(pixa, pixt, L_INSERT); 02257 } 02258 02259 /* Find the tiled output width, using just the first 02260 * ncols pix in the pixa. If all pix have the same width, 02261 * they will align properly in columns. */ 02262 width = 0; 02263 ncols = L_MIN(nsels, ncols); 02264 for (i = 0; i < ncols; i++) { 02265 pixt = pixaGetPix(pixa, i, L_CLONE); 02266 pixGetDimensions(pixt, &w, NULL, NULL); 02267 width += w; 02268 pixDestroy(&pixt); 02269 } 02270 width += (ncols + 1) * spacing; /* add spacing all around as well */ 02271 02272 pixd = pixaDisplayTiledInRows(pixa, 1, width, 1.0, 0, spacing, 0); 02273 pixaDestroy(&pixa); 02274 return pixd; 02275 } 02276